use super::{
BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData,
ValidationState,
};
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{
prelude::{AnyView, Get, RwSignal, Signal},
reactive::wrappers::write::SignalSetter,
};
use std::sync::Arc;
type DynamicOptionsGetter<FD> =
Arc<dyn Fn(RwSignal<FD>) -> Vec<(String, String)> + Send + Sync + 'static>;
pub struct SelectBuildData<FD: FormToolData> {
pub name: String,
pub label: Option<String>,
dynamic_options: Option<DynamicOptionsGetter<FD>>,
pub options: Signal<Vec<(String, String)>>,
pub blank_option: Option<String>,
}
impl<FD: FormToolData> Default for SelectBuildData<FD> {
fn default() -> Self {
SelectBuildData {
name: String::default(),
label: None,
dynamic_options: None,
options: Signal::default(),
blank_option: None,
}
}
}
impl<FD: FormToolData> Clone for SelectBuildData<FD> {
fn clone(&self) -> Self {
SelectBuildData {
name: self.name.clone(),
label: self.label.clone(),
dynamic_options: self.dynamic_options.clone(),
options: self.options,
blank_option: self.blank_option.clone(),
}
}
}
#[derive(Default, Clone)]
pub struct SelectData {
pub name: String,
pub label: Option<String>,
pub options: Signal<Vec<(String, String)>>,
pub blank_option: Option<String>,
}
impl<FD: FormToolData> ControlData<FD> for SelectBuildData<FD> {
type ReturnType = String;
fn render_control<FS: FormStyle>(
fs: &FS,
fd: RwSignal<FD>,
control: ControlRenderData<FS, Self>,
value_getter: Signal<Self::ReturnType>,
value_setter: SignalSetter<Self::ReturnType>,
validation_state: Signal<ValidationState>,
) -> AnyView {
let options = control
.data
.dynamic_options
.as_ref()
.map(|d| {
let d = d.clone();
Signal::derive(move || d(fd))
})
.unwrap_or(control.data.options);
let new_control = ControlRenderData {
styles: control.styles.clone(),
data: SelectData {
name: control.data.name.clone(),
label: control.data.label.clone(),
options,
blank_option: control.data.blank_option.clone(),
},
};
fs.select(new_control, value_getter, value_setter, validation_state)
}
}
impl<FD: FormToolData> ValidatedControlData<FD> for SelectBuildData<FD> {}
impl<FD: FormToolData> FormBuilder<FD> {
pub fn select<FDT: Clone + PartialEq + 'static>(
self,
builder: impl BuilderFn<ControlBuilder<FD, SelectBuildData<FD>, FDT>>,
) -> Self {
self.new_control(builder)
}
pub fn select_cx<FDT: Clone + PartialEq + 'static>(
self,
builder: impl BuilderCxFn<ControlBuilder<FD, SelectBuildData<FD>, FDT>, FD::Context>,
) -> Self {
self.new_control_cx(builder)
}
}
impl<FD: FormToolData, FDT> ControlBuilder<FD, SelectBuildData<FD>, FDT> {
pub fn named(mut self, control_name: impl ToString) -> Self {
self.data.name = control_name.to_string();
self
}
pub fn labeled(mut self, label: impl ToString) -> Self {
self.data.label = Some(label.to_string());
self
}
pub fn with_options(mut self, options: impl Iterator<Item = impl ToString>) -> Self {
self.data.dynamic_options = None;
let options = options.map(|v| (v.to_string(), v.to_string())).collect();
self.data.options = Signal::stored(options);
self
}
pub fn with_options_valued(
mut self,
options: impl Iterator<Item = (impl ToString, impl ToString)>,
) -> Self {
self.data.dynamic_options = None;
let options = options
.map(|(d, v)| (d.to_string(), v.to_string()))
.collect();
self.data.options = Signal::stored(options);
self
}
pub fn with_options_signal(mut self, options: Signal<Vec<String>>) -> Self {
self.data.dynamic_options = None;
let options = move || {
options
.get()
.into_iter()
.map(|v| (v.clone(), v))
.collect::<Vec<_>>()
};
self.data.options = Signal::derive(options);
self
}
pub fn with_options_valued_signal(mut self, options: Signal<Vec<(String, String)>>) -> Self {
self.data.dynamic_options = None;
self.data.options = options;
self
}
pub fn with_dynamic_options(
mut self,
derived_signal: impl Fn(RwSignal<FD>) -> Vec<String> + Send + Sync + 'static,
) -> Self {
let derived_signal = move |fd| {
derived_signal(fd)
.into_iter()
.map(|v| (v.clone(), v))
.collect::<Vec<_>>()
};
self.data.dynamic_options = Some(Arc::new(derived_signal));
self
}
pub fn with_dynamic_options_valued(
mut self,
derived_signal: impl Fn(RwSignal<FD>) -> Vec<(String, String)> + Send + Sync + 'static,
) -> Self {
self.data.dynamic_options = Some(Arc::new(derived_signal));
self
}
pub fn with_blank_option(mut self) -> Self {
self.data.blank_option = Some(String::new());
self
}
pub fn with_blank_option_displayed(mut self, display: impl ToString) -> Self {
self.data.blank_option = Some(display.to_string());
self
}
}