Skip to main content

dioxus_tw_components/components/
lightswitch.rs

1use crate::components::icon::*;
2use dioxus::prelude::*;
3use serde_json::Value;
4
5pub struct LightSwitchState {
6    active: bool,
7}
8
9impl LightSwitchState {
10    pub fn new(active: bool) -> Self {
11        Self { active }
12    }
13
14    fn toggle(&mut self) -> bool {
15        self.active = !self.active;
16        self.active
17    }
18
19    fn get_active(&self) -> bool {
20        self.active
21    }
22
23    fn set_active(&mut self, active: bool) {
24        self.active = active;
25    }
26}
27
28#[derive(Clone, PartialEq, Props)]
29pub struct LightSwitchProps {
30    #[props(extends = GlobalAttributes)]
31    attributes: Vec<Attribute>,
32
33    #[props(optional)]
34    pub onclick: Option<EventHandler<MouseEvent>>,
35
36    children: Element,
37}
38
39/// This component inserts/remove "dark" in the DOM on the div with id of main
40#[component]
41pub fn LightSwitch(mut props: LightSwitchProps) -> Element {
42    let default_classes = "lightswitch";
43    crate::setup_class_attribute(&mut props.attributes, default_classes);
44
45    let storage_dark_theme = use_resource(move || async move {
46        // Get dark_theme from localStorage, if not found add it to false
47        let mut eval = document::eval(
48            r#"
49            var dark_theme = localStorage.getItem("dark_theme");
50            if (dark_theme == null) {
51                var dark_theme = false;
52                localStorage.setItem("dark_theme", dark_theme);
53            } else {
54                let main_div = document.getElementById("main");
55                if (main_div != null && dark_theme == "true") {
56                    main_div.classList.add("dark");
57                }
58            }
59            dioxus.send(dark_theme);
60            "#,
61        );
62
63        eval.recv().await
64    });
65
66    let mut state = use_signal(|| LightSwitchState::new(false));
67
68    use_effect(move || {
69        if let Some(Ok(Value::String(str))) = &*storage_dark_theme.read_unchecked() {
70            let parsed_str = str.parse::<bool>();
71            if let Ok(bool_value) = parsed_str {
72                state.write().set_active(bool_value);
73            }
74        };
75    });
76
77    let mut onclick = move |_| {
78        let dark_theme = state.write().toggle();
79        spawn(async move {
80            // Change value of dark_theme in localStorage
81            let eval = document::eval(
82                r#"
83                const dark_theme = await dioxus.recv();
84                localStorage.setItem("dark_theme", dark_theme);
85                let main_div = document.getElementById("main");
86                if (main != null) {
87                    if (dark_theme) {
88                        main_div.classList.add("dark");
89                    } else {
90                        main_div.classList.remove("dark");
91                    }
92                }
93                "#,
94            );
95            let _ = eval.send(dark_theme);
96        });
97    };
98
99    let icon = if state.read().get_active() {
100        rsx! {
101            Icon { icon: Icons::DarkMode }
102        }
103    } else {
104        rsx! {
105            Icon { icon: Icons::LightMode }
106        }
107    };
108
109    rsx! {
110        button {
111            r#type: "button",
112            onclick: move |e| {
113                match props.onclick {
114                    Some(p) => {
115                        state.write().toggle();
116                        p.call(e);
117                    }
118                    None => onclick(e),
119                }
120            },
121            ..props.attributes,
122            {icon}
123        }
124    }
125}