diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c23e5b43..4ee7bec8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: toolchain: stable target: wasm32-unknown-unknown override: true + - uses: Swatinem/rust-cache@v2 - name: Install Trunk uses: jetli/trunk-action@v0.4.0 with: @@ -47,6 +48,7 @@ jobs: toolchain: nightly target: wasm32-unknown-unknown override: true + - uses: Swatinem/rust-cache@v2 - name: Install Trunk uses: jetli/trunk-action@v0.4.0 with: @@ -67,14 +69,11 @@ jobs: toolchain: stable target: wasm32-unknown-unknown override: true - - name: Install Trunk - uses: jetli/trunk-action@v0.4.0 - with: - version: "latest" + - uses: Swatinem/rust-cache@v2 - name: Install Cargo BInstall uses: cargo-bins/cargo-binstall@main - name: Install Cargo Leptos - run: cargo binstall cargo-leptos -y + run: cargo binstall cargo-leptos --locked --no-confirm - name: Build run: | cd ./examples/ssr_axum @@ -91,14 +90,11 @@ jobs: toolchain: nightly target: wasm32-unknown-unknown override: true - - name: Install Trunk - uses: jetli/trunk-action@v0.4.0 - with: - version: "latest" + - uses: Swatinem/rust-cache@v2 - name: Install Cargo BInstall uses: cargo-bins/cargo-binstall@main - name: Install Cargo Leptos - run: cargo binstall cargo-leptos -y + run: cargo binstall cargo-leptos --locked --no-confirm - name: Build run: | cd ./examples/ssr_axum @@ -116,6 +112,7 @@ jobs: toolchain: stable target: wasm32-unknown-unknown override: true + - uses: Swatinem/rust-cache@v2 - name: cargo test run: | cd thaw_utils diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index 337750ac..2826fd5a 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -31,6 +31,7 @@ jobs: toolchain: stable target: wasm32-unknown-unknown override: true + - uses: Swatinem/rust-cache@v2 - name: Install Trunk uses: jetli/trunk-action@v0.4.0 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 45d7e552..a1a7eccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## [0.5.0-beta](https://github.com/thaw-ui/thaw/compare/v0.5.0-alpha...v0.5.0-beta) (2025-05-04) + +### Bug Fixs + +* The arrow position of the InfoLabel component. + +### Breaking Changes + +* Update leptos to v0.8.0. + +## [0.5.0-alpha](https://github.com/thaw-ui/thaw/compare/v0.4.5...v0.5.0-alpha) (2025-03-30) + +### Features + +* Adds `Tree` component, closes [#392](https://github.com/thaw-ui/thaw/pull/392). +* `Menu` is changed to a generic component, closes [#390](https://github.com/thaw-ui/thaw/pull/390). + +### Breaking Changes + +* Update leptos to v0.8.0-beta. +* Theme is changed to use getset. +* RatingColor adds Marigold prop. +* InputType adds Number prop. +* The icon prop of `Icon` is changed to icondata_core::Icon type. +* The value prop of `MenuItem` is changed to generic type. + ## [0.4.8](https://github.com/thaw-ui/thaw/compare/v0.4.7...v0.4.8) (2025-08-03) ### Features @@ -94,4 +120,4 @@ ### 0.1.0-beta2 - 0.4.0-rc (2023-11-14 - 2024-12-09) -See [0.1 - 0.4 changelog](./changelog/CHANGELOG-0.1-0.4.md) +See [0.1 - 0.4 changelog](./changelogs/CHANGELOG-0.1-0.4.md) diff --git a/Cargo.toml b/Cargo.toml index 22ffe8ed..90446031 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,16 @@ exclude = ["examples"] rust-version = "1.79" [workspace.dependencies] -thaw = { version = "0.4.8", path = "./thaw" } -thaw_components = { version = "0.3.2", path = "./thaw_components" } +thaw = { version = "0.5.0-beta", path = "./thaw" } +thaw_components = { version = "0.4.0-beta", path = "./thaw_components" } thaw_macro = { version = "0.1.0", path = "./thaw_macro" } -thaw_utils = { version = "0.1.2", path = "./thaw_utils" } +thaw_utils = { version = "0.2.0-beta", path = "./thaw_utils" } -leptos = { version = "0.7.7" } -leptos_meta = { version = "0.7.7" } -leptos_router = { version = "0.7.7" } -reactive_stores = { version = "0.1.7" } +# leptos +leptos = { version = "0.8.5" } +leptos_meta = { version = "0.8.5" } +leptos_router = { version = "0.8.5" } +reactive_stores = { version = "0.2.5" } + +# web +wasm-bindgen = { version = "0.2.100" } diff --git a/LICENSE b/LICENSE index d090c54a..afa21f1d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 luoxiaozero +Copyright (c) 2023 lizidev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index cd864985..94469a41 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ You can refer to the [thaw-template](https://github.com/thaw-ui/thaw-template) r | Crate version | Compatible Leptos version | | ------------------------------------------------------------------------ | ------------------------- | | 0.3 / [thaw-v0.3 branch](https://github.com/thaw-ui/thaw/tree/thaw-v0.3) | 0.6 | -| 0.4 / main branch | 0.7 | +| 0.4 / [thaw-v0.4 branch](https://github.com/thaw-ui/thaw/tree/thaw-v0.4) | 0.7 | +| 0.5-beta / main branch | 0.8 | ## Resources diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 514f0d0a..6b6d22c2 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -26,7 +26,7 @@ uuid = { version = "1.10.0", features = ["v4", "js"] } console_error_panic_hook = "0.1.7" console_log = "1" log = "0.4" -js-sys = "0.3.70" +js-sys = "0.3.74" gloo-net = { version = "0.6.0" } [features] diff --git a/demo/index.html b/demo/index.html index a1879b17..928ed3e3 100644 --- a/demo/index.html +++ b/demo/index.html @@ -10,6 +10,7 @@ href="/favicon.ico" type="image/x-icon" /> + impl IntoView { + + @@ -120,6 +122,7 @@ fn TheRouter() -> impl IntoView { + diff --git a/demo/src/components/demo.rs b/demo/src/components/demo.rs index 0f57eee5..f4864af7 100644 --- a/demo/src/components/demo.rs +++ b/demo/src/components/demo.rs @@ -20,7 +20,7 @@ pub fn Demo( let css_vars = Memo::new(move |_| { let mut css_vars = String::new(); theme.with(|theme| { - if theme.color.color_scheme == "dark" { + if theme.color.color_scheme() == "dark" { css_vars.push_str("--demo-border-color: #383f52;"); css_vars.push_str("--demo-background-color: #242832;"); } else { @@ -32,7 +32,12 @@ pub fn Demo( }); let code_class = Memo::new(move |_| { - theme.with(|theme| format!("demo-demo__code color-scheme--{}", theme.color.color_scheme)) + theme.with(|theme| { + format!( + "demo-demo__code color-scheme--{}", + theme.color.color_scheme() + ) + }) }); let is_show_code = RwSignal::new(children.is_none()); diff --git a/demo/src/components/site_header.rs b/demo/src/components/site_header.rs index dd0bd9af..563cd6e3 100644 --- a/demo/src/components/site_header.rs +++ b/demo/src/components/site_header.rs @@ -5,6 +5,14 @@ use leptos_router::hooks::use_navigate; // use leptos_use::{storage::use_local_storage, utils::FromToStringCodec}; use thaw::*; +#[derive(Clone)] +enum HeaderAction { + ChangeTheme, + Github, + Discord, + Navigate(&'static str), +} + #[component] pub fn SiteHeader() -> impl IntoView { let navigate = use_navigate(); @@ -172,19 +180,18 @@ pub fn SiteHeader() -> impl IntoView { change_theme(MouseEvent::new("click").unwrap()), - "Light" => change_theme(MouseEvent::new("click").unwrap()), - "github" => { + on_select=move |value: HeaderAction| match value { + HeaderAction::ChangeTheme => change_theme(MouseEvent::new("click").unwrap()), + HeaderAction::Github => { _ = window().open_with_url("http://github.com/thaw-ui/thaw"); } - "discord" => { + HeaderAction::Discord => { _ = window() .open_with_url( "https://discord.gg/uMGvWBDV", ); } - _ => navigate_signal.get()(&value, Default::default()), + HeaderAction::Navigate(value) => navigate_signal.get()(&value, Default::default()), } > @@ -195,11 +202,11 @@ pub fn SiteHeader() -> impl IntoView { attr:style="font-size: 22px; padding: 0px 6px;" /> - {theme_name} - + {theme_name} + "Github" - + "Discord" { @@ -216,7 +223,7 @@ pub fn SiteHeader() -> impl IntoView { .into_iter() .map(|item| { let NavItemOption { label, value, .. } = item; - view! { {label} } + view! { {label} } }) .collect_view()} } diff --git a/demo/src/pages/components.rs b/demo/src/pages/components.rs index 026b0406..52e8bf65 100644 --- a/demo/src/pages/components.rs +++ b/demo/src/pages/components.rs @@ -155,6 +155,16 @@ pub(crate) fn gen_nav_data() -> Vec { value: "/guide/customize-theme", label: "Customize Theme", }, + NavItemOption { + group: Some("Theme"), + value: "/guide/style-element-position", + label: "Style Element Position", + }, + NavItemOption { + group: None, + value: "/guide/internationalization", + label: "Internationalization", + }, NavItemOption { group: None, value: "/guide/FAQs", @@ -469,6 +479,11 @@ pub(crate) fn gen_nav_data() -> Vec { value: "/components/tooltip", label: "Tooltip", }, + NavItemOption { + group: None, + value: "/components/tree", + label: "Tree", + }, NavItemOption { group: None, value: "/components/upload", diff --git a/demo_markdown/Cargo.toml b/demo_markdown/Cargo.toml index d9eb3e11..e2eb07ed 100644 --- a/demo_markdown/Cargo.toml +++ b/demo_markdown/Cargo.toml @@ -14,4 +14,7 @@ quote = "1.0.36" comrak = "0.26.0" proc-macro2 = "1.0.86" syn = "2.0.74" -syntect = "5.2.0" \ No newline at end of file +syntect = "5.2.0" + +# fix: https://github.com/jhpratt/deranged/issues/17 +deranged = "=0.4.0" \ No newline at end of file diff --git a/demo_markdown/docs/_guide/internationalization.md b/demo_markdown/docs/_guide/internationalization.md new file mode 100644 index 00000000..8e790905 --- /dev/null +++ b/demo_markdown/docs/_guide/internationalization.md @@ -0,0 +1,52 @@ +# Internationalization + +Some component, `Calendar` and `DatePicker`, can use another locale that the default. + +You can use one of the provided locales: +```rust +use thaw::locales; +let locale = RwSignal::new(LocaleConfig::from(locales::EnUS)); +``` + +Or you can create your own struct that implement the `LocaleExt` trait: +```rust +use thaw::locales::LocaleExt; +pub struct MyStruct; +impl LocaleExt for MyStruct { + fn locale(&self) -> &Locale {todo!()} + fn today(&self) -> &'static str {todo!()} +} +let locale = RwSignal::new(LocaleConfig::from(MyStruct)); +``` + +When you have your `RwSignal`, you can pass it to the `ConfigProvider` via the +`locale` props: +```rust +view! { + + ... + +} +``` + +If you need to change the locale, but don't have access to the `RwSignal` you passed to the `ConfigProvider`, you can call the `LocaleConfig::use_rw_locale()` function to get a `RwSignal` of the locale. + +Here is a demo of it working: +```rust demo +use chrono::prelude::*; +use Locale; +let value = RwSignal::new(Local::now().date_naive()); + +let locale = LocaleConfig::use_rw_locale(); + +view! { + + + + + + + + +} +``` diff --git a/demo_markdown/docs/_guide/server_sider_rendering.md b/demo_markdown/docs/_guide/server_sider_rendering.md index 4e41c407..54938be6 100644 --- a/demo_markdown/docs/_guide/server_sider_rendering.md +++ b/demo_markdown/docs/_guide/server_sider_rendering.md @@ -30,20 +30,6 @@ For [start-axum](https://github.com/leptos-rs/start-axum), it is in the [src/app For [start-actix](https://github.com/leptos-rs/start-actix), it is in the [src/main.rs](https://github.com/leptos-rs/start-actix/blob/943b1ad428072267f32217de4c62896f2bf70459/src/main.rs#L33) file. -## Features - -To enable the ssr mode, the following configurations are required: - -```toml -thaw = { ..., features = ["ssr"] } -``` - -To enable the hydrate mode, the following configurations are required: - -```toml -thaw = { ..., features = ["hydrate"] } -``` - ## cargo-leptos If you use [cargo-leptos](https://github.com/leptos-rs/cargo-leptos), Remember to add thaw to your `Cargo.toml` file in the corresponding feature, e.g. diff --git a/demo_markdown/docs/_guide/theme/customize_theme.md b/demo_markdown/docs/_guide/theme/customize_theme.md index aabc8996..83e0fd1d 100644 --- a/demo_markdown/docs/_guide/theme/customize_theme.md +++ b/demo_markdown/docs/_guide/theme/customize_theme.md @@ -4,9 +4,9 @@ let theme = RwSignal::new(Theme::light()); let on_customize_theme = move |_| { theme.update(|theme| { - theme.color.color_brand_background = "#f5222d".to_string(); - theme.color.color_brand_background_hover = "#ff4d4f".to_string(); - theme.color.color_brand_background_pressed = "#cf1322".to_string(); + theme.color.set_color_brand_background("#f5222d".to_string()); + theme.color.set_color_brand_background_hover("#ff4d4f".to_string()); + theme.color.set_color_brand_background_pressed("#cf1322".to_string()); }); }; diff --git a/demo_markdown/docs/_guide/theme/style_element_position.md b/demo_markdown/docs/_guide/theme/style_element_position.md new file mode 100644 index 00000000..b0017752 --- /dev/null +++ b/demo_markdown/docs/_guide/theme/style_element_position.md @@ -0,0 +1,14 @@ +# Style Element Position + +Sometimes you want to control where the style element should be inserted. + +You can create a element inside head element, then thaw-ui's style will be inserted right before it. + +```html + + + + + + +``` diff --git a/demo_markdown/src/lib.rs b/demo_markdown/src/lib.rs index ce4dfe3d..64e25b7f 100644 --- a/demo_markdown/src/lib.rs +++ b/demo_markdown/src/lib.rs @@ -24,6 +24,8 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt "InstallationMdPage" => "../docs/_guide/installation.md", "ServerSiderRenderingMdPage" => "../docs/_guide/server_sider_rendering.md", "CustomizeThemeMdPage" => "../docs/_guide/theme/customize_theme.md", + "StyleElementPositionMdPage" => "../docs/_guide/theme/style_element_position.md", + "InternationalizationMdPage" => "../docs/_guide/internationalization.md", "FAQsMdPage" => "../docs/_guide/FAQs.md", "AccordionMdPage" => "../../thaw/src/accordion/docs/mod.md", "AnchorMdPage" => "../../thaw/src/anchor/docs/mod.md", @@ -84,6 +86,7 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt "TimePickerMdPage" => "../../thaw/src/time_picker/docs/mod.md", "ToastMdPage" => "../../thaw/src/toast/docs/mod.md", "TooltipMdPage" => "../../thaw/src/tooltip/docs/mod.md", + "TreeMdPage" => "../../thaw/src/tree/docs/mod.md", "UploadMdPage" => "../../thaw/src/upload/docs/mod.md" }; diff --git a/examples/island/Cargo.toml b/examples/island/Cargo.toml index b93e5277..dda25657 100644 --- a/examples/island/Cargo.toml +++ b/examples/island/Cargo.toml @@ -16,7 +16,7 @@ leptos_router = { version = "0.7.7" } tokio = { version = "1", features = ["rt-multi-thread"], optional = true } tower = { version = "0.4", optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true } -wasm-bindgen = "0.2.95" +wasm-bindgen = "0.2.100" thiserror = "1" tracing = { version = "0.1", optional = true } http = "1" diff --git a/examples/ssr_axum/Cargo.toml b/examples/ssr_axum/Cargo.toml index f83c2281..37ce134e 100644 --- a/examples/ssr_axum/Cargo.toml +++ b/examples/ssr_axum/Cargo.toml @@ -7,21 +7,27 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -axum = { version = "0.7.5", optional = true } -console_error_panic_hook = "0.1" -leptos = { version = "0.7.7" } -leptos_axum = { version = "0.7.7", optional = true } -leptos_meta = { version = "0.7.7" } -leptos_router = { version = "0.7.7" } -tokio = { version = "1", features = ["rt-multi-thread"], optional = true } +http = "1" tower = { version = "0.5.0", optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true } -wasm-bindgen = "0.2.95" +axum = { version = "0.8.1", optional = true } + +tokio = { version = "1", features = ["rt-multi-thread"], optional = true } + +console_log = "1" +console_error_panic_hook = "0.1" + +leptos = { version = "0.8.5" } +leptos_axum = { version = "0.8.5", optional = true } +leptos_meta = { version = "0.8.5" } +leptos_router = { version = "0.8.5" } + +wasm-bindgen = "0.2.100" + thiserror = "1" tracing = { version = "0.1", optional = true } -http = "1" -console_log = "1" log = "0.4" + demo = { path = "../../demo", default-features = false } thaw = { path = "../../thaw" } diff --git a/thaw/Cargo.toml b/thaw/Cargo.toml index 59b806a4..818e75fc 100644 --- a/thaw/Cargo.toml +++ b/thaw/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "thaw" -version = "0.4.8" +version = "0.5.0-beta" edition = "2021" keywords = ["web", "leptos", "ui", "thaw", "component"] readme = "../README.md" -authors = ["luoxiaozero"] +authors = ["lizidev"] description = "An easy to use leptos component library" homepage = "https://github.com/thaw-ui/thaw" repository = "https://github.com/thaw-ui/thaw" @@ -19,7 +19,8 @@ leptos = { workspace = true } thaw_components = { workspace = true } thaw_macro = { workspace = true } thaw_utils = { workspace = true } -web-sys = { version = "0.3.70", features = [ +leptos_transition_group = { version = "0.2.0" } +web-sys = { version = "0.3.72", features = [ "DomRect", "File", "FileList", @@ -29,7 +30,7 @@ web-sys = { version = "0.3.70", features = [ "TreeWalker", "NodeFilter", ] } -wasm-bindgen = "0.2.95" +wasm-bindgen = { workspace = true } icondata_core = "0.1.0" icondata_ai = "0.0.10" uuid = { version = "1.10.0", features = ["v4", "js"] } @@ -39,6 +40,8 @@ palette = "0.7.6" num-traits = "0.2.19" send_wrapper = "0.6" slotmap = "1.0" +getset = "0.1.5" +pure-rust-locales = "0.8.1" [features] csr = ["leptos/csr", "thaw_components/csr", "thaw_utils/csr"] diff --git a/thaw/src/_binder/mod.rs b/thaw/src/_binder/mod.rs index ad0f79bd..f97006b3 100644 --- a/thaw/src/_binder/mod.rs +++ b/thaw/src/_binder/mod.rs @@ -1,8 +1,7 @@ use crate::ConfigInjection; use leptos::{context::Provider, prelude::*, tachys::html::node_ref::node_ref}; -use thaw_components::{ - use_binder, CSSTransition, Follower, FollowerInjection, Teleport, UseBinder, -}; +use leptos_transition_group::CSSTransition; +use thaw_components::{use_binder, Follower, FollowerInjection, Teleport, UseBinder}; use thaw_utils::BoxCallback; #[component] @@ -23,7 +22,7 @@ where placement: follower_placement, children: follower_children, auto_height, - arrow + arrow, } = follower; let UseBinder { @@ -43,7 +42,7 @@ where let on_before_enter = { let sync_position = sync_position.clone(); - move || { + move |_| { sync_position(); } }; @@ -65,7 +64,7 @@ where } }); - let on_after_leave = move || { + let on_after_leave = move |_| { if let Some(on_css_transition_after_leave) = &on_css_transition_after_leave { on_css_transition_after_leave(); } diff --git a/thaw/src/_motion/collapse_transition/index.css b/thaw/src/_motion/collapse_transition/index.css new file mode 100644 index 00000000..7ac5ba3a --- /dev/null +++ b/thaw/src/_motion/collapse_transition/index.css @@ -0,0 +1,17 @@ +.thaw-motion-collapse-enter-from, +.thaw-motion-collapse-leave-to { + opacity: 0; + padding-top: 0 !important; +} + +.thaw-motion-collapse-enter-to, +.thaw-motion-collapse-leave-from { + opacity: 1; +} + +.thaw-motion-collapse-enter-active, +.thaw-motion-collapse-leave-active { + overflow-y: hidden; + transition: max-height var(--durationNormal) var(--curveEasyEaseMax), + opacity var(--durationNormal) var(--curveEasyEaseMax); +} diff --git a/thaw/src/_motion/collapse_transition/mod.rs b/thaw/src/_motion/collapse_transition/mod.rs new file mode 100644 index 00000000..0eb51a16 --- /dev/null +++ b/thaw/src/_motion/collapse_transition/mod.rs @@ -0,0 +1,60 @@ +use leptos::prelude::*; +use leptos_transition_group::CSSTransition; +use thaw_utils::mount_style; +use web_sys::HtmlElement; + +#[component] +pub fn CollapseTransition( + #[prop(into)] show: Signal, + children: TypedChildren, +) -> impl IntoView +where + T: AddAnyAttr + IntoView + Send + 'static, +{ + mount_style("motion-collapse", include_str!("./index.css")); + + let on_enter = |el: HtmlElement| { + let style = el.style(); + let memorized_height = el.offset_height(); + let _ = style.set_property("max-height", "0"); + el.offset_width(); + let _ = style.set_property("transition", ""); + let _ = el + .style() + .set_property("max-height", &format!("{}px", memorized_height)); + el.offset_width(); + }; + + let on_after_enter = |el: HtmlElement| { + let _ = el.style().set_property("max-height", ""); + }; + + let on_before_leave = |el: HtmlElement| { + let _ = el + .style() + .set_property("max-height", &format!("{}px", el.offset_height())); + el.offset_width(); + }; + + let on_leave = |el: HtmlElement| { + let _ = el.style().set_property("max-height", "0"); + el.offset_width(); + }; + + let on_after_leave = |el: HtmlElement| { + let _ = el.style().set_property("max-height", ""); + }; + + view! { + + } +} diff --git a/thaw/src/_motion/mod.rs b/thaw/src/_motion/mod.rs new file mode 100644 index 00000000..3dcc6745 --- /dev/null +++ b/thaw/src/_motion/mod.rs @@ -0,0 +1,3 @@ +mod collapse_transition; + +pub use collapse_transition::*; \ No newline at end of file diff --git a/thaw/src/accordion/accordion_item.rs b/thaw/src/accordion/accordion_item.rs index a0f8ce49..eced4119 100644 --- a/thaw/src/accordion/accordion_item.rs +++ b/thaw/src/accordion/accordion_item.rs @@ -1,6 +1,6 @@ use crate::AccordionInjection; use leptos::prelude::*; -use thaw_components::CSSTransition; +use leptos_transition_group::CSSTransition; use thaw_utils::{class_list, mount_style, update, with}; #[component] diff --git a/thaw/src/back_top/mod.rs b/thaw/src/back_top/mod.rs index f3ba1d70..caed7deb 100644 --- a/thaw/src/back_top/mod.rs +++ b/thaw/src/back_top/mod.rs @@ -1,6 +1,7 @@ use crate::{ConfigInjection, Icon}; use leptos::{either::Either, ev, html, prelude::*}; -use thaw_components::{CSSTransition, Teleport}; +use leptos_transition_group::CSSTransition; +use thaw_components::Teleport; use thaw_utils::{ add_event_listener, class_list, get_scroll_parent_element, mount_style, BoxCallback, EventListenerHandle, diff --git a/thaw/src/calendar/mod.rs b/thaw/src/calendar/mod.rs index f894d88f..8a81a11d 100644 --- a/thaw/src/calendar/mod.rs +++ b/thaw/src/calendar/mod.rs @@ -1,5 +1,5 @@ -use crate::{Button, ButtonGroup}; -use chrono::{Datelike, Days, Local, Month, Months, NaiveDate}; +use crate::{Button, ButtonGroup, LocaleConfig}; +use chrono::{Datelike, Days, Local, Months, NaiveDate}; use leptos::{prelude::*, tachys::view::any_view::AnyView}; use std::{ops::Deref, sync::Arc}; use thaw_utils::{class_list, mount_style, OptionModel, OptionModelWithValue}; @@ -13,6 +13,9 @@ pub fn Calendar( #[prop(optional, into)] children: Option, ) -> impl IntoView { mount_style("calendar", include_str!("./calendar.css")); + + let locale = LocaleConfig::use_locale(); + let show_date = RwSignal::new(value.get_untracked().unwrap_or(now_date())); Effect::new(move |_| { if let Some(selected_date) = value.get() { @@ -30,19 +33,23 @@ pub fn Calendar( let show_date_month = show_date.month(); let mut dates = vec![]; + let first_weekday = locale.get().first_weekday(); + let last_weekday = first_weekday.pred(); + let mut current_date = show_date; - let mut current_weekday_number = None::; + let mut current_weekday = None; loop { let date = current_date - Days::new(1); if date.month() != show_date_month { - if current_weekday_number.is_none() { - current_weekday_number = Some(current_date.weekday().num_days_from_sunday()); + if current_weekday.is_none() { + current_weekday = Some(current_date.weekday()); } - let weekday_number = current_weekday_number.unwrap(); - if weekday_number == 0 { + let weekday = current_weekday.unwrap(); + + if weekday == first_weekday { break; } - current_weekday_number = Some(weekday_number - 1); + current_weekday = Some(weekday.pred()); dates.push(CalendarItemDate::Previous(date)); } else { @@ -53,18 +60,18 @@ pub fn Calendar( dates.reverse(); dates.push(CalendarItemDate::Current(show_date)); current_date = show_date; - current_weekday_number = None; + current_weekday = None; loop { let date = current_date + Days::new(1); if date.month() != show_date_month { - if current_weekday_number.is_none() { - current_weekday_number = Some(current_date.weekday().num_days_from_sunday()); + if current_weekday.is_none() { + current_weekday = Some(current_date.weekday()); } - let weekday_number = current_weekday_number.unwrap(); - if weekday_number == 6 { + let weekday = current_weekday.unwrap(); + if weekday == last_weekday { break; } - current_weekday_number = Some(weekday_number + 1); + current_weekday = Some(weekday.succ()); dates.push(CalendarItemDate::Next(date)); } else { dates.push(CalendarItemDate::Current(date)); @@ -98,7 +105,7 @@ pub fn Calendar( .with(|date| { format!( "{} {}", - Month::try_from(date.month() as u8).unwrap().name(), + locale.get().month(date.month() as u8), date.year(), ) }) @@ -107,7 +114,7 @@ pub fn Calendar( + diff --git a/thaw/src/date_picker/panel/month_panel.rs b/thaw/src/date_picker/panel/month_panel.rs index 86455930..3e8b513f 100644 --- a/thaw/src/date_picker/panel/month_panel.rs +++ b/thaw/src/date_picker/panel/month_panel.rs @@ -1,5 +1,5 @@ use super::PanelVariant; -use crate::{Button, ButtonAppearance, ButtonSize}; +use crate::{Button, ButtonAppearance, ButtonSize, LocaleConfig}; use chrono::{Datelike, Month, Months, NaiveDate}; use leptos::{html, prelude::*}; use thaw_components::FollowerInjection; @@ -82,6 +82,8 @@ pub fn MonthPanel( #[component] fn MonthPanelItem(date_panel_show_date: RwSignal, month: Month) -> impl IntoView { + let locale = LocaleConfig::use_locale(); + let is_selected = Memo::new(move |_| { date_panel_show_date.with(|date| date.month() == month.number_from_month()) }); @@ -91,7 +93,7 @@ fn MonthPanelItem(date_panel_show_date: RwSignal, month: Month) -> im class="thaw-date-picker-month-panel__item" class=("thaw-date-picker-month-panel__item--selected", move || is_selected.get()) > -
{month.name().split_at(3).0}
+
{locale.get().ab_month(month.number_from_month() as u8)}
} } diff --git a/thaw/src/dialog/dialog.rs b/thaw/src/dialog/dialog.rs index 096797c2..dadc1efa 100644 --- a/thaw/src/dialog/dialog.rs +++ b/thaw/src/dialog/dialog.rs @@ -1,6 +1,7 @@ use crate::ConfigInjection; use leptos::{context::Provider, ev, prelude::*}; -use thaw_components::{CSSTransition, FocusTrap, Teleport}; +use thaw_components::{FocusTrap, Teleport}; +use leptos_transition_group::CSSTransition; use thaw_utils::{class_list, mount_style, Model}; #[component] diff --git a/thaw/src/dialog/dialog_surface.rs b/thaw/src/dialog/dialog_surface.rs index d8be5405..101e760e 100644 --- a/thaw/src/dialog/dialog_surface.rs +++ b/thaw/src/dialog/dialog_surface.rs @@ -1,6 +1,6 @@ use super::dialog::DialogInjection; use leptos::prelude::*; -use thaw_components::CSSTransition; +use leptos_transition_group::CSSTransition; use thaw_utils::class_list; #[component] diff --git a/thaw/src/drawer/inline_drawer.rs b/thaw/src/drawer/inline_drawer.rs index 7fdcc1ef..8d926432 100644 --- a/thaw/src/drawer/inline_drawer.rs +++ b/thaw/src/drawer/inline_drawer.rs @@ -1,6 +1,6 @@ use super::{DrawerPosition, DrawerSize}; use leptos::prelude::*; -use thaw_components::CSSTransition; +use leptos_transition_group::CSSTransition; use thaw_utils::{class_list, mount_style, Model}; #[component] diff --git a/thaw/src/drawer/overlay_drawer.rs b/thaw/src/drawer/overlay_drawer.rs index 1e1a6096..86818dc9 100644 --- a/thaw/src/drawer/overlay_drawer.rs +++ b/thaw/src/drawer/overlay_drawer.rs @@ -1,7 +1,8 @@ use super::{DrawerModalType, DrawerPosition, DrawerSize}; use crate::ConfigInjection; use leptos::{either::Either, ev, prelude::*}; -use thaw_components::{CSSTransition, FocusTrap, Teleport}; +use leptos_transition_group::CSSTransition; +use thaw_components::{FocusTrap, Teleport}; use thaw_utils::{class_list, mount_style, use_lock_html_scroll, Model}; #[component] @@ -42,7 +43,7 @@ pub fn OverlayDrawer( open_drawer.set(is_open); }); use_lock_html_scroll(is_lock.into()); - let on_after_leave = move || { + let on_after_leave = move |_| { is_lock.set(false); }; diff --git a/thaw/src/icon/docs/mod.md b/thaw/src/icon/docs/mod.md index 38636554..df8efad4 100644 --- a/thaw/src/icon/docs/mod.md +++ b/thaw/src/icon/docs/mod.md @@ -11,11 +11,11 @@ view! { ### Icon Props -| Name | Type | Default | Desciption | -| -------- | ---------------------------------------- | -------------------- | ----------------------------------- | -| class | `MaybeProp` | `Default::default()` | HTML class attribute. | -| style | `Option>` | `Default::default()` | HTML style attribute. | -| icon | `Signal` | | The icon to show. | -| width | `MaybeProp` | `1em` | The width of the icon. | -| height | `MaybeProp` | `1em` | The height of the icon. | -| on_click | `Option>` | `None` | Callback when clicking on the icon. | +| Name | Type | Default | Desciption | +| --- | --- | --- | --- | +| class | `MaybeProp` | `Default::default()` | HTML class attribute. | +| style | `Option>` | `Default::default()` | HTML style attribute. | +| icon | [`icondata_core::Icon`](https://docs.rs/icondata_core/0.1.0/icondata_core/type.Icon.html) | | The icon to show. | +| width | `MaybeProp` | `1em` | The width of the icon. | +| height | `MaybeProp` | `1em` | The height of the icon. | +| on_click | `Option>` | `None` | Callback when clicking on the icon. | diff --git a/thaw/src/icon/icons/mod.rs b/thaw/src/icon/icons/mod.rs index b7bf6463..40439c23 100644 --- a/thaw/src/icon/icons/mod.rs +++ b/thaw/src/icon/icons/mod.rs @@ -19,6 +19,25 @@ pub fn ChevronDownRegularIcon() -> impl IntoView { } } +#[component] +pub fn ChevronRight12RegularIcon() -> impl IntoView { + view! { + + } +} + #[component] pub fn DismissRegularIcon() -> impl IntoView { view! { diff --git a/thaw/src/icon/mod.rs b/thaw/src/icon/mod.rs index f0c75170..17832118 100644 --- a/thaw/src/icon/mod.rs +++ b/thaw/src/icon/mod.rs @@ -12,7 +12,7 @@ use thaw_utils::{class_list, mount_style, ArcOneCallback}; pub fn Icon( /// The icon to render. #[prop(into)] - icon: Signal, + icon: icondata_core::Icon, /// The width of the icon (horizontal side length of the square surrounding the icon). /// Defaults to "1em". #[prop(into, default = "1em".into())] @@ -32,43 +32,36 @@ pub fn Icon( on_click: Option>, ) -> impl IntoView { mount_style("icon", include_str!("./icon.css")); - let class = class_list!["thaw-icon", class]; - move || { - let icon = icon.get(); + let style = match (style, icon.style) { + (Some(a), Some(b)) => Some(ArcMemo::new(move |_| format!("{b} {}", a.get())).into()), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b.into()), + (None, None) => None, + }; - let style = match (style.clone(), icon.style) { - (Some(a), Some(b)) => Some(ArcMemo::new(move |_| format!("{b} {}", a.get())).into()), - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b.into()), - (None, None) => None, - }; - let width = width.clone(); - let height = height.clone(); - let on_click = on_click.clone(); - let on_click = move |ev| { - if let Some(click) = on_click.as_ref() { - click(ev); - } - }; - - view! { - + let on_click = move |ev| { + if let Some(click) = on_click.as_ref() { + click(ev); } + }; + + view! { + } } diff --git a/thaw/src/input/docs/mod.md b/thaw/src/input/docs/mod.md index 3356b26b..be0801cc 100644 --- a/thaw/src/input/docs/mod.md +++ b/thaw/src/input/docs/mod.md @@ -143,7 +143,7 @@ view! { | autofocus | `Signal` | `false` | Whether the input receives focus automatically. | | disabled | `Signal` | `false` | Whether the input is disabled. | | readonly | `Signal` | `false` | Whether the input is readonly. | -| input_size | `Signal>` | `None` | The input size width. | +| input_size | `Signal>` | `None` | The input size width. | | on_focus | `Option>` | `None` | Callback triggered when the input is focussed on. | | on_blur | `Option>` | `None` | Callback triggered when the input is blurred. | | parser | `OptionalProp>>` | `None` | Modifies the user input before assigning it to the value. | diff --git a/thaw/src/input/mod.rs b/thaw/src/input/mod.rs index e7ec429f..9b36181a 100644 --- a/thaw/src/input/mod.rs +++ b/thaw/src/input/mod.rs @@ -49,7 +49,7 @@ pub fn Input( readonly: Signal, /// Input size width. #[prop(optional, into)] - input_size: Signal>, + input_size: Signal>, #[prop(optional)] input_prefix: Option, #[prop(optional)] input_suffix: Option, #[prop(optional, into)] input_style: MaybeProp, diff --git a/thaw/src/input/types.rs b/thaw/src/input/types.rs index ed8a5732..73aaee59 100644 --- a/thaw/src/input/types.rs +++ b/thaw/src/input/types.rs @@ -28,6 +28,7 @@ pub enum InputType { DatetimeLocal, Month, Week, + Number, } impl InputType { @@ -44,6 +45,7 @@ impl InputType { Self::DatetimeLocal => "datetime-local", Self::Month => "month", Self::Week => "week", + Self::Number => "number", } } } @@ -92,4 +94,4 @@ impl InputSize { Self::Large => "large", } } -} \ No newline at end of file +} diff --git a/thaw/src/lib.rs b/thaw/src/lib.rs index 83b98ba7..596918b2 100644 --- a/thaw/src/lib.rs +++ b/thaw/src/lib.rs @@ -1,5 +1,6 @@ mod _aria; mod _binder; +mod _motion; mod accordion; mod anchor; mod auto_complete; @@ -30,6 +31,7 @@ mod label; mod layout; mod link; mod loading_bar; +mod locale; mod menu; mod message_bar; mod nav; @@ -57,6 +59,7 @@ mod theme; mod time_picker; mod toast; mod tooltip; +mod tree; mod upload; pub use accordion::*; @@ -89,6 +92,7 @@ pub use label::*; pub use layout::*; pub use link::*; pub use loading_bar::*; +pub use locale::*; pub use menu::*; pub use message_bar::*; pub use nav::*; @@ -117,6 +121,7 @@ pub use theme::*; pub use time_picker::*; pub use toast::*; pub use tooltip::*; +pub use tree::*; pub use upload::*; pub mod ssr { diff --git a/thaw/src/locale/locales/mod.rs b/thaw/src/locale/locales/mod.rs new file mode 100644 index 00000000..b185b67d --- /dev/null +++ b/thaw/src/locale/locales/mod.rs @@ -0,0 +1,26 @@ +use pure_rust_locales::Locale; + +pub trait LocaleExt { + fn locale(&self) -> Locale; + fn today(&self) -> &'static str; +} + +pub struct EnUS; +impl LocaleExt for EnUS { + fn locale(&self) -> Locale { + Locale::en_US + } + fn today(&self) -> &'static str { + "Today" + } +} + +pub struct FrFR; +impl LocaleExt for FrFR { + fn locale(&self) -> Locale { + Locale::fr_FR + } + fn today(&self) -> &'static str { + "Aujourd'hui" + } +} diff --git a/thaw/src/locale/mod.rs b/thaw/src/locale/mod.rs new file mode 100644 index 00000000..2833ce9b --- /dev/null +++ b/thaw/src/locale/mod.rs @@ -0,0 +1,86 @@ +use std::sync::Arc; + +use crate::ConfigInjection; +use chrono::Weekday; +use leptos::prelude::*; +use pure_rust_locales::locale_match; + +pub use pure_rust_locales::Locale; + +pub mod locales; + +use locales::LocaleExt; +#[derive(Clone)] +pub struct LocaleConfig(Arc); + +impl From for LocaleConfig { + fn from(value: T) -> Self { + Self(Arc::new(value)) + } +} + +impl Default for LocaleConfig { + fn default() -> Self { + LocaleConfig::from(locales::EnUS) + } +} + +impl LocaleConfig { + pub fn use_locale() -> ReadSignal { + use_context::() + .map_or_else(|| RwSignal::new(LocaleConfig::default()), |c| c.locale) + .split() + .0 + } + + pub fn use_rw_locale() -> RwSignal { + expect_context::().locale + } + + pub fn locale(&self) -> Locale { + self.0.locale() + } + + pub fn today(&self) -> &'static str { + self.0.today() + } + + pub(crate) fn months(&self) -> &'static [&'static str] { + locale_match!(self.locale() => LC_TIME::MON) + } + + pub(crate) fn month(&self, month: u8) -> &'static str { + self.months()[(month - 1) as usize] + } + + pub(crate) fn ab_months(&self) -> &'static [&'static str] { + locale_match!(self.locale() => LC_TIME::ABMON) + } + + pub(crate) fn ab_month(&self, month: u8) -> &'static str { + self.ab_months()[(month - 1) as usize] + } + + pub(crate) fn ab_days(&self) -> &'static [&'static str] { + locale_match!(self.locale() => LC_TIME::ABDAY) + } + + /// day is the number of day from sunday (sunday=0, monday=1, ...) + pub(crate) fn ab_day(&self, day: u8) -> &'static str { + self.ab_days()[day as usize] + } + + pub(crate) fn ab_weekday(&self, day: Weekday) -> &'static str { + self.ab_day(((day as u8) + 1) % 7) + } + + /// Return the first day of the week + pub(crate) fn first_weekday(&self) -> Weekday { + // the `LC_TIME::FIRST_WEEKDAY` value start at 1 for sunday, 2 for monday, ... + // the `Weekday` values start at 0 for monday, 1 for tuesday, ... + // this little operation convert between the two + let number_of_days_since_mondays = + ((locale_match!(self.locale() => LC_TIME::FIRST_WEEKDAY).unwrap_or(1) + 5) % 7) as u8; + number_of_days_since_mondays.try_into().unwrap() + } +} diff --git a/thaw/src/menu/docs/mod.md b/thaw/src/menu/docs/mod.md index a05d1759..cc5bad25 100644 --- a/thaw/src/menu/docs/mod.md +++ b/thaw/src/menu/docs/mod.md @@ -3,12 +3,13 @@ ```rust demo let toaster = ToasterInjection::expect_context(); -let on_select = move |key: String| { +let on_select = move |key: &str| { leptos::logging::warn!("{}", key); + let key = key.to_owned(); toaster.dispatch_toast(move || view! { - "key" + {key} }, Default::default()); @@ -41,7 +42,7 @@ view! { ```rust demo use leptos_meta::Style; -let on_select = move |value| leptos::logging::warn!("{}", value); +let on_select = move |value: &str| leptos::logging::warn!("{}", value); view! {