use blinc_layout::prelude::*;
use blinc_theme::{ColorToken, ThemeState, TypographyTokens};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum LabelSize {
Small,
#[default]
Medium,
Large,
}
impl LabelSize {
fn font_size(&self, typography: &TypographyTokens) -> f32 {
match self {
LabelSize::Small => typography.text_xs,
LabelSize::Medium => typography.text_sm,
LabelSize::Large => typography.text_base,
}
}
}
pub struct Label {
inner: Div,
}
impl Label {
pub fn new(text: impl Into<String>) -> Self {
Self::with_config(LabelConfig::new(text.into()))
}
fn with_config(config: LabelConfig) -> Self {
let theme = ThemeState::get();
let typography = theme.typography();
let text_color = if config.disabled {
theme.color(ColorToken::TextTertiary)
} else {
theme.color(ColorToken::TextPrimary)
};
let font_size = config.size.font_size(&typography);
let disabled_class = if config.disabled {
"cn-label--disabled"
} else {
""
};
let inner = if config.required {
let required_color = theme.color(ColorToken::Error);
div()
.class("cn-label")
.class(disabled_class)
.flex_row()
.h_fit()
.gap(2.0)
.child(
text(&config.text)
.size(font_size)
.color(text_color)
.medium(),
)
.child(text("*").size(font_size).color(required_color).medium())
} else {
div().class("cn-label").class(disabled_class).h_fit().child(
text(&config.text)
.size(font_size)
.color(text_color)
.medium(),
)
};
Self { inner }
}
pub fn class(mut self, name: impl Into<String>) -> Self {
self.inner = self.inner.class(name);
self
}
pub fn id(mut self, id: &str) -> Self {
self.inner = self.inner.id(id);
self
}
}
#[derive(Clone)]
pub(crate) struct LabelConfig {
text: String,
size: LabelSize,
required: bool,
disabled: bool,
}
impl LabelConfig {
fn new(text: String) -> Self {
Self {
text,
size: LabelSize::default(),
required: false,
disabled: false,
}
}
}
pub struct LabelBuilder {
pub(crate) config: LabelConfig,
built: std::cell::OnceCell<Label>,
}
impl LabelBuilder {
pub fn new(text: impl Into<String>) -> Self {
Self {
config: LabelConfig::new(text.into()),
built: std::cell::OnceCell::new(),
}
}
fn get_or_build(&self) -> &Label {
self.built
.get_or_init(|| Label::with_config(self.config.clone()))
}
pub fn size(mut self, size: LabelSize) -> Self {
self.config.size = size;
self
}
pub fn required(mut self) -> Self {
self.config.required = true;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.config.disabled = disabled;
self
}
pub fn build_component(self) -> Label {
Label::with_config(self.config)
}
}
impl ElementBuilder for Label {
fn build(&self, tree: &mut blinc_layout::tree::LayoutTree) -> blinc_layout::tree::LayoutNodeId {
self.inner.build(tree)
}
fn render_props(&self) -> blinc_layout::element::RenderProps {
self.inner.render_props()
}
fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
self.inner.children_builders()
}
fn element_type_id(&self) -> blinc_layout::div::ElementTypeId {
self.inner.element_type_id()
}
fn element_classes(&self) -> &[String] {
self.inner.element_classes()
}
}
impl ElementBuilder for LabelBuilder {
fn build(&self, tree: &mut blinc_layout::tree::LayoutTree) -> blinc_layout::tree::LayoutNodeId {
self.get_or_build().build(tree)
}
fn render_props(&self) -> blinc_layout::element::RenderProps {
self.get_or_build().render_props()
}
fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
self.get_or_build().children_builders()
}
fn element_type_id(&self) -> blinc_layout::div::ElementTypeId {
self.get_or_build().element_type_id()
}
fn element_classes(&self) -> &[String] {
self.get_or_build().element_classes()
}
}
pub fn label(text: impl Into<String>) -> LabelBuilder {
LabelBuilder::new(text)
}
#[cfg(test)]
mod tests {
use super::*;
use blinc_theme::TypographyTokens;
#[test]
fn test_label_size_values() {
let typography = TypographyTokens::default();
assert_eq!(LabelSize::Small.font_size(&typography), typography.text_xs);
assert_eq!(LabelSize::Medium.font_size(&typography), typography.text_sm);
assert_eq!(
LabelSize::Large.font_size(&typography),
typography.text_base
);
}
#[test]
fn test_label_builder() {
let lbl = label("Username").required().size(LabelSize::Large);
assert!(lbl.config.required);
assert_eq!(lbl.config.size, LabelSize::Large);
}
}