@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.
Files changed (295) hide show
  1. package/README.md +184 -184
  2. package/manifest.json +680 -492
  3. package/package.json +20 -10
  4. package/src/components/accordion/accordion.meta.md +5 -4
  5. package/src/components/accordion/accordion.stories.tsx +14 -9
  6. package/src/components/accordion/accordion.tsx +104 -8
  7. package/src/components/affix/affix.meta.md +20 -2
  8. package/src/components/affix/affix.stories.tsx +102 -25
  9. package/src/components/affix/affix.tsx +79 -9
  10. package/src/components/alert/alert.meta.md +44 -13
  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 +61 -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 +8 -3
  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 +9 -4
  20. package/src/components/app/app.stories.tsx +9 -7
  21. package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
  22. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
  23. package/src/components/auto-complete/auto-complete.meta.md +14 -6
  24. package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
  25. package/src/components/auto-complete/auto-complete.tsx +119 -71
  26. package/src/components/avatar/avatar.meta.md +6 -7
  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 +10 -9
  30. package/src/components/badge/badge.stories.tsx +2 -2
  31. package/src/components/badge/badge.tsx +9 -15
  32. package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
  33. package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
  34. package/src/components/breadcrumb/breadcrumb.tsx +22 -8
  35. package/src/components/button/button.meta.md +258 -21
  36. package/src/components/button/button.stories.tsx +549 -41
  37. package/src/components/button/button.tsx +335 -33
  38. package/src/components/button/demo/as-child.tsx +24 -0
  39. package/src/components/button/demo/basic.tsx +8 -0
  40. package/src/components/button/demo/block.tsx +16 -0
  41. package/src/components/button/demo/loading.tsx +19 -0
  42. package/src/components/button/demo/shapes.tsx +18 -0
  43. package/src/components/button/demo/sizes.tsx +19 -0
  44. package/src/components/button/demo/variants.tsx +19 -0
  45. package/src/components/button/demo/with-icon.tsx +20 -0
  46. package/src/components/calendar/calendar.meta.md +13 -3
  47. package/src/components/calendar/calendar.stories.tsx +6 -6
  48. package/src/components/calendar/calendar.tsx +73 -8
  49. package/src/components/card/card.meta.md +27 -5
  50. package/src/components/card/card.stories.tsx +42 -3
  51. package/src/components/card/card.tsx +146 -63
  52. package/src/components/carousel/carousel.meta.md +4 -3
  53. package/src/components/carousel/carousel.stories.tsx +11 -6
  54. package/src/components/cascader/cascader.meta.md +47 -17
  55. package/src/components/cascader/cascader.stories.tsx +22 -10
  56. package/src/components/cascader/cascader.tsx +428 -85
  57. package/src/components/checkbox/checkbox.meta.md +75 -7
  58. package/src/components/checkbox/checkbox.stories.tsx +161 -3
  59. package/src/components/checkbox/checkbox.tsx +77 -9
  60. package/src/components/collapsible/collapsible.meta.md +14 -6
  61. package/src/components/collapsible/collapsible.stories.tsx +10 -2
  62. package/src/components/collapsible/collapsible.tsx +93 -6
  63. package/src/components/color-picker/color-picker.meta.md +12 -7
  64. package/src/components/color-picker/color-picker.stories.tsx +86 -7
  65. package/src/components/color-picker/color-picker.tsx +20 -9
  66. package/src/components/command/command.meta.md +29 -13
  67. package/src/components/command/command.stories.tsx +4 -4
  68. package/src/components/command/command.tsx +19 -8
  69. package/src/components/context-menu/context-menu.meta.md +11 -8
  70. package/src/components/context-menu/context-menu.stories.tsx +11 -3
  71. package/src/components/context-menu/context-menu.tsx +21 -8
  72. package/src/components/data-table/data-table.meta.md +6 -5
  73. package/src/components/data-table/data-table.stories.tsx +13 -6
  74. package/src/components/data-table/data-table.tsx +2 -2
  75. package/src/components/date-picker/date-picker.meta.md +88 -19
  76. package/src/components/date-picker/date-picker.stories.tsx +55 -5
  77. package/src/components/date-picker/date-picker.tsx +1489 -91
  78. package/src/components/descriptions/descriptions.meta.md +10 -5
  79. package/src/components/descriptions/descriptions.stories.tsx +3 -3
  80. package/src/components/descriptions/descriptions.tsx +22 -14
  81. package/src/components/dialog/dialog.meta.md +76 -13
  82. package/src/components/dialog/dialog.stories.tsx +182 -20
  83. package/src/components/dialog/dialog.tsx +67 -15
  84. package/src/components/dialog/imperative.tsx +252 -0
  85. package/src/components/drawer/drawer.meta.md +33 -34
  86. package/src/components/drawer/drawer.stories.tsx +29 -12
  87. package/src/components/drawer/drawer.tsx +22 -113
  88. package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
  89. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
  90. package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
  91. package/src/components/ellipsis/ellipsis.meta.md +87 -0
  92. package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
  93. package/src/components/ellipsis/ellipsis.tsx +153 -0
  94. package/src/components/empty/empty.meta.md +9 -4
  95. package/src/components/empty/empty.stories.tsx +4 -4
  96. package/src/components/empty/empty.tsx +10 -3
  97. package/src/components/field/field.meta.md +47 -9
  98. package/src/components/field/field.stories.tsx +385 -5
  99. package/src/components/field/field.tsx +263 -35
  100. package/src/components/filter-bar/filter-bar.meta.md +92 -0
  101. package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
  102. package/src/components/filter-bar/filter-bar.tsx +568 -0
  103. package/src/components/flex/flex.meta.md +54 -6
  104. package/src/components/flex/flex.stories.tsx +107 -20
  105. package/src/components/flex/flex.tsx +27 -4
  106. package/src/components/float-button/float-button.meta.md +8 -3
  107. package/src/components/float-button/float-button.stories.tsx +9 -7
  108. package/src/components/float-button/float-button.tsx +1 -1
  109. package/src/components/form/form.meta.md +39 -17
  110. package/src/components/form/form.stories.tsx +350 -3
  111. package/src/components/form/form.tsx +101 -35
  112. package/src/components/grid/grid.meta.md +7 -2
  113. package/src/components/grid/grid.stories.tsx +6 -4
  114. package/src/components/hover-card/hover-card.meta.md +20 -9
  115. package/src/components/hover-card/hover-card.stories.tsx +34 -5
  116. package/src/components/hover-card/hover-card.tsx +51 -13
  117. package/src/components/icon/DEVELOPMENT.md +809 -0
  118. package/src/components/icon/icon.meta.md +170 -0
  119. package/src/components/icon/icon.stories.tsx +344 -0
  120. package/src/components/icon/icon.tsx +248 -0
  121. package/src/components/image/image.meta.md +9 -4
  122. package/src/components/image/image.stories.tsx +3 -3
  123. package/src/components/image/image.tsx +6 -4
  124. package/src/components/input/demo/basic.tsx +12 -0
  125. package/src/components/input/demo/clearable.tsx +21 -0
  126. package/src/components/input/demo/show-count.tsx +18 -0
  127. package/src/components/input/demo/sizes.tsx +15 -0
  128. package/src/components/input/input.meta.md +39 -33
  129. package/src/components/input/input.stories.tsx +62 -35
  130. package/src/components/input/input.tsx +97 -98
  131. package/src/components/input-group/input-group.meta.md +54 -22
  132. package/src/components/input-group/input-group.stories.tsx +49 -16
  133. package/src/components/input-group/input-group.tsx +44 -8
  134. package/src/components/input-number/input-number.meta.md +64 -7
  135. package/src/components/input-number/input-number.stories.tsx +46 -8
  136. package/src/components/input-number/input-number.tsx +99 -26
  137. package/src/components/input-otp/input-otp.meta.md +4 -3
  138. package/src/components/input-otp/input-otp.stories.tsx +3 -3
  139. package/src/components/input-otp/input-otp.tsx +1 -1
  140. package/src/components/item/item.meta.md +8 -3
  141. package/src/components/item/item.stories.tsx +8 -5
  142. package/src/components/item/item.tsx +7 -6
  143. package/src/components/kbd/kbd.meta.md +13 -4
  144. package/src/components/kbd/kbd.stories.tsx +4 -4
  145. package/src/components/kbd/kbd.tsx +10 -5
  146. package/src/components/label/label.meta.md +18 -10
  147. package/src/components/label/label.stories.tsx +64 -6
  148. package/src/components/label/label.tsx +91 -19
  149. package/src/components/masonry/masonry.meta.md +8 -3
  150. package/src/components/masonry/masonry.stories.tsx +7 -5
  151. package/src/components/masonry/masonry.tsx +1 -0
  152. package/src/components/mentions/mentions.meta.md +36 -6
  153. package/src/components/mentions/mentions.stories.tsx +120 -6
  154. package/src/components/mentions/mentions.tsx +11 -5
  155. package/src/components/menubar/menubar.meta.md +30 -12
  156. package/src/components/menubar/menubar.stories.tsx +62 -2
  157. package/src/components/menubar/menubar.tsx +9 -9
  158. package/src/components/native-select/native-select.meta.md +8 -3
  159. package/src/components/native-select/native-select.stories.tsx +8 -5
  160. package/src/components/native-select/native-select.tsx +1 -1
  161. package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
  162. package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
  163. package/src/components/navigation-menu/navigation-menu.tsx +8 -4
  164. package/src/components/notification/notification.meta.md +52 -10
  165. package/src/components/notification/notification.stories.tsx +11 -9
  166. package/src/components/notification/notification.tsx +36 -21
  167. package/src/components/page-header/DEVELOPMENT.md +842 -0
  168. package/src/components/page-header/page-header.meta.md +208 -0
  169. package/src/components/page-header/page-header.stories.tsx +421 -0
  170. package/src/components/page-header/page-header.tsx +281 -0
  171. package/src/components/pagination/pagination.meta.md +140 -37
  172. package/src/components/pagination/pagination.stories.tsx +232 -10
  173. package/src/components/pagination/pagination.tsx +355 -63
  174. package/src/components/popconfirm/popconfirm.meta.md +9 -4
  175. package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
  176. package/src/components/popconfirm/popconfirm.tsx +2 -2
  177. package/src/components/popover/popover.meta.md +62 -5
  178. package/src/components/popover/popover.stories.tsx +83 -7
  179. package/src/components/popover/popover.tsx +77 -28
  180. package/src/components/progress/progress.meta.md +38 -6
  181. package/src/components/progress/progress.stories.tsx +3 -3
  182. package/src/components/progress/progress.tsx +24 -16
  183. package/src/components/radio-group/radio-group.meta.md +79 -7
  184. package/src/components/radio-group/radio-group.stories.tsx +39 -3
  185. package/src/components/radio-group/radio-group.tsx +149 -18
  186. package/src/components/rate/rate.meta.md +35 -4
  187. package/src/components/rate/rate.stories.tsx +13 -5
  188. package/src/components/rate/rate.tsx +37 -10
  189. package/src/components/resizable/resizable.meta.md +7 -4
  190. package/src/components/resizable/resizable.stories.tsx +6 -6
  191. package/src/components/resizable/resizable.tsx +1 -1
  192. package/src/components/result/result.meta.md +7 -2
  193. package/src/components/result/result.stories.tsx +4 -8
  194. package/src/components/result/result.tsx +24 -15
  195. package/src/components/scroll-area/scroll-area.meta.md +4 -3
  196. package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
  197. package/src/components/scroll-area/scroll-area.tsx +3 -3
  198. package/src/components/segmented/segmented.meta.md +7 -4
  199. package/src/components/segmented/segmented.stories.tsx +37 -8
  200. package/src/components/segmented/segmented.tsx +15 -7
  201. package/src/components/select/select.meta.md +197 -52
  202. package/src/components/select/select.stories.tsx +238 -63
  203. package/src/components/select/select.tsx +718 -171
  204. package/src/components/separator/separator.meta.md +4 -3
  205. package/src/components/separator/separator.stories.tsx +3 -3
  206. package/src/components/separator/separator.tsx +3 -7
  207. package/src/components/sheet/sheet.meta.md +32 -16
  208. package/src/components/sheet/sheet.stories.tsx +116 -10
  209. package/src/components/sheet/sheet.tsx +116 -29
  210. package/src/components/sidebar/sidebar.meta.md +37 -18
  211. package/src/components/sidebar/sidebar.stories.tsx +701 -29
  212. package/src/components/sidebar/sidebar.tsx +615 -142
  213. package/src/components/skeleton/skeleton.meta.md +4 -5
  214. package/src/components/skeleton/skeleton.stories.tsx +4 -4
  215. package/src/components/skeleton/skeleton.tsx +7 -7
  216. package/src/components/slider/slider.meta.md +57 -5
  217. package/src/components/slider/slider.stories.tsx +58 -6
  218. package/src/components/slider/slider.tsx +154 -13
  219. package/src/components/sonner/sonner.meta.md +58 -7
  220. package/src/components/sonner/sonner.stories.tsx +78 -5
  221. package/src/components/sonner/sonner.tsx +137 -8
  222. package/src/components/spinner/spinner.meta.md +62 -13
  223. package/src/components/spinner/spinner.stories.tsx +66 -14
  224. package/src/components/spinner/spinner.tsx +111 -9
  225. package/src/components/statistic/statistic.meta.md +7 -2
  226. package/src/components/statistic/statistic.stories.tsx +3 -7
  227. package/src/components/statistic/statistic.tsx +5 -6
  228. package/src/components/steps/steps.meta.md +18 -4
  229. package/src/components/steps/steps.stories.tsx +43 -3
  230. package/src/components/steps/steps.tsx +15 -12
  231. package/src/components/switch/switch.meta.md +51 -5
  232. package/src/components/switch/switch.stories.tsx +6 -6
  233. package/src/components/switch/switch.tsx +109 -41
  234. package/src/components/table/table.meta.md +17 -6
  235. package/src/components/table/table.stories.tsx +10 -5
  236. package/src/components/table/table.tsx +4 -4
  237. package/src/components/tabs/tabs.meta.md +38 -25
  238. package/src/components/tabs/tabs.stories.tsx +111 -25
  239. package/src/components/tabs/tabs.tsx +125 -54
  240. package/src/components/tag/tag.meta.md +105 -40
  241. package/src/components/tag/tag.stories.tsx +189 -16
  242. package/src/components/tag/tag.tsx +222 -21
  243. package/src/components/textarea/textarea.meta.md +35 -19
  244. package/src/components/textarea/textarea.stories.tsx +32 -6
  245. package/src/components/textarea/textarea.tsx +33 -9
  246. package/src/components/time-picker/time-picker.meta.md +124 -32
  247. package/src/components/time-picker/time-picker.stories.tsx +85 -15
  248. package/src/components/time-picker/time-picker.tsx +913 -61
  249. package/src/components/timeline/timeline.meta.md +14 -6
  250. package/src/components/timeline/timeline.stories.tsx +37 -7
  251. package/src/components/timeline/timeline.tsx +35 -14
  252. package/src/components/toggle/toggle.meta.md +5 -4
  253. package/src/components/toggle/toggle.stories.tsx +4 -4
  254. package/src/components/toggle/toggle.tsx +4 -3
  255. package/src/components/toggle-group/toggle-group.meta.md +5 -4
  256. package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
  257. package/src/components/toggle-group/toggle-group.tsx +2 -2
  258. package/src/components/tooltip/tooltip.meta.md +55 -5
  259. package/src/components/tooltip/tooltip.stories.tsx +42 -5
  260. package/src/components/tooltip/tooltip.tsx +81 -21
  261. package/src/components/tour/tour.meta.md +9 -4
  262. package/src/components/tour/tour.stories.tsx +3 -3
  263. package/src/components/tour/tour.tsx +4 -4
  264. package/src/components/transfer/transfer.meta.md +11 -6
  265. package/src/components/transfer/transfer.stories.tsx +4 -8
  266. package/src/components/transfer/transfer.tsx +28 -21
  267. package/src/components/tree/tree.meta.md +63 -5
  268. package/src/components/tree/tree.stories.tsx +31 -12
  269. package/src/components/tree/tree.tsx +9 -8
  270. package/src/components/tree-select/tree-select.meta.md +59 -8
  271. package/src/components/tree-select/tree-select.stories.tsx +3 -3
  272. package/src/components/tree-select/tree-select.tsx +42 -7
  273. package/src/components/typography/typography.meta.md +61 -14
  274. package/src/components/typography/typography.stories.tsx +12 -11
  275. package/src/components/typography/typography.tsx +43 -28
  276. package/src/components/upload/upload.meta.md +49 -4
  277. package/src/components/upload/upload.stories.tsx +72 -12
  278. package/src/components/upload/upload.tsx +170 -37
  279. package/src/components/watermark/watermark.meta.md +7 -2
  280. package/src/components/watermark/watermark.stories.tsx +101 -9
  281. package/src/components/watermark/watermark.tsx +1 -0
  282. package/src/hooks/use-breakpoint.ts +117 -0
  283. package/src/hooks/use-debounce-callback.ts +52 -0
  284. package/src/hooks/use-mobile.ts +23 -0
  285. package/src/stories/theme-tokens.stories.tsx +747 -0
  286. package/src/utils/trigger-input.ts +53 -0
  287. package/src/components/button-group/button-group.meta.md +0 -92
  288. package/src/components/button-group/button-group.stories.tsx +0 -90
  289. package/src/components/button-group/button-group.tsx +0 -75
  290. package/src/components/combobox/combobox.meta.md +0 -93
  291. package/src/components/combobox/combobox.stories.tsx +0 -55
  292. package/src/components/combobox/combobox.tsx +0 -130
  293. package/src/components/space/space.meta.md +0 -94
  294. package/src/components/space/space.stories.tsx +0 -94
  295. package/src/components/space/space.tsx +0 -106
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  id: color-picker
3
3
  name: ColorPicker
4
+ displayName: 颜色选择
4
5
  type: component
5
- category: form
6
+ category: data-entry
6
7
  since: 0.1.0
7
- package: "@teamix-evo/ui"
8
+ package: '@teamix-evo/ui'
8
9
  ---
9
10
 
10
- # ColorPicker
11
+ # ColorPicker 颜色选择
11
12
 
12
13
  颜色选择 — antd 独有补足。**等价 antd `ColorPicker`**(v5.5+)。基于原生 `<input type="color">` + alpha 滑块 + hex 文本输入 + 预设色块,提供"触发器(色块)→ 弹出面板"的标准交互。可选透明度(8 位 hex)。
13
14
 
@@ -24,6 +25,8 @@ package: "@teamix-evo/ui"
24
25
  - 渐变 / 多色阶 → 当前不支持,需配合第三方 picker
25
26
  - HSL / RGB 各通道独立调节 → 当前仅 hex + alpha,复杂场景建议引入专业 picker
26
27
 
28
+ ## Props
29
+
27
30
  <!-- auto:props:begin -->
28
31
  | 名称 | 类型 | 默认值 | 必填 | 说明 |
29
32
  | --- | --- | --- | --- | --- |
@@ -33,10 +36,12 @@ package: "@teamix-evo/ui"
33
36
  | `allowAlpha` | `boolean` | `false` | – | 是否允许透明度(antd `format=hex8` 类似行为) — 启用后展示 alpha 滑块,value 变为 8 位 hex。 |
34
37
  | `presets` | `string[]` | – | – | 预设色块(antd `presets` 并集) — 一组常用颜色快捷选择。 |
35
38
  | `disabled` | `boolean` | – | – | 整体禁用。 |
36
- | `size` | `'sm' \| 'default' \| 'lg'` | `"default"` | – | 触发器尺寸。 |
39
+ | `size` | `'sm' \| 'md' \| 'default' \| 'lg'` | `"default"` | – | 触发器尺寸。 |
37
40
  | `className` | `string` | – | – | 触发器 className。 |
38
41
  <!-- auto:props:end -->
39
42
 
43
+ ## 依赖
44
+
40
45
  <!-- auto:deps:begin -->
41
46
  ### 同库依赖
42
47
 
@@ -45,8 +50,8 @@ package: "@teamix-evo/ui"
45
50
  | Entry | 类型 | 描述 |
46
51
  | --- | --- | --- |
47
52
  | `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 并集 |
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 命名统一) |
50
55
 
51
56
  ### npm 依赖
52
57
 
@@ -55,7 +60,7 @@ _无 — 本组件不依赖任何 npm 包。_
55
60
 
56
61
  ## AI 生成纪律
57
62
 
58
- - **value 必须是 `#RRGGBB`(6位)或 `#RRGGBBAA`(8位 + 透明度)** — 不接受 `rgb()` / `hsl()` / 命名色
63
+ - **value 必须是 `#RRGGBB`(6 位)或 `#RRGGBBAA`(8 位 + 透明度)** — 不接受 `rgb()` / `hsl()` / 命名色
59
64
  - **`allowAlpha=true`** 时 value 自动变为 8 位 hex;关闭时即使有 alpha 也会被截断到 6 位
60
65
  - **`presets`** 应是品牌色板而非随手凑数 — 6-12 个 stable 颜色为佳,避免视觉混乱
61
66
  - **`disabled`** 是只读语义 — 配 value 展示当前选中色;**不要**用做"暂时锁定"等业务态(用 form-level 控制)
@@ -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+)。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
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
  }
@@ -48,12 +48,13 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
48
48
  (
49
49
  {
50
50
  value,
51
+ // eslint-disable-next-line teamix-evo/no-color-literal -- ColorPicker default value is a hex literal by API contract (antd parity); not a styling decision.
51
52
  defaultValue = '#000000',
52
53
  onChange,
53
54
  allowAlpha = false,
54
55
  presets,
55
56
  disabled = false,
56
- size = 'default',
57
+ size = 'md',
57
58
  className,
58
59
  },
59
60
  ref,
@@ -69,10 +70,12 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
69
70
  };
70
71
 
71
72
  const baseHex = current.slice(0, 7);
72
- const alphaHex = allowAlpha && current.length >= 9 ? current.slice(7, 9) : 'ff';
73
+ const alphaHex =
74
+ allowAlpha && current.length >= 9 ? current.slice(7, 9) : 'ff';
73
75
  const alphaInt = parseInt(alphaHex, 16);
74
76
 
75
- 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';
76
79
 
77
80
  return (
78
81
  <Popover>
@@ -82,7 +85,7 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
82
85
  type="button"
83
86
  disabled={disabled}
84
87
  className={cn(
85
- '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',
86
89
  'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
87
90
  'disabled:cursor-not-allowed disabled:opacity-50',
88
91
  swatchSize,
@@ -103,12 +106,18 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
103
106
  type="color"
104
107
  value={baseHex}
105
108
  disabled={disabled}
106
- 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
+ }
107
114
  className="h-10 w-full cursor-pointer rounded-md border border-input bg-transparent"
108
115
  />
109
116
  {allowAlpha ? (
110
117
  <div className="flex items-center gap-2">
111
- <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>
112
121
  <input
113
122
  type="range"
114
123
  min={0}
@@ -116,7 +125,9 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
116
125
  value={alphaInt}
117
126
  disabled={disabled}
118
127
  onChange={(e) => {
119
- 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');
120
131
  update(`${baseHex}${a}`);
121
132
  }}
122
133
  className="flex-1"
@@ -141,7 +152,7 @@ const ColorPicker = React.forwardRef<HTMLButtonElement, ColorPickerProps>(
141
152
  aria-label={p}
142
153
  onClick={() => update(p)}
143
154
  className={cn(
144
- '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',
145
156
  'hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
146
157
  )}
147
158
  style={{ backgroundColor: p }}
@@ -2,15 +2,16 @@
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
- package: "@teamix-evo/ui"
7
+ package: '@teamix-evo/ui'
8
+ displayName: 命令面板
8
9
  ---
9
10
 
10
- # Command
11
+ # Command 命令面板
11
12
 
12
- 命令面板 — 基于 [`cmdk`](https://cmdk.paco.me/),提供"按 ⌘K 打开"的全局搜索 / 命令执行体验(Linear / Raycast / Vercel 风格)。
13
- **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 能力。
14
15
 
15
16
  ## When to use
16
17
 
@@ -26,10 +27,14 @@ package: "@teamix-evo/ui"
26
27
  - 静态菜单 → `DropdownMenu`
27
28
  - 需要表单语义 → `Combobox`(基于本组件)
28
29
 
30
+ ## Props
31
+
29
32
  <!-- auto:props:begin -->
30
33
  _(no props)_
31
34
  <!-- auto:props:end -->
32
35
 
36
+ ## 依赖
37
+
33
38
  <!-- auto:deps:begin -->
34
39
  ### 同库依赖
35
40
 
@@ -38,7 +43,7 @@ _(no props)_
38
43
  | Entry | 类型 | 描述 |
39
44
  | --- | --- | --- |
40
45
  | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
41
- | `dialog` | component | 模态对话框 — Radix Dialog + antd Modal 并集(组合式 Header/Footer/Title/Description) |
46
+ | `dialog` | component | 模态对话框 — Radix Dialog + antd Modal 并集(组合式 Header/Footer/Title/Description,size sm/md/lg/xl 绑定 layout-dialog tokens,圆角 radius-dialog 16px,无 border) |
42
47
 
43
48
  ### npm 依赖
44
49
 
@@ -63,8 +68,13 @@ pnpm add cmdk@^1.0.0 lucide-react@^0.460.0
63
68
 
64
69
  ```tsx
65
70
  import {
66
- Command, CommandInput, CommandList, CommandEmpty,
67
- CommandGroup, CommandItem, CommandShortcut,
71
+ Command,
72
+ CommandInput,
73
+ CommandList,
74
+ CommandEmpty,
75
+ CommandGroup,
76
+ CommandItem,
77
+ CommandShortcut,
68
78
  } from '@/components/ui/command';
69
79
  import { Calendar, Mail, User } from 'lucide-react';
70
80
 
@@ -74,14 +84,20 @@ import { Calendar, Mail, User } from 'lucide-react';
74
84
  <CommandList>
75
85
  <CommandEmpty>无结果。</CommandEmpty>
76
86
  <CommandGroup heading="建议">
77
- <CommandItem><Calendar /> 日历 <CommandShortcut>⌘K</CommandShortcut></CommandItem>
78
- <CommandItem><Mail /> 邮件</CommandItem>
87
+ <CommandItem>
88
+ <Calendar /> 日历 <CommandShortcut>⌘K</CommandShortcut>
89
+ </CommandItem>
90
+ <CommandItem>
91
+ <Mail /> 邮件
92
+ </CommandItem>
79
93
  </CommandGroup>
80
94
  <CommandGroup heading="设置">
81
- <CommandItem><User /> 个人资料</CommandItem>
95
+ <CommandItem>
96
+ <User /> 个人资料
97
+ </CommandItem>
82
98
  </CommandGroup>
83
99
  </CommandList>
84
- </Command>
100
+ </Command>;
85
101
 
86
102
  // 模态(全局 ⌘K)
87
103
  import { CommandDialog } from '@/components/ui/command';
@@ -100,5 +116,5 @@ React.useEffect(() => {
100
116
  <CommandDialog open={open} onOpenChange={setOpen}>
101
117
  <CommandInput placeholder="..." />
102
118
  <CommandList>...</CommandList>
103
- </CommandDialog>
119
+ </CommandDialog>;
104
120
  ```
@@ -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 全局触发。OpenTrek tokens 适配,无 mock。',
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}
@@ -60,7 +71,7 @@ const CommandList = React.forwardRef<
60
71
  >(({ className, ...props }, ref) => (
61
72
  <CommandPrimitive.List
62
73
  ref={ref}
63
- className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
74
+ className={cn('max-h-listbox overflow-y-auto overflow-x-hidden', className)}
64
75
  {...props}
65
76
  />
66
77
  ));
@@ -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,12 +2,13 @@
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
- package: "@teamix-evo/ui"
7
+ package: '@teamix-evo/ui'
8
+ displayName: 右键菜单
8
9
  ---
9
10
 
10
- # ContextMenu
11
+ # ContextMenu 右键菜单
11
12
 
12
13
  右键菜单 — Radix ContextMenu 完整实现。**与 DropdownMenu 形态一致**(Item / CheckboxItem / RadioItem / Sub / Label / Separator / Shortcut),仅触发方式不同(右键 vs click)。
13
14
  shadcn 显式版,antd 用 Dropdown 模拟同样可达。
@@ -54,8 +55,6 @@ pnpm add @radix-ui/react-context-menu@^2.2.0 lucide-react@^0.460.0
54
55
  ```
55
56
  <!-- auto:deps:end -->
56
57
 
57
- > 子组件与 DropdownMenu 完全一致(只差名称前缀):`ContextMenu / Trigger / Content / Item / CheckboxItem / RadioItem / Label / Separator / Shortcut / Sub / SubTrigger / SubContent / Group / RadioGroup / Portal`。
58
-
59
58
  ## AI 生成纪律
60
59
 
61
60
  - **Trigger 必有可见区域**:不能是空区域,否则无右键目标 — 通常 wrap 一个文本块 / 图片 / 卡片
@@ -67,8 +66,12 @@ pnpm add @radix-ui/react-context-menu@^2.2.0 lucide-react@^0.460.0
67
66
 
68
67
  ```tsx
69
68
  import {
70
- ContextMenu, ContextMenuTrigger, ContextMenuContent,
71
- ContextMenuItem, ContextMenuSeparator, ContextMenuShortcut,
69
+ ContextMenu,
70
+ ContextMenuTrigger,
71
+ ContextMenuContent,
72
+ ContextMenuItem,
73
+ ContextMenuSeparator,
74
+ ContextMenuShortcut,
72
75
  } from '@/components/ui/context-menu';
73
76
 
74
77
  <ContextMenu>
@@ -86,5 +89,5 @@ import {
86
89
  删除 <ContextMenuShortcut>⌫</ContextMenuShortcut>
87
90
  </ContextMenuItem>
88
91
  </ContextMenuContent>
89
- </ContextMenu>
92
+ </ContextMenu>;
90
93
  ```
@@ -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,9 +9,17 @@ 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
+ parameters: {
16
+ docs: {
17
+ description: {
18
+ component:
19
+ '⚠️ **已废弃** — 仅 Storybook 留档,不通过 manifest 对外发布。\n\n右键菜单 — 右键触发的操作菜单,与 DropdownMenu 形态一致(Item / CheckboxItem / RadioItem / Sub),仅触发方式不同。Radix ContextMenu 实现,shadcn 显式版,antd 用 Dropdown 模拟同样可达。',
20
+ },
21
+ },
22
+ },
15
23
  };
16
24
 
17
25
  export default meta;
@@ -20,7 +28,7 @@ type Story = StoryObj<typeof ContextMenuContent>;
20
28
  export const Default: Story = {
21
29
  render: () => (
22
30
  <ContextMenu>
23
- <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">
24
32
  在此处右键(或长按)打开菜单
25
33
  </ContextMenuTrigger>
26
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-[8rem] 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-[8rem] 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
  )}