selects can have different values and customized button builder

This commit is contained in:
Mitchell Marino 2024-06-12 15:49:30 -05:00
parent 1dc676cc37
commit a39f2ce664
5 changed files with 104 additions and 67 deletions

View File

@ -1,48 +1,57 @@
use super::{ControlRenderData, VanityControlBuilder, VanityControlData};
use super::ControlRenderData;
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{RwSignal, Signal, View};
use leptos::RwSignal;
use std::rc::Rc;
use web_sys::MouseEvent;
#[derive(Clone)]
#[derive(Default, Clone)]
pub struct ButtonData<FD: FormToolData> {
pub(crate) text: String,
pub(crate) action: Option<Rc<dyn Fn(MouseEvent, &mut FD)>>,
// this will need to be set before calling the build method
pub(crate) fd_signal: RwSignal<FD>,
}
impl<FD: FormToolData> VanityControlData for ButtonData<FD> {
fn build_control<FS: FormStyle>(
fs: &FS,
control: ControlRenderData<FS, Self>,
_value_getter: Option<Signal<String>>,
) -> View {
fs.button(control)
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn button(
mut self,
builder: impl Fn(
VanityControlBuilder<FD, FS, ButtonData<FD>>,
) -> VanityControlBuilder<FD, FS, ButtonData<FD>>,
builder: impl Fn(ButtonBuilder<FD, FS>) -> ButtonBuilder<FD, FS>,
) -> Self {
let data = ButtonData {
text: String::default(),
action: None,
fd_signal: self.fd,
let button_builder = ButtonBuilder::new();
let control = builder(button_builder);
let render_data = ControlRenderData {
data: Box::new(control.data),
styles: control.styles,
};
let vanity_builder = VanityControlBuilder::new(data);
let control = builder(vanity_builder);
self.add_vanity(control);
let render_fn = move |fs: &FS, fd: RwSignal<FD>| {
let view = fs.button(render_data, fd);
(view, None)
};
self.render_fns.push(Box::new(render_fn));
self
}
}
impl<FD: FormToolData, FS: FormStyle> VanityControlBuilder<FD, FS, ButtonData<FD>> {
#[derive(Clone)]
pub struct ButtonBuilder<FD: FormToolData, FS: FormStyle> {
pub(crate) styles: Vec<FS::StylingAttributes>,
pub(crate) data: ButtonData<FD>,
}
impl<FD: FormToolData, FS: FormStyle> ButtonBuilder<FD, FS> {
fn new() -> Self {
ButtonBuilder {
styles: Vec::default(),
data: ButtonData::default(),
}
}
pub fn style(mut self, style: FS::StylingAttributes) -> Self {
self.styles.push(style);
self
}
pub fn text(mut self, text: impl ToString) -> Self {
self.data.text = text.to_string();
self

View File

@ -71,7 +71,7 @@ pub trait ValidatedControlData: ControlData {}
/// The data needed to render a interactive control of type `C`.
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
pub data: Box<C>,
pub style: Vec<FS::StylingAttributes>,
pub styles: Vec<FS::StylingAttributes>,
}
/// The data needed to render a read-only control of type `C`.
@ -101,7 +101,7 @@ impl<FD: FormToolData, FS: FormStyle, C: VanityControlData> VanityControlBuilder
BuiltVanityControlData {
render_data: ControlRenderData {
data: Box::new(self.data),
style: self.style_attributes,
styles: self.style_attributes,
},
getter: self.getter,
}
@ -209,7 +209,7 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
Ok(BuiltControlData {
render_data: ControlRenderData {
data: Box::new(self.data),
style: self.style_attributes,
styles: self.style_attributes,
},
getter,
setter,

View File

@ -1,7 +1,6 @@
use leptos::{Signal, View};
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{Signal, View};
// TODO: have an option to have a display string and a value string in the options field
@ -9,7 +8,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
pub struct SelectData {
pub(crate) name: String,
pub(crate) label: Option<String>,
pub(crate) options: Vec<String>,
pub(crate) options: Vec<(String, String)>,
}
impl ControlData for SelectData {
@ -50,13 +49,36 @@ impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, SelectData, FD
}
pub fn with_option(mut self, option: impl ToString) -> Self {
self.data.options.push(option.to_string());
self.data
.options
.push((option.to_string(), option.to_string()));
self
}
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
}
pub fn with_options(mut self, options: impl Iterator<Item = impl ToString>) -> Self {
for option in options {
self.data.options.push(option.to_string());
self.data
.options
.push((option.to_string(), option.to_string()));
}
self
}
pub fn with_options_valued(
mut self,
options: impl Iterator<Item = (impl ToString, impl ToString)>,
) -> Self {
for option in options {
self.data
.options
.push((option.0.to_string(), option.1.to_string()));
}
self
}

View File

@ -1,8 +1,11 @@
use super::FormStyle;
use crate::controls::{
checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData, output::OutputData,
select::SelectData, spacer::SpacerData, submit::SubmitData, text_area::TextAreaData,
text_input::TextInputData, ControlData, ControlRenderData,
use crate::{
controls::{
button::ButtonData, checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData,
output::OutputData, select::SelectData, spacer::SpacerData, submit::SubmitData,
text_area::TextAreaData, text_input::TextInputData, ControlData, ControlRenderData,
},
FormToolData,
};
use leptos::*;
use std::rc::Rc;
@ -35,7 +38,7 @@ impl FormStyle for GridFormStyle {
) -> View {
// TODO: extract this to a common thing
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -78,14 +81,10 @@ impl FormStyle for GridFormStyle {
.data
.options
.into_iter()
.map(|value| {
let cloned_value = value.clone();
.map(|(display, value)| {
view! {
<option
value=value.clone()
selected=move || {value_getter.get() == *cloned_value}
>
{value.clone()}
<option value=value.clone() selected=move || { value_getter.get() == *value }>
{display.clone()}
</option>
}
})
@ -112,9 +111,7 @@ impl FormStyle for GridFormStyle {
}
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View {
view! {
<input type="submit" value=control.data.text class="form_submit"/>
}
view! { <input type="submit" value=control.data.text class="form_submit"/> }
.into_view()
}
@ -137,7 +134,8 @@ impl FormStyle for GridFormStyle {
on:change=move |ev| {
value_setter(event_target_value(&ev));
}
></textarea>
>
</textarea>
</div>
}
@ -168,7 +166,7 @@ impl FormStyle for GridFormStyle {
validation_state: Signal<Result<(), String>>,
) -> View {
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -198,6 +196,7 @@ impl FormStyle for GridFormStyle {
}
}
/>
<label for=&o>{&o}</label>
<br/>
}
@ -230,7 +229,7 @@ impl FormStyle for GridFormStyle {
value_setter: Box<dyn Fn(<CheckboxData as ControlData>::ReturnType)>,
) -> View {
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -270,7 +269,7 @@ impl FormStyle for GridFormStyle {
validation_state: Signal<Result<(), String>>,
) -> View {
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -317,7 +316,7 @@ impl FormStyle for GridFormStyle {
validation_state: Signal<Result<(), String>>,
) -> View {
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -352,27 +351,32 @@ impl FormStyle for GridFormStyle {
.into_view()
}
fn button<FD: crate::FormToolData>(
fn button<FD: FormToolData>(
&self,
control: ControlRenderData<Self, crate::controls::button::ButtonData<FD>>,
control: ControlRenderData<Self, ButtonData<FD>>,
data_signal: RwSignal<FD>,
) -> View {
let mut width = 1;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
}
let action = control.data.action.clone();
let signal = control.data.fd_signal.clone();
let on_click = move |ev: MouseEvent| {
if let Some(action) = action.clone() {
signal.update(|fd| action(ev, fd));
data_signal.update(|fd| action(ev, fd));
}
};
view! {
<button type="button" class="form_button" on:click=on_click style:grid-column=format!("span {}", width)>
<button
type="button"
class="form_button"
on:click=on_click
style:grid-column=format!("span {}", width)
>
{control.data.text}
</button>
}
@ -398,15 +402,13 @@ impl FormStyle for GridFormStyle {
fn spacer(&self, control: ControlRenderData<Self, SpacerData>) -> View {
let mut width = 12;
for style in control.style {
for style in control.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
}
view! {
<div style:grid-column=format!("span {}", width) style:height=control.data.height/>
}
view! { <div style:grid-column=format!("span {}", width) style:height=control.data.height></div> }
.into_view()
}
}

View File

@ -9,7 +9,7 @@ use crate::{
},
FormToolData,
};
use leptos::{Signal, View};
use leptos::{RwSignal, Signal, View};
pub use grid_form::{GridFormStyle, GridFormStylingAttributes};
@ -58,7 +58,11 @@ pub trait FormStyle: Default + 'static {
value_setter: Box<dyn Fn(<SelectData as ControlData>::ReturnType)>,
validation_state: Signal<Result<(), String>>,
) -> View;
fn button<FD: FormToolData>(&self, control: ControlRenderData<Self, ButtonData<FD>>) -> View;
fn button<FD: FormToolData>(
&self,
control: ControlRenderData<Self, ButtonData<FD>>,
data_signal: RwSignal<FD>,
) -> View;
fn checkbox(
&self,
control: ControlRenderData<Self, CheckboxData>,