Skip to main content

ui_storybook/components/recorder/
recording_selects.rs

1//! Recording-footer select pills + shortcut badge group (M-UI.11 /
2//! AUT-131).
3//!
4//! `AutoZoomSelect` and `CountdownSelect` are thin wrappers around
5//! UI-04 `SelectPill` that bake in a leading glyph + the expected
6//! label format so the parent (and the `TrayRecordPopover` in UI-12)
7//! doesn't have to redeclare them.
8//!
9//! `ShortcutBadgeGroup` renders the keyboard hint inside the red
10//! Start button — dark chips on a red field. It's distinct from
11//! UI-01 `Kbd`: the visual treatment is inverted (light text on the
12//! action color, not the neutral surface treatment `Kbd` uses).
13
14use leptos::prelude::*;
15
16use crate::components::primitives::SelectPill;
17
18/// Compact pill that opens the auto-zoom strength menu. Visual only —
19/// the popover content is owned by the parent.
20#[component]
21pub fn AutoZoomSelect(
22    /// Pre-formatted label shown inside the pill. Examples:
23    /// `"Auto-zoom 2.0×"`, `"Auto-zoom off"`. The footer fixture builds
24    /// this via `format_auto_zoom_label`.
25    #[prop(into)]
26    label: String,
27    /// `true` when the parent's popover is open.
28    #[prop(optional)]
29    open: bool,
30    /// `true` to render disabled.
31    #[prop(optional)]
32    disabled: bool,
33) -> impl IntoView {
34    view! {
35        <SelectPill icon="⊙".to_string() label=label open=open disabled=disabled />
36    }
37}
38
39/// Compact pill that opens the countdown-length menu.
40#[component]
41pub fn CountdownSelect(
42    /// Pre-formatted label. Examples: `"3s Countdown"`, `"No countdown"`.
43    /// Build via `format_countdown_label`.
44    #[prop(into)]
45    label: String,
46    #[prop(optional)] open: bool,
47    #[prop(optional)] disabled: bool,
48) -> impl IntoView {
49    view! {
50        <SelectPill icon="⏱".to_string() label=label open=open disabled=disabled />
51    }
52}
53
54/// Group of small keyboard-shortcut badges (light-on-red, used inside
55/// the Start button).
56#[component]
57pub fn ShortcutBadgeGroup(
58    /// Keys to render in display order.
59    keys: Vec<String>,
60    /// `true` when sitting on a colored action surface (red Start
61    /// button). Drives the inverted color treatment.
62    #[prop(optional)]
63    on_action: bool,
64) -> impl IntoView {
65    let class = if on_action {
66        "shortcut-badges shortcut-badges-on-action"
67    } else {
68        "shortcut-badges"
69    };
70    view! {
71        <span class=class aria-hidden="true">
72            {keys.into_iter()
73                .map(|k| view! { <span class="shortcut-badge">{k}</span> })
74                .collect_view()}
75        </span>
76    }
77}
78
79/// Format `"Auto-zoom 2.0×"` / `"Auto-zoom off"`. `None` → off.
80#[must_use]
81pub fn format_auto_zoom_label(zoom: Option<f32>) -> String {
82    match zoom {
83        None => "Auto-zoom off".to_owned(),
84        Some(z) => format!("Auto-zoom {z:.1}×"),
85    }
86}
87
88/// Format `"3s Countdown"` / `"No countdown"`. `0` → off.
89#[must_use]
90pub fn format_countdown_label(seconds: u8) -> String {
91    if seconds == 0 {
92        "No countdown".to_owned()
93    } else {
94        format!("{seconds}s Countdown")
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn auto_zoom_label_formats_one_decimal() {
104        assert_eq!(format_auto_zoom_label(None), "Auto-zoom off");
105        assert_eq!(format_auto_zoom_label(Some(1.0)), "Auto-zoom 1.0×");
106        assert_eq!(format_auto_zoom_label(Some(2.0)), "Auto-zoom 2.0×");
107        assert_eq!(format_auto_zoom_label(Some(2.5)), "Auto-zoom 2.5×");
108    }
109
110    #[test]
111    fn countdown_label_zero_is_off() {
112        assert_eq!(format_countdown_label(0), "No countdown");
113        assert_eq!(format_countdown_label(3), "3s Countdown");
114        assert_eq!(format_countdown_label(10), "10s Countdown");
115    }
116}