Pref/class (#58)

* feat: add class_list macro

* fix: ssr mode class_list

* pref: replace with class_list
This commit is contained in:
luoxiaozero 2023-12-23 12:25:54 +08:00 committed by GitHub
parent 0b69e94181
commit 5ab09bb281
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 316 additions and 304 deletions

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Icon, Theme,
};
use icondata::AiIcon;
@ -86,14 +84,8 @@ pub fn Alert(
.into()
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class:thaw-alert=true
style=move || css_vars.get()
>
<div class=class_list!["thaw-alert", move || class.get()] style=move || css_vars.get()>
<Icon icon class="thaw-alert__icon"/>
<div>

View file

@ -1,10 +1,9 @@
mod theme;
use crate::utils::{dyn_classes, ssr_class};
use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
use_theme,
utils::{mount_style, StoredMaybeSignal},
utils::{class_list::class_list, mount_style, StoredMaybeSignal},
ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme,
};
use leptos::*;
@ -126,13 +125,10 @@ pub fn AutoComplete(
comp_ref.load(AutoCompleteRef { input_ref });
});
let ssr_class = ssr_class(&class);
view! {
<Binder target_ref=auto_complete_ref>
<div
class=ssr_class
use:dyn_classes=class
class:thaw-auto-complete=true
class=class_list!["thaw-auto-complete", move || class.get()]
ref=auto_complete_ref
on:keydown=on_keydown
>

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -35,14 +33,8 @@ pub fn Avatar(
});
mount_style("avatar", include_str!("./avatar.css"));
let ssr_class = ssr_class(&class);
view! {
<span
class=ssr_class
use:dyn_classes=class
class:thaw-avatar=true
style=move || css_vars.get()
>
<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/> })

View file

@ -1,9 +1,6 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -59,18 +56,13 @@ pub fn Badge(
value.to_string()
}
});
let ssr_class = ssr_class(&class);
view! {
<div class="thaw-badge" style=move || css_vars.get()>
<div
class=ssr_class
use:dyn_classes=class
class="thaw-badge__sup"
class=("thaw-badge__sup--value", move || !dot.get() && !value.get().is_empty())
class=("thaw-badge__sup--dot", move || dot.get())
>
{move || value.get()}
</div>
<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()
]>{move || value.get()}</div>
{children()}
</div>
}

View file

@ -1,22 +1,19 @@
use super::use_breadcrumb_separator;
use crate::utils::class_list::class_list;
use leptos::*;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::ssr_class;
#[component]
pub fn BreadcrumbItem(
#[prop(optional, into)] class: MaybeSignal<String>,
children: Children,
) -> impl IntoView {
let breadcrumb_separator = use_breadcrumb_separator();
let ssr_class = ssr_class(&class);
view! {
<li class="thaw-breadcrumb-item">
<span class=ssr_class use:dyn_classes=class class="thaw-breadcrumb-item__link">
{children()}
</span>
<span class=class_list![
"thaw-breadcrumb-item__link", move || class.get()
]>{children()}</span>
<span class="thaw-breadcrumb-item__separator">
{move || breadcrumb_separator.0.get()}
</span>

View file

@ -1,10 +1,9 @@
mod breadcrumb_item;
mod theme;
use crate::utils::dyn_classes;
use crate::{
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
pub use breadcrumb_item::BreadcrumbItem;
@ -37,13 +36,11 @@ pub fn Breadcrumb(
});
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<Provider value=BreadcrumbSeparatorInjection(separator)>
<nav
class=ssr_class
use:dyn_classes=class
class="thaw-breadcrumb"
class=class_list!["thaw-breadcrumb", move || class.get()]
style=move || css_vars.get()
>
<ul>{children()}</ul>

View file

@ -1,13 +1,11 @@
mod button_group;
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
components::{OptionComp, Wave, WaveRef},
icon::*,
theme::*,
utils::{mount_style, ssr_class, ComponentRef},
utils::{class_list::class_list, mount_style, ComponentRef},
};
pub use button_group::ButtonGroup;
use leptos::*;
@ -222,18 +220,17 @@ pub fn Button(
callback.call(event);
};
let ssr_class = ssr_class(&class);
view! {
<button
class=ssr_class
use:dyn_classes=class
class:thaw-button=true
class=("thaw-button--solid", move || variant.get() == ButtonVariant::Solid)
class=("thaw-button--text", move || variant.get() == ButtonVariant::Text)
class=("thaw-button--link", move || variant.get() == ButtonVariant::Link)
class=("thaw-button--round", move || round.get())
class=("thaw-button--circle", move || circle.get())
class=("thaw-button--disabled", move || disabled.get())
class=class_list![
"thaw-button", ("thaw-button--solid", move || variant.get() ==
ButtonVariant::Solid), ("thaw-button--text", move || variant.get() ==
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()
]
style=move || format!("{}{}", css_vars.get(), style.get())
disabled=move || disabled.get()
on:click=on_click

View file

@ -1,11 +1,9 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
chrono::{Datelike, Days, Local, NaiveDate},
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Button, ButtonGroup, ButtonVariant, Theme,
};
use chrono::{Month, Months};
@ -117,14 +115,9 @@ pub fn Calendar(
*date = *date + Months::new(1);
});
};
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-calendar"
style=move || css_vars.get()
>
<div class=class_list!["thaw-calendar", move || class.get()] style=move || css_vars.get()>
<div class="thaw-calendar__header">
<span class="thaw-calendar__header-title">
@ -132,7 +125,8 @@ pub fn Calendar(
show_date
.with(|date| {
format!(
"{} {}", Month::try_from(date.month() as u8).unwrap().name(),
"{} {}",
Month::try_from(date.month() as u8).unwrap().name(),
date.year(),
)
})

View file

@ -1,9 +1,7 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
components::*,
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -59,9 +57,8 @@ pub fn Card(
let header = store_value(card_header);
let header_extra = store_value(card_header_extra);
let ssr_class = ssr_class(&class);
view! {
<div class=ssr_class use:dyn_classes=class class="thaw-card" style=move || css_vars.get()>
<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">

View file

@ -1,13 +1,11 @@
mod checkbox_group;
mod checkbox_item;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
components::*,
icon::*,
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
pub use checkbox_group::CheckboxGroup;
@ -35,13 +33,13 @@ pub fn Checkbox(
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class:thaw-checkbox=true
class=("thaw-checkbox--checked", move || value.get())
class=class_list![
"thaw-checkbox", ("thaw-checkbox--checked", move || value.get()), move || class
.get()
]
style=move || css_vars.get()
on:click=move |_| value.set(!value.get_untracked())
>

View file

@ -1,11 +1,10 @@
mod color;
mod theme;
use crate::components::{Binder, Follower, FollowerPlacement};
use crate::utils::dyn_classes;
use crate::{
components::{Binder, Follower, FollowerPlacement},
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
pub use color::*;
@ -98,13 +97,10 @@ pub fn ColorPicker(
on_cleanup(move || timer.remove());
}
let ssr_class = ssr_class(&class);
view! {
<Binder target_ref=trigger_ref>
<div
class=ssr_class
use:dyn_classes=class
class="thaw-color-picker-trigger"
class=class_list!["thaw-color-picker-trigger", move || class.get()]
on:click=show_popover
ref=trigger_ref
>
@ -189,8 +185,9 @@ fn ColorPanel(hue: ReadSignal<u16>, sv: RwSignal<(f64, f64)>) -> impl IntoView {
class="thaw-color-picker-popover__handle"
style=move || {
format!(
"left: calc({}% - 6px); bottom: calc({}% - 6px)", sv.get().0 * 100.0, sv
.get().1 * 100.0,
"left: calc({}% - 6px); bottom: calc({}% - 6px)",
sv.get().0 * 100.0,
sv.get().1 * 100.0,
)
}
>

View file

@ -1,15 +1,13 @@
use leptos::*;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::{mount_style, ssr_class};
use crate::utils::{class_list::class_list, mount_style};
#[component]
pub fn Divider(#[prop(optional, into)] class: MaybeSignal<String>) -> impl IntoView {
mount_style("divider", include_str!("./divider.css"));
let ssr_class = ssr_class(&class);
view! {
<div class=ssr_class use:dyn_classes=class class="thaw-divider">
<div class=class_list!["thaw-divider", move || class.get()]>
<div class="thaw-divider__line"></div>
</div>
}

View file

@ -1,10 +1,7 @@
use super::use_grid;
use crate::utils::class_list::class_list;
use leptos::*;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::ssr_class;
#[component]
pub fn GridItem(
#[prop(default = MaybeSignal::Static(1u16), into)] column: MaybeSignal<u16>,
@ -37,9 +34,9 @@ pub fn GridItem(
style
});
let ssr_class = ssr_class(&class);
view! {
<div class=ssr_class use:dyn_classes=class class="thaw-grid-item" style=move || style.get()>
<div class=class_list!["thaw-grid-item", move || class.get()] style=move || style.get()>
{children()}
</div>
}

View file

@ -1,8 +1,6 @@
mod grid_item;
use crate::utils::dyn_classes;
use crate::utils::ssr_class;
use crate::utils::class_list::class_list;
pub use grid_item::*;
use leptos::*;
@ -24,10 +22,9 @@ pub fn Grid(
style
});
let ssr_class = ssr_class(&class);
view! {
<Provider value=GridInjection::new(x_gap)>
<div class=ssr_class use:dyn_classes=class class="thaw-grid" style=move || style.get()>
<div class=class_list!["thaw-grid", move || class.get()] style=move || style.get()>
{children()}
</div>
</Provider>

View file

@ -1,9 +1,5 @@
use leptos::*;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::ssr_class;
#[component]
pub fn Image(
#[prop(optional, into)] src: MaybeSignal<String>,
@ -35,11 +31,9 @@ pub fn Image(
style
};
let ssr_class = ssr_class(&class);
view! {
<img
class=ssr_class
use:dyn_classes=class
class=move || class.get()
src=move || src.get()
alt=move || alt.get()
style=style

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::{use_theme, Theme},
utils::{mount_style, ssr_class, ComponentRef},
utils::{class_list::class_list, mount_style, ComponentRef},
};
use leptos::*;
pub use theme::InputTheme;
@ -127,15 +125,14 @@ pub fn Input(
comp_ref.load(InputRef { input_ref });
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-input"
class=("thaw-input--focus", move || is_focus.get())
class=("thaw-input--disabled", move || disabled.get())
class=("thaw-input--invalid", move || invalid.get())
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()
]
style=move || css_vars.get()
>
{if let Some(prefix) = input_prefix.and_then(|prefix| prefix.if_.then_some(prefix)) {

View file

@ -1,8 +1,6 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -22,14 +20,9 @@ pub fn MenuGroup(
});
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-menu-group"
style=move || css_vars.get()
>
<div class=class_list!["thaw-menu-group", move || class.get()] style=move || css_vars.get()>
{label}
</div>
{children()}

View file

@ -1,10 +1,7 @@
use super::use_menu;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -40,14 +37,15 @@ pub fn MenuItem(
});
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div class="thaw-menu-item">
<div
class=ssr_class
use:dyn_classes=class
class="thaw-menu-item__content"
class=("thaw-menu-item__content--selected", move || menu.0.get() == key.get())
class=class_list![
"thaw-menu-item__content", ("thaw-menu-item__content--selected", move || menu.0
.get() == key.get()), move || class.get()
]
on:click=on_click
style=move || css_vars.get()
>

View file

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

View file

@ -1,12 +1,10 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
components::*,
icon::*,
use_theme,
utils::{mount_style, ssr_class, StoredMaybeSignal},
utils::{class_list::class_list, mount_style, StoredMaybeSignal},
Theme,
};
use leptos::*;
@ -48,14 +46,8 @@ pub fn NavBar(
}
};
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-nav-bar"
style=move || css_vars.get()
>
<div class=class_list!["thaw-nav-bar", move || class.get()] style=move || css_vars.get()>
<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>

View file

@ -1,14 +1,13 @@
mod theme;
use std::time::Duration;
use crate::{
components::{Binder, Follower, FollowerPlacement},
use_theme,
utils::{add_event_listener, dyn_classes, mount_style, ssr_class},
utils::{add_event_listener, class_list::class_list, mount_style},
Theme,
};
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
use std::time::Duration;
pub use theme::PopoverTheme;
#[slot]
@ -108,13 +107,10 @@ pub fn Popover(
});
});
let ssr_class = ssr_class(&class);
view! {
<Binder target_ref>
<div
class=ssr_class
use:dyn_classes=class
class="thaw-popover-trigger"
class=class_list!["thaw-popover-trigger", move || class.get()]
ref=target_ref
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave

View file

@ -1,8 +1,6 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -26,13 +24,12 @@ pub fn Radio(
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-radio"
class=("thaw-radio--checked", move || value.get())
class=class_list![
"thaw-radio", ("thaw-radio--checked", move || value.get()), move || class.get()
]
style=move || css_vars.get()
on:click=move |_| value.set(!value.get_untracked())
>

View file

@ -1,10 +1,9 @@
mod theme;
use crate::utils::dyn_classes;
use crate::{
components::{Binder, Follower, FollowerPlacement, FollowerWidth},
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -108,13 +107,11 @@ where
.map_or(String::new(), |v| v.label.clone()),
None => String::new(),
});
let ssr_class = ssr_class(&class);
view! {
<Binder target_ref=trigger_ref>
<div
class=ssr_class
use:dyn_classes=class
class="thaw-select"
class=class_list!["thaw-select", move || class.get()]
ref=trigger_ref
on:click=show_menu
style=move || css_vars.get()

View file

@ -1,12 +1,10 @@
mod slider_label;
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
components::OptionComp,
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -118,12 +116,9 @@ pub fn Slider(
});
on_cleanup(move || on_mouse_move.remove());
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-slider"
class=class_list!["thaw-slider", move || class.get()]
style=move || css_vars.get()
on:click=on_mouse_click
>

View file

@ -1,6 +1,4 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::{mount_style, ssr_class};
use crate::utils::{class_list::class_list, mount_style};
use leptos::*;
#[derive(Default)]
@ -30,12 +28,9 @@ pub fn Space(
SpaceGap::WH(width, height) => format!("{width}px {height}px"),
};
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-space"
class=class_list!["thaw-space", move || class.get()]
style:gap=gap
style:flex-direction=if vertical { "column" } else { "row" }
>

View file

@ -1,14 +1,11 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
pub use theme::SpinnerTheme;
#[derive(Default, Clone)]
@ -54,12 +51,9 @@ pub fn Spinner(
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-spinner"
class=class_list!["thaw-spinner", move || class.get()]
style=move || css_vars.get()
></div>
}

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -31,13 +29,13 @@ pub fn Switch(
});
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-switch"
class=("thaw-switch--active", move || value.get())
class=class_list![
"thaw-switch", ("thaw-switch--active", move || value.get()), move || class.get()
]
style=move || css_vars.get()
on:click=move |_| value.set(!value.get_untracked())
>

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -43,14 +41,14 @@ pub fn Table(
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<table
class=ssr_class
use:dyn_classes=class
class="thaw-table"
class=("thaw-table--single-row", move || single_row.get())
class=("thaw-table--single-column", move || single_column.get())
class=class_list![
"thaw-table", ("thaw-table--single-row", move || single_row.get()),
("thaw-table--single-column", move || single_column.get()), move || class.get()
]
style=move || format!("{}{}", css_vars.get(), style.get())
>
{children()}

View file

@ -1,10 +1,8 @@
mod tab;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -61,9 +59,9 @@ fn TabsInner(
let label_list_ref = create_node_ref::<html::Div>();
let children = children();
let ssr_class = ssr_class(&class);
view! {
<div class=ssr_class use:dyn_classes=class class="thaw-tabs" style=move || css_vars.get()>
<div class=class_list!["thaw-tabs", move || class.get()] style=move || css_vars.get()>
<div class="thaw-tabs__label-list" ref=label_list_ref>
<For
each=move || tab_options_vec.get()
@ -74,9 +72,11 @@ fn TabsInner(
create_effect({
let key = key.clone();
move |_| {
let Some(label) = label_ref.get() else { return;
let Some(label) = label_ref.get() else {
return;
};
let Some(label_list) = label_list_ref.get() else { return;
let Some(label_list) = label_list_ref.get() else {
return;
};
if key.clone() == value.get() {
request_animation_frame(move || {

View file

@ -1,7 +1,5 @@
use super::use_tabs;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::utils::{mount_style, ssr_class};
use crate::utils::{class_list::class_list, mount_style};
use leptos::*;
#[derive(Clone)]
@ -23,15 +21,10 @@ pub fn Tab(
key: key.clone(),
label,
});
let ssr_class = ssr_class(&class);
view! {
<div
class=ssr_class
use:dyn_classes=class
class="thaw-tab"
class=("thaw-tab--hidden", move || key != tabs.get_key())
>
{children()}
</div>
<div class=class_list![
"thaw-tab", ("thaw-tab--hidden", move || key != tabs.get_key()), move || class.get()
]>{children()}</div>
}
}

View file

@ -1,10 +1,8 @@
mod theme;
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
theme::use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -73,9 +71,9 @@ pub fn Tag(
});
css_vars
});
let ssr_class = ssr_class(&class);
view! {
<div class=ssr_class use:dyn_classes=class class="thaw-tag" style=move || css_vars.get()>
<div class=class_list!["thaw-tag", move || class.get()] style=move || css_vars.get()>
<span class="thaw-tag__content">{children()}</span>
</div>
}

View file

@ -1,8 +1,6 @@
#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
use_theme,
utils::{mount_style, ssr_class},
utils::{class_list::class_list, mount_style},
Theme,
};
use leptos::*;
@ -26,13 +24,10 @@ pub fn Text(
css_vars
});
let ssr_class = ssr_class(&class);
if code {
view! {
<code
class=ssr_class
use:dyn_classes=class
class="thaw-text thaw-text--code"
class=class_list!["thaw-text thaw-text--code", move || class.get()]
style=move || css_vars.get()
>
{children()}
@ -40,11 +35,7 @@ pub fn Text(
}
.into_any()
} else {
view! {
<span class=ssr_class use:dyn_classes=class class="thaw-text">
{children()}
</span>
}
view! { <span class=class_list!["thaw-text", move || class.get()]>{children()}</span> }
.into_any()
}
}

186
src/utils/class_list.rs Normal file
View file

@ -0,0 +1,186 @@
#[cfg(not(feature = "ssr"))]
use leptos::create_render_effect;
use leptos::{Attribute, IntoAttribute, Oco, RwSignal, SignalUpdate, SignalWith};
use std::{collections::HashSet, rc::Rc};
pub struct ClassList(RwSignal<HashSet<Oco<'static, str>>>);
impl ClassList {
pub fn new() -> Self {
Self(RwSignal::new(HashSet::new()))
}
pub fn add(self, value: impl IntoClass) -> Self {
let class = value.into_class();
match class {
Class::String(name) => {
self.0.update(move |set| {
set.insert(name);
});
}
Class::FnString(f) => {
#[cfg(feature = "ssr")]
{
let name = f();
self.0.update(|set| {
set.insert(name);
});
}
#[cfg(not(feature = "ssr"))]
create_render_effect(move |old_name| {
let name = f();
if let Some(old_name) = old_name {
if old_name != name {
self.0.update(|set| {
set.remove(&old_name);
set.insert(name.clone());
});
}
} else {
self.0.update(|set| {
set.insert(name.clone());
});
}
name
});
}
Class::Fn(name, f) => {
#[cfg(feature = "ssr")]
{
let new = f();
self.0.update(|set| {
if new {
set.insert(name);
}
});
}
#[cfg(not(feature = "ssr"))]
create_render_effect(move |old| {
let name = name.clone();
let new = f();
if old.is_none() {
if new {
self.0.update(|set| {
set.insert(name);
});
}
} else if old.as_ref() != Some(&new) {
self.0.update(|set| {
if new {
set.insert(name);
} else {
set.remove(&name);
}
});
}
new
});
}
}
self
}
}
impl IntoAttribute for ClassList {
fn into_attribute(self) -> Attribute {
Attribute::Fn(Rc::new(move || {
self.0.with(|set| {
let mut class = String::new();
set.iter().enumerate().for_each(|(index, name)| {
if name.is_empty() {
return;
}
if index != 0 {
class.push(' ');
}
class.push_str(name)
});
class.into_attribute()
})
}))
}
fn into_attribute_boxed(self: Box<Self>) -> Attribute {
self.into_attribute()
}
}
pub enum Class {
String(Oco<'static, str>),
FnString(Box<dyn Fn() -> Oco<'static, str>>),
Fn(Oco<'static, str>, Box<dyn Fn() -> bool>),
}
pub trait IntoClass {
fn into_class(self) -> Class;
}
impl IntoClass for String {
fn into_class(self) -> Class {
Class::String(self.into())
}
}
impl IntoClass for &'static str {
fn into_class(self) -> Class {
Class::String(self.into())
}
}
impl<T, U> IntoClass for T
where
T: Fn() -> U + 'static,
U: ToString,
{
fn into_class(self) -> Class {
Class::FnString(Box::new(move || (self)().to_string().into()))
}
}
impl<T> IntoClass for (&'static str, T)
where
T: Fn() -> bool + 'static,
{
fn into_class(self) -> Class {
Class::Fn(self.0.into(), Box::new(self.1))
}
}
impl<T> IntoClass for (String, T)
where
T: Fn() -> bool + 'static,
{
fn into_class(self) -> Class {
Class::Fn(self.0.into(), Box::new(self.1))
}
}
macro_rules! class_list {
($($name:expr),+) => {
{
use crate::utils::class_list::ClassList;
ClassList::new()$(.add($name))+
}
};
}
pub(crate) use class_list;
#[cfg(test)]
mod tests {
use leptos::{create_runtime, Attribute, IntoAttribute};
#[test]
fn macro_class_list() {
let rt = create_runtime();
let class_list = class_list!("aa", ("bb", || true), move || "cc");
if let Attribute::Fn(f) = class_list.into_attribute() {
if let Attribute::String(class) = f() {
assert!(class.contains("aa"));
assert!(class.contains("bb"));
assert!(class.contains("cc"));
}
}
rt.dispose();
}
}

View file

@ -1,35 +0,0 @@
use cfg_if::cfg_if;
use leptos::{html::AnyElement, HtmlElement, MaybeSignal};
pub fn dyn_classes(el: HtmlElement<AnyElement>, classes_signal: MaybeSignal<String>) {
cfg_if! {
if #[cfg(feature = "ssr")] {
let _ = el;
let _ = classes_signal;
} else {
use leptos::SignalGet;
let _ = el.dyn_classes(move || {
let classes = classes_signal.get();
if classes.is_empty() {
return vec![];
}
classes
.split_ascii_whitespace()
.map(|class| class.to_string())
.collect::<Vec<String>>()
});
}
};
}
pub fn ssr_class(class: &MaybeSignal<String>) -> String {
cfg_if! {
if #[cfg(feature = "ssr")] {
use leptos::SignalGetUntracked;
class.get_untracked()
} else {
let _ = class;
String::new()
}
}
}

View file

@ -1,6 +1,6 @@
// mod callback;
pub(crate) mod class_list;
mod component_ref;
mod dyn_classes;
mod event_listener;
mod mount_style;
mod signal;
@ -9,7 +9,6 @@ mod time;
// pub use callback::AsyncCallback;
pub use component_ref::{create_component_ref, ComponentRef};
pub(crate) use dyn_classes::*;
pub(crate) use event_listener::*;
pub(crate) use mount_style::mount_style;
pub use signal::SignalWatch;