textiler_core/theme/
typography.rs1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
4
5use serde::{Deserialize, Deserializer};
6use yew::html::IntoPropValue;
7
8use crate::style::Size;
9use crate::theme::sx::SxValue;
10use crate::Sx;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum TypographyLevel {
15 H1,
16 H2,
17 H3,
18 H4,
19 Title { size: Size },
20 Body { size: Size },
21 Custom(String),
22 Star,
23}
24
25impl<'de> Deserialize<'de> for TypographyLevel {
26 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
27 where
28 D: Deserializer<'de>,
29 {
30 let as_string = <&'de str as Deserialize<'de>>::deserialize(deserializer)?;
31 let parsed = TypographyLevel::from(as_string);
32 Ok(parsed)
33 }
34}
35
36impl Display for TypographyLevel {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 match self {
39 TypographyLevel::H1 => {
40 write!(f, "h1")
41 }
42 TypographyLevel::H2 => {
43 write!(f, "h2")
44 }
45 TypographyLevel::H3 => {
46 write!(f, "h3")
47 }
48 TypographyLevel::H4 => {
49 write!(f, "h4")
50 }
51 TypographyLevel::Title { size } => {
52 write!(f, "title-{size}")
53 }
54 TypographyLevel::Body { size } => {
55 write!(f, "body-{size}")
56 }
57 TypographyLevel::Custom(s) => {
58 write!(f, "{s}")
59 }
60 TypographyLevel::Star => {
61 write!(f, "*")
62 }
63 }
64 }
65}
66
67impl IntoPropValue<TypographyLevel> for &str {
68 fn into_prop_value(self) -> TypographyLevel {
69 TypographyLevel::from(self)
70 }
71}
72
73impl From<&str> for TypographyLevel {
74 fn from(value: &str) -> Self {
75 match value {
76 "*" => TypographyLevel::Star,
77 "h1" => TypographyLevel::H1,
78 "h2" => TypographyLevel::H2,
79 "h3" => TypographyLevel::H3,
80 "h4" => TypographyLevel::H4,
81 title if title.starts_with("title-") => {
82 let size = title.strip_prefix("title-").unwrap();
83 let size = IntoPropValue::<Size>::into_prop_value(size);
84 TypographyLevel::Title { size }
85 }
86 body if body.starts_with("body-") => {
87 let size = body.strip_prefix("body-").unwrap();
88 let size = IntoPropValue::<Size>::into_prop_value(size);
89 TypographyLevel::Body { size }
90 }
91 other => TypographyLevel::Custom(other.to_string()),
92 }
93 }
94}
95
96impl Default for TypographyLevel {
97 fn default() -> Self {
98 TypographyLevel::Body { size: Size::Md }
99 }
100}
101
102#[derive(Debug, Default, Clone, PartialEq)]
104pub struct TypographyScale {
105 levels: HashMap<TypographyLevel, LevelScale>,
107}
108
109impl TypographyScale {
110 pub fn new<I: IntoIterator<Item = (TypographyLevel, LevelScale)>>(levels: I) -> Self {
112 Self {
113 levels: levels.into_iter().collect(),
114 }
115 }
116
117 pub fn at(&self, level: &TypographyLevel) -> Option<Sx> {
119 self.scale(level)
120 .map(LevelScale::sx)
121 .map(|sx| match self.scale(&TypographyLevel::Star) {
122 None => sx,
123 Some(star_sx) => {
124 let star_sx = star_sx.sx();
125 sx.merge(star_sx)
126 }
127 })
128 }
129
130 pub fn scale(&self, level: &TypographyLevel) -> Option<&LevelScale> {
132 self.levels.get(level)
133 }
134
135 pub fn scale_mut(&mut self, level: &TypographyLevel) -> Option<&mut LevelScale> {
137 self.levels.get_mut(level)
138 }
139
140 pub fn insert(&mut self, level: TypographyLevel, level_sx: LevelScale) {
142 let _ = self.levels.insert(level, level_sx);
143 }
144}
145
146impl<'a> IntoIterator for &'a TypographyScale {
147 type Item = (&'a TypographyLevel, &'a LevelScale);
148 type IntoIter = <&'a HashMap<TypographyLevel, LevelScale> as IntoIterator>::IntoIter;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.levels.iter()
152 }
153}
154
155#[derive(Debug, Clone, PartialEq)]
157pub struct LevelScale {
158 sx: Sx,
159}
160
161impl LevelScale {
162 pub fn new(sx: Sx) -> Self {
164 Self { sx }
165 }
166
167 pub fn sx(&self) -> Sx {
169 self.sx.clone()
170 }
171}
172
173impl From<Sx> for LevelScale {
174 fn from(value: Sx) -> Self {
175 Self::new(value)
176 }
177}