use dioxus::prelude::*;
use crate::types::Color;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum Theme {
#[default]
Light,
Dark,
}
impl Theme {
pub fn toggle(self) -> Self {
match self {
Theme::Light => Theme::Dark,
Theme::Dark => Theme::Light,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Theme::Light => "light",
Theme::Dark => "dark",
}
}
}
impl std::fmt::Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Clone, PartialEq, Props)]
pub struct ThemeProviderProps {
pub theme: Signal<Theme>,
}
#[component]
pub fn ThemeProvider(props: ThemeProviderProps) -> Element {
let theme_signal = props.theme;
use_effect(move || {
let theme = *theme_signal.read();
let theme_str = theme.as_str();
document::eval(&format!(
"document.documentElement.setAttribute('data-bs-theme', '{theme_str}');"
));
});
rsx! {}
}
#[derive(Clone, PartialEq, Props)]
pub struct ThemeToggleProps {
pub theme: Signal<Theme>,
#[props(default)]
pub color: Option<Color>,
#[props(default)]
pub class: String,
}
#[component]
pub fn ThemeToggle(props: ThemeToggleProps) -> Element {
let theme = *props.theme.read();
let mut theme_signal = props.theme;
let icon = match theme {
Theme::Light => "moon-stars",
Theme::Dark => "sun",
};
let label = match theme {
Theme::Light => "Switch to dark mode",
Theme::Dark => "Switch to light mode",
};
let btn_class = match &props.color {
Some(c) => format!("btn btn-outline-{c}"),
None => "btn btn-outline-secondary".to_string(),
};
let full_class = if props.class.is_empty() {
btn_class
} else {
format!("{btn_class} {}", props.class)
};
rsx! {
button {
class: "{full_class}",
r#type: "button",
title: "{label}",
"aria-label": "{label}",
onclick: move |_| {
let new_theme = theme.toggle();
theme_signal.set(new_theme);
},
i { class: "bi bi-{icon}" }
}
}
}