@qijenchen/design-system 0.1.0-beta.67 → 0.1.0-beta.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Alert/alert.d.ts.map +1 -1
- package/dist/components/Alert/alert.js +4 -4
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +0 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +1 -2
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/Popover/popover.d.ts +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +9 -10
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +0 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/patterns/overlay-surface/index.js +2 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +8 -0
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js +3 -1
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +1 -1
- package/ds-canonical/templates/dashboard-app.tsx +9 -4
- package/llms-full.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/components/Alert/alert.spec.md +1 -1
- package/src/components/Alert/alert.tsx +7 -4
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Checkbox/checkbox.principles.stories.tsx +18 -15
- package/src/components/Coachmark/coachmark.principles.stories.tsx +3 -2
- package/src/components/DataTable/data-table-filter-panel.tsx +3 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +3 -3
- package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -1
- package/src/components/Dialog/dialog.spec.md +11 -11
- package/src/components/Dialog/dialog.tsx +7 -8
- package/src/components/DropdownMenu/dropdown-menu.tsx +4 -1
- package/src/components/FileItem/file-item.spec.md +1 -1
- package/src/components/FileItem/file-item.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -4
- package/src/components/FileViewer/file-viewer.spec.md +4 -4
- package/src/components/FileViewer/file-viewer.tsx +6 -3
- package/src/components/Notice/notice.anatomy.stories.tsx +4 -4
- package/src/components/Notice/notice.spec.md +1 -0
- package/src/components/Notice/notice.stories.tsx +4 -4
- package/src/components/Popover/popover.anatomy.stories.tsx +13 -11
- package/src/components/Popover/popover.principles.stories.tsx +10 -8
- package/src/components/Popover/popover.spec.md +2 -2
- package/src/components/Popover/popover.tsx +14 -11
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +3 -2
- package/src/components/Sheet/sheet.principles.stories.tsx +5 -4
- package/src/components/Sidebar/sidebar.spec.md +2 -0
- package/src/components/Tooltip/tooltip.tsx +3 -1
- package/src/patterns/header-canonical/header-canonical.spec.md +3 -2
- package/src/patterns/overlay-surface/overlay-surface.spec.md +12 -10
- package/src/patterns/overlay-surface/overlay-surface.tsx +20 -8
- package/src/tokens/density/density.spec.md +33 -22
- package/src/tokens/layoutSpace/layoutSpace.stories.tsx +4 -4
|
@@ -294,7 +294,7 @@ expect_block "16. R9 手刻 <div px-loose border-b border-divider> overlay heade
|
|
|
294
294
|
run_hook "PreToolUse" "Write" "$TMP_DIR/r9-correct.stories.tsx" '
|
|
295
295
|
export const Panel = () => (
|
|
296
296
|
<div className="max-w-md flex flex-col rounded-lg border-border bg-surface-raised shadow-[var(--elevation-200)]">
|
|
297
|
-
<SurfaceHeader className="justify-between
|
|
297
|
+
<SurfaceHeader className="justify-between">
|
|
298
298
|
<div className="flex-1 min-w-0"><PopoverTitle>正在上傳 3 個項目</PopoverTitle></div>
|
|
299
299
|
</SurfaceHeader>
|
|
300
300
|
</div>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// 3. 替換 DashboardPage 為真實業務 widgets(DataTable / Chart / Card 等 DS 元件)
|
|
21
21
|
// 4. 替換 PageHeader rightSlot 的 primary action(若有)
|
|
22
22
|
|
|
23
|
-
import { useState } from 'react'
|
|
23
|
+
import { useState, type ReactElement } from 'react'
|
|
24
24
|
import {
|
|
25
25
|
AppShell,
|
|
26
26
|
SidebarProvider,
|
|
@@ -97,9 +97,14 @@ function AppSidebar() {
|
|
|
97
97
|
// 繞過 header-canonical 全部機械簽名 + 違反「消費 primitive 不 hand-craft」canonical;
|
|
98
98
|
// 對齊 _demo-helpers.tsx PageHeader 同款消費形)──
|
|
99
99
|
// SidebarTrigger 必有(primary-sidebar mode 的 menu toggle 入口,⌘B keyboard shortcut)
|
|
100
|
-
// rightSlot
|
|
101
|
-
//
|
|
102
|
-
|
|
100
|
+
// rightSlot 型別用 ReactElement<any, any>(= 舊全域 JSX.Element 的去全域等價寫法):
|
|
101
|
+
// - 不用裸 JSX.Element:React 19 @types/react 移除「全域」JSX namespace → `JSX.Element` 在 fresh React19 install
|
|
102
|
+
// 下 TS2503「Cannot find namespace 'JSX'」(本機 @types/react 19.2.15 仍含全域 shim → 源端 tsc 假陰性,
|
|
103
|
+
// 只在 receiver 拿到無 shim 的 19.x fresh install 才炸;2026-06-12 bde81e7e 引入,brick 下游 receiver build + audit)
|
|
104
|
+
// - 不用裸 ReactElement:@types/react@19 預設參數由 any 改 unknown,`ReactElement<unknown>` 因 ReactPortal.children
|
|
105
|
+
// 分支不可指派給 ReactNode(TS2322)→ 必顯式 <any, any>(JSX.Element 本就是 ReactElement<any, any>,語意不變)
|
|
106
|
+
// - 不用 ReactNode:workspace app 自帶 @types/react 副本與 DS .d.ts 的 ReactNode 版本 bigint 差異不相容
|
|
107
|
+
function PageHeader({ title, rightSlot }: { title: string; rightSlot?: ReactElement<any, any> }) {
|
|
103
108
|
return (
|
|
104
109
|
<ChromeHeader className="bg-surface">
|
|
105
110
|
<SidebarTrigger />
|
package/llms-full.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @qijenchen/design-system — 完整設計參考(llms-full)
|
|
2
2
|
|
|
3
|
-
> 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.
|
|
3
|
+
> 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.68。
|
|
4
4
|
|
|
5
5
|
# Components
|
|
6
6
|
|
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @qijenchen/design-system
|
|
2
2
|
|
|
3
3
|
> World-class React design system(Radix/shadcn + Tailwind v4 + 自訂 design token)。
|
|
4
|
-
> 54 components + 4 public patterns + design tokens。v0.1.0-beta.
|
|
4
|
+
> 54 components + 4 public patterns + design tokens。v0.1.0-beta.68。
|
|
5
5
|
|
|
6
6
|
本檔由 source(spec.md frontmatter + Storybook index)build-time 自動生成,**禁手改**(CI --check drift gate 守)。
|
|
7
7
|
每元件 / pattern 的完整 variants / sizes / 禁止事項 全文見 [llms-full.txt](./llms-full.txt)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qijenchen/design-system",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.68",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
|
|
6
6
|
"type": "module",
|
|
@@ -142,7 +142,7 @@ Alert chrome corner 可承載多個 action(close 左側並排):
|
|
|
142
142
|
|
|
143
143
|
### Subtle(預設)
|
|
144
144
|
|
|
145
|
-
淺底色 + 四邊 1px border(
|
|
145
|
+
淺底色 + 四邊 1px border(邊框採 `--{hue}-text` = icon 同色,2026-06-15 user 拍板;原借 `-hover` 狀態 token 是語意 smell 已改)。99% 場景用 subtle——視覺重量適中,使用者注意但可繼續主要任務。不設 `data-theme`,元素跟隨頁面 theme。
|
|
146
146
|
|
|
147
147
|
### Solid
|
|
148
148
|
|
|
@@ -23,12 +23,15 @@ import { Notice, useInverseTheme, SUBTLE_ICON_COLOR, type NoticeVariant } from '
|
|
|
23
23
|
* fixed: 無圓角,full-width,無 border
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
// border 用 `--{hue}-text`(= icon 同色,2026-06-15 user 拍板「直接共用 hue-text,不開新 token」)。
|
|
27
|
+
// 原借 `--{hue}-hover`(互動狀態 token)當靜態 border 是語意 smell + 跟 icon 不同色;
|
|
28
|
+
// 改共用 hue-text(SUBTLE_ICON_COLOR 用的同一顆,blue-7 等)→ 0 新 token + border/icon cohesion。
|
|
26
29
|
const SUBTLE_CONTAINER: Record<NoticeVariant, string> = {
|
|
27
30
|
neutral: 'bg-muted border border-border',
|
|
28
|
-
info: 'bg-info-subtle border border-[var(--info-
|
|
29
|
-
success: 'bg-success-subtle border border-[var(--success-
|
|
30
|
-
warning: 'bg-warning-subtle border border-[var(--warning-
|
|
31
|
-
error: 'bg-error-subtle border border-[var(--error-
|
|
31
|
+
info: 'bg-info-subtle border border-[var(--info-text)]',
|
|
32
|
+
success: 'bg-success-subtle border border-[var(--success-text)]',
|
|
33
|
+
warning: 'bg-warning-subtle border border-[var(--warning-text)]',
|
|
34
|
+
error: 'bg-error-subtle border border-[var(--error-text)]',
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
const SOLID_HUE_BG: Record<string, string> = {
|
|
@@ -370,9 +370,9 @@ Button 自動加 **`data-unbounded="true"`** attribute 當 **`variant === 'text'
|
|
|
370
370
|
|
|
371
371
|
**實際應用**:`SurfaceHeader` / `SurfaceFooter` 內建 CSS rule(SSOT 在 `overlay-surface.tsx` `CHROME_UNBOUNDED_SLOT`,此處僅引述不重述完整公式):
|
|
372
372
|
```css
|
|
373
|
-
[&_[data-unbounded]]:my-[calc((var(--chrome-slot-h,var(--
|
|
373
|
+
[&_[data-unbounded]]:my-[calc((var(--chrome-slot-h,calc(var(--font-body-lg-size)*1.5))-var(--field-height-sm))/2)]
|
|
374
374
|
```
|
|
375
|
-
→ slot 高度由 `--chrome-slot-h` 參數化(default `var(--
|
|
375
|
+
→ slot 高度由 `--chrome-slot-h` 參數化(default 衍生 `calc(var(--font-body-lg-size)*1.5)`=24;Popover-tier override = `COMPACT_HEADER_SLOT` 衍生 21;皆 title 字級改自動跟)
|
|
376
376
|
→ default(md): my=-2px / lg: my=-4px
|
|
377
377
|
→ 效果:Button native size 不變(sm 28/32,touch target 亦同),**layout 佔位縮到 24**(等效 xs 幾何),header = 24 + 2×tight = 48/56 = `--chrome-header-height` ✓
|
|
378
378
|
|
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import LinkTo from '@storybook/addon-links/react'
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
4
4
|
import { Checkbox } from './checkbox'
|
|
5
|
+
import { CheckboxGroup } from './checkbox-group'
|
|
5
6
|
import { Switch } from '@/design-system/components/Switch/switch'
|
|
6
7
|
import { RadioGroup, RadioGroupItem } from '@/design-system/components/RadioGroup/radio-group'
|
|
7
8
|
|
|
@@ -64,17 +65,19 @@ export const UsageGuidance: Story = {
|
|
|
64
65
|
note="勾選不代表立刻生效——使用者按「儲存」前都可以反悔。心智模型是「選擇 / 同意」,視覺語言強調「尚未確定」"
|
|
65
66
|
>
|
|
66
67
|
<div className="border border-border rounded-lg p-4 space-y-3">
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
<CheckboxGroup>
|
|
69
|
+
<Checkbox
|
|
70
|
+
label="我同意服務條款與隱私政策"
|
|
71
|
+
checked={agree}
|
|
72
|
+
onCheckedChange={(v) => setAgree(v === true)}
|
|
73
|
+
/>
|
|
74
|
+
<Checkbox
|
|
75
|
+
label="訂閱行銷訊息"
|
|
76
|
+
description="每月最多 2 封,可隨時取消"
|
|
77
|
+
checked={notif === 'checked'}
|
|
78
|
+
onCheckedChange={(v) => setNotif(v ? 'checked' : 'unchecked')}
|
|
79
|
+
/>
|
|
80
|
+
</CheckboxGroup>
|
|
78
81
|
<div className="flex gap-2 pt-2">
|
|
79
82
|
<button className="h-field-md px-3 text-body rounded-md bg-primary text-inverse-fg">儲存</button>
|
|
80
83
|
<button className="h-field-md px-3 text-body rounded-md border border-border">取消</button>
|
|
@@ -119,7 +122,7 @@ export const UsageGuidance: Story = {
|
|
|
119
122
|
title="vs Radio — Checkbox 是獨立 toggle(多選 或 單一 agreement)"
|
|
120
123
|
note="每個 Checkbox 是獨立 boolean——可同時勾多個。也用於單一「我同意 X」agreement"
|
|
121
124
|
>
|
|
122
|
-
<
|
|
125
|
+
<CheckboxGroup>
|
|
123
126
|
<Checkbox
|
|
124
127
|
label="讀取權限"
|
|
125
128
|
description="可查看資料"
|
|
@@ -138,7 +141,7 @@ export const UsageGuidance: Story = {
|
|
|
138
141
|
checked={perms.admin}
|
|
139
142
|
onCheckedChange={(v) => setPerms({ ...perms, admin: v === true })}
|
|
140
143
|
/>
|
|
141
|
-
</
|
|
144
|
+
</CheckboxGroup>
|
|
142
145
|
<Label>↑ 權限可以任意組合——多選用 Checkbox</Label>
|
|
143
146
|
</Rule>
|
|
144
147
|
|
|
@@ -158,11 +161,11 @@ export const UsageGuidance: Story = {
|
|
|
158
161
|
title="❌ 多選一用 Checkbox"
|
|
159
162
|
note="使用者可以同時勾 3 個 → 邏輯錯誤,破壞互斥語意"
|
|
160
163
|
>
|
|
161
|
-
<
|
|
164
|
+
<CheckboxGroup>
|
|
162
165
|
<Checkbox label="信用卡" />
|
|
163
166
|
<Checkbox label="銀行轉帳" />
|
|
164
167
|
<Checkbox label="貨到付款" />
|
|
165
|
-
</
|
|
168
|
+
</CheckboxGroup>
|
|
166
169
|
<Label warn>↑ 付款方式只能一個,卻用 Checkbox 允許多選 → 用 RadioGroup</Label>
|
|
167
170
|
</Rule>
|
|
168
171
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from '@/design-system/components/Dialog/dialog'
|
|
27
27
|
import { Button } from '@/design-system/components/Button/button'
|
|
28
28
|
import { Checkbox } from '@/design-system/components/Checkbox/checkbox'
|
|
29
|
+
import { CheckboxGroup } from '@/design-system/components/Checkbox/checkbox-group'
|
|
29
30
|
|
|
30
31
|
const meta: Meta = {
|
|
31
32
|
title: 'Design System/Components/Coachmark/設計原則',
|
|
@@ -138,10 +139,10 @@ export const UsageGuidance: Story = {
|
|
|
138
139
|
<PopoverContent align="start">
|
|
139
140
|
<PopoverHeader><PopoverTitle>依狀態篩選</PopoverTitle></PopoverHeader>
|
|
140
141
|
<PopoverBody>
|
|
141
|
-
<
|
|
142
|
+
<CheckboxGroup>
|
|
142
143
|
<Checkbox defaultChecked label="進行中" />
|
|
143
144
|
<Checkbox label="已完成" />
|
|
144
|
-
</
|
|
145
|
+
</CheckboxGroup>
|
|
145
146
|
</PopoverBody>
|
|
146
147
|
</PopoverContent>
|
|
147
148
|
</Popover>
|
|
@@ -13,7 +13,7 @@ import { NumberInput } from '@/design-system/components/NumberInput/number-input
|
|
|
13
13
|
import { DatePicker, DatePickerRange } from '@/design-system/components/DatePicker/date-picker'
|
|
14
14
|
import { PeoplePicker } from '@/design-system/components/PeoplePicker/people-picker'
|
|
15
15
|
import type { PersonValue } from '@/design-system/components/PeoplePicker/person-display'
|
|
16
|
-
import { SurfaceHeader, SurfaceBody } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
16
|
+
import { SurfaceHeader, SurfaceBody, COMPACT_HEADER_SLOT } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
17
17
|
import { PopoverTitle, PopoverClose } from '@/design-system/components/Popover/popover'
|
|
18
18
|
import { ButtonDivider } from '@/design-system/components/Button/button-group'
|
|
19
19
|
import { FieldControlGroup } from '@/design-system/components/FieldControlGroup/field-control-group'
|
|
@@ -551,8 +551,8 @@ function DataTableFilterPanelInner<TData>({
|
|
|
551
551
|
: 'w-[min(640px,calc(100vw-2rem))]',
|
|
552
552
|
className,
|
|
553
553
|
)}>
|
|
554
|
-
{/* Popover 派輕量 chrome — slot
|
|
555
|
-
<SurfaceHeader className=
|
|
554
|
+
{/* Popover 派輕量 chrome — slot 走 COMPACT_HEADER_SLOT(=21,衍生自 PopoverTitle text-body line-box),header 自然 ~45px */}
|
|
555
|
+
<SurfaceHeader className={COMPACT_HEADER_SLOT}>
|
|
556
556
|
<PopoverTitle className="flex-1">篩選</PopoverTitle>
|
|
557
557
|
{/* Refresh icon — 只在 value ≠ defaultValue 時顯示(對齊 sort modified-from-default UX)
|
|
558
558
|
含 ButtonDivider 對齊「欄位顯示」+「排序」chrome corner action canonical(2026-05-04) */}
|
|
@@ -10,7 +10,7 @@ import { cn } from '@/lib/utils'
|
|
|
10
10
|
import { dragSourceStyle, dragHandleCursorClass } from '@/design-system/lib/drag-visual'
|
|
11
11
|
import { Button } from '@/design-system/components/Button/button'
|
|
12
12
|
import { Select, type SelectOption } from '@/design-system/components/Select/select'
|
|
13
|
-
import { SurfaceHeader, SurfaceBody } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
13
|
+
import { SurfaceHeader, SurfaceBody, COMPACT_HEADER_SLOT } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
14
14
|
import { ButtonDivider } from '@/design-system/components/Button/button-group'
|
|
15
15
|
import { PopoverTitle, PopoverClose } from '@/design-system/components/Popover/popover'
|
|
16
16
|
import { ItemInlineActionButton } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
@@ -108,8 +108,8 @@ export function DataTableSortManager<TData>({
|
|
|
108
108
|
// 2026-05-23 Phase A.4 Decision 2: w-[480px] → token `--data-table-sort-panel-width`(SSOT in uiSize.css)
|
|
109
109
|
return (
|
|
110
110
|
<div className={cn('flex flex-col h-full min-h-0 w-[var(--data-table-sort-panel-width)]', className)}>
|
|
111
|
-
{/* Popover 派輕量 chrome — slot
|
|
112
|
-
<SurfaceHeader className=
|
|
111
|
+
{/* Popover 派輕量 chrome — slot 走 COMPACT_HEADER_SLOT(=21,衍生自 PopoverTitle text-body line-box),header 自然 ~45px */}
|
|
112
|
+
<SurfaceHeader className={COMPACT_HEADER_SLOT}>
|
|
113
113
|
<PopoverTitle className="flex-1">排序</PopoverTitle>
|
|
114
114
|
{onReset && sorting.length > 0 && (
|
|
115
115
|
<>
|
|
@@ -417,7 +417,7 @@ export const ColorMatrix: Story = {
|
|
|
417
417
|
<div className="flex flex-col gap-8">
|
|
418
418
|
<div>
|
|
419
419
|
<H3>Density</H3>
|
|
420
|
-
<Desc>Dialog
|
|
420
|
+
<Desc>Dialog 鎖 `data-layout-space="lg"`(只鎖版面間距 tier)+ ui-size 繼承 page `data-density`(控件不被撐大)。modal 值得寬鬆呼吸 padding:body px-loose 24 / header py-tight 16 → header 56(宣告 lg tier)。reinstate c3d3b736 decouple(只鎖 layout-space,非當年連 ui-size 一起鎖撐高 header 的 density)。世界級:Material M3 / Atlassian modal padding 24。</Desc>
|
|
421
421
|
</div>
|
|
422
422
|
|
|
423
423
|
<div>
|
|
@@ -58,17 +58,17 @@ DialogContent (fixed, centered)
|
|
|
58
58
|
|
|
59
59
|
**Padding SSOT**:Header / Body / Footer 的 padding + 分隔線由 `patterns/overlay-surface/overlay-surface.spec.md` own——Dialog 與 Popover 共用同一套 primitive,避免 token 漂移。Dialog 特有行為:Header 的 Close 按鈕;Body 用 `<ScrollArea>` wrap(viewport-fill 專用,SSOT 見 overlay-surface.spec.md 「Body overflow canonical」節 + `components/ScrollArea/scroll-area.spec.md`)。
|
|
60
60
|
|
|
61
|
-
## Density(2026-
|
|
61
|
+
## Density(2026-06-16 定論:全繼承 page,不鎖)
|
|
62
62
|
|
|
63
|
-
Dialog
|
|
63
|
+
Dialog **不自設任何 density attribute**,layout-space + ui-size 全繼承 page。效果:md page → body `px-loose`=16 / header `py-tight`=12(header 48);lg page → 24 / 16(header 56),隨 page。
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
**為何不鎖(撤回本 session 一度加的 `data-layout-space="lg"`)**:本 `density.spec` 第 10 行親自定義 layout-space 管「dialog body padding」—— Dialog 若鎖死 layout-space,等於 override 我們自家 dial 對它點名要管的對象失效 = **自相矛盾**。有「同類 padding-density dial」的世界級 DS(SAP Fiori cozy/compact 做 `syncStyleClass` 專門把 page density 同步進 dialog;AWS Cloudscape compact「propagate to all components, in all view types」,例外只列 informational + tiny dropdown,modal 不在內)都讓 **modal 跟 page dial 走**,沒有一家把 modal 釘固定 tier。Material/Atlassian 的固定 24 是因為它們**沒有** density dial(其 24 = 我方 lg)。
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
**「modal 要寬鬆呼吸」怎麼滿足**:lg 產品自動拿 24(Material 級);md 產品本就要緊湊(資訊密集),16 是合格 modal padding(Polaris Modal = 16,世界級下限)。**「button 不撐高 header」**由 ui-size 繼承 page 解決(button = page sm),與 layout-space 鎖不鎖無關 → 故不需鎖。
|
|
68
|
+
|
|
69
|
+
**Header 高度**:padding-based = title line-box 24 + `py-tight`×2 → md 48 / lg 56,**隨 page tier**(= `--chrome-header-height` 同 tier 對齊)。
|
|
70
|
+
|
|
71
|
+
**世界級對照**:Material M3 / Atlassian 24(無 dial,= 我方 lg)/ Polaris 16(= 我方 md 下限)/ SAP Fiori + Cloudscape(有 dial → modal 跟 dial = 我方繼承)。
|
|
72
72
|
|
|
73
73
|
## Layout
|
|
74
74
|
|
|
@@ -99,8 +99,8 @@ Modal 與 viewport 四邊保持 `--layout-space-bottom`(48px)最小間距。
|
|
|
99
99
|
|
|
100
100
|
永遠存在於 DialogHeader 右側。使用 `<Button data-dismiss iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />`,不可移除——使用者永遠需要明確的關閉手段。
|
|
101
101
|
|
|
102
|
-
**Size canonical(v5 chrome-unbounded)**:Button native size **sm**(28 md / 32 lg),touch target 亦同。SurfaceHeader 的 `[data-unbounded]` CSS rule 自動對 text variant / dismiss 套負 my
|
|
103
|
-
- Header 只有 title + close X → max layout = 24 → header = 24 + 2
|
|
102
|
+
**Size canonical(v5 chrome-unbounded)**:Button native size **sm**(隨 page density:28 md / 32 lg),touch target 亦同。SurfaceHeader 的 `[data-unbounded]` CSS rule 自動對 text variant / dismiss 套負 my → **layout 佔位 = title line-box**(slot 衍生自 `--font-body-lg-size`×1.5=24,見 `overlay-surface.spec.md` slot 段)。效果:
|
|
103
|
+
- Header 只有 title + close X → max layout = 24 → header = 24 + 2×`py-tight`(隨 page:md 12 / lg 16)= **48 md / 56 lg**(= `--chrome-header-height` 同 tier 對齊)✓
|
|
104
104
|
- Header 塞 bounded primary(無 `data-unbounded`)→ header 自然長高
|
|
105
105
|
|
|
106
106
|
**Canonical 來源**:Dialog 是 overlay chrome,corner close X 屬 action group region,必用 Button(非 Inline Action / 非自刻 button)。詳見 `patterns/element-anatomy/item-anatomy.spec.md`「Dismiss canonical」+ `patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」。
|
|
@@ -142,7 +142,7 @@ Dialog 是容器,無整體 disabled / loading / empty 狀態——這些屬於
|
|
|
142
142
|
|
|
143
143
|
**Dark mode**:由 semantic token(`bg-surface-raised` / `border-border`)自動切換,無自訂 palette。
|
|
144
144
|
|
|
145
|
-
**Density**:Dialog
|
|
145
|
+
**Density**:Dialog **全繼承 page**(layout-space + ui-size 皆不自鎖),見上「Density」段。
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
@@ -95,14 +95,13 @@ const DialogContent = React.forwardRef<
|
|
|
95
95
|
<DialogOverlay />
|
|
96
96
|
<DialogPrimitive.Content
|
|
97
97
|
ref={ref}
|
|
98
|
-
// Density
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
// 詳 overlay-surface.spec.md「Chrome dismiss size canonical」
|
|
98
|
+
// Density:**全繼承 page**(layout-space + ui-size 都不自鎖)。2026-06-16 定論(撤回本 session 一度加的
|
|
99
|
+
// data-layout-space="lg"):density.spec 第 10 行親自定義 layout-space 管「dialog body padding」——
|
|
100
|
+
// Dialog 鎖死它 = override 自家 dial 對它點名要管的對象失效 = 自相矛盾。有同類 padding-density dial 的
|
|
101
|
+
// 世界級(SAP Fiori syncStyleClass / AWS Cloudscape「all view types」)都讓 modal 跟 page dial 走、不鎖固定 tier。
|
|
102
|
+
// 效果:md page → body px-loose 16 / header py-tight 12(header 48);lg page → 24 / 16(header 56),隨 page。
|
|
103
|
+
// 「modal 要寬鬆」需求在 lg 階自然滿足(Polaris modal 16 = 世界級下限,證明 md 16 合格);「button 不撐高
|
|
104
|
+
// header」由 ui-size 繼承 page 解決(button=page sm),與 layout-space 鎖不鎖無關 → 故不需鎖。
|
|
106
105
|
onOpenAutoFocus={handleOpenAutoFocus}
|
|
107
106
|
className={cn(
|
|
108
107
|
"fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2",
|
|
@@ -129,7 +129,10 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
129
129
|
sideOffset={sideOffset}
|
|
130
130
|
collisionPadding={collisionPadding}
|
|
131
131
|
align={align}
|
|
132
|
-
|
|
132
|
+
// Density:繼承 page density(2026-06-15 canonical)。menu item 高度 = field-height-{size},而
|
|
133
|
+
// field-height 隨 density 變(md 28/32/36 → lg 32/36/40)→ 鎖 data-density="md" 會把選單釘在 md-scale,
|
|
134
|
+
// lg page 上對不上 lg 觸發點。原 data-density 是 409b91da a11y 批次順手加(對齊 Popover),非設計
|
|
135
|
+
// 決策 → 移除,item 隨 page density 與觸發點一致(tier 仍由 size prop 決定)。
|
|
133
136
|
// Focus return on close:不 override `onCloseAutoFocus` — 用 Radix 內建 default
|
|
134
137
|
// (close 時 focus 還 trigger;outside-interaction 例外由 Radix `hasInteractedOutsideRef` 自管)。
|
|
135
138
|
// W3C APG menubar「Escape: …return focus to the element…from which the menu was opened」
|
|
@@ -293,7 +293,7 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
|
|
|
293
293
|
- **它是 popover-class 浮層 surface,但不是 Radix `<Popover>`**(常駐面板:不靠 trigger 開、不 outside-click 關、用 chevron 收合非 X dismiss)→ **不包 `<Popover>`**,而是直接消費 overlay-surface 三件套 primitive。
|
|
294
294
|
- **殼 + header + body 全消費 overlay-surface SSOT(禁手刻)**:
|
|
295
295
|
- 殼:用 Popover 同款 chrome token `rounded-lg border border-border bg-surface-raised shadow-[var(--elevation-200)] flex flex-col`(DS 無獨立 shell primitive — `PopoverContent`/`DialogContent` 各自套這組 token;常駐面板鏡像同值)。
|
|
296
|
-
- header:`<SurfaceHeader className="justify-between
|
|
296
|
+
- header:`<SurfaceHeader className={cn("justify-between", COMPACT_HEADER_SLOT)}>` + `<PopoverTitle>`(輕量浮層 header SSOT,slot 走 `COMPACT_HEADER_SLOT`=21 衍生自 text-body title;padding = px-loose py-tight + border-b + unbounded-slot 負 my trick)。
|
|
297
297
|
- body:**`<SurfaceBody>`(body SSOT,含 px-loose py-tight + flex-1 scroll 鏈)**,FileItem-specific padding 用 className override(見下)。**這不是「List-as-region」**(那專指 edge-to-edge 選單清單:item hover-bg 貼容器、px-0;Cmd+K / menu / nav)—— upload-manager list 有 px-loose、item 無 hover-bg、是 chrome-padded body,故就用 SurfaceBody。**scroll(consumer 注意)**:SurfaceBody 的 `flex-1 / overflow-y-auto` 只在 shell 有 `max-h` + `overflow-hidden` 時生效;常駐面板若檔案數可超 viewport,shell 須加 `max-h`(對齊 overlay-surface.spec.md「viewport-aware scroll」),demo 短內容不需。
|
|
298
298
|
- **禁手刻**:`<div px-loose py-2 border-b>`(header)/ 手刻 `<div px-loose py-tight>`(body)= drift(2026-06-03/04 user 抓:py-2≠py-tight / 殼 token 全偏 / body 重刻 SurfaceBody)。Hook `check_story_invariants.sh R9` 機械攔手刻 header。
|
|
299
299
|
|
|
@@ -6,7 +6,7 @@ import { FileItem } from './file-item'
|
|
|
6
6
|
import { Button } from '@/design-system/components/Button/button'
|
|
7
7
|
import { FileViewer, type FileInfo } from '@/design-system/components/FileViewer/file-viewer'
|
|
8
8
|
// upload-manager 面板消費 overlay-surface header + body SSOT(非手刻)—— 跟 Popover/Dialog 同一組 primitive
|
|
9
|
-
import { SurfaceHeader, SurfaceBody } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
9
|
+
import { SurfaceHeader, SurfaceBody, COMPACT_HEADER_SLOT } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
10
10
|
import { PopoverTitle } from '@/design-system/components/Popover/popover'
|
|
11
11
|
|
|
12
12
|
// 錯誤 description 範例(含 clickable "View log"):consumer 自由 ReactNode,通常用底線 link 表 clickable
|
|
@@ -217,7 +217,7 @@ export const UploadManagerSurface = {
|
|
|
217
217
|
<div className="max-w-md flex flex-col rounded-lg border border-border bg-surface-raised shadow-[var(--elevation-200)]">
|
|
218
218
|
{/* header 消費 overlay-surface SurfaceHeader + PopoverTitle(輕量浮層 chrome SSOT,非手刻):
|
|
219
219
|
px-loose py-tight + border-b + chevron(variant=text → 自動 data-unbounded → 套 slot 負 my trick)*/}
|
|
220
|
-
<SurfaceHeader className=
|
|
220
|
+
<SurfaceHeader className={`justify-between ${COMPACT_HEADER_SLOT}`}>
|
|
221
221
|
<div className="flex-1 min-w-0"><PopoverTitle>正在上傳 3 個項目</PopoverTitle></div>
|
|
222
222
|
<Button iconOnly variant="text" size="sm" startIcon={ChevronDown} aria-label="收合" onClick={noop} />
|
|
223
223
|
</SurfaceHeader>
|
|
@@ -243,7 +243,7 @@ export const UploadManagerCompactSurface = {
|
|
|
243
243
|
render: () => (
|
|
244
244
|
<div className="max-w-md flex flex-col rounded-lg border border-border bg-surface-raised shadow-[var(--elevation-200)]">
|
|
245
245
|
{/* header 消費同一個 overlay-surface SurfaceHeader + PopoverTitle SSOT(同 rich panel)*/}
|
|
246
|
-
<SurfaceHeader className=
|
|
246
|
+
<SurfaceHeader className={`justify-between ${COMPACT_HEADER_SLOT}`}>
|
|
247
247
|
<div className="flex-1 min-w-0"><PopoverTitle>同步 3 個檔案</PopoverTitle></div>
|
|
248
248
|
<Button iconOnly variant="text" size="sm" startIcon={ChevronDown} aria-label="收合" onClick={noop} />
|
|
249
249
|
</SurfaceHeader>
|
|
@@ -394,7 +394,7 @@ export const Inspector: Story = {
|
|
|
394
394
|
</tr>
|
|
395
395
|
<tr>
|
|
396
396
|
<Td>Toolbar 高度</Td>
|
|
397
|
-
<Td mono>--chrome-header-height(lg
|
|
397
|
+
<Td mono>--chrome-header-height(隨 page:48 md / 56 lg,ChromeHeader 繼承 page,與 InfoPanel header 等高)</Td>
|
|
398
398
|
</tr>
|
|
399
399
|
<tr>
|
|
400
400
|
<Td>Toolbar 水平 padding</Td>
|
|
@@ -632,9 +632,9 @@ export const SizeMatrix: Story = {
|
|
|
632
632
|
</tr>
|
|
633
633
|
<tr>
|
|
634
634
|
<Td>Toolbar 高</Td>
|
|
635
|
-
<Td mono>--chrome-header-height(lg
|
|
635
|
+
<Td mono>--chrome-header-height(48 md / 56 lg)</Td>
|
|
636
636
|
<Td>Figma toolbar 48–56 / macOS Preview 56</Td>
|
|
637
|
-
<Td>ChromeHeader
|
|
637
|
+
<Td>ChromeHeader 繼承 page density,token 驅動;md=48 / lg=56(2026-06-15 移除 lockDensity)</Td>
|
|
638
638
|
</tr>
|
|
639
639
|
<tr>
|
|
640
640
|
<Td>InfoPanel 寬</Td>
|
|
@@ -698,7 +698,7 @@ export const SizeMatrix: Story = {
|
|
|
698
698
|
<tr>
|
|
699
699
|
<Td>Panel header 高</Td>
|
|
700
700
|
<Td mono>--chrome-header-height</Td>
|
|
701
|
-
<Td>消費 <ChromeHeader
|
|
701
|
+
<Td>消費 <ChromeHeader>(繼承 page density);md=48 / lg=56,與 Toolbar 等高,視覺水平對齊</Td>
|
|
702
702
|
</tr>
|
|
703
703
|
<tr>
|
|
704
704
|
<Td>Body 水平 padding</Td>
|
|
@@ -100,7 +100,7 @@ Family 的 canonical 規定的是「同用途同 layout」;FileViewer 用途(ful
|
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
**分區決策**:
|
|
103
|
-
- **Toolbar 高度 = `--chrome-header-height`**(md=48 / lg=56)——與 InfoPanel header 同高,視覺對齊;**code 已消費 `<ChromeHeader>` primitive**(
|
|
103
|
+
- **Toolbar 高度 = `--chrome-header-height`**(隨 page density:md=48 / lg=56)——與 InfoPanel header 同高,視覺對齊;**code 已消費 `<ChromeHeader>` primitive**(Toolbar + InfoPanel header,皆 `<ChromeHeader>` **繼承 page density**,2026-06-15 移除 lockDensity),高度由 primitive 內部套 `h-[var(--chrome-header-height)]`(`chrome-header.tsx`),tsx 不硬寫 height className。**跨家族 SSOT pointer**:FileViewer Toolbar + InfoPanel header 屬 **Chrome header(Fixed-h)家族**,border / padding / dismiss size / withTabs 跨家族契約 SSOT 詳 `patterns/header-canonical/header-canonical.spec.md`。FileViewer **不鎖 density**(全 surface 繼承 page;原 `lockDensity="lg"` 只鎖兩個 header、沒鎖 body → header(px-loose@lg=24)與 body(px-loose@page=16)左緣不對齊 = 圖二 bug,已移除,見 `density.spec.md` 消費者清單)。
|
|
104
104
|
- **Viewport `flex-1`**——填滿剩餘空間;InfoPanel 透過 `w-80 shrink-0` 從右側切出,不吃 viewport 自然寬
|
|
105
105
|
- **Filmstrip 固定 h-24**——預留 thumb 64 + padding;只在 `showFilmstrip && files.length > 1` 時顯示
|
|
106
106
|
- **Prev/Next arrows 絕對定位**——避免 layout shift,只在 `files.length > 1` 渲染
|
|
@@ -244,7 +244,7 @@ Shell 看到 `pageNumber` capability 時自動在 toolbar 顯示 page navigator(
|
|
|
244
244
|
|
|
245
245
|
### 同 flex 列幾何鐵律(CLAUDE.md 規則)
|
|
246
246
|
|
|
247
|
-
`[−]` / `[%input]` / `[+]` 三個 slot **都是 h-field-sm**,統一高度確保 gap 不被 hover bg 吃掉。Toolbar 包在 `<ChromeHeader
|
|
247
|
+
`[−]` / `[%input]` / `[+]` 三個 slot **都是 h-field-sm**,統一高度確保 gap 不被 hover bg 吃掉。Toolbar 包在 `<ChromeHeader>`(繼承 page density,2026-06-15 移除 lockDensity)內 → h-field-sm 隨 page density 解析(`uiSize.spec.md`:sm 在 md = 28px / lg = 32px)。Button iconOnly size="sm" aspect-square 與 Input size="sm" 同高,視覺嚴格對齊。
|
|
248
248
|
|
|
249
249
|
### Why inline(不抽獨立 primitive)
|
|
250
250
|
|
|
@@ -255,7 +255,7 @@ Shell 看到 `pageNumber` capability 時自動在 toolbar 顯示 page navigator(
|
|
|
255
255
|
## InfoPanel 規則
|
|
256
256
|
|
|
257
257
|
- **寬度固定 w-80(320px)**——對齊 Figma right panel(320)的業界慣例;Google Photos 用 360 偏寬,FileViewer 走 Figma 偏窄以讓 viewport 多一些空間 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
258
|
-
- **Header 高度 = `--chrome-header-height`**(
|
|
258
|
+
- **Header 高度 = `--chrome-header-height`**(隨 page density:48 md / 56 lg)——與 Toolbar 等高,視覺對齊;消費 `<ChromeHeader>`(繼承 page,2026-06-15 移除 lockDensity)
|
|
259
259
|
- **內容分兩區**:
|
|
260
260
|
- 「說明」Textarea(可編輯 / readOnly 依 `readOnly` prop)
|
|
261
261
|
- 「檔案資訊」`<dl>`:檔名 / 類型 / 大小 / 自訂 metadata 條目
|
|
@@ -310,7 +310,7 @@ Shell 看到 `pageNumber` capability 時自動在 toolbar 顯示 page navigator(
|
|
|
310
310
|
|
|
311
311
|
**Dark mode**:FileViewer chrome 鎖 dark(`data-theme="dark"` subtree);背景頁面的 theme 不影響 viewer chrome——viewer 是獨立沉浸式 context,類似 Tooltip / 全螢幕影片播放器的 convention。
|
|
312
312
|
|
|
313
|
-
**Density**:FileViewer
|
|
313
|
+
**Density**:FileViewer **全 surface 繼承 page density**(2026-06-15 移除 lockDensity — 原本只鎖兩個 ChromeHeader、沒鎖 body → header(px-loose@lg=24)與 body(px-loose@page=16)左緣不對齊,圖二 bug)。chrome-header-height 隨 page(48 md / 56 lg),header 與 body 同密度 → 左緣對齊;Filmstrip `h-24`(96px)+ thumb 64×64 屬媒體展示框尺寸,**刻意不隨 density 放大**(viewer 是展示殼不是工作區)。Toolbar 內 `<Button size="sm">` 與 `<ZoomInput h-field-sm>` 隨 page density(sm 在 md = 28px / lg = 32px)。
|
|
314
314
|
|
|
315
315
|
---
|
|
316
316
|
|
|
@@ -338,7 +338,9 @@ const Toolbar: React.FC<ToolbarProps> = ({
|
|
|
338
338
|
}) => {
|
|
339
339
|
return (
|
|
340
340
|
<ChromeHeader
|
|
341
|
-
lockDensity="lg"
|
|
341
|
+
// Density:繼承 page(2026-06-15 canonical)。原 lockDensity="lg" 只鎖在 header、沒鎖 body →
|
|
342
|
+
// InfoPanel header(px-loose@lg=24)與其 body(px-loose@page=16)左緣不對齊(圖二 bug)。移除 lock
|
|
343
|
+
// → header + body 全繼承 page density,左緣對齊;FileViewer 全 surface 同一密度(non-special)。
|
|
342
344
|
className={cn(
|
|
343
345
|
// Chrome layer — `bg-surface-raised` 對齊 token semantic「遮蓋型浮層必須不透明」。
|
|
344
346
|
// FileViewer 整體是 overlay,chrome 屬其 raised surface(同 DropdownMenuContent line 244)。
|
|
@@ -467,8 +469,9 @@ const InfoPanel: React.FC<InfoPanelProps> = ({
|
|
|
467
469
|
)}
|
|
468
470
|
aria-label={labels.detailPanel}
|
|
469
471
|
>
|
|
470
|
-
{/* Panel header — 與 Toolbar
|
|
471
|
-
|
|
472
|
+
{/* Panel header — 與 Toolbar 同 ChromeHeader,繼承 page density(2026-06-15:移除 lockDensity,
|
|
473
|
+
修 header(原 px-loose@lg=24)與 body(px-loose@page=16)左緣不對齊 = 圖二 bug;全 surface 同密度)。 */}
|
|
474
|
+
<ChromeHeader className="justify-between">
|
|
472
475
|
<h3 className="text-body-lg font-medium text-foreground">{labels.detailsHeading}</h3>
|
|
473
476
|
{/* InfoPanel close 走 dismiss canonical `<Button iconOnly dismiss />`,對齊 button.spec.md
|
|
474
477
|
「Dismiss 視覺類」+ inline-action.spec.md「Dismiss canonical — X close only」。 */}
|
|
@@ -53,10 +53,10 @@ const VARIANT_ICON: Record<NoticeVariant, LucideIcon | null> = {
|
|
|
53
53
|
|
|
54
54
|
const SUBTLE_CONTAINER: Record<NoticeVariant, string> = {
|
|
55
55
|
neutral: 'bg-muted border border-border',
|
|
56
|
-
info: 'bg-info-subtle border border-[var(--info-
|
|
57
|
-
success: 'bg-success-subtle border border-[var(--success-
|
|
58
|
-
warning: 'bg-warning-subtle border border-[var(--warning-
|
|
59
|
-
error: 'bg-error-subtle border border-[var(--error-
|
|
56
|
+
info: 'bg-info-subtle border border-[var(--info-text)]',
|
|
57
|
+
success: 'bg-success-subtle border border-[var(--success-text)]',
|
|
58
|
+
warning: 'bg-warning-subtle border border-[var(--warning-text)]',
|
|
59
|
+
error: 'bg-error-subtle border border-[var(--error-text)]',
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function SubtleShell({ variant, children }: { variant: NoticeVariant; children: React.ReactNode }) {
|
|
@@ -26,10 +26,10 @@ type Story = StoryObj<typeof Notice>
|
|
|
26
26
|
|
|
27
27
|
const SUBTLE_CONTAINER: Record<NoticeVariant, string> = {
|
|
28
28
|
neutral: 'bg-muted border border-border',
|
|
29
|
-
info: 'bg-info-subtle border border-[var(--info-
|
|
30
|
-
success: 'bg-success-subtle border border-[var(--success-
|
|
31
|
-
warning: 'bg-warning-subtle border border-[var(--warning-
|
|
32
|
-
error: 'bg-error-subtle border border-[var(--error-
|
|
29
|
+
info: 'bg-info-subtle border border-[var(--info-text)]',
|
|
30
|
+
success: 'bg-success-subtle border border-[var(--success-text)]',
|
|
31
|
+
warning: 'bg-warning-subtle border border-[var(--warning-text)]',
|
|
32
|
+
error: 'bg-error-subtle border border-[var(--error-text)]',
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const SOLID_BG: Record<Exclude<NoticeVariant, 'neutral'>, string> = {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
3
|
import { Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverBody, PopoverFooter, PopoverTitle } from './popover'
|
|
4
4
|
import { Button } from '@/design-system/components/Button/button'
|
|
5
|
+
import { Checkbox } from '@/design-system/components/Checkbox/checkbox'
|
|
6
|
+
import { CheckboxGroup } from '@/design-system/components/Checkbox/checkbox-group'
|
|
5
7
|
import { H3, Desc, Td, Th, TokenCell } from '@/design-system/stories-helpers/anatomy/anatomy-utils'
|
|
6
8
|
|
|
7
9
|
const meta: Meta = {
|
|
@@ -26,11 +28,11 @@ export const Overview: Story = {
|
|
|
26
28
|
<PopoverContent>
|
|
27
29
|
<PopoverHeader><PopoverTitle>依類型篩選</PopoverTitle></PopoverHeader>
|
|
28
30
|
<PopoverBody>
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
<label
|
|
33
|
-
</
|
|
31
|
+
<CheckboxGroup>
|
|
32
|
+
<Checkbox defaultChecked label="待處理" />
|
|
33
|
+
<Checkbox defaultChecked label="進行中" />
|
|
34
|
+
<Checkbox label="已完成" />
|
|
35
|
+
</CheckboxGroup>
|
|
34
36
|
</PopoverBody>
|
|
35
37
|
<PopoverFooter>
|
|
36
38
|
<Button variant="tertiary" size="sm" className="flex-1">清除</Button>
|
|
@@ -129,11 +131,11 @@ export const Inspector: Story = {
|
|
|
129
131
|
<PopoverHeader><PopoverTitle>依類型篩選</PopoverTitle></PopoverHeader>
|
|
130
132
|
)}
|
|
131
133
|
<PopoverBody>
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
<label
|
|
136
|
-
</
|
|
134
|
+
<CheckboxGroup>
|
|
135
|
+
<Checkbox defaultChecked label="待處理" />
|
|
136
|
+
<Checkbox defaultChecked label="進行中" />
|
|
137
|
+
<Checkbox label="已完成" />
|
|
138
|
+
</CheckboxGroup>
|
|
137
139
|
</PopoverBody>
|
|
138
140
|
{showFooter && (
|
|
139
141
|
<PopoverFooter>
|
|
@@ -194,7 +196,7 @@ export const ColorMatrix: Story = {
|
|
|
194
196
|
<tr><Td>次要文字</Td><Td><TokenCell token="--fg-secondary" display="text-fg-secondary" /></Td><Td>說明 / 次要資訊</Td></tr>
|
|
195
197
|
<tr><Td>圓角</Td><Td mono>rounded-lg(8px)</Td><Td>對齊 Dialog radius(浮層視覺語言一致)</Td></tr>
|
|
196
198
|
<tr><Td>陰影</Td><Td mono>--elevation-200</Td><Td>浮層級 elevation(見 `elevation.spec.md`)</Td></tr>
|
|
197
|
-
<tr><Td>
|
|
199
|
+
<tr><Td>Layout-space</Td><Td mono>data-layout-space="md"</Td><Td>只鎖版面間距(header py-tight 精簡);ui-size 跟 page → 內部控件對齊觸發點(2026-06-15 改,原 data-density master switch)</Td></tr>
|
|
198
200
|
<tr><Td>Header/Body/Footer padding</Td><Td mono>px-[loose] py-[tight]</Td><Td>結構化 sub-components 採 Dialog 同一套 padding token</Td></tr>
|
|
199
201
|
<tr><Td>Portal z-index</Td><Td mono>z-50</Td><Td>Radix Portal 統一層級,避開一般頁面內容</Td></tr>
|
|
200
202
|
</tbody>
|