@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,35 @@
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ const Skeleton = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn("animate-pulse rounded-md bg-muted motion-reduce:animate-none", className)}
11
+ {...props}
12
+ />
13
+ ))
14
+ Skeleton.displayName = "Skeleton"
15
+
16
+ // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
17
+ // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
18
+ export const skeletonMeta = {
19
+ component: 'Skeleton',
20
+ family: null, // non-family composite / overlay / layout
21
+ variants: {
22
+
23
+ },
24
+ sizes: {
25
+
26
+ },
27
+ states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
28
+ tokens: {
29
+ bg: ['bg-muted'],
30
+ fg: [],
31
+ ring: [],
32
+ },
33
+ } as const
34
+
35
+ export { Skeleton }
@@ -0,0 +1,158 @@
1
+ import * as React from 'react'
2
+ import * as SliderPrimitive from '@radix-ui/react-slider'
3
+ import { cva, type VariantProps } from 'class-variance-authority'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ /**
7
+ * Slider — 數值範圍選取器
8
+ *
9
+ * 基於 Radix Slider primitive,橋接設計系統 token。詳細設計原則見 `slider.spec.md`。
10
+ *
11
+ * ── 核心設計 ──
12
+ * 1. **視覺單一**:track 厚度、thumb 直徑、ring 尺寸都是固定值,不隨 `size` 變
13
+ * 2. **`size` 只控容器外高**:對齊 Field family 的 `h-field-*` tier,讓 Slider 能跟
14
+ * Input / NumberInput / Select 在 Field 內並排對齊
15
+ * 3. **Range mode 免費**:Radix 原生支援 `value: number[]`,傳多值自動多 thumb
16
+ * 4. **Hover / active 用 elevation 陰影**:不用色變,避免暗示「這是 button」
17
+ */
18
+
19
+ const sliderRootVariants = cva(
20
+ // 容器外層:水平置中 + relative(Radix 會絕對定位內部元素)
21
+ // flex items-center 讓 track+thumb 在任何 field-height 下都垂直置中
22
+ //
23
+ // ── Disabled 策略:灰階 token swap(對齊 Button / Checkbox)──
24
+ // Slider 的藍色 range 是美學視覺,不是 semantic state——使用者從 disabled
25
+ // slider 需要辨識的是 thumb 位置 + range 長度,這兩者不依賴顏色。失去藍色
26
+ // 沒有資訊損失。
27
+ //
28
+ // 跟 Switch 的差別:Switch 的 on/off 是純顏色差異(沒有形狀差異),所以必須
29
+ // 靠 opacity 保留色彩身分。Slider 的位置/長度是形狀差異,不需要保留顏色身分,
30
+ // 跟 Checkbox(checkmark 形狀 = semantic 載體)同類,應該走灰階。
31
+ //
32
+ // 詳細判準見 `slider.spec.md` 的「Disabled 策略」章節。
33
+ [
34
+ 'relative flex items-center w-full min-w-0 touch-none select-none',
35
+ 'data-[disabled]:cursor-not-allowed',
36
+ ],
37
+ {
38
+ variants: {
39
+ size: {
40
+ sm: 'h-field-sm',
41
+ md: 'h-field-md',
42
+ lg: 'h-field-lg',
43
+ },
44
+ },
45
+ defaultVariants: {
46
+ size: 'md',
47
+ },
48
+ },
49
+ )
50
+
51
+ export interface SliderProps
52
+ extends Omit<React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, 'children'>,
53
+ VariantProps<typeof sliderRootVariants> {}
54
+
55
+ const Slider = React.forwardRef<
56
+ React.ElementRef<typeof SliderPrimitive.Root>,
57
+ SliderProps
58
+ >(({ className, size, value, defaultValue, 'aria-label': ariaLabel, ...props }, ref) => {
59
+ // 推導要渲染幾個 thumb:controlled 用 value,uncontrolled 用 defaultValue,
60
+ // 都沒有時 fallback 單 thumb(Radix 預設行為)
61
+ const thumbCount =
62
+ (Array.isArray(value) && value.length) ||
63
+ (Array.isArray(defaultValue) && defaultValue.length) ||
64
+ 1
65
+
66
+ return (
67
+ <SliderPrimitive.Root
68
+ ref={ref}
69
+ value={value}
70
+ defaultValue={defaultValue}
71
+ className={cn(sliderRootVariants({ size }), className)}
72
+ {...props}
73
+ >
74
+ {/*
75
+ Track — rest 用 bg-secondary(n-3,「微淡可辨」),disabled 用 bg-muted(n-2,退化)。
76
+ 跟 Tag neutral / Badge low 同家族。
77
+ */}
78
+ <SliderPrimitive.Track className={cn(
79
+ 'relative grow overflow-hidden rounded-full h-1',
80
+ // Rest:bg-secondary(n-3,「微淡可辨」的 subtle fill,跟 Tag neutral / Badge low 同級)
81
+ // Disabled:bg-muted(n-2,「disabled-like 退化」底色)
82
+ 'bg-secondary data-[disabled]:bg-muted',
83
+ )}>
84
+ {/*
85
+ Range — 填滿段。
86
+
87
+ ── Range 色 = Thumb border 色(語意一致) ──
88
+ Rest 兩者都是 `primary`,disabled 兩者都是 `border`(n-5)。為什麼要一致?
89
+ Range 是「填充視覺」,Thumb border 是「thumb 的輪廓線」——視覺上 thumb
90
+ 的 border 剛好是 range 的延續(thumb 坐落在 range 的端點上,border 跟
91
+ range 在色彩上融為一體,看起來像「range 包住 thumb」而不是「thumb 浮在
92
+ range 上」)。兩個 token 綁在一起,不論什麼 state 都一致。
93
+ */}
94
+ <SliderPrimitive.Range
95
+ className={cn(
96
+ 'absolute h-full bg-primary',
97
+ 'data-[disabled]:bg-border',
98
+ )}
99
+ />
100
+ </SliderPrimitive.Track>
101
+
102
+ {/*
103
+ Thumb — N 個(由 thumbCount 決定)。
104
+ 白底 + 2px 邊框,邊框色 = Range 色(**一致綁定**):
105
+ - Rest: `border-primary` ↔ Range `bg-primary`
106
+ - Disabled: `border-border` ↔ Range `bg-border`
107
+ 這個一致性讓 thumb border 跟 range 融為一體,看起來像「range 包住 thumb」
108
+ 的連續視覺。thumb 的白底則是「被 range 圍住的空心洞」,讓 thumb 的位置
109
+ 清楚浮出。不論 state,thumb border 跟 range 永遠同色。
110
+
111
+ **為什麼 thumb bg 不能改**:`bg-surface`(白)必須在 rest / disabled 都維持,
112
+ 否則會融入 track 的 `bg-muted` 裡消失。這是之前踩過的同色融色 bug
113
+ (曾經寫成 `data-[disabled]:bg-muted` 讓 thumb 跟 track 完全融合)。
114
+ */}
115
+ {Array.from({ length: thumbCount }).map((_, i) => (
116
+ <SliderPrimitive.Thumb
117
+ key={i}
118
+ className={cn(
119
+ 'block h-4 w-4 shrink-0 rounded-full cursor-grab',
120
+ 'bg-surface border-2 border-primary',
121
+ 'transition-all duration-150',
122
+ // Hover:border 加深到 primary-hover + elevation 陰影
123
+ 'hover:border-primary-hover hover:[box-shadow:var(--elevation-100)]',
124
+ 'active:cursor-grabbing active:border-primary-hover active:[box-shadow:var(--elevation-200)]',
125
+ // Focus:border 加深(跟 hover 同視覺),不加 ring 或 halo
126
+ 'outline-none focus-visible:border-primary-hover',
127
+ // Disabled:border 跟 Range 一起退成 border(n-5),bg 保留 bg-surface
128
+ 'data-[disabled]:cursor-not-allowed data-[disabled]:border-border',
129
+ 'data-[disabled]:hover:[box-shadow:none]',
130
+ )}
131
+ aria-label={ariaLabel ? (thumbCount > 1 ? `${ariaLabel} (${i + 1})` : ariaLabel) : (thumbCount > 1 ? `Thumb ${i + 1}` : undefined)}
132
+ />
133
+ ))}
134
+ </SliderPrimitive.Root>
135
+ )
136
+ })
137
+ Slider.displayName = 'Slider'
138
+
139
+ // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
140
+ // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
141
+ export const sliderMeta = {
142
+ component: 'Slider',
143
+ family: 4,
144
+ variants: {},
145
+ sizes: {
146
+ sm: { px: 28, when: 'Toolbar / inline 編輯' },
147
+ md: { px: 36, when: '預設 — Form / cell inline edit' },
148
+ lg: { px: 44, when: 'Marketing / 高 touch 區' },
149
+ },
150
+ states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
151
+ tokens: {
152
+ bg: ['bg-muted', 'bg-primary', 'bg-secondary', 'bg-surface'],
153
+ fg: [],
154
+ ring: [],
155
+ },
156
+ } as const
157
+
158
+ export { Slider, sliderRootVariants }