@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,11 +1,19 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { Toaster, toast } from './sonner';
|
|
3
3
|
import { Button } from '@/components/button/button';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Toaster> = {
|
|
6
|
-
title: '
|
|
6
|
+
title: '反馈 · Feedback/Sonner',
|
|
7
7
|
component: Toaster,
|
|
8
8
|
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'Toast 轻提示 —— 函数式触发的悬浮反馈,基于 [`sonner`](https://sonner.emilkowal.ski/) 包装。能力是 antd `message`(简短行内) + `notification`(标题 + 描述 + 行动)的并集:覆盖 `success` / `error` / `warning` / `info` / `loading` 五种状态,支持 `toast()` / `toast.promise()` / `action`。视觉走 OpenTrek semantic tokens —— **无边框、纯 shadow 分界、面性(filled)状态图标**,所有样式来自 `@teamix-evo/tokens`,无 mock。',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
9
17
|
argTypes: {
|
|
10
18
|
position: {
|
|
11
19
|
control: 'select',
|
|
@@ -18,23 +26,27 @@ const meta: Meta<typeof Toaster> = {
|
|
|
18
26
|
'bottom-right',
|
|
19
27
|
],
|
|
20
28
|
},
|
|
21
|
-
richColors: { control: 'boolean' },
|
|
22
29
|
expand: { control: 'boolean' },
|
|
23
30
|
closeButton: { control: 'boolean' },
|
|
31
|
+
visibleToasts: { control: { type: 'number', min: 1, max: 9 } },
|
|
24
32
|
},
|
|
25
33
|
args: {
|
|
26
34
|
position: 'top-right',
|
|
27
|
-
richColors: true,
|
|
28
35
|
closeButton: true,
|
|
36
|
+
expand: false,
|
|
37
|
+
visibleToasts: 5,
|
|
29
38
|
},
|
|
30
39
|
};
|
|
31
40
|
|
|
32
41
|
export default meta;
|
|
33
42
|
type Story = StoryObj<typeof Toaster>;
|
|
34
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Playground —— 触发不同状态的 toast,观察无边框 + 实心图标的整体节奏。
|
|
46
|
+
*/
|
|
35
47
|
export const Playground: Story = {
|
|
36
48
|
render: (args) => (
|
|
37
|
-
<div className="flex flex-col gap-2">
|
|
49
|
+
<div className="flex flex-col items-start gap-2">
|
|
38
50
|
<Toaster {...args} />
|
|
39
51
|
<Button onClick={() => toast('已复制到剪贴板')}>普通</Button>
|
|
40
52
|
<Button
|
|
@@ -61,6 +73,20 @@ export const Playground: Story = {
|
|
|
61
73
|
>
|
|
62
74
|
Warning
|
|
63
75
|
</Button>
|
|
76
|
+
<Button
|
|
77
|
+
variant="outline"
|
|
78
|
+
onClick={() =>
|
|
79
|
+
toast.info('新功能上线', { description: '试试新版本的智能搜索。' })
|
|
80
|
+
}
|
|
81
|
+
>
|
|
82
|
+
Info
|
|
83
|
+
</Button>
|
|
84
|
+
<Button
|
|
85
|
+
variant="outline"
|
|
86
|
+
onClick={() => toast.loading('保存中...', { duration: 2000 })}
|
|
87
|
+
>
|
|
88
|
+
Loading
|
|
89
|
+
</Button>
|
|
64
90
|
<Button
|
|
65
91
|
variant="outline"
|
|
66
92
|
onClick={() =>
|
|
@@ -89,3 +115,50 @@ export const Playground: Story = {
|
|
|
89
115
|
</div>
|
|
90
116
|
),
|
|
91
117
|
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 五种语义状态 —— 一次性弹出全部状态,直观对比无边框 + 浅底 + 实心图标的视觉节奏。
|
|
121
|
+
*/
|
|
122
|
+
export const AllStates: Story = {
|
|
123
|
+
parameters: { controls: { disable: true } },
|
|
124
|
+
render: (args) => (
|
|
125
|
+
<div className="flex flex-col items-start gap-2">
|
|
126
|
+
<Toaster {...args} expand visibleToasts={9} />
|
|
127
|
+
<Button
|
|
128
|
+
onClick={() => {
|
|
129
|
+
toast.success('保存成功', { description: '所有更改已生效。' });
|
|
130
|
+
toast.error('提交失败', { description: '请检查网络后重试。' });
|
|
131
|
+
toast.warning('账号即将过期', { description: '请在 7 天内续费。' });
|
|
132
|
+
toast.info('新版本可用', { description: '建议升级到最新版本。' });
|
|
133
|
+
toast.loading('正在同步...', { duration: 6000 });
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
弹出全部 5 种状态
|
|
137
|
+
</Button>
|
|
138
|
+
</div>
|
|
139
|
+
),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 与 Action 行动按钮组合 —— 撤销 / 重试等可执行操作。
|
|
144
|
+
*/
|
|
145
|
+
export const WithAction: Story = {
|
|
146
|
+
parameters: { controls: { disable: true } },
|
|
147
|
+
render: (args) => (
|
|
148
|
+
<div className="flex flex-col items-start gap-2">
|
|
149
|
+
<Toaster {...args} />
|
|
150
|
+
<Button
|
|
151
|
+
onClick={() =>
|
|
152
|
+
toast('草稿已删除', {
|
|
153
|
+
action: {
|
|
154
|
+
label: '撤销',
|
|
155
|
+
onClick: () => toast.success('已恢复草稿'),
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
>
|
|
160
|
+
删除草稿(可撤销)
|
|
161
|
+
</Button>
|
|
162
|
+
</div>
|
|
163
|
+
),
|
|
164
|
+
};
|
|
@@ -1,33 +1,162 @@
|
|
|
1
1
|
import { Toaster as Sonner, type ToasterProps as SonnerProps } from 'sonner';
|
|
2
|
+
import {
|
|
3
|
+
CircleCheck,
|
|
4
|
+
CircleX,
|
|
5
|
+
Info,
|
|
6
|
+
Loader2,
|
|
7
|
+
TriangleAlert,
|
|
8
|
+
} from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
import { cn } from '@/utils/cn';
|
|
2
11
|
|
|
3
12
|
/**
|
|
4
13
|
* Toaster 容器 — 应用根挂一次,后续在任意地方调 `toast()` / `toast.success()` / `toast.error()` /
|
|
5
|
-
* `toast.warning()` / `toast.loading()` / `toast.promise()` 等触发 toast。
|
|
14
|
+
* `toast.warning()` / `toast.info()` / `toast.loading()` / `toast.promise()` 等触发 toast。
|
|
6
15
|
*
|
|
7
|
-
*
|
|
16
|
+
* 视觉走 design tokens —— **去边框、纯 shadow 分界、面性状态图标**:
|
|
17
|
+
* - 卡片:`border-0` + `shadow-lg`,卡片不再有线条切割感
|
|
18
|
+
* - 5 状态:`success` / `error` / `warning` / `info` / `loading` 都有对齐 OpenTrek 的配色与图标
|
|
19
|
+
* - 图标:`fill-* + text-*-foreground` 实心圆/三角面性风格(基于 lucide-react)
|
|
8
20
|
*
|
|
9
21
|
* 来自 antd `message` + `notification` 的并集 — sonner 的 toast API 已经覆盖了:
|
|
10
22
|
* - 短文字提示(对应 message)
|
|
11
23
|
* - 标题 + 描述(对应 notification)
|
|
12
24
|
* - 操作按钮(action)
|
|
13
|
-
* - Promise 状态切换(loading → success/error)
|
|
25
|
+
* - Promise 状态切换(loading → success / error)
|
|
14
26
|
*/
|
|
15
|
-
export interface ToasterProps
|
|
27
|
+
export interface ToasterProps
|
|
28
|
+
extends Omit<
|
|
29
|
+
SonnerProps,
|
|
30
|
+
| 'position'
|
|
31
|
+
| 'duration'
|
|
32
|
+
| 'visibleToasts'
|
|
33
|
+
| 'expand'
|
|
34
|
+
| 'richColors'
|
|
35
|
+
| 'closeButton'
|
|
36
|
+
| 'theme'
|
|
37
|
+
| 'dir'
|
|
38
|
+
> {
|
|
39
|
+
/**
|
|
40
|
+
* Toast 弹出位置(对齐 antd `message.config.placement`)。
|
|
41
|
+
* @default "top-right"
|
|
42
|
+
*/
|
|
43
|
+
position?: SonnerProps['position'];
|
|
44
|
+
/**
|
|
45
|
+
* 自动消失时长(ms)— 0 表示常驻不自动关闭。
|
|
46
|
+
* @default 4000
|
|
47
|
+
*/
|
|
48
|
+
duration?: number;
|
|
49
|
+
/**
|
|
50
|
+
* 同屏最大可见 toast 数(超出旧的自动收起)。
|
|
51
|
+
* @default 3
|
|
52
|
+
*/
|
|
53
|
+
visibleToasts?: number;
|
|
54
|
+
/**
|
|
55
|
+
* 鼠标 hover 时是否展开堆叠的 toast(否则保持折叠堆叠状)。
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
expand?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* 启用 sonner 自带的"丰富配色"(更高对比度的状态色背景);
|
|
61
|
+
* 关闭则走我们的视觉(对齐 OpenTrek tokens)。
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
richColors?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 每张 toast 是否显示关闭按钮(右上角 ×)。
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
closeButton?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* 主题 — `'light'` / `'dark'` / `'system'`(跟随 OS 偏好)。
|
|
72
|
+
* 通常无需手传,组件会跟随 `<html>` 上的 `.dark` class 自动切换。
|
|
73
|
+
*/
|
|
74
|
+
theme?: SonnerProps['theme'];
|
|
75
|
+
/**
|
|
76
|
+
* 文字方向 — `'ltr'` / `'rtl'` / `'auto'`。
|
|
77
|
+
* @default "auto"
|
|
78
|
+
*/
|
|
79
|
+
dir?: SonnerProps['dir'];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 面性状态图标 —— 通过 `fill-*` 给 SVG 根元素填充语义色,
|
|
84
|
+
* 再用 `text-*-foreground` 把 currentColor 设为对比色;
|
|
85
|
+
* lucide SVG 内部 `<circle>` / `<path>` 默认 `stroke="currentColor"`,
|
|
86
|
+
* 因此环线与勾叉自动呈现高对比 foreground 颜色,得到"色块底 + 白色记号"的实心视觉。
|
|
87
|
+
*/
|
|
88
|
+
const filledIcons = {
|
|
89
|
+
success: (
|
|
90
|
+
<CircleCheck
|
|
91
|
+
aria-hidden
|
|
92
|
+
className="size-5 fill-success text-success-foreground"
|
|
93
|
+
/>
|
|
94
|
+
),
|
|
95
|
+
error: (
|
|
96
|
+
<CircleX
|
|
97
|
+
aria-hidden
|
|
98
|
+
className="size-5 fill-destructive text-destructive-foreground"
|
|
99
|
+
/>
|
|
100
|
+
),
|
|
101
|
+
warning: (
|
|
102
|
+
<TriangleAlert
|
|
103
|
+
aria-hidden
|
|
104
|
+
className="size-5 fill-warning text-warning-foreground"
|
|
105
|
+
/>
|
|
106
|
+
),
|
|
107
|
+
info: <Info aria-hidden className="size-5 fill-info text-info-foreground" />,
|
|
108
|
+
loading: (
|
|
109
|
+
<Loader2
|
|
110
|
+
aria-hidden
|
|
111
|
+
className="size-5 animate-spin text-muted-foreground"
|
|
112
|
+
/>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
16
115
|
|
|
17
|
-
const Toaster = ({ ...props }: ToasterProps) => {
|
|
116
|
+
const Toaster = ({ className, toastOptions, ...props }: ToasterProps) => {
|
|
18
117
|
return (
|
|
19
118
|
<Sonner
|
|
20
119
|
theme="system"
|
|
21
|
-
className=
|
|
120
|
+
className={cn('toaster group', className)}
|
|
121
|
+
icons={filledIcons}
|
|
22
122
|
toastOptions={{
|
|
123
|
+
...toastOptions,
|
|
23
124
|
classNames: {
|
|
24
|
-
|
|
25
|
-
|
|
125
|
+
// 基础卡片:去边框,纯 shadow 分界,留出图标 / 文本节奏
|
|
126
|
+
toast: cn(
|
|
127
|
+
'group toast',
|
|
128
|
+
'group-[.toaster]:bg-background group-[.toaster]:text-foreground',
|
|
129
|
+
'group-[.toaster]:border-0 group-[.toaster]:shadow-lg',
|
|
130
|
+
'group-[.toaster]:rounded-md group-[.toaster]:px-4 group-[.toaster]:py-3',
|
|
131
|
+
'group-[.toaster]:gap-3',
|
|
132
|
+
),
|
|
133
|
+
// 状态色:每种状态对应"浅底 + 同色文字",图标已在 icons 内填充实心色块
|
|
134
|
+
success: cn(
|
|
135
|
+
'group-[.toaster]:bg-success-subtle group-[.toaster]:text-success',
|
|
136
|
+
),
|
|
137
|
+
error: cn(
|
|
138
|
+
'group-[.toaster]:bg-destructive/10 group-[.toaster]:text-destructive',
|
|
139
|
+
),
|
|
140
|
+
warning: cn(
|
|
141
|
+
'group-[.toaster]:bg-warning-subtle group-[.toaster]:text-warning',
|
|
142
|
+
),
|
|
143
|
+
info: cn(
|
|
144
|
+
'group-[.toaster]:bg-info-subtle group-[.toaster]:text-info',
|
|
145
|
+
),
|
|
146
|
+
loading: cn(
|
|
147
|
+
'group-[.toaster]:bg-muted group-[.toaster]:text-foreground',
|
|
148
|
+
),
|
|
149
|
+
// 默认 / 非状态 toast
|
|
150
|
+
default: cn(
|
|
151
|
+
'group-[.toaster]:bg-background group-[.toaster]:text-foreground',
|
|
152
|
+
),
|
|
26
153
|
description: 'group-[.toast]:text-muted-foreground',
|
|
27
154
|
actionButton:
|
|
28
155
|
'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
|
29
156
|
cancelButton:
|
|
30
157
|
'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
|
|
158
|
+
icon: 'group-[.toast]:shrink-0',
|
|
159
|
+
...(toastOptions?.classNames ?? {}),
|
|
31
160
|
},
|
|
32
161
|
}}
|
|
33
162
|
{...props}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: spinner
|
|
3
3
|
name: Spinner
|
|
4
|
+
displayName: 加载指示器
|
|
4
5
|
type: component
|
|
5
|
-
category:
|
|
6
|
+
category: feedback
|
|
6
7
|
since: 0.1.0
|
|
7
|
-
package:
|
|
8
|
+
package: '@teamix-evo/ui'
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# Spinner
|
|
11
|
+
# Spinner 加载指示器
|
|
11
12
|
|
|
12
|
-
旋转加载指示器 — shadcn 2025-10 新增,**等价 antd `Spin`**
|
|
13
|
+
旋转加载指示器 — shadcn 2025-10 新增,**等价 antd `Spin`** 能力并集。核心 `Spinner` 是行内 / 局部旋转指示;配套 `SpinnerOverlay` 实现 antd `<Spin>{children}</Spin>` 的包裹遮罩形态。
|
|
13
14
|
|
|
14
15
|
## When to use
|
|
15
16
|
|
|
16
17
|
- 按钮 / 表单内联 loading(`Button loading` 内部已用同一图标,这里独立 spinner 用于自定义触发器)
|
|
17
|
-
- 容器局部加载(请求中)
|
|
18
|
+
- 容器局部加载(请求中) — 需要遮罩时用 `SpinnerOverlay`
|
|
18
19
|
- 全屏 loading 占位(`size="xl"` + 居中)
|
|
19
20
|
|
|
20
21
|
## When NOT to use
|
|
@@ -23,14 +24,22 @@ package: "@teamix-evo/ui"
|
|
|
23
24
|
- 整页"模态阻塞 loading" → `Dialog` + Spinner 自行组合(本组件不内置遮罩)
|
|
24
25
|
- 进度可知场景 → `Progress`(线形)或 `ProgressCircle`(环形)
|
|
25
26
|
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
|
|
30
|
+
|
|
26
31
|
<!-- auto:props:begin -->
|
|
27
32
|
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
28
33
|
| --- | --- | --- | --- | --- |
|
|
29
|
-
| `size` | `'sm' \| 'default' \| 'lg' \| 'xl'` | `"default"` | – | 尺寸 — 与 antd Spin 的 small/middle/large 对齐(加 xl 用于全屏 loading)。 |
|
|
30
|
-
| `
|
|
34
|
+
| `size` | `'sm' \| 'md' \| 'default' \| 'lg' \| 'xl'` | `"default"` | – | 尺寸 — 与 antd Spin 的 small/middle/large 对齐(加 xl 用于全屏 loading)。 |
|
|
35
|
+
| `color` | `'default' \| 'muted' \| 'primary' \| 'destructive'` | `"default"` | – | 配色 — `muted` 走次级文本色;`primary` / `destructive` 走语义色。对齐 [ADR 0021](../../../../../docs/adr/0021-semantic-color-api-unification.md)。 |
|
|
31
36
|
| `label` | `string` | – | – | 给读屏器的描述(默认 "Loading...")。 |
|
|
32
37
|
<!-- auto:props:end -->
|
|
33
38
|
|
|
39
|
+
## 依赖
|
|
40
|
+
|
|
41
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
|
|
42
|
+
|
|
34
43
|
<!-- auto:deps:begin -->
|
|
35
44
|
### 同库依赖
|
|
36
45
|
|
|
@@ -52,25 +61,65 @@ pnpm add class-variance-authority@^0.7.0 lucide-react@^0.460.0
|
|
|
52
61
|
## AI 生成纪律
|
|
53
62
|
|
|
54
63
|
- **不要自己写 `<div className="animate-spin">`** — 用本组件,保留读屏可访问性
|
|
55
|
-
- **`
|
|
64
|
+
- **`color="primary"` 配主操作 loading**,`color="destructive"` 仅用于真正失败回退 — 不要乱用
|
|
56
65
|
- **不要给 spinner 加旋转字幕 / 进度数字** — 那是 Progress 的职责,语义不同
|
|
57
|
-
-
|
|
66
|
+
- **包裹遮罩场景请用 `SpinnerOverlay`** — 不要手写 `relative` + 遮罩 div,不要把遮罩逻辑塞进 `Spinner` 本体
|
|
67
|
+
- **使用 SpinnerOverlay 时,外层 `className` 应控制边框 / 背景 / 圆角** — 本组件只负责 loading 的遮罩与 spinner 居中
|
|
58
68
|
|
|
59
69
|
## Examples
|
|
60
70
|
|
|
61
71
|
```tsx
|
|
62
|
-
import { Spinner } from '@/components/ui/spinner';
|
|
72
|
+
import { Spinner, SpinnerOverlay } from '@/components/ui/spinner';
|
|
63
73
|
|
|
64
74
|
// 行内
|
|
65
|
-
<span>正在保存 <Spinner size="sm"
|
|
75
|
+
<span>正在保存 <Spinner size="sm" color="muted" /></span>
|
|
66
76
|
|
|
67
77
|
// 居中
|
|
68
78
|
<div className="flex h-40 items-center justify-center">
|
|
69
|
-
<Spinner size="lg"
|
|
79
|
+
<Spinner size="lg" color="primary" label="加载用户信息..." />
|
|
70
80
|
</div>
|
|
71
81
|
|
|
72
82
|
// 全屏
|
|
73
83
|
<div className="fixed inset-0 grid place-items-center bg-background/80 backdrop-blur-sm">
|
|
74
|
-
<Spinner size="xl"
|
|
84
|
+
<Spinner size="xl" color="primary" />
|
|
75
85
|
</div>
|
|
86
|
+
|
|
87
|
+
// 包裹子元遮罩(antd Spin wrap children 并集)
|
|
88
|
+
<SpinnerOverlay loading={isFetching} tip="加载中..." className="rounded-md border">
|
|
89
|
+
<Card>...</Card>
|
|
90
|
+
</SpinnerOverlay>
|
|
76
91
|
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Loading 形态 — 旧库 API → 新映射
|
|
96
|
+
|
|
97
|
+
> 旧库 `Loading`(hybridcloud) → 新库 `Spinner`(行内) + `SpinnerOverlay`(包裹遮罩)。
|
|
98
|
+
> 新库用 lucide `Loader2` + Tailwind `animate-spin`,视觉走 OpenTrek tokens。
|
|
99
|
+
|
|
100
|
+
### 命名 & 结构映射
|
|
101
|
+
|
|
102
|
+
| 旧库 | 新库 | 说明 |
|
|
103
|
+
| ----------------------- | ------------------------- | --------------------- |
|
|
104
|
+
| `Loading` (无 children) | `Spinner` | 行内旋转图标 |
|
|
105
|
+
| `Loading` (有 children) | `SpinnerOverlay` | 包裹遮罩加载 |
|
|
106
|
+
| `visible` | `loading` | prop 重命名 |
|
|
107
|
+
| `size: 'small'` | `size: 'sm'` | 尺寸缩写 |
|
|
108
|
+
| `size: 'medium'` | `size: 'md' \| 'default'` | md = medium = default |
|
|
109
|
+
| `size: 'large'` | `size: 'lg'` | 尺寸缩写 |
|
|
110
|
+
| — | `size: 'xl'` | 新增(全屏加载用) |
|
|
111
|
+
| `color='blue'` | `color='primary'` | 语义化 |
|
|
112
|
+
| `tip` | `tip` | 保留(SpinnerOverlay) |
|
|
113
|
+
| `tipAlign='right'` | 不复刻 | tip 固定下方 |
|
|
114
|
+
| `indicator` 自定义 | 不复刻 | 固定 Loader2 图标 |
|
|
115
|
+
| `fullScreen` | 不复刻 | 外层 div 自行组合 |
|
|
116
|
+
| `disableScroll` | 不复刻 | 自行 overflow-hidden |
|
|
117
|
+
|
|
118
|
+
### 迁移 FAQ
|
|
119
|
+
|
|
120
|
+
| 问题 | 回答 |
|
|
121
|
+
| ------------------ | ------------------------------------------------------------------------------------ |
|
|
122
|
+
| visible → loading? | prop 改名,语义相同(boolean) |
|
|
123
|
+
| 包裹加载怎么写? | `<SpinnerOverlay loading={flag}>{children}</SpinnerOverlay>` |
|
|
124
|
+
| 全屏加载怎么做? | `<div className="fixed inset-0 grid place-items-center"><Spinner size="xl" /></div>` |
|
|
125
|
+
| blur 效果? | `<SpinnerOverlay blur loading>...</SpinnerOverlay>` — 遮罩加模糊 |
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { Spinner } from './spinner';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Spinner, SpinnerOverlay } from './spinner';
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof Spinner> = {
|
|
5
|
-
title: '
|
|
5
|
+
title: '反馈 · Feedback/Spinner',
|
|
6
6
|
component: Spinner,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
parameters: {
|
|
9
9
|
docs: {
|
|
10
10
|
description: {
|
|
11
11
|
component:
|
|
12
|
-
'旋转加载指示器 — 局部 / 行内 loading
|
|
12
|
+
'旋转加载指示器 — 局部 / 行内 loading(请求中、按钮提交、占位等待)。基于 lucide Loader2,4 档尺寸 + 4 档配色,自带 role="status" 与 sr-only 读屏文案。配套 `SpinnerOverlay` 可实现 antd `<Spin>{children}</Spin>` 的包裹遮罩形态。',
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
argTypes: {
|
|
17
17
|
size: { control: 'inline-radio', options: ['sm', 'default', 'lg', 'xl'] },
|
|
18
|
-
|
|
18
|
+
color: {
|
|
19
19
|
control: 'inline-radio',
|
|
20
20
|
options: ['default', 'muted', 'primary', 'destructive'],
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
|
-
args: { size: 'default',
|
|
23
|
+
args: { size: 'default', color: 'default' },
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export default meta;
|
|
@@ -40,14 +40,14 @@ export const Sizes: Story = {
|
|
|
40
40
|
),
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
export const
|
|
43
|
+
export const Colors: Story = {
|
|
44
44
|
parameters: { controls: { disable: true } },
|
|
45
45
|
render: () => (
|
|
46
46
|
<div className="flex items-center gap-6">
|
|
47
|
-
<Spinner
|
|
48
|
-
<Spinner
|
|
49
|
-
<Spinner
|
|
50
|
-
<Spinner
|
|
47
|
+
<Spinner color="default" />
|
|
48
|
+
<Spinner color="muted" />
|
|
49
|
+
<Spinner color="primary" />
|
|
50
|
+
<Spinner color="destructive" />
|
|
51
51
|
</div>
|
|
52
52
|
),
|
|
53
53
|
};
|
|
@@ -56,7 +56,7 @@ export const Inline: Story = {
|
|
|
56
56
|
parameters: { controls: { disable: true } },
|
|
57
57
|
render: () => (
|
|
58
58
|
<p className="text-sm">
|
|
59
|
-
正在保存草稿 <Spinner size="sm"
|
|
59
|
+
正在保存草稿 <Spinner size="sm" color="muted" />
|
|
60
60
|
</p>
|
|
61
61
|
),
|
|
62
62
|
};
|
|
@@ -64,8 +64,60 @@ export const Inline: Story = {
|
|
|
64
64
|
export const Centered: Story = {
|
|
65
65
|
parameters: { controls: { disable: true } },
|
|
66
66
|
render: () => (
|
|
67
|
-
<div className="flex h-40 items-center justify-center rounded-md border">
|
|
68
|
-
<Spinner size="lg"
|
|
67
|
+
<div className="flex h-40 items-center justify-center rounded-md border border-border">
|
|
68
|
+
<Spinner size="lg" color="primary" />
|
|
69
69
|
</div>
|
|
70
70
|
),
|
|
71
71
|
};
|
|
72
|
+
|
|
73
|
+
// ─── SpinnerOverlay (wrap children) ─────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export const OverlayBasic: StoryObj<typeof SpinnerOverlay> = {
|
|
76
|
+
name: 'Overlay · 包裹子元遮罩',
|
|
77
|
+
parameters: { controls: { disable: true } },
|
|
78
|
+
render: () => (
|
|
79
|
+
<SpinnerOverlay tip="加载中…" className="w-80 rounded-md border border-border">
|
|
80
|
+
<div className="flex flex-col gap-2 p-6">
|
|
81
|
+
<div className="text-base font-semibold">项目名称</div>
|
|
82
|
+
<p className="text-sm text-muted-foreground">
|
|
83
|
+
内容被半透明遮罩覆盖,但仍然占据原有空间,加载结束后不会抖动。
|
|
84
|
+
</p>
|
|
85
|
+
<div className="text-xs text-muted-foreground">2 小时前·北京</div>
|
|
86
|
+
</div>
|
|
87
|
+
</SpinnerOverlay>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const OverlayBlur: StoryObj<typeof SpinnerOverlay> = {
|
|
92
|
+
name: 'Overlay · 高斯模糊',
|
|
93
|
+
parameters: { controls: { disable: true } },
|
|
94
|
+
render: () => (
|
|
95
|
+
<SpinnerOverlay
|
|
96
|
+
blur
|
|
97
|
+
tip="提交中…"
|
|
98
|
+
className="w-80 rounded-md border border-border"
|
|
99
|
+
>
|
|
100
|
+
<div className="flex flex-col gap-2 p-6">
|
|
101
|
+
<div className="text-base font-semibold">提交表单</div>
|
|
102
|
+
<p className="text-sm text-muted-foreground">
|
|
103
|
+
开启 blur 后,内容会轻微模糊,强化 “不可交互” 的视觉提示。
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
</SpinnerOverlay>
|
|
107
|
+
),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const OverlayInactive: StoryObj<typeof SpinnerOverlay> = {
|
|
111
|
+
name: 'Overlay · 未加载状态',
|
|
112
|
+
parameters: { controls: { disable: true } },
|
|
113
|
+
render: () => (
|
|
114
|
+
<SpinnerOverlay loading={false} className="w-80 rounded-md border border-border">
|
|
115
|
+
<div className="flex flex-col gap-2 p-6">
|
|
116
|
+
<div className="text-base font-semibold">加载完成</div>
|
|
117
|
+
<p className="text-sm text-muted-foreground">
|
|
118
|
+
loading=false 时仅渲染 children,零额外开销。
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
</SpinnerOverlay>
|
|
122
|
+
),
|
|
123
|
+
};
|