Skip to main content

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}