Feat/option (#93)

* feat: alert, avatar, badge option

* feat: some component option

* feat: add OptionalProp

* feat: some component option

* feat: some component option
This commit is contained in:
luoxiaozero 2024-01-31 20:13:26 +08:00
parent 40e0e51444
commit 80d190e15d
49 changed files with 371 additions and 230 deletions

View file

@ -1,8 +1,9 @@
mod theme; mod theme;
use crate::{ use crate::{
components::OptionComp,
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Icon, Theme, Icon, Theme,
}; };
use leptos::*; use leptos::*;
@ -41,8 +42,8 @@ impl AlertVariant {
#[component] #[component]
pub fn Alert( pub fn Alert(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: Option<MaybeSignal<String>>,
#[prop(into)] variant: MaybeSignal<AlertVariant>, #[prop(into)] variant: MaybeSignal<AlertVariant>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -84,18 +85,15 @@ pub fn Alert(
}); });
view! { view! {
<div class=class_list!["thaw-alert", move || class.get()] style=move || css_vars.get()> <div
class=class_list!["thaw-alert", class.map(| c | move || c.get())]
style=move || css_vars.get()
>
<Icon icon class="thaw-alert__icon"/> <Icon icon class="thaw-alert__icon"/>
<div> <div>
<OptionComp value=title let:title>
{move || { <div class="thaw-alert__header">{move || title.get()}</div>
let title = title.get(); </OptionComp>
if title.is_empty() {
None
} else {
view! { <div class="thaw-alert__header">{title}</div> }.into()
}
}}
<div class="thaw-alert__content">{children()}</div> <div class="thaw-alert__content">{children()}</div>
</div> </div>
</div> </div>

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth}, components::{Binder, Follower, FollowerPlacement, FollowerWidth},
use_theme, use_theme,
utils::{class_list::class_list, mount_style, Model, StoredMaybeSignal}, utils::{class_list::class_list, mount_style, Model, OptionalProp, StoredMaybeSignal},
ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme, ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme,
}; };
use leptos::*; use leptos::*;
@ -28,13 +28,13 @@ pub struct AutoCompleteSuffix {
#[component] #[component]
pub fn AutoComplete( pub fn AutoComplete(
#[prop(optional, into)] value: Model<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>, #[prop(optional, into)] options: MaybeSignal<Vec<AutoCompleteOption>>,
#[prop(optional, into)] clear_after_select: MaybeSignal<bool>, #[prop(optional, into)] clear_after_select: MaybeSignal<bool>,
#[prop(optional, into)] on_select: Option<Callback<String>>, #[prop(optional, into)] on_select: Option<Callback<String>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>, #[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] invalid: MaybeSignal<bool>, #[prop(optional, into)] invalid: MaybeSignal<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] auto_complete_prefix: Option<AutoCompletePrefix>, #[prop(optional)] auto_complete_prefix: Option<AutoCompletePrefix>,
#[prop(optional)] auto_complete_suffix: Option<AutoCompleteSuffix>, #[prop(optional)] auto_complete_suffix: Option<AutoCompleteSuffix>,
#[prop(optional)] comp_ref: ComponentRef<AutoCompleteRef>, #[prop(optional)] comp_ref: ComponentRef<AutoCompleteRef>,
@ -130,7 +130,7 @@ pub fn AutoComplete(
view! { view! {
<Binder target_ref=auto_complete_ref> <Binder target_ref=auto_complete_ref>
<div <div
class=class_list!["thaw-auto-complete", move || class.get()] class=class_list!["thaw-auto-complete", class.map(|c| move || c.get())]
ref=auto_complete_ref ref=auto_complete_ref
on:keydown=on_keydown on:keydown=on_keydown
> >

View file

@ -1,8 +1,9 @@
mod theme; mod theme;
use crate::{ use crate::{
components::OptionComp,
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -10,10 +11,10 @@ pub use theme::AvatarTheme;
#[component] #[component]
pub fn Avatar( pub fn Avatar(
#[prop(optional, into)] src: MaybeSignal<String>, #[prop(optional, into)] src: Option<MaybeSignal<String>>,
#[prop(optional, into)] round: MaybeSignal<bool>, #[prop(optional, into)] round: MaybeSignal<bool>,
#[prop(default = MaybeSignal::Static(30), into)] size: MaybeSignal<u16>, #[prop(default = MaybeSignal::Static(30), into)] size: MaybeSignal<u16>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| { let css_vars = create_memo(move |_| {
@ -34,12 +35,13 @@ pub fn Avatar(
mount_style("avatar", include_str!("./avatar.css")); mount_style("avatar", include_str!("./avatar.css"));
view! { view! {
<span class=class_list!["thaw-avatar", move || class.get()] style=move || css_vars.get()> <span
{move || { class=class_list!["thaw-avatar", class.map(| c | move || c.get())]
let src = src.get(); style=move || css_vars.get()
(!src.is_empty()).then(|| view! { <img src=src/> }) >
}} <OptionComp value=src let:src>
<img src=move || src.get()/>
</OptionComp>
</span> </span>
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -29,7 +29,7 @@ pub fn Badge(
#[prop(default = MaybeSignal::Static(u32::MAX), into)] max: MaybeSignal<u32>, #[prop(default = MaybeSignal::Static(u32::MAX), into)] max: MaybeSignal<u32>,
#[prop(optional, into)] variant: MaybeSignal<BadgeVariant>, #[prop(optional, into)] variant: MaybeSignal<BadgeVariant>,
#[prop(optional, into)] dot: MaybeSignal<bool>, #[prop(optional, into)] dot: MaybeSignal<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -61,7 +61,8 @@ pub fn Badge(
<div class="thaw-badge" style=move || css_vars.get()> <div class="thaw-badge" style=move || css_vars.get()>
<div class=class_list![ <div class=class_list![
"thaw-badge__sup", ("thaw-badge__sup--value", move || ! dot.get() && ! value.get() "thaw-badge__sup", ("thaw-badge__sup--value", move || ! dot.get() && ! value.get()
.is_empty()), ("thaw-badge__sup--dot", move || dot.get()), move || class.get() .is_empty()), ("thaw-badge__sup--dot", move || dot.get()), class.map(| c | move || c
.get())
]>{move || value.get()}</div> ]>{move || value.get()}</div>
{children()} {children()}
</div> </div>

View file

@ -1,10 +1,10 @@
use super::use_breadcrumb_separator; use super::use_breadcrumb_separator;
use crate::utils::class_list::class_list; use crate::utils::{class_list::class_list, OptionalProp};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn BreadcrumbItem( pub fn BreadcrumbItem(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let breadcrumb_separator = use_breadcrumb_separator(); let breadcrumb_separator = use_breadcrumb_separator();
@ -12,7 +12,7 @@ pub fn BreadcrumbItem(
view! { view! {
<li class="thaw-breadcrumb-item"> <li class="thaw-breadcrumb-item">
<span class=class_list![ <span class=class_list![
"thaw-breadcrumb-item__link", move || class.get() "thaw-breadcrumb-item__link", class.map(|c| move || c.get())
]>{children()}</span> ]>{children()}</span>
<span class="thaw-breadcrumb-item__separator"> <span class="thaw-breadcrumb-item__separator">
{move || breadcrumb_separator.0.get()} {move || breadcrumb_separator.0.get()}

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
pub use breadcrumb_item::BreadcrumbItem; pub use breadcrumb_item::BreadcrumbItem;
@ -13,7 +13,7 @@ pub use theme::BreadcrumbTheme;
#[component] #[component]
pub fn Breadcrumb( pub fn Breadcrumb(
#[prop(default = MaybeSignal::Static("/".to_string()),into)] separator: MaybeSignal<String>, #[prop(default = MaybeSignal::Static("/".to_string()),into)] separator: MaybeSignal<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("breadcrumb", include_str!("./breadcrumb.css")); mount_style("breadcrumb", include_str!("./breadcrumb.css"));
@ -40,7 +40,7 @@ pub fn Breadcrumb(
view! { view! {
<Provider value=BreadcrumbSeparatorInjection(separator)> <Provider value=BreadcrumbSeparatorInjection(separator)>
<nav <nav
class=class_list!["thaw-breadcrumb", move || class.get()] class=class_list!["thaw-breadcrumb", class.map(|c| move || c.get())]
style=move || css_vars.get() style=move || css_vars.get()
> >
<ul>{children()}</ul> <ul>{children()}</ul>

View file

@ -5,7 +5,7 @@ use crate::{
components::{OptionComp, Wave, WaveRef}, components::{OptionComp, Wave, WaveRef},
icon::Icon, icon::Icon,
theme::*, theme::*,
utils::{class_list::class_list, mount_style, ComponentRef}, utils::{class_list::class_list, mount_style, ComponentRef, OptionalProp},
}; };
pub use button_group::ButtonGroup; pub use button_group::ButtonGroup;
use leptos::*; use leptos::*;
@ -97,8 +97,8 @@ impl ButtonSize {
#[component] #[component]
pub fn Button( pub fn Button(
#[prop(optional, into)] style: MaybeSignal<String>, #[prop(optional, into)] style: Option<MaybeSignal<String>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] variant: MaybeSignal<ButtonVariant>, #[prop(optional, into)] variant: MaybeSignal<ButtonVariant>,
#[prop(optional, into)] color: MaybeSignal<ButtonColor>, #[prop(optional, into)] color: MaybeSignal<ButtonColor>,
#[prop(optional, into)] size: MaybeSignal<ButtonSize>, #[prop(optional, into)] size: MaybeSignal<ButtonSize>,
@ -228,10 +228,10 @@ pub fn Button(
ButtonVariant::Text), ("thaw-button--link", move || variant.get() == ButtonVariant::Text), ("thaw-button--link", move || variant.get() ==
ButtonVariant::Link), ("thaw-button--round", move || round.get()), ButtonVariant::Link), ("thaw-button--round", move || round.get()),
("thaw-button--circle", move || circle.get()), ("thaw-button--disabled", move || ("thaw-button--circle", move || circle.get()), ("thaw-button--disabled", move ||
disabled.get()), move || class.get() disabled.get()), class.map(|c| move || c.get())
] ]
style=move || format!("{}{}", css_vars.get(), style.get()) style=move || format!("{}{}", css_vars.get(), style.as_ref().map(|s| s.get()).unwrap_or_default())
disabled=move || disabled.get() disabled=move || disabled.get()
on:click=on_click on:click=on_click
> >

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
chrono::{Datelike, Days, Local, NaiveDate}, chrono::{Datelike, Days, Local, NaiveDate},
use_theme, use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Button, ButtonGroup, ButtonVariant, Theme, Button, ButtonGroup, ButtonVariant, Theme,
}; };
use chrono::{Month, Months}; use chrono::{Month, Months};
@ -13,7 +13,7 @@ pub use theme::CalendarTheme;
#[component] #[component]
pub fn Calendar( pub fn Calendar(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] value: Model<Option<NaiveDate>>, #[prop(optional, into)] value: Model<Option<NaiveDate>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("calendar", include_str!("./calendar.css")); mount_style("calendar", include_str!("./calendar.css"));
@ -116,7 +116,7 @@ pub fn Calendar(
}; };
view! { view! {
<div class=class_list!["thaw-calendar", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-calendar", class.map(|c| move || c.get())] style=move || css_vars.get()>
<div class="thaw-calendar__header"> <div class="thaw-calendar__header">
<span class="thaw-calendar__header-title"> <span class="thaw-calendar__header-title">

View file

@ -1,21 +1,19 @@
use crate::{ use crate::{
components::*, components::*,
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
#[derive(Clone)]
#[slot] #[slot]
pub struct CardHeader { pub struct CardHeader {
children: ChildrenFn, children: Children,
} }
#[derive(Clone)]
#[slot] #[slot]
pub struct CardHeaderExtra { pub struct CardHeaderExtra {
children: ChildrenFn, children: Children,
} }
#[slot] #[slot]
@ -27,10 +25,10 @@ pub struct CardFooter {
#[component] #[component]
pub fn Card( pub fn Card(
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] card_header: Option<CardHeader>, #[prop(optional)] card_header: Option<CardHeader>,
#[prop(optional)] card_header_extra: Option<CardHeaderExtra>, #[prop(optional)] card_header_extra: Option<CardHeaderExtra>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
#[prop(optional)] card_footer: Option<CardFooter>, #[prop(optional)] card_footer: Option<CardFooter>,
) -> impl IntoView { ) -> impl IntoView {
@ -51,29 +49,32 @@ pub fn Card(
css_vars css_vars
}); });
let title = store_value(title);
let is_header = card_header.is_some();
let is_header = MaybeSignal::derive(move || is_header || !title.get_value().get().is_empty());
let header = store_value(card_header);
let header_extra = store_value(card_header_extra);
view! { view! {
<div class=class_list!["thaw-card", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-card", class.map(|c| move || c.get())] style=move || css_vars.get()>
<If cond=is_header> {
<Then slot> if card_header.is_some() || title.is_some() {
<div class="thaw-card__header"> view! {
<div class="thaw-card__header-content"> <div class="thaw-card__header">
<OptionComp value=header.get_value() let:header> <div class="thaw-card__header-content">
<Fallback slot>{move || title.get_value().get()}</Fallback> {
{(header.children)()} if let Some(header) = card_header {
(header.children)().into_view()
} else if let Some(title) = title.into_option() {
(move || title.get()).into_view()
} else {
unreachable!()
}
}
</div>
<OptionComp value=card_header_extra let:header_extra>
<div class="thaw-card__header-extra">{(header_extra.children)()}</div>
</OptionComp> </OptionComp>
</div> </div>
<OptionComp value=header_extra.get_value() let:header_extra> }.into()
<div class="thaw-card__header-extra">{(header_extra.children)()}</div> } else {
</OptionComp> None
</div> }
</Then> }
</If>
<div class="thaw-card__content">{children()}</div> <div class="thaw-card__content">{children()}</div>
<OptionComp value=card_footer let:footer> <OptionComp value=card_footer let:footer>
<If cond=footer.if_> <If cond=footer.if_>

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
checkbox::{checkbox_group::use_checkbox_group, Checkbox}, checkbox::{checkbox_group::use_checkbox_group, Checkbox},
utils::OptionalProp,
SignalWatch, SignalWatch,
}; };
use leptos::*; use leptos::*;
@ -7,7 +8,7 @@ use leptos::*;
#[component] #[component]
pub fn CheckboxItem( pub fn CheckboxItem(
#[prop(optional, into)] label: Option<String>, #[prop(optional, into)] label: Option<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(into)] key: String, #[prop(into)] key: String,
) -> impl IntoView { ) -> impl IntoView {
let checkbox_group_value = use_checkbox_group().0; let checkbox_group_value = use_checkbox_group().0;

View file

@ -5,7 +5,7 @@ use crate::{
components::*, components::*,
icon::*, icon::*,
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
pub use checkbox_group::CheckboxGroup; pub use checkbox_group::CheckboxGroup;
@ -15,7 +15,7 @@ use leptos::*;
#[component] #[component]
pub fn Checkbox( pub fn Checkbox(
#[prop(optional, into)] value: Model<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] children: Option<Children>, #[prop(optional)] children: Option<Children>,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -35,8 +35,7 @@ pub fn Checkbox(
view! { view! {
<div <div
class=class_list![ class=class_list![
"thaw-checkbox", ("thaw-checkbox--checked", move || value.get()), move || class "thaw-checkbox", ("thaw-checkbox--checked", move || value.get()), class.map(|c| move || c.get())
.get()
] ]
style=move || css_vars.get() style=move || css_vars.get()

View file

@ -1,14 +1,14 @@
use super::use_collapse; use super::use_collapse;
use crate::{ use crate::{
components::CSSTransition, components::CSSTransition,
utils::{class_list::class_list, StoredMaybeSignal}, utils::{class_list::class_list, OptionalProp, StoredMaybeSignal},
Icon, Icon,
}; };
use leptos::*; use leptos::*;
#[component] #[component]
pub fn CollapseItem( pub fn CollapseItem(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(into)] title: MaybeSignal<String>, #[prop(into)] title: MaybeSignal<String>,
#[prop(into)] key: MaybeSignal<String>, #[prop(into)] key: MaybeSignal<String>,
children: Children, children: Children,
@ -44,7 +44,7 @@ pub fn CollapseItem(
view! { view! {
<div class=class_list![ <div class=class_list![
"thaw-collapse-item", ("thaw-collapse-item--active", move || is_show_content.get()), "thaw-collapse-item", ("thaw-collapse-item--active", move || is_show_content.get()),
move || class.get() class.map(|c| move || c.get())
]> ]>
<div class="thaw-collapse-item__header" on:click=on_click> <div class="thaw-collapse-item__header" on:click=on_click>
<Icon icon=icondata::AiRightOutlined class="thaw-collapse-item-arrow"/> <Icon icon=icondata::AiRightOutlined class="thaw-collapse-item-arrow"/>

View file

@ -6,7 +6,7 @@ pub use theme::CollapseTheme;
use crate::{ use crate::{
use_theme, use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -14,7 +14,7 @@ use std::collections::HashSet;
#[component] #[component]
pub fn Collapse( pub fn Collapse(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] value: Model<HashSet<String>>, #[prop(optional, into)] value: Model<HashSet<String>>,
#[prop(optional)] accordion: bool, #[prop(optional)] accordion: bool,
children: Children, children: Children,
@ -31,7 +31,7 @@ pub fn Collapse(
accordion, accordion,
}> }>
<div <div
class=class_list!["thaw-collapse", move || class.get()] class=class_list!["thaw-collapse", class.map(|c| move || c.get())]
style=move || css_vars.get() style=move || css_vars.get()
> >
{children()} {children()}

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
use_theme, use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
pub use color::*; pub use color::*;
@ -15,7 +15,7 @@ pub use theme::ColorPickerTheme;
#[component] #[component]
pub fn ColorPicker( pub fn ColorPicker(
#[prop(optional, into)] value: Model<RGBA>, #[prop(optional, into)] value: Model<RGBA>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("color-picker", include_str!("./color-picker.css")); mount_style("color-picker", include_str!("./color-picker.css"));
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -100,7 +100,7 @@ pub fn ColorPicker(
view! { view! {
<Binder target_ref=trigger_ref> <Binder target_ref=trigger_ref>
<div <div
class=class_list!["thaw-color-picker-trigger", move || class.get()] class=class_list!["thaw-color-picker-trigger", class.map(|c| move || c.get())]
on:click=show_popover on:click=show_popover
ref=trigger_ref ref=trigger_ref
> >

View file

@ -8,7 +8,7 @@ pub fn OptionComp<T, CF, IV>(
#[prop(optional)] fallback: Option<Fallback>, #[prop(optional)] fallback: Option<Fallback>,
) -> impl IntoView ) -> impl IntoView
where where
CF: Fn(T) -> IV + 'static, CF: FnOnce(T) -> IV + 'static,
IV: IntoView, IV: IntoView,
{ {
if let Some(value) = value { if let Some(value) = value {

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
chrono::NaiveDate, chrono::NaiveDate,
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
utils::{mount_style, now_date, ComponentRef, Model}, utils::{mount_style, now_date, ComponentRef, Model, OptionalProp},
Icon, Input, InputSuffix, SignalWatch, Icon, Input, InputSuffix, SignalWatch,
}; };
use leptos::*; use leptos::*;
@ -14,7 +14,7 @@ pub use theme::DatePickerTheme;
#[component] #[component]
pub fn DatePicker( pub fn DatePicker(
#[prop(optional, into)] value: Model<Option<NaiveDate>>, #[prop(optional, into)] value: Model<Option<NaiveDate>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("date-picker", include_str!("./date-picker.css")); mount_style("date-picker", include_str!("./date-picker.css"));

View file

@ -1,13 +1,12 @@
use crate::utils::{class_list::class_list, mount_style, OptionalProp};
use leptos::*; use leptos::*;
use crate::utils::{class_list::class_list, mount_style};
#[component] #[component]
pub fn Divider(#[prop(optional, into)] class: MaybeSignal<String>) -> impl IntoView { pub fn Divider(#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>) -> impl IntoView {
mount_style("divider", include_str!("./divider.css")); mount_style("divider", include_str!("./divider.css"));
view! { view! {
<div class=class_list!["thaw-divider", move || class.get()]> <div class=class_list!["thaw-divider", class.map(|c| move || c.get())]>
<div class="thaw-divider__line"></div> <div class="thaw-divider__line"></div>
</div> </div>
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
components::Teleport, components::Teleport,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Card, Card,
}; };
use leptos::*; use leptos::*;
@ -8,11 +8,11 @@ use leptos::*;
#[component] #[component]
pub fn Drawer( pub fn Drawer(
#[prop(into)] show: Model<bool>, #[prop(into)] show: Model<bool>,
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] placement: MaybeSignal<DrawerPlacement>, #[prop(optional, into)] placement: MaybeSignal<DrawerPlacement>,
#[prop(default = MaybeSignal::Static("520px".to_string()), into)] width: MaybeSignal<String>, #[prop(default = MaybeSignal::Static("520px".to_string()), into)] width: MaybeSignal<String>,
#[prop(default = MaybeSignal::Static("260px".to_string()), into)] height: MaybeSignal<String>, #[prop(default = MaybeSignal::Static("260px".to_string()), into)] height: MaybeSignal<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("drawer", include_str!("./drawer.css")); mount_style("drawer", include_str!("./drawer.css"));
@ -33,7 +33,7 @@ pub fn Drawer(
<div <div
class=class_list![ class=class_list![
"thaw-drawer", move || format!("thaw-drawer--placement-{}", placement.get() "thaw-drawer", move || format!("thaw-drawer--placement-{}", placement.get()
.as_str()), move || class.get() .as_str()), class.map(|c| move || c.get())
] ]
style=move || css_vars.get() style=move || css_vars.get()
> >

View file

@ -1,12 +1,12 @@
use super::use_grid; use super::use_grid;
use crate::utils::class_list::class_list; use crate::utils::{class_list::class_list, OptionalProp};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn GridItem( pub fn GridItem(
#[prop(default = MaybeSignal::Static(1u16), into)] column: MaybeSignal<u16>, #[prop(default = MaybeSignal::Static(1u16), into)] column: MaybeSignal<u16>,
#[prop(optional, into)] offset: MaybeSignal<u16>, #[prop(optional, into)] offset: MaybeSignal<u16>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let grid = use_grid(); let grid = use_grid();
@ -36,7 +36,7 @@ pub fn GridItem(
}); });
view! { view! {
<div class=class_list!["thaw-grid-item", move || class.get()] style=move || style.get()> <div class=class_list!["thaw-grid-item", class.map(|c| move || c.get())] style=move || style.get()>
{children()} {children()}
</div> </div>
} }

View file

@ -1,6 +1,6 @@
mod grid_item; mod grid_item;
use crate::utils::class_list::class_list; use crate::utils::{class_list::class_list, OptionalProp};
pub use grid_item::*; pub use grid_item::*;
use leptos::*; use leptos::*;
@ -9,7 +9,7 @@ pub fn Grid(
#[prop(default = MaybeSignal::Static(1u16), into)] cols: MaybeSignal<u16>, #[prop(default = MaybeSignal::Static(1u16), into)] cols: MaybeSignal<u16>,
#[prop(optional, into)] x_gap: MaybeSignal<u16>, #[prop(optional, into)] x_gap: MaybeSignal<u16>,
#[prop(optional, into)] y_gap: MaybeSignal<u16>, #[prop(optional, into)] y_gap: MaybeSignal<u16>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let style = create_memo(move |_| { let style = create_memo(move |_| {
@ -24,7 +24,7 @@ pub fn Grid(
view! { view! {
<Provider value=GridInjection::new(x_gap)> <Provider value=GridInjection::new(x_gap)>
<div class=class_list!["thaw-grid", move || class.get()] style=move || style.get()> <div class=class_list!["thaw-grid", class.map(|c| move || c.get())] style=move || style.get()>
{children()} {children()}
</div> </div>
</Provider> </Provider>

View file

@ -1,14 +1,15 @@
use crate::utils::OptionalProp;
use leptos::*; use leptos::*;
#[component] #[component]
pub fn Image( pub fn Image(
#[prop(optional, into)] src: MaybeSignal<String>, #[prop(optional, into)] src: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] alt: MaybeSignal<String>, #[prop(optional, into)] alt: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] width: MaybeSignal<String>, #[prop(optional, into)] width: MaybeSignal<String>,
#[prop(optional, into)] height: MaybeSignal<String>, #[prop(optional, into)] height: MaybeSignal<String>,
#[prop(optional, into)] border_radius: MaybeSignal<String>, #[prop(optional, into)] border_radius: MaybeSignal<String>,
#[prop(optional, into)] object_fit: MaybeSignal<String>, #[prop(optional, into)] object_fit: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
let style = move || { let style = move || {
let mut style = String::new(); let mut style = String::new();
@ -33,11 +34,11 @@ pub fn Image(
view! { view! {
<img <img
class=move || class.get() class=class.map(|c| move || c.get())
src=move || src.get() src=src.map(|s| move || s.get())
alt=move || alt.get() alt=alt.map(|a| move || a.get())
style=style style=style
object_fit=move || object_fit.get() object_fit=object_fit.map(|o| move || o.get())
/> />
} }
} }

View file

@ -6,7 +6,7 @@ pub use theme::InputTheme;
use crate::{ use crate::{
theme::{use_theme, Theme}, theme::{use_theme, Theme},
utils::{class_list::class_list, mount_style, ComponentRef, Model}, utils::{class_list::class_list, mount_style, ComponentRef, Model, OptionalProp},
}; };
use leptos::*; use leptos::*;
@ -45,7 +45,7 @@ pub fn Input(
#[prop(optional, into)] value: Model<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>, #[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] variant: MaybeSignal<InputVariant>, #[prop(optional, into)] variant: MaybeSignal<InputVariant>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>, #[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>, #[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>, #[prop(optional, into)] disabled: MaybeSignal<bool>,
@ -53,7 +53,7 @@ pub fn Input(
#[prop(optional)] input_prefix: Option<InputPrefix>, #[prop(optional)] input_prefix: Option<InputPrefix>,
#[prop(optional)] input_suffix: Option<InputSuffix>, #[prop(optional)] input_suffix: Option<InputSuffix>,
#[prop(optional)] comp_ref: ComponentRef<InputRef>, #[prop(optional)] comp_ref: ComponentRef<InputRef>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -160,7 +160,7 @@ pub fn Input(
class=class_list![ class=class_list![
"thaw-input", ("thaw-input--focus", move || is_focus.get()), "thaw-input", ("thaw-input--focus", move || is_focus.get()),
("thaw-input--disabled", move || disabled.get()), ("thaw-input--invalid", move || ("thaw-input--disabled", move || disabled.get()), ("thaw-input--invalid", move ||
invalid.get()), move || class.get() invalid.get()), class.map(|c| move || c.get())
] ]
style=move || css_vars.get() style=move || css_vars.get()
@ -185,7 +185,7 @@ pub fn Input(
on:blur=on_internal_blur on:blur=on_internal_blur
class="thaw-input__input-el" class="thaw-input__input-el"
disabled=move || disabled.get() disabled=move || disabled.get()
placeholder=move || placeholder.get() placeholder=placeholder.map(|p| move || p.get())
ref=input_ref ref=input_ref
/> />

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
theme::{use_theme, Theme}, theme::{use_theme, Theme},
utils::{class_list::class_list, mount_style, ComponentRef, Model}, utils::{class_list::class_list, mount_style, ComponentRef, Model, OptionalProp},
}; };
use leptos::*; use leptos::*;
@ -8,13 +8,13 @@ use leptos::*;
pub fn TextArea( pub fn TextArea(
#[prop(optional, into)] value: Model<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>, #[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>, #[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>, #[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>, #[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] invalid: MaybeSignal<bool>, #[prop(optional, into)] invalid: MaybeSignal<bool>,
#[prop(optional)] comp_ref: ComponentRef<TextAreaRef>, #[prop(optional)] comp_ref: ComponentRef<TextAreaRef>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -108,7 +108,7 @@ pub fn TextArea(
class=class_list![ class=class_list![
"thaw-textarea", ("thaw-textarea--focus", move || is_focus.get()), "thaw-textarea", ("thaw-textarea--focus", move || is_focus.get()),
("thaw-textarea--disabled", move || disabled.get()), ("thaw-textarea--invalid", move || ("thaw-textarea--disabled", move || disabled.get()), ("thaw-textarea--invalid", move ||
invalid.get()), move || class.get() invalid.get()), class.map(|c| move || c.get())
] ]
style=move || css_vars.get() style=move || css_vars.get()
@ -125,7 +125,7 @@ pub fn TextArea(
on:blur=on_internal_blur on:blur=on_internal_blur
class="thaw-textarea__textarea-el" class="thaw-textarea__textarea-el"
disabled=move || disabled.get() disabled=move || disabled.get()
placeholder=move || placeholder.get() placeholder=placeholder.map(|p| move || p.get())
ref=textarea_ref ref=textarea_ref
/> />
</div> </div>

View file

@ -1,4 +1,4 @@
use crate::utils::{Model, StoredMaybeSignal}; use crate::utils::{Model, OptionalProp, StoredMaybeSignal};
use crate::{Button, ButtonVariant, ComponentRef, Icon, Input, InputRef, InputSuffix}; use crate::{Button, ButtonVariant, ComponentRef, Icon, Input, InputRef, InputSuffix};
use leptos::*; use leptos::*;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -7,11 +7,11 @@ use std::str::FromStr;
#[component] #[component]
pub fn InputNumber<T>( pub fn InputNumber<T>(
#[prop(optional, into)] value: Model<T>, #[prop(optional, into)] value: Model<T>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(into)] step: MaybeSignal<T>, #[prop(into)] step: MaybeSignal<T>,
#[prop(optional, into)] disabled: MaybeSignal<bool>, #[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] invalid: MaybeSignal<bool>, #[prop(optional, into)] invalid: MaybeSignal<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] comp_ref: ComponentRef<InputNumberRef>, #[prop(optional)] comp_ref: ComponentRef<InputNumberRef>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView ) -> impl IntoView

View file

@ -1,14 +1,14 @@
use crate::utils::class_list::class_list; use crate::utils::{class_list::class_list, OptionalProp};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn LayoutHeader( pub fn LayoutHeader(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: MaybeSignal<String>, #[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<div class=class_list!["thaw-layout-header", move || class.get()] style=move || style.get()> <div class=class_list!["thaw-layout-header", class.map(|c| move || c.get())] style=style.map(|s| move || s.get())>
{children()} {children()}
</div> </div>
} }

View file

@ -1,15 +1,15 @@
use crate::utils::{class_list::class_list, mount_style}; use crate::utils::{class_list::class_list, mount_style, OptionalProp};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn LayoutSider( pub fn LayoutSider(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: MaybeSignal<String>, #[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("layout-sider", include_str!("./layout-sider.css")); mount_style("layout-sider", include_str!("./layout-sider.css"));
view! { view! {
<div class=class_list!["thaw-layout-sider", move || class.get()] style=move || style.get()> <div class=class_list!["thaw-layout-sider", class.map(|c| move || c.get())] style=style.map(|s| move || s.get())>
{children()} {children()}
</div> </div>
} }

View file

@ -1,7 +1,7 @@
mod layout_header; mod layout_header;
mod layout_sider; mod layout_sider;
use crate::utils::{class_list::class_list, mount_style}; use crate::utils::{class_list::class_list, mount_style, OptionalProp};
pub use layout_header::*; pub use layout_header::*;
pub use layout_sider::*; pub use layout_sider::*;
use leptos::*; use leptos::*;
@ -24,8 +24,8 @@ impl LayoutPosition {
#[component] #[component]
pub fn Layout( pub fn Layout(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: MaybeSignal<String>, #[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] position: LayoutPosition, #[prop(optional)] position: LayoutPosition,
#[prop(optional, into)] has_sider: MaybeSignal<bool>, #[prop(optional, into)] has_sider: MaybeSignal<bool>,
children: Children, children: Children,
@ -33,15 +33,21 @@ pub fn Layout(
mount_style("layout", include_str!("./layout.css")); mount_style("layout", include_str!("./layout.css"));
let style = create_memo(move |_| { let style = create_memo(move |_| {
let mut style = style.get(); let mut new_style = style.as_ref().map(|s| s.get()).unwrap_or_default();
if has_sider.get() { if has_sider.get() {
style.push_str("display: flex; flex-wrap: nowrap; flex-direction: row; width: 100%;") new_style
.push_str("display: flex; flex-wrap: nowrap; flex-direction: row; width: 100%;");
Some(new_style)
} else if style.is_some() {
Some(new_style)
} else {
None
} }
style
}); });
view! { view! {
<div <div
class=class_list![gen_class(position), move || class.get()] class=class_list![gen_class(position), class.map(|c| move || c.get())]
style=move || style.get() style=move || style.get()
> >
{children()} {children()}

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -8,7 +8,7 @@ use leptos::*;
#[component] #[component]
pub fn MenuGroup( pub fn MenuGroup(
#[prop(into)] label: String, #[prop(into)] label: String,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("menu-group", include_str!("./menu-group.css")); mount_style("menu-group", include_str!("./menu-group.css"));
@ -22,7 +22,7 @@ pub fn MenuGroup(
}); });
view! { view! {
<div class=class_list!["thaw-menu-group", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-menu-group", class.map(|c| move || c.get())] style=move || css_vars.get()>
{label} {label}
</div> </div>
{children()} {children()}

View file

@ -1,7 +1,7 @@
use super::use_menu; use super::use_menu;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -10,7 +10,7 @@ use leptos::*;
pub fn MenuItem( pub fn MenuItem(
#[prop(into)] key: MaybeSignal<String>, #[prop(into)] key: MaybeSignal<String>,
#[prop(into)] label: MaybeSignal<String>, #[prop(into)] label: MaybeSignal<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("menu-item", include_str!("./menu-item.css")); mount_style("menu-item", include_str!("./menu-item.css"));
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -43,7 +43,7 @@ pub fn MenuItem(
<div <div
class=class_list![ class=class_list![
"thaw-menu-item__content", ("thaw-menu-item__content--selected", move || menu.0 "thaw-menu-item__content", ("thaw-menu-item__content--selected", move || menu.0
.get() == key.get()), move || class.get() .get() == key.get()), class.map(|c| move || c.get())
] ]
on:click=on_click on:click=on_click

View file

@ -2,7 +2,7 @@ mod menu_group;
mod menu_item; mod menu_item;
mod theme; mod theme;
use crate::utils::{class_list::class_list, Model}; use crate::utils::{class_list::class_list, Model, OptionalProp};
use leptos::*; use leptos::*;
pub use menu_group::MenuGroup; pub use menu_group::MenuGroup;
pub use menu_item::*; pub use menu_item::*;
@ -11,12 +11,12 @@ pub use theme::MenuTheme;
#[component] #[component]
pub fn Menu( pub fn Menu(
#[prop(optional, into)] value: Model<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
view! { view! {
<Provider value=MenuInjection(value)> <Provider value=MenuInjection(value)>
<div class=class_list!["thaw-menu", move || class.get()]>{children()}</div> <div class=class_list!["thaw-menu", class.map(|c| move || c.get())]>{children()}</div>
</Provider> </Provider>
} }
} }

View file

@ -4,7 +4,7 @@ use crate::{
components::*, components::*,
icon::*, icon::*,
use_theme, use_theme,
utils::{class_list::class_list, mount_style, StoredMaybeSignal}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -13,14 +13,14 @@ pub use theme::NavBarTheme;
#[slot] #[slot]
pub struct NavBarLeft { pub struct NavBarLeft {
#[prop(optional, into)] #[prop(optional, into)]
class: MaybeSignal<String>, class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
} }
#[slot] #[slot]
pub struct NavBarRight { pub struct NavBarRight {
#[prop(optional, into)] #[prop(optional, into)]
class: MaybeSignal<String>, class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
} }
@ -28,13 +28,13 @@ pub struct NavBarRight {
pub fn NavBar( pub fn NavBar(
#[prop(optional, into)] title: MaybeSignal<String>, #[prop(optional, into)] title: MaybeSignal<String>,
#[prop(optional, into)] left_arrow: MaybeSignal<bool>, #[prop(optional, into)] left_arrow: MaybeSignal<bool>,
#[prop(optional, into)] left_text: MaybeSignal<String>, #[prop(optional, into)] left_text: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] nav_bar_left: Option<NavBarLeft>, #[prop(optional)] nav_bar_left: Option<NavBarLeft>,
#[prop(optional, into)] on_click_left: Option<Callback<ev::MouseEvent>>, #[prop(optional, into)] on_click_left: Option<Callback<ev::MouseEvent>>,
#[prop(optional, into)] right_text: MaybeSignal<String>, #[prop(optional, into)] right_text: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] nav_bar_right: Option<NavBarRight>, #[prop(optional)] nav_bar_right: Option<NavBarRight>,
#[prop(optional, into)] on_click_right: Option<Callback<ev::MouseEvent>>, #[prop(optional, into)] on_click_right: Option<Callback<ev::MouseEvent>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("nav-bar", include_str!("./nav-bar.css")); mount_style("nav-bar", include_str!("./nav-bar.css"));
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -46,9 +46,6 @@ pub fn NavBar(
) )
}) })
}); });
let title: StoredMaybeSignal<_> = title.into();
let left_text: StoredMaybeSignal<_> = left_text.into();
let right_text: StoredMaybeSignal<_> = right_text.into();
let on_click_left = move |ev| { let on_click_left = move |ev| {
if let Some(click_left) = on_click_left.as_ref() { if let Some(click_left) = on_click_left.as_ref() {
@ -63,49 +60,55 @@ pub fn NavBar(
}; };
view! { view! {
<div class=class_list!["thaw-nav-bar", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-nav-bar", class.map(|c| move || c.get())] style=move || css_vars.get()>
{ {
if let Some(NavBarLeft { class, children }) = nav_bar_left { if let Some(NavBarLeft { class, children }) = nav_bar_left {
view! { view! {
<div class=class_list!["thaw-nav-bar__left", move || class.get()] on:click=on_click_left> <div class=class_list!["thaw-nav-bar__left", class.map(|c| move || c.get())] on:click=on_click_left>
{children()} {children()}
</div> </div>
}.into_view() }.into_view()
} else { } else if let Some(left_text) = left_text.into_option() {
view! { view! {
<If cond=MaybeSignal::derive(move || left_arrow.get() || !left_text.get().is_empty())> <div class="thaw-nav-bar__left" on:click=on_click_left>
<Then slot> <If cond=left_arrow>
<div class="thaw-nav-bar__left" on:click=on_click_left> <Then slot>
<If cond=left_arrow> <Icon icon=icondata::AiLeftOutlined/>
<Then slot> </Then>
<Icon icon=icondata::AiLeftOutlined/> </If>
</Then> {move || left_text.get()}
</If> </div>
{move || left_text.get()}
</div>
</Then>
</If>
}.into_view() }.into_view()
} else {
(move || {
if left_arrow.get() {
view! {
<div class="thaw-nav-bar__left" on:click=on_click_left>
<Icon icon=icondata::AiLeftOutlined/>
</div>
}.into()
} else {
None
}
}).into_view()
} }
} }
<div class="thaw-nav-bar__center">{move || title.get()}</div> <div class="thaw-nav-bar__center">{move || title.get()}</div>
{ {
if let Some(NavBarRight { class, children }) = nav_bar_right { if let Some(NavBarRight { class, children }) = nav_bar_right {
view! { view! {
<div class=class_list!["thaw-nav-bar__right", move || class.get()] on:click=on_click_right> <div class=class_list!["thaw-nav-bar__right", class.map(|c| move || c.get())] on:click=on_click_right>
{children()} {children()}
</div> </div>
}.into_view() }.into()
} else { } else if let Some(right_text) = right_text.into_option() {
view! { view! {
<If cond=MaybeSignal::derive(move || !right_text.get().is_empty())> <div class="thaw-nav-bar__right" on:click=on_click_right>
<Then slot> {move || right_text.get()}
<div class="thaw-nav-bar__right" on:click=on_click_right> </div>
{move || right_text.get()} }.into()
</div> } else {
</Then> None
</If>
}.into_view()
} }
} }
</div> </div>

View file

@ -11,7 +11,7 @@ pub fn show_toast(options: ToastOptions) {
mount_style("toast", include_str!("./toast.css")); mount_style("toast", include_str!("./toast.css"));
cfg_if! { if #[cfg(target_arch = "wasm32")] { cfg_if! { if #[cfg(target_arch = "wasm32")] {
use leptos::{leptos_dom::Mountable, *}; use leptos::{leptos_dom::Mountable, *};
let mount = document().body().expect("body element not to exist"); let mount = document().body().expect("body element to exist");
let children = view! { <div class="thaw-toast">{options.message}</div> }; let children = view! { <div class="thaw-toast">{options.message}</div> };
let node = children.into_view(); let node = children.into_view();
let node = node.get_mountable_node(); let node = node.get_mountable_node();

View file

@ -2,7 +2,7 @@ use crate::icon::*;
use crate::utils::Model; use crate::utils::Model;
use crate::{ use crate::{
components::{OptionComp, Teleport}, components::{OptionComp, Teleport},
utils::{mount_style, StoredMaybeSignal}, utils::mount_style,
Card, CardFooter, CardHeader, CardHeaderExtra, Card, CardFooter, CardHeader, CardHeaderExtra,
}; };
use leptos::*; use leptos::*;
@ -20,7 +20,6 @@ pub fn Modal(
#[prop(optional)] modal_footer: Option<ModalFooter>, #[prop(optional)] modal_footer: Option<ModalFooter>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("modal", include_str!("./modal.css")); mount_style("modal", include_str!("./modal.css"));
let title: StoredMaybeSignal<_> = title.into();
view! { view! {
<Teleport> <Teleport>

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
use_theme, use_theme,
utils::{add_event_listener, class_list::class_list, mount_style}, utils::{add_event_listener, class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::{leptos_dom::helpers::TimeoutHandle, *}; use leptos::{leptos_dom::helpers::TimeoutHandle, *};
@ -13,13 +13,13 @@ pub use theme::PopoverTheme;
#[slot] #[slot]
pub struct PopoverTrigger { pub struct PopoverTrigger {
#[prop(optional, into)] #[prop(optional, into)]
class: MaybeSignal<String>, class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
} }
#[component] #[component]
pub fn Popover( pub fn Popover(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] trigger_type: PopoverTriggerType, #[prop(optional)] trigger_type: PopoverTriggerType,
popover_trigger: PopoverTrigger, popover_trigger: PopoverTrigger,
#[prop(optional)] placement: PopoverPlacement, #[prop(optional)] placement: PopoverPlacement,
@ -115,7 +115,7 @@ pub fn Popover(
view! { view! {
<Binder target_ref> <Binder target_ref>
<div <div
class=class_list!["thaw-popover-trigger", move || trigger_class.get()] class=class_list!["thaw-popover-trigger", trigger_class.map(|c| move || c.get())]
ref=target_ref ref=target_ref
on:mouseenter=on_mouse_enter on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave on:mouseleave=on_mouse_leave
@ -130,7 +130,7 @@ pub fn Popover(
on:mouseenter=on_mouse_enter on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave on:mouseleave=on_mouse_leave
> >
<div class=move || class.get()>{children()}</div> <div class=class.map(|c| move || c.get())>{children()}</div>
<div class="thaw-popover__angle-container"> <div class="thaw-popover__angle-container">
<div class="thaw-popover__angle"></div> <div class="thaw-popover__angle"></div>
</div> </div>

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -8,7 +8,7 @@ use leptos::*;
#[component] #[component]
pub fn Radio( pub fn Radio(
#[prop(optional, into)] value: Model<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -27,7 +27,7 @@ pub fn Radio(
view! { view! {
<div <div
class=class_list![ class=class_list![
"thaw-radio", ("thaw-radio--checked", move || value.get()), move || class.get() "thaw-radio", ("thaw-radio--checked", move || value.get()), class.map(|c| move || c.get())
] ]
style=move || css_vars.get() style=move || css_vars.get()

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{ use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth}, components::{Binder, Follower, FollowerPlacement, FollowerWidth},
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -20,7 +20,7 @@ pub struct SelectOption<T> {
pub fn Select<T>( pub fn Select<T>(
#[prop(optional, into)] value: Model<Option<T>>, #[prop(optional, into)] value: Model<Option<T>>,
#[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>, #[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView ) -> impl IntoView
where where
T: Eq + Hash + Clone + 'static, T: Eq + Hash + Clone + 'static,
@ -111,7 +111,7 @@ where
view! { view! {
<Binder target_ref=trigger_ref> <Binder target_ref=trigger_ref>
<div <div
class=class_list!["thaw-select", move || class.get()] class=class_list!["thaw-select", class.map(|c| move || c.get())]
ref=trigger_ref ref=trigger_ref
on:click=show_menu on:click=show_menu
style=move || css_vars.get() style=move || css_vars.get()

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{ use crate::{
components::OptionComp, components::OptionComp,
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -18,7 +18,7 @@ pub fn Slider(
#[prop(optional, into)] value: Model<f64>, #[prop(optional, into)] value: Model<f64>,
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>, #[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
#[prop(optional, into)] step: MaybeSignal<f64>, #[prop(optional, into)] step: MaybeSignal<f64>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] children: Option<Children>, #[prop(optional)] children: Option<Children>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("slider", include_str!("./slider.css")); mount_style("slider", include_str!("./slider.css"));
@ -118,7 +118,7 @@ pub fn Slider(
view! { view! {
<div <div
class=class_list!["thaw-slider", move || class.get()] class=class_list!["thaw-slider", class.map(|c| move || c.get())]
style=move || css_vars.get() style=move || css_vars.get()
on:click=on_mouse_click on:click=on_mouse_click
> >

View file

@ -1,4 +1,4 @@
use crate::utils::{class_list::class_list, mount_style}; use crate::utils::{class_list::class_list, mount_style, OptionalProp};
use leptos::*; use leptos::*;
#[derive(Default)] #[derive(Default)]
@ -16,7 +16,7 @@ pub enum SpaceGap {
pub fn Space( pub fn Space(
#[prop(optional)] gap: SpaceGap, #[prop(optional)] gap: SpaceGap,
#[prop(optional)] vertical: bool, #[prop(optional)] vertical: bool,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("space", include_str!("./space.css")); mount_style("space", include_str!("./space.css"));
@ -30,7 +30,7 @@ pub fn Space(
view! { view! {
<div <div
class=class_list!["thaw-space", move || class.get()] class=class_list!["thaw-space", class.map(|c| move || c.get())]
style:gap=gap style:gap=gap
style:flex-direction=if vertical { "column" } else { "row" } style:flex-direction=if vertical { "column" } else { "row" }
> >

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -30,7 +30,7 @@ impl SpinnerSize {
#[component] #[component]
pub fn Spinner( pub fn Spinner(
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] size: MaybeSignal<SpinnerSize>, #[prop(optional, into)] size: MaybeSignal<SpinnerSize>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("spinner", include_str!("./spinner.css")); mount_style("spinner", include_str!("./spinner.css"));
@ -53,7 +53,7 @@ pub fn Spinner(
view! { view! {
<div <div
class=class_list!["thaw-spinner", move || class.get()] class=class_list!["thaw-spinner", class.map(|c| move || c.get())]
style=move || css_vars.get() style=move || css_vars.get()
></div> ></div>
} }

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -11,7 +11,7 @@ pub use theme::SwitchTheme;
#[component] #[component]
pub fn Switch( pub fn Switch(
#[prop(optional, into)] value: Model<bool>, #[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("switch", include_str!("./switch.css")); mount_style("switch", include_str!("./switch.css"));
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -33,7 +33,7 @@ pub fn Switch(
view! { view! {
<div <div
class=class_list![ class=class_list![
"thaw-switch", ("thaw-switch--active", move || value.get()), move || class.get() "thaw-switch", ("thaw-switch--active", move || value.get()), class.map(|c| move || c.get())
] ]
style=move || css_vars.get() style=move || css_vars.get()

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -12,7 +12,7 @@ pub use theme::TableTheme;
pub fn Table( pub fn Table(
#[prop(optional, into)] style: MaybeSignal<String>, #[prop(optional, into)] style: MaybeSignal<String>,
#[prop(default=true.into(), into)] single_row: MaybeSignal<bool>, #[prop(default=true.into(), into)] single_row: MaybeSignal<bool>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] single_column: MaybeSignal<bool>, #[prop(optional, into)] single_column: MaybeSignal<bool>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
@ -46,7 +46,7 @@ pub fn Table(
<table <table
class=class_list![ class=class_list![
"thaw-table", ("thaw-table--single-row", move || single_row.get()), "thaw-table", ("thaw-table--single-row", move || single_row.get()),
("thaw-table--single-column", move || single_column.get()), move || class.get() ("thaw-table--single-column", move || single_column.get()), class.map(|c| move || c.get())
] ]
style=move || format!("{}{}", css_vars.get(), style.get()) style=move || format!("{}{}", css_vars.get(), style.get())

View file

@ -2,7 +2,7 @@ mod tab;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style, Model}, utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -12,7 +12,7 @@ pub use tab::*;
#[component] #[component]
pub fn Tabs( pub fn Tabs(
#[prop(optional, into)] value: Model<String>, #[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css")); mount_style("tabs", include_str!("./tabs.css"));
@ -32,7 +32,7 @@ pub fn Tabs(
fn TabsInner( fn TabsInner(
value: Model<String>, value: Model<String>,
tab_options_vec: RwSignal<Vec<TabOption>>, tab_options_vec: RwSignal<Vec<TabOption>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css")); mount_style("tabs", include_str!("./tabs.css"));
@ -61,7 +61,7 @@ fn TabsInner(
let children = children(); let children = children();
view! { view! {
<div class=class_list!["thaw-tabs", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-tabs", class.map(|c| move || c.get())] style=move || css_vars.get()>
<div class="thaw-tabs__label-list" ref=label_list_ref role="tablist"> <div class="thaw-tabs__label-list" ref=label_list_ref role="tablist">
<For <For
each=move || tab_options_vec.get() each=move || tab_options_vec.get()
@ -103,7 +103,7 @@ fn TabsInner(
<span <span
class=class_list![ class=class_list![
"thaw-tabs__label", ("thaw-tabs__label--active", move || "thaw-tabs__label", ("thaw-tabs__label--active", move ||
is_active.get()), move || class.get() is_active.get()), class.map(|c| move || c.get())
] ]
on:click={ on:click={

View file

@ -1,5 +1,5 @@
use super::use_tabs; use super::use_tabs;
use crate::utils::{class_list::class_list, mount_style}; use crate::utils::{class_list::class_list, mount_style, OptionalProp};
use leptos::*; use leptos::*;
#[derive(Clone)] #[derive(Clone)]
@ -11,7 +11,7 @@ pub(crate) struct TabOption {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct TabLabelView { pub(crate) struct TabLabelView {
pub class: MaybeSignal<String>, pub class: OptionalProp<MaybeSignal<String>>,
pub children: Fragment, pub children: Fragment,
} }
@ -28,7 +28,7 @@ impl From<TabLabel> for TabLabelView {
#[slot] #[slot]
pub struct TabLabel { pub struct TabLabel {
#[prop(optional, into)] #[prop(optional, into)]
class: MaybeSignal<String>, class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
} }
@ -37,7 +37,7 @@ pub fn Tab(
#[prop(into)] key: String, #[prop(into)] key: String,
#[prop(optional, into)] label: String, #[prop(optional, into)] label: String,
#[prop(optional)] tab_label: Option<TabLabel>, #[prop(optional)] tab_label: Option<TabLabel>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tab", include_str!("./tab.css")); mount_style("tab", include_str!("./tab.css"));
@ -61,7 +61,7 @@ pub fn Tab(
view! { view! {
<div <div
class=class_list![ class=class_list![
"thaw-tab", ("thaw-tab--hidden", move || ! is_active.get()), move || class.get() "thaw-tab", ("thaw-tab--hidden", move || ! is_active.get()), class.map(|c| move || c.get())
] ]
role="tabpanel" role="tabpanel"

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::use_theme, theme::use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -47,7 +47,7 @@ impl TagVariant {
#[component] #[component]
pub fn Tag( pub fn Tag(
#[prop(optional, into)] variant: MaybeSignal<TagVariant>, #[prop(optional, into)] variant: MaybeSignal<TagVariant>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tag", include_str!("./tag.css")); mount_style("tag", include_str!("./tag.css"));
@ -73,7 +73,7 @@ pub fn Tag(
}); });
view! { view! {
<div class=class_list!["thaw-tag", move || class.get()] style=move || css_vars.get()> <div class=class_list!["thaw-tag", class.map(|c| move || c.get())] style=move || css_vars.get()>
<span class="thaw-tag__content">{children()}</span> <span class="thaw-tag__content">{children()}</span>
</div> </div>
} }

View file

@ -4,7 +4,7 @@ use crate::{
chrono::{Local, NaiveTime, Timelike}, chrono::{Local, NaiveTime, Timelike},
components::{Binder, Follower, FollowerPlacement}, components::{Binder, Follower, FollowerPlacement},
use_theme, use_theme,
utils::{mount_style, ComponentRef, Model}, utils::{mount_style, ComponentRef, Model, OptionalProp},
Button, ButtonSize, ButtonVariant, Icon, Input, InputSuffix, SignalWatch, Theme, Button, ButtonSize, ButtonVariant, Icon, Input, InputSuffix, SignalWatch, Theme,
}; };
use leptos::*; use leptos::*;
@ -13,7 +13,7 @@ pub use theme::TimePickerTheme;
#[component] #[component]
pub fn TimePicker( pub fn TimePicker(
#[prop(optional, into)] value: Model<Option<NaiveTime>>, #[prop(optional, into)] value: Model<Option<NaiveTime>>,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("time-picker", include_str!("./time-picker.css")); mount_style("time-picker", include_str!("./time-picker.css"));

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
use_theme, use_theme,
utils::{class_list::class_list, mount_style}, utils::{class_list::class_list, mount_style, OptionalProp},
Theme, Theme,
}; };
use leptos::*; use leptos::*;
@ -8,7 +8,7 @@ use leptos::*;
#[component] #[component]
pub fn Text( pub fn Text(
#[prop(optional)] code: bool, #[prop(optional)] code: bool,
#[prop(optional, into)] class: MaybeSignal<String>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("text", include_str!("./text.css")); mount_style("text", include_str!("./text.css"));
@ -27,7 +27,7 @@ pub fn Text(
if code { if code {
view! { view! {
<code <code
class=class_list!["thaw-text thaw-text--code", move || class.get()] class=class_list!["thaw-text thaw-text--code", class.map(|c| move || c.get())]
style=move || css_vars.get() style=move || css_vars.get()
> >
{children()} {children()}
@ -35,7 +35,7 @@ pub fn Text(
} }
.into_any() .into_any()
} else { } else {
view! { <span class=class_list!["thaw-text", move || class.get()]>{children()}</span> } view! { <span class=class_list!["thaw-text", class.map(|c| move || c.get())]>{children()}</span> }
.into_any() .into_any()
} }
} }

View file

@ -13,6 +13,7 @@ impl ClassList {
pub fn add(self, value: impl IntoClass) -> Self { pub fn add(self, value: impl IntoClass) -> Self {
let class = value.into_class(); let class = value.into_class();
match class { match class {
Class::None => (),
Class::String(name) => { Class::String(name) => {
self.0.update(move |set| { self.0.update(move |set| {
set.insert(name); set.insert(name);
@ -107,6 +108,7 @@ impl IntoAttribute for ClassList {
} }
pub enum Class { pub enum Class {
None,
String(Oco<'static, str>), String(Oco<'static, str>),
FnString(Box<dyn Fn() -> Oco<'static, str>>), FnString(Box<dyn Fn() -> Oco<'static, str>>),
Fn(Oco<'static, str>, Box<dyn Fn() -> bool>), Fn(Oco<'static, str>, Box<dyn Fn() -> bool>),
@ -138,6 +140,20 @@ where
} }
} }
impl<T, U> IntoClass for Option<T>
where
T: Fn() -> U + 'static,
U: ToString,
{
fn into_class(self) -> Class {
if let Some(f) = self {
Class::FnString(Box::new(move || f().to_string().into()))
} else {
Class::None
}
}
}
impl<T> IntoClass for (&'static str, T) impl<T> IntoClass for (&'static str, T)
where where
T: Fn() -> bool + 'static, T: Fn() -> bool + 'static,

View file

@ -4,6 +4,7 @@ mod component_ref;
mod event_listener; mod event_listener;
mod model; mod model;
mod mount_style; mod mount_style;
mod optional_prop;
mod signal; mod signal;
mod stored_maybe_signal; mod stored_maybe_signal;
mod time; mod time;
@ -13,6 +14,7 @@ pub use component_ref::{create_component_ref, ComponentRef};
pub(crate) use event_listener::*; pub(crate) use event_listener::*;
pub(crate) use model::Model; pub(crate) use model::Model;
pub(crate) use mount_style::mount_style; pub(crate) use mount_style::mount_style;
pub(crate) use optional_prop::OptionalProp;
pub use signal::SignalWatch; pub use signal::SignalWatch;
pub(crate) use stored_maybe_signal::*; pub(crate) use stored_maybe_signal::*;
pub(crate) use time::*; pub(crate) use time::*;

View file

@ -0,0 +1,113 @@
use leptos::{MaybeSignal, Memo, ReadSignal, RwSignal, Signal};
use std::ops::{Deref, DerefMut};
pub struct OptionalProp<T>(Option<T>);
impl<T> Default for OptionalProp<T> {
fn default() -> Self {
Self(None)
}
}
impl<T: Clone> Clone for OptionalProp<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> OptionalProp<T> {
pub fn map<U, F>(self, f: F) -> Option<U>
where
F: FnOnce(T) -> U,
{
match self.0 {
Some(x) => Some(f(x)),
None => None,
}
}
pub fn into_option(self) -> Option<T> {
self.0
}
}
impl<T> Deref for OptionalProp<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for OptionalProp<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<T> for OptionalProp<T> {
fn from(value: T) -> Self {
Self(Some(value))
}
}
impl From<&str> for OptionalProp<String> {
fn from(value: &str) -> Self {
Self(Some(value.to_string()))
}
}
impl From<&str> for OptionalProp<MaybeSignal<String>> {
fn from(value: &str) -> Self {
Self(Some(MaybeSignal::from(value.to_string())))
}
}
impl From<String> for OptionalProp<MaybeSignal<String>> {
fn from(value: String) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T> From<ReadSignal<T>> for OptionalProp<MaybeSignal<T>> {
fn from(value: ReadSignal<T>) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T> From<RwSignal<T>> for OptionalProp<MaybeSignal<T>> {
fn from(value: RwSignal<T>) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T> From<Memo<T>> for OptionalProp<MaybeSignal<T>> {
fn from(value: Memo<T>) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T> From<Signal<T>> for OptionalProp<MaybeSignal<T>> {
fn from(value: Signal<T>) -> Self {
Self(Some(MaybeSignal::from(value)))
}
}
impl<T> From<Option<T>> for OptionalProp<T> {
fn from(value: Option<T>) -> Self {
Self(value)
}
}
#[cfg(test)]
mod test {
use super::OptionalProp;
use leptos::MaybeSignal;
#[test]
fn from() {
let _prop: OptionalProp<MaybeSignal<String>> = "prop".into();
let _prop: OptionalProp<MaybeSignal<String>> = "prop".to_string().into();
let _prop: OptionalProp<String> = "prop".into();
}
}