generated from mitchell/rust_template
selects can have different values and customized button builder
This commit is contained in:
parent
1dc676cc37
commit
a39f2ce664
@ -1,48 +1,57 @@
|
|||||||
use super::{ControlRenderData, VanityControlBuilder, VanityControlData};
|
use super::ControlRenderData;
|
||||||
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
use leptos::{RwSignal, Signal, View};
|
use leptos::RwSignal;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use web_sys::MouseEvent;
|
use web_sys::MouseEvent;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct ButtonData<FD: FormToolData> {
|
pub struct ButtonData<FD: FormToolData> {
|
||||||
pub(crate) text: String,
|
pub(crate) text: String,
|
||||||
pub(crate) action: Option<Rc<dyn Fn(MouseEvent, &mut FD)>>,
|
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> {
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
pub fn button(
|
pub fn button(
|
||||||
mut self,
|
mut self,
|
||||||
builder: impl Fn(
|
builder: impl Fn(ButtonBuilder<FD, FS>) -> ButtonBuilder<FD, FS>,
|
||||||
VanityControlBuilder<FD, FS, ButtonData<FD>>,
|
|
||||||
) -> VanityControlBuilder<FD, FS, ButtonData<FD>>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let data = ButtonData {
|
let button_builder = ButtonBuilder::new();
|
||||||
text: String::default(),
|
let control = builder(button_builder);
|
||||||
action: None,
|
|
||||||
fd_signal: self.fd,
|
let render_data = ControlRenderData {
|
||||||
|
data: Box::new(control.data),
|
||||||
|
styles: control.styles,
|
||||||
};
|
};
|
||||||
let vanity_builder = VanityControlBuilder::new(data);
|
|
||||||
let control = builder(vanity_builder);
|
let render_fn = move |fs: &FS, fd: RwSignal<FD>| {
|
||||||
self.add_vanity(control);
|
let view = fs.button(render_data, fd);
|
||||||
|
(view, None)
|
||||||
|
};
|
||||||
|
self.render_fns.push(Box::new(render_fn));
|
||||||
|
|
||||||
self
|
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 {
|
pub fn text(mut self, text: impl ToString) -> Self {
|
||||||
self.data.text = text.to_string();
|
self.data.text = text.to_string();
|
||||||
self
|
self
|
||||||
|
|||||||
@ -71,7 +71,7 @@ pub trait ValidatedControlData: ControlData {}
|
|||||||
/// The data needed to render a interactive control of type `C`.
|
/// The data needed to render a interactive control of type `C`.
|
||||||
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
|
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
|
||||||
pub data: Box<C>,
|
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`.
|
/// 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 {
|
BuiltVanityControlData {
|
||||||
render_data: ControlRenderData {
|
render_data: ControlRenderData {
|
||||||
data: Box::new(self.data),
|
data: Box::new(self.data),
|
||||||
style: self.style_attributes,
|
styles: self.style_attributes,
|
||||||
},
|
},
|
||||||
getter: self.getter,
|
getter: self.getter,
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
|
|||||||
Ok(BuiltControlData {
|
Ok(BuiltControlData {
|
||||||
render_data: ControlRenderData {
|
render_data: ControlRenderData {
|
||||||
data: Box::new(self.data),
|
data: Box::new(self.data),
|
||||||
style: self.style_attributes,
|
styles: self.style_attributes,
|
||||||
},
|
},
|
||||||
getter,
|
getter,
|
||||||
setter,
|
setter,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use leptos::{Signal, View};
|
|
||||||
|
|
||||||
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
|
use super::{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};
|
||||||
|
|
||||||
// TODO: have an option to have a display string and a value string in the options field
|
// 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 struct SelectData {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) label: Option<String>,
|
pub(crate) label: Option<String>,
|
||||||
pub(crate) options: Vec<String>,
|
pub(crate) options: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ControlData for SelectData {
|
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 {
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
use super::FormStyle;
|
use super::FormStyle;
|
||||||
use crate::controls::{
|
use crate::{
|
||||||
checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData, output::OutputData,
|
controls::{
|
||||||
select::SelectData, spacer::SpacerData, submit::SubmitData, text_area::TextAreaData,
|
button::ButtonData, checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData,
|
||||||
text_input::TextInputData, ControlData, ControlRenderData,
|
output::OutputData, select::SelectData, spacer::SpacerData, submit::SubmitData,
|
||||||
|
text_area::TextAreaData, text_input::TextInputData, ControlData, ControlRenderData,
|
||||||
|
},
|
||||||
|
FormToolData,
|
||||||
};
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -35,7 +38,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
) -> View {
|
) -> View {
|
||||||
// TODO: extract this to a common thing
|
// TODO: extract this to a common thing
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
@ -78,14 +81,10 @@ impl FormStyle for GridFormStyle {
|
|||||||
.data
|
.data
|
||||||
.options
|
.options
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|value| {
|
.map(|(display, value)| {
|
||||||
let cloned_value = value.clone();
|
|
||||||
view! {
|
view! {
|
||||||
<option
|
<option value=value.clone() selected=move || { value_getter.get() == *value }>
|
||||||
value=value.clone()
|
{display.clone()}
|
||||||
selected=move || {value_getter.get() == *cloned_value}
|
|
||||||
>
|
|
||||||
{value.clone()}
|
|
||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -112,9 +111,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View {
|
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View {
|
||||||
view! {
|
view! { <input type="submit" value=control.data.text class="form_submit"/> }
|
||||||
<input type="submit" value=control.data.text class="form_submit"/>
|
|
||||||
}
|
|
||||||
.into_view()
|
.into_view()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +134,8 @@ impl FormStyle for GridFormStyle {
|
|||||||
on:change=move |ev| {
|
on:change=move |ev| {
|
||||||
value_setter(event_target_value(&ev));
|
value_setter(event_target_value(&ev));
|
||||||
}
|
}
|
||||||
></textarea>
|
>
|
||||||
|
</textarea>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -168,7 +166,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View {
|
) -> View {
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
@ -198,6 +196,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<label for=&o>{&o}</label>
|
<label for=&o>{&o}</label>
|
||||||
<br/>
|
<br/>
|
||||||
}
|
}
|
||||||
@ -230,7 +229,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
value_setter: Box<dyn Fn(<CheckboxData as ControlData>::ReturnType)>,
|
value_setter: Box<dyn Fn(<CheckboxData as ControlData>::ReturnType)>,
|
||||||
) -> View {
|
) -> View {
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
@ -270,7 +269,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View {
|
) -> View {
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
@ -317,7 +316,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View {
|
) -> View {
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
@ -352,27 +351,32 @@ impl FormStyle for GridFormStyle {
|
|||||||
.into_view()
|
.into_view()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button<FD: crate::FormToolData>(
|
fn button<FD: FormToolData>(
|
||||||
&self,
|
&self,
|
||||||
control: ControlRenderData<Self, crate::controls::button::ButtonData<FD>>,
|
control: ControlRenderData<Self, ButtonData<FD>>,
|
||||||
|
data_signal: RwSignal<FD>,
|
||||||
) -> View {
|
) -> View {
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = control.data.action.clone();
|
let action = control.data.action.clone();
|
||||||
let signal = control.data.fd_signal.clone();
|
|
||||||
let on_click = move |ev: MouseEvent| {
|
let on_click = move |ev: MouseEvent| {
|
||||||
if let Some(action) = action.clone() {
|
if let Some(action) = action.clone() {
|
||||||
signal.update(|fd| action(ev, fd));
|
data_signal.update(|fd| action(ev, fd));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
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}
|
{control.data.text}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@ -398,15 +402,13 @@ impl FormStyle for GridFormStyle {
|
|||||||
|
|
||||||
fn spacer(&self, control: ControlRenderData<Self, SpacerData>) -> View {
|
fn spacer(&self, control: ControlRenderData<Self, SpacerData>) -> View {
|
||||||
let mut width = 12;
|
let mut width = 12;
|
||||||
for style in control.style {
|
for style in control.styles {
|
||||||
match style {
|
match style {
|
||||||
GridFormStylingAttributes::Width(w) => width = w,
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view! {
|
view! { <div style:grid-column=format!("span {}", width) style:height=control.data.height></div> }
|
||||||
<div style:grid-column=format!("span {}", width) style:height=control.data.height/>
|
|
||||||
}
|
|
||||||
.into_view()
|
.into_view()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
FormToolData,
|
FormToolData,
|
||||||
};
|
};
|
||||||
use leptos::{Signal, View};
|
use leptos::{RwSignal, Signal, View};
|
||||||
|
|
||||||
pub use grid_form::{GridFormStyle, GridFormStylingAttributes};
|
pub use grid_form::{GridFormStyle, GridFormStylingAttributes};
|
||||||
|
|
||||||
@ -58,7 +58,11 @@ pub trait FormStyle: Default + 'static {
|
|||||||
value_setter: Box<dyn Fn(<SelectData as ControlData>::ReturnType)>,
|
value_setter: Box<dyn Fn(<SelectData as ControlData>::ReturnType)>,
|
||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View;
|
) -> 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(
|
fn checkbox(
|
||||||
&self,
|
&self,
|
||||||
control: ControlRenderData<Self, CheckboxData>,
|
control: ControlRenderData<Self, CheckboxData>,
|
||||||
|
|||||||
Reference in New Issue
Block a user