@qijenchen/design-system 0.1.0-beta.3

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.
Files changed (119) hide show
  1. package/package.json +93 -0
  2. package/src/README.md +32 -0
  3. package/src/components/Accordion/accordion.tsx +104 -0
  4. package/src/components/Alert/alert.tsx +188 -0
  5. package/src/components/AppShell/_demo-helpers.tsx +198 -0
  6. package/src/components/AppShell/app-shell.tsx +364 -0
  7. package/src/components/AspectRatio/aspect-ratio.tsx +58 -0
  8. package/src/components/Avatar/avatar.tsx +368 -0
  9. package/src/components/Badge/badge.tsx +104 -0
  10. package/src/components/Breadcrumb/breadcrumb.tsx +609 -0
  11. package/src/components/BulkActionBar/bulk-action-bar.tsx +156 -0
  12. package/src/components/Button/button-group.tsx +96 -0
  13. package/src/components/Button/button.tsx +539 -0
  14. package/src/components/Calendar/calendar.tsx +411 -0
  15. package/src/components/Carousel/carousel.tsx +371 -0
  16. package/src/components/Chart/chart.tsx +376 -0
  17. package/src/components/Checkbox/checkbox-group.tsx +94 -0
  18. package/src/components/Checkbox/checkbox.tsx +237 -0
  19. package/src/components/Chip/chip.tsx +359 -0
  20. package/src/components/CircularProgress/circular-progress.tsx +204 -0
  21. package/src/components/Coachmark/coachmark.tsx +255 -0
  22. package/src/components/Combobox/combobox.tsx +826 -0
  23. package/src/components/Command/command.tsx +187 -0
  24. package/src/components/DataTable/active-editor-controller.ts +72 -0
  25. package/src/components/DataTable/cell-registry.tsx +520 -0
  26. package/src/components/DataTable/column-types.ts +180 -0
  27. package/src/components/DataTable/data-table-column-visibility-panel.tsx +261 -0
  28. package/src/components/DataTable/data-table-filter-panel.tsx +813 -0
  29. package/src/components/DataTable/data-table-interaction-layer.tsx +483 -0
  30. package/src/components/DataTable/data-table-sort-manager.tsx +210 -0
  31. package/src/components/DataTable/data-table.css +165 -0
  32. package/src/components/DataTable/data-table.tsx +2924 -0
  33. package/src/components/DataTable/filter-operators.ts +225 -0
  34. package/src/components/DataTable/filter-tree.ts +313 -0
  35. package/src/components/DataTable/lib/column-meta.ts +79 -0
  36. package/src/components/DateGrid/date-grid.tsx +209 -0
  37. package/src/components/DatePicker/date-picker.tsx +1114 -0
  38. package/src/components/DescriptionList/description-list.tsx +141 -0
  39. package/src/components/Dialog/dialog.tsx +267 -0
  40. package/src/components/DropdownMenu/dropdown-menu.tsx +475 -0
  41. package/src/components/Empty/empty.tsx +108 -0
  42. package/src/components/Field/field-context.ts +136 -0
  43. package/src/components/Field/field-types.ts +52 -0
  44. package/src/components/Field/field-wrapper.tsx +348 -0
  45. package/src/components/Field/field.tsx +535 -0
  46. package/src/components/FieldControlGroup/field-control-group.tsx +136 -0
  47. package/src/components/FileItem/file-item.tsx +322 -0
  48. package/src/components/FileUpload/file-upload.tsx +326 -0
  49. package/src/components/FileViewer/file-viewer-types.ts +76 -0
  50. package/src/components/FileViewer/file-viewer.tsx +1065 -0
  51. package/src/components/FileViewer/image-renderer.tsx +256 -0
  52. package/src/components/HoverCard/hover-card.tsx +79 -0
  53. package/src/components/Input/input.tsx +233 -0
  54. package/src/components/LinkInput/link-input.tsx +304 -0
  55. package/src/components/Menu/menu-item.tsx +334 -0
  56. package/src/components/NameCard/name-card.tsx +319 -0
  57. package/src/components/Notice/notice.tsx +196 -0
  58. package/src/components/NumberInput/number-input.tsx +203 -0
  59. package/src/components/OverflowIndicator/overflow-indicator.tsx +156 -0
  60. package/src/components/PeoplePicker/avatar-stack-overflow.ts +100 -0
  61. package/src/components/PeoplePicker/people-picker-helpers.ts +76 -0
  62. package/src/components/PeoplePicker/people-picker.tsx +455 -0
  63. package/src/components/PeoplePicker/person-display.tsx +358 -0
  64. package/src/components/Popover/popover.tsx +183 -0
  65. package/src/components/ProgressBar/progress-bar.tsx +157 -0
  66. package/src/components/README.md +58 -0
  67. package/src/components/RadioGroup/radio-group.tsx +261 -0
  68. package/src/components/Rating/rating.tsx +295 -0
  69. package/src/components/ScrollArea/scroll-area.tsx +110 -0
  70. package/src/components/SegmentedControl/segmented-control.tsx +304 -0
  71. package/src/components/Select/select.tsx +658 -0
  72. package/src/components/SelectMenu/select-menu.tsx +430 -0
  73. package/src/components/SelectionControl/selection-item.tsx +261 -0
  74. package/src/components/Separator/separator.tsx +48 -0
  75. package/src/components/Sheet/sheet.tsx +240 -0
  76. package/src/components/Sidebar/sidebar.tsx +1280 -0
  77. package/src/components/Skeleton/skeleton.tsx +35 -0
  78. package/src/components/Slider/slider.tsx +158 -0
  79. package/src/components/Steps/steps.tsx +850 -0
  80. package/src/components/Switch/switch.tsx +285 -0
  81. package/src/components/Tabs/tabs.tsx +515 -0
  82. package/src/components/Tag/tag.tsx +246 -0
  83. package/src/components/Textarea/textarea.tsx +280 -0
  84. package/src/components/TimePicker/time-columns.tsx +260 -0
  85. package/src/components/TimePicker/time-picker.tsx +419 -0
  86. package/src/components/Toast/toast.tsx +129 -0
  87. package/src/components/Tooltip/tooltip.tsx +68 -0
  88. package/src/components/TreeView/tree-view.tsx +1031 -0
  89. package/src/hooks/use-controllable.ts +40 -0
  90. package/src/hooks/use-is-narrow-viewport.ts +19 -0
  91. package/src/hooks/use-is-touch-device.ts +21 -0
  92. package/src/hooks/use-overflow-items.ts +256 -0
  93. package/src/index.ts +85 -0
  94. package/src/lib/README.md +82 -0
  95. package/src/lib/drag-visual.ts +272 -0
  96. package/src/lib/i18n/README.md +60 -0
  97. package/src/lib/i18n/i18n-context.tsx +129 -0
  98. package/src/lib/multi-select-ordering.ts +61 -0
  99. package/src/lib/utils.ts +93 -0
  100. package/src/patterns/README.md +67 -0
  101. package/src/patterns/element-anatomy/item-anatomy.tsx +744 -0
  102. package/src/patterns/header-canonical/chrome-header.tsx +175 -0
  103. package/src/patterns/header-canonical/header-canonical.css +27 -0
  104. package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +217 -0
  105. package/src/patterns/overlay-surface/overlay-surface.tsx +191 -0
  106. package/src/patterns/resize-handle/resize-handle.tsx +188 -0
  107. package/src/stories-helpers/anatomy/anatomy-utils.tsx +64 -0
  108. package/src/tokens/README.md +53 -0
  109. package/src/tokens/color/primitives.css +429 -0
  110. package/src/tokens/color/semantic.css +539 -0
  111. package/src/tokens/elevation/overlay-geometry.ts +13 -0
  112. package/src/tokens/layoutSpace/layoutSpace.css +36 -0
  113. package/src/tokens/motion/motion.css +30 -0
  114. package/src/tokens/motion/motion.ts +17 -0
  115. package/src/tokens/opacity/opacity.css +23 -0
  116. package/src/tokens/radius/radius.css +19 -0
  117. package/src/tokens/typography/typography.css +118 -0
  118. package/src/tokens/uiSize/icon-size.ts +52 -0
  119. package/src/tokens/uiSize/uiSize.css +125 -0
@@ -0,0 +1,48 @@
1
+ import * as React from "react"
2
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const Separator = React.forwardRef<
7
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
9
+ >(
10
+ (
11
+ { className, orientation = "horizontal", decorative = true, ...props },
12
+ ref
13
+ ) => (
14
+ <SeparatorPrimitive.Root
15
+ ref={ref}
16
+ decorative={decorative}
17
+ orientation={orientation}
18
+ className={cn(
19
+ "shrink-0 bg-divider",
20
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
21
+ className
22
+ )}
23
+ {...props}
24
+ />
25
+ )
26
+ )
27
+ Separator.displayName = "Separator"
28
+
29
+ // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
30
+ // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
31
+ export const separatorMeta = {
32
+ component: 'Separator',
33
+ family: null, // non-family composite / overlay / layout
34
+ variants: {
35
+
36
+ },
37
+ sizes: {
38
+
39
+ },
40
+ states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
41
+ tokens: {
42
+ bg: [],
43
+ fg: [],
44
+ ring: [],
45
+ },
46
+ } as const
47
+
48
+ export { Separator }
@@ -0,0 +1,240 @@
1
+ // @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
+ import * as React from "react"
3
+ import * as SheetPrimitive from "@radix-ui/react-dialog"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+ import { X as XIcon } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+ import {
9
+ SurfaceHeader,
10
+ SurfaceFooter,
11
+ type SurfaceHeaderProps,
12
+ } from "@/design-system/patterns/overlay-surface/overlay-surface"
13
+ import { Button } from "@/design-system/components/Button/button"
14
+ import { ScrollArea } from "@/design-system/components/ScrollArea/scroll-area"
15
+
16
+ /**
17
+ * Sheet — **右側 Dialog primitive**(給消費者的 canonical)。
18
+ *
19
+ * ── 定位(2026-04-21 canonical)──
20
+ * Sheet 給**消費者**用的唯一合法形式 = **右側開啟的 modal**(side="right"),
21
+ * 內部結構跟 `Dialog` 一致:`SheetHeader` / `SheetBody` / `SheetFooter`(各自消費
22
+ * `SurfaceHeader` / `SurfaceBody` / `SurfaceFooter` primitive,padding token SSOT
23
+ * 在 `patterns/overlay-surface/`)。side="right" 是 defaultVariants,消費者不傳 side。
24
+ *
25
+ * ── 其他 side(top / bottom / left)——**非消費者 API**,內部基建用 ──
26
+ * top / bottom / left 變體保留給 DS 內部基建(例:Sidebar 在小尺寸視口時從 left 滑入)。
27
+ * 消費者 code **禁止** 傳 `side="top" | "bottom" | "left"` — 這些用途需 user 授權。
28
+ *
29
+ * ── 跟 Dialog 的差異 ──
30
+ * - Dialog = 中央 modal,用於「明確決策 / 表單 / 確認」
31
+ * - Sheet(side="right")= 側滑 modal,用於「補充資訊 / 多欄位表單 / 編輯 flow」
32
+ * - 兩者 API 結構 1:1 對應,差異只在 side / 動畫 / 初始寬度
33
+ *
34
+ * ── Header / Body / Footer 消費 SurfaceXxx SSOT ──
35
+ * 避免 padding 漂移 — Dialog / Popover / Sheet / Coachmark 共用同一套 overlay-surface
36
+ * padding token(px-loose / py-tight),改 overlay-surface.tsx 四者自動跟進。
37
+ */
38
+
39
+ const Sheet = SheetPrimitive.Root
40
+
41
+ const SheetTrigger = SheetPrimitive.Trigger
42
+
43
+ const SheetClose = SheetPrimitive.Close
44
+
45
+ const SheetPortal = SheetPrimitive.Portal
46
+
47
+ const SheetOverlay = React.forwardRef<
48
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
49
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
50
+ >(({ className, ...props }, ref) => (
51
+ <SheetPrimitive.Overlay
52
+ className={cn(
53
+ "fixed inset-0 z-50 bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
54
+ className
55
+ )}
56
+ {...props}
57
+ ref={ref}
58
+ />
59
+ ))
60
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
61
+
62
+ // ── sheetVariants ─────────────────────────────────────────────────────────
63
+ // side="right" 給**消費者**。top/bottom/left 給 **DS 內部基建**用(如 Sidebar 在
64
+ // narrow viewport 時切 side="left")。消費者 code 不傳 side,用 default。
65
+ const sheetVariants = cva(
66
+ // 核心容器 — 無 padding(由 SheetBody / SheetHeader / SheetFooter 自理 padding,
67
+ // 對齊 overlay-surface pattern + Dialog canonical)
68
+ // Animation canonical:300ms 雙向一致(D4 audit:500ms 太久 sluggish)+ motion-reduce 豁免
69
+ "fixed z-50 flex flex-col bg-surface-raised shadow-[var(--elevation-200)] transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-300 motion-reduce:transition-none motion-reduce:data-[state=open]:duration-0 motion-reduce:data-[state=closed]:duration-0",
70
+ {
71
+ variants: {
72
+ side: {
73
+ top: "inset-x-0 top-0 border-b border-divider data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
74
+ bottom:
75
+ "inset-x-0 bottom-0 border-t border-divider data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
76
+ left: "inset-y-0 left-0 h-full w-3/4 border-r border-divider data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-md",
77
+ right:
78
+ "inset-y-0 right-0 h-full w-3/4 border-l border-divider data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-md",
79
+ },
80
+ },
81
+ defaultVariants: {
82
+ side: "right",
83
+ },
84
+ }
85
+ )
86
+
87
+ interface SheetContentProps
88
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
89
+ VariantProps<typeof sheetVariants> {}
90
+
91
+ // AutoFocus canonical(對齊 Dialog / Material / Polaris)— 見 dialog.tsx handleOpenAutoFocus 註解
92
+ const handleSheetOpenAutoFocus = (e: Event) => {
93
+ e.preventDefault()
94
+ const content = e.currentTarget as HTMLElement
95
+ const firstBodyTarget = content.querySelector<HTMLElement>(
96
+ '[data-sheet-body] input:not([disabled]),[data-sheet-body] textarea:not([disabled]),[data-sheet-body] select:not([disabled]),[data-sheet-body] button:not([disabled]):not([data-dismiss])'
97
+ )
98
+ const firstFooterButton = content.querySelector<HTMLElement>(
99
+ '[data-sheet-footer] button:not([disabled]):not([data-dismiss])'
100
+ )
101
+ ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })
102
+ }
103
+
104
+ const SheetContent = React.forwardRef<
105
+ React.ElementRef<typeof SheetPrimitive.Content>,
106
+ SheetContentProps
107
+ >(({ side = "right", className, children, ...props }, ref) => (
108
+ <SheetPortal>
109
+ <SheetOverlay />
110
+ <SheetPrimitive.Content
111
+ ref={ref}
112
+ onOpenAutoFocus={handleSheetOpenAutoFocus}
113
+ // Sheet 不自設 density,繼承 page 層級的 `html[data-density]`(2026-04-21 canonical 定案)
114
+ className={cn(sheetVariants({ side }), className)}
115
+ {...props}
116
+ >
117
+ {children}
118
+ </SheetPrimitive.Content>
119
+ </SheetPortal>
120
+ ))
121
+ SheetContent.displayName = SheetPrimitive.Content.displayName
122
+
123
+ // ── SheetHeader:SurfaceHeader + Close X(對齊 DialogHeader canonical)──────────
124
+ // 2026-05-18 audit gap fix:type 對齊 SurfaceHeaderProps,withTabs / lockDensity expose
125
+ // 給 consumer(per header-canonical.spec.md W1 跨 6 consumer 同契約)。Spread 早 forward
126
+ // 過去,只是 TS type 沒 expose 導致 consumer 不能 type-safe 用 `<SheetHeader withTabs>`。
127
+ const SheetHeader = React.forwardRef<
128
+ HTMLDivElement,
129
+ SurfaceHeaderProps
130
+ >(({ className, children, ...props }, ref) => (
131
+ // 2026-05-18:className 不再硬加 justify-between(同 DialogHeader 邏輯,避 column mode 破裂)。
132
+ <SurfaceHeader
133
+ ref={ref}
134
+ className={className}
135
+ {...props}
136
+ >
137
+ <div className="flex-1 min-w-0">{children}</div>
138
+ <SheetPrimitive.Close asChild>
139
+ {/* Dismiss X = native sm,SurfaceHeader 負 my trick 讓 layout 佔位 24 → chrome-header-height */}
140
+ <Button data-dismiss iconOnly dismiss size="sm" startIcon={XIcon} aria-label="關閉" />
141
+ </SheetPrimitive.Close>
142
+ </SurfaceHeader>
143
+ ))
144
+ SheetHeader.displayName = "SheetHeader"
145
+
146
+ // ── SheetBody:flex-1 ScrollArea + chrome padding(對齊 DialogBody + ScrollArea canonical) ──
147
+ // 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。
148
+ // padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom。
149
+ // data-sheet-body:讓 SheetContent onOpenAutoFocus 找得到 body 第一個互動元素
150
+ //
151
+ // ── List-as-region 場景(menu / nav / settings list)──
152
+ // 不再提供 `flush` variant(2026-05-01 移除)。canonical = consumer 用 className override:
153
+ // `<SheetBody className="!px-0 !pt-0 !pb-0"><div className="py-2">{items}</div></SheetBody>`
154
+ // 詳 DialogBody comment + `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」
155
+ // `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——
156
+ // consumer `<SheetBody className="flex flex-col gap-X">` 期望作用於 children 排列;
157
+ // 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 Sheet form field 完全貼邊。
158
+ const SheetBody = React.forwardRef<
159
+ HTMLDivElement,
160
+ React.ComponentPropsWithoutRef<typeof ScrollArea>
161
+ >(({ className, children, ...props }, ref) => (
162
+ <ScrollArea ref={ref} data-sheet-body className="flex-1 min-h-0" {...props}>
163
+ <div
164
+ className={cn(
165
+ "px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]",
166
+ className,
167
+ )}
168
+ >
169
+ {children}
170
+ </div>
171
+ </ScrollArea>
172
+ ))
173
+ SheetBody.displayName = "SheetBody"
174
+
175
+ // ── SheetFooter:SurfaceFooter wrap 加 data-sheet-footer(autoFocus fallback target)──
176
+ const SheetFooter = React.forwardRef<
177
+ HTMLDivElement,
178
+ React.HTMLAttributes<HTMLDivElement>
179
+ >(({ ...props }, ref) => <SurfaceFooter ref={ref} data-sheet-footer {...props} />)
180
+ SheetFooter.displayName = "SheetFooter"
181
+
182
+ const SheetTitle = React.forwardRef<
183
+ React.ElementRef<typeof SheetPrimitive.Title>,
184
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
185
+ >(({ className, ...props }, ref) => (
186
+ <SheetPrimitive.Title
187
+ ref={ref}
188
+ className={cn("text-body-lg font-medium truncate text-foreground", className)}
189
+ {...props}
190
+ />
191
+ ))
192
+ SheetTitle.displayName = SheetPrimitive.Title.displayName
193
+
194
+ const SheetDescription = React.forwardRef<
195
+ React.ElementRef<typeof SheetPrimitive.Description>,
196
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
197
+ >(({ className, ...props }, ref) => (
198
+ <SheetPrimitive.Description
199
+ ref={ref}
200
+ // title → description 間距 canonical:SheetTitle body-lg(16)+ desc body(14)→ reading-lg token
201
+ // (label tier 決定;對齊 Dialog canonical。Tailwind preflight reset h2/p margin=0 → 必顯式 mt)
202
+ className={cn("mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary", className)}
203
+ {...props}
204
+ />
205
+ ))
206
+ SheetDescription.displayName = SheetPrimitive.Description.displayName
207
+
208
+ // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
209
+ // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
210
+ export const sheetMeta = {
211
+ component: 'Sheet',
212
+ family: null, // non-family composite / overlay / layout
213
+ variants: {
214
+
215
+ },
216
+ sizes: {
217
+
218
+ },
219
+ states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
220
+ tokens: {
221
+ bg: ['bg-surface-raised'],
222
+ fg: ['text-fg-secondary', 'text-foreground'],
223
+ ring: [],
224
+ },
225
+ } as const
226
+
227
+ export {
228
+ Sheet,
229
+ SheetPortal,
230
+ SheetOverlay,
231
+ SheetTrigger,
232
+ SheetClose,
233
+ SheetContent,
234
+ SheetHeader,
235
+ SheetBody,
236
+ SheetFooter,
237
+ SheetTitle,
238
+ SheetDescription,
239
+ sheetVariants,
240
+ }