ui_storybook/components/editor/
timeline_skeleton.rs1use leptos::prelude::*;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct TimelineTrackView {
13 pub id: &'static str,
15 pub label: &'static str,
17 pub placeholder: Option<&'static str>,
20 pub disabled: bool,
22 pub selected: bool,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct TimelineView {
29 pub playhead_label: &'static str,
31 pub duration_label: &'static str,
33 pub is_playing: bool,
35 pub tracks: Vec<TimelineTrackView>,
37}
38
39#[component]
40pub fn TimelineTransport(
41 #[prop(into)]
43 playhead_label: String,
44 #[prop(into)]
46 duration_label: String,
47 is_playing: bool,
49) -> impl IntoView {
50 let glyph = if is_playing { "❚❚" } else { "▶" };
51 let label = if is_playing { "Pause" } else { "Play" };
52 view! {
53 <div class="timeline-transport" role="group" aria-label="Transport">
54 <button class="timeline-transport-toggle" aria-label=label>{glyph}</button>
55 <span class="timeline-timecode">{playhead_label}</span>
56 <span class="timeline-timecode-sep">"/"</span>
57 <span class="timeline-timecode-total">{duration_label}</span>
58 </div>
59 }
60}
61
62#[component]
63pub fn TimelineTrackRow(track: TimelineTrackView) -> impl IntoView {
64 let mut class = String::from("timeline-track-row");
65 if track.selected {
66 class.push_str(" timeline-track-row-selected");
67 }
68 if track.disabled {
69 class.push_str(" timeline-track-row-disabled");
70 }
71 let placeholder = track.placeholder.unwrap_or("");
72 view! {
73 <li class=class data-id=track.id>
74 <span class="timeline-track-label">{track.label}</span>
75 <span class="timeline-track-body">
76 <span class="timeline-track-placeholder">{placeholder}</span>
77 </span>
78 </li>
79 }
80}
81
82#[component]
83pub fn TimelineSkeleton(view: TimelineView) -> impl IntoView {
84 let TimelineView {
85 playhead_label,
86 duration_label,
87 is_playing,
88 tracks,
89 } = view;
90 let track_rows: Vec<_> = tracks
91 .into_iter()
92 .map(|t| view! { <TimelineTrackRow track=t /> })
93 .collect();
94 view! {
95 <section class="timeline-skeleton" aria-label="Timeline">
96 <TimelineTransport
97 playhead_label=playhead_label
98 duration_label=duration_label
99 is_playing=is_playing
100 />
101 <ul class="timeline-tracks" role="list">{track_rows}</ul>
102 </section>
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn track_view_carries_state() {
112 let t = TimelineTrackView {
113 id: "video",
114 label: "Video",
115 placeholder: Some("drop a clip to fill"),
116 disabled: false,
117 selected: true,
118 };
119 assert!(t.selected);
120 assert!(t.placeholder.is_some());
121 }
122}