@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.
- package/package.json +93 -0
- package/src/README.md +32 -0
- package/src/components/Accordion/accordion.tsx +104 -0
- package/src/components/Alert/alert.tsx +188 -0
- package/src/components/AppShell/_demo-helpers.tsx +198 -0
- package/src/components/AppShell/app-shell.tsx +364 -0
- package/src/components/AspectRatio/aspect-ratio.tsx +58 -0
- package/src/components/Avatar/avatar.tsx +368 -0
- package/src/components/Badge/badge.tsx +104 -0
- package/src/components/Breadcrumb/breadcrumb.tsx +609 -0
- package/src/components/BulkActionBar/bulk-action-bar.tsx +156 -0
- package/src/components/Button/button-group.tsx +96 -0
- package/src/components/Button/button.tsx +539 -0
- package/src/components/Calendar/calendar.tsx +411 -0
- package/src/components/Carousel/carousel.tsx +371 -0
- package/src/components/Chart/chart.tsx +376 -0
- package/src/components/Checkbox/checkbox-group.tsx +94 -0
- package/src/components/Checkbox/checkbox.tsx +237 -0
- package/src/components/Chip/chip.tsx +359 -0
- package/src/components/CircularProgress/circular-progress.tsx +204 -0
- package/src/components/Coachmark/coachmark.tsx +255 -0
- package/src/components/Combobox/combobox.tsx +826 -0
- package/src/components/Command/command.tsx +187 -0
- package/src/components/DataTable/active-editor-controller.ts +72 -0
- package/src/components/DataTable/cell-registry.tsx +520 -0
- package/src/components/DataTable/column-types.ts +180 -0
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +261 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +813 -0
- package/src/components/DataTable/data-table-interaction-layer.tsx +483 -0
- package/src/components/DataTable/data-table-sort-manager.tsx +210 -0
- package/src/components/DataTable/data-table.css +165 -0
- package/src/components/DataTable/data-table.tsx +2924 -0
- package/src/components/DataTable/filter-operators.ts +225 -0
- package/src/components/DataTable/filter-tree.ts +313 -0
- package/src/components/DataTable/lib/column-meta.ts +79 -0
- package/src/components/DateGrid/date-grid.tsx +209 -0
- package/src/components/DatePicker/date-picker.tsx +1114 -0
- package/src/components/DescriptionList/description-list.tsx +141 -0
- package/src/components/Dialog/dialog.tsx +267 -0
- package/src/components/DropdownMenu/dropdown-menu.tsx +475 -0
- package/src/components/Empty/empty.tsx +108 -0
- package/src/components/Field/field-context.ts +136 -0
- package/src/components/Field/field-types.ts +52 -0
- package/src/components/Field/field-wrapper.tsx +348 -0
- package/src/components/Field/field.tsx +535 -0
- package/src/components/FieldControlGroup/field-control-group.tsx +136 -0
- package/src/components/FileItem/file-item.tsx +322 -0
- package/src/components/FileUpload/file-upload.tsx +326 -0
- package/src/components/FileViewer/file-viewer-types.ts +76 -0
- package/src/components/FileViewer/file-viewer.tsx +1065 -0
- package/src/components/FileViewer/image-renderer.tsx +256 -0
- package/src/components/HoverCard/hover-card.tsx +79 -0
- package/src/components/Input/input.tsx +233 -0
- package/src/components/LinkInput/link-input.tsx +304 -0
- package/src/components/Menu/menu-item.tsx +334 -0
- package/src/components/NameCard/name-card.tsx +319 -0
- package/src/components/Notice/notice.tsx +196 -0
- package/src/components/NumberInput/number-input.tsx +203 -0
- package/src/components/OverflowIndicator/overflow-indicator.tsx +156 -0
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +100 -0
- package/src/components/PeoplePicker/people-picker-helpers.ts +76 -0
- package/src/components/PeoplePicker/people-picker.tsx +455 -0
- package/src/components/PeoplePicker/person-display.tsx +358 -0
- package/src/components/Popover/popover.tsx +183 -0
- package/src/components/ProgressBar/progress-bar.tsx +157 -0
- package/src/components/README.md +58 -0
- package/src/components/RadioGroup/radio-group.tsx +261 -0
- package/src/components/Rating/rating.tsx +295 -0
- package/src/components/ScrollArea/scroll-area.tsx +110 -0
- package/src/components/SegmentedControl/segmented-control.tsx +304 -0
- package/src/components/Select/select.tsx +658 -0
- package/src/components/SelectMenu/select-menu.tsx +430 -0
- package/src/components/SelectionControl/selection-item.tsx +261 -0
- package/src/components/Separator/separator.tsx +48 -0
- package/src/components/Sheet/sheet.tsx +240 -0
- package/src/components/Sidebar/sidebar.tsx +1280 -0
- package/src/components/Skeleton/skeleton.tsx +35 -0
- package/src/components/Slider/slider.tsx +158 -0
- package/src/components/Steps/steps.tsx +850 -0
- package/src/components/Switch/switch.tsx +285 -0
- package/src/components/Tabs/tabs.tsx +515 -0
- package/src/components/Tag/tag.tsx +246 -0
- package/src/components/Textarea/textarea.tsx +280 -0
- package/src/components/TimePicker/time-columns.tsx +260 -0
- package/src/components/TimePicker/time-picker.tsx +419 -0
- package/src/components/Toast/toast.tsx +129 -0
- package/src/components/Tooltip/tooltip.tsx +68 -0
- package/src/components/TreeView/tree-view.tsx +1031 -0
- package/src/hooks/use-controllable.ts +40 -0
- package/src/hooks/use-is-narrow-viewport.ts +19 -0
- package/src/hooks/use-is-touch-device.ts +21 -0
- package/src/hooks/use-overflow-items.ts +256 -0
- package/src/index.ts +85 -0
- package/src/lib/README.md +82 -0
- package/src/lib/drag-visual.ts +272 -0
- package/src/lib/i18n/README.md +60 -0
- package/src/lib/i18n/i18n-context.tsx +129 -0
- package/src/lib/multi-select-ordering.ts +61 -0
- package/src/lib/utils.ts +93 -0
- package/src/patterns/README.md +67 -0
- package/src/patterns/element-anatomy/item-anatomy.tsx +744 -0
- package/src/patterns/header-canonical/chrome-header.tsx +175 -0
- package/src/patterns/header-canonical/header-canonical.css +27 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +217 -0
- package/src/patterns/overlay-surface/overlay-surface.tsx +191 -0
- package/src/patterns/resize-handle/resize-handle.tsx +188 -0
- package/src/stories-helpers/anatomy/anatomy-utils.tsx +64 -0
- package/src/tokens/README.md +53 -0
- package/src/tokens/color/primitives.css +429 -0
- package/src/tokens/color/semantic.css +539 -0
- package/src/tokens/elevation/overlay-geometry.ts +13 -0
- package/src/tokens/layoutSpace/layoutSpace.css +36 -0
- package/src/tokens/motion/motion.css +30 -0
- package/src/tokens/motion/motion.ts +17 -0
- package/src/tokens/opacity/opacity.css +23 -0
- package/src/tokens/radius/radius.css +19 -0
- package/src/tokens/typography/typography.css +118 -0
- package/src/tokens/uiSize/icon-size.ts +52 -0
- package/src/tokens/uiSize/uiSize.css +125 -0
|
@@ -0,0 +1,285 @@
|
|
|
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 SwitchPrimitives from '@radix-ui/react-switch'
|
|
4
|
+
import { Check } from 'lucide-react'
|
|
5
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
|
|
8
|
+
import { useFieldContext } from '@/design-system/components/Field/field-context'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Switch — 開關控件
|
|
12
|
+
*
|
|
13
|
+
* ── 結構 ──
|
|
14
|
+
* Track(pill 形容器)→ Thumb(白色圓 + 2px border + check icon)
|
|
15
|
+
* Track 寬 = 2 × 高,thumb 直徑 = track 高度
|
|
16
|
+
*
|
|
17
|
+
* ── 尺寸(sm = md)──
|
|
18
|
+
* sm/md: track 20×40, thumb 20, 白色圓 16, check 12(= checkbox sm/md)
|
|
19
|
+
* lg: track 24×48, thumb 24, 白色圓 20, check 16(= checkbox lg)
|
|
20
|
+
*
|
|
21
|
+
* ── 視覺狀態 ──
|
|
22
|
+
* OFF: track border (neutral-5), thumb 白色無 border 無 check
|
|
23
|
+
* ON: track primary, thumb 白色 + 2px primary border + primary check icon
|
|
24
|
+
* disabled: opacity-disabled(整體透明度)
|
|
25
|
+
* readOnly: 視覺同一般態,但 pointer-events-none + aria-readonly
|
|
26
|
+
*
|
|
27
|
+
* ── label / description / readOnly ──
|
|
28
|
+
* Switch 可以透過 `label` 和 `description` props 在元件內直接渲染緊鄰的文字,
|
|
29
|
+
* 樣式全部 codify 在元件內(text-body、foreground/fg-secondary、disabled 色)。
|
|
30
|
+
*
|
|
31
|
+
* 單獨使用時:
|
|
32
|
+
* <Switch label="啟用通知" description="收到新訊息時提醒" />
|
|
33
|
+
*
|
|
34
|
+
* Form 內使用(在 <Field> context 內):
|
|
35
|
+
* <Field>
|
|
36
|
+
* <FieldLabel>啟用通知</FieldLabel>
|
|
37
|
+
* <Switch /> ← label/description prop 會被自動忽略
|
|
38
|
+
* <FieldDescription>收到新訊息時提醒</FieldDescription>
|
|
39
|
+
* </Field>
|
|
40
|
+
*
|
|
41
|
+
* Field context 透過 useFieldContext() 偵測,避免雙層 label。
|
|
42
|
+
*
|
|
43
|
+
* readOnly 模式:
|
|
44
|
+
* <Switch readOnly checked={true} label="..." />
|
|
45
|
+
* 視覺維持 ON/OFF 正確狀態,但無法互動、不在 tab order 內、寫入 aria-readonly。
|
|
46
|
+
* 與 disabled 的差異:readonly 不降色(可讀),disabled 降色(弱化)。
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
const switchVariants = cva(
|
|
50
|
+
[
|
|
51
|
+
'group peer inline-flex shrink-0 cursor-pointer items-center rounded-full',
|
|
52
|
+
'transition-colors duration-150',
|
|
53
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
|
|
54
|
+
'disabled:cursor-not-allowed disabled:opacity-disabled',
|
|
55
|
+
// readOnly:鎖定互動但視覺正常
|
|
56
|
+
'data-[readonly=true]:pointer-events-none data-[readonly=true]:cursor-default',
|
|
57
|
+
// OFF → ON 背景色
|
|
58
|
+
'data-[state=unchecked]:bg-border',
|
|
59
|
+
'data-[state=checked]:bg-primary',
|
|
60
|
+
],
|
|
61
|
+
{
|
|
62
|
+
variants: {
|
|
63
|
+
size: {
|
|
64
|
+
sm: 'h-5 w-10', // 20×40
|
|
65
|
+
md: 'h-5 w-10', // 20×40
|
|
66
|
+
lg: 'h-6 w-12', // 24×48
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
defaultVariants: { size: 'md' },
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const SPECS: Record<string, { thumb: number; check: number; checkStroke: number; translate: string }> = {
|
|
74
|
+
// checkStroke:16px 以下 icon 視覺不夠顯眼 → 加粗 stroke 補償(跟 Checkbox 共用原則,
|
|
75
|
+
// 見 checkbox.tsx 的 checkStrokeWidth 註解)。12px 用 3.5(render ≈ 1.75px 線寬,比 Lucide
|
|
76
|
+
// 預設的 1px render 明顯更粗,視覺跟 16px 預設 stroke 的 1.33px 有足夠區別),16px 用 2.5
|
|
77
|
+
// (render ≈ 1.67px,比預設 1.33px 稍粗讓 toggle check 夠顯眼)。跨 size 配對值由 checkbox.tsx 共用。
|
|
78
|
+
// 2026-05-18 簡化 per user 視覺證 + Checkbox 同步(3.5 → 3 sm/md):effective render 差
|
|
79
|
+
// 0.08px 視覺看不出,保留 compensation 主旨但不過度差異化。
|
|
80
|
+
sm: { thumb: 20, check: 12, checkStroke: 3, translate: 'translateX(20px)' },
|
|
81
|
+
md: { thumb: 20, check: 12, checkStroke: 3, translate: 'translateX(20px)' },
|
|
82
|
+
lg: { thumb: 24, check: 16, checkStroke: 2.5, translate: 'translateX(24px)' },
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SwitchProps
|
|
86
|
+
extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>,
|
|
87
|
+
VariantProps<typeof switchVariants> {
|
|
88
|
+
/**
|
|
89
|
+
* Inline label。提供時 Switch 自動包一個 <label> 並連結 htmlFor,
|
|
90
|
+
* 套用 text-body / text-foreground / disabled 色 的 codified 樣式。
|
|
91
|
+
* 在 <Field> context 內時此 prop 會被忽略(由 FieldLabel 接管)。
|
|
92
|
+
*/
|
|
93
|
+
label?: React.ReactNode
|
|
94
|
+
/**
|
|
95
|
+
* Inline description(secondary 文字)。須與 label 搭配使用,
|
|
96
|
+
* 單獨設定 description 無效果。套用 text-body / text-fg-secondary 樣式。
|
|
97
|
+
* 在 <Field> context 內時此 prop 會被忽略(由 FieldDescription 接管)。
|
|
98
|
+
*/
|
|
99
|
+
description?: React.ReactNode
|
|
100
|
+
/**
|
|
101
|
+
* readonly 模式:鎖定互動但維持 ON/OFF 視覺正確。
|
|
102
|
+
* 與 disabled 的差異:readonly 不降色(可讀),disabled 降色(弱化)。
|
|
103
|
+
* 用於表單 readonly 呈現、DataTable cell 非編輯態。
|
|
104
|
+
*/
|
|
105
|
+
readOnly?: boolean
|
|
106
|
+
/**
|
|
107
|
+
* Field mode(2026-05-05 Phase B3 align):
|
|
108
|
+
* edit — 一般可互動 Switch(預設)
|
|
109
|
+
* display — **純展示**:渲染 ✓ / —(無互動 primitive、無 input chrome);
|
|
110
|
+
* 對齊 Carbon read-only / DataTable boolean cell。
|
|
111
|
+
* `readonly` 保留 toggle 視覺 + 鎖互動;`display` 完全無 toggle 形體 — 兩者語意分離(field-types.ts)。
|
|
112
|
+
* readonly — 同 readOnly prop
|
|
113
|
+
* disabled — 同 disabled prop
|
|
114
|
+
*/
|
|
115
|
+
mode?: FieldMode
|
|
116
|
+
/**
|
|
117
|
+
* Visual chrome — Switch 本體無 input wrapper variant,本 prop 對 Switch 主體無視覺影響;
|
|
118
|
+
* 為對齊 Field 4-mode + chrome 透傳契約而保留(M19 一致性)。
|
|
119
|
+
*/
|
|
120
|
+
variant?: FieldVariant
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const Switch = React.forwardRef<
|
|
124
|
+
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
125
|
+
SwitchProps
|
|
126
|
+
>(
|
|
127
|
+
(
|
|
128
|
+
{
|
|
129
|
+
className,
|
|
130
|
+
size,
|
|
131
|
+
label,
|
|
132
|
+
description,
|
|
133
|
+
readOnly = false,
|
|
134
|
+
disabled,
|
|
135
|
+
mode,
|
|
136
|
+
// chrome 對 Switch 主體無視覺影響(無 input wrapper)— 接收純為 prop 一致性;destructure 防 leak 到 DOM。
|
|
137
|
+
variant: _chrome,
|
|
138
|
+
id: idProp,
|
|
139
|
+
...props
|
|
140
|
+
},
|
|
141
|
+
ref
|
|
142
|
+
) => {
|
|
143
|
+
const sizeKey = size ?? 'md'
|
|
144
|
+
const spec = SPECS[sizeKey]
|
|
145
|
+
|
|
146
|
+
// ── mode='display' ─────────────────────────────────────────────────────
|
|
147
|
+
// 純展示模式:無互動 toggle、無 input variant,渲染 ✓ / —。
|
|
148
|
+
// 與 Checkbox display 對齊(同為 boolean primitive)— DataTable boolean cell 場景共用。
|
|
149
|
+
// 與 readonly 差異:readonly 保留 toggle 視覺 + 鎖互動;display 完全無 toggle 形體。
|
|
150
|
+
if (mode === 'display') {
|
|
151
|
+
const isChecked = props.checked === true
|
|
152
|
+
return isChecked
|
|
153
|
+
? <span className="text-foreground">✓</span>
|
|
154
|
+
: <span className="text-fg-muted">—</span>
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Field context 偵測:在 Field 內時忽略自己的 label/description,避免雙層
|
|
158
|
+
const fieldCtx = useFieldContext()
|
|
159
|
+
const insideField = fieldCtx?.hasFieldWrapper === true
|
|
160
|
+
const effectiveLabel = insideField ? undefined : label
|
|
161
|
+
const effectiveDescription = insideField ? undefined : description
|
|
162
|
+
|
|
163
|
+
// 在 horizontal Field 內自動齊右(iOS / macOS Settings canonical):
|
|
164
|
+
// horizontal Field 的 layout 是 label(固定寬)| control area(fill),Switch 作為
|
|
165
|
+
// trailing control 應靠右(對齊 horizontal DescriptionItem 的「label 左 / value 右」模式)。
|
|
166
|
+
// 世界級對照:iOS Settings / macOS System Settings / GitHub Settings / Figma prefs
|
|
167
|
+
// 一律 switch 齊右 — 視覺掃描快、對齊一致。
|
|
168
|
+
const alignRightInField =
|
|
169
|
+
insideField && fieldCtx?.orientation === 'horizontal' ? 'ml-auto' : ''
|
|
170
|
+
|
|
171
|
+
// Id 連結:優先使用 prop,再退到 Field context 的 id,最後用 useId 生成
|
|
172
|
+
const generatedId = React.useId()
|
|
173
|
+
const inputId = idProp ?? fieldCtx?.id ?? generatedId
|
|
174
|
+
|
|
175
|
+
const rootEl = (
|
|
176
|
+
<SwitchPrimitives.Root
|
|
177
|
+
id={inputId}
|
|
178
|
+
className={cn(switchVariants({ size }), alignRightInField, className)}
|
|
179
|
+
ref={ref}
|
|
180
|
+
disabled={disabled}
|
|
181
|
+
aria-readonly={readOnly || undefined}
|
|
182
|
+
data-readonly={readOnly || undefined}
|
|
183
|
+
tabIndex={readOnly ? -1 : undefined}
|
|
184
|
+
aria-describedby={fieldCtx?.descriptionId}
|
|
185
|
+
{...props}
|
|
186
|
+
>
|
|
187
|
+
<SwitchPrimitives.Thumb
|
|
188
|
+
className={cn(
|
|
189
|
+
'pointer-events-none flex items-center justify-center rounded-full bg-on-emphasis border-2',
|
|
190
|
+
'transition-all duration-150',
|
|
191
|
+
'data-[state=unchecked]:translate-x-0 data-[state=unchecked]:border-border',
|
|
192
|
+
'data-[state=checked]:border-primary',
|
|
193
|
+
sizeKey === 'lg' ? 'data-[state=checked]:translate-x-6' : 'data-[state=checked]:translate-x-5',
|
|
194
|
+
)}
|
|
195
|
+
style={{ width: spec.thumb, height: spec.thumb }}
|
|
196
|
+
>
|
|
197
|
+
{/* Check icon — Radix Thumb inherits data-state from Root */}
|
|
198
|
+
<Check
|
|
199
|
+
size={spec.check}
|
|
200
|
+
strokeWidth={spec.checkStroke}
|
|
201
|
+
className="text-primary opacity-0 transition-opacity duration-150 group-data-[state=checked]:opacity-100"
|
|
202
|
+
aria-hidden
|
|
203
|
+
/>
|
|
204
|
+
</SwitchPrimitives.Thumb>
|
|
205
|
+
</SwitchPrimitives.Root>
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
// 無 label → 只渲染 switch 本體
|
|
209
|
+
if (effectiveLabel == null) return rootEl
|
|
210
|
+
|
|
211
|
+
// 有 label → 包 <label> + codified 樣式
|
|
212
|
+
// Switch 慣例:label 在左、switch 在右(對齊 iOS / Polaris / Material 標準)
|
|
213
|
+
// label 行第一行對齊 switch 中線:容器用 items-start + switch 包 h-[1lh] flex-center
|
|
214
|
+
return (
|
|
215
|
+
<label
|
|
216
|
+
htmlFor={inputId}
|
|
217
|
+
className={cn(
|
|
218
|
+
'inline-flex items-start gap-3 select-none',
|
|
219
|
+
disabled ? 'cursor-not-allowed' : readOnly ? 'cursor-default' : 'cursor-pointer'
|
|
220
|
+
)}
|
|
221
|
+
>
|
|
222
|
+
{/* Label↔desc gap typography-mode-aware:
|
|
223
|
+
sm/md = reading(body+body 14/1.5),lg = reading-lg(body-lg+body 14/1.5) */}
|
|
224
|
+
<span
|
|
225
|
+
className={cn(
|
|
226
|
+
'flex-1 min-w-0 flex flex-col',
|
|
227
|
+
sizeKey === 'lg'
|
|
228
|
+
? 'gap-[var(--item-gap-label-desc-reading-lg)]'
|
|
229
|
+
: 'gap-[var(--item-gap-label-desc-reading)]',
|
|
230
|
+
)}
|
|
231
|
+
>
|
|
232
|
+
<span
|
|
233
|
+
className={cn(
|
|
234
|
+
// Reading mode 字級:lg → text-body-lg (16px),sm/md → text-body (14px)
|
|
235
|
+
sizeKey === 'lg' ? 'text-body-lg' : 'text-body',
|
|
236
|
+
disabled ? 'text-fg-disabled' : 'text-foreground'
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{effectiveLabel}
|
|
240
|
+
</span>
|
|
241
|
+
{effectiveDescription != null && (
|
|
242
|
+
<span
|
|
243
|
+
className={cn(
|
|
244
|
+
disabled ? 'text-fg-disabled' : 'text-fg-secondary'
|
|
245
|
+
)}
|
|
246
|
+
// Reading mode description:**最小 14px**(spec 14→14px, 16→14px),lh 預設 1.5。
|
|
247
|
+
// 用 inline style 直接繞過 tailwind-merge 對 text-body / text-fg-* 的潛在衝突。
|
|
248
|
+
style={{ fontSize: 'var(--font-body-size)' }}
|
|
249
|
+
>
|
|
250
|
+
{effectiveDescription}
|
|
251
|
+
</span>
|
|
252
|
+
)}
|
|
253
|
+
</span>
|
|
254
|
+
<span className="h-[1lh] flex items-center shrink-0">
|
|
255
|
+
{rootEl}
|
|
256
|
+
</span>
|
|
257
|
+
</label>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
Switch.displayName = SwitchPrimitives.Root.displayName
|
|
262
|
+
|
|
263
|
+
// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
|
|
264
|
+
// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
|
|
265
|
+
export const switchMeta = {
|
|
266
|
+
component: 'Switch',
|
|
267
|
+
family: 4,
|
|
268
|
+
variants: {
|
|
269
|
+
|
|
270
|
+
},
|
|
271
|
+
sizes: {
|
|
272
|
+
sm: { fieldHeight: 28, iconSize: 16, typography: 'body' },
|
|
273
|
+
md: { fieldHeight: 32, iconSize: 16, typography: 'body' },
|
|
274
|
+
lg: { fieldHeight: 40, iconSize: 20, typography: 'body' },
|
|
275
|
+
},
|
|
276
|
+
states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
|
|
277
|
+
tokens: {
|
|
278
|
+
bg: ['bg-primary'],
|
|
279
|
+
fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'],
|
|
280
|
+
ring: ['ring-ring'],
|
|
281
|
+
},
|
|
282
|
+
defaultSize: 'md',
|
|
283
|
+
} as const
|
|
284
|
+
|
|
285
|
+
export { Switch, switchVariants }
|