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: 配置与有效布局
description: 管理受控配置、持久化偏好和区域自动内移。
order: 20
toc:
- id: controlled
title: 受控与非受控
- id: effective
title: 有效布局
- id: persistence
title: 持久化
---
## 受控与非受控 {#controlled}
未提供 `layout` 时,`AppLayout` 使用内部状态:
```tsx
<AppLayout defaultLayout={{ sidebarOpen: false }} sidebar={NavigationSidebar}>
<RouterOutlet />
</AppLayout>
```
提供 `layout` 后,调用方必须在 `onLayoutChange` 中保存新快照:
```tsx
<AppLayout layout={layout} onLayoutChange={setLayout} aside={AssistantPanel}>
<RouterOutlet />
</AppLayout>
```
## 有效布局 {#effective}
配置保存用户意图,有效布局负责当前渲染。例如 `asideOpen` 可以为 `true`,但没有 Aside slot 或 `asideEnabled` 为 `false` 时不会渲染 Aside。`sidebarCollapsible` 为 `false` 时,即使保存的 `sidebarOpen` 为 `false`,Sidebar 也会保持显示。
Header 和 Footer 的起始位置遵循同样原则。只有 Rail 与 Sidebar 同时可见时,`"sidebar"` 才会生效;否则自动内移到 `"content"`。
## 持久化 {#persistence}
有效布局不会反向修改配置,因此临时缺少某个区域不会清除用户偏好。应用可以在 `onLayoutChange` 中把完整 `LayoutConfig` 写入 `@workspace/preferences`,并在加载时通过 `layout` 传回。
@@ -0,0 +1,54 @@
---
title: 工作区布局
description: 组合导航、内容和 AI 辅助面板。
order: 30
toc:
- id: layout
title: 创建布局
- id: controls
title: 添加控制
---
## 创建布局 {#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>
)
}
```
当用户折叠 Sidebar 后,Header 和 Footer 中以 `"sidebar"` 为起点的区域会自动内移到内容列,网格不会保留空轨道。
## 添加控制 {#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>
)
}
```
控制组件不需要从应用逐层接收回调;组件型 slot 会在布局上下文内部创建。
@@ -0,0 +1,52 @@
---
title: 设置应用布局
description: 配置样式源并组合应用布局区域。
order: 10
toc:
- id: styles
title: 导入样式源
- id: compose
title: 组合区域
- id: sizes
title: 调整尺寸
---
## 导入样式源 {#styles}
在应用全局样式中导入 Layout 的 Tailwind 源:
```css
@import "@workspace/layout/globals.css";
```
## 组合区域 {#compose}
`AppLayout` 接受 React 节点或无必需属性的组件定义。需要业务属性时传入节点;需要组件从布局上下文读取状态时,可以直接传入组件定义。
```tsx
<AppLayout
header={AppHeader}
rail={ActivityRail}
sidebar={<NavigationSidebar groups={groups} />}
aside={AssistantPanel}
footer={StatusBar}
>
<RouterOutlet />
</AppLayout>
```
组件定义会在 `AppLayout` 的 Provider 内创建,因此 `AppHeader` 和 `AssistantPanel` 可以调用 `useLayoutConfig`。
## 调整尺寸 {#sizes}
通过 CSS 自定义属性调整辅助列宽度:
```css
.application-layout {
--layout-rail-width: 4rem;
--layout-sidebar-width: 18rem;
--layout-aside-width: 22rem;
}
```
把该类名传给 `AppLayout`。内容列始终使用剩余空间,并允许收缩到零宽。
+67
View File
@@ -0,0 +1,67 @@
---
title: 概览
description: 使用可选区域和可持久化配置构建应用级布局。
order: 1
toc:
- id: role
title: 包的职责
- id: regions
title: 布局区域
- id: config
title: 配置与 Hook
---
## 包的职责 {#role}
`@workspace/layout` 提供路由无关的 `AppLayout`。它通过 CSS Grid 组合应用 Header、Navigation Rail、Sidebar、内容区、Aside 和 Footer,并根据实际可见区域自动计算有效网格。包不创建导航数据,不限制路由器,也不持久化用户偏好。
在应用全局样式中导入 Tailwind 源:
```css
@import "@workspace/layout/globals.css";
```
## 布局区域 {#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` 是唯一必需的内容区。Sidebar 可以折叠,Aside 适合 AI、文档和检查器等辅助功能。Header 可以从 Rail、Sidebar 或内容区开始;Footer 可以从 Sidebar 或内容区开始。缺少前置区域时,起始位置会自动向内容区内移。
## 配置与 Hook {#config}
`layout` 接收 `Partial<LayoutConfig>`。提供它时使用受控模式;否则 `AppLayout` 通过 `defaultLayout` 初始化内部状态。区域组件可以调用 `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>
)
}
```
临时隐藏或禁用区域只影响当前有效布局,不会覆盖 `LayoutConfig` 中保存的原始偏好,因此配置可以安全接入 `@workspace/preferences`。