generated from mitchell/rust_template
second round of impl
This commit is contained in:
parent
bf6529cc1d
commit
fbe746702a
@ -1,30 +1,37 @@
|
|||||||
use leptos::View;
|
use leptos::View;
|
||||||
|
|
||||||
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
|
use super::{VanityControl, VanityControlBuilder, VanityControlData};
|
||||||
|
use crate::{
|
||||||
|
form::{FormBuilder, FormData},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct HeadingData {
|
pub struct HeadingData {
|
||||||
pub(crate) title: String,
|
pub(crate) title: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadingData {
|
impl VanityControlData for HeadingData {
|
||||||
fn new(title: String) -> Self {
|
fn build_control<FD: FormData, FS: FormStyle>(
|
||||||
HeadingData { title }
|
fs: &FS,
|
||||||
|
control: VanityControl<FS, Self>,
|
||||||
|
) -> View {
|
||||||
|
fs.heading(control)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyleProvider> ControlData<FS> for HeadingData {
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
|
pub fn heading(
|
||||||
fs.heading(self, attributes)
|
self,
|
||||||
|
builder: impl Fn(VanityControlBuilder<FS, HeadingData>) -> VanityControl<FS, HeadingData>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_vanity(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
|
impl<FS: FormStyle> VanityControlBuilder<FS, HeadingData> {
|
||||||
pub fn heading(self, title: impl ToString) -> ControlBuilder<FD, FS, HeadingData> {
|
pub fn title(mut self, title: impl ToString) -> Self {
|
||||||
ControlBuilder {
|
self.data.title = title.to_string();
|
||||||
fb: self,
|
self
|
||||||
parse_fn: None,
|
|
||||||
style_attributes: Vec::new(),
|
|
||||||
data: HeadingData::new(title.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,119 @@
|
|||||||
|
use crate::{form::FormData, styles::FormStyle};
|
||||||
|
use leptos::View;
|
||||||
|
|
||||||
pub mod heading;
|
pub mod heading;
|
||||||
pub mod select;
|
pub mod select;
|
||||||
pub mod submit;
|
pub mod submit;
|
||||||
pub mod text_area;
|
pub mod text_area;
|
||||||
pub mod text_input;
|
pub mod text_input;
|
||||||
|
|
||||||
|
pub type ValidationFn<FD> = dyn Fn(&FD) -> Result<(), String> + 'static;
|
||||||
|
pub type ParseFn<FD, CReturnType> = dyn Fn(CReturnType, &mut FD) -> Result<(), String> + 'static;
|
||||||
|
|
||||||
|
pub trait VanityControlData: 'static {
|
||||||
|
fn build_control<FD: FormData, FS: FormStyle>(
|
||||||
|
fs: &FS,
|
||||||
|
control: VanityControl<FS, Self>,
|
||||||
|
) -> View;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ControlData: 'static {
|
||||||
|
type ReturnType;
|
||||||
|
|
||||||
|
// TODO: this should also return a getter for the data
|
||||||
|
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VanityControl<FS: FormStyle + ?Sized, C: VanityControlData + ?Sized> {
|
||||||
|
pub data: Box<C>,
|
||||||
|
pub style: Vec<FS::StylingAttributes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Control<FD: FormData + ?Sized, FS: FormStyle + ?Sized, C: ControlData + ?Sized> {
|
||||||
|
pub data: Box<C>,
|
||||||
|
pub parse_fn: Box<ParseFn<FD, C::ReturnType>>,
|
||||||
|
pub validation: Box<ValidationFn<FD>>,
|
||||||
|
pub style: Vec<FS::StylingAttributes>,
|
||||||
|
}
|
||||||
|
pub struct VanityControlBuilder<FS: FormStyle, C: VanityControlData> {
|
||||||
|
style_attributes: Vec<FS::StylingAttributes>,
|
||||||
|
data: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
|
||||||
|
pub(crate) fn new(data: C) -> Self {
|
||||||
|
VanityControlBuilder {
|
||||||
|
data,
|
||||||
|
style_attributes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> VanityControl<FS, C> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FS: FormStyle, C: VanityControlData> Into<VanityControl<FS, C>>
|
||||||
|
for VanityControlBuilder<FS, C>
|
||||||
|
{
|
||||||
|
fn into(self) -> VanityControl<FS, C> {
|
||||||
|
VanityControl {
|
||||||
|
data: Box::new(self.data),
|
||||||
|
style: self.style_attributes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlBuilder<FD: FormData, FS: FormStyle, C: ControlData> {
|
||||||
|
parse_fn: Box<ParseFn<FD, C::ReturnType>>,
|
||||||
|
validation_fn: Box<ValidationFn<FD>>,
|
||||||
|
style_attributes: Vec<FS::StylingAttributes>,
|
||||||
|
data: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormData, FS: FormStyle, C: ControlData> ControlBuilder<FD, FS, C> {
|
||||||
|
pub(crate) fn new(data: C) -> Self {
|
||||||
|
ControlBuilder {
|
||||||
|
data,
|
||||||
|
parse_fn: Box::new(|_, _| Ok(())),
|
||||||
|
validation_fn: Box::new(|_| Ok(())),
|
||||||
|
style_attributes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Control<FD, FS, C> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_fn(
|
||||||
|
mut self,
|
||||||
|
parse_fn: impl Fn(C::ReturnType, &mut FD) -> Result<(), String> + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.parse_fn = Box::new(parse_fn) as Box<ParseFn<FD, C::ReturnType>>;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validation_fn(
|
||||||
|
mut self,
|
||||||
|
validation_fn: impl Fn(&FD) -> Result<(), String> + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.validation_fn = Box::new(validation_fn) as Box<ValidationFn<FD>>;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn style(mut self, attribute: FS::StylingAttributes) -> Self {
|
||||||
|
self.style_attributes.push(attribute);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FD: FormData, FS: FormStyle, C: ControlData> Into<Control<FD, FS, C>>
|
||||||
|
for ControlBuilder<FD, FS, C>
|
||||||
|
{
|
||||||
|
fn into(self) -> Control<FD, FS, C> {
|
||||||
|
Control {
|
||||||
|
data: Box::new(self.data),
|
||||||
|
style: self.style_attributes,
|
||||||
|
parse_fn: self.parse_fn,
|
||||||
|
validation: self.validation_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,41 +1,42 @@
|
|||||||
use leptos::View;
|
use leptos::View;
|
||||||
|
|
||||||
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
|
use super::{Control, ControlBuilder, ControlData};
|
||||||
|
use crate::{
|
||||||
|
form::{FormBuilder, FormData},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[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) options: Vec<String>,
|
pub(crate) options: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectData {
|
impl ControlData for SelectData {
|
||||||
fn new(name: String) -> Self {
|
type ReturnType = String;
|
||||||
SelectData {
|
|
||||||
name,
|
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View {
|
||||||
options: Vec::new(),
|
fs.select(control)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyleProvider> ControlData<FS> for SelectData {
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
|
pub fn select(
|
||||||
fs.select(self, attributes)
|
self,
|
||||||
|
builder: impl Fn(ControlBuilder<FD, FS, SelectData>) -> Control<FD, FS, SelectData>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
|
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, SelectData> {
|
||||||
pub fn select(self, name: impl ToString) -> ControlBuilder<FD, FS, SelectData> {
|
|
||||||
ControlBuilder {
|
|
||||||
fb: self,
|
|
||||||
parse_fn: None,
|
|
||||||
style_attributes: Vec::new(),
|
|
||||||
data: SelectData::new(name.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> ControlBuilder<FD, FS, SelectData> {
|
|
||||||
pub fn options(mut self, options: Vec<String>) -> Self {
|
pub fn options(mut self, options: Vec<String>) -> Self {
|
||||||
self.data.options = options;
|
self.data.options = options;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn and_option(mut self, option: impl ToString) -> Self {
|
||||||
|
self.data.options.push(option.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,37 @@
|
|||||||
use leptos::View;
|
use leptos::View;
|
||||||
|
|
||||||
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
|
use super::{VanityControl, VanityControlBuilder, VanityControlData};
|
||||||
|
use crate::{
|
||||||
|
form::{FormBuilder, FormData},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct SubmitData {
|
pub struct SubmitData {
|
||||||
pub(crate) text: String,
|
pub(crate) text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubmitData {
|
impl VanityControlData for SubmitData {
|
||||||
fn new(text: String) -> Self {
|
fn build_control<FD: FormData, FS: FormStyle>(
|
||||||
SubmitData { text }
|
fs: &FS,
|
||||||
|
control: VanityControl<FS, Self>,
|
||||||
|
) -> View {
|
||||||
|
fs.submit(control)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyleProvider> ControlData<FS> for SubmitData {
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
|
pub fn submit(
|
||||||
fs.submit(self, attributes)
|
self,
|
||||||
|
builder: impl Fn(VanityControlBuilder<FS, SubmitData>) -> VanityControl<FS, SubmitData>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_vanity(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
|
impl<FS: FormStyle> VanityControlBuilder<FS, SubmitData> {
|
||||||
pub fn submit(self, text: impl ToString) -> ControlBuilder<FD, FS, SubmitData> {
|
pub fn text(mut self, text: impl ToString) -> Self {
|
||||||
ControlBuilder {
|
self.data.text = text.to_string();
|
||||||
fb: self,
|
self
|
||||||
parse_fn: None,
|
|
||||||
style_attributes: Vec::new(),
|
|
||||||
data: SubmitData::new(text.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,39 +1,35 @@
|
|||||||
use leptos::View;
|
use leptos::View;
|
||||||
|
|
||||||
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
|
use super::{Control, ControlBuilder, ControlData};
|
||||||
|
use crate::{
|
||||||
|
form::{FormBuilder, FormData},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct TextAreaData {
|
pub struct TextAreaData {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) placeholder: Option<String>,
|
pub(crate) placeholder: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextAreaData {
|
impl ControlData for TextAreaData {
|
||||||
fn new(name: String) -> Self {
|
type ReturnType = String;
|
||||||
TextAreaData {
|
|
||||||
name,
|
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View {
|
||||||
placeholder: None,
|
fs.text_area(control)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyleProvider> ControlData<FS> for TextAreaData {
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
|
pub fn text_area(
|
||||||
fs.text_area(self, attributes)
|
self,
|
||||||
|
builder: impl Fn(ControlBuilder<FD, FS, TextAreaData>) -> Control<FD, FS, TextAreaData>,
|
||||||
|
) -> Self {
|
||||||
|
self.new_control(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
|
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, TextAreaData> {
|
||||||
pub fn text_area(self, name: impl ToString) -> ControlBuilder<FD, FS, TextAreaData> {
|
|
||||||
ControlBuilder {
|
|
||||||
fb: self,
|
|
||||||
parse_fn: None,
|
|
||||||
style_attributes: Vec::new(),
|
|
||||||
data: TextAreaData::new(name.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> ControlBuilder<FD, FS, TextAreaData> {
|
|
||||||
pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
|
pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
|
||||||
self.data.placeholder = Some(placeholder.to_string());
|
self.data.placeholder = Some(placeholder.to_string());
|
||||||
self
|
self
|
||||||
|
|||||||
@ -1,45 +1,50 @@
|
|||||||
use leptos::View;
|
use leptos::View;
|
||||||
|
|
||||||
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
|
use super::{Control, ControlBuilder, ControlData};
|
||||||
|
use crate::{
|
||||||
|
form::{FormBuilder, FormData},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct TextInputData {
|
pub struct TextInputData {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) placeholder: Option<String>,
|
pub(crate) placeholder: Option<String>,
|
||||||
pub(crate) label: Option<String>,
|
pub(crate) label: Option<String>,
|
||||||
pub(crate) initital_text: String,
|
pub(crate) initial_text: String,
|
||||||
pub(crate) input_type: &'static str,
|
pub(crate) input_type: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextInputData {
|
impl Default for TextInputData {
|
||||||
fn new(name: String) -> Self {
|
fn default() -> Self {
|
||||||
TextInputData {
|
TextInputData {
|
||||||
name,
|
name: String::new(),
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
label: None,
|
label: None,
|
||||||
initital_text: String::new(),
|
initial_text: String::new(),
|
||||||
input_type: "input",
|
input_type: "input",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FS: FormStyleProvider> ControlData<FS> for TextInputData {
|
impl ControlData for TextInputData {
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
|
type ReturnType = String;
|
||||||
fs.text_input(self, attributes)
|
|
||||||
|
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View {
|
||||||
|
fs.text_input(control)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
pub fn text_input(self, name: impl ToString) -> ControlBuilder<FD, FS, TextInputData> {
|
pub fn text_input(
|
||||||
ControlBuilder {
|
self,
|
||||||
fb: self,
|
builder: impl Fn(ControlBuilder<FD, FS, TextInputData>) -> Control<FD, FS, TextInputData>,
|
||||||
parse_fn: None,
|
) -> Self {
|
||||||
style_attributes: Vec::new(),
|
self.new_control(builder)
|
||||||
data: TextInputData::new(name.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider> ControlBuilder<FD, FS, TextInputData> {
|
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, TextInputData> {
|
||||||
pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
|
pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
|
||||||
self.data.placeholder = Some(placeholder.to_string());
|
self.data.placeholder = Some(placeholder.to_string());
|
||||||
self
|
self
|
||||||
@ -51,7 +56,7 @@ impl<FD: FormData, FS: FormStyleProvider> ControlBuilder<FD, FS, TextInputData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn initial_text(mut self, text: impl ToString) -> Self {
|
pub fn initial_text(mut self, text: impl ToString) -> Self {
|
||||||
self.data.initital_text = text.to_string();
|
self.data.initial_text = text.to_string();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
src/form.rs
Normal file
68
src/form.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use crate::{
|
||||||
|
controls::{
|
||||||
|
Control, ControlBuilder, ControlData, ValidationFn, VanityControl, VanityControlBuilder,
|
||||||
|
VanityControlData,
|
||||||
|
},
|
||||||
|
styles::FormStyle,
|
||||||
|
};
|
||||||
|
use leptos::{IntoView, View};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct FormBuilder<FD: FormData, FS: FormStyle> {
|
||||||
|
_fd: PhantomData<FD>,
|
||||||
|
fs: FS,
|
||||||
|
validations: Vec<Box<ValidationFn<FD>>>,
|
||||||
|
views: Vec<View>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
|
||||||
|
pub fn new(form_style: FS) -> FormBuilder<FD, FS> {
|
||||||
|
FormBuilder {
|
||||||
|
_fd: PhantomData::default(),
|
||||||
|
fs: form_style,
|
||||||
|
validations: Vec::new(),
|
||||||
|
views: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_vanity<C: VanityControlData + Default>(
|
||||||
|
mut self,
|
||||||
|
builder: impl Fn(VanityControlBuilder<FS, C>) -> VanityControl<FS, C>,
|
||||||
|
) -> Self {
|
||||||
|
let vanity_builder = VanityControlBuilder::new(C::default());
|
||||||
|
let control = builder(vanity_builder);
|
||||||
|
self.add_vanity(control);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_control<C: ControlData + Default>(
|
||||||
|
mut self,
|
||||||
|
builder: impl Fn(ControlBuilder<FD, FS, C>) -> Control<FD, FS, C>,
|
||||||
|
) -> Self {
|
||||||
|
let control_builder = ControlBuilder::new(C::default());
|
||||||
|
let control = builder(control_builder);
|
||||||
|
self.add_control(control);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_vanity<C: VanityControlData>(&mut self, vanity_control: VanityControl<FS, C>) {
|
||||||
|
let view = VanityControlData::build_control::<FD, FS>(&self.fs, vanity_control);
|
||||||
|
self.views.push(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_control<C: ControlData>(&mut self, control: Control<FD, FS, C>) {
|
||||||
|
let view = ControlData::build_control(&self.fs, control);
|
||||||
|
self.views.push(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should return a Form object
|
||||||
|
// The Form should have `form_view()`, and `validate(&FD)` functions.
|
||||||
|
pub fn build(self) -> View {
|
||||||
|
self.views.into_view()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FormData: Default {
|
||||||
|
// TODO: this should return a Form Object
|
||||||
|
fn create_form() -> View;
|
||||||
|
}
|
||||||
62
src/lib.rs
62
src/lib.rs
@ -1,59 +1,3 @@
|
|||||||
use controls::{
|
pub mod controls;
|
||||||
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
|
pub mod form;
|
||||||
text_input::TextInputData,
|
pub mod styles;
|
||||||
};
|
|
||||||
use leptos::*;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
mod controls;
|
|
||||||
mod provider_impl;
|
|
||||||
|
|
||||||
pub trait FormStyleProvider {
|
|
||||||
type StylingAttributes;
|
|
||||||
|
|
||||||
fn heading(&self, data: HeadingData, attributes: Vec<Self::StylingAttributes>) -> View;
|
|
||||||
fn text_input(&self, data: TextInputData, attributes: Vec<Self::StylingAttributes>) -> View;
|
|
||||||
fn select(&self, data: SelectData, attributes: Vec<Self::StylingAttributes>) -> View;
|
|
||||||
fn submit(&self, data: SubmitData, attributes: Vec<Self::StylingAttributes>) -> View;
|
|
||||||
fn text_area(&self, data: TextAreaData, attributes: Vec<Self::StylingAttributes>) -> View;
|
|
||||||
fn custom_component(&self, component: View) -> View;
|
|
||||||
// TODO: add group
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ControlData<FS: FormStyleProvider>: 'static {
|
|
||||||
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FormBuilder<FD: FormData, FS: FormStyleProvider> {
|
|
||||||
_fd: PhantomData<FD>,
|
|
||||||
fs: FS,
|
|
||||||
controls: Vec<(Box<dyn ControlData<FS>>, Vec<FS::StylingAttributes>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FormData {}
|
|
||||||
|
|
||||||
pub struct ControlBuilder<FD: FormData, FS: FormStyleProvider, C: ControlData<FS>> {
|
|
||||||
fb: FormBuilder<FD, FS>,
|
|
||||||
parse_fn: Option<Box<dyn Fn(&str, &mut FD)>>,
|
|
||||||
style_attributes: Vec<FS::StylingAttributes>,
|
|
||||||
data: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<FD: FormData, FS: FormStyleProvider, C: ControlData<FS>> ControlBuilder<FD, FS, C> {
|
|
||||||
pub fn parse_fn(mut self, parse_fn: Box<dyn Fn(&str, &mut FD)>) -> Self {
|
|
||||||
self.parse_fn = Some(parse_fn);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn style(mut self, attribute: FS::StylingAttributes) -> Self {
|
|
||||||
self.style_attributes.push(attribute);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(mut self) -> FormBuilder<FD, FS> {
|
|
||||||
self.fb
|
|
||||||
.controls
|
|
||||||
.push((Box::new(self.data), self.style_attributes));
|
|
||||||
self.fb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,86 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
controls::{select::SelectData, submit::SubmitData, text_area::TextAreaData},
|
|
||||||
FormStyleProvider, HeadingData, TextInputData,
|
|
||||||
};
|
|
||||||
use leptos::*;
|
|
||||||
|
|
||||||
pub enum TailwindStylingAttibutes {
|
|
||||||
Width(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TailwindFormStyleProvider;
|
|
||||||
|
|
||||||
impl FormStyleProvider for TailwindFormStyleProvider {
|
|
||||||
type StylingAttributes = TailwindFormStyleProvider;
|
|
||||||
|
|
||||||
// fn label(&self, data: LabelData) -> View {
|
|
||||||
// view! {
|
|
||||||
// }.into_view()
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn heading(&self, data: HeadingData, attributes: Vec<Self::StylingAttributes>) -> View {
|
|
||||||
view! {
|
|
||||||
<h2 class="text-xl py-2 text-center font-bold text-gray-700 border-b-2 border-gray-800/60 mb-8">
|
|
||||||
{&data.title}
|
|
||||||
</h2>
|
|
||||||
}
|
|
||||||
.into_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_input(&self, data: TextInputData, attributes: Vec<Self::StylingAttributes>) -> View {
|
|
||||||
view! {
|
|
||||||
<label
|
|
||||||
for={&data.name}
|
|
||||||
class="block uppercase tracking-wide text-left text-gray-700 text-md font-bold ml-2 mb-1"
|
|
||||||
>
|
|
||||||
{data.label.as_ref()}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
// TODO:
|
|
||||||
// type=text_type
|
|
||||||
id=&data.name
|
|
||||||
name=data.name
|
|
||||||
// placeholder=placeholder
|
|
||||||
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
|
||||||
/>
|
|
||||||
}.into_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select(&self, data: SelectData, attributes: Vec<Self::StylingAttributes>) -> View {
|
|
||||||
view! {
|
|
||||||
<select
|
|
||||||
id=&data.name
|
|
||||||
name=data.name
|
|
||||||
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
|
||||||
>
|
|
||||||
{data.options}
|
|
||||||
</select>
|
|
||||||
}.into_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn submit(&self, data: SubmitData, attributes: Vec<Self::StylingAttributes>) -> View {
|
|
||||||
view! {
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
value=data.text
|
|
||||||
class="col-span-full rounded-2xl bg-sky-700 text-white font-bold hover:cursor-pointer hover:bg-sky-600 px-5 py-3 mx-auto mt-4"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
.into_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_area(&self, data: TextAreaData, attributes: Vec<Self::StylingAttributes>) -> View {
|
|
||||||
view! {
|
|
||||||
<textarea
|
|
||||||
id=&data.name
|
|
||||||
name=data.name
|
|
||||||
placeholder=data.placeholder
|
|
||||||
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
|
||||||
/>
|
|
||||||
}.into_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn custom_component(&self, component: View) -> View {
|
|
||||||
component
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
src/styles/mod.rs
Normal file
25
src/styles/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
mod tw_grid;
|
||||||
|
|
||||||
|
pub use tw_grid::{TailwindGridFormStyle, TailwindGridStylingAttributes};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
controls::{
|
||||||
|
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
|
||||||
|
text_input::TextInputData, Control, VanityControl,
|
||||||
|
},
|
||||||
|
form::FormData,
|
||||||
|
};
|
||||||
|
use leptos::View;
|
||||||
|
|
||||||
|
pub trait FormStyle: 'static {
|
||||||
|
type StylingAttributes;
|
||||||
|
|
||||||
|
// TODO: add form frame
|
||||||
|
fn heading(&self, control: VanityControl<Self, HeadingData>) -> View;
|
||||||
|
fn text_input<FD: FormData>(&self, control: Control<FD, Self, TextInputData>) -> View;
|
||||||
|
fn select<FD: FormData>(&self, control: Control<FD, Self, SelectData>) -> View;
|
||||||
|
fn submit(&self, control: VanityControl<Self, SubmitData>) -> View;
|
||||||
|
fn text_area<FD: FormData>(&self, control: Control<FD, Self, TextAreaData>) -> View;
|
||||||
|
fn custom_component(&self, view: View) -> View;
|
||||||
|
// TODO: add group
|
||||||
|
}
|
||||||
89
src/styles/tw_grid.rs
Normal file
89
src/styles/tw_grid.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use super::FormStyle;
|
||||||
|
use crate::{
|
||||||
|
controls::{
|
||||||
|
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
|
||||||
|
text_input::TextInputData, Control, VanityControl,
|
||||||
|
},
|
||||||
|
form::FormData,
|
||||||
|
};
|
||||||
|
use leptos::CollectView;
|
||||||
|
use leptos::{view, IntoView, View};
|
||||||
|
|
||||||
|
pub enum TailwindGridStylingAttributes {
|
||||||
|
Width(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TailwindGridFormStyle;
|
||||||
|
|
||||||
|
impl FormStyle for TailwindGridFormStyle {
|
||||||
|
type StylingAttributes = TailwindGridStylingAttributes;
|
||||||
|
|
||||||
|
fn heading(&self, control: VanityControl<Self, HeadingData>) -> View {
|
||||||
|
view! {
|
||||||
|
<h2 class="text-xl py-2 text-center font-bold text-gray-700 border-b-2 border-gray-800/60 mb-8">
|
||||||
|
{&control.data.title}
|
||||||
|
</h2>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_input<FD: FormData>(&self, control: Control<FD, Self, TextInputData>) -> View {
|
||||||
|
view! {
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
for={&control.data.name}
|
||||||
|
class="block uppercase tracking-wide text-left text-gray-700 text-md font-bold ml-2 mb-1"
|
||||||
|
>
|
||||||
|
{control.data.label.as_ref()}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
// TODO:
|
||||||
|
type=control.data.input_type
|
||||||
|
id=&control.data.name
|
||||||
|
name=control.data.name
|
||||||
|
placeholder=control.data.placeholder
|
||||||
|
value=control.data.initial_text
|
||||||
|
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select<FD: FormData>(&self, control: Control<FD, Self, SelectData>) -> View {
|
||||||
|
view! {
|
||||||
|
<select
|
||||||
|
id=&control.data.name
|
||||||
|
name=control.data.name
|
||||||
|
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
||||||
|
>
|
||||||
|
{control.data.options.iter().map(|option| view!{<option>{option}</option>}).collect_view()}
|
||||||
|
</select>
|
||||||
|
}.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit(&self, control: VanityControl<Self, SubmitData>) -> View {
|
||||||
|
view! {
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
value=control.data.text
|
||||||
|
class="col-span-full rounded-2xl bg-sky-700 text-white font-bold hover:cursor-pointer hover:bg-sky-600 px-5 py-3 mx-auto mt-4"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_area<FD: FormData>(&self, control: Control<FD, Self, TextAreaData>) -> View {
|
||||||
|
view! {
|
||||||
|
<textarea
|
||||||
|
id=&control.data.name
|
||||||
|
name=control.data.name
|
||||||
|
placeholder=control.data.placeholder
|
||||||
|
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
|
||||||
|
/>
|
||||||
|
}.into_view()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_component(&self, view: View) -> View {
|
||||||
|
view
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user