first stab

This commit is contained in:
Mitchell Marino 2024-03-15 21:12:19 -05:00
parent eab2e1b19b
commit bf6529cc1d
10 changed files with 355 additions and 5 deletions

View File

@ -3,7 +3,6 @@ name = "leptos_form_tool"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
leptos = "0.6.9"

30
src/controls/heading.rs Normal file
View File

@ -0,0 +1,30 @@
use leptos::View;
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
pub struct HeadingData {
pub(crate) title: String,
}
impl HeadingData {
fn new(title: String) -> Self {
HeadingData { title }
}
}
impl<FS: FormStyleProvider> ControlData<FS> for HeadingData {
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
fs.heading(self, attributes)
}
}
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
pub fn heading(self, title: impl ToString) -> ControlBuilder<FD, FS, HeadingData> {
ControlBuilder {
fb: self,
parse_fn: None,
style_attributes: Vec::new(),
data: HeadingData::new(title.to_string()),
}
}
}

5
src/controls/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod heading;
pub mod select;
pub mod submit;
pub mod text_area;
pub mod text_input;

41
src/controls/select.rs Normal file
View File

@ -0,0 +1,41 @@
use leptos::View;
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
pub struct SelectData {
pub(crate) name: String,
pub(crate) options: Vec<String>,
}
impl SelectData {
fn new(name: String) -> Self {
SelectData {
name,
options: Vec::new(),
}
}
}
impl<FS: FormStyleProvider> ControlData<FS> for SelectData {
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
fs.select(self, attributes)
}
}
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
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 {
self.data.options = options;
self
}
}

30
src/controls/submit.rs Normal file
View File

@ -0,0 +1,30 @@
use leptos::View;
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
pub struct SubmitData {
pub(crate) text: String,
}
impl SubmitData {
fn new(text: String) -> Self {
SubmitData { text }
}
}
impl<FS: FormStyleProvider> ControlData<FS> for SubmitData {
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
fs.submit(self, attributes)
}
}
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
pub fn submit(self, text: impl ToString) -> ControlBuilder<FD, FS, SubmitData> {
ControlBuilder {
fb: self,
parse_fn: None,
style_attributes: Vec::new(),
data: SubmitData::new(text.to_string()),
}
}
}

41
src/controls/text_area.rs Normal file
View File

@ -0,0 +1,41 @@
use leptos::View;
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
pub struct TextAreaData {
pub(crate) name: String,
pub(crate) placeholder: Option<String>,
}
impl TextAreaData {
fn new(name: String) -> Self {
TextAreaData {
name,
placeholder: None,
}
}
}
impl<FS: FormStyleProvider> ControlData<FS> for TextAreaData {
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
fs.text_area(self, attributes)
}
}
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
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 {
self.data.placeholder = Some(placeholder.to_string());
self
}
}

View File

@ -0,0 +1,62 @@
use leptos::View;
use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider};
pub struct TextInputData {
pub(crate) name: String,
pub(crate) placeholder: Option<String>,
pub(crate) label: Option<String>,
pub(crate) initital_text: String,
pub(crate) input_type: &'static str,
}
impl TextInputData {
fn new(name: String) -> Self {
TextInputData {
name,
placeholder: None,
label: None,
initital_text: String::new(),
input_type: "input",
}
}
}
impl<FS: FormStyleProvider> ControlData<FS> for TextInputData {
fn build_control(self, fs: &FS, attributes: Vec<FS::StylingAttributes>) -> View {
fs.text_input(self, attributes)
}
}
impl<FD: FormData, FS: FormStyleProvider> FormBuilder<FD, FS> {
pub fn text_input(self, name: impl ToString) -> ControlBuilder<FD, FS, TextInputData> {
ControlBuilder {
fb: self,
parse_fn: None,
style_attributes: Vec::new(),
data: TextInputData::new(name.to_string()),
}
}
}
impl<FD: FormData, FS: FormStyleProvider> ControlBuilder<FD, FS, TextInputData> {
pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
self.data.placeholder = Some(placeholder.to_string());
self
}
pub fn label(mut self, label: impl ToString) -> Self {
self.data.label = Some(label.to_string());
self
}
pub fn initial_text(mut self, text: impl ToString) -> Self {
self.data.initital_text = text.to_string();
self
}
pub fn password(mut self) -> Self {
self.data.input_type = "password";
self
}
}

59
src/lib.rs Normal file
View File

@ -0,0 +1,59 @@
use controls::{
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
text_input::TextInputData,
};
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
}
}

View File

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

86
src/provider_impl.rs Normal file
View File

@ -0,0 +1,86 @@
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
}
}