generated from mitchell/rust_template
implemented some other controls
This commit is contained in:
parent
63153d76a0
commit
ead75f050a
47
src/controls/checkbox.rs
Normal file
47
src/controls/checkbox.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct CheckboxData {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlData for CheckboxData {
|
||||||
|
type ReturnType = bool;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
_validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.checkbox(control, value_getter, value_setter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn checkbox<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, CheckboxData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, CheckboxData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, CheckboxData, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/controls/group.rs
Normal file
30
src/controls/group.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use super::{ControlRenderData, VanityControlBuilder, VanityControlData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
use leptos::View;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct GroupData {
|
||||||
|
pub(crate) title: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VanityControlData for GroupData {
|
||||||
|
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View {
|
||||||
|
fs.group(control)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn group(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(VanityControlBuilder<FS, GroupData>) -> VanityControlBuilder<FS, GroupData>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_vanity(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FS: FormStyle> VanityControlBuilder<FS, GroupData> {
|
||||||
|
pub fn title(mut self, title: impl ToString) -> Self {
|
||||||
|
self.data.title = Some(title.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
use leptos::View;
|
|
||||||
|
|
||||||
use super::{ControlRenderData, VanityControlBuilder, VanityControlData};
|
use super::{ControlRenderData, VanityControlBuilder, VanityControlData};
|
||||||
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
use leptos::View;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct HeadingData {
|
pub struct HeadingData {
|
||||||
|
|||||||
32
src/controls/hidden.rs
Normal file
32
src/controls/hidden.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct HiddenData;
|
||||||
|
|
||||||
|
impl ControlData for HiddenData {
|
||||||
|
type ReturnType = String;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
_value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
_validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.hidden(control, value_getter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn hidden<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, HiddenData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, HiddenData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,16 @@
|
|||||||
use std::{fmt::Display, rc::Rc, str::FromStr};
|
|
||||||
|
|
||||||
use crate::{form::FormToolData, styles::FormStyle};
|
use crate::{form::FormToolData, styles::FormStyle};
|
||||||
use leptos::{RwSignal, Signal, View};
|
use leptos::{RwSignal, Signal, View};
|
||||||
|
use std::{fmt::Display, rc::Rc, str::FromStr};
|
||||||
|
|
||||||
|
pub mod checkbox;
|
||||||
|
pub mod group;
|
||||||
pub mod heading;
|
pub mod heading;
|
||||||
|
pub mod hidden;
|
||||||
|
pub mod output;
|
||||||
|
pub mod radio_buttons;
|
||||||
pub mod select;
|
pub mod select;
|
||||||
|
pub mod slider;
|
||||||
|
pub mod stepper;
|
||||||
pub mod submit;
|
pub mod submit;
|
||||||
pub mod text_area;
|
pub mod text_area;
|
||||||
pub mod text_input;
|
pub mod text_input;
|
||||||
@ -32,7 +38,9 @@ impl<FS, FD, F> RenderFn<FS, FD> for F where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for the data needed to render an static control.
|
// TODO: vanity signals should have an optional getter.
|
||||||
|
|
||||||
|
/// A trait for the data needed to render an read-only control.
|
||||||
pub trait VanityControlData: 'static {
|
pub trait VanityControlData: 'static {
|
||||||
/// Builds the control, returning the [`View`] that was built.
|
/// Builds the control, returning the [`View`] that was built.
|
||||||
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View;
|
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View;
|
||||||
@ -51,6 +59,7 @@ pub trait ControlData: 'static {
|
|||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View;
|
) -> View;
|
||||||
}
|
}
|
||||||
|
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> {
|
||||||
@ -58,7 +67,7 @@ pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
|
|||||||
pub style: Vec<FS::StylingAttributes>,
|
pub style: Vec<FS::StylingAttributes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data needed to render a static control of type `C`.
|
/// The data needed to render a read-only control of type `C`.
|
||||||
pub struct VanityControlBuilder<FS: FormStyle, C: VanityControlData> {
|
pub struct VanityControlBuilder<FS: FormStyle, C: VanityControlData> {
|
||||||
pub(crate) style_attributes: Vec<FS::StylingAttributes>,
|
pub(crate) style_attributes: Vec<FS::StylingAttributes>,
|
||||||
pub(crate) data: C,
|
pub(crate) data: C,
|
||||||
@ -212,6 +221,14 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a styling attribute to this control.
|
||||||
|
pub fn style(mut self, attribute: FS::StylingAttributes) -> Self {
|
||||||
|
self.style_attributes.push(attribute);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle, C: ValidatedControlData, FDT> ControlBuilder<FD, FS, C, FDT> {
|
||||||
/// Sets the validation function for this control
|
/// Sets the validation function for this control
|
||||||
///
|
///
|
||||||
/// This allows you to check if the parsed value is a valid value.
|
/// This allows you to check if the parsed value is a valid value.
|
||||||
@ -229,11 +246,6 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
|
|||||||
self.validation_fn = Some(Rc::new(validation_fn));
|
self.validation_fn = Some(Rc::new(validation_fn));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(mut self, attribute: FS::StylingAttributes) -> Self {
|
|
||||||
self.style_attributes.push(attribute);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD, FS, C, FDT> ControlBuilder<FD, FS, C, FDT>
|
impl<FD, FS, C, FDT> ControlBuilder<FD, FS, C, FDT>
|
||||||
|
|||||||
32
src/controls/output.rs
Normal file
32
src/controls/output.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct OutputData;
|
||||||
|
|
||||||
|
impl ControlData for OutputData {
|
||||||
|
type ReturnType = String;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
_value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
_validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.output(control, value_getter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn output<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, OutputData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, OutputData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/controls/radio_buttons.rs
Normal file
61
src/controls/radio_buttons.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct RadioButtonsData {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
|
pub(crate) options: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlData for RadioButtonsData {
|
||||||
|
type ReturnType = String;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.radio_buttons(control, value_getter, value_setter, validation_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ValidatedControlData for RadioButtonsData {}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn radio_buttons<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, RadioButtonsData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, RadioButtonsData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, RadioButtonsData, 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_option(mut self, option: impl ToString) -> Self {
|
||||||
|
self.data.options.push(option.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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,14 @@
|
|||||||
use leptos::{Signal, View};
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
use super::{ControlBuilder, ControlData, ControlRenderData};
|
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
|
||||||
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
// TODO: have an option to have a display string and a value string in the options field
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct SelectData {
|
pub struct SelectData {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
pub(crate) options: Vec<String>,
|
pub(crate) options: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +25,7 @@ impl ControlData for SelectData {
|
|||||||
fs.select(control, value_getter, value_setter, validation_state)
|
fs.select(control, value_getter, value_setter, validation_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ValidatedControlData for SelectData {}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
pub fn select<FDT: Clone + PartialEq + 'static>(
|
pub fn select<FDT: Clone + PartialEq + 'static>(
|
||||||
@ -35,13 +39,25 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, SelectData, FDT> {
|
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, SelectData, FDT> {
|
||||||
pub fn options(mut self, options: Vec<String>) -> Self {
|
pub fn named(mut self, control_name: impl ToString) -> Self {
|
||||||
self.data.options = options;
|
self.data.name = control_name.to_string();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn and_option(mut self, option: impl ToString) -> Self {
|
pub fn labeled(mut self, label: impl ToString) -> Self {
|
||||||
|
self.data.label = Some(label.to_string());
|
||||||
|
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());
|
||||||
self
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/controls/slider.rs
Normal file
78
src/controls/slider.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct SliderData {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
|
pub(crate) min: i32,
|
||||||
|
pub(crate) max: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SliderData {
|
||||||
|
fn default() -> Self {
|
||||||
|
SliderData {
|
||||||
|
name: String::new(),
|
||||||
|
label: None,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlData for SliderData {
|
||||||
|
type ReturnType = i32;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.slider(control, value_getter, value_setter, validation_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn slider<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, SliderData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, SliderData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, SliderData, 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 min(mut self, min: i32) -> Self {
|
||||||
|
self.data.min = min;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(mut self, max: i32) -> Self {
|
||||||
|
self.data.max = max;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(mut self, range: RangeInclusive<i32>) -> Self {
|
||||||
|
self.data.min = *range.start();
|
||||||
|
self.data.max = *range.end();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/controls/stepper.rs
Normal file
74
src/controls/stepper.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
|
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
|
||||||
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct StepperData {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) label: Option<String>,
|
||||||
|
pub(crate) step: Option<i32>,
|
||||||
|
pub(crate) min: Option<i32>,
|
||||||
|
pub(crate) max: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlData for StepperData {
|
||||||
|
type ReturnType = String;
|
||||||
|
|
||||||
|
fn build_control<FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: ControlRenderData<FS, Self>,
|
||||||
|
value_getter: Signal<Self::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(Self::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
fs.stepper(control, value_getter, value_setter, validation_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ValidatedControlData for StepperData {}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn stepper<FDT: Clone + PartialEq + 'static>(
|
||||||
|
self,
|
||||||
|
builder: impl Fn(
|
||||||
|
ControlBuilder<FD, FS, StepperData, FDT>,
|
||||||
|
) -> ControlBuilder<FD, FS, StepperData, FDT>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormToolData, FS: FormStyle, FDT> ControlBuilder<FD, FS, StepperData, 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 step(mut self, step: i32) -> Self {
|
||||||
|
self.data.step = Some(step);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min(mut self, min: i32) -> Self {
|
||||||
|
self.data.min = Some(min);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(mut self, max: i32) -> Self {
|
||||||
|
self.data.max = Some(max);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(mut self, range: RangeInclusive<i32>) -> Self {
|
||||||
|
self.data.min = Some(*range.start());
|
||||||
|
self.data.max = Some(*range.end());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
use super::{ControlBuilder, ControlData, ControlRenderData};
|
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};
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ impl ControlData for TextAreaData {
|
|||||||
fs.text_area(control, value_getter, value_setter, validation_state)
|
fs.text_area(control, value_getter, value_setter, validation_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ValidatedControlData for TextAreaData {}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
pub fn text_area<FDT: Clone + PartialEq + 'static>(
|
pub fn text_area<FDT: Clone + PartialEq + 'static>(
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use leptos::{Signal, View};
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
use super::{ControlBuilder, ControlData, ControlRenderData};
|
use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData};
|
||||||
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@ -37,6 +37,7 @@ impl ControlData for TextInputData {
|
|||||||
fs.text_input(control, value_getter, value_setter, validation_state)
|
fs.text_input(control, value_getter, value_setter, validation_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ValidatedControlData for TextInputData {}
|
||||||
|
|
||||||
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
pub fn text_input<FDT: Clone + PartialEq + 'static>(
|
pub fn text_input<FDT: Clone + PartialEq + 'static>(
|
||||||
|
|||||||
@ -61,7 +61,11 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_vanity<C: VanityControlData>(&mut self, vanity_control: VanityControlBuilder<FS, C>) {
|
// TODO: test this from a user context. A user adding a custom defined component.
|
||||||
|
pub fn add_vanity<C: VanityControlData>(
|
||||||
|
&mut self,
|
||||||
|
vanity_control: VanityControlBuilder<FS, C>,
|
||||||
|
) {
|
||||||
let render_data = vanity_control.build();
|
let render_data = vanity_control.build();
|
||||||
|
|
||||||
let render_fn = move |fs: &FS, _| {
|
let render_fn = move |fs: &FS, _| {
|
||||||
@ -72,7 +76,8 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
|
|||||||
self.render_fns.push(Box::new(render_fn));
|
self.render_fns.push(Box::new(render_fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_control<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
// TODO: test this from a user context. A user adding a custom defined component.
|
||||||
|
pub fn add_control<C: ControlData, FDT: Clone + PartialEq + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
control: ControlBuilder<FD, FS, C, FDT>,
|
control: ControlBuilder<FD, FS, C, FDT>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -1,9 +1,24 @@
|
|||||||
use super::FormStyle;
|
use super::FormStyle;
|
||||||
use crate::controls::{
|
use crate::controls::{
|
||||||
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
|
checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData, output::OutputData,
|
||||||
text_input::TextInputData, ControlData, ControlRenderData,
|
select::SelectData, submit::SubmitData, text_area::TextAreaData, text_input::TextInputData,
|
||||||
|
ControlData, ControlRenderData,
|
||||||
};
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
// TODO: move validation from happening on set, to happening on get
|
||||||
|
// I think.
|
||||||
|
// That might fix some issues where the field updates but the validation doesn't
|
||||||
|
/// I don't know if that will cause any loops or not...
|
||||||
|
|
||||||
|
// TODO: send the server fn directly instead of parsing from form data perhaps.
|
||||||
|
// This would need a note in the docs about graceful degration.
|
||||||
|
|
||||||
|
// TODO: some components dont have validation functions. They should not be able
|
||||||
|
// to specify one in the builder.
|
||||||
|
|
||||||
|
// TODO: add button
|
||||||
|
|
||||||
pub enum GridFormStylingAttributes {
|
pub enum GridFormStylingAttributes {
|
||||||
Width(u32),
|
Width(u32),
|
||||||
@ -40,6 +55,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
value_setter: Box<dyn Fn(<TextInputData as ControlData>::ReturnType)>,
|
value_setter: Box<dyn Fn(<TextInputData as ControlData>::ReturnType)>,
|
||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View {
|
) -> View {
|
||||||
|
// TODO: extract this to a common thing
|
||||||
let mut width = 1;
|
let mut width = 1;
|
||||||
for style in control.style {
|
for style in control.style {
|
||||||
match style {
|
match style {
|
||||||
@ -50,12 +66,12 @@ impl FormStyle for GridFormStyle {
|
|||||||
view! {
|
view! {
|
||||||
<div style:grid-column=format!("span {}", width)>
|
<div style:grid-column=format!("span {}", width)>
|
||||||
<div>
|
<div>
|
||||||
<label for={&control.data.name} class="form_label">
|
<label for={&control.data.name} class="form_label">
|
||||||
{control.data.label.as_ref()}
|
{control.data.label.as_ref()}
|
||||||
</label>
|
</label>
|
||||||
<span class="form_error">
|
<span class="form_error">
|
||||||
{move || format!("{}", validation_state.get().err().unwrap_or_default())}
|
{move || validation_state.get().err()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type=control.data.input_type
|
type=control.data.input_type
|
||||||
@ -63,7 +79,7 @@ impl FormStyle for GridFormStyle {
|
|||||||
name=control.data.name
|
name=control.data.name
|
||||||
placeholder=control.data.placeholder
|
placeholder=control.data.placeholder
|
||||||
prop:value=move || value_getter.get()
|
prop:value=move || value_getter.get()
|
||||||
on:change=move |ev| {
|
on:focusout=move |ev| {
|
||||||
value_setter(event_target_value(&ev));
|
value_setter(event_target_value(&ev));
|
||||||
}
|
}
|
||||||
class="form_input"
|
class="form_input"
|
||||||
@ -90,10 +106,10 @@ impl FormStyle for GridFormStyle {
|
|||||||
let cloned_value = value.clone();
|
let cloned_value = value.clone();
|
||||||
view! {
|
view! {
|
||||||
<option
|
<option
|
||||||
value={value}
|
value=value.clone()
|
||||||
selected=move || value_getter.get() == *cloned_value
|
selected=move || value_getter.get() == *cloned_value
|
||||||
>
|
>
|
||||||
*value
|
{value.clone()}
|
||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -101,7 +117,8 @@ impl FormStyle for GridFormStyle {
|
|||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
{move || format!("{:?}", validation_state.get())}
|
<span>{control.data.label}</span>
|
||||||
|
{move || validation_state.get().err()}
|
||||||
<select
|
<select
|
||||||
id=&control.data.name
|
id=&control.data.name
|
||||||
name=control.data.name
|
name=control.data.name
|
||||||
@ -156,4 +173,200 @@ impl FormStyle for GridFormStyle {
|
|||||||
fn custom_component(&self, view: View) -> View {
|
fn custom_component(&self, view: View) -> View {
|
||||||
view
|
view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hidden(
|
||||||
|
&self,
|
||||||
|
_control: ControlRenderData<Self, HiddenData>,
|
||||||
|
value_getter: Signal<String>,
|
||||||
|
) -> View {
|
||||||
|
view! {
|
||||||
|
<input prop:value=value_getter style="visibility: hidden"/>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn radio_buttons(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, crate::controls::radio_buttons::RadioButtonsData>,
|
||||||
|
value_getter: Signal<
|
||||||
|
<crate::controls::radio_buttons::RadioButtonsData as ControlData>::ReturnType,
|
||||||
|
>,
|
||||||
|
value_setter: Box<
|
||||||
|
dyn Fn(<crate::controls::radio_buttons::RadioButtonsData as ControlData>::ReturnType),
|
||||||
|
>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
let mut width = 1;
|
||||||
|
for style in control.style {
|
||||||
|
match style {
|
||||||
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value_setter = Rc::new(value_setter);
|
||||||
|
let buttons_view = control
|
||||||
|
.data
|
||||||
|
.options
|
||||||
|
.into_iter()
|
||||||
|
.map(|o| {
|
||||||
|
let value_setter = value_setter.clone();
|
||||||
|
let o_clone1 = o.clone();
|
||||||
|
let o_clone2 = o.clone();
|
||||||
|
view! {
|
||||||
|
<input type="radio" id=o.clone()_str name=&control.data.name value=o.clone() prop:checked=move || {value_getter.get() == o_clone1} on:input=move |ev| {
|
||||||
|
let new_value = event_target_checked(&ev);
|
||||||
|
if new_value {
|
||||||
|
value_setter(o_clone2.clone());
|
||||||
|
}
|
||||||
|
} />
|
||||||
|
<label for=&o> {&o} </label>
|
||||||
|
<br/>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_view();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div style:grid-column=format!("span {}", width)>
|
||||||
|
<div>
|
||||||
|
<label for={&control.data.name} class="form_label">
|
||||||
|
{control.data.label.as_ref()}
|
||||||
|
</label>
|
||||||
|
<span class="form_error">
|
||||||
|
{move || validation_state.get().err()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="form_input" class:form_input_invalid=move || validation_state.get().is_err()>
|
||||||
|
{buttons_view}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkbox(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, CheckboxData>,
|
||||||
|
value_getter: Signal<<CheckboxData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<CheckboxData as ControlData>::ReturnType)>,
|
||||||
|
) -> View {
|
||||||
|
let mut width = 1;
|
||||||
|
for style in control.style {
|
||||||
|
match style {
|
||||||
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div style:grid-column=format!("span {}", width)>
|
||||||
|
<label for={&control.data.name} class="form_label">
|
||||||
|
{control.data.label.as_ref()}
|
||||||
|
</label>
|
||||||
|
<label class="form_input" for=&control.data.name>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id=&control.data.name
|
||||||
|
name=&control.data.name
|
||||||
|
prop:checked=value_getter
|
||||||
|
on:input=move |ev| {
|
||||||
|
let new_value = event_target_checked(&ev);
|
||||||
|
value_setter(new_value);
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span>{control.data.label}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stepper(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, crate::controls::stepper::StepperData>,
|
||||||
|
value_getter: Signal<<crate::controls::stepper::StepperData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<
|
||||||
|
dyn Fn(<crate::controls::stepper::StepperData as ControlData>::ReturnType),
|
||||||
|
>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
let mut width = 1;
|
||||||
|
for style in control.style {
|
||||||
|
match style {
|
||||||
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div style:grid-column=format!("span {}", width)>
|
||||||
|
<div>
|
||||||
|
<label for={&control.data.name} class="form_label">
|
||||||
|
{control.data.label.as_ref()}
|
||||||
|
</label>
|
||||||
|
<span class="form_error">
|
||||||
|
{move || validation_state.get().err()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="number" id=&control.data.name name=&control.data.name step=control.data.step
|
||||||
|
prop:value=move || value_getter.get()
|
||||||
|
on:change=move |ev| {
|
||||||
|
value_setter(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
class="form_input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output(
|
||||||
|
&self,
|
||||||
|
_control: ControlRenderData<Self, OutputData>,
|
||||||
|
value_getter: Signal<String>,
|
||||||
|
) -> View {
|
||||||
|
view! {
|
||||||
|
<div> {move || value_getter.get()} </div>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slider(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, crate::controls::slider::SliderData>,
|
||||||
|
value_getter: Signal<<crate::controls::slider::SliderData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<crate::controls::slider::SliderData as ControlData>::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View {
|
||||||
|
let mut width = 1;
|
||||||
|
for style in control.style {
|
||||||
|
match style {
|
||||||
|
GridFormStylingAttributes::Width(w) => width = w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div style:grid-column=format!("span {}", width)>
|
||||||
|
<div>
|
||||||
|
<label for={&control.data.name} class="form_label">
|
||||||
|
{control.data.label.as_ref()}
|
||||||
|
</label>
|
||||||
|
<span class="form_error">
|
||||||
|
{move || validation_state.get().err()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type="range" id=&control.data.name name=&control.data.name min=control.data.min max=control.data.max
|
||||||
|
prop:value=move || value_getter.get()
|
||||||
|
on:input=move |ev| {
|
||||||
|
let value = event_target_value(&ev).parse::<i32>().ok();
|
||||||
|
if let Some(value) = value {
|
||||||
|
value_setter(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class="form_input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group(&self, _control: ControlRenderData<Self, crate::controls::group::GroupData>) -> View {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,10 @@ mod grid_form;
|
|||||||
pub use grid_form::{GridFormStyle, GridFormStylingAttributes};
|
pub use grid_form::{GridFormStyle, GridFormStylingAttributes};
|
||||||
|
|
||||||
use crate::controls::{
|
use crate::controls::{
|
||||||
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
|
checkbox::CheckboxData, group::GroupData, heading::HeadingData, hidden::HiddenData,
|
||||||
text_input::TextInputData, ControlData, ControlRenderData,
|
output::OutputData, radio_buttons::RadioButtonsData, select::SelectData, slider::SliderData,
|
||||||
|
stepper::StepperData, submit::SubmitData, text_area::TextAreaData, text_input::TextInputData,
|
||||||
|
ControlData, ControlRenderData,
|
||||||
};
|
};
|
||||||
use leptos::{Signal, View};
|
use leptos::{Signal, View};
|
||||||
|
|
||||||
@ -19,6 +21,11 @@ pub trait FormStyle: Default + 'static {
|
|||||||
/// wrapping should be done with `div` or similar elements.
|
/// wrapping should be done with `div` or similar elements.
|
||||||
fn form_frame(&self, children: View) -> View;
|
fn form_frame(&self, children: View) -> View;
|
||||||
fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View;
|
fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View;
|
||||||
|
fn hidden(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, HiddenData>,
|
||||||
|
value_getter: Signal<String>,
|
||||||
|
) -> View;
|
||||||
fn text_input(
|
fn text_input(
|
||||||
&self,
|
&self,
|
||||||
control: ControlRenderData<Self, TextInputData>,
|
control: ControlRenderData<Self, TextInputData>,
|
||||||
@ -26,6 +33,20 @@ pub trait FormStyle: Default + 'static {
|
|||||||
value_setter: Box<dyn Fn(<TextInputData as ControlData>::ReturnType)>,
|
value_setter: Box<dyn Fn(<TextInputData as ControlData>::ReturnType)>,
|
||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View;
|
) -> View;
|
||||||
|
fn text_area(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, TextAreaData>,
|
||||||
|
value_getter: Signal<<TextAreaData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<TextAreaData as ControlData>::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View;
|
||||||
|
fn radio_buttons(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, RadioButtonsData>,
|
||||||
|
value_getter: Signal<<RadioButtonsData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<RadioButtonsData as ControlData>::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View;
|
||||||
fn select(
|
fn select(
|
||||||
&self,
|
&self,
|
||||||
control: ControlRenderData<Self, SelectData>,
|
control: ControlRenderData<Self, SelectData>,
|
||||||
@ -33,15 +54,34 @@ 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 submit(&self, control: ControlRenderData<Self, SubmitData>) -> View;
|
fn checkbox(
|
||||||
fn text_area(
|
|
||||||
&self,
|
&self,
|
||||||
control: ControlRenderData<Self, TextAreaData>,
|
control: ControlRenderData<Self, CheckboxData>,
|
||||||
value_getter: Signal<<TextAreaData as ControlData>::ReturnType>,
|
value_getter: Signal<<CheckboxData as ControlData>::ReturnType>,
|
||||||
value_setter: Box<dyn Fn(<TextAreaData as ControlData>::ReturnType)>,
|
value_setter: Box<dyn Fn(<CheckboxData as ControlData>::ReturnType)>,
|
||||||
|
) -> View;
|
||||||
|
fn stepper(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, StepperData>,
|
||||||
|
value_getter: Signal<<StepperData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<StepperData as ControlData>::ReturnType)>,
|
||||||
validation_state: Signal<Result<(), String>>,
|
validation_state: Signal<Result<(), String>>,
|
||||||
) -> View;
|
) -> View;
|
||||||
|
fn output(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, OutputData>,
|
||||||
|
value_getter: Signal<String>,
|
||||||
|
) -> View;
|
||||||
|
fn slider(
|
||||||
|
&self,
|
||||||
|
control: ControlRenderData<Self, SliderData>,
|
||||||
|
value_getter: Signal<<SliderData as ControlData>::ReturnType>,
|
||||||
|
value_setter: Box<dyn Fn(<SliderData as ControlData>::ReturnType)>,
|
||||||
|
validation_state: Signal<Result<(), String>>,
|
||||||
|
) -> View;
|
||||||
|
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View;
|
||||||
// TODO: test custom component
|
// TODO: test custom component
|
||||||
fn custom_component(&self, view: View) -> View;
|
fn custom_component(&self, view: View) -> View;
|
||||||
// TODO: add group
|
// TODO: add group
|
||||||
|
fn group(&self, control: ControlRenderData<Self, GroupData>) -> View;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user