docs(layout): document app layout composition
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Configuration and effective layout
|
||||
description: Manage controlled configuration, persistent preferences, and automatic region resolution.
|
||||
order: 20
|
||||
toc:
|
||||
- id: controlled
|
||||
title: Controlled and uncontrolled
|
||||
- id: effective
|
||||
title: Effective layout
|
||||
- id: persistence
|
||||
title: Persistence
|
||||
---
|
||||
|
||||
## Controlled and uncontrolled {#controlled}
|
||||
|
||||
Without `layout`, `AppLayout` owns its state:
|
||||
|
||||
```tsx
|
||||
<AppLayout defaultLayout={{ sidebarOpen: false }} sidebar={NavigationSidebar}>
|
||||
<RouterOutlet />
|
||||
</AppLayout>
|
||||
```
|
||||
|
||||
After supplying `layout`, the caller must save each new snapshot from `onLayoutChange`:
|
||||
|
||||
```tsx
|
||||
<AppLayout layout={layout} onLayoutChange={setLayout} aside={AssistantPanel}>
|
||||
<RouterOutlet />
|
||||
</AppLayout>
|
||||
```
|
||||
|
||||
## Effective layout {#effective}
|
||||
|
||||
Configuration stores user intent, while the effective layout controls the current rendering. For example, `asideOpen` can remain `true` while a missing Aside slot or `asideEnabled: false` prevents the Aside from rendering. When `sidebarCollapsible` is `false`, the Sidebar remains visible even if the stored `sidebarOpen` value is `false`.
|
||||
|
||||
Header and Footer starts follow the same principle. `"sidebar"` only takes effect while both the Rail and Sidebar are visible; otherwise it moves inward to `"content"`.
|
||||
|
||||
## Persistence {#persistence}
|
||||
|
||||
The effective layout never writes back into configuration, so temporarily missing regions do not clear user preferences. An application can persist the complete `LayoutConfig` through `@workspace/preferences` in `onLayoutChange`, then supply it through `layout` when loading.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Workspace layout
|
||||
description: Compose navigation, content, and an AI assistance panel.
|
||||
order: 30
|
||||
toc:
|
||||
- id: layout
|
||||
title: Create the layout
|
||||
- id: controls
|
||||
title: Add controls
|
||||
---
|
||||
|
||||
## Create the layout {#layout}
|
||||
|
||||
```tsx
|
||||
export function Workspace() {
|
||||
return (
|
||||
<AppLayout
|
||||
className="application-layout"
|
||||
header={WorkspaceHeader}
|
||||
rail={ActivityRail}
|
||||
sidebar={Explorer}
|
||||
aside={Assistant}
|
||||
footer={StatusBar}
|
||||
defaultLayout={{
|
||||
headerStart: "content",
|
||||
footerStart: "sidebar",
|
||||
}}
|
||||
>
|
||||
<Editor />
|
||||
</AppLayout>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
After the user collapses the Sidebar, Header and Footer regions starting at `"sidebar"` automatically move inward to the content column without retaining an empty grid track.
|
||||
|
||||
## Add controls {#controls}
|
||||
|
||||
```tsx
|
||||
function WorkspaceHeader() {
|
||||
const { layout, toggleAside, toggleSidebar } = useLayoutConfig()
|
||||
|
||||
return (
|
||||
<header>
|
||||
{layout.sidebarCollapsible && (
|
||||
<button onClick={toggleSidebar}>Explorer</button>
|
||||
)}
|
||||
{layout.asideEnabled && <button onClick={toggleAside}>Assistant</button>}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The control component does not need callbacks threaded through the application; component slots are created inside the layout context.
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Set up the application layout
|
||||
description: Configure the style source and compose application layout regions.
|
||||
order: 10
|
||||
toc:
|
||||
- id: styles
|
||||
title: Import the style source
|
||||
- id: compose
|
||||
title: Compose regions
|
||||
- id: sizes
|
||||
title: Adjust sizes
|
||||
---
|
||||
|
||||
## Import the style source {#styles}
|
||||
|
||||
Import the Layout Tailwind source from the application stylesheet:
|
||||
|
||||
```css
|
||||
@import "@workspace/layout/globals.css";
|
||||
```
|
||||
|
||||
## Compose regions {#compose}
|
||||
|
||||
`AppLayout` accepts React nodes or component definitions without required props. Pass a node when the region needs business props, or pass a component definition when it should read layout state from context.
|
||||
|
||||
```tsx
|
||||
<AppLayout
|
||||
header={AppHeader}
|
||||
rail={ActivityRail}
|
||||
sidebar={<NavigationSidebar groups={groups} />}
|
||||
aside={AssistantPanel}
|
||||
footer={StatusBar}
|
||||
>
|
||||
<RouterOutlet />
|
||||
</AppLayout>
|
||||
```
|
||||
|
||||
Component definitions are created inside the `AppLayout` provider, so `AppHeader` and `AssistantPanel` can call `useLayoutConfig`.
|
||||
|
||||
## Adjust sizes {#sizes}
|
||||
|
||||
Use CSS custom properties to size the supporting columns:
|
||||
|
||||
```css
|
||||
.application-layout {
|
||||
--layout-rail-width: 4rem;
|
||||
--layout-sidebar-width: 18rem;
|
||||
--layout-aside-width: 22rem;
|
||||
}
|
||||
```
|
||||
|
||||
Pass that class to `AppLayout`. The content column always consumes the remaining space and can shrink to zero width.
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Build application layouts with optional regions and persistent configuration.
|
||||
order: 1
|
||||
toc:
|
||||
- id: role
|
||||
title: Package role
|
||||
- id: regions
|
||||
title: Layout regions
|
||||
- id: config
|
||||
title: Configuration and hook
|
||||
---
|
||||
|
||||
## Package role {#role}
|
||||
|
||||
`@workspace/layout` provides the router-neutral `AppLayout`. It composes an application header, navigation rail, sidebar, content, aside, and footer with CSS Grid, then resolves the effective grid from the currently visible regions. The package does not create navigation data, constrain the router, or persist user preferences.
|
||||
|
||||
Import the Tailwind source from the application stylesheet:
|
||||
|
||||
```css
|
||||
@import "@workspace/layout/globals.css";
|
||||
```
|
||||
|
||||
## Layout regions {#regions}
|
||||
|
||||
```tsx
|
||||
import { AppLayout } from "@workspace/layout"
|
||||
|
||||
export function Application() {
|
||||
return (
|
||||
<AppLayout
|
||||
header={AppHeader}
|
||||
rail={ActivityRail}
|
||||
sidebar={NavigationSidebar}
|
||||
aside={AssistantPanel}
|
||||
footer={StatusBar}
|
||||
defaultLayout={{ asideOpen: true }}
|
||||
>
|
||||
<RouterOutlet />
|
||||
</AppLayout>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`children` is the only required content region. The sidebar can collapse, while the aside suits assistants, documentation, and inspectors. The header can begin at the rail, sidebar, or content; the footer can begin at the sidebar or content. A missing preceding region automatically moves the effective start inward.
|
||||
|
||||
## Configuration and hook {#config}
|
||||
|
||||
`layout` accepts `Partial<LayoutConfig>`. Supplying it enables controlled mode; otherwise `AppLayout` initializes internal state from `defaultLayout`. Region components can call `useLayoutConfig`:
|
||||
|
||||
```tsx
|
||||
import { useLayoutConfig } from "@workspace/layout"
|
||||
|
||||
export function AppHeader() {
|
||||
const { layout, toggleAside, toggleSidebar } = useLayoutConfig()
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<button onClick={toggleSidebar}>Sidebar</button>
|
||||
<button onClick={toggleAside}>Assistant</button>
|
||||
<output>{layout.headerStart}</output>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Temporarily hiding or disabling a region only changes the effective layout; it does not overwrite the original preference stored in `LayoutConfig`. The configuration can therefore integrate safely with `@workspace/preferences`.
|
||||
Reference in New Issue
Block a user