docs(layout): document app layout composition

This commit is contained in:
Maofeng
2026-09-30 01:30:56 +08:00
parent ffda0b9099
commit 57d006a39b
10 changed files with 573 additions and 0 deletions
@@ -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.
+67
View File
@@ -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`.