stratum_components/typography/
text.rs1use crate::common::{Size, merge_classes};
4use stratum_core::render::RenderOutput;
5
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
8pub enum FontWeight {
9 Light,
10 #[default]
11 Normal,
12 Medium,
13 Semibold,
14 Bold,
15}
16
17impl FontWeight {
18 pub fn class(&self) -> &'static str {
19 match self {
20 FontWeight::Light => "font-light",
21 FontWeight::Normal => "font-normal",
22 FontWeight::Medium => "font-medium",
23 FontWeight::Semibold => "font-semibold",
24 FontWeight::Bold => "font-bold",
25 }
26 }
27}
28
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
31pub enum TextColor {
32 #[default]
33 Default,
34 Muted,
35 Primary,
36 Destructive,
37}
38
39impl TextColor {
40 pub fn class(&self) -> &'static str {
41 match self {
42 TextColor::Default => "text-foreground",
43 TextColor::Muted => "text-muted-foreground",
44 TextColor::Primary => "text-primary",
45 TextColor::Destructive => "text-destructive",
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Default)]
52pub struct TextProps {
53 pub size: Size,
54 pub weight: FontWeight,
55 pub color: TextColor,
56 pub as_element: Option<String>,
57 pub class: Option<String>,
58}
59
60pub struct Text;
61
62impl Text {
63 pub fn classes(props: &TextProps) -> String {
64 let size_cls = match props.size {
65 Size::Xs => "text-xs",
66 Size::Sm => "text-sm",
67 Size::Md => "text-base",
68 Size::Lg => "text-lg",
69 Size::Xl => "text-xl",
70 };
71
72 let computed = format!(
73 "{} {} {}",
74 size_cls,
75 props.weight.class(),
76 props.color.class(),
77 );
78 merge_classes(&computed, &props.class)
79 }
80
81 pub fn render(props: &TextProps) -> RenderOutput {
82 let tag = props.as_element.as_deref().unwrap_or("p");
83 RenderOutput::new()
84 .with_tag(tag)
85 .with_class(Self::classes(props))
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn text_default_classes() {
95 let props = TextProps::default();
96 let classes = Text::classes(&props);
97 assert!(classes.contains("text-base"));
98 assert!(classes.contains("font-normal"));
99 assert!(classes.contains("text-foreground"));
100 }
101
102 #[test]
103 fn text_custom_size_weight() {
104 let props = TextProps {
105 size: Size::Lg,
106 weight: FontWeight::Bold,
107 ..Default::default()
108 };
109 let classes = Text::classes(&props);
110 assert!(classes.contains("text-lg"));
111 assert!(classes.contains("font-bold"));
112 }
113
114 #[test]
115 fn text_muted_color() {
116 let props = TextProps {
117 color: TextColor::Muted,
118 ..Default::default()
119 };
120 let classes = Text::classes(&props);
121 assert!(classes.contains("text-muted-foreground"));
122 }
123
124 #[test]
125 fn text_default_tag_is_p() {
126 let props = TextProps::default();
127 let output = Text::render(&props);
128 assert_eq!(output.effective_tag(), "p");
129 }
130
131 #[test]
132 fn text_custom_tag() {
133 let props = TextProps {
134 as_element: Some("span".to_string()),
135 ..Default::default()
136 };
137 let output = Text::render(&props);
138 assert_eq!(output.effective_tag(), "span");
139 }
140}