ratatui_kit/components/theme/
mod.rs1mod palette;
14pub use palette::*;
15
16use std::marker::PhantomData;
17
18use ratatui::style::Style;
19use ratatui_kit_macros::Props;
20
21use crate::{AnyElement, Component, ComponentUpdater, Context, Hooks, UseContext};
22
23pub trait ComponentTheme: Clone + Default + 'static {
28 fn from_palette(palette: &Palette) -> Self;
31}
32
33pub(crate) fn resolve_style(theme: Style, override_style: Option<Style>) -> Style {
38 theme.patch(override_style.unwrap_or_default())
39}
40
41mod private {
42 pub trait Sealed {}
43 impl Sealed for crate::Hooks<'_, '_> {}
44}
45
46pub trait UseTheme: private::Sealed {
50 fn use_palette(&self) -> Palette;
52 fn use_component_theme<T: ComponentTheme>(&self) -> T;
54}
55
56impl UseTheme for Hooks<'_, '_> {
57 fn use_palette(&self) -> Palette {
58 self.try_use_context::<Palette>()
60 .map(|p| *p)
61 .unwrap_or_default()
62 }
63
64 fn use_component_theme<T: ComponentTheme>(&self) -> T {
65 match self.try_use_context::<T>() {
66 Some(t) => t.clone(),
67 None => T::from_palette(&self.use_palette()),
68 }
69 }
70}
71
72impl ComponentUpdater<'_, '_> {
73 pub fn use_palette(&self) -> Palette {
75 self.get_context::<Palette>()
76 .map(|p| *p)
77 .unwrap_or_default()
78 }
79
80 pub fn use_component_theme<T: ComponentTheme>(&self) -> T {
84 match self.get_context::<T>() {
85 Some(t) => t.clone(),
86 None => T::from_palette(&self.use_palette()),
87 }
88 }
89}
90
91#[derive(Default, Props)]
96pub struct PaletteProviderProps<'a> {
97 pub children: Vec<AnyElement<'a>>,
99 pub palette: Palette,
101}
102
103pub struct PaletteProvider;
105
106impl Component for PaletteProvider {
107 type Props<'a> = PaletteProviderProps<'a>;
108
109 fn new(_props: &Self::Props<'_>) -> Self {
110 Self
111 }
112
113 fn update(
114 &mut self,
115 props: &mut Self::Props<'_>,
116 _hooks: Hooks,
117 updater: &mut ComponentUpdater,
118 ) {
119 updater.set_transparent_layout(true);
120 let mut ctx = Context::owned(props.palette);
121 updater.update_children(props.children.iter_mut(), Some(ctx.borrow()));
122 }
123}
124
125#[derive(Props)]
130pub struct ThemeOverrideProps<'a, T>
131where
132 T: ComponentTheme,
133{
134 pub children: Vec<AnyElement<'a>>,
136 pub theme: T,
138}
139
140impl<T> Default for ThemeOverrideProps<'_, T>
141where
142 T: ComponentTheme,
143{
144 fn default() -> Self {
145 Self {
146 children: Vec::new(),
147 theme: T::default(),
148 }
149 }
150}
151
152pub struct ThemeOverride<T>(PhantomData<fn() -> T>);
155
156impl<T> Component for ThemeOverride<T>
157where
158 T: ComponentTheme,
159{
160 type Props<'a> = ThemeOverrideProps<'a, T>;
161
162 fn new(_props: &Self::Props<'_>) -> Self {
163 Self(PhantomData)
164 }
165
166 fn update(
167 &mut self,
168 props: &mut Self::Props<'_>,
169 _hooks: Hooks,
170 updater: &mut ComponentUpdater,
171 ) {
172 updater.set_transparent_layout(true);
173 let mut ctx = Context::owned(props.theme.clone());
174 updater.update_children(props.children.iter_mut(), Some(ctx.borrow()));
175 }
176}