use azul_core::{
dom::{Dom, IdOrClass, IdOrClass::Class, IdOrClassVec},
resources::{ImageRef, OptionImageRef},
};
use azul_css::dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec};
use azul_css::{
props::{
basic::{color::ColorU, StyleFontSize},
layout::{LayoutDisplay, LayoutFlexDirection, LayoutJustifyContent, LayoutAlignItems, LayoutAlignSelf, LayoutFlexGrow, LayoutWidth, LayoutHeight, LayoutOverflow},
property::{CssProperty, *},
style::{StyleBackgroundContent, StyleBackgroundContentVec, StyleBorderTopLeftRadius, StyleBorderTopRightRadius, StyleBorderBottomLeftRadius, StyleBorderBottomRightRadius, StyleTextAlign, StyleTextColor},
},
AzString,
};
static AVATAR_CLASS: &[IdOrClass] = &[Class(AzString::from_const_str("__azul-native-avatar"))];
static AVATAR_IMAGE_CLASS: &[IdOrClass] =
&[Class(AzString::from_const_str("__azul-native-avatar-image"))];
static AVATAR_INITIALS_CLASS: &[IdOrClass] = &[Class(AzString::from_const_str(
"__azul-native-avatar-initials",
))];
const AVATAR_BG_COLOR: ColorU = ColorU { r: 108, g: 117, b: 125, a: 255 };
const AVATAR_TEXT_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 255 };
const AVATAR_BG_ITEMS: &[StyleBackgroundContent] =
&[StyleBackgroundContent::Color(AVATAR_BG_COLOR)];
const AVATAR_BG: StyleBackgroundContentVec =
StyleBackgroundContentVec::from_const_slice(AVATAR_BG_ITEMS);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
#[repr(C)]
pub enum AvatarSize {
Small,
#[default]
Medium,
Large,
}
impl AvatarSize {
#[allow(clippy::trivially_copy_pass_by_ref)] const fn diameter(&self) -> isize {
match self {
Self::Small => 24,
Self::Medium => 40,
Self::Large => 64,
}
}
#[allow(clippy::trivially_copy_pass_by_ref)] const fn radius(&self) -> isize {
self.diameter() / 2
}
#[allow(clippy::trivially_copy_pass_by_ref)] const fn font_size(&self) -> isize {
match self {
Self::Small => 11,
Self::Medium => 16,
Self::Large => 24,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C)]
pub struct Avatar {
pub image: OptionImageRef,
pub initials: AzString,
pub size: AvatarSize,
pub avatar_style: CssPropertyWithConditionsVec,
}
fn build_avatar_style(size: AvatarSize) -> CssPropertyWithConditionsVec {
let d = size.diameter();
let r = size.radius();
CssPropertyWithConditionsVec::from_vec(alloc::vec![
CssPropertyWithConditions::simple(CssProperty::const_display(LayoutDisplay::Flex)),
CssPropertyWithConditions::simple(CssProperty::const_flex_direction(
LayoutFlexDirection::Row,
)),
CssPropertyWithConditions::simple(CssProperty::const_justify_content(
LayoutJustifyContent::Center,
)),
CssPropertyWithConditions::simple(CssProperty::const_align_items(LayoutAlignItems::Center)),
CssPropertyWithConditions::simple(CssProperty::align_self(LayoutAlignSelf::Start)),
CssPropertyWithConditions::simple(CssProperty::const_flex_grow(LayoutFlexGrow::const_new(
0,
))),
CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(d))),
CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(d))),
CssPropertyWithConditions::simple(CssProperty::const_border_top_left_radius(
StyleBorderTopLeftRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_top_right_radius(
StyleBorderTopRightRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_bottom_left_radius(
StyleBorderBottomLeftRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_bottom_right_radius(
StyleBorderBottomRightRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_overflow_x(LayoutOverflow::Hidden)),
CssPropertyWithConditions::simple(CssProperty::const_overflow_y(LayoutOverflow::Hidden)),
CssPropertyWithConditions::simple(CssProperty::const_font_size(StyleFontSize::const_px(
size.font_size(),
))),
CssPropertyWithConditions::simple(CssProperty::const_text_align(StyleTextAlign::Center)),
CssPropertyWithConditions::simple(CssProperty::const_text_color(StyleTextColor {
inner: AVATAR_TEXT_COLOR,
})),
CssPropertyWithConditions::simple(CssProperty::const_background_content(AVATAR_BG)),
])
}
fn build_image_style(size: AvatarSize) -> CssPropertyWithConditionsVec {
let d = size.diameter();
let r = size.radius();
CssPropertyWithConditionsVec::from_vec(alloc::vec![
CssPropertyWithConditions::simple(CssProperty::const_flex_grow(LayoutFlexGrow::const_new(
0,
))),
CssPropertyWithConditions::simple(CssProperty::const_width(LayoutWidth::const_px(d))),
CssPropertyWithConditions::simple(CssProperty::const_height(LayoutHeight::const_px(d))),
CssPropertyWithConditions::simple(CssProperty::const_border_top_left_radius(
StyleBorderTopLeftRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_top_right_radius(
StyleBorderTopRightRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_bottom_left_radius(
StyleBorderBottomLeftRadius::const_px(r),
)),
CssPropertyWithConditions::simple(CssProperty::const_border_bottom_right_radius(
StyleBorderBottomRightRadius::const_px(r),
)),
])
}
impl Avatar {
#[inline]
#[must_use] pub fn create(initials: AzString) -> Self {
Self {
image: None.into(),
initials,
size: AvatarSize::Medium,
avatar_style: build_avatar_style(AvatarSize::Medium),
}
}
#[inline]
#[must_use] pub fn create_with_image(image: ImageRef) -> Self {
Self {
image: Some(image).into(),
initials: AzString::from_const_str(""),
size: AvatarSize::Medium,
avatar_style: build_avatar_style(AvatarSize::Medium),
}
}
#[inline]
pub fn set_image(&mut self, image: ImageRef) {
self.image = Some(image).into();
}
#[inline]
#[must_use] pub fn with_image(mut self, image: ImageRef) -> Self {
self.set_image(image);
self
}
#[inline]
pub fn set_size(&mut self, size: AvatarSize) {
self.size = size;
self.avatar_style = build_avatar_style(size);
}
#[inline]
#[must_use] pub fn with_size(mut self, size: AvatarSize) -> Self {
self.set_size(size);
self
}
#[inline]
#[must_use] pub fn swap_with_default(&mut self) -> Self {
let mut s = Self::create(AzString::from_const_str(""));
core::mem::swap(&mut s, self);
s
}
#[inline]
#[must_use] pub fn dom(self) -> Dom {
let size = self.size;
let child = match self.image.into_option() {
Some(image) => Dom::create_image(image)
.with_ids_and_classes(IdOrClassVec::from_const_slice(AVATAR_IMAGE_CLASS))
.with_css_props(build_image_style(size)),
None => Dom::create_text(self.initials)
.with_ids_and_classes(IdOrClassVec::from_const_slice(AVATAR_INITIALS_CLASS)),
};
Dom::create_div()
.with_ids_and_classes(IdOrClassVec::from_const_slice(AVATAR_CLASS))
.with_css_props(self.avatar_style)
.with_children(alloc::vec![child].into())
}
}
impl Default for Avatar {
fn default() -> Self {
Self::create(AzString::from_const_str(""))
}
}
impl From<Avatar> for Dom {
fn from(a: Avatar) -> Self {
a.dom()
}
}