refactor: avatar

This commit is contained in:
luoxiao 2024-05-10 21:51:35 +08:00
parent 824ca9c4b1
commit d00e970a6a
7 changed files with 208 additions and 62 deletions

View file

@ -2,10 +2,53 @@
```rust demo ```rust demo
view! { view! {
<Space> <Avatar />
}
```
### Name
```rust demo
view! {
<Avatar name="Ashley McCarthy" />
}
```
### Image
```rust demo
view! {
<Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg" /> <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/> ```
### Shape
```rust demo
view! {
<Avatar shape=AvatarShape::Square />
}
```
### Size
```rust demo
view! {
<Space>
<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> </Space>
} }
``` ```

View file

@ -1,13 +1,55 @@
.thaw-avatar { .thaw-avatar {
display: inline-block; display: inline-block;
width: var(--thaw-size); flex-shrink: 0;
height: var(--thaw-size); position: relative;
background-color: var(--thaw-background-color); vertical-align: middle;
border-radius: var(--thaw-border-radius); 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%; width: 100%;
height: 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);
} }

View file

@ -1,45 +1,107 @@
mod theme;
pub use theme::AvatarTheme;
use crate::{use_theme, Theme};
use leptos::*; use leptos::*;
use thaw_components::OptionComp; use thaw_components::OptionComp;
use thaw_utils::{class_list, mount_style, OptionalProp}; use thaw_utils::{class_list, mount_style, OptionalProp, StoredMaybeSignal};
#[component] #[component]
pub fn Avatar( pub fn Avatar(
#[prop(optional, into)] src: Option<MaybeSignal<String>>, /// The Avatar's image.
#[prop(optional, into)] round: MaybeSignal<bool>, #[prop(optional, into)]
#[prop(default = MaybeSignal::Static(30), into)] size: MaybeSignal<u16>, 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>>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
) -> impl IntoView { ) -> 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")); 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! { view! {
<span <span
class=class_list!["thaw-avatar", class.map(| c | move || c.get())] class=class_list!["thaw-avatar", move || format!("thaw-avatar--{}", shape.get().as_str()), class.map(| c | move || c.get())]
style=move || css_vars.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> <OptionComp value=src let:src>
<img src=move || src.get()/> <img src=move || src.get() class="thaw-avatar__image"/>
</OptionComp> </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> </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",
}
}
}

View file

@ -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(),
}
}
}

View file

@ -6,6 +6,7 @@ pub struct ColorTheme {
pub color_neutral_background_1: String, pub color_neutral_background_1: String,
pub color_neutral_background_1_hover: String, pub color_neutral_background_1_hover: String,
pub color_neutral_background_1_pressed: String, pub color_neutral_background_1_pressed: String,
pub color_neutral_background_6: String,
pub color_neutral_foreground_disabled: String, pub color_neutral_foreground_disabled: String,
pub color_neutral_foreground_1: 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_pressed: String,
pub color_neutral_foreground_2_brand_hover: String, pub color_neutral_foreground_2_brand_hover: String,
pub color_neutral_foreground_2_brand_pressed: 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_4: String,
pub color_neutral_foreground_on_brand: String, pub color_neutral_foreground_on_brand: String,
@ -47,6 +49,8 @@ impl ColorTheme {
color_neutral_background_1: "#fff".into(), color_neutral_background_1: "#fff".into(),
color_neutral_background_1_hover: "#f5f5f5".into(), color_neutral_background_1_hover: "#f5f5f5".into(),
color_neutral_background_1_pressed: "#e0e0e0".into(), color_neutral_background_1_pressed: "#e0e0e0".into(),
color_neutral_background_6: "#e6e6e6".into(),
color_neutral_foreground_disabled: "#bdbdbd".into(), color_neutral_foreground_disabled: "#bdbdbd".into(),
color_neutral_foreground_1: "#242424".into(), color_neutral_foreground_1: "#242424".into(),
color_neutral_foreground_1_hover: "#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_pressed: "#242424".into(),
color_neutral_foreground_2_brand_hover: "#0f6cbd".into(), color_neutral_foreground_2_brand_hover: "#0f6cbd".into(),
color_neutral_foreground_2_brand_pressed: "#115ea3".into(), color_neutral_foreground_2_brand_pressed: "#115ea3".into(),
color_neutral_foreground_3: "#616161".into(),
color_neutral_foreground_4: "#707070".into(), color_neutral_foreground_4: "#707070".into(),
color_neutral_foreground_on_brand: "#fff".into(), color_neutral_foreground_on_brand: "#fff".into(),
@ -87,6 +92,8 @@ impl ColorTheme {
color_neutral_background_1: "#292929".into(), color_neutral_background_1: "#292929".into(),
color_neutral_background_1_hover: "#3d3d3d".into(), color_neutral_background_1_hover: "#3d3d3d".into(),
color_neutral_background_1_pressed: "#1f1f1f".into(), color_neutral_background_1_pressed: "#1f1f1f".into(),
color_neutral_background_6: "#333333".into(),
color_neutral_foreground_disabled: "#5c5c5c".into(), color_neutral_foreground_disabled: "#5c5c5c".into(),
color_neutral_foreground_1: "#fff".into(), color_neutral_foreground_1: "#fff".into(),
color_neutral_foreground_1_hover: "#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_pressed: "#fff".into(),
color_neutral_foreground_2_brand_hover: "#479ef5".into(), color_neutral_foreground_2_brand_hover: "#479ef5".into(),
color_neutral_foreground_2_brand_pressed: "#2886de".into(), color_neutral_foreground_2_brand_pressed: "#2886de".into(),
color_neutral_foreground_3: "#adadad".into(),
color_neutral_foreground_4: "#999999".into(), color_neutral_foreground_4: "#999999".into(),
color_neutral_foreground_on_brand: "#fff".into(), color_neutral_foreground_on_brand: "#fff".into(),

View file

@ -20,9 +20,16 @@ pub struct CommonTheme {
pub color_error_hover: String, pub color_error_hover: String,
pub color_error_active: String, pub color_error_active: String,
pub font_size_base_100: String,
pub font_size_base_200: String, pub font_size_base_200: String,
pub font_size_base_300: String, pub font_size_base_300: String,
pub font_size_base_400: 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_200: String,
pub line_height_base_300: String, pub line_height_base_300: String,
@ -81,9 +88,16 @@ impl CommonTheme {
color_error_hover: "".into(), color_error_hover: "".into(),
color_error_active: "".into(), color_error_active: "".into(),
font_size_base_100: "10px".into(),
font_size_base_200: "12px".into(), font_size_base_200: "12px".into(),
font_size_base_300: "14px".into(), font_size_base_300: "14px".into(),
font_size_base_400: "16px".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_200: "16px".into(),
line_height_base_300: "20px".into(), line_height_base_300: "20px".into(),

View file

@ -4,7 +4,7 @@ mod common;
use self::common::CommonTheme; use self::common::CommonTheme;
use crate::{ use crate::{
mobile::{NavBarTheme, TabbarTheme}, mobile::{NavBarTheme, TabbarTheme},
AlertTheme, AnchorTheme, AutoCompleteTheme, AvatarTheme, BackTopTheme, BreadcrumbTheme, AlertTheme, AnchorTheme, AutoCompleteTheme, BackTopTheme, BreadcrumbTheme,
CalendarTheme, CollapseTheme, ColorPickerTheme, DatePickerTheme, InputTheme, MenuTheme, CalendarTheme, CollapseTheme, ColorPickerTheme, DatePickerTheme, InputTheme, MenuTheme,
MessageTheme, PopoverTheme, ProgressTheme, ScrollbarTheme, SelectTheme, SkeletionTheme, MessageTheme, PopoverTheme, ProgressTheme, ScrollbarTheme, SelectTheme, SkeletionTheme,
SliderTheme, SpinnerTheme, SwitchTheme, TableTheme, TagTheme, TimePickerTheme, TypographyTheme, SliderTheme, SpinnerTheme, SwitchTheme, TableTheme, TagTheme, TimePickerTheme, TypographyTheme,
@ -29,7 +29,6 @@ pub struct Theme {
pub alert: AlertTheme, pub alert: AlertTheme,
pub skeletion: SkeletionTheme, pub skeletion: SkeletionTheme,
pub tag: TagTheme, pub tag: TagTheme,
pub avatar: AvatarTheme,
pub message: MessageTheme, pub message: MessageTheme,
pub select: SelectTheme, pub select: SelectTheme,
pub slider: SliderTheme, pub slider: SliderTheme,
@ -65,7 +64,6 @@ impl Theme {
alert: AlertTheme::light(), alert: AlertTheme::light(),
skeletion: SkeletionTheme::light(), skeletion: SkeletionTheme::light(),
tag: TagTheme::light(), tag: TagTheme::light(),
avatar: AvatarTheme::light(),
message: MessageTheme::light(), message: MessageTheme::light(),
select: SelectTheme::light(), select: SelectTheme::light(),
slider: SliderTheme::light(), slider: SliderTheme::light(),
@ -100,7 +98,6 @@ impl Theme {
alert: AlertTheme::dark(), alert: AlertTheme::dark(),
skeletion: SkeletionTheme::dark(), skeletion: SkeletionTheme::dark(),
tag: TagTheme::dark(), tag: TagTheme::dark(),
avatar: AvatarTheme::dark(),
message: MessageTheme::dark(), message: MessageTheme::dark(),
select: SelectTheme::dark(), select: SelectTheme::dark(),
slider: SliderTheme::dark(), slider: SliderTheme::dark(),