@teamix-evo/ui 0.2.0 → 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.
Files changed (282) hide show
  1. package/README.md +184 -184
  2. package/manifest.json +680 -492
  3. package/package.json +15 -9
  4. package/src/components/accordion/accordion.meta.md +5 -9
  5. package/src/components/accordion/accordion.stories.tsx +3 -3
  6. package/src/components/accordion/accordion.tsx +104 -8
  7. package/src/components/affix/affix.meta.md +21 -12
  8. package/src/components/affix/affix.stories.tsx +101 -26
  9. package/src/components/affix/affix.tsx +79 -9
  10. package/src/components/alert/alert.meta.md +52 -26
  11. package/src/components/alert/alert.stories.tsx +66 -21
  12. package/src/components/alert/alert.tsx +81 -34
  13. package/src/components/alert-dialog/alert-dialog.meta.md +48 -16
  14. package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
  15. package/src/components/alert-dialog/alert-dialog.tsx +60 -13
  16. package/src/components/anchor/anchor.meta.md +10 -14
  17. package/src/components/anchor/anchor.stories.tsx +3 -3
  18. package/src/components/anchor/anchor.tsx +2 -2
  19. package/src/components/app/app.meta.md +10 -14
  20. package/src/components/app/app.stories.tsx +6 -6
  21. package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -8
  22. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
  23. package/src/components/auto-complete/auto-complete.meta.md +19 -20
  24. package/src/components/auto-complete/auto-complete.stories.tsx +44 -3
  25. package/src/components/auto-complete/auto-complete.tsx +119 -71
  26. package/src/components/avatar/avatar.meta.md +9 -22
  27. package/src/components/avatar/avatar.stories.tsx +21 -3
  28. package/src/components/avatar/avatar.tsx +24 -23
  29. package/src/components/badge/badge.meta.md +14 -18
  30. package/src/components/badge/badge.stories.tsx +2 -2
  31. package/src/components/badge/badge.tsx +2 -2
  32. package/src/components/breadcrumb/breadcrumb.meta.md +29 -20
  33. package/src/components/breadcrumb/breadcrumb.stories.tsx +120 -5
  34. package/src/components/breadcrumb/breadcrumb.tsx +22 -8
  35. package/src/components/button/button.meta.md +261 -29
  36. package/src/components/button/button.stories.tsx +549 -41
  37. package/src/components/button/button.tsx +335 -33
  38. package/src/components/calendar/calendar.meta.md +19 -14
  39. package/src/components/calendar/calendar.stories.tsx +5 -5
  40. package/src/components/calendar/calendar.tsx +73 -8
  41. package/src/components/card/card.meta.md +31 -34
  42. package/src/components/card/card.stories.tsx +34 -3
  43. package/src/components/card/card.tsx +146 -63
  44. package/src/components/carousel/carousel.meta.md +10 -14
  45. package/src/components/carousel/carousel.stories.tsx +1 -1
  46. package/src/components/cascader/cascader.meta.md +43 -22
  47. package/src/components/cascader/cascader.stories.tsx +13 -2
  48. package/src/components/cascader/cascader.tsx +427 -84
  49. package/src/components/checkbox/checkbox.meta.md +74 -24
  50. package/src/components/checkbox/checkbox.stories.tsx +160 -2
  51. package/src/components/checkbox/checkbox.tsx +77 -9
  52. package/src/components/collapsible/collapsible.meta.md +7 -6
  53. package/src/components/collapsible/collapsible.stories.tsx +2 -2
  54. package/src/components/collapsible/collapsible.tsx +93 -6
  55. package/src/components/color-picker/color-picker.meta.md +16 -20
  56. package/src/components/color-picker/color-picker.stories.tsx +86 -7
  57. package/src/components/color-picker/color-picker.tsx +19 -9
  58. package/src/components/command/command.meta.md +7 -11
  59. package/src/components/command/command.stories.tsx +4 -4
  60. package/src/components/command/command.tsx +18 -7
  61. package/src/components/context-menu/context-menu.meta.md +5 -25
  62. package/src/components/context-menu/context-menu.stories.tsx +4 -4
  63. package/src/components/context-menu/context-menu.tsx +21 -8
  64. package/src/components/data-table/data-table.meta.md +14 -18
  65. package/src/components/data-table/data-table.stories.tsx +1 -1
  66. package/src/components/data-table/data-table.tsx +2 -2
  67. package/src/components/date-picker/date-picker.meta.md +90 -41
  68. package/src/components/date-picker/date-picker.stories.tsx +55 -5
  69. package/src/components/date-picker/date-picker.tsx +1489 -91
  70. package/src/components/descriptions/descriptions.meta.md +12 -16
  71. package/src/components/descriptions/descriptions.stories.tsx +2 -2
  72. package/src/components/descriptions/descriptions.tsx +22 -14
  73. package/src/components/dialog/dialog.meta.md +67 -17
  74. package/src/components/dialog/dialog.stories.tsx +182 -20
  75. package/src/components/dialog/dialog.tsx +67 -15
  76. package/src/components/dialog/imperative.tsx +252 -0
  77. package/src/components/drawer/drawer.meta.md +27 -39
  78. package/src/components/drawer/drawer.stories.tsx +29 -12
  79. package/src/components/drawer/drawer.tsx +22 -114
  80. package/src/components/dropdown-menu/dropdown-menu.meta.md +64 -24
  81. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +81 -3
  82. package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
  83. package/src/components/ellipsis/ellipsis.meta.md +87 -0
  84. package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
  85. package/src/components/ellipsis/ellipsis.tsx +153 -0
  86. package/src/components/empty/empty.meta.md +10 -14
  87. package/src/components/empty/empty.stories.tsx +3 -3
  88. package/src/components/empty/empty.tsx +10 -3
  89. package/src/components/field/field.meta.md +46 -25
  90. package/src/components/field/field.stories.tsx +380 -3
  91. package/src/components/field/field.tsx +263 -35
  92. package/src/components/filter-bar/filter-bar.meta.md +92 -0
  93. package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
  94. package/src/components/filter-bar/filter-bar.tsx +568 -0
  95. package/src/components/flex/flex.meta.md +59 -20
  96. package/src/components/flex/flex.stories.tsx +65 -10
  97. package/src/components/flex/flex.tsx +27 -4
  98. package/src/components/float-button/float-button.meta.md +10 -29
  99. package/src/components/float-button/float-button.stories.tsx +6 -6
  100. package/src/components/form/form.meta.md +31 -52
  101. package/src/components/form/form.stories.tsx +350 -3
  102. package/src/components/form/form.tsx +101 -35
  103. package/src/components/grid/grid.meta.md +4 -24
  104. package/src/components/grid/grid.stories.tsx +2 -2
  105. package/src/components/hover-card/hover-card.meta.md +9 -10
  106. package/src/components/hover-card/hover-card.stories.tsx +29 -4
  107. package/src/components/hover-card/hover-card.tsx +51 -13
  108. package/src/components/icon/DEVELOPMENT.md +809 -0
  109. package/src/components/icon/icon.meta.md +170 -0
  110. package/src/components/icon/icon.stories.tsx +344 -0
  111. package/src/components/icon/icon.tsx +248 -0
  112. package/src/components/image/image.meta.md +14 -18
  113. package/src/components/image/image.stories.tsx +3 -3
  114. package/src/components/image/image.tsx +2 -0
  115. package/src/components/input/demo/sizes.tsx +2 -2
  116. package/src/components/input/input.meta.md +44 -43
  117. package/src/components/input/input.stories.tsx +62 -35
  118. package/src/components/input/input.tsx +96 -101
  119. package/src/components/input-group/input-group.meta.md +53 -39
  120. package/src/components/input-group/input-group.stories.tsx +49 -16
  121. package/src/components/input-group/input-group.tsx +43 -8
  122. package/src/components/input-number/input-number.meta.md +68 -20
  123. package/src/components/input-number/input-number.stories.tsx +33 -6
  124. package/src/components/input-number/input-number.tsx +79 -20
  125. package/src/components/input-otp/input-otp.meta.md +8 -20
  126. package/src/components/input-otp/input-otp.stories.tsx +3 -3
  127. package/src/components/input-otp/input-otp.tsx +1 -1
  128. package/src/components/item/item.meta.md +8 -26
  129. package/src/components/item/item.stories.tsx +3 -3
  130. package/src/components/item/item.tsx +7 -6
  131. package/src/components/kbd/kbd.meta.md +7 -19
  132. package/src/components/kbd/kbd.stories.tsx +4 -4
  133. package/src/components/kbd/kbd.tsx +8 -4
  134. package/src/components/label/label.meta.md +21 -18
  135. package/src/components/label/label.stories.tsx +64 -6
  136. package/src/components/label/label.tsx +91 -19
  137. package/src/components/masonry/masonry.meta.md +8 -12
  138. package/src/components/masonry/masonry.stories.tsx +4 -4
  139. package/src/components/mentions/mentions.meta.md +42 -21
  140. package/src/components/mentions/mentions.stories.tsx +120 -6
  141. package/src/components/mentions/mentions.tsx +10 -5
  142. package/src/components/menubar/menubar.meta.md +4 -8
  143. package/src/components/menubar/menubar.stories.tsx +55 -3
  144. package/src/components/menubar/menubar.tsx +9 -9
  145. package/src/components/native-select/native-select.meta.md +7 -11
  146. package/src/components/native-select/native-select.stories.tsx +4 -4
  147. package/src/components/native-select/native-select.tsx +1 -1
  148. package/src/components/navigation-menu/navigation-menu.meta.md +4 -8
  149. package/src/components/navigation-menu/navigation-menu.stories.tsx +106 -3
  150. package/src/components/navigation-menu/navigation-menu.tsx +6 -3
  151. package/src/components/notification/notification.meta.md +41 -8
  152. package/src/components/notification/notification.stories.tsx +9 -9
  153. package/src/components/notification/notification.tsx +34 -19
  154. package/src/components/page-header/DEVELOPMENT.md +842 -0
  155. package/src/components/page-header/page-header.meta.md +208 -0
  156. package/src/components/page-header/page-header.stories.tsx +421 -0
  157. package/src/components/page-header/page-header.tsx +281 -0
  158. package/src/components/pagination/pagination.meta.md +122 -50
  159. package/src/components/pagination/pagination.stories.tsx +227 -11
  160. package/src/components/pagination/pagination.tsx +355 -63
  161. package/src/components/popconfirm/popconfirm.meta.md +19 -23
  162. package/src/components/popconfirm/popconfirm.stories.tsx +2 -2
  163. package/src/components/popconfirm/popconfirm.tsx +1 -1
  164. package/src/components/popover/popover.meta.md +64 -12
  165. package/src/components/popover/popover.stories.tsx +83 -7
  166. package/src/components/popover/popover.tsx +77 -28
  167. package/src/components/progress/progress.meta.md +43 -26
  168. package/src/components/progress/progress.stories.tsx +2 -2
  169. package/src/components/progress/progress.tsx +19 -11
  170. package/src/components/radio-group/radio-group.meta.md +78 -11
  171. package/src/components/radio-group/radio-group.stories.tsx +38 -2
  172. package/src/components/radio-group/radio-group.tsx +149 -18
  173. package/src/components/rate/rate.meta.md +41 -19
  174. package/src/components/rate/rate.stories.tsx +2 -2
  175. package/src/components/rate/rate.tsx +37 -10
  176. package/src/components/resizable/resizable.meta.md +4 -12
  177. package/src/components/resizable/resizable.stories.tsx +5 -5
  178. package/src/components/resizable/resizable.tsx +1 -1
  179. package/src/components/result/result.meta.md +10 -14
  180. package/src/components/result/result.stories.tsx +2 -2
  181. package/src/components/result/result.tsx +21 -12
  182. package/src/components/scroll-area/scroll-area.meta.md +4 -8
  183. package/src/components/scroll-area/scroll-area.stories.tsx +5 -5
  184. package/src/components/segmented/segmented.meta.md +15 -17
  185. package/src/components/segmented/segmented.stories.tsx +3 -3
  186. package/src/components/segmented/segmented.tsx +15 -7
  187. package/src/components/select/select.meta.md +199 -67
  188. package/src/components/select/select.stories.tsx +238 -63
  189. package/src/components/select/select.tsx +718 -171
  190. package/src/components/separator/separator.meta.md +10 -14
  191. package/src/components/separator/separator.stories.tsx +2 -2
  192. package/src/components/separator/separator.tsx +3 -7
  193. package/src/components/sheet/sheet.meta.md +26 -21
  194. package/src/components/sheet/sheet.stories.tsx +116 -10
  195. package/src/components/sheet/sheet.tsx +116 -29
  196. package/src/components/sidebar/sidebar.meta.md +28 -38
  197. package/src/components/sidebar/sidebar.stories.tsx +696 -29
  198. package/src/components/sidebar/sidebar.tsx +615 -142
  199. package/src/components/skeleton/skeleton.meta.md +7 -31
  200. package/src/components/skeleton/skeleton.stories.tsx +3 -3
  201. package/src/components/skeleton/skeleton.tsx +7 -7
  202. package/src/components/slider/slider.meta.md +60 -13
  203. package/src/components/slider/slider.stories.tsx +58 -6
  204. package/src/components/slider/slider.tsx +154 -13
  205. package/src/components/sonner/sonner.meta.md +54 -8
  206. package/src/components/sonner/sonner.stories.tsx +79 -11
  207. package/src/components/sonner/sonner.tsx +137 -8
  208. package/src/components/spinner/spinner.meta.md +57 -21
  209. package/src/components/spinner/spinner.stories.tsx +66 -14
  210. package/src/components/spinner/spinner.tsx +111 -9
  211. package/src/components/statistic/statistic.meta.md +14 -30
  212. package/src/components/statistic/statistic.stories.tsx +1 -1
  213. package/src/components/statistic/statistic.tsx +4 -5
  214. package/src/components/steps/steps.meta.md +20 -15
  215. package/src/components/steps/steps.stories.tsx +37 -2
  216. package/src/components/steps/steps.tsx +15 -12
  217. package/src/components/switch/switch.meta.md +56 -15
  218. package/src/components/switch/switch.stories.tsx +5 -5
  219. package/src/components/switch/switch.tsx +59 -13
  220. package/src/components/table/table.meta.md +3 -7
  221. package/src/components/table/table.stories.tsx +1 -1
  222. package/src/components/table/table.tsx +4 -4
  223. package/src/components/tabs/tabs.meta.md +40 -32
  224. package/src/components/tabs/tabs.stories.tsx +104 -26
  225. package/src/components/tabs/tabs.tsx +125 -54
  226. package/src/components/tag/tag.meta.md +104 -68
  227. package/src/components/tag/tag.stories.tsx +183 -15
  228. package/src/components/tag/tag.tsx +222 -21
  229. package/src/components/textarea/textarea.meta.md +42 -31
  230. package/src/components/textarea/textarea.stories.tsx +32 -6
  231. package/src/components/textarea/textarea.tsx +32 -8
  232. package/src/components/time-picker/time-picker.meta.md +119 -50
  233. package/src/components/time-picker/time-picker.stories.tsx +65 -33
  234. package/src/components/time-picker/time-picker.tsx +889 -101
  235. package/src/components/timeline/timeline.meta.md +16 -17
  236. package/src/components/timeline/timeline.stories.tsx +24 -4
  237. package/src/components/timeline/timeline.tsx +32 -12
  238. package/src/components/toggle/toggle.meta.md +8 -12
  239. package/src/components/toggle/toggle.stories.tsx +4 -4
  240. package/src/components/toggle/toggle.tsx +4 -3
  241. package/src/components/toggle-group/toggle-group.meta.md +10 -14
  242. package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
  243. package/src/components/toggle-group/toggle-group.tsx +2 -2
  244. package/src/components/tooltip/tooltip.meta.md +63 -18
  245. package/src/components/tooltip/tooltip.stories.tsx +42 -5
  246. package/src/components/tooltip/tooltip.tsx +81 -21
  247. package/src/components/tour/tour.meta.md +16 -20
  248. package/src/components/tour/tour.stories.tsx +3 -3
  249. package/src/components/tour/tour.tsx +3 -3
  250. package/src/components/transfer/transfer.meta.md +18 -22
  251. package/src/components/transfer/transfer.stories.tsx +2 -2
  252. package/src/components/transfer/transfer.tsx +28 -21
  253. package/src/components/tree/tree.meta.md +67 -22
  254. package/src/components/tree/tree.stories.tsx +1 -1
  255. package/src/components/tree/tree.tsx +9 -8
  256. package/src/components/tree-select/tree-select.meta.md +59 -23
  257. package/src/components/tree-select/tree-select.stories.tsx +2 -2
  258. package/src/components/tree-select/tree-select.tsx +42 -7
  259. package/src/components/typography/typography.meta.md +61 -39
  260. package/src/components/typography/typography.stories.tsx +14 -9
  261. package/src/components/typography/typography.tsx +38 -25
  262. package/src/components/upload/upload.meta.md +61 -25
  263. package/src/components/upload/upload.stories.tsx +69 -3
  264. package/src/components/upload/upload.tsx +170 -37
  265. package/src/components/watermark/watermark.meta.md +15 -19
  266. package/src/components/watermark/watermark.stories.tsx +98 -8
  267. package/src/hooks/use-breakpoint.ts +117 -0
  268. package/src/hooks/use-debounce-callback.ts +52 -0
  269. package/src/hooks/use-mobile.ts +23 -0
  270. package/src/stories/theme-tokens.stories.tsx +747 -0
  271. package/src/utils/trigger-input.ts +53 -0
  272. package/src/components/button-group/button-group.meta.md +0 -101
  273. package/src/components/button-group/button-group.stories.tsx +0 -93
  274. package/src/components/button-group/button-group.tsx +0 -75
  275. package/src/components/combobox/combobox.meta.md +0 -102
  276. package/src/components/combobox/combobox.stories.tsx +0 -55
  277. package/src/components/combobox/combobox.tsx +0 -130
  278. package/src/components/input/demo/addon.tsx +0 -15
  279. package/src/components/input/demo/with-prefix-suffix.tsx +0 -19
  280. package/src/components/space/space.meta.md +0 -103
  281. package/src/components/space/space.stories.tsx +0 -108
  282. package/src/components/space/space.tsx +0 -106
@@ -3,7 +3,7 @@ id: color-picker
3
3
  name: ColorPicker
4
4
  displayName: 颜色选择
5
5
  type: component
6
- category: form
6
+ category: data-entry
7
7
  since: 0.1.0
8
8
  package: '@teamix-evo/ui'
9
9
  ---
@@ -28,38 +28,34 @@ package: '@teamix-evo/ui'
28
28
  ## Props
29
29
 
30
30
  <!-- auto:props:begin -->
31
-
32
- | 名称 | 类型 | 默认值 | 必填 | 说明 |
33
- | -------------- | --------------------------- | ----------- | ---- | ----------------------------------------------------------------------------------------- |
34
- | `value` | `string` | | – | 受控值 `#RRGGBB` 或 `#RRGGBBAA`(开 `allowAlpha` 时)。 |
35
- | `defaultValue` | `string` | `"#000000"` | – | uncontrolled 初值。 |
36
- | `onChange` | `(value: string) => void` | – | | 值变化回调。 |
37
- | `allowAlpha` | `boolean` | `false` | – | 是否允许透明度(antd `format=hex8` 类似行为) — 启用后展示 alpha 滑块,value 变为 8 位 hex。 |
38
- | `presets` | `string[]` | – | – | 预设色块(antd `presets` 并集) — 一组常用颜色快捷选择。 |
39
- | `disabled` | `boolean` | | – | 整体禁用。 |
40
- | `size` | `'sm' \| 'default' \| 'lg'` | `"default"` | – | 触发器尺寸。 |
41
- | `className` | `string` | – | – | 触发器 className。 |
42
-
31
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
32
+ | --- | --- | --- | --- | --- |
33
+ | `value` | `string` | | | 受控值 — `#RRGGBB` 或 `#RRGGBBAA`(开 `allowAlpha` 时)。 |
34
+ | `defaultValue` | `string` | `"#000000"` | – | uncontrolled 初值。 |
35
+ | `onChange` | `(value: string) => void` | | – | 值变化回调。 |
36
+ | `allowAlpha` | `boolean` | `false` | – | 是否允许透明度(antd `format=hex8` 类似行为) — 启用后展示 alpha 滑块,value 变为 8 位 hex。 |
37
+ | `presets` | `string[]` | | – | 预设色块(antd `presets` 并集) — 一组常用颜色快捷选择。 |
38
+ | `disabled` | `boolean` | – | – | 整体禁用。 |
39
+ | `size` | `'sm' \| 'md' \| 'default' \| 'lg'` | `"default"` | – | 触发器尺寸。 |
40
+ | `className` | `string` | | – | 触发器 className。 |
43
41
  <!-- auto:props:end -->
44
42
 
45
43
  ## 依赖
46
44
 
47
45
  <!-- auto:deps:begin -->
48
-
49
46
  ### 同库依赖
50
47
 
51
48
  > `teamix-evo ui add color-picker` 时,以下 entry 会被自动连带安装(无需手动 add)。
52
49
 
53
- | Entry | 类型 | 描述 |
54
- | --------- | --------- | ----------------------------------------------------------------------------------------------- |
55
- | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
56
- | `input` | component | 文本输入shadcn 简洁基底 + antd prefix/suffix/clearable/showCount/addonBefore/addonAfter/size |
57
- | `popover` | component | 可交互浮层 — Radix Popover + antd arrow 并集 |
50
+ | Entry | 类型 | 描述 |
51
+ | --- | --- | --- |
52
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
53
+ | `input` | component | 单行文本输入clearable / showCount / size(sm 24 / md 32 / lg 36,与 Button 同档)/ error 四档内建能力。前缀/后缀/addon 等复合形态已拆出独立 InputGroup;多行已拆出独立 Textarea |
54
+ | `popover` | component | 可交互浮层 — Radix Popover + antd arrow 并集,使用 showArrow 控制尖角(与 Tooltip / HoverCard 命名统一) |
58
55
 
59
56
  ### npm 依赖
60
57
 
61
58
  _无 — 本组件不依赖任何 npm 包。_
62
-
63
59
  <!-- auto:deps:end -->
64
60
 
65
61
  ## AI 生成纪律
@@ -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 { ColorPicker } from './color-picker';
4
4
 
5
5
  const meta: Meta<typeof ColorPicker> = {
6
- title: '表单与输入 · Form/ColorPicker',
6
+ title: '数据录入 · Data Entry/ColorPicker',
7
7
  component: ColorPicker,
8
8
  tags: ['autodocs'],
9
9
  parameters: {
10
10
  docs: {
11
11
  description: {
12
12
  component:
13
- '颜色选择 触发器色块 → Popover 面板(原生 color input + alpha + hex 输入 + 预设色板)。可选 8 位 hex(含透明度)。等价 antd `ColorPicker`(v5.5+)。',
13
+ '颜色选择 —— 触发器色块 → Popover 面板,选取一个 `#RRGGBB` 或 `#RRGGBBAA` 颜色。基于原生 `<input type="color">` + Radix `Popover` + `Input` 组合,等价 antd `ColorPicker`(v5.5+):`value` / `defaultValue` / `onChange` 受控双轨、`allowAlpha` 透明度档(8 位 hex)、`presets` 预设色板、`size`(`sm`/`default`/`lg`)三档触发器、`disabled`。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/tokens`,无 mock。',
14
14
  },
15
15
  },
16
16
  },
@@ -26,11 +26,27 @@ export default meta;
26
26
  type Story = StoryObj<typeof ColorPicker>;
27
27
 
28
28
  export const Playground: Story = {
29
+ parameters: {
30
+ docs: {
31
+ description: {
32
+ story:
33
+ '基础颜色选择 —— uncontrolled,内部维护当前色,点击触发器弹出原生 color input + hex 文本输入双通道。',
34
+ },
35
+ },
36
+ },
29
37
  render: (args) => <ColorPicker {...args} defaultValue="#f43f5e" />,
30
38
  };
31
39
 
32
40
  export const Controlled: Story = {
33
- parameters: { controls: { disable: true } },
41
+ parameters: {
42
+ controls: { disable: true },
43
+ docs: {
44
+ description: {
45
+ story:
46
+ '受控形态 —— `value` + `onChange` 由消费侧持有真值,常用于"主题色配置 + 实时预览"场景。',
47
+ },
48
+ },
49
+ },
34
50
  render: () => {
35
51
  const [c, setC] = React.useState('#3b82f6');
36
52
  return (
@@ -43,7 +59,15 @@ export const Controlled: Story = {
43
59
  };
44
60
 
45
61
  export const WithPresets: Story = {
46
- parameters: { controls: { disable: true } },
62
+ parameters: {
63
+ controls: { disable: true },
64
+ docs: {
65
+ description: {
66
+ story:
67
+ '预设色板 —— `presets` 数组渲染为面板底部色块,点击即写入 value。建议给 6~12 个品牌色,避免视觉混乱。',
68
+ },
69
+ },
70
+ },
47
71
  render: () => (
48
72
  <ColorPicker
49
73
  defaultValue="#3b82f6"
@@ -64,12 +88,49 @@ export const WithPresets: Story = {
64
88
  };
65
89
 
66
90
  export const WithAlpha: Story = {
67
- parameters: { controls: { disable: true } },
91
+ parameters: {
92
+ controls: { disable: true },
93
+ docs: {
94
+ description: {
95
+ story:
96
+ '透明度档 —— `allowAlpha` 启用后 value 自动变 8 位 hex,面板内多出 `Alpha` 滑块与百分比读数。',
97
+ },
98
+ },
99
+ },
68
100
  render: () => <ColorPicker allowAlpha defaultValue="#3b82f680" />,
69
101
  };
70
102
 
103
+ export const HexInput: Story = {
104
+ parameters: {
105
+ controls: { disable: true },
106
+ docs: {
107
+ description: {
108
+ story:
109
+ '自定义 hex 输入 —— 面板内嵌的 `Input` 接受手动 hex(`#RRGGBB` 或 `#RRGGBBAA`),不合法字符自动剥除,长度按 `allowAlpha` 截断。',
110
+ },
111
+ },
112
+ },
113
+ render: () => {
114
+ const [c, setC] = React.useState('#10b981');
115
+ return (
116
+ <div className="flex items-center gap-3">
117
+ <ColorPicker value={c} onChange={setC} allowAlpha />
118
+ <code className="rounded bg-muted px-2 py-1 text-xs">{c}</code>
119
+ </div>
120
+ );
121
+ },
122
+ };
123
+
71
124
  export const Sizes: Story = {
72
- parameters: { controls: { disable: true } },
125
+ parameters: {
126
+ controls: { disable: true },
127
+ docs: {
128
+ description: {
129
+ story:
130
+ '三档触发器尺寸 —— `sm` / `default` / `lg`,对齐其它表单控件高度档位。',
131
+ },
132
+ },
133
+ },
73
134
  render: () => (
74
135
  <div className="flex items-center gap-3">
75
136
  <ColorPicker size="sm" defaultValue="#ef4444" />
@@ -78,3 +139,21 @@ export const Sizes: Story = {
78
139
  </div>
79
140
  ),
80
141
  };
142
+
143
+ export const Disabled: Story = {
144
+ parameters: {
145
+ controls: { disable: true },
146
+ docs: {
147
+ description: {
148
+ story:
149
+ '禁用态 —— 整组 `disabled`,点击不弹面板,触发器降透明度。仅用于只读语义,业务态锁定建议在 form 层处理。',
150
+ },
151
+ },
152
+ },
153
+ render: () => (
154
+ <div className="flex items-center gap-3">
155
+ <ColorPicker defaultValue="#3b82f6" disabled />
156
+ <ColorPicker defaultValue="#10b981" allowAlpha disabled />
157
+ </div>
158
+ ),
159
+ };
@@ -27,7 +27,7 @@ export interface ColorPickerProps {
27
27
  /** 整体禁用。 */
28
28
  disabled?: boolean;
29
29
  /** 触发器尺寸。 @default "default" */
30
- size?: 'sm' | 'default' | 'lg';
30
+ size?: 'sm' | 'md' | 'default' | 'lg';
31
31
  /** 触发器 className。 */
32
32
  className?: string;
33
33
  }
@@ -54,7 +54,7 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
54
54
  allowAlpha = false,
55
55
  presets,
56
56
  disabled = false,
57
- size = 'default',
57
+ size = 'md',
58
58
  className,
59
59
  },
60
60
  ref,
@@ -70,10 +70,12 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
70
70
  };
71
71
 
72
72
  const baseHex = current.slice(0, 7);
73
- const alphaHex = allowAlpha && current.length >= 9 ? current.slice(7, 9) : 'ff';
73
+ const alphaHex =
74
+ allowAlpha && current.length >= 9 ? current.slice(7, 9) : 'ff';
74
75
  const alphaInt = parseInt(alphaHex, 16);
75
76
 
76
- const swatchSize = size === 'sm' ? 'size-7' : size === 'lg' ? 'size-11' : 'size-9';
77
+ const swatchSize =
78
+ size === 'sm' ? 'size-7' : size === 'lg' ? 'size-11' : 'size-9';
77
79
 
78
80
  return (
79
81
  <Popover>
@@ -83,7 +85,7 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
83
85
  type="button"
84
86
  disabled={disabled}
85
87
  className={cn(
86
- 'inline-flex items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors',
88
+ 'inline-flex cursor-pointer items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors',
87
89
  'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
88
90
  'disabled:cursor-not-allowed disabled:opacity-50',
89
91
  swatchSize,
@@ -104,12 +106,18 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
104
106
  type="color"
105
107
  value={baseHex}
106
108
  disabled={disabled}
107
- onChange={(e) => update(allowAlpha ? `${e.target.value}${alphaHex}` : e.target.value)}
109
+ onChange={(e) =>
110
+ update(
111
+ allowAlpha ? `${e.target.value}${alphaHex}` : e.target.value,
112
+ )
113
+ }
108
114
  className="h-10 w-full cursor-pointer rounded-md border border-input bg-transparent"
109
115
  />
110
116
  {allowAlpha ? (
111
117
  <div className="flex items-center gap-2">
112
- <span className="w-12 text-xs text-muted-foreground">Alpha</span>
118
+ <span className="w-12 text-xs text-muted-foreground">
119
+ Alpha
120
+ </span>
113
121
  <input
114
122
  type="range"
115
123
  min={0}
@@ -117,7 +125,9 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
117
125
  value={alphaInt}
118
126
  disabled={disabled}
119
127
  onChange={(e) => {
120
- const a = Number(e.target.value).toString(16).padStart(2, '0');
128
+ const a = Number(e.target.value)
129
+ .toString(16)
130
+ .padStart(2, '0');
121
131
  update(`${baseHex}${a}`);
122
132
  }}
123
133
  className="flex-1"
@@ -142,7 +152,7 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
142
152
  aria-label={p}
143
153
  onClick={() => update(p)}
144
154
  className={cn(
145
- 'size-6 rounded-sm border border-border ring-offset-background transition-all',
155
+ 'size-6 cursor-pointer rounded-sm border border-border ring-offset-background transition-all',
146
156
  'hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
147
157
  )}
148
158
  style={{ backgroundColor: p }}
@@ -2,7 +2,7 @@
2
2
  id: command
3
3
  name: Command
4
4
  type: component
5
- category: shell
5
+ category: feedback
6
6
  since: 0.1.0
7
7
  package: '@teamix-evo/ui'
8
8
  displayName: 命令面板
@@ -10,8 +10,8 @@ displayName: 命令面板
10
10
 
11
11
  # Command 命令面板
12
12
 
13
- 命令面板 — 基于 [`cmdk`](https://cmdk.paco.me/),提供"按 ⌘K 打开"的全局搜索 / 命令执行体验(Linear / Raycast / Vercel 风格)。
14
- **shadcn-only**(antd 无对标)。是 `Combobox` 的底座(Combobox = Command + Popover)。
13
+ 命令面板 / 下拉底座 — 基于 [`cmdk`](https://cmdk.paco.me/),提供"按 ⌘K 打开"的全局搜索 / 命令执行体验(Linear / Raycast / Vercel 风格)。
14
+ **shadcn-only**(antd 无对标)。同时作为 `Combobox` / `Select` / `AutoComplete` 的同源下拉内核(ADR 0029),提供过滤 / 键盘导航 / a11y 能力。
15
15
 
16
16
  ## When to use
17
17
 
@@ -30,23 +30,20 @@ displayName: 命令面板
30
30
  ## Props
31
31
 
32
32
  <!-- auto:props:begin -->
33
-
34
33
  _(no props)_
35
-
36
34
  <!-- auto:props:end -->
37
35
 
38
36
  ## 依赖
39
37
 
40
38
  <!-- auto:deps:begin -->
41
-
42
39
  ### 同库依赖
43
40
 
44
41
  > `teamix-evo ui add command` 时,以下 entry 会被自动连带安装(无需手动 add)。
45
42
 
46
- | Entry | 类型 | 描述 |
47
- | -------- | --------- | ----------------------------------------------------------------------------------- |
48
- | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
49
- | `dialog` | component | 模态对话框 — Radix Dialog + antd Modal 并集(组合式 Header/Footer/Title/Description) |
43
+ | Entry | 类型 | 描述 |
44
+ | --- | --- | --- |
45
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
46
+ | `dialog` | component | 模态对话框 — Radix Dialog + antd Modal 并集(组合式 Header/Footer/Title/Description,size sm/md/lg/xl 绑定 layout-dialog tokens,圆角 radius-dialog 16px,无 border) |
50
47
 
51
48
  ### npm 依赖
52
49
 
@@ -55,7 +52,6 @@ _(no props)_
55
52
  ```bash
56
53
  pnpm add cmdk@^1.0.0 lucide-react@^0.460.0
57
54
  ```
58
-
59
55
  <!-- auto:deps:end -->
60
56
 
61
57
  > 子组件:`Command`(Root 容器)/ `CommandDialog`(模态版,内部组合 Dialog)/ `CommandInput`(自带搜索图标)/ `CommandList`(可滚动列表)/ `CommandEmpty`(无匹配空态)/ `CommandGroup`(分组,带 heading)/ `CommandItem`(选项,支持 `onSelect`)/ `CommandSeparator` / `CommandShortcut`(右对齐快捷键)。
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { Calendar, Mail, Settings, User } from 'lucide-react';
3
3
  import {
4
4
  Command,
@@ -12,14 +12,14 @@ import {
12
12
  } from './command';
13
13
 
14
14
  const meta: Meta<typeof Command> = {
15
- title: '应用壳 · Shell/Command',
15
+ title: '反馈 · Feedback/Command',
16
16
  component: Command,
17
17
  tags: ['autodocs'],
18
18
  parameters: {
19
19
  docs: {
20
20
  description: {
21
21
  component:
22
- '命令面板 — 全局搜索 / 命令执行(Linear / Raycast / Vercel 风格)。基于 cmdk,提供键盘导航、模糊搜索、分组、空态、快捷键提示;模态版用 CommandDialog 监听 ⌘K 全局触发。',
22
+ '命令面板 / 下拉底座 — 全局搜索 / 命令执行(Linear / Raycast / Vercel 风格)。基于 cmdk,提供键盘导航、模糊搜索、分组、空态、快捷键提示;模态版用 CommandDialog 监听 ⌘K 全局触发。同时作为 `Combobox` / `Select` / `AutoComplete` 的同源下拉内核(ADR 0029)。',
23
23
  },
24
24
  },
25
25
  },
@@ -30,7 +30,7 @@ type Story = StoryObj<typeof Command>;
30
30
 
31
31
  export const Inline: Story = {
32
32
  render: () => (
33
- <Command className="w-80 rounded-lg border">
33
+ <Command className="w-80 rounded-lg border border-border">
34
34
  <CommandInput placeholder="输入命令或搜索..." />
35
35
  <CommandList>
36
36
  <CommandEmpty>无结果。</CommandEmpty>
@@ -3,7 +3,7 @@ import { Command as CommandPrimitive } from 'cmdk';
3
3
  import { Search } from 'lucide-react';
4
4
 
5
5
  import { cn } from '@/utils/cn';
6
- import { Dialog, DialogContent } from '@/components/dialog/dialog';
6
+ import { Dialog, DialogContent, DialogTitle } from '@/components/dialog/dialog';
7
7
 
8
8
  export interface CommandProps
9
9
  extends React.ComponentPropsWithoutRef<typeof CommandPrimitive> {}
@@ -24,11 +24,19 @@ const Command = React.forwardRef<
24
24
  Command.displayName = CommandPrimitive.displayName;
25
25
 
26
26
  interface CommandDialogProps
27
- extends React.ComponentPropsWithoutRef<typeof Dialog> {}
27
+ extends React.ComponentPropsWithoutRef<typeof Dialog> {
28
+ /** 命令面板的可访问性标题(默认隐藏视觉)。 */
29
+ title?: string;
30
+ }
28
31
 
29
- const CommandDialog = ({ children, ...props }: CommandDialogProps) => (
32
+ const CommandDialog = ({
33
+ title = '命令面板',
34
+ children,
35
+ ...props
36
+ }: CommandDialogProps) => (
30
37
  <Dialog {...props}>
31
38
  <DialogContent className="overflow-hidden p-0">
39
+ <DialogTitle className="sr-only">{title}</DialogTitle>
32
40
  <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:size-5">
33
41
  {children}
34
42
  </Command>
@@ -40,12 +48,15 @@ const CommandInput = React.forwardRef<
40
48
  React.ElementRef<typeof CommandPrimitive.Input>,
41
49
  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
42
50
  >(({ className, ...props }, ref) => (
43
- <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
51
+ <div
52
+ className="flex items-center border-b border-b-border px-3"
53
+ cmdk-input-wrapper=""
54
+ >
44
55
  <Search className="mr-2 size-4 shrink-0 opacity-50" />
45
56
  <CommandPrimitive.Input
46
57
  ref={ref}
47
58
  className={cn(
48
- 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
59
+ 'flex h-10 w-full rounded-md bg-transparent py-3 text-xs outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
49
60
  className,
50
61
  )}
51
62
  {...props}
@@ -72,7 +83,7 @@ const CommandEmpty = React.forwardRef<
72
83
  >((props, ref) => (
73
84
  <CommandPrimitive.Empty
74
85
  ref={ref}
75
- className="py-6 text-center text-sm text-muted-foreground"
86
+ className="py-6 text-center text-xs text-muted-foreground"
76
87
  {...props}
77
88
  />
78
89
  ));
@@ -112,7 +123,7 @@ const CommandItem = React.forwardRef<
112
123
  <CommandPrimitive.Item
113
124
  ref={ref}
114
125
  className={cn(
115
- "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
126
+ "relative flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-xs outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
116
127
  className,
117
128
  )}
118
129
  {...props}
@@ -2,7 +2,7 @@
2
2
  id: context-menu
3
3
  name: ContextMenu
4
4
  type: component
5
- category: navigation
5
+ category: deprecated
6
6
  since: 0.1.0
7
7
  package: '@teamix-evo/ui'
8
8
  displayName: 右键菜单
@@ -30,25 +30,7 @@ shadcn 显式版,antd 用 Dropdown 模拟同样可达。
30
30
  > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `ContextMenuContent` 的 props。
31
31
 
32
32
  <!-- auto:props:begin -->
33
-
34
- #### ContextMenuSubTrigger
35
-
36
- | 名称 | 类型 | 默认值 | 必填 | 说明 |
37
- | ------- | --------- | ------- | ---- | ------------------------------------------- |
38
- | `inset` | `boolean` | `false` | – | 左侧留出 icon 宽度的缩进,对齐同级 icon 项。 |
39
-
40
- #### ContextMenuItem
41
-
42
- | 名称 | 类型 | 默认值 | 必填 | 说明 |
43
- | ------- | --------- | ------- | ---- | ------------------------------------------- |
44
- | `inset` | `boolean` | `false` | – | 左侧留出 icon 宽度的缩进,对齐同级 icon 项。 |
45
-
46
- #### ContextMenuLabel
47
-
48
- | 名称 | 类型 | 默认值 | 必填 | 说明 |
49
- | ------- | --------- | ------- | ---- | ------------------------------------------- |
50
- | `inset` | `boolean` | `false` | – | 左侧留出 icon 宽度的缩进,对齐同级 icon 项。 |
51
-
33
+ _(组件无 `<Name>Props` interface — props 详见 [`context-menu.tsx`](./context-menu.tsx))_
52
34
  <!-- auto:props:end -->
53
35
 
54
36
  ## 依赖
@@ -56,14 +38,13 @@ shadcn 显式版,antd 用 Dropdown 模拟同样可达。
56
38
  > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
57
39
 
58
40
  <!-- auto:deps:begin -->
59
-
60
41
  ### 同库依赖
61
42
 
62
43
  > `teamix-evo ui add context-menu` 时,以下 entry 会被自动连带安装(无需手动 add)。
63
44
 
64
- | Entry | 类型 | 描述 |
65
- | ----- | ---- | -------------------------------------------------- |
66
- | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
45
+ | Entry | 类型 | 描述 |
46
+ | --- | --- | --- |
47
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
67
48
 
68
49
  ### npm 依赖
69
50
 
@@ -72,7 +53,6 @@ shadcn 显式版,antd 用 Dropdown 模拟同样可达。
72
53
  ```bash
73
54
  pnpm add @radix-ui/react-context-menu@^2.2.0 lucide-react@^0.460.0
74
55
  ```
75
-
76
56
  <!-- auto:deps:end -->
77
57
 
78
58
  ## AI 生成纪律
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import {
3
3
  ContextMenu,
4
4
  ContextMenuTrigger,
@@ -9,14 +9,14 @@ import {
9
9
  } from './context-menu';
10
10
 
11
11
  const meta: Meta<typeof ContextMenuContent> = {
12
- title: '导航 · Navigation/ContextMenu',
12
+ title: '废弃 · Deprecated/ContextMenu',
13
13
  component: ContextMenuContent,
14
14
  tags: ['autodocs'],
15
15
  parameters: {
16
16
  docs: {
17
17
  description: {
18
18
  component:
19
- '右键菜单 — 右键触发的操作菜单,与 DropdownMenu 形态一致(Item / CheckboxItem / RadioItem / Sub),仅触发方式不同。Radix ContextMenu 实现,shadcn 显式版,antd 用 Dropdown 模拟同样可达。',
19
+ '⚠️ **已废弃** — 仅 Storybook 留档,不通过 manifest 对外发布。\n\n右键菜单 — 右键触发的操作菜单,与 DropdownMenu 形态一致(Item / CheckboxItem / RadioItem / Sub),仅触发方式不同。Radix ContextMenu 实现,shadcn 显式版,antd 用 Dropdown 模拟同样可达。',
20
20
  },
21
21
  },
22
22
  },
@@ -28,7 +28,7 @@ type Story = StoryObj<typeof ContextMenuContent>;
28
28
  export const Default: Story = {
29
29
  render: () => (
30
30
  <ContextMenu>
31
- <ContextMenuTrigger className="flex h-32 w-72 items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground">
31
+ <ContextMenuTrigger className="flex h-32 w-72 items-center justify-center rounded-md border border-border border-dashed text-sm text-muted-foreground">
32
32
  在此处右键(或长按)打开菜单
33
33
  </ContextMenuTrigger>
34
34
  <ContextMenuContent className="w-56">
@@ -20,7 +20,7 @@ const ContextMenuSubTrigger = React.forwardRef<
20
20
  <ContextMenuPrimitive.SubTrigger
21
21
  ref={ref}
22
22
  className={cn(
23
- 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
23
+ 'flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
24
24
  inset && 'pl-8',
25
25
  className,
26
26
  )}
@@ -39,7 +39,7 @@ const ContextMenuSubContent = React.forwardRef<
39
39
  <ContextMenuPrimitive.SubContent
40
40
  ref={ref}
41
41
  className={cn(
42
- 'z-50 min-w-menu overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
42
+ 'z-50 min-w-menu overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
43
43
  className,
44
44
  )}
45
45
  {...props}
@@ -47,15 +47,28 @@ const ContextMenuSubContent = React.forwardRef<
47
47
  ));
48
48
  ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
49
49
 
50
+ /**
51
+ * `onCloseAutoFocus` 默认 `preventDefault` — 阻止 Radix 关闭时把焦点程序化
52
+ * restore 回 trigger。
53
+ *
54
+ * 原因:Chromium/Safari 把"程序化 restore 的 focus"判定为非鼠标焦点 → 触发
55
+ * `:focus-visible` → trigger 如果是 button-like 元素会残留蓝色 ring(ContextMenu
56
+ * trigger 常是任意容器,常见场景不明显,但作为 popper 类一致默认策略)。
57
+ * 与 DropdownMenu / Popover 走同一默认,保证全库 popper 类鼠标体感一致。
58
+ *
59
+ * 取舍:键盘 Esc 关闭后焦点也不会 restore 回 trigger,而是落到 body。业务侧
60
+ * 需要保留 restore 行为时,显式传 `onCloseAutoFocus` 即可完全覆盖默认。
61
+ */
50
62
  const ContextMenuContent = React.forwardRef<
51
63
  React.ElementRef<typeof ContextMenuPrimitive.Content>,
52
64
  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
53
- >(({ className, ...props }, ref) => (
65
+ >(({ className, onCloseAutoFocus, ...props }, ref) => (
54
66
  <ContextMenuPrimitive.Portal>
55
67
  <ContextMenuPrimitive.Content
56
68
  ref={ref}
69
+ onCloseAutoFocus={onCloseAutoFocus ?? ((event) => event.preventDefault())}
57
70
  className={cn(
58
- 'z-50 min-w-menu overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
71
+ 'z-50 min-w-menu overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
59
72
  className,
60
73
  )}
61
74
  {...props}
@@ -73,7 +86,7 @@ const ContextMenuItem = React.forwardRef<
73
86
  <ContextMenuPrimitive.Item
74
87
  ref={ref}
75
88
  className={cn(
76
- 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
89
+ 'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
77
90
  inset && 'pl-8',
78
91
  className,
79
92
  )}
@@ -89,7 +102,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
89
102
  <ContextMenuPrimitive.CheckboxItem
90
103
  ref={ref}
91
104
  className={cn(
92
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
105
+ 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-xs outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
93
106
  className,
94
107
  )}
95
108
  checked={checked}
@@ -113,7 +126,7 @@ const ContextMenuRadioItem = React.forwardRef<
113
126
  <ContextMenuPrimitive.RadioItem
114
127
  ref={ref}
115
128
  className={cn(
116
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
129
+ 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-xs outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
117
130
  className,
118
131
  )}
119
132
  {...props}
@@ -137,7 +150,7 @@ const ContextMenuLabel = React.forwardRef<
137
150
  <ContextMenuPrimitive.Label
138
151
  ref={ref}
139
152
  className={cn(
140
- 'px-2 py-1.5 text-sm font-semibold text-foreground',
153
+ 'px-2 py-1.5 text-xs font-semibold text-foreground',
141
154
  inset && 'pl-8',
142
155
  className,
143
156
  )}