From fd1b364cd5ef2c20ad20bc1f51769793be764b85 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 6 Feb 2025 22:19:19 +0100 Subject: [PATCH] feat: UI Improvements --- crates/components/src/body.rs | 36 ++++++++++++++------ crates/components/src/hooks/use_form.rs | 1 + crates/components/src/input.rs | 44 +++++++++++++++--------- crates/components/src/table.rs | 4 +-- crates/hooks/src/theming/base.rs | 33 +++++++++--------- crates/hooks/src/theming/mod.rs | 3 -- examples/input.rs | 45 +++++++++++++++++++------ examples/table.rs | 4 +-- 8 files changed, 109 insertions(+), 61 deletions(-) diff --git a/crates/components/src/body.rs b/crates/components/src/body.rs index 12e631def..d3842f34b 100644 --- a/crates/components/src/body.rs +++ b/crates/components/src/body.rs @@ -13,6 +13,16 @@ pub struct BodyProps { pub theme: Option, /// Inner children for the Body. pub children: Element, + /// Spacing for the inner children of the Body. + pub spacing: Option, + /// Padding for the inner children of the Body. + pub padding: Option, + /// Width of the Body. + #[props(default = "fill".to_string())] + pub width: String, + /// Height of the Body. + #[props(default = "fill".to_string())] + pub height: String, } /// Usually used to wrap the application root component. @@ -35,22 +45,28 @@ pub struct BodyProps { /// } /// ``` #[allow(non_snake_case)] -pub fn Body(props: BodyProps) -> Element { - let theme = use_applied_theme!(&props.theme, body); - let BodyTheme { - background, - color, +pub fn Body( + BodyProps { + children, + theme, + spacing, padding, - } = theme; + width, + height, + }: BodyProps, +) -> Element { + let theme = use_applied_theme!(&theme, body); + let BodyTheme { background, color } = theme; rsx!( rect { - width: "fill", - height: "fill", + width, + height, color: "{color}", background: "{background}", - padding: "{padding}", - {&props.children} + spacing, + padding, + {&children} } ) } diff --git a/crates/components/src/hooks/use_form.rs b/crates/components/src/hooks/use_form.rs index 71f083108..407b93b90 100644 --- a/crates/components/src/hooks/use_form.rs +++ b/crates/components/src/hooks/use_form.rs @@ -42,6 +42,7 @@ impl UseForm { placeholder: Some(placeholder), auto_focus: false, onvalidate: None, + width: "150".to_string(), } } diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs index 78055a519..b6d009d25 100644 --- a/crates/components/src/input.rs +++ b/crates/components/src/input.rs @@ -106,6 +106,8 @@ pub struct InputProps { pub auto_focus: bool, /// Handler for the `onvalidate` function. pub onvalidate: Option>, + #[props(default = "150".to_string())] + pub width: String, } /// Small box to edit text. @@ -159,6 +161,7 @@ pub fn Input( placeholder, auto_focus, onvalidate, + width, }: InputProps, ) -> Element { let platform = use_platform(); @@ -170,7 +173,6 @@ pub fn Input( let InputTheme { border_fill, focus_border_fill, - width, margin, corner_radius, font_theme, @@ -180,6 +182,7 @@ pub fn Input( hover_background, } = use_applied_theme!(&theme, input); let mut focus = use_focus(); + let mut drag_origin = use_signal(|| None); let is_focused = focus.is_focused(); let display_placeholder = value.is_empty() && placeholder.is_some() && !is_focused; @@ -242,14 +245,22 @@ pub fn Input( let onmousedown = move |e: MouseEvent| { e.stop_propagation(); + drag_origin.set(Some(e.get_screen_coordinates() - e.element_coordinates)); if !display_placeholder { editable.process_event(&EditableEvent::MouseDown(e.data, 0)); } focus.focus(); }; - let onmousemove = move |e: MouseEvent| { - editable.process_event(&EditableEvent::MouseMove(e.data, 0)); + let onglobalmousemove = move |mut e: MouseEvent| { + if focus.is_focused() { + if let Some(drag_origin) = drag_origin() { + let data = Rc::get_mut(&mut e.data).unwrap(); + data.element_coordinates.x -= drag_origin.x; + data.element_coordinates.y -= drag_origin.y; + editable.process_event(&EditableEvent::MouseMove(e.data, 0)); + } + } }; let onmouseenter = move |_| { @@ -262,15 +273,18 @@ pub fn Input( *status.write() = InputStatus::default(); }; - let onglobalclick = move |_| match *status.read() { - InputStatus::Idle if focus.is_focused() => { - focus.unfocus(); - editable.process_event(&EditableEvent::Click); - } - InputStatus::Hovering => { - editable.process_event(&EditableEvent::Click); - } - _ => {} + let onglobalclick = move |_| { + match *status.read() { + InputStatus::Idle if focus.is_focused() => { + focus.unfocus(); + editable.process_event(&EditableEvent::Click); + } + InputStatus::Hovering => { + editable.process_event(&EditableEvent::Click); + } + _ => {} + }; + drag_origin.set(None); }; let a11y_id = focus.attribute(); @@ -305,7 +319,7 @@ pub fn Input( rsx!( rect { - width: "{width}", + width, direction: "vertical", color: "{color}", background: "{background}", @@ -330,10 +344,10 @@ pub fn Input( show_scrollbar: false, paragraph { min_width: "1", - margin: "8 12", + margin: "6 10", onglobalclick, onmousedown, - onmousemove, + onglobalmousemove, cursor_id: "0", cursor_index: "{cursor_char}", cursor_mode: "editable", diff --git a/crates/components/src/table.rs b/crates/components/src/table.rs index a84b651c2..2d83e29dd 100644 --- a/crates/components/src/table.rs +++ b/crates/components/src/table.rs @@ -215,7 +215,7 @@ pub fn Table( let TableTheme { background, corner_radius, - shadow, + divider_fill, font_theme: FontTheme { color }, .. } = use_applied_theme!(&theme, table); @@ -226,8 +226,8 @@ pub fn Table( color: "{color}", background: "{background}", corner_radius: "{corner_radius}", - shadow: "{shadow}", height: "{height}", + border: "1 outer {divider_fill}", {children} }) } diff --git a/crates/hooks/src/theming/base.rs b/crates/hooks/src/theming/base.rs index 3a9a59f2e..4fcec38fe 100644 --- a/crates/hooks/src/theming/base.rs +++ b/crates/hooks/src/theming/base.rs @@ -27,7 +27,6 @@ pub(crate) const BASE_THEME: Theme = Theme { body: BodyTheme { background: cow_borrowed!("key(background)"), color: cow_borrowed!("key(color)"), - padding: cow_borrowed!("none"), }, slider: SliderTheme { background: cow_borrowed!("key(surface)"), @@ -43,10 +42,10 @@ pub(crate) const BASE_THEME: Theme = Theme { }, border_fill: cow_borrowed!("key(surface)"), focus_border_fill: cow_borrowed!("key(focused_border)"), - shadow: cow_borrowed!("0 4 5 0 rgb(0, 0, 0, 0.1)"), - padding: cow_borrowed!("8 12"), + shadow: cow_borrowed!("0 2 3 0 rgb(0, 0, 0, 0.1)"), + padding: cow_borrowed!("6 12"), margin: cow_borrowed!("0"), - corner_radius: cow_borrowed!("8"), + corner_radius: cow_borrowed!("6"), width: cow_borrowed!("auto"), height: cow_borrowed!("auto"), }, @@ -57,11 +56,11 @@ pub(crate) const BASE_THEME: Theme = Theme { color: cow_borrowed!("key(primary_color)"), }, border_fill: cow_borrowed!("none"), - focus_border_fill: cow_borrowed!("key(focused_border)"), - shadow: cow_borrowed!("0 4 5 0 rgb(0, 0, 0, 0.1)"), - padding: cow_borrowed!("8 12"), + focus_border_fill: cow_borrowed!("key(primary_color)"), + shadow: cow_borrowed!("0 2 3 0 rgb(0, 0, 0, 0.1)"), + padding: cow_borrowed!("6 12"), margin: cow_borrowed!("0"), - corner_radius: cow_borrowed!("8"), + corner_radius: cow_borrowed!("6"), width: cow_borrowed!("auto"), height: cow_borrowed!("auto"), }, @@ -74,9 +73,9 @@ pub(crate) const BASE_THEME: Theme = Theme { border_fill: cow_borrowed!("key(surface)"), focus_border_fill: cow_borrowed!("key(secondary_opposite_surface)"), shadow: cow_borrowed!("none"), - padding: cow_borrowed!("8 12"), + padding: cow_borrowed!("6 12"), margin: cow_borrowed!("0"), - corner_radius: cow_borrowed!("8"), + corner_radius: cow_borrowed!("6"), width: cow_borrowed!("auto"), height: cow_borrowed!("auto"), }, @@ -91,10 +90,9 @@ pub(crate) const BASE_THEME: Theme = Theme { }, border_fill: cow_borrowed!("key(surface)"), focus_border_fill: cow_borrowed!("key(focused_border)"), - width: cow_borrowed!("150"), margin: cow_borrowed!("0"), - corner_radius: cow_borrowed!("10"), - shadow: cow_borrowed!("0 4 5 0 rgb(0, 0, 0, 0.1)"), + corner_radius: cow_borrowed!("6"), + shadow: cow_borrowed!("0 2 3 0 rgb(0, 0, 0, 0.1)"), }, switch: SwitchTheme { margin: cow_borrowed!("0"), @@ -161,13 +159,12 @@ pub(crate) const BASE_THEME: Theme = Theme { font_theme: FontTheme { color: cow_borrowed!("key(color)"), }, - background: cow_borrowed!("key(background)"), + background: cow_borrowed!("key(neutral_surface)"), arrow_fill: cow_borrowed!("key(solid)"), row_background: cow_borrowed!("transparent"), - alternate_row_background: cow_borrowed!("key(neutral_surface)"), - divider_fill: cow_borrowed!("key(secondary_surface)"), + alternate_row_background: cow_borrowed!("key(secondary_surface)"), + divider_fill: cow_borrowed!("key(surface)"), corner_radius: cow_borrowed!("6"), - shadow: cow_borrowed!("0 2 15 5 rgb(35, 35, 35, 70)"), }, graph: GraphTheme { width: cow_borrowed!("100%"), @@ -248,7 +245,7 @@ pub(crate) const BASE_THEME: Theme = Theme { font_theme: FontTheme { color: cow_borrowed!("key(color)"), }, - padding: cow_borrowed!("8 10"), + padding: cow_borrowed!("6 12"), width: cow_borrowed!("auto"), height: cow_borrowed!("auto"), }, diff --git a/crates/hooks/src/theming/mod.rs b/crates/hooks/src/theming/mod.rs index 8eddb741a..653da407d 100644 --- a/crates/hooks/src/theming/mod.rs +++ b/crates/hooks/src/theming/mod.rs @@ -243,7 +243,6 @@ define_theme! { border_fill: str, focus_border_fill: str, shadow: str, - width: str, margin: str, corner_radius: str, %[subthemes] @@ -292,7 +291,6 @@ define_theme! { %[cows] background: str, color: str, - padding: str, } } @@ -364,7 +362,6 @@ define_theme! { row_background: str, divider_fill: str, corner_radius: str, - shadow: str, %[subthemes] font_theme: FontTheme, } diff --git a/examples/input.rs b/examples/input.rs index 35f1d23ad..693989396 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -11,39 +11,62 @@ fn main() { fn app() -> Element { let mut values = use_signal(|| (String::new(), String::new())); + use_init_theme(|| DARK_THEME); rsx!( - rect { - overflow: "clip", - padding: "7", - width: "100%", - height: "100%", - spacing: "4", + Body { + padding: "10", + spacing: "10", label { - color: "black", "Your name:" } Input { value: values().0, - placeholder: "Name", + placeholder: "Enter your name...", + width: "fill", onchange: move |txt| { values.write().0 = txt; } } label { - color: "black", "Your age:" } Input { value: values().1, - placeholder: "Age", + placeholder: "Enter your age...", + width: "fill", onvalidate: |validator: InputValidator| { - validator.set_valid(validator.text().parse::().is_err()) + validator.set_valid(validator.text().parse::().is_ok()) }, onchange: move |txt| { values.write().1 = txt; } }, + rect { + width: "fill", + content: "flex", + direction: "horizontal", + spacing: "10", + Button { + theme: theme_with!(ButtonTheme { + width: "flex(1)".into(), + }), + onpress: move |_| { + *values.write() = (String::new(), String::new()); + }, + label { + "Clear" + } + } + FilledButton { + theme: theme_with!(ButtonTheme { + width: "flex(1)".into(), + }), + label { + "Submit" + } + } + } } ) } diff --git a/examples/table.rs b/examples/table.rs index d83979037..5e52165c6 100644 --- a/examples/table.rs +++ b/examples/table.rs @@ -90,10 +90,10 @@ fn app() -> Element { }; rsx!( - rect { + Body { padding: "10", + spacing: "10", label { - height: "25", "Ordering by {order}" } Table {