@teamix-evo/ui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -184
- package/manifest.json +680 -492
- package/package.json +20 -10
- package/src/components/accordion/accordion.meta.md +5 -4
- package/src/components/accordion/accordion.stories.tsx +14 -9
- package/src/components/accordion/accordion.tsx +104 -8
- package/src/components/affix/affix.meta.md +20 -2
- package/src/components/affix/affix.stories.tsx +102 -25
- package/src/components/affix/affix.tsx +79 -9
- package/src/components/alert/alert.meta.md +44 -13
- package/src/components/alert/alert.stories.tsx +66 -21
- package/src/components/alert/alert.tsx +81 -34
- package/src/components/alert-dialog/alert-dialog.meta.md +61 -16
- package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
- package/src/components/alert-dialog/alert-dialog.tsx +60 -13
- package/src/components/anchor/anchor.meta.md +8 -3
- package/src/components/anchor/anchor.stories.tsx +3 -3
- package/src/components/anchor/anchor.tsx +2 -2
- package/src/components/app/app.meta.md +9 -4
- package/src/components/app/app.stories.tsx +9 -7
- package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
- package/src/components/auto-complete/auto-complete.meta.md +14 -6
- package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
- package/src/components/auto-complete/auto-complete.tsx +119 -71
- package/src/components/avatar/avatar.meta.md +6 -7
- package/src/components/avatar/avatar.stories.tsx +21 -3
- package/src/components/avatar/avatar.tsx +24 -23
- package/src/components/badge/badge.meta.md +10 -9
- package/src/components/badge/badge.stories.tsx +2 -2
- package/src/components/badge/badge.tsx +9 -15
- package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
- package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
- package/src/components/breadcrumb/breadcrumb.tsx +22 -8
- package/src/components/button/button.meta.md +258 -21
- package/src/components/button/button.stories.tsx +549 -41
- package/src/components/button/button.tsx +335 -33
- package/src/components/button/demo/as-child.tsx +24 -0
- package/src/components/button/demo/basic.tsx +8 -0
- package/src/components/button/demo/block.tsx +16 -0
- package/src/components/button/demo/loading.tsx +19 -0
- package/src/components/button/demo/shapes.tsx +18 -0
- package/src/components/button/demo/sizes.tsx +19 -0
- package/src/components/button/demo/variants.tsx +19 -0
- package/src/components/button/demo/with-icon.tsx +20 -0
- package/src/components/calendar/calendar.meta.md +13 -3
- package/src/components/calendar/calendar.stories.tsx +6 -6
- package/src/components/calendar/calendar.tsx +73 -8
- package/src/components/card/card.meta.md +27 -5
- package/src/components/card/card.stories.tsx +42 -3
- package/src/components/card/card.tsx +146 -63
- package/src/components/carousel/carousel.meta.md +4 -3
- package/src/components/carousel/carousel.stories.tsx +11 -6
- package/src/components/cascader/cascader.meta.md +47 -17
- package/src/components/cascader/cascader.stories.tsx +22 -10
- package/src/components/cascader/cascader.tsx +428 -85
- package/src/components/checkbox/checkbox.meta.md +75 -7
- package/src/components/checkbox/checkbox.stories.tsx +161 -3
- package/src/components/checkbox/checkbox.tsx +77 -9
- package/src/components/collapsible/collapsible.meta.md +14 -6
- package/src/components/collapsible/collapsible.stories.tsx +10 -2
- package/src/components/collapsible/collapsible.tsx +93 -6
- package/src/components/color-picker/color-picker.meta.md +12 -7
- package/src/components/color-picker/color-picker.stories.tsx +86 -7
- package/src/components/color-picker/color-picker.tsx +20 -9
- package/src/components/command/command.meta.md +29 -13
- package/src/components/command/command.stories.tsx +4 -4
- package/src/components/command/command.tsx +19 -8
- package/src/components/context-menu/context-menu.meta.md +11 -8
- package/src/components/context-menu/context-menu.stories.tsx +11 -3
- package/src/components/context-menu/context-menu.tsx +21 -8
- package/src/components/data-table/data-table.meta.md +6 -5
- package/src/components/data-table/data-table.stories.tsx +13 -6
- package/src/components/data-table/data-table.tsx +2 -2
- package/src/components/date-picker/date-picker.meta.md +88 -19
- package/src/components/date-picker/date-picker.stories.tsx +55 -5
- package/src/components/date-picker/date-picker.tsx +1489 -91
- package/src/components/descriptions/descriptions.meta.md +10 -5
- package/src/components/descriptions/descriptions.stories.tsx +3 -3
- package/src/components/descriptions/descriptions.tsx +22 -14
- package/src/components/dialog/dialog.meta.md +76 -13
- package/src/components/dialog/dialog.stories.tsx +182 -20
- package/src/components/dialog/dialog.tsx +67 -15
- package/src/components/dialog/imperative.tsx +252 -0
- package/src/components/drawer/drawer.meta.md +33 -34
- package/src/components/drawer/drawer.stories.tsx +29 -12
- package/src/components/drawer/drawer.tsx +22 -113
- package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
- package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
- package/src/components/ellipsis/ellipsis.meta.md +87 -0
- package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
- package/src/components/ellipsis/ellipsis.tsx +153 -0
- package/src/components/empty/empty.meta.md +9 -4
- package/src/components/empty/empty.stories.tsx +4 -4
- package/src/components/empty/empty.tsx +10 -3
- package/src/components/field/field.meta.md +47 -9
- package/src/components/field/field.stories.tsx +385 -5
- package/src/components/field/field.tsx +263 -35
- package/src/components/filter-bar/filter-bar.meta.md +92 -0
- package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
- package/src/components/filter-bar/filter-bar.tsx +568 -0
- package/src/components/flex/flex.meta.md +54 -6
- package/src/components/flex/flex.stories.tsx +107 -20
- package/src/components/flex/flex.tsx +27 -4
- package/src/components/float-button/float-button.meta.md +8 -3
- package/src/components/float-button/float-button.stories.tsx +9 -7
- package/src/components/float-button/float-button.tsx +1 -1
- package/src/components/form/form.meta.md +39 -17
- package/src/components/form/form.stories.tsx +350 -3
- package/src/components/form/form.tsx +101 -35
- package/src/components/grid/grid.meta.md +7 -2
- package/src/components/grid/grid.stories.tsx +6 -4
- package/src/components/hover-card/hover-card.meta.md +20 -9
- package/src/components/hover-card/hover-card.stories.tsx +34 -5
- package/src/components/hover-card/hover-card.tsx +51 -13
- package/src/components/icon/DEVELOPMENT.md +809 -0
- package/src/components/icon/icon.meta.md +170 -0
- package/src/components/icon/icon.stories.tsx +344 -0
- package/src/components/icon/icon.tsx +248 -0
- package/src/components/image/image.meta.md +9 -4
- package/src/components/image/image.stories.tsx +3 -3
- package/src/components/image/image.tsx +6 -4
- package/src/components/input/demo/basic.tsx +12 -0
- package/src/components/input/demo/clearable.tsx +21 -0
- package/src/components/input/demo/show-count.tsx +18 -0
- package/src/components/input/demo/sizes.tsx +15 -0
- package/src/components/input/input.meta.md +39 -33
- package/src/components/input/input.stories.tsx +62 -35
- package/src/components/input/input.tsx +97 -98
- package/src/components/input-group/input-group.meta.md +54 -22
- package/src/components/input-group/input-group.stories.tsx +49 -16
- package/src/components/input-group/input-group.tsx +44 -8
- package/src/components/input-number/input-number.meta.md +64 -7
- package/src/components/input-number/input-number.stories.tsx +46 -8
- package/src/components/input-number/input-number.tsx +99 -26
- package/src/components/input-otp/input-otp.meta.md +4 -3
- package/src/components/input-otp/input-otp.stories.tsx +3 -3
- package/src/components/input-otp/input-otp.tsx +1 -1
- package/src/components/item/item.meta.md +8 -3
- package/src/components/item/item.stories.tsx +8 -5
- package/src/components/item/item.tsx +7 -6
- package/src/components/kbd/kbd.meta.md +13 -4
- package/src/components/kbd/kbd.stories.tsx +4 -4
- package/src/components/kbd/kbd.tsx +10 -5
- package/src/components/label/label.meta.md +18 -10
- package/src/components/label/label.stories.tsx +64 -6
- package/src/components/label/label.tsx +91 -19
- package/src/components/masonry/masonry.meta.md +8 -3
- package/src/components/masonry/masonry.stories.tsx +7 -5
- package/src/components/masonry/masonry.tsx +1 -0
- package/src/components/mentions/mentions.meta.md +36 -6
- package/src/components/mentions/mentions.stories.tsx +120 -6
- package/src/components/mentions/mentions.tsx +11 -5
- package/src/components/menubar/menubar.meta.md +30 -12
- package/src/components/menubar/menubar.stories.tsx +62 -2
- package/src/components/menubar/menubar.tsx +9 -9
- package/src/components/native-select/native-select.meta.md +8 -3
- package/src/components/native-select/native-select.stories.tsx +8 -5
- package/src/components/native-select/native-select.tsx +1 -1
- package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
- package/src/components/navigation-menu/navigation-menu.tsx +8 -4
- package/src/components/notification/notification.meta.md +52 -10
- package/src/components/notification/notification.stories.tsx +11 -9
- package/src/components/notification/notification.tsx +36 -21
- package/src/components/page-header/DEVELOPMENT.md +842 -0
- package/src/components/page-header/page-header.meta.md +208 -0
- package/src/components/page-header/page-header.stories.tsx +421 -0
- package/src/components/page-header/page-header.tsx +281 -0
- package/src/components/pagination/pagination.meta.md +140 -37
- package/src/components/pagination/pagination.stories.tsx +232 -10
- package/src/components/pagination/pagination.tsx +355 -63
- package/src/components/popconfirm/popconfirm.meta.md +9 -4
- package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
- package/src/components/popconfirm/popconfirm.tsx +2 -2
- package/src/components/popover/popover.meta.md +62 -5
- package/src/components/popover/popover.stories.tsx +83 -7
- package/src/components/popover/popover.tsx +77 -28
- package/src/components/progress/progress.meta.md +38 -6
- package/src/components/progress/progress.stories.tsx +3 -3
- package/src/components/progress/progress.tsx +24 -16
- package/src/components/radio-group/radio-group.meta.md +79 -7
- package/src/components/radio-group/radio-group.stories.tsx +39 -3
- package/src/components/radio-group/radio-group.tsx +149 -18
- package/src/components/rate/rate.meta.md +35 -4
- package/src/components/rate/rate.stories.tsx +13 -5
- package/src/components/rate/rate.tsx +37 -10
- package/src/components/resizable/resizable.meta.md +7 -4
- package/src/components/resizable/resizable.stories.tsx +6 -6
- package/src/components/resizable/resizable.tsx +1 -1
- package/src/components/result/result.meta.md +7 -2
- package/src/components/result/result.stories.tsx +4 -8
- package/src/components/result/result.tsx +24 -15
- package/src/components/scroll-area/scroll-area.meta.md +4 -3
- package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
- package/src/components/scroll-area/scroll-area.tsx +3 -3
- package/src/components/segmented/segmented.meta.md +7 -4
- package/src/components/segmented/segmented.stories.tsx +37 -8
- package/src/components/segmented/segmented.tsx +15 -7
- package/src/components/select/select.meta.md +197 -52
- package/src/components/select/select.stories.tsx +238 -63
- package/src/components/select/select.tsx +718 -171
- package/src/components/separator/separator.meta.md +4 -3
- package/src/components/separator/separator.stories.tsx +3 -3
- package/src/components/separator/separator.tsx +3 -7
- package/src/components/sheet/sheet.meta.md +32 -16
- package/src/components/sheet/sheet.stories.tsx +116 -10
- package/src/components/sheet/sheet.tsx +116 -29
- package/src/components/sidebar/sidebar.meta.md +37 -18
- package/src/components/sidebar/sidebar.stories.tsx +701 -29
- package/src/components/sidebar/sidebar.tsx +615 -142
- package/src/components/skeleton/skeleton.meta.md +4 -5
- package/src/components/skeleton/skeleton.stories.tsx +4 -4
- package/src/components/skeleton/skeleton.tsx +7 -7
- package/src/components/slider/slider.meta.md +57 -5
- package/src/components/slider/slider.stories.tsx +58 -6
- package/src/components/slider/slider.tsx +154 -13
- package/src/components/sonner/sonner.meta.md +58 -7
- package/src/components/sonner/sonner.stories.tsx +78 -5
- package/src/components/sonner/sonner.tsx +137 -8
- package/src/components/spinner/spinner.meta.md +62 -13
- package/src/components/spinner/spinner.stories.tsx +66 -14
- package/src/components/spinner/spinner.tsx +111 -9
- package/src/components/statistic/statistic.meta.md +7 -2
- package/src/components/statistic/statistic.stories.tsx +3 -7
- package/src/components/statistic/statistic.tsx +5 -6
- package/src/components/steps/steps.meta.md +18 -4
- package/src/components/steps/steps.stories.tsx +43 -3
- package/src/components/steps/steps.tsx +15 -12
- package/src/components/switch/switch.meta.md +51 -5
- package/src/components/switch/switch.stories.tsx +6 -6
- package/src/components/switch/switch.tsx +109 -41
- package/src/components/table/table.meta.md +17 -6
- package/src/components/table/table.stories.tsx +10 -5
- package/src/components/table/table.tsx +4 -4
- package/src/components/tabs/tabs.meta.md +38 -25
- package/src/components/tabs/tabs.stories.tsx +111 -25
- package/src/components/tabs/tabs.tsx +125 -54
- package/src/components/tag/tag.meta.md +105 -40
- package/src/components/tag/tag.stories.tsx +189 -16
- package/src/components/tag/tag.tsx +222 -21
- package/src/components/textarea/textarea.meta.md +35 -19
- package/src/components/textarea/textarea.stories.tsx +32 -6
- package/src/components/textarea/textarea.tsx +33 -9
- package/src/components/time-picker/time-picker.meta.md +124 -32
- package/src/components/time-picker/time-picker.stories.tsx +85 -15
- package/src/components/time-picker/time-picker.tsx +913 -61
- package/src/components/timeline/timeline.meta.md +14 -6
- package/src/components/timeline/timeline.stories.tsx +37 -7
- package/src/components/timeline/timeline.tsx +35 -14
- package/src/components/toggle/toggle.meta.md +5 -4
- package/src/components/toggle/toggle.stories.tsx +4 -4
- package/src/components/toggle/toggle.tsx +4 -3
- package/src/components/toggle-group/toggle-group.meta.md +5 -4
- package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
- package/src/components/toggle-group/toggle-group.tsx +2 -2
- package/src/components/tooltip/tooltip.meta.md +55 -5
- package/src/components/tooltip/tooltip.stories.tsx +42 -5
- package/src/components/tooltip/tooltip.tsx +81 -21
- package/src/components/tour/tour.meta.md +9 -4
- package/src/components/tour/tour.stories.tsx +3 -3
- package/src/components/tour/tour.tsx +4 -4
- package/src/components/transfer/transfer.meta.md +11 -6
- package/src/components/transfer/transfer.stories.tsx +4 -8
- package/src/components/transfer/transfer.tsx +28 -21
- package/src/components/tree/tree.meta.md +63 -5
- package/src/components/tree/tree.stories.tsx +31 -12
- package/src/components/tree/tree.tsx +9 -8
- package/src/components/tree-select/tree-select.meta.md +59 -8
- package/src/components/tree-select/tree-select.stories.tsx +3 -3
- package/src/components/tree-select/tree-select.tsx +42 -7
- package/src/components/typography/typography.meta.md +61 -14
- package/src/components/typography/typography.stories.tsx +12 -11
- package/src/components/typography/typography.tsx +43 -28
- package/src/components/upload/upload.meta.md +49 -4
- package/src/components/upload/upload.stories.tsx +72 -12
- package/src/components/upload/upload.tsx +170 -37
- package/src/components/watermark/watermark.meta.md +7 -2
- package/src/components/watermark/watermark.stories.tsx +101 -9
- package/src/components/watermark/watermark.tsx +1 -0
- package/src/hooks/use-breakpoint.ts +117 -0
- package/src/hooks/use-debounce-callback.ts +52 -0
- package/src/hooks/use-mobile.ts +23 -0
- package/src/stories/theme-tokens.stories.tsx +747 -0
- package/src/utils/trigger-input.ts +53 -0
- package/src/components/button-group/button-group.meta.md +0 -92
- package/src/components/button-group/button-group.stories.tsx +0 -90
- package/src/components/button-group/button-group.tsx +0 -75
- package/src/components/combobox/combobox.meta.md +0 -93
- package/src/components/combobox/combobox.stories.tsx +0 -55
- package/src/components/combobox/combobox.tsx +0 -130
- package/src/components/space/space.meta.md +0 -94
- package/src/components/space/space.stories.tsx +0 -94
- package/src/components/space/space.tsx +0 -106
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: color-picker
|
|
3
3
|
name: ColorPicker
|
|
4
|
+
displayName: 颜色选择
|
|
4
5
|
type: component
|
|
5
|
-
category:
|
|
6
|
+
category: data-entry
|
|
6
7
|
since: 0.1.0
|
|
7
|
-
package:
|
|
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 |
|
|
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: '
|
|
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
|
-
'颜色选择
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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 = '
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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">
|
|
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)
|
|
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:
|
|
5
|
+
category: feedback
|
|
6
6
|
since: 0.1.0
|
|
7
|
-
package:
|
|
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 无对标)
|
|
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,
|
|
67
|
-
|
|
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
|
|
78
|
-
|
|
87
|
+
<CommandItem>
|
|
88
|
+
<Calendar /> 日历 <CommandShortcut>⌘K</CommandShortcut>
|
|
89
|
+
</CommandItem>
|
|
90
|
+
<CommandItem>
|
|
91
|
+
<Mail /> 邮件
|
|
92
|
+
</CommandItem>
|
|
79
93
|
</CommandGroup>
|
|
80
94
|
<CommandGroup heading="设置">
|
|
81
|
-
<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: '
|
|
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
|
-
'命令面板 — 全局搜索 /
|
|
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 = ({
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
5
|
+
category: deprecated
|
|
6
6
|
since: 0.1.0
|
|
7
|
-
package:
|
|
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,
|
|
71
|
-
|
|
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: '
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
153
|
+
'px-2 py-1.5 text-xs font-semibold text-foreground',
|
|
141
154
|
inset && 'pl-8',
|
|
142
155
|
className,
|
|
143
156
|
)}
|