use crate::hooks::Hooks;
use crate::style::{BorderStyle, Color};
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Theme {
pub accent: Color,
pub surface: Color,
pub border: Color,
pub muted: Color,
pub foreground: Color,
pub danger: Color,
pub success: Color,
pub border_style: BorderStyle,
}
impl Default for Theme {
fn default() -> Self {
Theme {
accent: Color::Rgb(124, 58, 237),
surface: Color::Rgb(30, 30, 36),
border: Color::DarkGrey,
muted: Color::Rgb(140, 140, 150),
foreground: Color::White,
danger: Color::Rgb(220, 38, 38),
success: Color::Rgb(34, 197, 94),
border_style: BorderStyle::Round,
}
}
}
impl<'a> Hooks<'a> {
pub fn use_theme(&mut self) -> Theme {
self.use_context::<Theme>().map(|t| *t).unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::component::Component;
use crate::element::Element;
use crate::props::TextProps;
use crate::testing::TestTerminal;
struct Reader;
#[derive(Clone, PartialEq, Default)]
struct ReaderProps;
impl Component for Reader {
type Props = ReaderProps;
fn render(_: &ReaderProps, hooks: &mut Hooks) -> Element {
let theme = hooks.use_theme();
Element::text(TextProps {
content: format!("{:?}", theme.accent),
..Default::default()
})
}
}
#[tokio::test]
async fn falls_back_to_default_without_a_provider() {
let t = TestTerminal::new(40, 1, Element::component::<Reader>(ReaderProps)).unwrap();
assert!(
t.frame_text()
.contains(&format!("{:?}", Theme::default().accent))
);
}
#[tokio::test]
async fn nearest_provided_theme_wins() {
let custom = Theme {
accent: Color::Rgb(1, 2, 3),
..Theme::default()
};
let el = Element::provider(custom, vec![Element::component::<Reader>(ReaderProps)]);
let t = TestTerminal::new(40, 1, el).unwrap();
assert!(t.frame_text().contains("Rgb(1, 2, 3)"));
}
}