docs(appearance): add bilingual package guides
This commit is contained in:
@@ -35,6 +35,14 @@ const packageDefinitions: readonly PackageDefinition[] = [
|
||||
us: "Build an application shell and add navigation, notifications, chat, and appearance controls.",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "appearance",
|
||||
label: "Appearance",
|
||||
description: {
|
||||
zh: "使用原子化控件、主题词典和可组合偏好副作用构建应用外观设置。",
|
||||
us: "Build application appearance settings with atomic controls, theme tokens, and composable preference effects.",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "i18n",
|
||||
label: "I18n",
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Atomic composition
|
||||
description: Build a custom appearance surface from controlled components without the preset dialog.
|
||||
order: 1
|
||||
toc:
|
||||
- id: controlled-atoms
|
||||
title: Controlled atoms
|
||||
- id: compose-card
|
||||
title: Compose a theme card
|
||||
- id: connected-layer
|
||||
title: State bindings
|
||||
---
|
||||
|
||||
## Controlled atoms {#controlled-atoms}
|
||||
|
||||
`ThemeModeField`, `CompactLayoutField`, `ThemeColorPicker`, and `ThemeToggle` are driven by values and callbacks. They do not require preference context, so they can work with forms, preview tools, or another state library.
|
||||
|
||||
```tsx
|
||||
<ThemeModeField value={mode} onValueChange={setMode} />
|
||||
<CompactLayoutField checked={compacted} onCheckedChange={setCompacted} />
|
||||
```
|
||||
|
||||
## Compose a theme card {#compose-card}
|
||||
|
||||
`ThemeConfigCard` combines a preview with base and accent color controls while keeping the configuration owned by the caller.
|
||||
|
||||
```tsx
|
||||
<ThemeConfigCard
|
||||
active={resolvedTheme === "light"}
|
||||
compacted={compacted}
|
||||
config={lightTheme}
|
||||
onConfigChange={setLightTheme}
|
||||
scheme="light"
|
||||
/>
|
||||
```
|
||||
|
||||
For a more granular layout, compose `ThemePreview`, `baseColorSwatches`, `accentColorSwatches`, and `ThemeColorSwatchButton` directly.
|
||||
|
||||
## State bindings {#connected-layer}
|
||||
|
||||
`AppearanceSettings` is the default binding layer between the atoms and the shared `PreferencesProvider`. A custom surface can call `usePreference` from `@workspace/preferences` for individual values without reusing the default layout.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: Appearance settings dialog
|
||||
description: Compose a trigger, controller, and hotkeys into a complete appearance settings flow.
|
||||
order: 1
|
||||
toc:
|
||||
- id: controller
|
||||
title: Mount the controller
|
||||
- id: trigger
|
||||
title: Add a trigger
|
||||
- id: custom-handle
|
||||
title: Independent dialog instance
|
||||
---
|
||||
|
||||
## Mount the controller {#controller}
|
||||
|
||||
Mount one `AppearanceController` inside the application's shared `PreferencesProvider`. It composes the default settings content and registers `Mod+,` to open the dialog and `Mod+D` to toggle the theme.
|
||||
|
||||
```tsx
|
||||
<PreferencesProvider
|
||||
effects={[appearancePreferenceEffect]}
|
||||
initialPreferences={preferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
<App />
|
||||
</PreferencesProvider>
|
||||
```
|
||||
|
||||
## Add a trigger {#trigger}
|
||||
|
||||
Place `AppearanceDialogTrigger` in a user menu, settings button, or command palette. By default it connects to the handle used by the default controller.
|
||||
|
||||
```tsx
|
||||
<AppearanceDialogTrigger render={<button type="button" />}>
|
||||
Appearance
|
||||
</AppearanceDialogTrigger>
|
||||
```
|
||||
|
||||
## Independent dialog instance {#custom-handle}
|
||||
|
||||
When a page needs an isolated appearance dialog, pass the same new handle to its trigger and controller.
|
||||
|
||||
```tsx
|
||||
const handle = createAppearanceDialogHandle()
|
||||
|
||||
<AppearanceDialogTrigger handle={handle} render={<button type="button" />}>
|
||||
Open theme settings
|
||||
</AppearanceDialogTrigger>
|
||||
<AppearanceController handle={handle} />
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Set up appearance preferences
|
||||
description: Aggregate appearance definitions and effects with the application's shared preferences.
|
||||
order: 1
|
||||
toc:
|
||||
- id: provider
|
||||
title: Aggregate preferences
|
||||
- id: effects
|
||||
title: Register effects
|
||||
- id: theme-style
|
||||
title: Inject theme variables
|
||||
- id: persistence
|
||||
title: Persist updates
|
||||
---
|
||||
|
||||
## Aggregate preferences {#provider}
|
||||
|
||||
Build one complete preference definition map at the application boundary. Spread definitions from appearance, locale, keyboard, and other feature packages into this map, then create or load the initial values.
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createDefaultPreferences,
|
||||
PreferencesProvider,
|
||||
} from "@workspace/preferences"
|
||||
import {
|
||||
appearancePreferenceDefinitions,
|
||||
appearancePreferenceEffect,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
const preferenceDefinitions = {
|
||||
...appearancePreferenceDefinitions,
|
||||
...localePreferenceDefinitions,
|
||||
}
|
||||
|
||||
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||||
const preferenceEffects = [
|
||||
appearancePreferenceEffect,
|
||||
localePreferenceEffect,
|
||||
] as const
|
||||
|
||||
export function Root({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={initialPreferences}
|
||||
>
|
||||
{children}
|
||||
</PreferencesProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`defaultAppearancePreferences` remains available when only the appearance slice is needed, but the definition map is the preferred source when several packages are combined.
|
||||
|
||||
## Register effects {#effects}
|
||||
|
||||
`appearancePreferenceEffect` combines two exported callbacks: `applyAppearancePreferenceEffect` updates document classes and theme variables before paint, while `subscribeSystemThemePreferenceEffect` watches the operating-system theme after paint. Register the composed effect in the stable `effects` array passed to `PreferencesProvider`.
|
||||
|
||||
## Inject theme variables {#theme-style}
|
||||
|
||||
Use `buildThemeStyleSheet` to generate the CSS variables for light, dark, and system modes. Keep the stable element ID so the appearance effect can update the same node when color preferences change.
|
||||
|
||||
```tsx
|
||||
import {
|
||||
buildThemeStyleSheet,
|
||||
THEME_STYLE_ELEMENT_ID,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
;<style
|
||||
id={THEME_STYLE_ELEMENT_ID}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: buildThemeStyleSheet(themeLight, themeDark),
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## Persist updates {#persistence}
|
||||
|
||||
The shared `onPreferenceChange` receives a typed `{ key, value }` only when a value actually changes. Forward it to a server function or cookie writer owned by the application.
|
||||
|
||||
```tsx
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={preferences}
|
||||
onPreferenceChange={(update) => savePreference(update)}
|
||||
>
|
||||
{children}
|
||||
</PreferencesProvider>
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Understand the Appearance package atoms, theme data, and preference boundary.
|
||||
order: 1
|
||||
toc:
|
||||
- id: package-role
|
||||
title: Package role
|
||||
- id: atomic-components
|
||||
title: Atomic components
|
||||
- id: preferences
|
||||
title: Preference integration
|
||||
---
|
||||
|
||||
## Package role {#package-role}
|
||||
|
||||
`@workspace/appearance` provides the definitions, effects, theme data, and UI components for theme modes, base colors, accent colors, and compact layouts. It does not create its own provider or store. The application aggregates it with other preference packages through `@workspace/preferences`.
|
||||
|
||||
## Atomic components {#atomic-components}
|
||||
|
||||
The controlled components can be composed independently without preference context:
|
||||
|
||||
- `ThemeModeField` selects light, dark, or system mode.
|
||||
- `CompactLayoutField` toggles the main content width.
|
||||
- `ThemeColorPicker` and `ThemeColorSwatchButton` select theme colors.
|
||||
- `ThemePreview` previews a set of theme variables.
|
||||
- `ThemeConfigCard` combines the preview, base color, and accent color controls.
|
||||
- `ThemeToggle` switches between light and dark themes.
|
||||
|
||||
`AppearanceSettings`, `AppearanceDialog`, and `AppearanceController` are optional composition layers built from these atoms.
|
||||
|
||||
## Preference integration {#preferences}
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createDefaultPreferences,
|
||||
PreferencesProvider,
|
||||
} from "@workspace/preferences"
|
||||
import {
|
||||
AppearanceController,
|
||||
appearancePreferenceDefinitions,
|
||||
appearancePreferenceEffect,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
const preferenceDefinitions = {
|
||||
...appearancePreferenceDefinitions,
|
||||
}
|
||||
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||||
const preferenceEffects = [appearancePreferenceEffect] as const
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={initialPreferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
</PreferencesProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Add definitions and effects from locale, keyboard, or other packages to the same objects. The application can persist every `{ key, value }` update through the shared `onPreferenceChange` and parse cookies on the server with the aggregated definitions.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: 原子化组合
|
||||
description: 不依赖预设弹窗,使用受控组件构建自己的外观界面。
|
||||
order: 1
|
||||
toc:
|
||||
- id: controlled-atoms
|
||||
title: 受控原子组件
|
||||
- id: compose-card
|
||||
title: 组合主题卡片
|
||||
- id: connected-layer
|
||||
title: 状态连接层
|
||||
---
|
||||
|
||||
## 受控原子组件 {#controlled-atoms}
|
||||
|
||||
`ThemeModeField`、`CompactLayoutField`、`ThemeColorPicker` 和 `ThemeToggle` 都由 `value` 与回调驱动。它们不要求使用偏好上下文,因此可以接入表单、预览工具或其他状态库。
|
||||
|
||||
```tsx
|
||||
<ThemeModeField value={mode} onValueChange={setMode} />
|
||||
<CompactLayoutField checked={compacted} onCheckedChange={setCompacted} />
|
||||
```
|
||||
|
||||
## 组合主题卡片 {#compose-card}
|
||||
|
||||
`ThemeConfigCard` 把预览、基础色和强调色选择组合在一起,但仍由调用方持有配置。
|
||||
|
||||
```tsx
|
||||
<ThemeConfigCard
|
||||
active={resolvedTheme === "light"}
|
||||
compacted={compacted}
|
||||
config={lightTheme}
|
||||
onConfigChange={setLightTheme}
|
||||
scheme="light"
|
||||
/>
|
||||
```
|
||||
|
||||
需要更细粒度的布局时,可以分别使用 `ThemePreview`、`baseColorSwatches`、`accentColorSwatches` 和 `ThemeColorSwatchButton`。
|
||||
|
||||
## 状态连接层 {#connected-layer}
|
||||
|
||||
`AppearanceSettings` 是原子组件与共享 `PreferencesProvider` 之间的默认连接层。自定义界面可以直接使用 `@workspace/preferences` 的 `usePreference` 读取单个字段,而不必复用默认布局。
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: 外观设置弹窗
|
||||
description: 组合触发器、控制器和快捷键得到完整的外观设置流程。
|
||||
order: 1
|
||||
toc:
|
||||
- id: controller
|
||||
title: 挂载控制器
|
||||
- id: trigger
|
||||
title: 添加触发器
|
||||
- id: custom-handle
|
||||
title: 独立弹窗实例
|
||||
---
|
||||
|
||||
## 挂载控制器 {#controller}
|
||||
|
||||
在应用共享的 `PreferencesProvider` 内挂载一次 `AppearanceController`。它组合默认设置内容,并注册 `Mod+,` 打开弹窗和 `Mod+D` 切换主题。
|
||||
|
||||
```tsx
|
||||
<PreferencesProvider
|
||||
effects={[appearancePreferenceEffect]}
|
||||
initialPreferences={preferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
<App />
|
||||
</PreferencesProvider>
|
||||
```
|
||||
|
||||
## 添加触发器 {#trigger}
|
||||
|
||||
`AppearanceDialogTrigger` 可以放在用户菜单、设置按钮或命令面板中。默认情况下,它会连接到默认控制器使用的 handle。
|
||||
|
||||
```tsx
|
||||
<AppearanceDialogTrigger render={<button type="button" />}>
|
||||
界面外观
|
||||
</AppearanceDialogTrigger>
|
||||
```
|
||||
|
||||
## 独立弹窗实例 {#custom-handle}
|
||||
|
||||
页面中需要隔离的外观弹窗时,为控制器和触发器传入同一个新 handle。
|
||||
|
||||
```tsx
|
||||
const handle = createAppearanceDialogHandle()
|
||||
|
||||
<AppearanceDialogTrigger handle={handle} render={<button type="button" />}>
|
||||
打开主题设置
|
||||
</AppearanceDialogTrigger>
|
||||
<AppearanceController handle={handle} />
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: 接入外观偏好
|
||||
description: 使用应用共享的偏好系统聚合外观定义与副作用。
|
||||
order: 1
|
||||
toc:
|
||||
- id: provider
|
||||
title: 聚合偏好
|
||||
- id: effects
|
||||
title: 注册副作用
|
||||
- id: theme-style
|
||||
title: 注入主题变量
|
||||
- id: persistence
|
||||
title: 持久化更新
|
||||
---
|
||||
|
||||
## 聚合偏好 {#provider}
|
||||
|
||||
在应用边界构建一份完整的偏好定义。把外观、语言、快捷键等功能 package 的 definitions 合并,再创建默认值或从服务端加载初始值。
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createDefaultPreferences,
|
||||
PreferencesProvider,
|
||||
} from "@workspace/preferences"
|
||||
import {
|
||||
appearancePreferenceDefinitions,
|
||||
appearancePreferenceEffect,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
const preferenceDefinitions = {
|
||||
...appearancePreferenceDefinitions,
|
||||
...localePreferenceDefinitions,
|
||||
}
|
||||
|
||||
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||||
const preferenceEffects = [
|
||||
appearancePreferenceEffect,
|
||||
localePreferenceEffect,
|
||||
] as const
|
||||
|
||||
export function Root({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={initialPreferences}
|
||||
>
|
||||
{children}
|
||||
</PreferencesProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
只需要外观偏好切片时仍可使用 `defaultAppearancePreferences`;聚合多个 package 时,应优先以 definitions 作为生成完整默认值的单一来源。
|
||||
|
||||
## 注册副作用 {#effects}
|
||||
|
||||
`appearancePreferenceEffect` 组合了两个公开回调:`applyAppearancePreferenceEffect` 在浏览器绘制前更新文档类名和主题变量,`subscribeSystemThemePreferenceEffect` 在绘制后监听操作系统主题。把组合后的 effect 放进传给 `PreferencesProvider` 的稳定 `effects` 数组中。
|
||||
|
||||
## 注入主题变量 {#theme-style}
|
||||
|
||||
使用 `buildThemeStyleSheet` 生成浅色、深色以及跟随系统时使用的 CSS 变量。保留固定的元素 ID 后,外观 effect 可以在颜色偏好变化时更新同一个节点。
|
||||
|
||||
```tsx
|
||||
import {
|
||||
buildThemeStyleSheet,
|
||||
THEME_STYLE_ELEMENT_ID,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
;<style
|
||||
id={THEME_STYLE_ELEMENT_ID}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: buildThemeStyleSheet(themeLight, themeDark),
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## 持久化更新 {#persistence}
|
||||
|
||||
共享的 `onPreferenceChange` 只在值实际变化时收到类型安全的 `{ key, value }`。应用可以把它交给服务端函数或 Cookie 写入器。
|
||||
|
||||
```tsx
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={preferences}
|
||||
onPreferenceChange={(update) => savePreference(update)}
|
||||
>
|
||||
{children}
|
||||
</PreferencesProvider>
|
||||
```
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: 概览
|
||||
description: 了解 Appearance 包的原子组件、主题数据和偏好边界。
|
||||
order: 1
|
||||
toc:
|
||||
- id: package-role
|
||||
title: 包的职责
|
||||
- id: atomic-components
|
||||
title: 原子组件
|
||||
- id: preferences
|
||||
title: 偏好集成
|
||||
---
|
||||
|
||||
## 包的职责 {#package-role}
|
||||
|
||||
`@workspace/appearance` 提供主题模式、基础色、强调色和紧凑布局所需的定义、副作用、主题数据与界面组件。它不创建自己的 Provider 或 store,而是由应用通过 `@workspace/preferences` 与其他偏好 package 统一聚合。
|
||||
|
||||
## 原子组件 {#atomic-components}
|
||||
|
||||
受控组件可以独立组合,不依赖偏好上下文:
|
||||
|
||||
- `ThemeModeField` 选择浅色、深色或跟随系统。
|
||||
- `CompactLayoutField` 切换主内容宽度。
|
||||
- `ThemeColorPicker` 与 `ThemeColorSwatchButton` 选择主题颜色。
|
||||
- `ThemePreview` 预览一组主题变量。
|
||||
- `ThemeConfigCard` 组合预览、基础色与强调色设置。
|
||||
- `ThemeToggle` 在浅色和深色主题之间切换。
|
||||
|
||||
`AppearanceSettings`、`AppearanceDialog` 与 `AppearanceController` 是建立在这些原子组件之上的可选组合层。
|
||||
|
||||
## 偏好集成 {#preferences}
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createDefaultPreferences,
|
||||
PreferencesProvider,
|
||||
} from "@workspace/preferences"
|
||||
import {
|
||||
AppearanceController,
|
||||
appearancePreferenceDefinitions,
|
||||
appearancePreferenceEffect,
|
||||
} from "@workspace/appearance"
|
||||
|
||||
const preferenceDefinitions = {
|
||||
...appearancePreferenceDefinitions,
|
||||
}
|
||||
const initialPreferences = createDefaultPreferences(preferenceDefinitions)
|
||||
const preferenceEffects = [appearancePreferenceEffect] as const
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<PreferencesProvider
|
||||
effects={preferenceEffects}
|
||||
initialPreferences={initialPreferences}
|
||||
>
|
||||
<AppearanceController />
|
||||
</PreferencesProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
把语言、快捷键等 package 的 definitions 和 effects 添加到相同对象即可。应用可以通过共享的 `onPreferenceChange` 持久化所有 `{ key, value }` 更新,并在服务端使用聚合后的 definitions 解析 Cookie。
|
||||
Reference in New Issue
Block a user