@teamix-evo/ui 0.1.1 → 0.3.0
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/README.md +184 -184
- package/manifest.json +680 -492
- package/package.json +20 -10
- package/src/components/accordion/accordion.meta.md +5 -4
- package/src/components/accordion/accordion.stories.tsx +14 -9
- package/src/components/accordion/accordion.tsx +104 -8
- package/src/components/affix/affix.meta.md +20 -2
- package/src/components/affix/affix.stories.tsx +102 -25
- package/src/components/affix/affix.tsx +79 -9
- package/src/components/alert/alert.meta.md +44 -13
- package/src/components/alert/alert.stories.tsx +66 -21
- package/src/components/alert/alert.tsx +81 -34
- package/src/components/alert-dialog/alert-dialog.meta.md +61 -16
- package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
- package/src/components/alert-dialog/alert-dialog.tsx +60 -13
- package/src/components/anchor/anchor.meta.md +8 -3
- package/src/components/anchor/anchor.stories.tsx +3 -3
- package/src/components/anchor/anchor.tsx +2 -2
- package/src/components/app/app.meta.md +9 -4
- package/src/components/app/app.stories.tsx +9 -7
- package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
- package/src/components/auto-complete/auto-complete.meta.md +14 -6
- package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
- package/src/components/auto-complete/auto-complete.tsx +119 -71
- package/src/components/avatar/avatar.meta.md +6 -7
- package/src/components/avatar/avatar.stories.tsx +21 -3
- package/src/components/avatar/avatar.tsx +24 -23
- package/src/components/badge/badge.meta.md +10 -9
- package/src/components/badge/badge.stories.tsx +2 -2
- package/src/components/badge/badge.tsx +9 -15
- package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
- package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
- package/src/components/breadcrumb/breadcrumb.tsx +22 -8
- package/src/components/button/button.meta.md +258 -21
- package/src/components/button/button.stories.tsx +549 -41
- package/src/components/button/button.tsx +335 -33
- package/src/components/button/demo/as-child.tsx +24 -0
- package/src/components/button/demo/basic.tsx +8 -0
- package/src/components/button/demo/block.tsx +16 -0
- package/src/components/button/demo/loading.tsx +19 -0
- package/src/components/button/demo/shapes.tsx +18 -0
- package/src/components/button/demo/sizes.tsx +19 -0
- package/src/components/button/demo/variants.tsx +19 -0
- package/src/components/button/demo/with-icon.tsx +20 -0
- package/src/components/calendar/calendar.meta.md +13 -3
- package/src/components/calendar/calendar.stories.tsx +6 -6
- package/src/components/calendar/calendar.tsx +73 -8
- package/src/components/card/card.meta.md +27 -5
- package/src/components/card/card.stories.tsx +42 -3
- package/src/components/card/card.tsx +146 -63
- package/src/components/carousel/carousel.meta.md +4 -3
- package/src/components/carousel/carousel.stories.tsx +11 -6
- package/src/components/cascader/cascader.meta.md +47 -17
- package/src/components/cascader/cascader.stories.tsx +22 -10
- package/src/components/cascader/cascader.tsx +428 -85
- package/src/components/checkbox/checkbox.meta.md +75 -7
- package/src/components/checkbox/checkbox.stories.tsx +161 -3
- package/src/components/checkbox/checkbox.tsx +77 -9
- package/src/components/collapsible/collapsible.meta.md +14 -6
- package/src/components/collapsible/collapsible.stories.tsx +10 -2
- package/src/components/collapsible/collapsible.tsx +93 -6
- package/src/components/color-picker/color-picker.meta.md +12 -7
- package/src/components/color-picker/color-picker.stories.tsx +86 -7
- package/src/components/color-picker/color-picker.tsx +20 -9
- package/src/components/command/command.meta.md +29 -13
- package/src/components/command/command.stories.tsx +4 -4
- package/src/components/command/command.tsx +19 -8
- package/src/components/context-menu/context-menu.meta.md +11 -8
- package/src/components/context-menu/context-menu.stories.tsx +11 -3
- package/src/components/context-menu/context-menu.tsx +21 -8
- package/src/components/data-table/data-table.meta.md +6 -5
- package/src/components/data-table/data-table.stories.tsx +13 -6
- package/src/components/data-table/data-table.tsx +2 -2
- package/src/components/date-picker/date-picker.meta.md +88 -19
- package/src/components/date-picker/date-picker.stories.tsx +55 -5
- package/src/components/date-picker/date-picker.tsx +1489 -91
- package/src/components/descriptions/descriptions.meta.md +10 -5
- package/src/components/descriptions/descriptions.stories.tsx +3 -3
- package/src/components/descriptions/descriptions.tsx +22 -14
- package/src/components/dialog/dialog.meta.md +76 -13
- package/src/components/dialog/dialog.stories.tsx +182 -20
- package/src/components/dialog/dialog.tsx +67 -15
- package/src/components/dialog/imperative.tsx +252 -0
- package/src/components/drawer/drawer.meta.md +33 -34
- package/src/components/drawer/drawer.stories.tsx +29 -12
- package/src/components/drawer/drawer.tsx +22 -113
- package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
- package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
- package/src/components/ellipsis/ellipsis.meta.md +87 -0
- package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
- package/src/components/ellipsis/ellipsis.tsx +153 -0
- package/src/components/empty/empty.meta.md +9 -4
- package/src/components/empty/empty.stories.tsx +4 -4
- package/src/components/empty/empty.tsx +10 -3
- package/src/components/field/field.meta.md +47 -9
- package/src/components/field/field.stories.tsx +385 -5
- package/src/components/field/field.tsx +263 -35
- package/src/components/filter-bar/filter-bar.meta.md +92 -0
- package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
- package/src/components/filter-bar/filter-bar.tsx +568 -0
- package/src/components/flex/flex.meta.md +54 -6
- package/src/components/flex/flex.stories.tsx +107 -20
- package/src/components/flex/flex.tsx +27 -4
- package/src/components/float-button/float-button.meta.md +8 -3
- package/src/components/float-button/float-button.stories.tsx +9 -7
- package/src/components/float-button/float-button.tsx +1 -1
- package/src/components/form/form.meta.md +39 -17
- package/src/components/form/form.stories.tsx +350 -3
- package/src/components/form/form.tsx +101 -35
- package/src/components/grid/grid.meta.md +7 -2
- package/src/components/grid/grid.stories.tsx +6 -4
- package/src/components/hover-card/hover-card.meta.md +20 -9
- package/src/components/hover-card/hover-card.stories.tsx +34 -5
- package/src/components/hover-card/hover-card.tsx +51 -13
- package/src/components/icon/DEVELOPMENT.md +809 -0
- package/src/components/icon/icon.meta.md +170 -0
- package/src/components/icon/icon.stories.tsx +344 -0
- package/src/components/icon/icon.tsx +248 -0
- package/src/components/image/image.meta.md +9 -4
- package/src/components/image/image.stories.tsx +3 -3
- package/src/components/image/image.tsx +6 -4
- package/src/components/input/demo/basic.tsx +12 -0
- package/src/components/input/demo/clearable.tsx +21 -0
- package/src/components/input/demo/show-count.tsx +18 -0
- package/src/components/input/demo/sizes.tsx +15 -0
- package/src/components/input/input.meta.md +39 -33
- package/src/components/input/input.stories.tsx +62 -35
- package/src/components/input/input.tsx +97 -98
- package/src/components/input-group/input-group.meta.md +54 -22
- package/src/components/input-group/input-group.stories.tsx +49 -16
- package/src/components/input-group/input-group.tsx +44 -8
- package/src/components/input-number/input-number.meta.md +64 -7
- package/src/components/input-number/input-number.stories.tsx +46 -8
- package/src/components/input-number/input-number.tsx +99 -26
- package/src/components/input-otp/input-otp.meta.md +4 -3
- package/src/components/input-otp/input-otp.stories.tsx +3 -3
- package/src/components/input-otp/input-otp.tsx +1 -1
- package/src/components/item/item.meta.md +8 -3
- package/src/components/item/item.stories.tsx +8 -5
- package/src/components/item/item.tsx +7 -6
- package/src/components/kbd/kbd.meta.md +13 -4
- package/src/components/kbd/kbd.stories.tsx +4 -4
- package/src/components/kbd/kbd.tsx +10 -5
- package/src/components/label/label.meta.md +18 -10
- package/src/components/label/label.stories.tsx +64 -6
- package/src/components/label/label.tsx +91 -19
- package/src/components/masonry/masonry.meta.md +8 -3
- package/src/components/masonry/masonry.stories.tsx +7 -5
- package/src/components/masonry/masonry.tsx +1 -0
- package/src/components/mentions/mentions.meta.md +36 -6
- package/src/components/mentions/mentions.stories.tsx +120 -6
- package/src/components/mentions/mentions.tsx +11 -5
- package/src/components/menubar/menubar.meta.md +30 -12
- package/src/components/menubar/menubar.stories.tsx +62 -2
- package/src/components/menubar/menubar.tsx +9 -9
- package/src/components/native-select/native-select.meta.md +8 -3
- package/src/components/native-select/native-select.stories.tsx +8 -5
- package/src/components/native-select/native-select.tsx +1 -1
- package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
- package/src/components/navigation-menu/navigation-menu.tsx +8 -4
- package/src/components/notification/notification.meta.md +52 -10
- package/src/components/notification/notification.stories.tsx +11 -9
- package/src/components/notification/notification.tsx +36 -21
- package/src/components/page-header/DEVELOPMENT.md +842 -0
- package/src/components/page-header/page-header.meta.md +208 -0
- package/src/components/page-header/page-header.stories.tsx +421 -0
- package/src/components/page-header/page-header.tsx +281 -0
- package/src/components/pagination/pagination.meta.md +140 -37
- package/src/components/pagination/pagination.stories.tsx +232 -10
- package/src/components/pagination/pagination.tsx +355 -63
- package/src/components/popconfirm/popconfirm.meta.md +9 -4
- package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
- package/src/components/popconfirm/popconfirm.tsx +2 -2
- package/src/components/popover/popover.meta.md +62 -5
- package/src/components/popover/popover.stories.tsx +83 -7
- package/src/components/popover/popover.tsx +77 -28
- package/src/components/progress/progress.meta.md +38 -6
- package/src/components/progress/progress.stories.tsx +3 -3
- package/src/components/progress/progress.tsx +24 -16
- package/src/components/radio-group/radio-group.meta.md +79 -7
- package/src/components/radio-group/radio-group.stories.tsx +39 -3
- package/src/components/radio-group/radio-group.tsx +149 -18
- package/src/components/rate/rate.meta.md +35 -4
- package/src/components/rate/rate.stories.tsx +13 -5
- package/src/components/rate/rate.tsx +37 -10
- package/src/components/resizable/resizable.meta.md +7 -4
- package/src/components/resizable/resizable.stories.tsx +6 -6
- package/src/components/resizable/resizable.tsx +1 -1
- package/src/components/result/result.meta.md +7 -2
- package/src/components/result/result.stories.tsx +4 -8
- package/src/components/result/result.tsx +24 -15
- package/src/components/scroll-area/scroll-area.meta.md +4 -3
- package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
- package/src/components/scroll-area/scroll-area.tsx +3 -3
- package/src/components/segmented/segmented.meta.md +7 -4
- package/src/components/segmented/segmented.stories.tsx +37 -8
- package/src/components/segmented/segmented.tsx +15 -7
- package/src/components/select/select.meta.md +197 -52
- package/src/components/select/select.stories.tsx +238 -63
- package/src/components/select/select.tsx +718 -171
- package/src/components/separator/separator.meta.md +4 -3
- package/src/components/separator/separator.stories.tsx +3 -3
- package/src/components/separator/separator.tsx +3 -7
- package/src/components/sheet/sheet.meta.md +32 -16
- package/src/components/sheet/sheet.stories.tsx +116 -10
- package/src/components/sheet/sheet.tsx +116 -29
- package/src/components/sidebar/sidebar.meta.md +37 -18
- package/src/components/sidebar/sidebar.stories.tsx +701 -29
- package/src/components/sidebar/sidebar.tsx +615 -142
- package/src/components/skeleton/skeleton.meta.md +4 -5
- package/src/components/skeleton/skeleton.stories.tsx +4 -4
- package/src/components/skeleton/skeleton.tsx +7 -7
- package/src/components/slider/slider.meta.md +57 -5
- package/src/components/slider/slider.stories.tsx +58 -6
- package/src/components/slider/slider.tsx +154 -13
- package/src/components/sonner/sonner.meta.md +58 -7
- package/src/components/sonner/sonner.stories.tsx +78 -5
- package/src/components/sonner/sonner.tsx +137 -8
- package/src/components/spinner/spinner.meta.md +62 -13
- package/src/components/spinner/spinner.stories.tsx +66 -14
- package/src/components/spinner/spinner.tsx +111 -9
- package/src/components/statistic/statistic.meta.md +7 -2
- package/src/components/statistic/statistic.stories.tsx +3 -7
- package/src/components/statistic/statistic.tsx +5 -6
- package/src/components/steps/steps.meta.md +18 -4
- package/src/components/steps/steps.stories.tsx +43 -3
- package/src/components/steps/steps.tsx +15 -12
- package/src/components/switch/switch.meta.md +51 -5
- package/src/components/switch/switch.stories.tsx +6 -6
- package/src/components/switch/switch.tsx +109 -41
- package/src/components/table/table.meta.md +17 -6
- package/src/components/table/table.stories.tsx +10 -5
- package/src/components/table/table.tsx +4 -4
- package/src/components/tabs/tabs.meta.md +38 -25
- package/src/components/tabs/tabs.stories.tsx +111 -25
- package/src/components/tabs/tabs.tsx +125 -54
- package/src/components/tag/tag.meta.md +105 -40
- package/src/components/tag/tag.stories.tsx +189 -16
- package/src/components/tag/tag.tsx +222 -21
- package/src/components/textarea/textarea.meta.md +35 -19
- package/src/components/textarea/textarea.stories.tsx +32 -6
- package/src/components/textarea/textarea.tsx +33 -9
- package/src/components/time-picker/time-picker.meta.md +124 -32
- package/src/components/time-picker/time-picker.stories.tsx +85 -15
- package/src/components/time-picker/time-picker.tsx +913 -61
- package/src/components/timeline/timeline.meta.md +14 -6
- package/src/components/timeline/timeline.stories.tsx +37 -7
- package/src/components/timeline/timeline.tsx +35 -14
- package/src/components/toggle/toggle.meta.md +5 -4
- package/src/components/toggle/toggle.stories.tsx +4 -4
- package/src/components/toggle/toggle.tsx +4 -3
- package/src/components/toggle-group/toggle-group.meta.md +5 -4
- package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
- package/src/components/toggle-group/toggle-group.tsx +2 -2
- package/src/components/tooltip/tooltip.meta.md +55 -5
- package/src/components/tooltip/tooltip.stories.tsx +42 -5
- package/src/components/tooltip/tooltip.tsx +81 -21
- package/src/components/tour/tour.meta.md +9 -4
- package/src/components/tour/tour.stories.tsx +3 -3
- package/src/components/tour/tour.tsx +4 -4
- package/src/components/transfer/transfer.meta.md +11 -6
- package/src/components/transfer/transfer.stories.tsx +4 -8
- package/src/components/transfer/transfer.tsx +28 -21
- package/src/components/tree/tree.meta.md +63 -5
- package/src/components/tree/tree.stories.tsx +31 -12
- package/src/components/tree/tree.tsx +9 -8
- package/src/components/tree-select/tree-select.meta.md +59 -8
- package/src/components/tree-select/tree-select.stories.tsx +3 -3
- package/src/components/tree-select/tree-select.tsx +42 -7
- package/src/components/typography/typography.meta.md +61 -14
- package/src/components/typography/typography.stories.tsx +12 -11
- package/src/components/typography/typography.tsx +43 -28
- package/src/components/upload/upload.meta.md +49 -4
- package/src/components/upload/upload.stories.tsx +72 -12
- package/src/components/upload/upload.tsx +170 -37
- package/src/components/watermark/watermark.meta.md +7 -2
- package/src/components/watermark/watermark.stories.tsx +101 -9
- package/src/components/watermark/watermark.tsx +1 -0
- package/src/hooks/use-breakpoint.ts +117 -0
- package/src/hooks/use-debounce-callback.ts +52 -0
- package/src/hooks/use-mobile.ts +23 -0
- package/src/stories/theme-tokens.stories.tsx +747 -0
- package/src/utils/trigger-input.ts +53 -0
- package/src/components/button-group/button-group.meta.md +0 -92
- package/src/components/button-group/button-group.stories.tsx +0 -90
- package/src/components/button-group/button-group.tsx +0 -75
- package/src/components/combobox/combobox.meta.md +0 -93
- package/src/components/combobox/combobox.stories.tsx +0 -55
- package/src/components/combobox/combobox.tsx +0 -130
- package/src/components/space/space.meta.md +0 -94
- package/src/components/space/space.stories.tsx +0 -94
- package/src/components/space/space.tsx +0 -106
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
id: checkbox
|
|
3
3
|
name: Checkbox
|
|
4
4
|
type: component
|
|
5
|
-
category:
|
|
5
|
+
category: data-entry
|
|
6
6
|
since: 0.1.0
|
|
7
|
-
package:
|
|
7
|
+
package: '@teamix-evo/ui'
|
|
8
|
+
displayName: 复选框
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# Checkbox
|
|
11
|
+
# Checkbox 复选框
|
|
11
12
|
|
|
12
13
|
复选框 — Radix Checkbox(原生支持 indeterminate)+ antd `Checkbox.Group`(`options` 数组驱动)。
|
|
13
14
|
|
|
@@ -28,7 +29,16 @@ package: "@teamix-evo/ui"
|
|
|
28
29
|
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
|
|
29
30
|
|
|
30
31
|
<!-- auto:props:begin -->
|
|
31
|
-
|
|
32
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
33
|
+
| --- | --- | --- | --- | --- |
|
|
34
|
+
| `checked` | `boolean \| 'indeterminate'` | – | – | 受控选中状态(等价 antd `checked`)。 取值 `true` / `false` / `'indeterminate'`(半选,只影响视觉,不改变 checked 语义)。 与 `defaultChecked` 二选一。 |
|
|
35
|
+
| `defaultChecked` | `boolean \| 'indeterminate'` | `false` | – | 非受控初始选中态(等价 antd `defaultChecked`)。 |
|
|
36
|
+
| `onCheckedChange` | `(checked: boolean \| 'indeterminate') => void` | – | – | 选中状态变化回调 — Radix 命名,等价 antd `onChange(checked)`。 业务侧迁移自 antd 时把 `onChange={(e) => fn(e.target.checked)}` 改为 `onCheckedChange={fn}`。 |
|
|
37
|
+
| `disabled` | `boolean` | `false` | – | 禁用整个 checkbox(灰化 + 不响应交互)。 |
|
|
38
|
+
| `required` | `boolean` | `false` | – | 必填 — 设置 HTML 原生 `required`,表单层校验依赖此值。 |
|
|
39
|
+
| `name` | `string` | – | – | 表单字段名(原生 `<input name>`,提交表单时使用)。 |
|
|
40
|
+
| `value` | `string` | – | – | 表单字段值(勾选时随表单提交)。 |
|
|
41
|
+
| `id` | `string` | – | – | id 属性,配合 `<label htmlFor>` 关联标签。 |
|
|
32
42
|
<!-- auto:props:end -->
|
|
33
43
|
|
|
34
44
|
## 依赖
|
|
@@ -53,16 +63,18 @@ pnpm add @radix-ui/react-checkbox@^1.1.0 lucide-react@^0.460.0
|
|
|
53
63
|
```
|
|
54
64
|
<!-- auto:deps:end -->
|
|
55
65
|
|
|
56
|
-
> 透传 Radix Checkbox.Root 所有 props
|
|
57
|
-
> 子组件 `CheckboxGroup` 的 props 详见 [`checkbox.tsx`](./checkbox.tsx) 的 `CheckboxGroupProps`。
|
|
66
|
+
> 透传 Radix Checkbox.Root 所有 props(`checked` / `defaultChecked` / `onCheckedChange` / `disabled` / `required` / `name` / `value` 等)。
|
|
58
67
|
|
|
59
68
|
## AI 生成纪律
|
|
60
69
|
|
|
61
70
|
- **`checked="indeterminate"` 不是布尔**:Radix 用字面量 `'indeterminate'` 表示半选,**不要**传 number 或其他值
|
|
62
|
-
- **全选 + 半选模式**:列表全选场景 — 全选用 `checked={all}`,部分选用 `checked="indeterminate"`,父级 onCheckedChange
|
|
71
|
+
- **全选 + 半选模式**:列表全选场景 — 全选用 `checked={all}`,部分选用 `checked="indeterminate"`,父级 onCheckedChange 触发批量选/全清。完整 demo 见 [`SelectAll` Story](./checkbox.stories.tsx)
|
|
63
72
|
- **必带 Label**:Checkbox 单独无文字,必须 `<label>...<Checkbox /></label>` 或 `<Checkbox id /> + <Label htmlFor>`
|
|
64
73
|
- **`CheckboxGroup` options 必须有稳定的 `value`**:不要用 index 或动态值;否则 controlled diff 失效
|
|
65
74
|
- **disabled 优先级**:整组 `disabled={true}` 会覆盖单项的 `disabled={false}`
|
|
75
|
+
- **对钩视觉居中**:Indicator 内部使用 `flex items-center justify-center size-full` + `<Check className="size-3.5" strokeWidth={3} />`(`strokeWidth` prop 避免 `stroke-[\*]` arbitrary value),**不要随意改回 `size-3`**(会因 lucide Check viewBox 偏上出现视觉偏离中线)
|
|
76
|
+
- **hover 反馈用 `enabled:` 前缀**(ADR 0024):`enabled:hover:border-primary` / `data-[state=unchecked]:enabled:hover:bg-primary/5` / checked 态用 `data-[state=checked]:enabled:hover:bg-primary/90` 加深;disabled 态不响应 hover
|
|
77
|
+
- **激活态视觉自管,不被 scoped CSS 染灰**(ADR 0024):`data-state="checked"` / `"indeterminate"` 态由组件自身 `border-primary` + `bg-primary` 控制,uni-manager scoped CSS 已统一在 `:not()` 链里排除;新增激活态视觉时确保 scoped CSS 排除链覆盖
|
|
66
78
|
|
|
67
79
|
## Examples
|
|
68
80
|
|
|
@@ -97,3 +109,59 @@ const [v, setV] = React.useState(['apple']);
|
|
|
97
109
|
// Group 纵向
|
|
98
110
|
<CheckboxGroup orientation="vertical" options={...} defaultValue={[]} />
|
|
99
111
|
```
|
|
112
|
+
|
|
113
|
+
## Checkbox 形态 — 旧库 API → 新映射
|
|
114
|
+
|
|
115
|
+
> 决策:**不新增独立组件**,直接用 `Checkbox` + `CheckboxGroup` 承接 cloud-design `Checkbox` / `Checkbox.Group` 全部用法。本章节是从旧库平滑迁移的查表索引,与 [上面的 Props 表](#props) 互补。
|
|
116
|
+
|
|
117
|
+
### 命名映射
|
|
118
|
+
|
|
119
|
+
| cloud-design 旧库 | `@teamix-evo/ui` | 说明 |
|
|
120
|
+
| ----------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------- |
|
|
121
|
+
| `Checkbox` | `Checkbox` | 等价 — Radix Root,原生支持 indeterminate |
|
|
122
|
+
| `Checkbox.Group`(命名空间子组件) | `CheckboxGroup`(独立 named export) | shadcn 风格命名,可独立 import / tree-shake |
|
|
123
|
+
| `<Checkbox.Group dataSource={[{label,value}]}>` | `<CheckboxGroup options={[{label,value}]}>` | `dataSource` → `options`(对齐 antd 通用命名) |
|
|
124
|
+
| `<Checkbox.Group direction="hor\|ver">` | `<CheckboxGroup orientation="horizontal\|vertical">` | 完整单词,与 Radix `orientation` 命名一致 |
|
|
125
|
+
| `<Checkbox.Group value={[]} onChange={fn}>` | `<CheckboxGroup value={[]} onChange={fn}>` | 同名同语义,签名 `(next: string[]) => void` |
|
|
126
|
+
| `<Checkbox.Group defaultValue={[]}>` | `<CheckboxGroup defaultValue={[]}>` | 同名 |
|
|
127
|
+
| `<Checkbox.Group disabled>` | `<CheckboxGroup disabled>` | 整组禁用,覆盖单项 |
|
|
128
|
+
| `<Checkbox indeterminate>` | `<Checkbox checked="indeterminate">` | Radix 用字面量代替布尔 prop,行为等价 |
|
|
129
|
+
| 全选联动(旧库手动) | 父框 `checked={all ? true : some ? 'indeterminate' : false}` | 双方都需手动实现,新库示意见上面 Indeterminate 示例 |
|
|
130
|
+
|
|
131
|
+
### Breaking Changes(从旧库迁移时需改写)
|
|
132
|
+
|
|
133
|
+
1. `Checkbox.Group` → `CheckboxGroup`(import 路径变动:`import { CheckboxGroup }`)
|
|
134
|
+
2. `direction` → `orientation`(取值同时从缩写 `hor/ver` 改为完整 `horizontal/vertical`)
|
|
135
|
+
3. `dataSource` → `options`(签名一致,只是 prop 名)
|
|
136
|
+
4. `indeterminate` boolean → `checked="indeterminate"` 字面量(与 Radix 对齐)
|
|
137
|
+
|
|
138
|
+
## cd hybridcloud 视觉对齐(本组件已落地)
|
|
139
|
+
|
|
140
|
+
> 对齐 cd hybridcloud `--checkbox-*` token,本次落地。token 真值取自 `node_modules/@alicloudfe/components/lib/theme/hybridcloud/css-var-definition.scss`。
|
|
141
|
+
|
|
142
|
+
| 状态 | cd token / 真值 | 我们的 className | 备注 |
|
|
143
|
+
| --- | --- | --- | --- |
|
|
144
|
+
| default border | `--checkbox-border-color: #cccccc`(中性灰) | `border-input`(uni-manager `#d9d9d9`) | 视觉等价 |
|
|
145
|
+
| default bg | `--checkbox-bg-color: #FFFFFF` | `bg-background` | ✓ |
|
|
146
|
+
| hover border | `--checkbox-hovered-border-color: #589ADB`(浅蓝) | `enabled:hover:border-primary` | 取主色更聚焦,enabled-only |
|
|
147
|
+
| hover bg | `--checkbox-hovered-bg-color: #F0F7FF`(超浅蓝) | `data-[state=unchecked]:enabled:hover:bg-primary/5` | 极浅主色 5% 透明 |
|
|
148
|
+
| checked bg | `--checkbox-checked-bg-color: #0064C8`(primary) | `data-[state=checked]:bg-primary` | ✓ |
|
|
149
|
+
| checked-hover bg | `--checkbox-checked-hovered-bg-color: #0057AD`(深蓝) | `data-[state=checked]:enabled:hover:bg-primary/90` | 主色 90% 透明 |
|
|
150
|
+
| checked border | `--checkbox-checked-border-color: transparent` | `data-[state=checked]:border-primary` | 视觉等价(主色 bg + 主色 border 边缘融合) |
|
|
151
|
+
| indeterminate | 与 checked 相同 + 横线 indicator | `data-[state=indeterminate]:bg-primary` + `<Minus />` | ✓ |
|
|
152
|
+
| 圆角 | `--checkbox-border-radius: 2px`(corner-1) | `rounded-md`(uni-manager `--radius-md: 2px`) | ✓ |
|
|
153
|
+
| disabled | `opacity-50` 整体淡化 + 不响应 hover | `disabled:opacity-50` + 所有 hover 加 `enabled:` 前缀 | ADR 0024 |
|
|
154
|
+
|
|
155
|
+
### 不修复 / 后续工序清单
|
|
156
|
+
|
|
157
|
+
- ~~**圆角微调**(2px → 3px)~~:本波次已落地 — `rounded-md` 在 uni-manager 即 2px(`--radius-md`),与 cd `corner-1` 一致
|
|
158
|
+
- **disabled 灰色 vs opacity:0.5**:全局策略,报告 §5 已列入 "保留新库优势",**不修复**
|
|
159
|
+
- **全选联动内置 prop**:旧库也需手动,本次保持手动模式(API 简洁优先);如有强需求未来可加 `<CheckboxGroupAll>` slot
|
|
160
|
+
|
|
161
|
+
### Checkbox 专项 AI 生成纪律
|
|
162
|
+
|
|
163
|
+
- `CheckboxGroup` 独立导入:`import { CheckboxGroup } from '@/components/ui/checkbox'`,**不要**写 `Checkbox.Group`(命名空间访问会编译错)
|
|
164
|
+
- 半选状态用字面量 `'indeterminate'`,**不要**传 `boolean | null` 或自造常量
|
|
165
|
+
- `options[].value` 必须稳定唯一(string),**不要**用 index / 动态 ID,否则 controlled diff 失效
|
|
166
|
+
- 整组 `disabled` 优先级最高,会覆盖单项 `options[].disabled = false`(旧库行为一致)
|
|
167
|
+
- `onChange` 签名是 `(next: string[]) => void`,**不要**期待事件对象第二参数
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
3
|
import { Checkbox, CheckboxGroup } from './checkbox';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Checkbox> = {
|
|
6
|
-
title: '
|
|
6
|
+
title: '数据录入 · Data Entry/Checkbox',
|
|
7
7
|
component: Checkbox,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
9
|
parameters: {
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
'复选框 — 在一组选项中进行多选,或单独切换某个布尔状态。基于 Radix Checkbox
|
|
13
|
+
'复选框 — 在一组选项中进行多选,或单独切换某个布尔状态。基于 Radix Checkbox(原生可访问 + indeterminate 三态)+ antd `Checkbox.Group` 批量管理;支持受控 / 非受控、`disabled`、中间态。',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
@@ -128,3 +128,161 @@ export const VerticalGroup: Story = {
|
|
|
128
128
|
/>
|
|
129
129
|
),
|
|
130
130
|
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* cd hybridcloud 视觉对齐 — 默认中性灰边 / hover 主色边 + 极浅蓝底 / checked 主色填充 /
|
|
134
|
+
* checked-hover 加深 / disabled opacity-50 + 不响应 hover。规则细节见 [ADR 0024](../../../../docs/adr/0024-scoped-css-radix-state-conflict.md)。
|
|
135
|
+
*/
|
|
136
|
+
export const CdAlignment: Story = {
|
|
137
|
+
name: 'cd hybridcloud 视觉对齐',
|
|
138
|
+
parameters: { controls: { disable: true } },
|
|
139
|
+
render: () => (
|
|
140
|
+
<div className="flex flex-col gap-4 text-sm">
|
|
141
|
+
<div className="flex items-center gap-6">
|
|
142
|
+
<label className="flex items-center gap-2">
|
|
143
|
+
<Checkbox /> default(中性灰边)
|
|
144
|
+
</label>
|
|
145
|
+
<label className="flex items-center gap-2">
|
|
146
|
+
<Checkbox defaultChecked /> checked(主色填充)
|
|
147
|
+
</label>
|
|
148
|
+
<label className="flex items-center gap-2">
|
|
149
|
+
<Checkbox checked="indeterminate" /> indeterminate(主色 + 横线)
|
|
150
|
+
</label>
|
|
151
|
+
</div>
|
|
152
|
+
<div className="flex items-center gap-6">
|
|
153
|
+
<label className="flex items-center gap-2 opacity-100">
|
|
154
|
+
<Checkbox disabled /> disabled(不响应 hover)
|
|
155
|
+
</label>
|
|
156
|
+
<label className="flex items-center gap-2 opacity-100">
|
|
157
|
+
<Checkbox disabled defaultChecked /> disabled checked
|
|
158
|
+
</label>
|
|
159
|
+
<label className="flex items-center gap-2 opacity-100">
|
|
160
|
+
<Checkbox disabled checked="indeterminate" /> disabled indeterm.
|
|
161
|
+
</label>
|
|
162
|
+
</div>
|
|
163
|
+
<p className="text-xs text-muted-foreground">
|
|
164
|
+
Hover 任意可交互项观察 — enabled 态边框转主色 + 极浅蓝底色;
|
|
165
|
+
disabled 态(opacity-50)无 hover 视觉变化。
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 表单集成 — Checkbox 跟 Label 一体的常见模式;主标签 + 辅助说明文本,
|
|
173
|
+
* 用 `flex flex-col gap` 而非 `space-y` 做布局(ADR / preference)。
|
|
174
|
+
*/
|
|
175
|
+
export const WithDescription: Story = {
|
|
176
|
+
parameters: { controls: { disable: true } },
|
|
177
|
+
render: () => (
|
|
178
|
+
<div className="flex flex-col gap-3">
|
|
179
|
+
<label
|
|
180
|
+
htmlFor="cb-marketing"
|
|
181
|
+
className="flex cursor-pointer items-start gap-3"
|
|
182
|
+
>
|
|
183
|
+
<Checkbox id="cb-marketing" defaultChecked className="mt-0.5" />
|
|
184
|
+
<span className="flex flex-col gap-0.5">
|
|
185
|
+
<span className="text-sm font-medium leading-none">订阅产品营销邮件</span>
|
|
186
|
+
<span className="text-xs text-muted-foreground">
|
|
187
|
+
每月不超过两封,介绍重大版本更新与最佳实践,可随时取消订阅。
|
|
188
|
+
</span>
|
|
189
|
+
</span>
|
|
190
|
+
</label>
|
|
191
|
+
<label
|
|
192
|
+
htmlFor="cb-tos"
|
|
193
|
+
className="flex cursor-pointer items-start gap-3"
|
|
194
|
+
>
|
|
195
|
+
<Checkbox id="cb-tos" required className="mt-0.5" />
|
|
196
|
+
<span className="flex flex-col gap-0.5">
|
|
197
|
+
<span className="text-sm font-medium leading-none">
|
|
198
|
+
我已阅读并同意 <a className="underline">用户协议</a> 和 <a className="underline">隐私政策</a>
|
|
199
|
+
<span className="text-destructive"> *</span>
|
|
200
|
+
</span>
|
|
201
|
+
<span className="text-xs text-muted-foreground">
|
|
202
|
+
必填项 — 未勾选时表单提交会校验失败。
|
|
203
|
+
</span>
|
|
204
|
+
</span>
|
|
205
|
+
</label>
|
|
206
|
+
</div>
|
|
207
|
+
),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 长标签换行 — Checkbox 与 label 用 `items-start` 顶对齐,标签自然换行不影响勾选框位置。
|
|
212
|
+
*/
|
|
213
|
+
export const LongLabelWrap: Story = {
|
|
214
|
+
parameters: { controls: { disable: true } },
|
|
215
|
+
render: () => (
|
|
216
|
+
<div className="flex w-72 flex-col gap-3">
|
|
217
|
+
<label className="flex cursor-pointer items-start gap-2 text-sm">
|
|
218
|
+
<Checkbox className="mt-1 shrink-0" />
|
|
219
|
+
<span>
|
|
220
|
+
这是一条非常长的文案,主要用来演示 Checkbox 与多行标签的对齐方式 —
|
|
221
|
+
建议外层用 `items-start` + 给 Checkbox 加 `mt-1` 让它对齐到第一行文字基线。
|
|
222
|
+
</span>
|
|
223
|
+
</label>
|
|
224
|
+
</div>
|
|
225
|
+
),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 全选联动(完整版) — 父框 indeterminate 半选 + 全选切换 + 子项独立切换 +
|
|
230
|
+
* disabled 子项不参与计数。这是 cd hybridcloud Checkbox.Group 全选模式的等价写法。
|
|
231
|
+
*/
|
|
232
|
+
export const SelectAll: Story = {
|
|
233
|
+
parameters: { controls: { disable: true } },
|
|
234
|
+
render: () => {
|
|
235
|
+
const [items, setItems] = React.useState([
|
|
236
|
+
{ id: 'cn', label: '中国', checked: true, disabled: false },
|
|
237
|
+
{ id: 'jp', label: '日本', checked: false, disabled: false },
|
|
238
|
+
{ id: 'us', label: '美国', checked: false, disabled: false },
|
|
239
|
+
{ id: 'kr', label: '韩国(暂不支持)', checked: false, disabled: true },
|
|
240
|
+
]);
|
|
241
|
+
const enabledItems = items.filter((i) => !i.disabled);
|
|
242
|
+
const allChecked = enabledItems.every((i) => i.checked);
|
|
243
|
+
const someChecked = enabledItems.some((i) => i.checked);
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<div className="flex flex-col gap-3 text-sm">
|
|
247
|
+
<label className="flex items-center gap-2 font-medium">
|
|
248
|
+
<Checkbox
|
|
249
|
+
checked={
|
|
250
|
+
allChecked ? true : someChecked ? 'indeterminate' : false
|
|
251
|
+
}
|
|
252
|
+
onCheckedChange={(c) =>
|
|
253
|
+
setItems(
|
|
254
|
+
items.map((it) =>
|
|
255
|
+
it.disabled ? it : { ...it, checked: c === true },
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
/>
|
|
260
|
+
全选 {someChecked && !allChecked
|
|
261
|
+
? `(已选 ${enabledItems.filter((i) => i.checked).length} / ${enabledItems.length})`
|
|
262
|
+
: ''}
|
|
263
|
+
</label>
|
|
264
|
+
<div className="ml-6 flex flex-col gap-2">
|
|
265
|
+
{items.map((it) => (
|
|
266
|
+
<label
|
|
267
|
+
key={it.id}
|
|
268
|
+
className={`flex items-center gap-2 ${it.disabled ? 'opacity-60' : ''}`}
|
|
269
|
+
>
|
|
270
|
+
<Checkbox
|
|
271
|
+
disabled={it.disabled}
|
|
272
|
+
checked={it.checked}
|
|
273
|
+
onCheckedChange={(c) =>
|
|
274
|
+
setItems(
|
|
275
|
+
items.map((x) =>
|
|
276
|
+
x.id === it.id ? { ...x, checked: c === true } : x,
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
/>
|
|
281
|
+
{it.label}
|
|
282
|
+
</label>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
};
|
|
@@ -5,7 +5,50 @@ import { Check, Minus } from 'lucide-react';
|
|
|
5
5
|
import { cn } from '@/utils/cn';
|
|
6
6
|
|
|
7
7
|
export interface CheckboxProps
|
|
8
|
-
extends
|
|
8
|
+
extends Omit<
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,
|
|
10
|
+
| 'checked'
|
|
11
|
+
| 'defaultChecked'
|
|
12
|
+
| 'onCheckedChange'
|
|
13
|
+
| 'disabled'
|
|
14
|
+
| 'required'
|
|
15
|
+
| 'name'
|
|
16
|
+
| 'value'
|
|
17
|
+
| 'id'
|
|
18
|
+
> {
|
|
19
|
+
/**
|
|
20
|
+
* 受控选中状态(等价 antd `checked`)。
|
|
21
|
+
* 取值 `true` / `false` / `'indeterminate'`(半选,只影响视觉,不改变 checked 语义)。
|
|
22
|
+
* 与 `defaultChecked` 二选一。
|
|
23
|
+
*/
|
|
24
|
+
checked?: boolean | 'indeterminate';
|
|
25
|
+
/**
|
|
26
|
+
* 非受控初始选中态(等价 antd `defaultChecked`)。
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
defaultChecked?: boolean | 'indeterminate';
|
|
30
|
+
/**
|
|
31
|
+
* 选中状态变化回调 — Radix 命名,等价 antd `onChange(checked)`。
|
|
32
|
+
* 业务侧迁移自 antd 时把 `onChange={(e) => fn(e.target.checked)}` 改为 `onCheckedChange={fn}`。
|
|
33
|
+
*/
|
|
34
|
+
onCheckedChange?: (checked: boolean | 'indeterminate') => void;
|
|
35
|
+
/**
|
|
36
|
+
* 禁用整个 checkbox(灰化 + 不响应交互)。
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* 必填 — 设置 HTML 原生 `required`,表单层校验依赖此值。
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
required?: boolean;
|
|
45
|
+
/** 表单字段名(原生 `<input name>`,提交表单时使用)。 */
|
|
46
|
+
name?: string;
|
|
47
|
+
/** 表单字段值(勾选时随表单提交)。 */
|
|
48
|
+
value?: string;
|
|
49
|
+
/** id 属性,配合 `<label htmlFor>` 关联标签。 */
|
|
50
|
+
id?: string;
|
|
51
|
+
}
|
|
9
52
|
|
|
10
53
|
const Checkbox = React.forwardRef<
|
|
11
54
|
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
|
@@ -14,19 +57,37 @@ const Checkbox = React.forwardRef<
|
|
|
14
57
|
<CheckboxPrimitive.Root
|
|
15
58
|
ref={ref}
|
|
16
59
|
className={cn(
|
|
17
|
-
|
|
60
|
+
// 视觉对齐 cd hybridcloud(--checkbox-* tokens):
|
|
61
|
+
// default : border #cccccc / bg #FFFFFF → border-input + bg-background
|
|
62
|
+
// hover : border #589ADB / bg #F0F7FF → hover:border-primary + hover:bg-primary/5
|
|
63
|
+
// checked : bg #0064C8 (primary) / tick white → bg-primary + text-primary-foreground
|
|
64
|
+
// ck-hovered : bg #0057AD (deeper) → data-[state=checked]:hover:bg-primary/90
|
|
65
|
+
// indeterm. : 同 checked,只是 indicator 换成 minus
|
|
66
|
+
// border-radius: 2px (corner-1) → rounded-md(uni-manager 2px / opentrek 6px)
|
|
67
|
+
'peer size-4 shrink-0 cursor-pointer rounded-md border border-input bg-background shadow-sm transition-colors',
|
|
68
|
+
// hover 反馈用 `enabled:` 前缀 — disabled 态不触发 hover 样式(border/bg 变化)
|
|
69
|
+
'enabled:hover:border-primary data-[state=unchecked]:enabled:hover:bg-primary/5',
|
|
70
|
+
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
|
71
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
72
|
+
'data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:enabled:hover:bg-primary/90',
|
|
73
|
+
'data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground data-[state=indeterminate]:enabled:hover:bg-primary/90',
|
|
18
74
|
className,
|
|
19
75
|
)}
|
|
20
76
|
{...props}
|
|
21
77
|
>
|
|
22
78
|
<CheckboxPrimitive.Indicator
|
|
23
|
-
className={cn(
|
|
79
|
+
className={cn(
|
|
80
|
+
// 视觉居中:lucide Check / Minus 的笔画在 viewBox 内偏上,
|
|
81
|
+
// 配合 stroke-[3] 加粗与 size-3.5 略大于父框 (size-4) 的视觉补偿,
|
|
82
|
+
// 让对钩与减号在 16x16 框内呈现机械居中 + 视觉居中。
|
|
83
|
+
'pointer-events-none flex size-full items-center justify-center text-current',
|
|
84
|
+
)}
|
|
24
85
|
>
|
|
25
86
|
{/* Radix sets data-state="indeterminate" automatically when checked === "indeterminate" */}
|
|
26
87
|
{props.checked === 'indeterminate' ? (
|
|
27
|
-
<Minus className="size-3" />
|
|
88
|
+
<Minus className="size-3" strokeWidth={3} />
|
|
28
89
|
) : (
|
|
29
|
-
<Check className="size-3" />
|
|
90
|
+
<Check className="size-3.5" strokeWidth={3} />
|
|
30
91
|
)}
|
|
31
92
|
</CheckboxPrimitive.Indicator>
|
|
32
93
|
</CheckboxPrimitive.Root>
|
|
@@ -42,7 +103,10 @@ export interface CheckboxGroupOption {
|
|
|
42
103
|
}
|
|
43
104
|
|
|
44
105
|
export interface CheckboxGroupProps
|
|
45
|
-
extends Omit<
|
|
106
|
+
extends Omit<
|
|
107
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
108
|
+
'onChange' | 'defaultValue'
|
|
109
|
+
> {
|
|
46
110
|
/** 选项数据。 */
|
|
47
111
|
options: CheckboxGroupOption[];
|
|
48
112
|
/** 受控值;`string[]` 表示当前选中的 value 集合。 */
|
|
@@ -78,11 +142,15 @@ const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
78
142
|
ref,
|
|
79
143
|
) => {
|
|
80
144
|
const isControlled = value !== undefined;
|
|
81
|
-
const [internal, setInternal] = React.useState<string[]>(
|
|
145
|
+
const [internal, setInternal] = React.useState<string[]>(
|
|
146
|
+
defaultValue ?? [],
|
|
147
|
+
);
|
|
82
148
|
const current = isControlled ? value! : internal;
|
|
83
149
|
|
|
84
150
|
const toggle = (val: string, checked: boolean) => {
|
|
85
|
-
const next = checked
|
|
151
|
+
const next = checked
|
|
152
|
+
? [...current, val]
|
|
153
|
+
: current.filter((v) => v !== val);
|
|
86
154
|
if (!isControlled) setInternal(next);
|
|
87
155
|
onChange?.(next);
|
|
88
156
|
};
|
|
@@ -102,7 +170,7 @@ const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
|
102
170
|
<label
|
|
103
171
|
key={opt.value}
|
|
104
172
|
className={cn(
|
|
105
|
-
'flex items-center gap-2 text-
|
|
173
|
+
'flex items-center gap-2 text-xs',
|
|
106
174
|
(disabled || opt.disabled) && 'cursor-not-allowed opacity-60',
|
|
107
175
|
)}
|
|
108
176
|
>
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
id: collapsible
|
|
3
3
|
name: Collapsible
|
|
4
4
|
type: component
|
|
5
|
-
category:
|
|
5
|
+
category: data-display
|
|
6
6
|
since: 0.1.0
|
|
7
|
-
package:
|
|
7
|
+
package: '@teamix-evo/ui'
|
|
8
|
+
displayName: 折叠面板
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# Collapsible
|
|
11
|
+
# Collapsible 折叠面板
|
|
11
12
|
|
|
12
13
|
单区域展开收起 — Radix Collapsible 薄包装。**shadcn-only**(antd Collapse 单 item 也可以模拟,但语义粗糙)。
|
|
13
14
|
|
|
@@ -27,7 +28,12 @@ package: "@teamix-evo/ui"
|
|
|
27
28
|
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。`Collapsible` 是 `CollapsibleRoot` 别名,接受 Radix 的 `open / defaultOpen / onOpenChange / disabled`。
|
|
28
29
|
|
|
29
30
|
<!-- auto:props:begin -->
|
|
30
|
-
|
|
31
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `open` | `boolean` | – | – | 受控展开状态(等价 antd Collapse `activeKey` 单项模式)。 与 `defaultOpen` 二选一,不同时传。 |
|
|
34
|
+
| `defaultOpen` | `boolean` | `false` | – | 非受控初始展开态。 |
|
|
35
|
+
| `onOpenChange` | `(open: boolean) => void` | – | – | 展开状态变化回调 — Radix 命名。 |
|
|
36
|
+
| `disabled` | `boolean` | `false` | – | 禁用展开/收起交互(灰化 Trigger + 不响应点击)。 |
|
|
31
37
|
<!-- auto:props:end -->
|
|
32
38
|
|
|
33
39
|
## 依赖
|
|
@@ -60,7 +66,9 @@ pnpm add @radix-ui/react-collapsible@^1.1.0
|
|
|
60
66
|
|
|
61
67
|
```tsx
|
|
62
68
|
import {
|
|
63
|
-
Collapsible,
|
|
69
|
+
Collapsible,
|
|
70
|
+
CollapsibleTrigger,
|
|
71
|
+
CollapsibleContent,
|
|
64
72
|
} from '@/components/ui/collapsible';
|
|
65
73
|
import { Button } from '@/components/ui/button';
|
|
66
74
|
import { ChevronsUpDown } from 'lucide-react';
|
|
@@ -76,5 +84,5 @@ import { ChevronsUpDown } from 'lucide-react';
|
|
|
76
84
|
<p className="text-sm text-muted-foreground">高级配置 1</p>
|
|
77
85
|
<p className="text-sm text-muted-foreground">高级配置 2</p>
|
|
78
86
|
</CollapsibleContent>
|
|
79
|
-
</Collapsible
|
|
87
|
+
</Collapsible>;
|
|
80
88
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { ChevronsUpDown } from 'lucide-react';
|
|
3
3
|
import {
|
|
4
4
|
Collapsible,
|
|
@@ -8,9 +8,17 @@ import {
|
|
|
8
8
|
import { Button } from '@/components/button/button';
|
|
9
9
|
|
|
10
10
|
const meta: Meta<typeof Collapsible> = {
|
|
11
|
-
title: '
|
|
11
|
+
title: '数据展示 · Data Display/Collapsible',
|
|
12
12
|
component: Collapsible,
|
|
13
13
|
tags: ['autodocs'],
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
'折叠面板 — 单区域展开收起,常用于“查看更多 / 收起”、表单高级选项开关等场景。Radix Collapsible 实现,shadcn 专有(antd Collapse 单 item 可模拟但语义粗糙)。',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
export default meta;
|
|
@@ -1,5 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
1
2
|
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
2
3
|
|
|
4
|
+
import { cn } from '@/utils/cn';
|
|
5
|
+
|
|
6
|
+
// ─── CollapsibleProps(ADR 0025 合规:显式声明交互 props)─────────────────
|
|
7
|
+
|
|
8
|
+
export interface CollapsibleProps
|
|
9
|
+
extends Omit<
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.Root>,
|
|
11
|
+
'open' | 'defaultOpen' | 'onOpenChange' | 'disabled'
|
|
12
|
+
> {
|
|
13
|
+
/**
|
|
14
|
+
* 受控展开状态(等价 antd Collapse `activeKey` 单项模式)。
|
|
15
|
+
* 与 `defaultOpen` 二选一,不同时传。
|
|
16
|
+
*/
|
|
17
|
+
open?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* 非受控初始展开态。
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
defaultOpen?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 展开状态变化回调 — Radix 命名。
|
|
25
|
+
*/
|
|
26
|
+
onOpenChange?: (open: boolean) => void;
|
|
27
|
+
/**
|
|
28
|
+
* 禁用展开/收起交互(灰化 Trigger + 不响应点击)。
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
/**
|
|
4
35
|
* 单区域展开 / 收起 — Radix Collapsible 的薄包装。
|
|
5
36
|
*
|
|
@@ -8,11 +39,67 @@ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
|
|
8
39
|
*
|
|
9
40
|
* shadcn-only(antd Collapse 单 item 也可以模拟,但语义粗糙)。
|
|
10
41
|
*/
|
|
11
|
-
const Collapsible =
|
|
12
|
-
|
|
13
|
-
|
|
42
|
+
const Collapsible = React.forwardRef<
|
|
43
|
+
React.ElementRef<typeof CollapsiblePrimitive.Root>,
|
|
44
|
+
CollapsibleProps
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<CollapsiblePrimitive.Root ref={ref} className={cn(className)} {...props} />
|
|
47
|
+
));
|
|
48
|
+
Collapsible.displayName = 'Collapsible';
|
|
49
|
+
|
|
50
|
+
// ─── CollapsibleTrigger ─────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
export interface CollapsibleTriggerProps
|
|
53
|
+
extends Omit<
|
|
54
|
+
React.ComponentPropsWithoutRef<
|
|
55
|
+
typeof CollapsiblePrimitive.CollapsibleTrigger
|
|
56
|
+
>,
|
|
57
|
+
'asChild'
|
|
58
|
+
> {
|
|
59
|
+
/**
|
|
60
|
+
* Slot 模式 — 子元素作为 trigger(不额外包裹 DOM)。
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
asChild?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const CollapsibleTrigger = React.forwardRef<
|
|
67
|
+
React.ElementRef<typeof CollapsiblePrimitive.CollapsibleTrigger>,
|
|
68
|
+
CollapsibleTriggerProps
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<CollapsiblePrimitive.CollapsibleTrigger
|
|
71
|
+
ref={ref}
|
|
72
|
+
className={cn('cursor-pointer', className)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
));
|
|
76
|
+
CollapsibleTrigger.displayName =
|
|
77
|
+
CollapsiblePrimitive.CollapsibleTrigger.displayName;
|
|
78
|
+
|
|
79
|
+
// ─── CollapsibleContent ─────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export interface CollapsibleContentProps
|
|
82
|
+
extends React.ComponentPropsWithoutRef<
|
|
83
|
+
typeof CollapsiblePrimitive.CollapsibleContent
|
|
84
|
+
> {
|
|
85
|
+
/**
|
|
86
|
+
* 强制挂载(即使收起也保留 DOM,利于 SEO 或保持内部组件状态)。
|
|
87
|
+
* @default false
|
|
88
|
+
*/
|
|
89
|
+
forceMount?: true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const CollapsibleContent = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof CollapsiblePrimitive.CollapsibleContent>,
|
|
94
|
+
CollapsibleContentProps
|
|
95
|
+
>(({ className, ...props }, ref) => (
|
|
96
|
+
<CollapsiblePrimitive.CollapsibleContent
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn(className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
));
|
|
102
|
+
CollapsibleContent.displayName =
|
|
103
|
+
CollapsiblePrimitive.CollapsibleContent.displayName;
|
|
14
104
|
|
|
15
105
|
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
|
16
|
-
export type CollapsibleProps = React.ComponentPropsWithoutRef<
|
|
17
|
-
typeof CollapsiblePrimitive.Root
|
|
18
|
-
>;
|