mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
refactor: spinner
This commit is contained in:
parent
bba3f00657
commit
18bfea1731
8 changed files with 250 additions and 85 deletions
|
@ -167,6 +167,14 @@ pub(crate) fn gen_menu_data() -> Vec<MenuGroupOption> {
|
|||
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<MenuGroupOption> {
|
|||
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<MenuGroupOption> {
|
|||
value: "/components/progress".into(),
|
||||
label: "Progress".into(),
|
||||
},
|
||||
MenuItemOption {
|
||||
value: "/components/spinner".into(),
|
||||
label: "Spinner".into(),
|
||||
},
|
||||
MenuItemOption {
|
||||
value: "/components/skeleton".into(),
|
||||
label: "Skeleton".into(),
|
||||
|
|
|
@ -10,11 +10,15 @@ view! {
|
|||
|
||||
```rust demo
|
||||
view! {
|
||||
<Space>
|
||||
<Spinner size=SpinnerSize::Tiny/>
|
||||
<Spinner size=SpinnerSize::Small/>
|
||||
<Spinner size=SpinnerSize::Medium/>
|
||||
<Spinner size=SpinnerSize::Large/>
|
||||
<Space vertical=true>
|
||||
<Spinner size=SpinnerSize::ExtraTiny label="Extra Tiny Spinner"/>
|
||||
<Spinner size=SpinnerSize::Tiny label="Tiny Spinner"/>
|
||||
<Spinner size=SpinnerSize::ExtraSmall label="Extra Small Spinner"/>
|
||||
<Spinner size=SpinnerSize::Small label="Small Spinner"/>
|
||||
<Spinner size=SpinnerSize::Medium label="Medium Spinner"/>
|
||||
<Spinner size=SpinnerSize::Large label="Large Spinner"/>
|
||||
<Spinner size=SpinnerSize::ExtraLarge label="Extra Large Spinner"/>
|
||||
<Spinner size=SpinnerSize::Huge label="Huge Spinner"/>
|
||||
</Space>
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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<MaybeSignal<String>>,
|
||||
#[prop(optional, into)] size: MaybeSignal<SpinnerSize>,
|
||||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
/// An optional label for the Spinner.
|
||||
#[prop(optional, into)]
|
||||
label: MaybeProp<String>,
|
||||
/// The size of the spinner.
|
||||
#[prop(optional, into)]
|
||||
size: MaybeSignal<SpinnerSize>,
|
||||
) -> 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! {
|
||||
<div
|
||||
class=class_list!["thaw-spinner", class.map(| c | move || c.get())]
|
||||
style=move || css_vars.get()
|
||||
></div>
|
||||
class=class_list!["thaw-spinner", move || format!("thaw-spinner--{}", size.get().as_str()), class]
|
||||
role="progressbar"
|
||||
aria-labelledby=labelledby
|
||||
>
|
||||
<span class="thaw-spinner__spinner">
|
||||
<span class="thaw-spinner__spinner-tail"></span>
|
||||
</span>
|
||||
{
|
||||
move || {
|
||||
if let Some(label) = label.get() {
|
||||
view! {
|
||||
<label class="thaw-spinner__label" id=id.get_value()>
|
||||
{label}
|
||||
</label>
|
||||
}.into()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Add table
Reference in a new issue