Skip to main content

fret_ui_kit/recipes/
surface.rs

1use fret_core::{Color, Px};
2use fret_ui::Theme;
3
4use crate::style::PaddingRefinement;
5use crate::style::{ColorFallback, MetricFallback};
6use crate::{ChromeRefinement, ColorRef, MetricRef};
7
8#[derive(Debug, Clone, Copy)]
9pub struct SurfaceTokenKeys {
10    pub padding_x: Option<&'static str>,
11    pub padding_y: Option<&'static str>,
12    pub radius: Option<&'static str>,
13    pub border_width: Option<&'static str>,
14    pub bg: Option<&'static str>,
15    pub border: Option<&'static str>,
16}
17
18#[derive(Debug, Clone, Copy)]
19pub struct ResolvedSurfaceChrome {
20    pub padding_x: Px,
21    pub padding_y: Px,
22    pub radius: Px,
23    pub border_width: Px,
24    pub background: Color,
25    pub border_color: Color,
26}
27
28pub fn resolve_surface_chrome(
29    theme: &Theme,
30    style: &ChromeRefinement,
31    keys: SurfaceTokenKeys,
32) -> ResolvedSurfaceChrome {
33    let default_padding_x = MetricRef::Token {
34        key: "component.surface.padding_x",
35        fallback: MetricFallback::ThemePaddingSm,
36    };
37    let default_padding_y = MetricRef::Token {
38        key: "component.surface.padding_y",
39        fallback: MetricFallback::ThemePaddingSm,
40    };
41    let default_radius = MetricRef::Token {
42        key: "component.surface.radius",
43        fallback: MetricFallback::ThemeRadiusSm,
44    };
45    let default_bg = ColorRef::Token {
46        key: "component.surface.bg",
47        fallback: ColorFallback::ThemePanelBackground,
48    };
49    let default_border = ColorRef::Token {
50        key: "component.surface.border",
51        fallback: ColorFallback::ThemePanelBorder,
52    };
53
54    // Note: surface chrome is interpreted as symmetric `padding_x/padding_y`. To avoid Tailwind
55    // per-edge no-ops, accept either edge as a shorthand for the axis (e.g. `pr-*` behaves like
56    // setting `px-*` for surfaces).
57    let padding_x = style
58        .padding
59        .as_ref()
60        .and_then(|p| p.left.as_ref().or(p.right.as_ref()))
61        .map(|m| m.resolve(theme))
62        .or_else(|| keys.padding_x.and_then(|k| theme.metric_by_key(k)))
63        .or_else(|| theme.metric_by_key("component.surface.padding_x"))
64        .unwrap_or_else(|| default_padding_x.resolve(theme));
65    let padding_y = style
66        .padding
67        .as_ref()
68        .and_then(|p| p.top.as_ref().or(p.bottom.as_ref()))
69        .map(|m| m.resolve(theme))
70        .or_else(|| keys.padding_y.and_then(|k| theme.metric_by_key(k)))
71        .or_else(|| theme.metric_by_key("component.surface.padding_y"))
72        .unwrap_or_else(|| default_padding_y.resolve(theme));
73    let radius = style
74        .radius
75        .as_ref()
76        .map(|m| m.resolve(theme))
77        .or_else(|| keys.radius.and_then(|k| theme.metric_by_key(k)))
78        .or_else(|| theme.metric_by_key("component.surface.radius"))
79        .unwrap_or_else(|| default_radius.resolve(theme));
80    let border_width = style
81        .border_width
82        .as_ref()
83        .map(|m| m.resolve(theme))
84        .or_else(|| keys.border_width.and_then(|k| theme.metric_by_key(k)))
85        .or_else(|| theme.metric_by_key("component.surface.border_width"))
86        .unwrap_or(Px(1.0));
87
88    let background = style
89        .background
90        .as_ref()
91        .map(|c| c.resolve(theme))
92        .or_else(|| keys.bg.and_then(|k| theme.color_by_key(k)))
93        .or_else(|| theme.color_by_key("component.surface.bg"))
94        .unwrap_or_else(|| default_bg.resolve(theme));
95    let border_color = style
96        .border_color
97        .as_ref()
98        .map(|c| c.resolve(theme))
99        .or_else(|| keys.border.and_then(|k| theme.color_by_key(k)))
100        .or_else(|| theme.color_by_key("component.surface.border"))
101        .unwrap_or_else(|| default_border.resolve(theme));
102
103    ResolvedSurfaceChrome {
104        padding_x: Px(padding_x.0.max(0.0)),
105        padding_y: Px(padding_y.0.max(0.0)),
106        radius: Px(radius.0.max(0.0)),
107        border_width: Px(border_width.0.max(0.0)),
108        background,
109        border_color,
110    }
111}
112
113pub fn surface_base_refinement() -> ChromeRefinement {
114    ChromeRefinement {
115        padding: Some(PaddingRefinement {
116            top: Some(MetricRef::Token {
117                key: "component.surface.padding_y",
118                fallback: MetricFallback::ThemePaddingSm,
119            }),
120            right: Some(MetricRef::Token {
121                key: "component.surface.padding_x",
122                fallback: MetricFallback::ThemePaddingSm,
123            }),
124            bottom: Some(MetricRef::Token {
125                key: "component.surface.padding_y",
126                fallback: MetricFallback::ThemePaddingSm,
127            }),
128            left: Some(MetricRef::Token {
129                key: "component.surface.padding_x",
130                fallback: MetricFallback::ThemePaddingSm,
131            }),
132        }),
133        radius: Some(MetricRef::Token {
134            key: "component.surface.radius",
135            fallback: MetricFallback::ThemeRadiusSm,
136        }),
137        border_width: Some(MetricRef::Token {
138            key: "component.surface.border_width",
139            fallback: MetricFallback::Px(Px(1.0)),
140        }),
141        background: Some(ColorRef::Token {
142            key: "component.surface.bg",
143            fallback: ColorFallback::ThemePanelBackground,
144        }),
145        border_color: Some(ColorRef::Token {
146            key: "component.surface.border",
147            fallback: ColorFallback::ThemePanelBorder,
148        }),
149        ..ChromeRefinement::default()
150    }
151}