@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
|
@@ -8,18 +8,19 @@ const spinnerVariants = cva('inline-block shrink-0 animate-spin', {
|
|
|
8
8
|
variants: {
|
|
9
9
|
size: {
|
|
10
10
|
sm: 'size-3',
|
|
11
|
+
md: 'size-4',
|
|
11
12
|
default: 'size-4',
|
|
12
13
|
lg: 'size-6',
|
|
13
14
|
xl: 'size-8',
|
|
14
15
|
},
|
|
15
|
-
|
|
16
|
+
color: {
|
|
16
17
|
default: 'text-foreground',
|
|
17
18
|
muted: 'text-muted-foreground',
|
|
18
19
|
primary: 'text-primary',
|
|
19
20
|
destructive: 'text-destructive',
|
|
20
21
|
},
|
|
21
22
|
},
|
|
22
|
-
defaultVariants: { size: 'default',
|
|
23
|
+
defaultVariants: { size: 'default', color: 'default' },
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
export interface SpinnerProps
|
|
@@ -29,12 +30,12 @@ export interface SpinnerProps
|
|
|
29
30
|
* 尺寸 — 与 antd Spin 的 small/middle/large 对齐(加 xl 用于全屏 loading)。
|
|
30
31
|
* @default "default"
|
|
31
32
|
*/
|
|
32
|
-
size?: 'sm' | 'default' | 'lg' | 'xl';
|
|
33
|
+
size?: 'sm' | 'md' | 'default' | 'lg' | 'xl';
|
|
33
34
|
/**
|
|
34
|
-
* 配色 — `muted` 走次级文本色;`primary` / `destructive`
|
|
35
|
+
* 配色 — `muted` 走次级文本色;`primary` / `destructive` 走语义色。对齐 [ADR 0021](../../../../../docs/adr/0021-semantic-color-api-unification.md)。
|
|
35
36
|
* @default "default"
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
color?: 'default' | 'muted' | 'primary' | 'destructive';
|
|
38
39
|
/**
|
|
39
40
|
* 给读屏器的描述(默认 "Loading...")。
|
|
40
41
|
*/
|
|
@@ -43,10 +44,10 @@ export interface SpinnerProps
|
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* 简单旋转 loading — shadcn 2025-10 新增,等价 antd `Spin`(无遮罩版)。
|
|
46
|
-
* 需要"包裹内容显示遮罩 loading"
|
|
47
|
+
* 需要"包裹内容显示遮罩 loading"时使用配套的 `SpinnerOverlay`。
|
|
47
48
|
*/
|
|
48
49
|
const Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(
|
|
49
|
-
({ size,
|
|
50
|
+
({ size, color, label = 'Loading...', className, ...props }, ref) => (
|
|
50
51
|
<span
|
|
51
52
|
ref={ref}
|
|
52
53
|
role="status"
|
|
@@ -54,11 +55,112 @@ const Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(
|
|
|
54
55
|
className={cn('inline-flex items-center', className)}
|
|
55
56
|
{...props}
|
|
56
57
|
>
|
|
57
|
-
<Loader2
|
|
58
|
+
<Loader2
|
|
59
|
+
className={cn(spinnerVariants({ size, color }))}
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
/>
|
|
58
62
|
<span className="sr-only">{label}</span>
|
|
59
63
|
</span>
|
|
60
64
|
),
|
|
61
65
|
);
|
|
62
66
|
Spinner.displayName = 'Spinner';
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
// ─── SpinnerOverlay(antd `Spin` wrap children 并集)─────────────────────────
|
|
69
|
+
|
|
70
|
+
export interface SpinnerOverlayProps
|
|
71
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
72
|
+
/**
|
|
73
|
+
* 是否处于加载中 — `true` 时渲染半透明遮罩 + 居中 spinner;`false` 时仅渲染 children。
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
loading?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* 内层 spinner 尺寸(透传给 [`Spinner`](./spinner.tsx) 的 `size`)。
|
|
79
|
+
* @default "lg"
|
|
80
|
+
*/
|
|
81
|
+
size?: SpinnerProps['size'];
|
|
82
|
+
/**
|
|
83
|
+
* 内层 spinner 配色(透传给 [`Spinner`](./spinner.tsx) 的 `color`)。
|
|
84
|
+
* @default "primary"
|
|
85
|
+
*/
|
|
86
|
+
color?: SpinnerProps['color'];
|
|
87
|
+
/**
|
|
88
|
+
* 图标下方的提示文案(antd `Spin.tip` 并集);为空则只渲染 spinner。
|
|
89
|
+
*/
|
|
90
|
+
tip?: React.ReactNode;
|
|
91
|
+
/**
|
|
92
|
+
* 被包裹的内容 — 加载中时仍渲染但被遮罩覆盖,占据原有空间不抖动。
|
|
93
|
+
*/
|
|
94
|
+
children?: React.ReactNode;
|
|
95
|
+
/**
|
|
96
|
+
* 是否对内容做高斯模糊(配合半透明遮罩,营造 "内容被禁用" 视感)。
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
99
|
+
blur?: boolean;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 包裹内容的加载遮罩 — 等价 antd `<Spin>{children}</Spin>` 的 wrap children 形态。
|
|
104
|
+
* 加载中时:外层 `relative` 容器 + 半透明遮罩 + 居中 spinner + 可选 tip;
|
|
105
|
+
* 非加载中时:children 透传,无任何额外节点(零运行时开销)。
|
|
106
|
+
*
|
|
107
|
+
* 实现纪律:
|
|
108
|
+
* - 不内嵌别的容器样式 — 由消费方决定 Card / Section / 自定义边框
|
|
109
|
+
* - 遮罩用 `bg-background/60 backdrop-blur-[1px]` 走 OpenTrek tokens,无字面色
|
|
110
|
+
* - 加载中保留 children 在 DOM(`aria-busy="true"`),不做条件渲染避免布局抖动
|
|
111
|
+
*/
|
|
112
|
+
const SpinnerOverlay = React.forwardRef<HTMLDivElement, SpinnerOverlayProps>(
|
|
113
|
+
(
|
|
114
|
+
{
|
|
115
|
+
loading = true,
|
|
116
|
+
size = 'lg',
|
|
117
|
+
color = 'primary',
|
|
118
|
+
tip,
|
|
119
|
+
blur = false,
|
|
120
|
+
className,
|
|
121
|
+
children,
|
|
122
|
+
...props
|
|
123
|
+
},
|
|
124
|
+
ref,
|
|
125
|
+
) => {
|
|
126
|
+
if (!loading) {
|
|
127
|
+
return (
|
|
128
|
+
<div ref={ref} className={cn('relative', className)} {...props}>
|
|
129
|
+
{children}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return (
|
|
134
|
+
<div
|
|
135
|
+
ref={ref}
|
|
136
|
+
aria-busy="true"
|
|
137
|
+
className={cn('relative', className)}
|
|
138
|
+
{...props}
|
|
139
|
+
>
|
|
140
|
+
<div
|
|
141
|
+
aria-hidden="true"
|
|
142
|
+
className={cn(
|
|
143
|
+
'pointer-events-none transition-[filter,opacity] duration-200',
|
|
144
|
+
blur && 'opacity-60 blur-[1px]',
|
|
145
|
+
)}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</div>
|
|
149
|
+
<div
|
|
150
|
+
className={cn(
|
|
151
|
+
'absolute inset-0 z-10 flex flex-col items-center justify-center gap-2',
|
|
152
|
+
'bg-background/60 backdrop-blur-[1px]',
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
|
+
<Spinner size={size} color={color} />
|
|
156
|
+
{tip ? (
|
|
157
|
+
<span className="text-xs text-muted-foreground">{tip}</span>
|
|
158
|
+
) : null}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
SpinnerOverlay.displayName = 'SpinnerOverlay';
|
|
165
|
+
|
|
166
|
+
export { Spinner, SpinnerOverlay, spinnerVariants };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: statistic
|
|
3
3
|
name: Statistic
|
|
4
|
+
displayName: 统计数值
|
|
4
5
|
type: component
|
|
5
6
|
category: data-display
|
|
6
7
|
since: 0.1.0
|
|
7
|
-
package:
|
|
8
|
+
package: '@teamix-evo/ui'
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# Statistic
|
|
11
|
+
# Statistic 统计数值
|
|
11
12
|
|
|
12
13
|
数字展示 — antd 独有补足。**仪表板专用**:大字号数值 + 标题 + 前缀(¥/$ / 图标)+ 后缀(单位)+ 趋势(上 / 下,自动配色)+ 千分位 + 精度。
|
|
13
14
|
|
|
@@ -23,6 +24,8 @@ package: "@teamix-evo/ui"
|
|
|
23
24
|
- 表格内数字 → 直接 `<td className="tabular-nums">`
|
|
24
25
|
- 图表 → `Chart`(本仓暂未提供)
|
|
25
26
|
|
|
27
|
+
## Props
|
|
28
|
+
|
|
26
29
|
<!-- auto:props:begin -->
|
|
27
30
|
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
28
31
|
| --- | --- | --- | --- | --- |
|
|
@@ -37,6 +40,8 @@ package: "@teamix-evo/ui"
|
|
|
37
40
|
| `valueClassName` | `string` | – | – | 数值颜色覆盖(默认随 trend / 否则 foreground)。 |
|
|
38
41
|
<!-- auto:props:end -->
|
|
39
42
|
|
|
43
|
+
## 依赖
|
|
44
|
+
|
|
40
45
|
<!-- auto:deps:begin -->
|
|
41
46
|
### 同库依赖
|
|
42
47
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { ShoppingCart, Users } from 'lucide-react';
|
|
3
3
|
import { Statistic } from './statistic';
|
|
4
4
|
import { Card, CardContent } from '@/components/card/card';
|
|
@@ -11,7 +11,7 @@ const meta: Meta<typeof Statistic> = {
|
|
|
11
11
|
docs: {
|
|
12
12
|
description: {
|
|
13
13
|
component:
|
|
14
|
-
'数字展示 — 仪表板核心指标。大字号 + 千分位 + 精度 + 前后缀 +
|
|
14
|
+
'数字展示 — 仪表板核心指标。大字号 + 千分位 + 精度 + 前后缀 + 趋势(上 / 下,自动配色)。等价 antd Statistic。',
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
},
|
|
@@ -59,11 +59,7 @@ export const InCards: Story = {
|
|
|
59
59
|
</Card>
|
|
60
60
|
<Card>
|
|
61
61
|
<CardContent className="pt-6">
|
|
62
|
-
<Statistic
|
|
63
|
-
title="活跃用户"
|
|
64
|
-
value={12480}
|
|
65
|
-
prefix={<Users />}
|
|
66
|
-
/>
|
|
62
|
+
<Statistic title="活跃用户" value={12480} prefix={<Users />} />
|
|
67
63
|
</CardContent>
|
|
68
64
|
</Card>
|
|
69
65
|
</div>
|
|
@@ -33,8 +33,7 @@ function formatNumber(
|
|
|
33
33
|
precision?: number,
|
|
34
34
|
groupSeparator = true,
|
|
35
35
|
): string {
|
|
36
|
-
const fixed =
|
|
37
|
-
precision !== undefined ? v.toFixed(precision) : v.toString();
|
|
36
|
+
const fixed = precision !== undefined ? v.toFixed(precision) : v.toString();
|
|
38
37
|
if (!groupSeparator) return fixed;
|
|
39
38
|
const [intPart, decPart] = fixed.split('.');
|
|
40
39
|
const grouped = intPart!.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
@@ -42,7 +41,7 @@ function formatNumber(
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
const trendStyles = {
|
|
45
|
-
up: 'text-
|
|
44
|
+
up: 'text-success',
|
|
46
45
|
down: 'text-destructive',
|
|
47
46
|
none: '',
|
|
48
47
|
} as const;
|
|
@@ -77,7 +76,7 @@ const Statistic = React.forwardRef<HTMLDivElement, StatisticProps>(
|
|
|
77
76
|
{...props}
|
|
78
77
|
>
|
|
79
78
|
{title ? (
|
|
80
|
-
<div className="text-
|
|
79
|
+
<div className="text-xs text-muted-foreground">{title}</div>
|
|
81
80
|
) : null}
|
|
82
81
|
<div
|
|
83
82
|
className={cn(
|
|
@@ -91,12 +90,12 @@ const Statistic = React.forwardRef<HTMLDivElement, StatisticProps>(
|
|
|
91
90
|
) : null}
|
|
92
91
|
<span>{display}</span>
|
|
93
92
|
{suffix ? (
|
|
94
|
-
<span className="text-
|
|
93
|
+
<span className="text-xs font-normal text-muted-foreground">
|
|
95
94
|
{suffix}
|
|
96
95
|
</span>
|
|
97
96
|
) : null}
|
|
98
97
|
{TrendIcon ? (
|
|
99
|
-
<span className="inline-flex items-baseline gap-0.5 text-
|
|
98
|
+
<span className="inline-flex items-baseline gap-0.5 text-xs font-medium">
|
|
100
99
|
<TrendIcon className="size-3" />
|
|
101
100
|
{trendValue}
|
|
102
101
|
</span>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: steps
|
|
3
3
|
name: Steps
|
|
4
|
+
displayName: 步骤条
|
|
4
5
|
type: component
|
|
5
6
|
category: navigation
|
|
6
7
|
since: 0.1.0
|
|
7
|
-
package:
|
|
8
|
+
package: '@teamix-evo/ui'
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# Steps
|
|
11
|
+
# Steps 步骤条
|
|
11
12
|
|
|
12
13
|
步骤条 — antd 独有补足。引导用户完成多步流程(注册 / 下单 / 部署),清晰展示当前 / 已完成 / 待完成 / 错误状态。
|
|
13
14
|
|
|
@@ -23,6 +24,8 @@ package: "@teamix-evo/ui"
|
|
|
23
24
|
- 简单进度 → `Progress` / `ProgressCircle`
|
|
24
25
|
- 历史时间线 → 自定义 timeline(暂无组件)
|
|
25
26
|
|
|
27
|
+
## Props
|
|
28
|
+
|
|
26
29
|
<!-- auto:props:begin -->
|
|
27
30
|
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
28
31
|
| --- | --- | --- | --- | --- |
|
|
@@ -30,9 +33,11 @@ package: "@teamix-evo/ui"
|
|
|
30
33
|
| `current` | `number` | `0` | – | 当前步骤(0-based)。`current` 之前的为 finish,`current` 为 process,之后为 wait。 |
|
|
31
34
|
| `status` | `'process' \| 'error'` | `"process"` | – | 当前步骤的状态(只影响 `current` 那一步;`process` 默认,`error` 标红)。 |
|
|
32
35
|
| `direction` | `'horizontal' \| 'vertical'` | `"horizontal"` | – | 排列方向。 |
|
|
33
|
-
| `size` | `'sm' \| 'default'` | `"default"` | – | 尺寸。 |
|
|
36
|
+
| `size` | `'sm' \| 'md' \| 'default'` | `"default"` | – | 尺寸。 |
|
|
34
37
|
<!-- auto:props:end -->
|
|
35
38
|
|
|
39
|
+
## 依赖
|
|
40
|
+
|
|
36
41
|
<!-- auto:deps:begin -->
|
|
37
42
|
### 同库依赖
|
|
38
43
|
|
|
@@ -51,12 +56,21 @@ pnpm add lucide-react@^0.460.0
|
|
|
51
56
|
```
|
|
52
57
|
<!-- auto:deps:end -->
|
|
53
58
|
|
|
54
|
-
> `StepItem` 类型:`{ title; description?; status?: 'wait' | 'process' | 'finish' | 'error'; icon? }`。
|
|
59
|
+
> `StepItem` 类型:`{ title; description?; status?: 'wait' | 'process' | 'finish' | 'error' | 'disabled'; icon? }`。5 状态视觉区分:
|
|
60
|
+
>
|
|
61
|
+
> | 状态 | 圆点 | 连线 | 标题 | 交互 |
|
|
62
|
+
> | --- | --- | --- | --- | --- |
|
|
63
|
+
> | `wait` | 描边 + muted | border | muted | 可点击 |
|
|
64
|
+
> | `process` | primary 填充 + shadow | border | foreground + medium | 可点击 |
|
|
65
|
+
> | `finish` | primary 填充 + Check | primary | foreground | 可点击 |
|
|
66
|
+
> | `error` | destructive 填充 + X | border | destructive + medium | 可点击 |
|
|
67
|
+
> | `disabled` | muted + opacity-60 | border + opacity-60 | muted + opacity-60 | `pointer-events: none` + `aria-disabled` |
|
|
55
68
|
|
|
56
69
|
## AI 生成纪律
|
|
57
70
|
|
|
58
71
|
- **`current` 0-based**:第一步是 0,与数组索引一致
|
|
59
72
|
- **`status="error"` 仅当前步**:把整体状态置错;**不要**对历史已完成步标 error
|
|
73
|
+
- **单项 `disabled`**:用 `StepItem.status: 'disabled'` 表示该步不可点击(需升级 / 该路径不适用),视觉 muted 变深 + 60% 透明度 + 提示 `aria-disabled`
|
|
60
74
|
- **`description` 简短**:不超过 1 行,长说明放主区
|
|
61
75
|
- **vertical 用于侧栏**:横向用于宽屏 / 顶部
|
|
62
76
|
- **不要堆叠超过 6 步**:认知超载;> 6 步改用 Wizard 模式 + 折叠
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { Steps } from './steps';
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof Steps> = {
|
|
@@ -9,7 +9,7 @@ const meta: Meta<typeof Steps> = {
|
|
|
9
9
|
docs: {
|
|
10
10
|
description: {
|
|
11
11
|
component:
|
|
12
|
-
'步骤条 —
|
|
12
|
+
'Steps 步骤条 — 在多步流程(注册 / 下单 / 部署)中明示当前阶段与整体进度。**antd 独有补足**,shadcn 未提供对应原子;API 能力取 antd `Steps` 起点并保留准则需要的子集:`current` / `status` / `direction` / `size` / `StepItem.status`(`wait` / `process` / `finish` / `error` / `disabled` 5 状态) / `StepItem.icon`。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/tokens`,无 mock。',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
},
|
|
@@ -19,7 +19,12 @@ const meta: Meta<typeof Steps> = {
|
|
|
19
19
|
direction: { control: 'inline-radio', options: ['horizontal', 'vertical'] },
|
|
20
20
|
size: { control: 'inline-radio', options: ['sm', 'default'] },
|
|
21
21
|
},
|
|
22
|
-
args: {
|
|
22
|
+
args: {
|
|
23
|
+
current: 1,
|
|
24
|
+
status: 'process',
|
|
25
|
+
direction: 'horizontal',
|
|
26
|
+
size: 'default',
|
|
27
|
+
},
|
|
23
28
|
};
|
|
24
29
|
|
|
25
30
|
export default meta;
|
|
@@ -73,3 +78,38 @@ export const Small: Story = {
|
|
|
73
78
|
</div>
|
|
74
79
|
),
|
|
75
80
|
};
|
|
81
|
+
|
|
82
|
+
export const AllStatuses: Story = {
|
|
83
|
+
parameters: { controls: { disable: true } },
|
|
84
|
+
render: () => (
|
|
85
|
+
<div className="w-[720px]">
|
|
86
|
+
<Steps
|
|
87
|
+
current={1}
|
|
88
|
+
items={[
|
|
89
|
+
{ title: 'Finish', description: '已完成', status: 'finish' },
|
|
90
|
+
{ title: 'Process', description: '进行中', status: 'process' },
|
|
91
|
+
{ title: 'Wait', description: '等待中', status: 'wait' },
|
|
92
|
+
{ title: 'Error', description: '处理失败', status: 'error' },
|
|
93
|
+
{ title: 'Disabled', description: '不可点击', status: 'disabled' },
|
|
94
|
+
]}
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const DisabledStep: Story = {
|
|
101
|
+
parameters: { controls: { disable: true } },
|
|
102
|
+
render: () => (
|
|
103
|
+
<div className="w-[600px]">
|
|
104
|
+
<Steps
|
|
105
|
+
current={0}
|
|
106
|
+
items={[
|
|
107
|
+
{ title: '账户信息' },
|
|
108
|
+
{ title: '验证邮箱' },
|
|
109
|
+
{ title: '高级设置', status: 'disabled', description: '需升级后可用' },
|
|
110
|
+
{ title: '完成' },
|
|
111
|
+
]}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
),
|
|
115
|
+
};
|
|
@@ -3,14 +3,14 @@ import { Check, X } from 'lucide-react';
|
|
|
3
3
|
|
|
4
4
|
import { cn } from '@/utils/cn';
|
|
5
5
|
|
|
6
|
-
export type StepStatus = 'wait' | 'process' | 'finish' | 'error';
|
|
6
|
+
export type StepStatus = 'wait' | 'process' | 'finish' | 'error' | 'disabled';
|
|
7
7
|
|
|
8
8
|
export interface StepItem {
|
|
9
9
|
/** 步骤标题(必填)。 */
|
|
10
10
|
title: React.ReactNode;
|
|
11
11
|
/** 描述(可选)。 */
|
|
12
12
|
description?: React.ReactNode;
|
|
13
|
-
/** 自定义状态(覆盖父级根据 current 推导出的状态)
|
|
13
|
+
/** 自定义状态(覆盖父级根据 current 推导出的状态)。`disabled` 状态不响应点击。 */
|
|
14
14
|
status?: StepStatus;
|
|
15
15
|
/** 自定义图标(覆盖默认数字 / 勾选 / 错误)。 */
|
|
16
16
|
icon?: React.ReactNode;
|
|
@@ -38,7 +38,7 @@ export interface StepsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
38
38
|
* 尺寸。
|
|
39
39
|
* @default "default"
|
|
40
40
|
*/
|
|
41
|
-
size?: 'sm' | 'default';
|
|
41
|
+
size?: 'sm' | 'md' | 'default';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function statusFor(
|
|
@@ -55,9 +55,10 @@ function statusFor(
|
|
|
55
55
|
|
|
56
56
|
const dotColor: Record<StepStatus, string> = {
|
|
57
57
|
wait: 'border-border bg-background text-muted-foreground',
|
|
58
|
-
process: 'border-primary bg-primary text-primary-foreground',
|
|
58
|
+
process: 'border-primary bg-primary text-primary-foreground shadow-sm',
|
|
59
59
|
finish: 'border-primary bg-primary text-primary-foreground',
|
|
60
60
|
error: 'border-destructive bg-destructive text-destructive-foreground',
|
|
61
|
+
disabled: 'border-border bg-muted text-muted-foreground opacity-60',
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const lineColor: Record<StepStatus, string> = {
|
|
@@ -65,13 +66,15 @@ const lineColor: Record<StepStatus, string> = {
|
|
|
65
66
|
process: 'bg-border',
|
|
66
67
|
finish: 'bg-primary',
|
|
67
68
|
error: 'bg-border',
|
|
69
|
+
disabled: 'bg-border opacity-60',
|
|
68
70
|
};
|
|
69
71
|
|
|
70
72
|
const titleColor: Record<StepStatus, string> = {
|
|
71
73
|
wait: 'text-muted-foreground',
|
|
72
74
|
process: 'text-foreground font-medium',
|
|
73
75
|
finish: 'text-foreground',
|
|
74
|
-
error: 'text-destructive',
|
|
76
|
+
error: 'text-destructive font-medium',
|
|
77
|
+
disabled: 'text-muted-foreground opacity-60',
|
|
75
78
|
};
|
|
76
79
|
|
|
77
80
|
const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
@@ -81,14 +84,14 @@ const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
|
81
84
|
current = 0,
|
|
82
85
|
status = 'process',
|
|
83
86
|
direction = 'horizontal',
|
|
84
|
-
size = '
|
|
87
|
+
size = 'md',
|
|
85
88
|
className,
|
|
86
89
|
...props
|
|
87
90
|
},
|
|
88
91
|
ref,
|
|
89
92
|
) => {
|
|
90
93
|
const isVertical = direction === 'vertical';
|
|
91
|
-
const dotSize = size === 'sm' ? 'size-6 text-xs' : 'size-8 text-
|
|
94
|
+
const dotSize = size === 'sm' ? 'size-6 text-xs' : 'size-8 text-xs';
|
|
92
95
|
return (
|
|
93
96
|
<div
|
|
94
97
|
ref={ref}
|
|
@@ -102,6 +105,7 @@ const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
|
102
105
|
{items.map((it, i) => {
|
|
103
106
|
const s = statusFor(i, current, status, it.status);
|
|
104
107
|
const isLast = i === items.length - 1;
|
|
108
|
+
const isDisabled = s === 'disabled';
|
|
105
109
|
const icon =
|
|
106
110
|
it.icon ??
|
|
107
111
|
(s === 'finish' ? (
|
|
@@ -114,9 +118,11 @@ const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
|
114
118
|
return (
|
|
115
119
|
<div
|
|
116
120
|
key={i}
|
|
121
|
+
aria-disabled={isDisabled || undefined}
|
|
117
122
|
className={cn(
|
|
118
123
|
'flex flex-1',
|
|
119
124
|
isVertical ? 'flex-row gap-3' : 'flex-col gap-2',
|
|
125
|
+
isDisabled && 'pointer-events-none',
|
|
120
126
|
)}
|
|
121
127
|
>
|
|
122
128
|
<div
|
|
@@ -146,12 +152,9 @@ const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
|
146
152
|
) : null}
|
|
147
153
|
</div>
|
|
148
154
|
<div
|
|
149
|
-
className={cn(
|
|
150
|
-
'flex flex-col gap-0.5',
|
|
151
|
-
!isVertical && 'pr-3',
|
|
152
|
-
)}
|
|
155
|
+
className={cn('flex flex-col gap-0.5', !isVertical && 'pr-3')}
|
|
153
156
|
>
|
|
154
|
-
<div className={cn('text-
|
|
157
|
+
<div className={cn('text-xs', titleColor[s])}>{it.title}</div>
|
|
155
158
|
{it.description ? (
|
|
156
159
|
<div className="text-xs text-muted-foreground">
|
|
157
160
|
{it.description}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: switch
|
|
3
3
|
name: Switch
|
|
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
|
-
# Switch
|
|
11
|
+
# Switch 开关
|
|
11
12
|
|
|
12
13
|
开关 — Radix Switch + antd 的 `loading / checkedChildren / unCheckedChildren / size`。
|
|
13
14
|
|
|
@@ -28,10 +29,18 @@ package: "@teamix-evo/ui"
|
|
|
28
29
|
<!-- auto:props:begin -->
|
|
29
30
|
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
30
31
|
| --- | --- | --- | --- | --- |
|
|
32
|
+
| `checked` | `boolean` | – | – | 受控选中状态(等价 antd `checked`)。 与 `defaultChecked` 二选一,不同时传。 |
|
|
33
|
+
| `defaultChecked` | `boolean` | `false` | – | 非受控初始选中态(等价 antd `defaultChecked`)。 |
|
|
34
|
+
| `onCheckedChange` | `(checked: boolean) => void` | – | – | 选中状态变化回调 — Radix 命名,等价 antd `onChange(checked)`。 |
|
|
35
|
+
| `disabled` | `boolean` | `false` | – | 禁用整个 Switch(灰化 + 不响应交互)。`loading` 也会自动禁用。 |
|
|
36
|
+
| `required` | `boolean` | `false` | – | 表单必填 — 设置 HTML 原生 `required`,表单层校验依赖此值。 |
|
|
37
|
+
| `name` | `string` | – | – | 表单字段名(原生 `<input name>`,提交表单时使用)。 |
|
|
38
|
+
| `value` | `string` | – | – | 表单字段值(选中时随表单提交)。 |
|
|
39
|
+
| `id` | `string` | – | – | id 属性,配合 `<label htmlFor>` 关联标签。 |
|
|
31
40
|
| `loading` | `boolean` | `false` | – | 加载态 — 自动 `disabled`,thumb 上叠加自旋 spinner(antd 并集)。 |
|
|
32
41
|
| `checkedChildren` | `React.ReactNode` | – | – | 选中态显示的内文(antd `checkedChildren` 并集)。 |
|
|
33
42
|
| `unCheckedChildren` | `React.ReactNode` | – | – | 未选中态显示的内文(antd `unCheckedChildren` 并集)。 |
|
|
34
|
-
| `size` | `'sm' \| 'default'` | `"
|
|
43
|
+
| `size` | `'sm' \| 'md' \| 'default'` | `"md"` | – | 尺寸。`md` 与 `default` 完全等价,后者作为向后兼容别名保留;新代码请优先 使用 `md`,与 Button / Input / Checkbox / Radio 等表单原子组件的 size enum 一致。 |
|
|
35
44
|
<!-- auto:props:end -->
|
|
36
45
|
|
|
37
46
|
## 依赖
|
|
@@ -63,7 +72,11 @@ pnpm add @radix-ui/react-switch@^1.1.0 lucide-react@^0.460.0
|
|
|
63
72
|
- **`loading` 期间禁止操作**:已自动 `disabled`,**不要**手动叠加 disabled 影响 UX 一致
|
|
64
73
|
- **`checkedChildren` 文字 ≤ 2 个汉字**:仅"开/关"或"On/Off",过长会溢出胶囊
|
|
65
74
|
- **配 Label 无障碍**:Switch 不带可见文字时,必须用 `aria-label` 或外部 `<Label>` + `id` 关联
|
|
66
|
-
- **size
|
|
75
|
+
- **size 命名**:首选 `'sm' | 'md'`,与 Button / Input / Checkbox / Radio 等表单原子保持一致;`'default'` 作为向后兼容别名保留(等价 `'md'`)
|
|
76
|
+
- **size 对齐 cloud-design**:`sm` 外框 16x28(h-4 w-7) 对齐旧库 `small`,`md`/`default` 外框 24x44(h-6 w-11) 对齐旧库 `medium`,**不要随意改尺寸**(会与表单/表格里的密集布局打架)
|
|
77
|
+
- **内文模式 sm 限制**:`sm` 下内文使用 10px 字号(inline style,绕开 `no-arbitrary-tw-value`),实际可视宽度仅够容纳 1 个汉字(如"开/关");如需多字请用 `md` size
|
|
78
|
+
- **thumb 紧贴边缘规范**:开/关两态下 thumb 始终紧贴 track 一侧;有 `checkedChildren / unCheckedChildren` 时,文字 span 仅在 thumb 反向加 padding,thumb 与轨道边缘距离对称(checked: thumb 贴右,文字偏左 padding;unchecked: thumb 贴左,文字偏右 padding)
|
|
79
|
+
- **inner text 自适应宽度**:文字态下 track `min-w` 弹性,文字 span 通过 `flex-1` + 单侧 padding 撑开容器,**不要给 track 写死 w-\* 数值**
|
|
67
80
|
|
|
68
81
|
## Examples
|
|
69
82
|
|
|
@@ -89,4 +102,37 @@ const [on, setOn] = React.useState(false);
|
|
|
89
102
|
|
|
90
103
|
// 小尺寸
|
|
91
104
|
<Switch size="sm" />
|
|
105
|
+
|
|
106
|
+
// md(推荐写法,与 Button/Input/Checkbox 一致)
|
|
107
|
+
<Switch size="md" />
|
|
92
108
|
```
|
|
109
|
+
|
|
110
|
+
## Switch 形态 — 旧库 API → 新映射
|
|
111
|
+
|
|
112
|
+
> 决策:**不新增独立组件**,直接在 `@teamix-evo/ui Switch` 上承接 cloud-design `Switch` 的所有有效用法。本章节是从旧库平滑迁移的查表索引,与 [上面的 Props 表](#props) 互补。
|
|
113
|
+
|
|
114
|
+
### Props 映射
|
|
115
|
+
|
|
116
|
+
| cloud-design `Switch` | `@teamix-evo/ui Switch` | 说明 |
|
|
117
|
+
| --------------------------------------- | ------------------------------- | ------------------------------------------------- |
|
|
118
|
+
| `checked` / `defaultChecked` | 同名 | 透传 Radix `Switch.Root` |
|
|
119
|
+
| `onChange(checked, e)` | `onCheckedChange(checked)` | Radix 命名,签名仅返回 `boolean`(无 event 形参) |
|
|
120
|
+
| `disabled` | 同名 | 透传 |
|
|
121
|
+
| `size="small"` | `size="sm"` | 命名对齐(与 Button/Input/Checkbox 同 enum) |
|
|
122
|
+
| `size="medium"` | `size="md"` 或 `size="default"` | `md` 为新写法,`default` 作为兼容别名保留 |
|
|
123
|
+
| `loading` | 同名 | 自动 `disabled` + thumb 上叠 spinner(antd 并集) |
|
|
124
|
+
| `checkedChildren` / `unCheckedChildren` | 同名 | 透传,thumb 紧贴边缘 + 文字 span 单侧 padding 渲染 |
|
|
125
|
+
| `autoWidth`(默认 true) | 内置行为 | track `min-w` + 文字 span `flex-1`,无需 prop |
|
|
126
|
+
|
|
127
|
+
### 不修复 / 后续工序清单
|
|
128
|
+
|
|
129
|
+
- **视觉尺寸 / 颜色微调**(40→36 / 22→20 / 18→16 / `#D9D9D9` → `#E5E5E5`):涉及 `@teamix-evo/tokens` 包,本次不动,留作 tokens 包 MVP 工序
|
|
130
|
+
- **报告 §1/§3 的 "checkedChildren / unCheckedChildren 缺失" 描述过时**:实际本仓库自 0.1.0 起已完整实现(thumb 紧贴边缘 + 文字 span 单侧 padding + min-w 弹性宽度),无需补齐
|
|
131
|
+
- **报告 §6 的 size 命名 `'medium'` → `'md'` 描述部分过时**:本仓库实际命名是 `'sm' | 'default'`,本次新增 `'md'` 别名后与其他原子组件对齐
|
|
132
|
+
|
|
133
|
+
### Switch 专项 AI 生成纪律
|
|
134
|
+
|
|
135
|
+
- 写新代码请优先用 `size="md"`,与 Button / Input / Checkbox / Radio 一致;遗留 `size="default"` 仍兼容,**不要主动批量改动**(避免无意义 diff)
|
|
136
|
+
- `onCheckedChange` 签名只有 `(checked: boolean) => void`,**不要按旧库习惯**期待 `(checked, event)` 双参数
|
|
137
|
+
- 受控模式 `checked` + `onCheckedChange` 必须配对;只写 `checked` 不写回调会冻结状态
|
|
138
|
+
- 内文模式下 thumb / 文字的 padding 与 flex order 已在组件内固化,**不要在外层覆盖** `[&>span]:` 等 Tailwind 选择器
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { Switch } from './switch';
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof Switch> = {
|
|
5
|
-
title: '
|
|
5
|
+
title: '数据录入 · Data Entry/Switch',
|
|
6
6
|
component: Switch,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
parameters: {
|
|
9
9
|
docs: {
|
|
10
10
|
description: {
|
|
11
11
|
component:
|
|
12
|
-
'开关 — 在开 / 关两种状态间切换,适用于需要即时生效的二值设置。Radix Switch 实现 + antd Switch 的并集能力:支持 `size` (sm / default)、`loading` 异步状态、以及 `checkedChildren` / `unCheckedChildren` 在轨道内渲染状态文本(对齐 antd
|
|
12
|
+
'开关 — 在开 / 关两种状态间切换,适用于需要即时生效的二值设置。Radix Switch 实现 + antd Switch 的并集能力:支持 `size` (sm / default)、`loading` 异步状态、以及 `checkedChildren` / `unCheckedChildren` 在轨道内渲染状态文本(对齐 antd)。',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
argTypes: {
|
|
17
|
-
size: { control: 'inline-radio', options: ['sm', '
|
|
17
|
+
size: { control: 'inline-radio', options: ['sm', 'md'] },
|
|
18
18
|
loading: { control: 'boolean' },
|
|
19
19
|
disabled: { control: 'boolean' },
|
|
20
20
|
checkedChildren: { control: 'text' },
|
|
21
21
|
unCheckedChildren: { control: 'text' },
|
|
22
22
|
},
|
|
23
|
-
args: { size: '
|
|
23
|
+
args: { size: 'md', loading: false, disabled: false },
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export default meta;
|
|
@@ -33,7 +33,7 @@ export const Sizes: Story = {
|
|
|
33
33
|
render: () => (
|
|
34
34
|
<div className="flex items-center gap-4">
|
|
35
35
|
<Switch size="sm" defaultChecked />
|
|
36
|
-
<Switch size="
|
|
36
|
+
<Switch size="md" defaultChecked />
|
|
37
37
|
</div>
|
|
38
38
|
),
|
|
39
39
|
};
|