From 18bfea1731c37a25ff4562e9268c88b3281e8904 Mon Sep 17 00:00:00 2001 From: luoxiao Date: Tue, 4 Jun 2024 14:18:15 +0800 Subject: [PATCH] refactor: spinner --- demo/src/pages/components.rs | 16 +-- demo_markdown/docs/spinner/mod.md | 14 ++- thaw/src/spinner/mod.rs | 78 +++++++------ thaw/src/spinner/spinner.css | 181 +++++++++++++++++++++++++++--- thaw/src/spinner/theme.rs | 20 ---- thaw/src/theme/color.rs | 8 ++ thaw/src/theme/common.rs | 8 ++ thaw/src/theme/mod.rs | 10 +- 8 files changed, 250 insertions(+), 85 deletions(-) delete mode 100644 thaw/src/spinner/theme.rs diff --git a/demo/src/pages/components.rs b/demo/src/pages/components.rs index f0a66e2..0094390 100644 --- a/demo/src/pages/components.rs +++ b/demo/src/pages/components.rs @@ -167,6 +167,14 @@ pub(crate) fn gen_menu_data() -> Vec { value: "/components/icon".into(), label: "Icon".into(), }, + MenuItemOption { + value: "/components/spin-button".into(), + label: "Spin Button".into(), + }, + MenuItemOption { + value: "/components/spinner".into(), + label: "Spinner".into(), + }, MenuItemOption { value: "/components/tag".into(), label: "Tag".into(), @@ -175,10 +183,6 @@ pub(crate) fn gen_menu_data() -> Vec { value: "/components/text".into(), label: "Text".into(), }, - MenuItemOption { - value: "/components/spin-button".into(), - label: "Spin Button".into(), - }, MenuItemOption { value: "/components/auto-complete".into(), label: "Auto Complete".into(), @@ -279,10 +283,6 @@ pub(crate) fn gen_menu_data() -> Vec { value: "/components/progress".into(), label: "Progress".into(), }, - MenuItemOption { - value: "/components/spinner".into(), - label: "Spinner".into(), - }, MenuItemOption { value: "/components/skeleton".into(), label: "Skeleton".into(), diff --git a/demo_markdown/docs/spinner/mod.md b/demo_markdown/docs/spinner/mod.md index f3f2f9c..3e9c507 100644 --- a/demo_markdown/docs/spinner/mod.md +++ b/demo_markdown/docs/spinner/mod.md @@ -10,11 +10,15 @@ view! { ```rust demo view! { - - - - - + + + + + + + + + } ``` diff --git a/thaw/src/spinner/mod.rs b/thaw/src/spinner/mod.rs index 42c9e88..8b68b5f 100644 --- a/thaw/src/spinner/mod.rs +++ b/thaw/src/spinner/mod.rs @@ -1,58 +1,72 @@ -mod theme; - -pub use theme::SpinnerTheme; - -use crate::{theme::use_theme, Theme}; use leptos::*; -use thaw_utils::{class_list, mount_style, OptionalProp}; +use thaw_utils::{class_list, mount_style}; #[derive(Default, Clone)] pub enum SpinnerSize { + ExtraTiny, Tiny, + ExtraSmall, Small, #[default] Medium, Large, + ExtraLarge, + Huge, } impl SpinnerSize { - fn theme_height(&self, theme: &Theme) -> String { + pub fn as_str(&self) -> &'static str { match self { - SpinnerSize::Tiny => theme.common.height_tiny.clone(), - SpinnerSize::Small => theme.common.height_small.clone(), - SpinnerSize::Medium => theme.common.height_medium.clone(), - SpinnerSize::Large => theme.common.height_large.clone(), + SpinnerSize::ExtraTiny => "extra-tiny", + SpinnerSize::Tiny => "tiny", + SpinnerSize::ExtraSmall => "extra-small", + SpinnerSize::Small => "small", + SpinnerSize::Medium => "medium", + SpinnerSize::Large => "large", + SpinnerSize::ExtraLarge => "extra-large", + SpinnerSize::Huge => "huge", } } } #[component] pub fn Spinner( - #[prop(optional, into)] class: OptionalProp>, - #[prop(optional, into)] size: MaybeSignal, + #[prop(optional, into)] class: MaybeProp, + /// An optional label for the Spinner. + #[prop(optional, into)] + label: MaybeProp, + /// The size of the spinner. + #[prop(optional, into)] + size: MaybeSignal, ) -> impl IntoView { mount_style("spinner", include_str!("./spinner.css")); - 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-height: {};", - size.get().theme_height(theme) - )); - css_vars.push_str(&format!( - "--thaw-background-color: {};", - &theme.spinner.background_color - )); - css_vars.push_str(&format!("--thaw-color: {};", &theme.common.color_primary)); - }); - css_vars - }); + let id = StoredValue::new(uuid::Uuid::new_v4().to_string()); + + let spinner_label = label.clone(); + let labelledby = move || spinner_label.with(|_| true).map(|_| id.get_value()); view! {
+ class=class_list!["thaw-spinner", move || format!("thaw-spinner--{}", size.get().as_str()), class] + role="progressbar" + aria-labelledby=labelledby + > + + + + { + move || { + if let Some(label) = label.get() { + view! { + + }.into() + } else { + None + } + } + } + } } diff --git a/thaw/src/spinner/spinner.css b/thaw/src/spinner/spinner.css index 79ab833..d84d433 100644 --- a/thaw/src/spinner/spinner.css +++ b/thaw/src/spinner/spinner.css @@ -1,17 +1,172 @@ .thaw-spinner { - border-color: var(--thaw-color); - border-left-color: var(--thaw-background-color); - border-style: solid; - border-radius: 100px; - border-width: 2px; - width: var(--thaw-height); - height: var(--thaw-height); - outline: 1px solid transparent; - animation: 1s linear 0s infinite normal none running spin; - box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + line-height: 0; + gap: 8px; + overflow: hidden; } -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } +.thaw-spinner__spinner { + width: 32px; + height: 32px; + position: relative; + flex-shrink: 0; + mask-image: radial-gradient( + closest-side, + transparent calc(100% - var(--thaw-spinner--stroke-width) - 1px), + white calc(100% - var(--thaw-spinner--stroke-width)) calc(100% - 1px), + transparent 100% + ); + background-color: var(--colorBrandStroke2Contrast); + color: var(--colorBrandStroke1); + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-name: thaw-spinner; + + --thaw-spinner--stroke-width: var(--strokeWidthThicker); +} + +.thaw-spinner--extra-tiny > .thaw-spinner__spinner { + --thaw-spinner--stroke-width: var(--strokeWidthThick); + width: 16px; + height: 16px; +} + +.thaw-spinner--tiny > .thaw-spinner__spinner { + --thaw-spinner--stroke-width: var(--strokeWidthThick); + width: 20px; + height: 20px; +} + +.thaw-spinner--extra-small > .thaw-spinner__spinner { + --thaw-spinner--stroke-width: var(--strokeWidthThick); + width: 24px; + height: 24px; +} + +.thaw-spinner--small > .thaw-spinner__spinner { + --thaw-spinner--stroke-width: var(--strokeWidthThick); + width: 28px; + height: 28px; +} + +.thaw-spinner--medium > .thaw-spinner__spinner { + width: 32px; + height: 32px; +} + +.thaw-spinner--large > .thaw-spinner__spinner { + width: 36px; + height: 36px; +} + +.thaw-spinner--extra-large > .thaw-spinner__spinner { + width: 40px; + height: 40px; +} + +.thaw-spinner--huge > .thaw-spinner__spinner { + --thaw-spinner--stroke-width: var(--strokeWidthThickest); + width: 44px; + height: 44px; +} + +@keyframes thaw-spinner { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.thaw-spinner__spinner-tail { + position: absolute; + display: block; + width: 100%; + height: 100%; + mask-image: conic-gradient(transparent 105deg, white 105deg); + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-timing-function: var(--curveEasyEase); + animation-name: thaw-spinner-tail; +} + +@keyframes thaw-spinner-tail { + 0% { + transform: rotate(-135deg); + } + 50% { + transform: rotate(0deg); + } + 100% { + transform: rotate(225deg); + } +} + +.thaw-spinner__spinner-tail::before, +.thaw-spinner__spinner-tail::after { + content: ""; + position: absolute; + display: block; + width: 100%; + height: 100%; + animation: inherit; + background-image: conic-gradient(currentcolor 135deg, transparent 135deg); +} + +.thaw-spinner__spinner-tail::before { + animation-name: thaw-spinner-tail-before; +} + +@keyframes thaw-spinner-tail-before { + 0% { + transform: rotate(0deg); + } + 50% { + transform: rotate(105deg); + } + 100% { + transform: rotate(0deg); + } +} + +.thaw-spinner__spinner-tail::after { + animation-name: thaw-spinner-tail-after; +} + +@keyframes thaw-spinner-tail-after { + 0% { + transform: rotate(0deg); + } + 50% { + transform: rotate(225deg); + } + 100% { + transform: rotate(0deg); + } +} + +.thaw-spinner__label { + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + font-size: var(--fontSizeBase400); + line-height: var(--lineHeightBase400); + color: var(--colorNeutralForeground1); +} + +.thaw-spinner--extra-tiny > .thaw-spinner__label, +.thaw-spinner--tiny > .thaw-spinner__label, +.thaw-spinner--extra-small > .thaw-spinner__label, +.thaw-spinner--small > .thaw-spinner__label { + font-weight: var(--fontWeightRegular); + font-size: var(--fontSizeBase300); + line-height: var(--lineHeightBase300); +} + +.thaw-spinner--huge > .thaw-spinner__label { + font-size: var(--fontSizeBase500); + line-height: var(--lineHeightBase500); } diff --git a/thaw/src/spinner/theme.rs b/thaw/src/spinner/theme.rs deleted file mode 100644 index e7e7bb2..0000000 --- a/thaw/src/spinner/theme.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::theme::ThemeMethod; - -#[derive(Clone)] -pub struct SpinnerTheme { - pub background_color: String, -} - -impl ThemeMethod for SpinnerTheme { - fn light() -> Self { - Self { - background_color: "#0000000a".into(), - } - } - - fn dark() -> Self { - Self { - background_color: "#2b2f31".into(), - } - } -} diff --git a/thaw/src/theme/color.rs b/thaw/src/theme/color.rs index d859ffd..f5148bf 100644 --- a/thaw/src/theme/color.rs +++ b/thaw/src/theme/color.rs @@ -50,6 +50,9 @@ pub struct ColorTheme { pub color_brand_background: String, pub color_brand_background_hover: String, pub color_brand_background_pressed: String, + pub color_brand_stroke_1: String, + pub color_brand_stroke_2_contrast: String, + pub color_subtle_background: String, pub color_subtle_background_hover: String, pub color_subtle_background_pressed: String, @@ -113,6 +116,8 @@ impl ColorTheme { color_brand_background: "#0f6cbd".into(), color_brand_background_hover: "#115ea3".into(), color_brand_background_pressed: "#0c3b5e".into(), + color_brand_stroke_1: "#0f6cbd".into(), + color_brand_stroke_2_contrast: "#b4d6fa".into(), color_subtle_background: "transparent".into(), color_subtle_background_hover: "#f5f5f5".into(), color_subtle_background_pressed: "#e0e0e0".into(), @@ -176,6 +181,9 @@ impl ColorTheme { color_brand_background: "#115ea3".into(), color_brand_background_hover: "#0f6cbd".into(), color_brand_background_pressed: "#0c3b5e".into(), + color_brand_stroke_1: "#479ef5".into(), + color_brand_stroke_2_contrast: "#0e4775".into(), + color_subtle_background: "transparent".into(), color_subtle_background_hover: "#383838".into(), color_subtle_background_pressed: "#2e2e2e".into(), diff --git a/thaw/src/theme/common.rs b/thaw/src/theme/common.rs index fd74955..2baa548 100644 --- a/thaw/src/theme/common.rs +++ b/thaw/src/theme/common.rs @@ -34,12 +34,16 @@ pub struct CommonTheme { pub line_height_base_200: String, pub line_height_base_300: String, pub line_height_base_400: String, + pub line_height_base_500: String, pub font_weight_regular: String, pub font_weight_semibold: String, pub font_weight_bold: String, pub stroke_width_thin: String, + pub stroke_width_thick: String, + pub stroke_width_thicker: String, + pub stroke_width_thickest: String, pub border_radius_none: String, pub border_radius_medium: String, @@ -107,12 +111,16 @@ impl CommonTheme { line_height_base_200: "16px".into(), line_height_base_300: "20px".into(), line_height_base_400: "22px".into(), + line_height_base_500: "28px".into(), font_weight_regular: "400".into(), font_weight_semibold: "600".into(), font_weight_bold: "700".into(), stroke_width_thin: "1px".into(), + stroke_width_thick: "2px".into(), + stroke_width_thicker: "3px".into(), + stroke_width_thickest: "4px".into(), border_radius_none: "0".into(), border_radius_medium: "4px".into(), diff --git a/thaw/src/theme/mod.rs b/thaw/src/theme/mod.rs index 639c291..e053a62 100644 --- a/thaw/src/theme/mod.rs +++ b/thaw/src/theme/mod.rs @@ -4,10 +4,9 @@ mod common; use self::common::CommonTheme; use crate::{ mobile::{NavBarTheme, TabbarTheme}, - AlertTheme, AnchorTheme, AutoCompleteTheme, BackTopTheme, CalendarTheme, - ColorPickerTheme, DatePickerTheme, InputTheme, MessageTheme, PopoverTheme, ProgressTheme, - ScrollbarTheme, SelectTheme, SkeletionTheme, SpinnerTheme, TableTheme, TimePickerTheme, - UploadTheme, + AlertTheme, AnchorTheme, AutoCompleteTheme, BackTopTheme, CalendarTheme, ColorPickerTheme, + DatePickerTheme, InputTheme, MessageTheme, PopoverTheme, ProgressTheme, ScrollbarTheme, + SelectTheme, SkeletionTheme, TableTheme, TimePickerTheme, UploadTheme, }; pub use color::ColorTheme; use leptos::*; @@ -28,7 +27,6 @@ pub struct Theme { pub skeletion: SkeletionTheme, pub message: MessageTheme, pub select: SelectTheme, - pub spinner: SpinnerTheme, pub upload: UploadTheme, pub nav_bar: NavBarTheme, pub tabbar: TabbarTheme, @@ -56,7 +54,6 @@ impl Theme { skeletion: SkeletionTheme::light(), message: MessageTheme::light(), select: SelectTheme::light(), - spinner: SpinnerTheme::light(), upload: UploadTheme::light(), nav_bar: NavBarTheme::light(), tabbar: TabbarTheme::light(), @@ -83,7 +80,6 @@ impl Theme { skeletion: SkeletionTheme::dark(), message: MessageTheme::dark(), select: SelectTheme::dark(), - spinner: SpinnerTheme::dark(), upload: UploadTheme::dark(), nav_bar: NavBarTheme::dark(), tabbar: TabbarTheme::dark(),