Skip to main content

ui_storybook/stories/
library.rs

1//! Library stories — UI-14 (`LibrarySidebar`) + UI-15
2//! (`RecordingCard` + `LibraryGrid`).
3
4use leptos::prelude::*;
5
6use crate::components::library::{LibraryGrid, LibrarySidebar, RecordingCard, RecordingCardState};
7use crate::fixtures::library::{
8    sample_library_grid, sample_library_grid_empty, sample_library_grid_list_mode,
9    sample_library_sidebar, sample_library_sidebar_high_storage,
10    sample_library_sidebar_inbox_active, sample_recording_cards,
11};
12
13use super::{Story, StoryViewport, render};
14
15const RAIL_W: u16 = 260;
16const RAIL_H: u16 = 620;
17const GRID_W: u16 = 880;
18const GRID_H: u16 = 520;
19const CARD_W: u16 = 360;
20const CARD_H: u16 = 280;
21
22/// All library-surface stories.
23#[must_use]
24pub fn stories() -> Vec<Story> {
25    vec![
26        story(
27            "library-sidebar-default",
28            "Library sidebar — default",
29            render_default,
30        ),
31        story(
32            "library-sidebar-inbox-active",
33            "Library sidebar — inbox active",
34            render_inbox,
35        ),
36        story(
37            "library-sidebar-high-storage",
38            "Library sidebar — 95% storage",
39            render_high_storage,
40        ),
41        story(
42            "library-sidebar-empty-spaces",
43            "Library sidebar — no spaces",
44            render_empty_spaces,
45        ),
46        story(
47            "library-sidebar-long-labels",
48            "Library sidebar — overflow labels",
49            render_long_labels,
50        ),
51        card_story(
52            "recording-card-ready",
53            "Recording card — ready",
54            render_card_ready,
55        ),
56        card_story(
57            "recording-card-processing",
58            "Recording card — processing",
59            render_card_processing,
60        ),
61        card_story(
62            "recording-card-empty-thumbnail",
63            "Recording card — missing thumbnail",
64            render_card_empty_thumb,
65        ),
66        grid_story(
67            "library-grid-default",
68            "Library grid — default",
69            render_grid_default,
70        ),
71        grid_story(
72            "library-grid-empty",
73            "Library grid — empty state",
74            render_grid_empty,
75        ),
76        grid_story(
77            "library-grid-list-mode",
78            "Library grid — list mode",
79            render_grid_list,
80        ),
81    ]
82}
83
84fn card_story(id: &'static str, title: &'static str, render: fn() -> String) -> Story {
85    Story {
86        id,
87        category: "Library",
88        title,
89        viewport: StoryViewport::Fixed {
90            width: CARD_W,
91            height: CARD_H,
92        },
93        render,
94    }
95}
96
97fn grid_story(id: &'static str, title: &'static str, render: fn() -> String) -> Story {
98    Story {
99        id,
100        category: "Library",
101        title,
102        viewport: StoryViewport::Fixed {
103            width: GRID_W,
104            height: GRID_H,
105        },
106        render,
107    }
108}
109
110fn story(id: &'static str, title: &'static str, render: fn() -> String) -> Story {
111    Story {
112        id,
113        category: "Library",
114        title,
115        viewport: StoryViewport::Fixed {
116            width: RAIL_W,
117            height: RAIL_H,
118        },
119        render,
120    }
121}
122
123fn render_default() -> String {
124    render(view! { <LibrarySidebar view=sample_library_sidebar(3) /> })
125}
126
127fn render_inbox() -> String {
128    render(view! { <LibrarySidebar view=sample_library_sidebar_inbox_active() /> })
129}
130
131fn render_high_storage() -> String {
132    render(view! { <LibrarySidebar view=sample_library_sidebar_high_storage() /> })
133}
134
135fn render_empty_spaces() -> String {
136    let mut v = sample_library_sidebar(0);
137    v.sections.retain(|s| s.heading != "SPACES");
138    render(view! { <LibrarySidebar view=v /> })
139}
140
141fn render_long_labels() -> String {
142    let mut v = sample_library_sidebar(99);
143    if let Some(item) = v.primary.iter_mut().find(|i| i.id == "shared") {
144        item.label = "Shared with me — Northwind Studio quarterly review";
145    }
146    render(view! { <LibrarySidebar view=v /> })
147}
148
149fn render_card_ready() -> String {
150    let cards = sample_recording_cards();
151    let card = cards.into_iter().next().expect("at least one card");
152    render(view! { <RecordingCard view=card /> })
153}
154
155fn render_card_processing() -> String {
156    let cards = sample_recording_cards();
157    let mut card = cards.into_iter().nth(1).expect("processing fixture");
158    card.state = RecordingCardState::Processing { percent: 42 };
159    render(view! { <RecordingCard view=card /> })
160}
161
162fn render_card_empty_thumb() -> String {
163    let cards = sample_recording_cards();
164    let card = cards.into_iter().last().expect("empty-thumb fixture");
165    render(view! { <RecordingCard view=card /> })
166}
167
168fn render_grid_default() -> String {
169    render(view! { <LibraryGrid view=sample_library_grid() /> })
170}
171
172fn render_grid_empty() -> String {
173    render(view! { <LibraryGrid view=sample_library_grid_empty() /> })
174}
175
176fn render_grid_list() -> String {
177    render(view! { <LibraryGrid view=sample_library_grid_list_mode() /> })
178}