ui_storybook/components/shell/app_shell.rs
1//! `AppShell` — top-level layout with slots for rail / main / inspector /
2//! titlebar / footer (M-UI.2 / AUT-122).
3//!
4//! Structural component only — owns no state, picks no router. Each
5//! product screen (library, editor, cursor, prefs) composes one
6//! `AppShell` with the panes it needs. `rail` and `main` are required;
7//! `titlebar`, `inspector`, and `footer` are opt-in via
8//! `Option<Children>`.
9
10use leptos::prelude::*;
11
12#[component]
13pub fn AppShell(
14 /// Left-edge navigation rail. Typically `NavigationRail`.
15 rail: Children,
16 /// The primary surface (record setup, library grid, editor canvas,
17 /// cursor studio).
18 main: Children,
19 /// Optional right-edge inspector / property panel.
20 #[prop(optional)]
21 inspector: Option<Children>,
22 /// Optional top window-chrome strip.
23 #[prop(optional)]
24 titlebar: Option<Children>,
25 /// Optional bottom status / recording-controls footer.
26 #[prop(optional)]
27 footer: Option<Children>,
28 #[prop(optional, into)] extra_class: String,
29) -> impl IntoView {
30 let class = format!(
31 "app-shell{}{}",
32 if extra_class.is_empty() { "" } else { " " },
33 extra_class,
34 );
35 view! {
36 <div class=class>
37 {titlebar.map(|t| view! { <header class="app-shell-titlebar">{t()}</header> })}
38 <div class="app-shell-body">
39 <aside class="app-shell-rail">{rail()}</aside>
40 <main class="app-shell-main">{main()}</main>
41 {inspector.map(|i| view! { <aside class="app-shell-inspector">{i()}</aside> })}
42 </div>
43 {footer.map(|f| view! { <footer class="app-shell-footer">{f()}</footer> })}
44 </div>
45 }
46}