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

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
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,
};
use leptos::*;
@ -28,13 +28,13 @@ pub struct AutoCompleteSuffix {
#[component]
pub fn AutoComplete(
#[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)] clear_after_select: MaybeSignal<bool>,
#[prop(optional, into)] on_select: Option<Callback<String>>,
#[prop(optional, into)] disabled: 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_suffix: Option<AutoCompleteSuffix>,
#[prop(optional)] comp_ref: ComponentRef<AutoCompleteRef>,
@ -130,7 +130,7 @@ pub fn AutoComplete(
view! {
<Binder target_ref=auto_complete_ref>
<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
on:keydown=on_keydown
>

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@ mod theme;
use crate::{
use_theme,
utils::{class_list::class_list, mount_style},
utils::{class_list::class_list, mount_style, OptionalProp},
Theme,
};
pub use breadcrumb_item::BreadcrumbItem;
@ -13,7 +13,7 @@ pub use theme::BreadcrumbTheme;
#[component]
pub fn Breadcrumb(
#[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,
) -> impl IntoView {
mount_style("breadcrumb", include_str!("./breadcrumb.css"));
@ -40,7 +40,7 @@ pub fn Breadcrumb(
view! {
<Provider value=BreadcrumbSeparatorInjection(separator)>
<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()
>
<ul>{children()}</ul>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@ pub use theme::CollapseTheme;
use crate::{
use_theme,
utils::{class_list::class_list, mount_style, Model},
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme,
};
use leptos::*;
@ -14,7 +14,7 @@ use std::collections::HashSet;
#[component]
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)] accordion: bool,
children: Children,
@ -31,7 +31,7 @@ pub fn Collapse(
accordion,
}>
<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()
>
{children()}

View file

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

View file

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

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{
chrono::NaiveDate,
components::{Binder, Follower, FollowerPlacement},
utils::{mount_style, now_date, ComponentRef, Model},
utils::{mount_style, now_date, ComponentRef, Model, OptionalProp},
Icon, Input, InputSuffix, SignalWatch,
};
use leptos::*;
@ -14,7 +14,7 @@ pub use theme::DatePickerTheme;
#[component]
pub fn DatePicker(
#[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)>,
) -> impl IntoView {
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 crate::utils::{class_list::class_list, mount_style};
#[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"));
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>
}

View file

@ -1,6 +1,6 @@
use crate::{
components::Teleport,
utils::{class_list::class_list, mount_style, Model},
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Card,
};
use leptos::*;
@ -8,11 +8,11 @@ use leptos::*;
#[component]
pub fn Drawer(
#[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(default = MaybeSignal::Static("520px".to_string()), into)] width: 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,
) -> impl IntoView {
mount_style("drawer", include_str!("./drawer.css"));
@ -33,7 +33,7 @@ pub fn Drawer(
<div
class=class_list![
"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()
>

View file

@ -1,12 +1,12 @@
use super::use_grid;
use crate::utils::class_list::class_list;
use crate::utils::{class_list::class_list, OptionalProp};
use leptos::*;
#[component]
pub fn GridItem(
#[prop(default = MaybeSignal::Static(1u16), into)] column: MaybeSignal<u16>,
#[prop(optional, into)] offset: MaybeSignal<u16>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
let grid = use_grid();
@ -36,7 +36,7 @@ pub fn GridItem(
});
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()}
</div>
}

View file

@ -1,6 +1,6 @@
mod grid_item;
use crate::utils::class_list::class_list;
use crate::utils::{class_list::class_list, OptionalProp};
pub use grid_item::*;
use leptos::*;
@ -9,7 +9,7 @@ pub fn Grid(
#[prop(default = MaybeSignal::Static(1u16), into)] cols: MaybeSignal<u16>,
#[prop(optional, into)] x_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,
) -> impl IntoView {
let style = create_memo(move |_| {
@ -24,7 +24,7 @@ pub fn Grid(
view! {
<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()}
</div>
</Provider>

View file

@ -1,14 +1,15 @@
use crate::utils::OptionalProp;
use leptos::*;
#[component]
pub fn Image(
#[prop(optional, into)] src: MaybeSignal<String>,
#[prop(optional, into)] alt: MaybeSignal<String>,
#[prop(optional, into)] src: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] alt: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] width: MaybeSignal<String>,
#[prop(optional, into)] height: MaybeSignal<String>,
#[prop(optional, into)] border_radius: MaybeSignal<String>,
#[prop(optional, into)] object_fit: MaybeSignal<String>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] object_fit: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView {
let style = move || {
let mut style = String::new();
@ -33,11 +34,11 @@ pub fn Image(
view! {
<img
class=move || class.get()
src=move || src.get()
alt=move || alt.get()
class=class.map(|c| move || c.get())
src=src.map(|s| move || s.get())
alt=alt.map(|a| move || a.get())
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::{
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::*;
@ -45,7 +45,7 @@ pub fn Input(
#[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[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_blur: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
@ -53,7 +53,7 @@ pub fn Input(
#[prop(optional)] input_prefix: Option<InputPrefix>,
#[prop(optional)] input_suffix: Option<InputSuffix>,
#[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)>,
) -> impl IntoView {
let theme = use_theme(Theme::light);
@ -160,7 +160,7 @@ pub fn Input(
class=class_list![
"thaw-input", ("thaw-input--focus", move || is_focus.get()),
("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()
@ -185,7 +185,7 @@ pub fn Input(
on:blur=on_internal_blur
class="thaw-input__input-el"
disabled=move || disabled.get()
placeholder=move || placeholder.get()
placeholder=placeholder.map(|p| move || p.get())
ref=input_ref
/>

View file

@ -1,6 +1,6 @@
use crate::{
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::*;
@ -8,13 +8,13 @@ use leptos::*;
pub fn TextArea(
#[prop(optional, into)] value: Model<String>,
#[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_blur: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] invalid: MaybeSignal<bool>,
#[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)>,
) -> impl IntoView {
let theme = use_theme(Theme::light);
@ -108,7 +108,7 @@ pub fn TextArea(
class=class_list![
"thaw-textarea", ("thaw-textarea--focus", move || is_focus.get()),
("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()
@ -125,7 +125,7 @@ pub fn TextArea(
on:blur=on_internal_blur
class="thaw-textarea__textarea-el"
disabled=move || disabled.get()
placeholder=move || placeholder.get()
placeholder=placeholder.map(|p| move || p.get())
ref=textarea_ref
/>
</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 leptos::*;
use std::ops::{Add, Sub};
@ -7,11 +7,11 @@ use std::str::FromStr;
#[component]
pub fn InputNumber<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(optional, into)] disabled: 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(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> 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::*;
#[component]
pub fn LayoutHeader(
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] style: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
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()}
</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::*;
#[component]
pub fn LayoutSider(
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] style: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
mount_style("layout-sider", include_str!("./layout-sider.css"));
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()}
</div>
}

View file

@ -1,7 +1,7 @@
mod layout_header;
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_sider::*;
use leptos::*;
@ -24,8 +24,8 @@ impl LayoutPosition {
#[component]
pub fn Layout(
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] style: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] style: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] position: LayoutPosition,
#[prop(optional, into)] has_sider: MaybeSignal<bool>,
children: Children,
@ -33,15 +33,21 @@ pub fn Layout(
mount_style("layout", include_str!("./layout.css"));
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() {
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! {
<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()
>
{children()}

View file

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

View file

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

View file

@ -2,7 +2,7 @@ mod menu_group;
mod menu_item;
mod theme;
use crate::utils::{class_list::class_list, Model};
use crate::utils::{class_list::class_list, Model, OptionalProp};
use leptos::*;
pub use menu_group::MenuGroup;
pub use menu_item::*;
@ -11,12 +11,12 @@ pub use theme::MenuTheme;
#[component]
pub fn Menu(
#[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
view! {
<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>
}
}

View file

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

View file

@ -11,7 +11,7 @@ pub fn show_toast(options: ToastOptions) {
mount_style("toast", include_str!("./toast.css"));
cfg_if! { if #[cfg(target_arch = "wasm32")] {
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 node = children.into_view();
let node = node.get_mountable_node();

View file

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

View file

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

View file

@ -1,6 +1,6 @@
use crate::{
theme::use_theme,
utils::{class_list::class_list, mount_style, Model},
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme,
};
use leptos::*;
@ -8,7 +8,7 @@ use leptos::*;
#[component]
pub fn Radio(
#[prop(optional, into)] value: Model<bool>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
let theme = use_theme(Theme::light);
@ -27,7 +27,7 @@ pub fn Radio(
view! {
<div
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()

View file

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

View file

@ -4,7 +4,7 @@ mod theme;
use crate::{
components::OptionComp,
theme::use_theme,
utils::{class_list::class_list, mount_style, Model},
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme,
};
use leptos::*;
@ -18,7 +18,7 @@ pub fn Slider(
#[prop(optional, into)] value: Model<f64>,
#[prop(default = MaybeSignal::Static(100f64), into)] max: 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>,
) -> impl IntoView {
mount_style("slider", include_str!("./slider.css"));
@ -118,7 +118,7 @@ pub fn Slider(
view! {
<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()
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::*;
#[derive(Default)]
@ -16,7 +16,7 @@ pub enum SpaceGap {
pub fn Space(
#[prop(optional)] gap: SpaceGap,
#[prop(optional)] vertical: bool,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
mount_style("space", include_str!("./space.css"));
@ -30,7 +30,7 @@ pub fn Space(
view! {
<div
class=class_list!["thaw-space", move || class.get()]
class=class_list!["thaw-space", class.map(|c| move || c.get())]
style:gap=gap
style:flex-direction=if vertical { "column" } else { "row" }
>

View file

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

View file

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

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{
theme::use_theme,
utils::{class_list::class_list, mount_style},
utils::{class_list::class_list, mount_style, OptionalProp},
Theme,
};
use leptos::*;
@ -12,7 +12,7 @@ pub use theme::TableTheme;
pub fn Table(
#[prop(optional, into)] style: MaybeSignal<String>,
#[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>,
children: Children,
) -> impl IntoView {
@ -46,7 +46,7 @@ pub fn Table(
<table
class=class_list![
"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())

View file

@ -2,7 +2,7 @@ mod tab;
use crate::{
theme::use_theme,
utils::{class_list::class_list, mount_style, Model},
utils::{class_list::class_list, mount_style, Model, OptionalProp},
Theme,
};
use leptos::*;
@ -12,7 +12,7 @@ pub use tab::*;
#[component]
pub fn Tabs(
#[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css"));
@ -32,7 +32,7 @@ pub fn Tabs(
fn TabsInner(
value: Model<String>,
tab_options_vec: RwSignal<Vec<TabOption>>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css"));
@ -61,7 +61,7 @@ fn TabsInner(
let children = children();
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">
<For
each=move || tab_options_vec.get()
@ -103,7 +103,7 @@ fn TabsInner(
<span
class=class_list![
"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={

View file

@ -1,5 +1,5 @@
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::*;
#[derive(Clone)]
@ -11,7 +11,7 @@ pub(crate) struct TabOption {
#[derive(Clone)]
pub(crate) struct TabLabelView {
pub class: MaybeSignal<String>,
pub class: OptionalProp<MaybeSignal<String>>,
pub children: Fragment,
}
@ -28,7 +28,7 @@ impl From<TabLabel> for TabLabelView {
#[slot]
pub struct TabLabel {
#[prop(optional, into)]
class: MaybeSignal<String>,
class: OptionalProp<MaybeSignal<String>>,
children: Children,
}
@ -37,7 +37,7 @@ pub fn Tab(
#[prop(into)] key: String,
#[prop(optional, into)] label: String,
#[prop(optional)] tab_label: Option<TabLabel>,
#[prop(optional, into)] class: MaybeSignal<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
children: Children,
) -> impl IntoView {
mount_style("tab", include_str!("./tab.css"));
@ -61,7 +61,7 @@ pub fn Tab(
view! {
<div
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"

View file

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

View file

@ -4,7 +4,7 @@ use crate::{
chrono::{Local, NaiveTime, Timelike},
components::{Binder, Follower, FollowerPlacement},
use_theme,
utils::{mount_style, ComponentRef, Model},
utils::{mount_style, ComponentRef, Model, OptionalProp},
Button, ButtonSize, ButtonVariant, Icon, Input, InputSuffix, SignalWatch, Theme,
};
use leptos::*;
@ -13,7 +13,7 @@ pub use theme::TimePickerTheme;
#[component]
pub fn TimePicker(
#[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)>,
) -> impl IntoView {
mount_style("time-picker", include_str!("./time-picker.css"));

View file

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

View file

@ -13,6 +13,7 @@ impl ClassList {
pub fn add(self, value: impl IntoClass) -> Self {
let class = value.into_class();
match class {
Class::None => (),
Class::String(name) => {
self.0.update(move |set| {
set.insert(name);
@ -107,6 +108,7 @@ impl IntoAttribute for ClassList {
}
pub enum Class {
None,
String(Oco<'static, str>),
FnString(Box<dyn Fn() -> Oco<'static, str>>),
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)
where
T: Fn() -> bool + 'static,

View file

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