add signals where appropriate

This commit is contained in:
Mitchell Marino 2024-06-19 11:21:28 -05:00
parent c7c98f985f
commit 3c96a15680
6 changed files with 113 additions and 74 deletions

View File

@ -1,12 +1,12 @@
use super::{BuilderCxFn, BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use super::{BuilderCxFn, BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData};
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::View; use leptos::{MaybeSignal, Signal, View};
use std::rc::Rc; use std::rc::Rc;
/// Data used for the heading control. /// Data used for the heading control.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HeadingData { pub struct HeadingData {
pub title: String, pub title: MaybeSignal<String>,
} }
impl VanityControlData for HeadingData { impl VanityControlData for HeadingData {
@ -37,7 +37,13 @@ impl<FD: FormToolData> FormBuilder<FD> {
impl<FD: FormToolData> VanityControlBuilder<FD, HeadingData> { impl<FD: FormToolData> VanityControlBuilder<FD, HeadingData> {
/// Sets the title of this heading. /// Sets the title of this heading.
pub fn title(mut self, title: impl ToString) -> Self { pub fn title(mut self, title: impl ToString) -> Self {
self.data.title = title.to_string(); self.data.title = MaybeSignal::Static(title.to_string());
self
}
/// Sets the title of this heading to a signal.
pub fn title_signal(mut self, title: Signal<String>) -> Self {
self.data.title = MaybeSignal::Dynamic(title);
self self
} }
} }

View File

@ -2,19 +2,18 @@ use super::{
BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData, BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData,
}; };
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{Signal, View}; use leptos::{IntoSignal, MaybeSignal, Signal, SignalGet, View};
use std::rc::Rc; use std::rc::Rc;
/// Data used for the select control. /// Data used for the select control.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SelectData { pub struct SelectData {
pub name: String, pub name: String,
pub label: Option<String>, pub label: Option<String>,
/// The options for the select. /// The options for the select.
/// ///
/// The first value is the string to display, the second is the value. /// The first value is the string to display, the second is the value.
// TODO: maybe signal? pub options: MaybeSignal<Vec<(String, String)>>,
pub options: Vec<(String, String)>,
/// The display text for the blank option, if there is one. /// The display text for the blank option, if there is one.
pub blank_option: Option<String>, pub blank_option: Option<String>,
} }
@ -70,44 +69,51 @@ impl<FD: FormToolData, FDT> ControlBuilder<FD, SelectData, FDT> {
self self
} }
/// Adds the option to the select. /// Sets the options from the provided iterator.
pub fn with_option(mut self, option: impl ToString) -> Self { ///
self.data /// This will overwrite any pervious options setting.
.options
.push((option.to_string(), option.to_string()));
self
}
/// Adds the option to the select, specifying a different
/// value than what is displayed.
pub fn with_option_valued(mut self, display: impl ToString, value: impl ToString) -> Self {
self.data
.options
.push((display.to_string(), value.to_string()));
self
}
/// Adds all the options in the provided iterator to the select.
pub fn with_options(mut self, options: impl Iterator<Item = impl ToString>) -> Self { pub fn with_options(mut self, options: impl Iterator<Item = impl ToString>) -> Self {
for option in options { let options = options.map(|v| (v.to_string(), v.to_string())).collect();
self.data self.data.options = MaybeSignal::Static(options);
.options
.push((option.to_string(), option.to_string()));
}
self self
} }
/// Adds all the (display_string, value) pairs in the provided iterator /// Sets the options to the (display_string, value) pairs from the
/// to the select. /// provided iterator.
///
/// This will overwrite any pervious options setting.
pub fn with_options_valued( pub fn with_options_valued(
mut self, mut self,
options: impl Iterator<Item = (impl ToString, impl ToString)>, options: impl Iterator<Item = (impl ToString, impl ToString)>,
) -> Self { ) -> Self {
for option in options { let options = options
self.data .map(|(d, v)| (d.to_string(), v.to_string()))
.options .collect();
.push((option.0.to_string(), option.1.to_string())); self.data.options = MaybeSignal::Static(options);
} self
}
/// Sets the options from the provided signal.
///
/// This will overwrite any pervious options setting.
pub fn with_options_signal(mut self, options: Signal<Vec<String>>) -> Self {
let options = move || {
options
.get()
.into_iter()
.map(|v| (v.clone(), v))
.collect::<Vec<_>>()
};
self.data.options = MaybeSignal::Dynamic(options.into_signal());
self
}
/// Sets the options to the (display_string, value) pairs from the
/// provided signal.
///
/// This will overwrite any pervious options setting.
pub fn with_options_valued_signal(mut self, options: Signal<Vec<(String, String)>>) -> Self {
self.data.options = MaybeSignal::Dynamic(options);
self self
} }

View File

@ -1,15 +1,15 @@
use super::{BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use super::{BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData};
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{Signal, View}; use leptos::{MaybeSignal, Signal, View};
use std::{ops::RangeInclusive, rc::Rc}; use std::rc::Rc;
/// Data used for the slider control. /// Data used for the slider control.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct SliderData { pub struct SliderData {
pub name: String, pub name: String,
pub label: Option<String>, pub label: Option<String>,
pub min: i32, pub min: MaybeSignal<i32>,
pub max: i32, pub max: MaybeSignal<i32>,
} }
impl Default for SliderData { impl Default for SliderData {
@ -17,8 +17,8 @@ impl Default for SliderData {
SliderData { SliderData {
name: String::new(), name: String::new(),
label: None, label: None,
min: 0, min: MaybeSignal::Static(0),
max: 1, max: MaybeSignal::Static(1),
} }
} }
} }
@ -75,21 +75,25 @@ impl<FD: FormToolData, FDT> ControlBuilder<FD, SliderData, FDT> {
/// Sets the minimum value for the slider. /// Sets the minimum value for the slider.
pub fn min(mut self, min: i32) -> Self { pub fn min(mut self, min: i32) -> Self {
self.data.min = min; self.data.min = MaybeSignal::Static(min);
self
}
/// Sets the minimum value for the slider to a signal.
pub fn min_signal(mut self, min: Signal<i32>) -> Self {
self.data.min = MaybeSignal::Dynamic(min);
self self
} }
/// Sets the maximum value for the slider. /// Sets the maximum value for the slider.
pub fn max(mut self, max: i32) -> Self { pub fn max(mut self, max: i32) -> Self {
self.data.max = max; self.data.max = MaybeSignal::Static(max);
self self
} }
/// Sets the minimum and maximum values for the slider by providing a /// Sets the maximum value for the slider to a signal.
/// range. pub fn max_signal(mut self, max: Signal<i32>) -> Self {
pub fn range(mut self, range: RangeInclusive<i32>) -> Self { self.data.max = MaybeSignal::Dynamic(max);
self.data.min = *range.start();
self.data.max = *range.end();
self self
} }
} }

View File

@ -2,17 +2,17 @@ use super::{
BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData, BuilderCxFn, BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData,
}; };
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{Signal, View}; use leptos::{MaybeSignal, Signal, View};
use std::{ops::RangeInclusive, rc::Rc}; use std::rc::Rc;
/// Data used for the stepper control. /// Data used for the stepper control.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct StepperData { pub struct StepperData {
pub name: String, pub name: String,
pub label: Option<String>, pub label: Option<String>,
pub step: Option<i32>, pub step: Option<MaybeSignal<i32>>,
pub min: Option<i32>, pub min: Option<MaybeSignal<i32>>,
pub max: Option<i32>, pub max: Option<MaybeSignal<i32>>,
} }
impl ControlData for StepperData { impl ControlData for StepperData {
@ -68,26 +68,37 @@ impl<FD: FormToolData, FDT> ControlBuilder<FD, StepperData, FDT> {
/// Sets the step ammount. /// Sets the step ammount.
pub fn step(mut self, step: i32) -> Self { pub fn step(mut self, step: i32) -> Self {
self.data.step = Some(step); self.data.step = Some(MaybeSignal::Static(step));
self self
} }
/// Sets a minimum value. /// Sets the step ammount.
pub fn step_signal(mut self, step: Signal<i32>) -> Self {
self.data.step = Some(MaybeSignal::Dynamic(step));
self
}
/// Sets the minimum value for the slider.
pub fn min(mut self, min: i32) -> Self { pub fn min(mut self, min: i32) -> Self {
self.data.min = Some(min); self.data.min = Some(MaybeSignal::Static(min));
self self
} }
/// Sets a maximum value. /// Sets the minimum value for the slider to a signal.
pub fn min_signal(mut self, min: Signal<i32>) -> Self {
self.data.min = Some(MaybeSignal::Dynamic(min));
self
}
/// Sets the maximum value for the slider.
pub fn max(mut self, max: i32) -> Self { pub fn max(mut self, max: i32) -> Self {
self.data.max = Some(max); self.data.max = Some(MaybeSignal::Static(max));
self self
} }
/// Sets the minimum and maximum values using the range. /// Sets the maximum value for the slider to a signal.
pub fn range(mut self, range: RangeInclusive<i32>) -> Self { pub fn max_signal(mut self, max: Signal<i32>) -> Self {
self.data.min = Some(*range.start()); self.data.max = Some(MaybeSignal::Dynamic(max));
self.data.max = Some(*range.end());
self self
} }
} }

View File

@ -32,6 +32,7 @@ impl<FD: FormToolData> FormValidator<FD> {
/// ///
/// With this, you can render the form, get the form data, or get /// With this, you can render the form, get the form data, or get
/// a validator for the data. /// a validator for the data.
#[derive(Clone)]
pub struct Form<FD: FormToolData> { pub struct Form<FD: FormToolData> {
/// The form data signal. /// The form data signal.
pub fd: RwSignal<FD>, pub fd: RwSignal<FD>,

View File

@ -60,8 +60,7 @@ impl FormStyle for GridFormStyle {
} }
fn group(&self, group: Rc<ControlRenderData<Self, View>>) -> View { fn group(&self, group: Rc<ControlRenderData<Self, View>>) -> View {
let view = view! { <div class="form_group form_grid">{&group.data}</div> } let view = view! { <div class="form_group form_grid">{&group.data}</div> }.into_view();
.into_view();
self.custom_component(&group.styles, view) self.custom_component(&group.styles, view)
} }
@ -76,7 +75,7 @@ impl FormStyle for GridFormStyle {
fn heading(&self, control: Rc<ControlRenderData<Self, HeadingData>>) -> View { fn heading(&self, control: Rc<ControlRenderData<Self, HeadingData>>) -> View {
self.custom_component( self.custom_component(
&control.styles, &control.styles,
view! { <h2 class="form_heading">{&control.data.title}</h2> }.into_view(), view! { <h2 class="form_heading">{control.data.title.clone()}</h2> }.into_view(),
) )
} }
@ -132,7 +131,7 @@ impl FormStyle for GridFormStyle {
style="visibility: hidden; column-span: none" style="visibility: hidden; column-span: none"
/> />
} }
.into_view() .into_view()
} }
fn text_input( fn text_input(
@ -264,9 +263,12 @@ impl FormStyle for GridFormStyle {
value_setter: Rc<dyn Fn(<SelectData as ControlData>::ReturnType)>, value_setter: Rc<dyn Fn(<SelectData as ControlData>::ReturnType)>,
validation_state: Signal<Result<(), String>>, validation_state: Signal<Result<(), String>>,
) -> View { ) -> View {
let options_view = control let control_clone = control.clone();
let options_view = move || {
control_clone
.data .data
.options .options
.get()
.iter() .iter()
.map(|(display, value)| { .map(|(display, value)| {
let display = display.clone(); let display = display.clone();
@ -277,7 +279,16 @@ impl FormStyle for GridFormStyle {
</option> </option>
} }
}) })
.collect_view(); .collect_view()
};
let blank_option_view = control.data.blank_option.as_ref().map(|display| {
view! {
<option value="" selected=move || { value_getter.get().as_str() == "" }>
{display}
</option>
}
});
let view = view! { let view = view! {
<div> <div>
@ -294,7 +305,7 @@ impl FormStyle for GridFormStyle {
value_setter(event_target_value(&ev)); value_setter(event_target_value(&ev));
} }
> >
{blank_option_view}
{options_view} {options_view}
</select> </select>
} }