From d00e970a6a05fd755ce440ca246358be986f4c3f Mon Sep 17 00:00:00 2001 From: luoxiao <luoxiaozero@163.com> Date: Fri, 10 May 2024 21:51:35 +0800 Subject: [PATCH] refactor: avatar --- demo_markdown/docs/avatar/mod.md | 49 ++++++++++++- thaw/src/avatar/avatar.css | 54 ++++++++++++-- thaw/src/avatar/mod.rs | 118 +++++++++++++++++++++++-------- thaw/src/avatar/theme.rs | 20 ------ thaw/src/theme/color.rs | 8 +++ thaw/src/theme/common.rs | 16 ++++- thaw/src/theme/mod.rs | 5 +- 7 files changed, 208 insertions(+), 62 deletions(-) delete mode 100644 thaw/src/avatar/theme.rs diff --git a/demo_markdown/docs/avatar/mod.md b/demo_markdown/docs/avatar/mod.md index 117633c..4ad2e7e 100644 --- a/demo_markdown/docs/avatar/mod.md +++ b/demo_markdown/docs/avatar/mod.md @@ -1,11 +1,54 @@ # Avatar +```rust demo +view! { + <Avatar /> +} +``` + +### Name + +```rust demo +view! { + <Avatar name="Ashley McCarthy" /> +} +``` + +### Image + +```rust demo +view! { + <Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg" /> +} +``` + +### Shape + +```rust demo +view! { + <Avatar shape=AvatarShape::Square /> +} +``` + +### Size + ```rust demo view! { <Space> - <Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg"/> - <Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg" round=true/> - <Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg" size=50/> + <Avatar initials="16" size=16 /> + <Avatar initials="20" size=20 /> + <Avatar initials="24" size=24 /> + <Avatar initials="28" size=28 /> + <Avatar initials="32" size=32 /> + <Avatar initials="36" size=36 /> + <Avatar initials="40" size=40 /> + <Avatar initials="48" size=48 /> + <Avatar initials="56" size=56 /> + <Avatar initials="64" size=64 /> + <Avatar initials="72" size=72 /> + <Avatar initials="96" size=96 /> + <Avatar initials="120" size=120 /> + <Avatar initials="128" size=128 /> </Space> } ``` diff --git a/thaw/src/avatar/avatar.css b/thaw/src/avatar/avatar.css index 68f131d..d10fb3d 100644 --- a/thaw/src/avatar/avatar.css +++ b/thaw/src/avatar/avatar.css @@ -1,13 +1,55 @@ .thaw-avatar { display: inline-block; - width: var(--thaw-size); - height: var(--thaw-size); - background-color: var(--thaw-background-color); - border-radius: var(--thaw-border-radius); + flex-shrink: 0; + position: relative; + vertical-align: middle; + border-radius: var(--borderRadiusCircular); + font-family: var(--fontFamilyBase); + font-weight: var(--fontWeightSemibold); + font-size: var(--fontSizeBase300); + width: 32px; + height: 32px; } -.thaw-avatar img { +.thaw-avatar--square { + border-radius: var(--borderRadiusMedium); +} + +.thaw-avatar__icon, +.thaw-avatar__initials { + position: absolute; + box-sizing: border-box; + top: 0px; + left: 0px; width: 100%; height: 100%; - border-radius: var(--thaw-border-radius); + line-height: 1; + border: var(--strokeWidthThin) solid var(--colorTransparentStroke); + display: flex; + align-items: center; + justify-content: center; + text-align: center; + user-select: none; + border-radius: inherit; + + background-color: var(--colorNeutralBackground6); + color: var(--colorNeutralForeground3); +} + +.thaw-avatar__icon { + font-size: 20px; +} + +.thaw-avatar__image { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + border-radius: inherit; + object-fit: cover; + vertical-align: top; + + background-color: var(--colorNeutralBackground6); + color: var(--colorNeutralForeground3); } diff --git a/thaw/src/avatar/mod.rs b/thaw/src/avatar/mod.rs index 1ea5270..ccb33ca 100644 --- a/thaw/src/avatar/mod.rs +++ b/thaw/src/avatar/mod.rs @@ -1,45 +1,107 @@ -mod theme; - -pub use theme::AvatarTheme; - -use crate::{use_theme, Theme}; use leptos::*; use thaw_components::OptionComp; -use thaw_utils::{class_list, mount_style, OptionalProp}; +use thaw_utils::{class_list, mount_style, OptionalProp, StoredMaybeSignal}; #[component] pub fn Avatar( - #[prop(optional, into)] src: Option<MaybeSignal<String>>, - #[prop(optional, into)] round: MaybeSignal<bool>, - #[prop(default = MaybeSignal::Static(30), into)] size: MaybeSignal<u16>, + /// The Avatar's image. + #[prop(optional, into)] + src: Option<MaybeSignal<String>>, + /// The name of the person or entity represented by this Avatar. + #[prop(optional, into)] + name: Option<MaybeSignal<String>>, + /// Custom initials. + #[prop(optional, into)] + initials: Option<MaybeSignal<String>>, + /// The avatar can have a circular or square shape. + #[prop(optional, into)] + shape: MaybeSignal<AvatarShape>, + /// Size of the avatar in pixels. + #[prop(optional, into)] size: Option<MaybeSignal<u8>>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>, ) -> impl IntoView { - let theme = use_theme(Theme::light); - let css_vars = create_memo(move |_| { - let mut css_vars = String::new(); - css_vars.push_str(&format!("--thaw-size: {}px;", size.get())); - css_vars.push_str(&format!( - "--thaw-border-radius: {};", - if round.get() { "50%" } else { "3px" } - )); - theme.with(|theme| { - css_vars.push_str(&format!( - "--thaw-background-color: {}", - theme.avatar.background_color - )); - }); - css_vars - }); mount_style("avatar", include_str!("./avatar.css")); + let style = move || { + let size = size?.get(); + + let mut style = format!("width: {0}px; height: {0}px;", size); + + if let Some(font_size) = match size { + 0..=24 => Some(100), + 25..=28 => Some(200), + 29..=40 => None, + 41..=56 => Some(400), + 57..=96 => Some(500), + 97..=128 => Some(600), + _ => Some(600), + } { + style.push_str(&format!("font-size: var(--fontSizeBase{});", font_size)) + } + + Some(style) + }; + + let is_show_default_icon = src.is_none() && initials.is_none() && name.is_none(); + let name: Option<StoredMaybeSignal<_>> = name.map(|n| n.into()); + view! { <span - class=class_list!["thaw-avatar", class.map(| c | move || c.get())] - style=move || css_vars.get() + class=class_list!["thaw-avatar", move || format!("thaw-avatar--{}", shape.get().as_str()), class.map(| c | move || c.get())] + style=style + role="img" + aria-label=move || name.as_ref().map(|n| n.get()) > + { + move || { + if let Some(initials) = initials.as_ref().map_or_else(|| name.as_ref().map(|n| initials_name(n.get())), |i| Some(i.get())) { + view! { + <span class="thaw-avatar__initials"> + {initials} + </span> + }.into() + } else { + None + } + } + } <OptionComp value=src let:src> - <img src=move || src.get()/> + <img src=move || src.get() class="thaw-avatar__image"/> </OptionComp> + { + if is_show_default_icon { + view! { + <span aria-hidden="true" class="thaw-avatar__icon"> + <svg fill="currentColor" aria-hidden="true" width="1em" height="1em" viewBox="0 0 20 20"> + <path d="M10 2a4 4 0 1 0 0 8 4 4 0 0 0 0-8ZM7 6a3 3 0 1 1 6 0 3 3 0 0 1-6 0Zm-2 5a2 2 0 0 0-2 2c0 1.7.83 2.97 2.13 3.8A9.14 9.14 0 0 0 10 18c1.85 0 3.58-.39 4.87-1.2A4.35 4.35 0 0 0 17 13a2 2 0 0 0-2-2H5Zm-1 2a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1c0 1.3-.62 2.28-1.67 2.95A8.16 8.16 0 0 1 10 17a8.16 8.16 0 0 1-4.33-1.05A3.36 3.36 0 0 1 4 13Z" fill="currentColor"></path> + </svg> + </span> + }.into() + } else { + None + } + } </span> } } + +// TODO +fn initials_name(name: String) -> String { + name.split_at(2).0.to_string().to_ascii_uppercase() +} + +#[derive(Default, Clone)] +pub enum AvatarShape { + #[default] + Circular, + Square, +} + +impl AvatarShape { + pub fn as_str(&self) -> &'static str { + match self { + Self::Circular => "circular", + Self::Square => "square", + } + } +} diff --git a/thaw/src/avatar/theme.rs b/thaw/src/avatar/theme.rs deleted file mode 100644 index a335f79..0000000 --- a/thaw/src/avatar/theme.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::theme::ThemeMethod; - -#[derive(Clone)] -pub struct AvatarTheme { - pub background_color: String, -} - -impl ThemeMethod for AvatarTheme { - fn light() -> Self { - Self { - background_color: "#f7f7f7".into(), - } - } - - fn dark() -> Self { - Self { - background_color: "#424245".into(), - } - } -} diff --git a/thaw/src/theme/color.rs b/thaw/src/theme/color.rs index c7ba47c..fa259d9 100644 --- a/thaw/src/theme/color.rs +++ b/thaw/src/theme/color.rs @@ -6,6 +6,7 @@ pub struct ColorTheme { pub color_neutral_background_1: String, pub color_neutral_background_1_hover: String, pub color_neutral_background_1_pressed: String, + pub color_neutral_background_6: String, pub color_neutral_foreground_disabled: String, pub color_neutral_foreground_1: String, @@ -16,6 +17,7 @@ pub struct ColorTheme { pub color_neutral_foreground_2_pressed: String, pub color_neutral_foreground_2_brand_hover: String, pub color_neutral_foreground_2_brand_pressed: String, + pub color_neutral_foreground_3: String, pub color_neutral_foreground_4: String, pub color_neutral_foreground_on_brand: String, @@ -47,6 +49,8 @@ impl ColorTheme { color_neutral_background_1: "#fff".into(), color_neutral_background_1_hover: "#f5f5f5".into(), color_neutral_background_1_pressed: "#e0e0e0".into(), + color_neutral_background_6: "#e6e6e6".into(), + color_neutral_foreground_disabled: "#bdbdbd".into(), color_neutral_foreground_1: "#242424".into(), color_neutral_foreground_1_hover: "#242424".into(), @@ -56,6 +60,7 @@ impl ColorTheme { color_neutral_foreground_2_pressed: "#242424".into(), color_neutral_foreground_2_brand_hover: "#0f6cbd".into(), color_neutral_foreground_2_brand_pressed: "#115ea3".into(), + color_neutral_foreground_3: "#616161".into(), color_neutral_foreground_4: "#707070".into(), color_neutral_foreground_on_brand: "#fff".into(), @@ -87,6 +92,8 @@ impl ColorTheme { color_neutral_background_1: "#292929".into(), color_neutral_background_1_hover: "#3d3d3d".into(), color_neutral_background_1_pressed: "#1f1f1f".into(), + color_neutral_background_6: "#333333".into(), + color_neutral_foreground_disabled: "#5c5c5c".into(), color_neutral_foreground_1: "#fff".into(), color_neutral_foreground_1_hover: "#fff".into(), @@ -96,6 +103,7 @@ impl ColorTheme { color_neutral_foreground_2_pressed: "#fff".into(), color_neutral_foreground_2_brand_hover: "#479ef5".into(), color_neutral_foreground_2_brand_pressed: "#2886de".into(), + color_neutral_foreground_3: "#adadad".into(), color_neutral_foreground_4: "#999999".into(), color_neutral_foreground_on_brand: "#fff".into(), diff --git a/thaw/src/theme/common.rs b/thaw/src/theme/common.rs index 828ead4..a430588 100644 --- a/thaw/src/theme/common.rs +++ b/thaw/src/theme/common.rs @@ -20,9 +20,16 @@ pub struct CommonTheme { pub color_error_hover: String, pub color_error_active: String, + pub font_size_base_100: String, pub font_size_base_200: String, pub font_size_base_300: String, pub font_size_base_400: String, + pub font_size_base_500: String, + pub font_size_base_600: String, + pub font_size_base_700: String, + pub font_size_base_800: String, + pub font_size_base_900: String, + pub font_size_base_1000: String, pub line_height_base_200: String, pub line_height_base_300: String, @@ -37,7 +44,7 @@ pub struct CommonTheme { pub border_radius_none: String, pub border_radius_medium: String, pub border_radius_circular: String, - + pub spacing_horizontal_x_x_s: String, pub spacing_horizontal_s_nudge: String, pub spacing_horizontal_s: String, @@ -81,9 +88,16 @@ impl CommonTheme { color_error_hover: "".into(), color_error_active: "".into(), + font_size_base_100: "10px".into(), font_size_base_200: "12px".into(), font_size_base_300: "14px".into(), font_size_base_400: "16px".into(), + font_size_base_500: "20px".into(), + font_size_base_600: "24px".into(), + font_size_base_700: "28px".into(), + font_size_base_800: "32px".into(), + font_size_base_900: "40px".into(), + font_size_base_1000: "60px".into(), line_height_base_200: "16px".into(), line_height_base_300: "20px".into(), diff --git a/thaw/src/theme/mod.rs b/thaw/src/theme/mod.rs index e270590..ff9943f 100644 --- a/thaw/src/theme/mod.rs +++ b/thaw/src/theme/mod.rs @@ -4,7 +4,7 @@ mod common; use self::common::CommonTheme; use crate::{ mobile::{NavBarTheme, TabbarTheme}, - AlertTheme, AnchorTheme, AutoCompleteTheme, AvatarTheme, BackTopTheme, BreadcrumbTheme, + AlertTheme, AnchorTheme, AutoCompleteTheme, BackTopTheme, BreadcrumbTheme, CalendarTheme, CollapseTheme, ColorPickerTheme, DatePickerTheme, InputTheme, MenuTheme, MessageTheme, PopoverTheme, ProgressTheme, ScrollbarTheme, SelectTheme, SkeletionTheme, SliderTheme, SpinnerTheme, SwitchTheme, TableTheme, TagTheme, TimePickerTheme, TypographyTheme, @@ -29,7 +29,6 @@ pub struct Theme { pub alert: AlertTheme, pub skeletion: SkeletionTheme, pub tag: TagTheme, - pub avatar: AvatarTheme, pub message: MessageTheme, pub select: SelectTheme, pub slider: SliderTheme, @@ -65,7 +64,6 @@ impl Theme { alert: AlertTheme::light(), skeletion: SkeletionTheme::light(), tag: TagTheme::light(), - avatar: AvatarTheme::light(), message: MessageTheme::light(), select: SelectTheme::light(), slider: SliderTheme::light(), @@ -100,7 +98,6 @@ impl Theme { alert: AlertTheme::dark(), skeletion: SkeletionTheme::dark(), tag: TagTheme::dark(), - avatar: AvatarTheme::dark(), message: MessageTheme::dark(), select: SelectTheme::dark(), slider: SliderTheme::dark(),