Skip to main content

teksilo_widgets/styles/
recipe_date_edit_style.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Default `DateEditStyle` impl + date-edit family design tokens.
5//!
6//! Design tokens for the date-edit family live as `pub const`s on this
7//! module. The four widgets (`DateEdit`, `TimeEdit`, `DateRangeEdit`,
8//! `DateTimeEdit`) read them directly when building the calendar / clock
9//! trigger icon and the segmented-field separators.
10//!
11//! In IntUI the trigger sits inside `TextInput`'s trailing slot, so the
12//! default `make_body` is a passthrough — the widget pre-assembles the
13//! complete body before handing it to the recipe. Custom recipes can
14//! wrap the body or surround it with siblings.
15
16use teksilo_core::build_context::BuildContext;
17use teksilo_core::styles::density::{dp, spacing};
18use teksilo_core::styles::{DateEditStyle, DateEditStyleConfig, SharedDateEditStyle};
19use teksilo_core::widget_id::WidgetId;
20use teksilo_tokens::{InputTokens, TargetRole};
21
22// ─── IntUI design tokens for the date-edit family ──────────────────
23
24/// Width of the trailing calendar / clock trigger button slot.
25pub const CALENDAR_BUTTON_WIDTH: f32 = 24.0;
26/// Edge length of the calendar / clock trigger glyph.
27pub const CALENDAR_ICON_SIZE: f32 = 14.0;
28/// Gap between adjacent segments (visual separator characters sit in
29/// this gap). Used by the segmented date/time editors.
30pub const SEGMENT_GAP: f32 = 1.0;
31
32/// Configurable dimension bundle for `RecipeDateEditStyle`.
33///
34/// The `Default` impl reads the module-level `pub const`s, so calling
35/// `DateEditRecipe::default()` is equivalent to the pre-recipe behaviour.
36#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct DateEditRecipe {
38    /// Width of the trailing calendar / clock trigger button slot.
39    pub calendar_button_width: f32,
40    /// Edge length of the calendar / clock trigger glyph.
41    pub calendar_icon_size: f32,
42    /// Gap between adjacent segments (visual separator characters sit in
43    /// this gap). Used by the segmented date/time editors.
44    pub segment_gap: f32,
45}
46
47impl DateEditRecipe {
48    /// This recipe's dimensions resolved against a density's [`InputTokens`].
49    ///
50    /// [`Default`] is `for_tokens(&InputTokens::default())` — the Compact
51    /// ladder — so the shipped values below are the Compact column by
52    /// construction and cannot drift from it.
53    pub fn for_tokens(tokens: &InputTokens) -> Self {
54        Self {
55            calendar_button_width: dp(CALENDAR_BUTTON_WIDTH, TargetRole::Target, tokens),
56            calendar_icon_size: CALENDAR_ICON_SIZE,
57            segment_gap: spacing(SEGMENT_GAP, tokens),
58        }
59    }
60}
61
62impl Default for DateEditRecipe {
63    fn default() -> Self {
64        Self::for_tokens(&InputTokens::default())
65    }
66}
67
68/// Default `DateEditStyle` shipped with Teksilo. Currently a
69/// passthrough — IntUI's date-edit chrome lives inside `TextInput`,
70/// which is already themed. Custom styles can wrap the body to add
71/// extra siblings (clear button, status icon, etc.).
72#[derive(Debug, Default, Clone, Copy)]
73pub struct RecipeDateEditStyle {
74    pub recipe: DateEditRecipe,
75}
76
77impl RecipeDateEditStyle {
78    pub fn new(recipe: DateEditRecipe) -> Self {
79        Self { recipe }
80    }
81
82    /// This style with every dimension resolved against a density's
83    /// [`InputTokens`], as `RecipeDateEditStyle::for_tokens(&ctx.theme().input)` at
84    /// the widget's own build site.
85    ///
86    /// [`Default`] is the `TargetDensity::Compact` projection, so a Compact
87    /// tree gets exactly the values this module documents.
88    pub fn for_tokens(tokens: &InputTokens) -> Self {
89        Self {
90            recipe: DateEditRecipe::for_tokens(tokens),
91        }
92    }
93}
94
95impl DateEditStyle for RecipeDateEditStyle {
96    fn make_body(&self, cfg: &DateEditStyleConfig, _ctx: &mut BuildContext) -> WidgetId {
97        cfg.body
98    }
99}
100
101/// Convenience for callers that need the active style (per-call
102/// override → theme slot → `RecipeDateEditStyle`).
103pub fn resolve_date_edit_style(
104    override_: &Option<SharedDateEditStyle>,
105    ctx: &BuildContext,
106) -> SharedDateEditStyle {
107    if let Some(s) = override_.clone() {
108        return s;
109    }
110    ctx.theme_signal()
111        .get()
112        .style_slots
113        .date_edit
114        .clone()
115        .unwrap_or_else(|| {
116            std::rc::Rc::new(RecipeDateEditStyle::for_tokens(&ctx.theme().input))
117                as SharedDateEditStyle
118        })
119}