Merge pull request #10 from thaw-ui/feat/leptos-v0.5.2

Feat/leptos v0.5.2
This commit is contained in:
luoxiaozero 2023-11-09 20:33:56 +08:00 committed by GitHub
commit 60c1cb0fad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 170 additions and 321 deletions

View file

@ -13,8 +13,8 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
leptos = { version = "0.5.1", features = ["csr"] } leptos = { version = "0.5.2", features = ["csr"] }
web-sys = { version = "0.3.62", features = [ web-sys = { version = "0.3.63", features = [
"DomRect", "DomRect",
"File", "File",
"FileList", "FileList",
@ -34,6 +34,7 @@ icondata = { version = "0.1.0", features = [
] } ] }
icondata_core = "0.0.2" icondata_core = "0.0.2"
uuid = { version = "1.5.0", features = ["v4"] } uuid = { version = "1.5.0", features = ["v4"] }
cfg-if = "1.0.0"
[workspace] [workspace]
members = ["demo"] members = ["demo"]

View file

@ -7,14 +7,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
leptos = { version = "0.5.1", features = ["csr"] } leptos = { version = "0.5.2", features = ["csr"] }
thaw = { path = "../" } thaw = { path = "../" }
icondata = { version = "0.1.0", features = [ icondata = { version = "0.1.0", features = [
"AiCloseOutlined", "AiCloseOutlined",
"AiCheckOutlined", "AiCheckOutlined",
"AiGithubOutlined", "AiGithubOutlined",
] } ] }
leptos_router = { version = "0.5.1", features = ["csr"] } leptos_router = { version = "0.5.2", features = ["csr"] }
leptos_devtools = "0.0.1" leptos_devtools = "0.0.1"
prisms = { git = "https://github.com/luoxiaozero/prisms", rev = "16d4d34b93fc20578ebf03137d54ecc7eafa4d4b" } prisms = { git = "https://github.com/luoxiaozero/prisms", rev = "16d4d34b93fc20578ebf03137d54ecc7eafa4d4b" }

View file

@ -1,12 +1,6 @@
mod theme; mod theme;
use crate::{ use crate::{mount_style, teleport::Teleport, use_theme, utils::StoredMaybeSignal, Input, Theme};
mount_style,
teleport::Teleport,
use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, StoredMaybeSignal},
Input, Theme,
};
use leptos::*; use leptos::*;
pub use theme::AutoCompleteTheme; pub use theme::AutoCompleteTheme;
@ -18,7 +12,7 @@ pub struct AutoCompleteOption {
#[component] #[component]
pub fn AutoComplete( pub fn AutoComplete(
#[prop(optional, into)] value: MaybeRwSignal<String>, #[prop(optional, into)] value: RwSignal<String>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: 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>,

View file

@ -95,12 +95,12 @@ pub fn Button(
"--thaw-background-color-active: {};", "--thaw-background-color-active: {};",
theme.button.color_text_active theme.button.color_text_active
)); ));
css_vars.push_str(&format!("--thaw-ripple-color: #0000;")); css_vars.push_str("--thaw-ripple-color: #0000;");
} else { } else {
css_vars.push_str(&format!("--thaw-font-color-hover: {bg_color};")); css_vars.push_str(&format!("--thaw-font-color-hover: {bg_color};"));
css_vars.push_str("--thaw-border-color: #555a;"); css_vars.push_str("--thaw-border-color: #555a;");
css_vars.push_str("--thaw-border-color-hover: #555;"); css_vars.push_str("--thaw-border-color-hover: #555;");
css_vars.push_str(&format!("--thaw-ripple-color: #0000;")); css_vars.push_str("--thaw-ripple-color: #0000;");
} }
}); });

View file

@ -1,29 +1,19 @@
use crate::utils::maybe_rw_signal::MaybeRwSignal;
use leptos::*; use leptos::*;
use std::collections::HashSet; use std::collections::HashSet;
#[component] #[component]
pub fn CheckboxGroup( pub fn CheckboxGroup(
#[prop(optional, into)] value: MaybeRwSignal<HashSet<String>>, #[prop(optional, into)] value: RwSignal<HashSet<String>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let injection_key = CheckboxGroupInjectionKey::new(value.into()); provide_context(CheckboxGroupInjection(value));
provide_context(injection_key);
children() children()
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CheckboxGroupInjectionKey { pub(crate) struct CheckboxGroupInjection(pub RwSignal<HashSet<String>>);
pub value: RwSignal<HashSet<String>>,
}
impl CheckboxGroupInjectionKey { pub(crate) fn use_checkbox_group() -> CheckboxGroupInjection {
pub fn new(value: RwSignal<HashSet<String>>) -> Self { expect_context()
Self { value }
}
}
pub fn use_checkbox_group() -> CheckboxGroupInjectionKey {
expect_context::<CheckboxGroupInjectionKey>()
} }

View file

@ -11,13 +11,13 @@ pub fn CheckboxItem(
) -> impl IntoView { ) -> impl IntoView {
let checkbox_group = use_checkbox_group(); let checkbox_group = use_checkbox_group();
let checked = checkbox_group let checked = checkbox_group
.value .0
.with_untracked(|checkbox_group| checkbox_group.contains(&key)); .with_untracked(|checkbox_group| checkbox_group.contains(&key));
let checked = create_rw_signal(checked); let checked = create_rw_signal(checked);
let item_key = store_value(key); let item_key = store_value(key);
_ = checked.watch(move |checked| { _ = checked.watch(move |checked| {
checkbox_group.value.update(move |checkbox_group| { checkbox_group.0.update(move |checkbox_group| {
if *checked { if *checked {
checkbox_group.insert(item_key.get_value()); checkbox_group.insert(item_key.get_value());
} else { } else {

View file

@ -1,13 +1,7 @@
mod checkbox_group; mod checkbox_group;
mod checkbox_item; mod checkbox_item;
use crate::{ use crate::{components::*, icon::*, theme::use_theme, utils::mount_style::mount_style, Theme};
components::*,
icon::*,
theme::use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
pub use checkbox_group::CheckboxGroup; pub use checkbox_group::CheckboxGroup;
pub use checkbox_item::CheckboxItem; pub use checkbox_item::CheckboxItem;
use icondata::AiIcon; use icondata::AiIcon;
@ -15,7 +9,7 @@ use leptos::*;
#[component] #[component]
pub fn Checkbox( pub fn Checkbox(
#[prop(optional, into)] value: MaybeRwSignal<bool>, #[prop(optional, into)] value: RwSignal<bool>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -41,7 +35,7 @@ pub fn Checkbox(
> >
<input class="thaw-checkbox__input" type="checkbox"/> <input class="thaw-checkbox__input" type="checkbox"/>
<div class="thaw-checkbox__dot"> <div class="thaw-checkbox__dot">
<If cond=value.clone_into()> <If cond=value>
<Then slot> <Then slot>
<Icon icon=Icon::from(AiIcon::AiCheckOutlined) style="color: white"/> <Icon icon=Icon::from(AiIcon::AiCheckOutlined) style="color: white"/>
</Then> </Then>

View file

@ -1,16 +1,14 @@
mod color; mod color;
mod theme; mod theme;
use crate::{ use crate::{mount_style, teleport::Teleport, use_theme, Theme};
mount_style, teleport::Teleport, use_theme, utils::maybe_rw_signal::MaybeRwSignal, Theme,
};
pub use color::*; pub use color::*;
use leptos::*; use leptos::*;
use leptos::{leptos_dom::helpers::WindowListenerHandle, wasm_bindgen::__rt::IntoJsResult}; use leptos::{leptos_dom::helpers::WindowListenerHandle, wasm_bindgen::__rt::IntoJsResult};
pub use theme::ColorPickerTheme; pub use theme::ColorPickerTheme;
#[component] #[component]
pub fn ColorPicker(#[prop(optional, into)] value: MaybeRwSignal<RGBA>) -> impl IntoView { pub fn ColorPicker(#[prop(optional, into)] value: RwSignal<RGBA>) -> 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);
let popover_css_vars = create_memo(move |_| { let popover_css_vars = create_memo(move |_| {

View file

@ -10,7 +10,7 @@ pub fn Grid(
#[prop(optional, into)] y_gap: MaybeSignal<i32>, #[prop(optional, into)] y_gap: MaybeSignal<i32>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
let grid_injection_key = GridInjectionKey::new(x_gap); let grid_injection_key = GridInjection::new(x_gap);
provide_context(grid_injection_key); provide_context(grid_injection_key);
let style = create_memo(move |_| { let style = create_memo(move |_| {
@ -31,16 +31,16 @@ pub fn Grid(
} }
#[derive(Clone)] #[derive(Clone)]
pub struct GridInjectionKey { pub(crate) struct GridInjection {
x_gap: MaybeSignal<i32>, x_gap: MaybeSignal<i32>,
} }
impl GridInjectionKey { impl GridInjection {
pub fn new(x_gap: MaybeSignal<i32>) -> Self { pub fn new(x_gap: MaybeSignal<i32>) -> Self {
Self { x_gap } Self { x_gap }
} }
} }
pub fn use_grid() -> GridInjectionKey { pub(crate) fn use_grid() -> GridInjection {
expect_context::<GridInjectionKey>() expect_context()
} }

View file

@ -2,7 +2,7 @@ mod theme;
use crate::{ use crate::{
theme::{use_theme, Theme}, theme::{use_theme, Theme},
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style}, utils::mount_style::mount_style,
}; };
use leptos::*; use leptos::*;
pub use theme::InputTheme; pub use theme::InputTheme;
@ -30,7 +30,7 @@ pub struct InputSuffix {
#[component] #[component]
pub fn Input( pub fn Input(
#[prop(optional, into)] value: MaybeRwSignal<String>, #[prop(optional, into)] value: RwSignal<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: MaybeSignal<String>,

View file

@ -1,14 +1,12 @@
use crate::utils::StoredMaybeSignal; use crate::utils::StoredMaybeSignal;
use crate::{ use crate::{AiIcon, Button, ButtonVariant, Icon, Input, InputSuffix};
utils::maybe_rw_signal::MaybeRwSignal, AiIcon, Button, ButtonVariant, Icon, Input, InputSuffix,
};
use leptos::*; use leptos::*;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
use std::str::FromStr; use std::str::FromStr;
#[component] #[component]
pub fn InputNumber<T>( pub fn InputNumber<T>(
#[prop(optional, into)] value: MaybeRwSignal<T>, #[prop(optional, into)] value: RwSignal<T>,
#[prop(optional, into)] placeholder: MaybeSignal<String>, #[prop(optional, into)] placeholder: MaybeSignal<String>,
#[prop(into)] step: MaybeSignal<T>, #[prop(into)] step: MaybeSignal<T>,
) -> impl IntoView ) -> impl IntoView

View file

@ -19,6 +19,7 @@ pub fn LoadingBarProvider(children: Children) -> impl IntoView {
pub struct LoadingBarInjection { pub struct LoadingBarInjection {
loading_bar_ref: ComponentRef<LoadingBarRef>, loading_bar_ref: ComponentRef<LoadingBarRef>,
} }
impl Copy for LoadingBarInjection {} impl Copy for LoadingBarInjection {}
impl LoadingBarInjection { impl LoadingBarInjection {

View file

@ -52,7 +52,7 @@ pub(crate) fn LoadingBar(#[prop(optional)] comp_ref: ComponentRef<LoadingBarRef>
.style("transition", "none") .style("transition", "none")
.style("max-width", "0"); .style("max-width", "0");
_ = loading_bar_ref.offset_width(); _ = loading_bar_ref.offset_width();
loading_bar_ref _ = loading_bar_ref
.style("transition", "max-width 4s linear") .style("transition", "max-width 4s linear")
.style("max-width", "80%"); .style("max-width", "80%");
} }
@ -66,7 +66,7 @@ pub(crate) fn LoadingBar(#[prop(optional)] comp_ref: ComponentRef<LoadingBarRef>
}; };
let finish = Callback::new(move |_| { let finish = Callback::new(move |_| {
if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() { if let Some(loading_bar_ref) = loading_bar_ref.get_untracked() {
loading_bar_ref _ = loading_bar_ref
.style("background-color", "var(--thaw-background-color)") .style("background-color", "var(--thaw-background-color)")
.style("transition", "max-width 0.5s linear") .style("transition", "max-width 0.5s linear")
.style("max-width", "100%"); .style("max-width", "100%");
@ -83,7 +83,7 @@ pub(crate) fn LoadingBar(#[prop(optional)] comp_ref: ComponentRef<LoadingBarRef>
.style("max-width", "0"); .style("max-width", "0");
_ = loading_bar_ref.offset_width(); _ = loading_bar_ref.offset_width();
} }
loading_bar_ref _ = loading_bar_ref
.style("background-color", "var(--thaw-background-color-error)") .style("background-color", "var(--thaw-background-color-error)")
.style("transition", "max-width 0.5s linear") .style("transition", "max-width 0.5s linear")
.style("max-width", "100%"); .style("max-width", "100%");
@ -99,7 +99,7 @@ pub(crate) fn LoadingBar(#[prop(optional)] comp_ref: ComponentRef<LoadingBarRef>
view! { view! {
<div <div
class="thaw-loading-bar-container" class="thaw-loading-bar-container"
style=move || (!loading.get()).then(|| "display: none;") style=move || (!loading.get()).then_some("display: none;")
> >
<div <div
class="thaw-loading-bar" class="thaw-loading-bar"

View file

@ -1,4 +1,4 @@
use super::{use_menu, MenuInjectionKey}; use super::use_menu;
use crate::{theme::use_theme, utils::mount_style::mount_style, Theme}; use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
use leptos::*; use leptos::*;
@ -12,7 +12,10 @@ pub fn MenuItem(
let menu = use_menu(); let menu = use_menu();
let click_key = key.clone(); let click_key = key.clone();
let on_click = move |_| { let on_click = move |_| {
menu.set(MenuInjectionKey::new(click_key.get())); let click_key = click_key.get();
if menu.0.with(|key| key != &click_key) {
menu.0.set(click_key);
}
}; };
let css_vars = create_memo(move |_| { let css_vars = create_memo(move |_| {
@ -33,7 +36,7 @@ pub fn MenuItem(
<div class="thaw-menu-item"> <div class="thaw-menu-item">
<div <div
class="thaw-menu-item__content" class="thaw-menu-item__content"
class=("thaw-menu-item__content--selected", move || menu.get().value == key.get()) class=("thaw-menu-item__content--selected", move || menu.0.get() == key.get())
on:click=on_click on:click=on_click
style=move || css_vars.get() style=move || css_vars.get()
> >

View file

@ -2,48 +2,20 @@ mod menu_group;
mod menu_item; mod menu_item;
mod theme; mod theme;
use crate::utils::maybe_rw_signal::MaybeRwSignal;
use leptos::*; use leptos::*;
pub use menu_group::MenuGroup; pub use menu_group::MenuGroup;
pub use menu_item::*; pub use menu_item::*;
pub use theme::MenuTheme; pub use theme::MenuTheme;
#[component] #[component]
pub fn Menu( pub fn Menu(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
#[prop(optional, into)] value: MaybeRwSignal<String>, provide_context(MenuInjection(value));
children: Children,
) -> impl IntoView {
let menu_injection_key = create_rw_signal(MenuInjectionKey::new(value.get_untracked()));
create_effect(move |_| {
let selected_key = value.get();
let key = menu_injection_key.get_untracked();
if selected_key != key.value {
menu_injection_key.set(MenuInjectionKey::new(selected_key));
}
});
create_effect(move |_| {
let selected_key = value.get_untracked();
let key = menu_injection_key.get();
if selected_key != key.value {
value.set(key.value);
}
});
provide_context(menu_injection_key);
view! { <div class="thaw-menu">{children()}</div> } view! { <div class="thaw-menu">{children()}</div> }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct MenuInjectionKey { pub(crate) struct MenuInjection(pub RwSignal<String>);
value: String,
}
impl MenuInjectionKey { pub(crate) fn use_menu() -> MenuInjection {
pub fn new(value: String) -> Self { expect_context()
Self { value }
}
}
pub fn use_menu() -> RwSignal<MenuInjectionKey> {
expect_context::<RwSignal<MenuInjectionKey>>()
} }

View file

@ -1,54 +0,0 @@
use crate::{theme::use_theme, Icon, Theme};
use icondata::*;
use leptos::*;
#[derive(Default, Clone)]
pub enum MessageVariant {
#[default]
Success,
Warning,
Error,
}
impl MessageVariant {
fn icon(&self) -> Icon {
match self {
MessageVariant::Success => icondata::Icon::Ai(AiCloseCircleFilled),
MessageVariant::Warning => icondata::Icon::Ai(AiExclamationCircleFilled),
MessageVariant::Error => icondata::Icon::Ai(AiCheckCircleFilled),
}
}
fn theme_color(&self, theme: &Theme) -> String {
match self {
MessageVariant::Success => theme.common.color_success.clone(),
MessageVariant::Warning => theme.common.color_warning.clone(),
MessageVariant::Error => theme.common.color_error.clone(),
}
}
}
#[component]
pub(crate) fn Message(variant: MessageVariant, content: String) -> impl IntoView {
let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| {
let mut css_vars = String::new();
theme.with(|theme| {
css_vars.push_str(&format!(
"--thaw-background-color: {}",
theme.message.background_color
))
});
css_vars
});
let style = theme.with_untracked(|theme| format!("color: {};", variant.theme_color(theme)));
view! {
<div class="thaw-message-wrapper">
<div class="thaw-message" style=move || css_vars.get()>
<div class="thaw-message__icon">
<Icon icon=variant.icon() style/>
</div>
<div class="thaw-message__content">{content}</div>
</div>
</div>
}
}

View file

@ -1,4 +1,4 @@
use super::{message::Message, message_provider::MessageType}; use super::{message_provider::MessageType, Message};
use leptos::*; use leptos::*;
use uuid::Uuid; use uuid::Uuid;

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use super::{message::MessageVariant, message_environment::MessageEnvironment}; use super::{message_environment::MessageEnvironment, MessageVariant};
use crate::{mount_style, teleport::Teleport}; use crate::{mount_style, teleport::Teleport};
use leptos::*; use leptos::*;
use uuid::Uuid; use uuid::Uuid;

View file

@ -1,8 +1,60 @@
mod message;
mod message_environment; mod message_environment;
mod message_provider; mod message_provider;
mod theme; mod theme;
pub use message::*; use crate::{theme::use_theme, Icon, Theme};
use icondata::*;
use leptos::*;
pub use message_provider::*; pub use message_provider::*;
pub use theme::MessageTheme; pub use theme::MessageTheme;
#[derive(Default, Clone)]
pub enum MessageVariant {
#[default]
Success,
Warning,
Error,
}
impl MessageVariant {
fn icon(&self) -> Icon {
match self {
MessageVariant::Success => icondata::Icon::Ai(AiCloseCircleFilled),
MessageVariant::Warning => icondata::Icon::Ai(AiExclamationCircleFilled),
MessageVariant::Error => icondata::Icon::Ai(AiCheckCircleFilled),
}
}
fn theme_color(&self, theme: &Theme) -> String {
match self {
MessageVariant::Success => theme.common.color_success.clone(),
MessageVariant::Warning => theme.common.color_warning.clone(),
MessageVariant::Error => theme.common.color_error.clone(),
}
}
}
#[component]
pub(crate) fn Message(variant: MessageVariant, content: String) -> impl IntoView {
let theme = use_theme(Theme::light);
let css_vars = create_memo(move |_| {
let mut css_vars = String::new();
theme.with(|theme| {
css_vars.push_str(&format!(
"--thaw-background-color: {}",
theme.message.background_color
))
});
css_vars
});
let style = theme.with_untracked(|theme| format!("color: {};", variant.theme_color(theme)));
view! {
<div class="thaw-message-wrapper">
<div class="thaw-message" style=move || css_vars.get()>
<div class="thaw-message__icon">
<Icon icon=variant.icon() style/>
</div>
<div class="thaw-message__content">{content}</div>
</div>
</div>
}
}

View file

@ -1,18 +1,14 @@
mod tabbar_item; mod tabbar_item;
mod theme; mod theme;
use crate::{ use crate::{use_theme, utils::mount_style::mount_style, Theme};
use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
use leptos::*; use leptos::*;
pub use tabbar_item::*; pub use tabbar_item::*;
pub use theme::TabbarTheme; pub use theme::TabbarTheme;
#[component] #[component]
pub fn Tabbar( pub fn Tabbar(
#[prop(optional, into)] value: MaybeRwSignal<String>, #[prop(optional, into)] value: RwSignal<String>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tabbar", include_str!("./tabbar.css")); mount_style("tabbar", include_str!("./tabbar.css"));
@ -25,24 +21,7 @@ pub fn Tabbar(
) )
}) })
}); });
provide_context(TabbarInjection(value));
let tabbar_injection_key = create_rw_signal(TabbarInjectionKey::new(value.get()));
create_effect(move |_| {
let selected_key = value.get();
let key = tabbar_injection_key.get_untracked();
if selected_key != key.value {
tabbar_injection_key.set(TabbarInjectionKey::new(selected_key));
}
});
create_effect(move |_| {
let selected_key = value.get_untracked();
let key = tabbar_injection_key.get();
if selected_key != key.value {
value.set(key.value);
}
});
provide_context(tabbar_injection_key);
view! { view! {
<div class="thaw-tabbar" style=move || css_vars.get()> <div class="thaw-tabbar" style=move || css_vars.get()>
{children()} {children()}
@ -51,16 +30,8 @@ pub fn Tabbar(
} }
#[derive(Clone)] #[derive(Clone)]
pub struct TabbarInjectionKey { pub(crate) struct TabbarInjection(pub RwSignal<String>);
value: String,
}
impl TabbarInjectionKey { pub(crate) fn use_tabbar() -> TabbarInjection {
pub fn new(value: String) -> Self { expect_context()
Self { value }
}
}
pub fn use_tabbar() -> RwSignal<TabbarInjectionKey> {
use_context::<RwSignal<TabbarInjectionKey>>().expect("TabbarInjectionKey not exist")
} }

View file

@ -1,4 +1,4 @@
use super::{use_tabbar, TabbarInjectionKey}; use super::use_tabbar;
use crate::components::*; use crate::components::*;
use crate::utils::StoredMaybeSignal; use crate::utils::StoredMaybeSignal;
use crate::{icon::*, theme::use_theme, utils::mount_style::mount_style, Theme}; use crate::{icon::*, theme::use_theme, utils::mount_style::mount_style, Theme};
@ -15,7 +15,10 @@ pub fn TabbarItem(
let tabbar = use_tabbar(); let tabbar = use_tabbar();
let key: StoredMaybeSignal<_> = key.into(); let key: StoredMaybeSignal<_> = key.into();
let on_click = move |_| { let on_click = move |_| {
tabbar.set(TabbarInjectionKey::new(key.get())); let click_key = key.get();
if tabbar.0.with(|key| key != &click_key) {
tabbar.0.set(click_key);
}
}; };
let css_vars = create_memo(move |_| { let css_vars = create_memo(move |_| {
@ -30,7 +33,7 @@ pub fn TabbarItem(
view! { view! {
<div <div
class="thaw-tabbar-item" class="thaw-tabbar-item"
class=("thaw-tabbar-item--selected", move || tabbar.get().value == key.get()) class=("thaw-tabbar-item--selected", move || tabbar.0.get() == key.get())
on:click=on_click on:click=on_click
style=move || css_vars.get() style=move || css_vars.get()
> >

View file

@ -1,7 +1,6 @@
use crate::utils::mount_style::mount_style; use crate::utils::mount_style::mount_style;
use leptos::*; use cfg_if::cfg_if;
use std::time::Duration; use std::time::Duration;
use web_sys::Element;
pub struct ToastOptions { pub struct ToastOptions {
pub message: String, pub message: String,
@ -10,26 +9,20 @@ pub struct ToastOptions {
pub fn show_toast(options: ToastOptions) { 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")] {
let parent = Element::from(document().body().expect("body element not to exist")); use leptos::{leptos_dom::Mountable, *};
let children = view! { <div class="thaw-toast">{options.message}</div> }; let mount = document().body().expect("body element not to exist");
let node = children.into_view(); let children = view! { <div class="thaw-toast">{options.message}</div> };
let node = children.into_view();
#[cfg(target_arch = "wasm32")]
{
use leptos::leptos_dom::Mountable;
let node = node.get_mountable_node(); let node = node.get_mountable_node();
parent.append_child(&node).unwrap(); _ = mount.append_child(&node);
set_timeout( set_timeout(
move || { move || {
_ = parent.remove_child(&node); _ = mount.remove_child(&node);
}, },
options.duration, options.duration,
); );
} } else {
#[cfg(not(target_arch = "wasm32"))] _ = options;
{ }}
_ = parent;
_ = node;
}
} }

View file

@ -1,15 +1,8 @@
use crate::{ use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
theme::use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
use leptos::*; use leptos::*;
#[component] #[component]
pub fn Radio( pub fn Radio(#[prop(optional, into)] value: RwSignal<bool>, children: Children) -> impl IntoView {
#[prop(optional, into)] value: MaybeRwSignal<bool>,
children: Children,
) -> impl IntoView {
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
mount_style("radio", include_str!("./radio.css")); mount_style("radio", include_str!("./radio.css"));

View file

@ -1,11 +1,6 @@
mod theme; mod theme;
use crate::{ use crate::{teleport::Teleport, theme::use_theme, utils::mount_style::mount_style, Theme};
teleport::Teleport,
theme::use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
use leptos::wasm_bindgen::__rt::IntoJsResult; use leptos::wasm_bindgen::__rt::IntoJsResult;
use leptos::*; use leptos::*;
use std::hash::Hash; use std::hash::Hash;
@ -19,7 +14,7 @@ pub struct SelectOption<T> {
#[component] #[component]
pub fn Select<T>( pub fn Select<T>(
#[prop(optional, into)] value: MaybeRwSignal<Option<T>>, #[prop(optional, into)] value: RwSignal<Option<T>>,
#[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>, #[prop(optional, into)] options: MaybeSignal<Vec<SelectOption<T>>>,
) -> impl IntoView ) -> impl IntoView
where where

View file

@ -1,16 +1,12 @@
mod theme; mod theme;
use crate::{ use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
theme::use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
use leptos::*; use leptos::*;
pub use theme::SliderTheme; pub use theme::SliderTheme;
#[component] #[component]
pub fn Slider( pub fn Slider(
#[prop(optional, into)] value: MaybeRwSignal<f64>, #[prop(optional, into)] value: RwSignal<f64>,
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>, #[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("slider", include_str!("./slider.css")); mount_style("slider", include_str!("./slider.css"));

View file

@ -1,11 +1,11 @@
mod theme; mod theme;
use crate::{mount_style, theme::use_theme, utils::maybe_rw_signal::MaybeRwSignal, Theme}; use crate::{mount_style, theme::use_theme, Theme};
use leptos::*; use leptos::*;
pub use theme::SwitchTheme; pub use theme::SwitchTheme;
#[component] #[component]
pub fn Switch(#[prop(optional, into)] value: MaybeRwSignal<bool>) -> impl IntoView { pub fn Switch(#[prop(optional, into)] value: RwSignal<bool>) -> 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);
let css_vars = create_memo(move |_| { let css_vars = create_memo(move |_| {

View file

@ -1,24 +1,16 @@
mod tab; mod tab;
use std::ops::Deref;
use crate::{ use crate::{theme::use_theme, utils::mount_style::mount_style, Theme};
theme::use_theme,
utils::{maybe_rw_signal::MaybeRwSignal, mount_style::mount_style},
Theme,
};
use leptos::*; use leptos::*;
pub use tab::*; pub use tab::*;
#[component] #[component]
pub fn Tabs( pub fn Tabs(#[prop(optional, into)] value: RwSignal<String>, children: Children) -> impl IntoView {
#[prop(optional, into)] value: MaybeRwSignal<String>,
children: Children,
) -> impl IntoView {
mount_style("tabs", include_str!("./tabs.css")); mount_style("tabs", include_str!("./tabs.css"));
let tab_options_vec = create_rw_signal(vec![]); let tab_options_vec = create_rw_signal(vec![]);
provide_context(TabsInjectionKey { provide_context(TabsInjection {
active_key: *value.deref(), active_key: value,
tab_options_vec, tab_options_vec,
}); });
let theme = use_theme(Theme::light); let theme = use_theme(Theme::light);
@ -114,12 +106,12 @@ pub(crate) struct TabsLabelLine {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct TabsInjectionKey { pub(crate) struct TabsInjection {
active_key: RwSignal<String>, active_key: RwSignal<String>,
tab_options_vec: RwSignal<Vec<TabOption>>, tab_options_vec: RwSignal<Vec<TabOption>>,
} }
impl TabsInjectionKey { impl TabsInjection {
pub fn get_key(&self) -> String { pub fn get_key(&self) -> String {
self.active_key.get() self.active_key.get()
} }
@ -131,6 +123,6 @@ impl TabsInjectionKey {
} }
} }
pub fn use_tabs() -> TabsInjectionKey { pub(crate) fn use_tabs() -> TabsInjection {
use_context::<TabsInjectionKey>().expect("TabsInjectionKey not exist") expect_context()
} }

View file

@ -1,34 +1,30 @@
use cfg_if::cfg_if;
use leptos::*; use leptos::*;
use web_sys::Element;
/// https://github.com/solidjs/solid/blob/main/packages/solid/web/src/index.ts#L56 /// https://github.com/solidjs/solid/blob/main/packages/solid/web/src/index.ts#L56
#[component] #[component]
pub fn Teleport(#[prop(optional)] to: Option<&'static str>, children: Children) -> impl IntoView { pub fn Teleport(
let parent = if let Some(to) = to { #[prop(into, optional)] mount: Option<web_sys::Element>,
document() children: Children,
.query_selector(to) ) -> impl IntoView {
.expect("element not to exist") cfg_if! { if #[cfg(target_arch = "wasm32")] {
.expect("element not to exist") use leptos::{
} else { wasm_bindgen::JsCast,
Element::from(document().body().expect("body element not to exist")) leptos_dom::Mountable
}; };
let mount = mount.unwrap_or_else(|| {
#[cfg(target_arch = "wasm32")] document()
{ .body()
use leptos::leptos_dom::Mountable; .expect("body element not to exist")
.unchecked_into()
});
let node = children().into_view(); let node = children().into_view();
let node = node.get_mountable_node(); let node = node.get_mountable_node();
parent.append_child(&node).unwrap(); _ = mount.append_child(&node);
on_cleanup(move || { on_cleanup(move || {
_ = parent.remove_child(&node); _ = mount.remove_child(&node);
}); });
} } else {
_ = mount;
#[cfg(not(target_arch = "wasm32"))]
{
_ = parent;
_ = children; _ = children;
} }}
view! { <></> }
} }

View file

@ -1,44 +0,0 @@
use leptos::RwSignal;
use std::ops::Deref;
pub struct MaybeRwSignal<T: Default + 'static>(RwSignal<T>);
impl<T: Default> MaybeRwSignal<T> {
pub fn clone_into(&self) -> RwSignal<T> {
self.0
}
}
impl<T: Default> Default for MaybeRwSignal<T> {
fn default() -> Self {
Self(RwSignal::new(Default::default()))
}
}
impl<T: Default> Clone for MaybeRwSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Default> Copy for MaybeRwSignal<T> {}
impl<T: Default> Deref for MaybeRwSignal<T> {
type Target = RwSignal<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Default> From<RwSignal<T>> for MaybeRwSignal<T> {
fn from(value: RwSignal<T>) -> Self {
Self(value)
}
}
impl<T: Default> From<MaybeRwSignal<T>> for RwSignal<T> {
fn from(value: MaybeRwSignal<T>) -> Self {
value.0
}
}

View file

@ -1,10 +1,9 @@
// mod callback; // mod callback;
mod component_ref; mod component_ref;
pub mod maybe_rw_signal;
mod maybe_signal_store;
pub mod mount_style; pub mod mount_style;
pub mod signal; pub mod signal;
mod stored_maybe_signal;
// pub use callback::AsyncCallback; // pub use callback::AsyncCallback;
pub use component_ref::ComponentRef; pub use component_ref::ComponentRef;
pub use maybe_signal_store::*; pub use stored_maybe_signal::*;

View file

@ -37,6 +37,12 @@ pub fn Wave(#[prop(optional)] comp_ref: ComponentRef<WaveRef>) -> impl IntoView
} }
}); });
comp_ref.load(WaveRef { play }); comp_ref.load(WaveRef { play });
on_cleanup(move || {
if let Some(handle) = animation_timeout_handle.get() {
handle.clear();
animation_timeout_handle.set(None);
}
});
view! { view! {
<div <div
class="thaw-wave" class="thaw-wave"