textiler_core/components/
system.rs

1//! Contains system components. Not meant for general use
2
3use std::ops::Deref;
4
5use strum::IntoEnumIterator;
6use web_sys::HtmlElement;
7use yew::{classes, Classes, function_component, html, Html, Properties, use_effect_with};
8use yew::html::{Children, ImplicitClone, IntoPropValue};
9
10use crate::hooks::{use_sx, use_theme};
11use crate::style::{Color, Variant};
12use crate::theme::sx::Sx;
13
14#[derive(Default, Debug, Clone, PartialEq, Properties)]
15pub struct StylingBoxProps {
16    #[prop_or_default]
17    pub sx: Sx,
18    #[prop_or_default]
19    pub variant: VariantProp,
20    #[prop_or_default]
21    pub color: ColorProp,
22    #[prop_or_else(|| "div".to_string())]
23    pub component: String,
24    #[prop_or_else(|| classes!("box"))]
25    pub class: Classes,
26    #[prop_or_default]
27    pub disabled: bool,
28    #[prop_or_default]
29    pub children: Children,
30}
31
32#[function_component]
33pub fn StylingBox(props: &StylingBoxProps) -> Html {
34    let sx = use_sx(props.sx.clone());
35    let theme = use_theme();
36    let mut classes = classes!(sx);
37    classes.extend(props.class.clone());
38    classes.extend(classes!(format!("{}-system", theme.prefix)));
39
40    let html_ref = yew::use_node_ref();
41    {
42        let html_ref = html_ref.clone();
43        use_effect_with(
44            (props.variant, props.color, props.disabled, html_ref),
45            |(variant, color, disabled, node)| {
46                info!("setting attributes for color and variant for node: {node:?}");
47                let element: HtmlElement = node
48                    .cast::<HtmlElement>()
49                    .expect("should be an html element");
50                if let Some(variant) = **variant {
51                    element
52                        .set_attribute("variant", &variant.to_string())
53                        .expect("could not set variant attribute");
54                }
55                if let Some(color) = **color {
56                    element
57                        .set_attribute("color", &color.to_string())
58                        .expect("could not set color attribute");
59                }
60                if *disabled {
61                    element
62                        .set_attribute("disabled", "")
63                        .expect("could not set color attribute");
64                } else {
65                    let _ = element.remove_attribute("disabled");
66                }
67            },
68        );
69    }
70
71    html! {
72        <@{props.component.clone()} class={classes} ref={html_ref}>
73            { for props.children.clone() }
74        </@>
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Default)]
79pub struct VariantProp(Option<Variant>);
80
81impl IntoPropValue<VariantProp> for &str {
82    fn into_prop_value(self) -> VariantProp {
83        for var in Variant::iter() {
84            if var.as_ref().to_lowercase() == self {
85                return VariantProp(Some(var));
86            }
87        }
88        panic!("no variant named {}", self)
89    }
90}
91
92impl IntoPropValue<VariantProp> for Variant {
93    fn into_prop_value(self) -> VariantProp {
94        VariantProp(Some(self))
95    }
96}
97
98impl Deref for VariantProp {
99    type Target = Option<Variant>;
100
101    fn deref(&self) -> &Self::Target {
102        &self.0
103    }
104}
105
106impl ImplicitClone for VariantProp {}
107
108#[derive(Debug, Clone, Copy, PartialEq, Default)]
109pub struct ColorProp(Option<Color>);
110
111impl IntoPropValue<ColorProp> for &str {
112    fn into_prop_value(self) -> ColorProp {
113        for var in Color::iter() {
114            if var.as_ref().to_lowercase() == self {
115                return ColorProp(Some(var));
116            }
117        }
118        panic!("no Color named {}", self)
119    }
120}
121
122impl IntoPropValue<ColorProp> for Color {
123    fn into_prop_value(self) -> ColorProp {
124        ColorProp(Some(self))
125    }
126}
127
128impl Deref for ColorProp {
129    type Target = Option<Color>;
130
131    fn deref(&self) -> &Self::Target {
132        &self.0
133    }
134}
135
136impl ImplicitClone for ColorProp {}
137
138#[cfg(test)]
139mod tests {
140    use yew::ServerRenderer;
141
142    use super::*;
143
144    #[tokio::test]
145    async fn styled_box() {
146        let renderer = ServerRenderer::<StylingBox>::new();
147        let s = renderer.render().await;
148        println!("{s}");
149    }
150}