Skip to main content

ui_storybook/components/primitives/
segmented_control.rs

1//! `SegmentedControl` — pill-style radio-tab control (M-UI.4 / AUT-124).
2//!
3//! The active segment is a string id passed via `active`. The component
4//! never owns that id; callers re-render with the new id when a segment
5//! is selected.
6
7use leptos::prelude::*;
8
9/// One segment in a `SegmentedControl`.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Segment {
12    /// Stable id matched against `active`. Use a kebab-case string.
13    pub id: &'static str,
14    /// Visible label.
15    pub label: &'static str,
16    /// Optional leading glyph.
17    pub icon: Option<&'static str>,
18    /// `true` to render disabled.
19    pub disabled: bool,
20}
21
22#[component]
23pub fn SegmentedControl(
24    /// Segments in display order.
25    segments: Vec<Segment>,
26    /// Id of the currently-active segment.
27    #[prop(into)]
28    active: String,
29    /// Accessible label for the whole control.
30    #[prop(into)]
31    label: String,
32) -> impl IntoView {
33    let active = active.clone();
34    view! {
35        <div class="segmented" role="radiogroup" aria-label=label>
36            {segments.into_iter().map(|seg| {
37                let is_active = seg.id == active;
38                let mut class = String::from("segment");
39                if is_active { class.push_str(" segment-active"); }
40                if seg.disabled { class.push_str(" segment-disabled"); }
41                view! {
42                    <button
43                        class=class
44                        role="radio"
45                        aria-checked=is_active
46                        aria-disabled=seg.disabled
47                        disabled=seg.disabled
48                        data-id=seg.id
49                    >
50                        {seg.icon.map(|i| view! { <span class="segment-icon" aria-hidden="true">{i}</span> })}
51                        <span class="segment-label">{seg.label}</span>
52                    </button>
53                }
54            }).collect_view()}
55        </div>
56    }
57}