textiler_core/components/
typography.rs

1//! The typography system allows for complex writing instrumentation
2
3use std::collections::HashMap;
4use std::fmt::Display;
5
6use yew::html::IntoPropValue;
7use yew::{
8    classes, function_component, html, html_nested, Children, ContextProvider, Html, Properties,
9};
10
11use crate::style::{Color, Size, Variant};
12use crate::system::{ColorProp, StylingBox, VariantProp};
13use crate::theme::typography::TypographyLevel;
14use crate::Sx;
15
16pub type TypographyLevelMapping = HashMap<TypographyLevel, String>;
17
18fn default_level_mapping() -> TypographyLevelMapping {
19    use Size::*;
20    use TypographyLevel::*;
21    [
22        (H1, "h1"),
23        (H2, "h2"),
24        (H3, "h3"),
25        (H4, "h4"),
26        (Title { size: Xs }, "p"),
27        (Title { size: Sm }, "p"),
28        (Title { size: Md }, "p"),
29        (Title { size: Lg }, "p"),
30        (Title { size: Xl }, "p"),
31        (Body { size: Xs }, "span"),
32        (Body { size: Sm }, "p"),
33        (Body { size: Md }, "p"),
34        (Body { size: Lg }, "p"),
35        (Body { size: Xl }, "p"),
36    ]
37    .into_iter()
38    .map(|(k, v)| (k, v.to_string()))
39    .collect()
40}
41
42#[derive(Debug, Clone, Properties, PartialEq)]
43pub struct TypographyProps {
44    #[prop_or_else(|| "".to_string())]
45    pub component: String,
46    #[prop_or_default]
47    pub sx: Sx,
48    #[prop_or_default]
49    pub level: TypographyLevel,
50    #[prop_or_default]
51    pub variant: VariantProp,
52    #[prop_or_default]
53    pub color: ColorProp,
54    #[prop_or_else(default_level_mapping)]
55    pub mapping: TypographyLevelMapping,
56    #[prop_or_default]
57    pub children: Children,
58}
59
60#[function_component]
61pub fn Typography(props: &TypographyProps) -> Html {
62    let context = yew::use_context::<TypographyContext>();
63    let TypographyProps {
64        component,
65        sx,
66        children,
67        level,
68        mapping,
69        variant,
70        color,
71        ..
72    } = props;
73
74    let component = yew::use_memo(
75        (
76            component.clone(),
77            context.clone(),
78            level.clone(),
79            mapping.clone(),
80        ),
81        |(comp, ctx, level, mapping)| {
82            if comp.is_empty() {
83                let default = &mapping[level];
84
85                return if ctx.is_some() {
86                    if default == "p" {
87                        "span".to_string()
88                    } else {
89                        default.clone()
90                    }
91                } else {
92                    default.clone()
93                };
94            } else {
95                comp.clone()
96            }
97        },
98    );
99
100    let classes = classes!("typography", level.to_string());
101    let inner = html_nested! {
102        <StylingBox {variant} {color} class={classes} sx={sx.clone()} component={(*component).clone()}>
103            { for props.children.iter() }
104        </StylingBox>
105    };
106
107    match context {
108        Some(context) => {
109            html! {
110                {inner}
111            }
112        }
113        None => {
114            html! {
115                <ContextProvider<TypographyContext> context={TypographyContext::default()}>
116                    {inner}
117                </ContextProvider<TypographyContext>>
118            }
119        }
120    }
121}
122
123#[derive(Debug, Clone, PartialEq, Default)]
124struct TypographyContext {}