@teamix-evo/ui 0.1.1

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 (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +336 -0
  3. package/_data.json +12 -0
  4. package/manifest.json +1688 -0
  5. package/package.json +90 -0
  6. package/src/components/accordion/accordion.meta.md +87 -0
  7. package/src/components/accordion/accordion.stories.tsx +67 -0
  8. package/src/components/accordion/accordion.tsx +58 -0
  9. package/src/components/affix/affix.meta.md +80 -0
  10. package/src/components/affix/affix.stories.tsx +57 -0
  11. package/src/components/affix/affix.tsx +97 -0
  12. package/src/components/alert/alert.meta.md +101 -0
  13. package/src/components/alert/alert.stories.tsx +93 -0
  14. package/src/components/alert/alert.tsx +132 -0
  15. package/src/components/alert-dialog/alert-dialog.meta.md +107 -0
  16. package/src/components/alert-dialog/alert-dialog.stories.tsx +81 -0
  17. package/src/components/alert-dialog/alert-dialog.tsx +136 -0
  18. package/src/components/anchor/anchor.meta.md +87 -0
  19. package/src/components/anchor/anchor.stories.tsx +74 -0
  20. package/src/components/anchor/anchor.tsx +130 -0
  21. package/src/components/app/app.meta.md +86 -0
  22. package/src/components/app/app.stories.tsx +62 -0
  23. package/src/components/app/app.tsx +58 -0
  24. package/src/components/aspect-ratio/aspect-ratio.meta.md +81 -0
  25. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +59 -0
  26. package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
  27. package/src/components/auto-complete/auto-complete.meta.md +102 -0
  28. package/src/components/auto-complete/auto-complete.stories.tsx +93 -0
  29. package/src/components/auto-complete/auto-complete.tsx +205 -0
  30. package/src/components/avatar/avatar.meta.md +94 -0
  31. package/src/components/avatar/avatar.stories.tsx +80 -0
  32. package/src/components/avatar/avatar.tsx +126 -0
  33. package/src/components/badge/badge.meta.md +119 -0
  34. package/src/components/badge/badge.stories.tsx +153 -0
  35. package/src/components/badge/badge.tsx +210 -0
  36. package/src/components/breadcrumb/breadcrumb.meta.md +107 -0
  37. package/src/components/breadcrumb/breadcrumb.stories.tsx +84 -0
  38. package/src/components/breadcrumb/breadcrumb.tsx +122 -0
  39. package/src/components/button/button.meta.md +98 -0
  40. package/src/components/button/button.stories.tsx +235 -0
  41. package/src/components/button/button.tsx +160 -0
  42. package/src/components/button-group/button-group.meta.md +92 -0
  43. package/src/components/button-group/button-group.stories.tsx +90 -0
  44. package/src/components/button-group/button-group.tsx +75 -0
  45. package/src/components/calendar/calendar.meta.md +118 -0
  46. package/src/components/calendar/calendar.stories.tsx +68 -0
  47. package/src/components/calendar/calendar.tsx +107 -0
  48. package/src/components/card/card.meta.md +117 -0
  49. package/src/components/card/card.stories.tsx +112 -0
  50. package/src/components/card/card.tsx +222 -0
  51. package/src/components/carousel/carousel.meta.md +117 -0
  52. package/src/components/carousel/carousel.stories.tsx +84 -0
  53. package/src/components/carousel/carousel.tsx +224 -0
  54. package/src/components/cascader/cascader.meta.md +110 -0
  55. package/src/components/cascader/cascader.stories.tsx +108 -0
  56. package/src/components/cascader/cascader.tsx +198 -0
  57. package/src/components/checkbox/checkbox.meta.md +99 -0
  58. package/src/components/checkbox/checkbox.stories.tsx +130 -0
  59. package/src/components/checkbox/checkbox.tsx +125 -0
  60. package/src/components/collapsible/collapsible.meta.md +80 -0
  61. package/src/components/collapsible/collapsible.stories.tsx +35 -0
  62. package/src/components/collapsible/collapsible.tsx +18 -0
  63. package/src/components/color-picker/color-picker.meta.md +84 -0
  64. package/src/components/color-picker/color-picker.stories.tsx +80 -0
  65. package/src/components/color-picker/color-picker.tsx +160 -0
  66. package/src/components/combobox/combobox.meta.md +93 -0
  67. package/src/components/combobox/combobox.stories.tsx +55 -0
  68. package/src/components/combobox/combobox.tsx +130 -0
  69. package/src/components/command/command.meta.md +104 -0
  70. package/src/components/command/command.stories.tsx +59 -0
  71. package/src/components/command/command.tsx +147 -0
  72. package/src/components/context-menu/context-menu.meta.md +90 -0
  73. package/src/components/context-menu/context-menu.stories.tsx +46 -0
  74. package/src/components/context-menu/context-menu.tsx +191 -0
  75. package/src/components/data-table/data-table.meta.md +149 -0
  76. package/src/components/data-table/data-table.stories.tsx +125 -0
  77. package/src/components/data-table/data-table.tsx +185 -0
  78. package/src/components/date-picker/date-picker.meta.md +106 -0
  79. package/src/components/date-picker/date-picker.stories.tsx +58 -0
  80. package/src/components/date-picker/date-picker.tsx +156 -0
  81. package/src/components/descriptions/descriptions.meta.md +78 -0
  82. package/src/components/descriptions/descriptions.stories.tsx +60 -0
  83. package/src/components/descriptions/descriptions.tsx +129 -0
  84. package/src/components/dialog/dialog.meta.md +105 -0
  85. package/src/components/dialog/dialog.stories.tsx +93 -0
  86. package/src/components/dialog/dialog.tsx +128 -0
  87. package/src/components/drawer/drawer.meta.md +96 -0
  88. package/src/components/drawer/drawer.stories.tsx +54 -0
  89. package/src/components/drawer/drawer.tsx +114 -0
  90. package/src/components/dropdown-menu/dropdown-menu.meta.md +103 -0
  91. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +112 -0
  92. package/src/components/dropdown-menu/dropdown-menu.tsx +195 -0
  93. package/src/components/empty/empty.meta.md +81 -0
  94. package/src/components/empty/empty.stories.tsx +46 -0
  95. package/src/components/empty/empty.tsx +47 -0
  96. package/src/components/field/field.meta.md +116 -0
  97. package/src/components/field/field.stories.tsx +117 -0
  98. package/src/components/field/field.tsx +164 -0
  99. package/src/components/flex/flex.meta.md +94 -0
  100. package/src/components/flex/flex.stories.tsx +112 -0
  101. package/src/components/flex/flex.tsx +122 -0
  102. package/src/components/float-button/float-button.meta.md +87 -0
  103. package/src/components/float-button/float-button.stories.tsx +78 -0
  104. package/src/components/float-button/float-button.tsx +143 -0
  105. package/src/components/form/form.meta.md +131 -0
  106. package/src/components/form/form.stories.tsx +122 -0
  107. package/src/components/form/form.tsx +194 -0
  108. package/src/components/grid/grid.meta.md +87 -0
  109. package/src/components/grid/grid.stories.tsx +99 -0
  110. package/src/components/grid/grid.tsx +130 -0
  111. package/src/components/hover-card/hover-card.meta.md +92 -0
  112. package/src/components/hover-card/hover-card.stories.tsx +68 -0
  113. package/src/components/hover-card/hover-card.tsx +29 -0
  114. package/src/components/image/image.meta.md +94 -0
  115. package/src/components/image/image.stories.tsx +55 -0
  116. package/src/components/image/image.tsx +138 -0
  117. package/src/components/input/input.meta.md +109 -0
  118. package/src/components/input/input.stories.tsx +117 -0
  119. package/src/components/input/input.tsx +213 -0
  120. package/src/components/input-group/input-group.meta.md +92 -0
  121. package/src/components/input-group/input-group.stories.tsx +88 -0
  122. package/src/components/input-group/input-group.tsx +107 -0
  123. package/src/components/input-number/input-number.meta.md +91 -0
  124. package/src/components/input-number/input-number.stories.tsx +87 -0
  125. package/src/components/input-number/input-number.tsx +210 -0
  126. package/src/components/input-otp/input-otp.meta.md +105 -0
  127. package/src/components/input-otp/input-otp.stories.tsx +65 -0
  128. package/src/components/input-otp/input-otp.tsx +97 -0
  129. package/src/components/item/item.meta.md +116 -0
  130. package/src/components/item/item.stories.tsx +113 -0
  131. package/src/components/item/item.tsx +171 -0
  132. package/src/components/kbd/kbd.meta.md +85 -0
  133. package/src/components/kbd/kbd.stories.tsx +70 -0
  134. package/src/components/kbd/kbd.tsx +81 -0
  135. package/src/components/label/label.meta.md +91 -0
  136. package/src/components/label/label.stories.tsx +87 -0
  137. package/src/components/label/label.tsx +66 -0
  138. package/src/components/masonry/masonry.meta.md +85 -0
  139. package/src/components/masonry/masonry.stories.tsx +66 -0
  140. package/src/components/masonry/masonry.tsx +59 -0
  141. package/src/components/mentions/mentions.meta.md +89 -0
  142. package/src/components/mentions/mentions.stories.tsx +75 -0
  143. package/src/components/mentions/mentions.tsx +237 -0
  144. package/src/components/menubar/menubar.meta.md +100 -0
  145. package/src/components/menubar/menubar.stories.tsx +81 -0
  146. package/src/components/menubar/menubar.tsx +232 -0
  147. package/src/components/native-select/native-select.meta.md +88 -0
  148. package/src/components/native-select/native-select.stories.tsx +80 -0
  149. package/src/components/native-select/native-select.tsx +54 -0
  150. package/src/components/navigation-menu/navigation-menu.meta.md +108 -0
  151. package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -0
  152. package/src/components/navigation-menu/navigation-menu.tsx +125 -0
  153. package/src/components/notification/notification.meta.md +91 -0
  154. package/src/components/notification/notification.stories.tsx +96 -0
  155. package/src/components/notification/notification.tsx +84 -0
  156. package/src/components/pagination/pagination.meta.md +127 -0
  157. package/src/components/pagination/pagination.stories.tsx +62 -0
  158. package/src/components/pagination/pagination.tsx +285 -0
  159. package/src/components/popconfirm/popconfirm.meta.md +109 -0
  160. package/src/components/popconfirm/popconfirm.stories.tsx +76 -0
  161. package/src/components/popconfirm/popconfirm.tsx +134 -0
  162. package/src/components/popover/popover.meta.md +97 -0
  163. package/src/components/popover/popover.stories.tsx +82 -0
  164. package/src/components/popover/popover.tsx +55 -0
  165. package/src/components/progress/progress.meta.md +86 -0
  166. package/src/components/progress/progress.stories.tsx +75 -0
  167. package/src/components/progress/progress.tsx +195 -0
  168. package/src/components/radio-group/radio-group.meta.md +103 -0
  169. package/src/components/radio-group/radio-group.stories.tsx +77 -0
  170. package/src/components/radio-group/radio-group.tsx +78 -0
  171. package/src/components/rate/rate.meta.md +87 -0
  172. package/src/components/rate/rate.stories.tsx +81 -0
  173. package/src/components/rate/rate.tsx +153 -0
  174. package/src/components/resizable/resizable.meta.md +92 -0
  175. package/src/components/resizable/resizable.stories.tsx +104 -0
  176. package/src/components/resizable/resizable.tsx +56 -0
  177. package/src/components/result/result.meta.md +90 -0
  178. package/src/components/result/result.stories.tsx +71 -0
  179. package/src/components/result/result.tsx +91 -0
  180. package/src/components/scroll-area/scroll-area.meta.md +84 -0
  181. package/src/components/scroll-area/scroll-area.stories.tsx +41 -0
  182. package/src/components/scroll-area/scroll-area.tsx +51 -0
  183. package/src/components/segmented/segmented.meta.md +103 -0
  184. package/src/components/segmented/segmented.stories.tsx +101 -0
  185. package/src/components/segmented/segmented.tsx +138 -0
  186. package/src/components/select/select.meta.md +110 -0
  187. package/src/components/select/select.stories.tsx +100 -0
  188. package/src/components/select/select.tsx +188 -0
  189. package/src/components/separator/separator.meta.md +74 -0
  190. package/src/components/separator/separator.stories.tsx +71 -0
  191. package/src/components/separator/separator.tsx +104 -0
  192. package/src/components/sheet/sheet.meta.md +97 -0
  193. package/src/components/sheet/sheet.stories.tsx +82 -0
  194. package/src/components/sheet/sheet.tsx +139 -0
  195. package/src/components/sidebar/sidebar.meta.md +131 -0
  196. package/src/components/sidebar/sidebar.stories.tsx +82 -0
  197. package/src/components/sidebar/sidebar.tsx +351 -0
  198. package/src/components/skeleton/skeleton.meta.md +95 -0
  199. package/src/components/skeleton/skeleton.stories.tsx +79 -0
  200. package/src/components/skeleton/skeleton.tsx +144 -0
  201. package/src/components/slider/slider.meta.md +94 -0
  202. package/src/components/slider/slider.stories.tsx +69 -0
  203. package/src/components/slider/slider.tsx +86 -0
  204. package/src/components/sonner/sonner.meta.md +96 -0
  205. package/src/components/sonner/sonner.stories.tsx +91 -0
  206. package/src/components/sonner/sonner.tsx +40 -0
  207. package/src/components/space/space.meta.md +94 -0
  208. package/src/components/space/space.stories.tsx +94 -0
  209. package/src/components/space/space.tsx +106 -0
  210. package/src/components/spinner/spinner.meta.md +76 -0
  211. package/src/components/spinner/spinner.stories.tsx +71 -0
  212. package/src/components/spinner/spinner.tsx +64 -0
  213. package/src/components/statistic/statistic.meta.md +99 -0
  214. package/src/components/statistic/statistic.stories.tsx +71 -0
  215. package/src/components/statistic/statistic.tsx +197 -0
  216. package/src/components/steps/steps.meta.md +102 -0
  217. package/src/components/steps/steps.stories.tsx +75 -0
  218. package/src/components/steps/steps.tsx +170 -0
  219. package/src/components/switch/switch.meta.md +92 -0
  220. package/src/components/switch/switch.stories.tsx +75 -0
  221. package/src/components/switch/switch.tsx +101 -0
  222. package/src/components/table/table.meta.md +95 -0
  223. package/src/components/table/table.stories.tsx +75 -0
  224. package/src/components/table/table.tsx +122 -0
  225. package/src/components/tabs/tabs.meta.md +98 -0
  226. package/src/components/tabs/tabs.stories.tsx +70 -0
  227. package/src/components/tabs/tabs.tsx +119 -0
  228. package/src/components/tag/tag.meta.md +94 -0
  229. package/src/components/tag/tag.stories.tsx +77 -0
  230. package/src/components/tag/tag.tsx +185 -0
  231. package/src/components/textarea/textarea.meta.md +83 -0
  232. package/src/components/textarea/textarea.stories.tsx +63 -0
  233. package/src/components/textarea/textarea.tsx +113 -0
  234. package/src/components/time-picker/time-picker.meta.md +83 -0
  235. package/src/components/time-picker/time-picker.stories.tsx +59 -0
  236. package/src/components/time-picker/time-picker.tsx +94 -0
  237. package/src/components/timeline/timeline.meta.md +102 -0
  238. package/src/components/timeline/timeline.stories.tsx +104 -0
  239. package/src/components/timeline/timeline.tsx +147 -0
  240. package/src/components/toggle/toggle.meta.md +88 -0
  241. package/src/components/toggle/toggle.stories.tsx +66 -0
  242. package/src/components/toggle/toggle.tsx +53 -0
  243. package/src/components/toggle-group/toggle-group.meta.md +90 -0
  244. package/src/components/toggle-group/toggle-group.stories.tsx +83 -0
  245. package/src/components/toggle-group/toggle-group.tsx +78 -0
  246. package/src/components/tooltip/tooltip.meta.md +99 -0
  247. package/src/components/tooltip/tooltip.stories.tsx +71 -0
  248. package/src/components/tooltip/tooltip.tsx +93 -0
  249. package/src/components/tour/tour.meta.md +116 -0
  250. package/src/components/tour/tour.stories.tsx +66 -0
  251. package/src/components/tour/tour.tsx +242 -0
  252. package/src/components/transfer/transfer.meta.md +90 -0
  253. package/src/components/transfer/transfer.stories.tsx +68 -0
  254. package/src/components/transfer/transfer.tsx +251 -0
  255. package/src/components/tree/tree.meta.md +111 -0
  256. package/src/components/tree/tree.stories.tsx +109 -0
  257. package/src/components/tree/tree.tsx +367 -0
  258. package/src/components/tree-select/tree-select.meta.md +100 -0
  259. package/src/components/tree-select/tree-select.stories.tsx +80 -0
  260. package/src/components/tree-select/tree-select.tsx +171 -0
  261. package/src/components/typography/typography.meta.md +102 -0
  262. package/src/components/typography/typography.stories.tsx +115 -0
  263. package/src/components/typography/typography.tsx +245 -0
  264. package/src/components/upload/upload.meta.md +111 -0
  265. package/src/components/upload/upload.stories.tsx +75 -0
  266. package/src/components/upload/upload.tsx +265 -0
  267. package/src/components/watermark/watermark.meta.md +95 -0
  268. package/src/components/watermark/watermark.stories.tsx +78 -0
  269. package/src/components/watermark/watermark.tsx +165 -0
  270. package/src/utils/cn.ts +6 -0
@@ -0,0 +1,99 @@
1
+ ---
2
+ id: checkbox
3
+ name: Checkbox
4
+ type: component
5
+ category: form
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Checkbox
11
+
12
+ 复选框 — Radix Checkbox(原生支持 indeterminate)+ antd `Checkbox.Group`(`options` 数组驱动)。
13
+
14
+ ## When to use
15
+
16
+ - 多选场景(待提交,如表单)
17
+ - 单独的开关(同意条款)
18
+ - 列表多选 + 全选(配 indeterminate)
19
+
20
+ ## When NOT to use
21
+
22
+ - 立即生效 → 用 `Switch`
23
+ - 互斥单选 → 用 `RadioGroup`
24
+ - 工具栏图标按钮 → 用 `Toggle`
25
+
26
+ ## Props
27
+
28
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
29
+
30
+ <!-- auto:props:begin -->
31
+ _(no props)_
32
+ <!-- auto:props:end -->
33
+
34
+ ## 依赖
35
+
36
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
37
+
38
+ <!-- auto:deps:begin -->
39
+ ### 同库依赖
40
+
41
+ > `teamix-evo ui add checkbox` 时,以下 entry 会被自动连带安装(无需手动 add)。
42
+
43
+ | Entry | 类型 | 描述 |
44
+ | --- | --- | --- |
45
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
46
+
47
+ ### npm 依赖
48
+
49
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
50
+
51
+ ```bash
52
+ pnpm add @radix-ui/react-checkbox@^1.1.0 lucide-react@^0.460.0
53
+ ```
54
+ <!-- auto:deps:end -->
55
+
56
+ > 透传 Radix Checkbox.Root 所有 props(`checked` / `defaultChecked` / `onCheckedChange` / `disabled` / `required` / `name` / `value` 等)。
57
+ > 子组件 `CheckboxGroup` 的 props 详见 [`checkbox.tsx`](./checkbox.tsx) 的 `CheckboxGroupProps`。
58
+
59
+ ## AI 生成纪律
60
+
61
+ - **`checked="indeterminate"` 不是布尔**:Radix 用字面量 `'indeterminate'` 表示半选,**不要**传 number 或其他值
62
+ - **全选 + 半选模式**:列表全选场景 — 全选用 `checked={all}`,部分选用 `checked="indeterminate"`,父级 onCheckedChange 触发批量选/全清
63
+ - **必带 Label**:Checkbox 单独无文字,必须 `<label>...<Checkbox /></label>` 或 `<Checkbox id /> + <Label htmlFor>`
64
+ - **`CheckboxGroup` options 必须有稳定的 `value`**:不要用 index 或动态值;否则 controlled diff 失效
65
+ - **disabled 优先级**:整组 `disabled={true}` 会覆盖单项的 `disabled={false}`
66
+
67
+ ## Examples
68
+
69
+ ```tsx
70
+ import { Checkbox, CheckboxGroup } from '@/components/ui/checkbox';
71
+
72
+ // 单个
73
+ <label className="flex items-center gap-2">
74
+ <Checkbox /> 接受用户协议
75
+ </label>
76
+
77
+ // 半选(全选模式的父框)
78
+ const all = items.length > 0 && items.every(i => i.checked);
79
+ const some = items.some(i => i.checked);
80
+ <Checkbox
81
+ checked={all ? true : some ? 'indeterminate' : false}
82
+ onCheckedChange={(c) => setAll(c === true)}
83
+ />
84
+
85
+ // Group
86
+ const [v, setV] = React.useState(['apple']);
87
+ <CheckboxGroup
88
+ value={v}
89
+ onChange={setV}
90
+ options={[
91
+ { label: '苹果', value: 'apple' },
92
+ { label: '香蕉', value: 'banana' },
93
+ { label: '橘子', value: 'orange', disabled: true },
94
+ ]}
95
+ />
96
+
97
+ // Group 纵向
98
+ <CheckboxGroup orientation="vertical" options={...} defaultValue={[]} />
99
+ ```
@@ -0,0 +1,130 @@
1
+ import * as React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { Checkbox, CheckboxGroup } from './checkbox';
4
+
5
+ const meta: Meta<typeof Checkbox> = {
6
+ title: '表单与输入 · Form/Checkbox',
7
+ component: Checkbox,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ '复选框 — 在一组选项中进行多选,或单独切换某个布尔状态。基于 Radix Checkbox(原生可访问 + indeterminate 三态) + antd `Checkbox.Group` 批量管理;支持受控 / 非受控、`disabled`、中间态。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ disabled: { control: 'boolean' },
19
+ required: { control: 'boolean' },
20
+ },
21
+ };
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof Checkbox>;
25
+
26
+ export const Playground: Story = {
27
+ args: { defaultChecked: false },
28
+ render: (args) => (
29
+ <label className="flex items-center gap-2 text-sm">
30
+ <Checkbox {...args} />
31
+ 接受用户协议
32
+ </label>
33
+ ),
34
+ };
35
+
36
+ export const States: Story = {
37
+ parameters: { controls: { disable: true } },
38
+ render: () => (
39
+ <div className="flex flex-col gap-2 text-sm">
40
+ <label className="flex items-center gap-2">
41
+ <Checkbox /> 默认
42
+ </label>
43
+ <label className="flex items-center gap-2">
44
+ <Checkbox defaultChecked /> 已选
45
+ </label>
46
+ <label className="flex items-center gap-2 opacity-60">
47
+ <Checkbox disabled /> 禁用
48
+ </label>
49
+ <label className="flex items-center gap-2 opacity-60">
50
+ <Checkbox disabled defaultChecked /> 禁用已选
51
+ </label>
52
+ </div>
53
+ ),
54
+ };
55
+
56
+ export const Indeterminate: Story = {
57
+ parameters: { controls: { disable: true } },
58
+ render: () => {
59
+ const [items, setItems] = React.useState([
60
+ { id: 1, label: '苹果', checked: true },
61
+ { id: 2, label: '香蕉', checked: false },
62
+ { id: 3, label: '橘子', checked: true },
63
+ ]);
64
+ const all = items.every((i) => i.checked);
65
+ const some = items.some((i) => i.checked);
66
+ return (
67
+ <div className="flex flex-col gap-2 text-sm">
68
+ <label className="flex items-center gap-2 font-medium">
69
+ <Checkbox
70
+ checked={all ? true : some ? 'indeterminate' : false}
71
+ onCheckedChange={(c) =>
72
+ setItems(items.map((it) => ({ ...it, checked: c === true })))
73
+ }
74
+ />
75
+ 全选
76
+ </label>
77
+ <div className="ml-6 flex flex-col gap-1">
78
+ {items.map((it) => (
79
+ <label key={it.id} className="flex items-center gap-2">
80
+ <Checkbox
81
+ checked={it.checked}
82
+ onCheckedChange={(c) =>
83
+ setItems(
84
+ items.map((x) =>
85
+ x.id === it.id ? { ...x, checked: c === true } : x,
86
+ ),
87
+ )
88
+ }
89
+ />
90
+ {it.label}
91
+ </label>
92
+ ))}
93
+ </div>
94
+ </div>
95
+ );
96
+ },
97
+ };
98
+
99
+ export const Group: Story = {
100
+ parameters: { controls: { disable: true } },
101
+ render: () => {
102
+ const [v, setV] = React.useState<string[]>(['apple']);
103
+ return (
104
+ <CheckboxGroup
105
+ value={v}
106
+ onChange={setV}
107
+ options={[
108
+ { label: '苹果', value: 'apple' },
109
+ { label: '香蕉', value: 'banana' },
110
+ { label: '橘子', value: 'orange', disabled: true },
111
+ ]}
112
+ />
113
+ );
114
+ },
115
+ };
116
+
117
+ export const VerticalGroup: Story = {
118
+ parameters: { controls: { disable: true } },
119
+ render: () => (
120
+ <CheckboxGroup
121
+ orientation="vertical"
122
+ defaultValue={['email']}
123
+ options={[
124
+ { label: '邮件', value: 'email' },
125
+ { label: '短信', value: 'sms' },
126
+ { label: '推送', value: 'push' },
127
+ ]}
128
+ />
129
+ ),
130
+ };
@@ -0,0 +1,125 @@
1
+ import * as React from 'react';
2
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
3
+ import { Check, Minus } from 'lucide-react';
4
+
5
+ import { cn } from '@/utils/cn';
6
+
7
+ export interface CheckboxProps
8
+ extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {}
9
+
10
+ const Checkbox = React.forwardRef<
11
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
12
+ CheckboxProps
13
+ >(({ className, ...props }, ref) => (
14
+ <CheckboxPrimitive.Root
15
+ ref={ref}
16
+ className={cn(
17
+ 'peer size-4 shrink-0 rounded-sm border border-primary shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground',
18
+ className,
19
+ )}
20
+ {...props}
21
+ >
22
+ <CheckboxPrimitive.Indicator
23
+ className={cn('flex items-center justify-center text-current')}
24
+ >
25
+ {/* Radix sets data-state="indeterminate" automatically when checked === "indeterminate" */}
26
+ {props.checked === 'indeterminate' ? (
27
+ <Minus className="size-3" />
28
+ ) : (
29
+ <Check className="size-3" />
30
+ )}
31
+ </CheckboxPrimitive.Indicator>
32
+ </CheckboxPrimitive.Root>
33
+ ));
34
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
35
+
36
+ // ─── CheckboxGroup(antd 并集)─────────────────────────────────────────────
37
+
38
+ export interface CheckboxGroupOption {
39
+ label: React.ReactNode;
40
+ value: string;
41
+ disabled?: boolean;
42
+ }
43
+
44
+ export interface CheckboxGroupProps
45
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> {
46
+ /** 选项数据。 */
47
+ options: CheckboxGroupOption[];
48
+ /** 受控值;`string[]` 表示当前选中的 value 集合。 */
49
+ value?: string[];
50
+ /** uncontrolled 初值。 */
51
+ defaultValue?: string[];
52
+ /** 整组禁用。 */
53
+ disabled?: boolean;
54
+ /** 整组的 name(submitted form 用)。 */
55
+ name?: string;
56
+ /**
57
+ * 排列方向。
58
+ * @default "horizontal"
59
+ */
60
+ orientation?: 'horizontal' | 'vertical';
61
+ /** 选中变化回调。 */
62
+ onChange?: (next: string[]) => void;
63
+ }
64
+
65
+ const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(
66
+ (
67
+ {
68
+ className,
69
+ options,
70
+ value,
71
+ defaultValue,
72
+ disabled,
73
+ name,
74
+ orientation = 'horizontal',
75
+ onChange,
76
+ ...props
77
+ },
78
+ ref,
79
+ ) => {
80
+ const isControlled = value !== undefined;
81
+ const [internal, setInternal] = React.useState<string[]>(defaultValue ?? []);
82
+ const current = isControlled ? value! : internal;
83
+
84
+ const toggle = (val: string, checked: boolean) => {
85
+ const next = checked ? [...current, val] : current.filter((v) => v !== val);
86
+ if (!isControlled) setInternal(next);
87
+ onChange?.(next);
88
+ };
89
+
90
+ return (
91
+ <div
92
+ ref={ref}
93
+ role="group"
94
+ className={cn(
95
+ 'flex gap-4',
96
+ orientation === 'vertical' && 'flex-col gap-2',
97
+ className,
98
+ )}
99
+ {...props}
100
+ >
101
+ {options.map((opt) => (
102
+ <label
103
+ key={opt.value}
104
+ className={cn(
105
+ 'flex items-center gap-2 text-sm',
106
+ (disabled || opt.disabled) && 'cursor-not-allowed opacity-60',
107
+ )}
108
+ >
109
+ <Checkbox
110
+ name={name}
111
+ value={opt.value}
112
+ checked={current.includes(opt.value)}
113
+ disabled={disabled || opt.disabled}
114
+ onCheckedChange={(c) => toggle(opt.value, c === true)}
115
+ />
116
+ <span>{opt.label}</span>
117
+ </label>
118
+ ))}
119
+ </div>
120
+ );
121
+ },
122
+ );
123
+ CheckboxGroup.displayName = 'CheckboxGroup';
124
+
125
+ export { Checkbox, CheckboxGroup };
@@ -0,0 +1,80 @@
1
+ ---
2
+ id: collapsible
3
+ name: Collapsible
4
+ type: component
5
+ category: navigation
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Collapsible
11
+
12
+ 单区域展开收起 — Radix Collapsible 薄包装。**shadcn-only**(antd Collapse 单 item 也可以模拟,但语义粗糙)。
13
+
14
+ ## When to use
15
+
16
+ - 详情页"查看更多 / 收起"
17
+ - 行内"展开评论"按钮 + 折叠区
18
+ - 表单的"高级选项"开关折叠
19
+
20
+ ## When NOT to use
21
+
22
+ - 多个折叠项 → `Accordion`
23
+ - 平级 tab 切换 → `Tabs`
24
+
25
+ ## Props
26
+
27
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。`Collapsible` 是 `CollapsibleRoot` 别名,接受 Radix 的 `open / defaultOpen / onOpenChange / disabled`。
28
+
29
+ <!-- auto:props:begin -->
30
+ _(组件无 `<Name>Props` interface — props 详见 [`collapsible.tsx`](./collapsible.tsx))_
31
+ <!-- auto:props:end -->
32
+
33
+ ## 依赖
34
+
35
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
36
+
37
+ <!-- auto:deps:begin -->
38
+ ### 同库依赖
39
+
40
+ _无 — 本组件不依赖其他 ui entry。_
41
+
42
+ ### npm 依赖
43
+
44
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
45
+
46
+ ```bash
47
+ pnpm add @radix-ui/react-collapsible@^1.1.0
48
+ ```
49
+ <!-- auto:deps:end -->
50
+
51
+ > 子组件:`Collapsible` / `CollapsibleTrigger`(触发器)/ `CollapsibleContent`(可折叠内容)。
52
+
53
+ ## AI 生成纪律
54
+
55
+ - **Trigger 用 `asChild`**:把已有按钮 wrap,而不是新增层级
56
+ - **`defaultOpen` 给初始视觉**:首次访问就该展开的内容显式声明 `defaultOpen`
57
+ - **不嵌套 Collapsible**:嵌套折叠是反模式;改用 `Accordion type="multiple"`
58
+
59
+ ## Examples
60
+
61
+ ```tsx
62
+ import {
63
+ Collapsible, CollapsibleTrigger, CollapsibleContent,
64
+ } from '@/components/ui/collapsible';
65
+ import { Button } from '@/components/ui/button';
66
+ import { ChevronsUpDown } from 'lucide-react';
67
+
68
+ <Collapsible className="w-72">
69
+ <CollapsibleTrigger asChild>
70
+ <Button variant="ghost" size="sm" className="w-full justify-between">
71
+ 高级选项
72
+ <ChevronsUpDown />
73
+ </Button>
74
+ </CollapsibleTrigger>
75
+ <CollapsibleContent className="space-y-2 pt-2">
76
+ <p className="text-sm text-muted-foreground">高级配置 1</p>
77
+ <p className="text-sm text-muted-foreground">高级配置 2</p>
78
+ </CollapsibleContent>
79
+ </Collapsible>
80
+ ```
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ChevronsUpDown } from 'lucide-react';
3
+ import {
4
+ Collapsible,
5
+ CollapsibleTrigger,
6
+ CollapsibleContent,
7
+ } from './collapsible';
8
+ import { Button } from '@/components/button/button';
9
+
10
+ const meta: Meta<typeof Collapsible> = {
11
+ title: '导航 · Navigation/Collapsible',
12
+ component: Collapsible,
13
+ tags: ['autodocs'],
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof Collapsible>;
18
+
19
+ export const Default: Story = {
20
+ render: () => (
21
+ <Collapsible className="w-72">
22
+ <CollapsibleTrigger asChild>
23
+ <Button variant="ghost" size="sm" className="w-full justify-between">
24
+ 高级选项
25
+ <ChevronsUpDown />
26
+ </Button>
27
+ </CollapsibleTrigger>
28
+ <CollapsibleContent className="space-y-2 pt-2 px-2">
29
+ <p className="text-sm text-muted-foreground">- 自动重启策略</p>
30
+ <p className="text-sm text-muted-foreground">- 容器健康检查</p>
31
+ <p className="text-sm text-muted-foreground">- 资源限制</p>
32
+ </CollapsibleContent>
33
+ </Collapsible>
34
+ ),
35
+ };
@@ -0,0 +1,18 @@
1
+ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
2
+
3
+ /**
4
+ * 单区域展开 / 收起 — Radix Collapsible 的薄包装。
5
+ *
6
+ * 与 `Accordion` 的差异:Accordion 是多 item 列表(支持互斥单展开 / 多展开);
7
+ * Collapsible 是**单个**区域,语义最简单 —— "更多内容" / "查看详情"。
8
+ *
9
+ * shadcn-only(antd Collapse 单 item 也可以模拟,但语义粗糙)。
10
+ */
11
+ const Collapsible = CollapsiblePrimitive.Root;
12
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
13
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
14
+
15
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
16
+ export type CollapsibleProps = React.ComponentPropsWithoutRef<
17
+ typeof CollapsiblePrimitive.Root
18
+ >;
@@ -0,0 +1,84 @@
1
+ ---
2
+ id: color-picker
3
+ name: ColorPicker
4
+ type: component
5
+ category: form
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # ColorPicker
11
+
12
+ 颜色选择 — antd 独有补足。**等价 antd `ColorPicker`**(v5.5+)。基于原生 `<input type="color">` + alpha 滑块 + hex 文本输入 + 预设色块,提供"触发器(色块)→ 弹出面板"的标准交互。可选透明度(8 位 hex)。
13
+
14
+ ## When to use
15
+
16
+ - 主题色 / 品牌色配置
17
+ - 文档 / 笔记的文字 / 高亮色选择
18
+ - 设计工具的填充色 / 描边色
19
+ - 标签 / 分类的色彩自定义
20
+
21
+ ## When NOT to use
22
+
23
+ - 仅几个预设色 → `RadioGroup` 配色块更简洁
24
+ - 渐变 / 多色阶 → 当前不支持,需配合第三方 picker
25
+ - HSL / RGB 各通道独立调节 → 当前仅 hex + alpha,复杂场景建议引入专业 picker
26
+
27
+ <!-- auto:props:begin -->
28
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
29
+ | --- | --- | --- | --- | --- |
30
+ | `value` | `string` | – | – | 受控值 — `#RRGGBB` 或 `#RRGGBBAA`(开 `allowAlpha` 时)。 |
31
+ | `defaultValue` | `string` | `"#000000"` | – | uncontrolled 初值。 |
32
+ | `onChange` | `(value: string) => void` | – | – | 值变化回调。 |
33
+ | `allowAlpha` | `boolean` | `false` | – | 是否允许透明度(antd `format=hex8` 类似行为) — 启用后展示 alpha 滑块,value 变为 8 位 hex。 |
34
+ | `presets` | `string[]` | – | – | 预设色块(antd `presets` 并集) — 一组常用颜色快捷选择。 |
35
+ | `disabled` | `boolean` | – | – | 整体禁用。 |
36
+ | `size` | `'sm' \| 'default' \| 'lg'` | `"default"` | – | 触发器尺寸。 |
37
+ | `className` | `string` | – | – | 触发器 className。 |
38
+ <!-- auto:props:end -->
39
+
40
+ <!-- auto:deps:begin -->
41
+ ### 同库依赖
42
+
43
+ > `teamix-evo ui add color-picker` 时,以下 entry 会被自动连带安装(无需手动 add)。
44
+
45
+ | Entry | 类型 | 描述 |
46
+ | --- | --- | --- |
47
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
48
+ | `input` | component | 文本输入 — shadcn 简洁基底 + antd prefix/suffix/clearable/showCount/addonBefore/addonAfter/size |
49
+ | `popover` | component | 可交互浮层 — Radix Popover + antd arrow 并集 |
50
+
51
+ ### npm 依赖
52
+
53
+ _无 — 本组件不依赖任何 npm 包。_
54
+ <!-- auto:deps:end -->
55
+
56
+ ## AI 生成纪律
57
+
58
+ - **value 必须是 `#RRGGBB`(6位)或 `#RRGGBBAA`(8位 + 透明度)** — 不接受 `rgb()` / `hsl()` / 命名色
59
+ - **`allowAlpha=true`** 时 value 自动变为 8 位 hex;关闭时即使有 alpha 也会被截断到 6 位
60
+ - **`presets`** 应是品牌色板而非随手凑数 — 6-12 个 stable 颜色为佳,避免视觉混乱
61
+ - **`disabled`** 是只读语义 — 配 value 展示当前选中色;**不要**用做"暂时锁定"等业务态(用 form-level 控制)
62
+ - **触发器是色块本身**,内部 padding 留出 2px 边框 — 不要在外面再裹一层 border
63
+ - **不要在 Popover 关闭时清空 value** — onChange 已是单一真值,无需 commit/cancel 区分
64
+
65
+ ## Examples
66
+
67
+ ```tsx
68
+ import { ColorPicker } from '@/components/ui/color-picker';
69
+ import * as React from 'react';
70
+
71
+ // 基础
72
+ <ColorPicker defaultValue="#f43f5e" />
73
+
74
+ // 受控 + 预设色
75
+ const [c, setC] = React.useState('#3b82f6');
76
+ <ColorPicker
77
+ value={c}
78
+ onChange={setC}
79
+ presets={['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6', '#ec4899']}
80
+ />
81
+
82
+ // 带透明度
83
+ <ColorPicker allowAlpha defaultValue="#3b82f680" />
84
+ ```
@@ -0,0 +1,80 @@
1
+ import * as React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ColorPicker } from './color-picker';
4
+
5
+ const meta: Meta<typeof ColorPicker> = {
6
+ title: '表单与输入 · Form/ColorPicker',
7
+ component: ColorPicker,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ '颜色选择 — 触发器色块 → Popover 面板(原生 color input + alpha + hex 输入 + 预设色板)。可选 8 位 hex(含透明度)。等价 antd `ColorPicker`(v5.5+)。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ size: { control: 'inline-radio', options: ['sm', 'default', 'lg'] },
19
+ allowAlpha: { control: 'boolean' },
20
+ disabled: { control: 'boolean' },
21
+ },
22
+ args: { size: 'default', allowAlpha: false, disabled: false },
23
+ };
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof ColorPicker>;
27
+
28
+ export const Playground: Story = {
29
+ render: (args) => <ColorPicker {...args} defaultValue="#f43f5e" />,
30
+ };
31
+
32
+ export const Controlled: Story = {
33
+ parameters: { controls: { disable: true } },
34
+ render: () => {
35
+ const [c, setC] = React.useState('#3b82f6');
36
+ return (
37
+ <div className="flex items-center gap-3">
38
+ <ColorPicker value={c} onChange={setC} />
39
+ <span className="text-sm tabular-nums">{c}</span>
40
+ </div>
41
+ );
42
+ },
43
+ };
44
+
45
+ export const WithPresets: Story = {
46
+ parameters: { controls: { disable: true } },
47
+ render: () => (
48
+ <ColorPicker
49
+ defaultValue="#3b82f6"
50
+ presets={[
51
+ '#ef4444',
52
+ '#f59e0b',
53
+ '#eab308',
54
+ '#10b981',
55
+ '#06b6d4',
56
+ '#3b82f6',
57
+ '#8b5cf6',
58
+ '#ec4899',
59
+ '#000000',
60
+ '#ffffff',
61
+ ]}
62
+ />
63
+ ),
64
+ };
65
+
66
+ export const WithAlpha: Story = {
67
+ parameters: { controls: { disable: true } },
68
+ render: () => <ColorPicker allowAlpha defaultValue="#3b82f680" />,
69
+ };
70
+
71
+ export const Sizes: Story = {
72
+ parameters: { controls: { disable: true } },
73
+ render: () => (
74
+ <div className="flex items-center gap-3">
75
+ <ColorPicker size="sm" defaultValue="#ef4444" />
76
+ <ColorPicker size="default" defaultValue="#10b981" />
77
+ <ColorPicker size="lg" defaultValue="#3b82f6" />
78
+ </div>
79
+ ),
80
+ };