@teamix-evo/ui 0.1.1
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/LICENSE +21 -0
- package/README.md +336 -0
- package/_data.json +12 -0
- package/manifest.json +1688 -0
- package/package.json +90 -0
- package/src/components/accordion/accordion.meta.md +87 -0
- package/src/components/accordion/accordion.stories.tsx +67 -0
- package/src/components/accordion/accordion.tsx +58 -0
- package/src/components/affix/affix.meta.md +80 -0
- package/src/components/affix/affix.stories.tsx +57 -0
- package/src/components/affix/affix.tsx +97 -0
- package/src/components/alert/alert.meta.md +101 -0
- package/src/components/alert/alert.stories.tsx +93 -0
- package/src/components/alert/alert.tsx +132 -0
- package/src/components/alert-dialog/alert-dialog.meta.md +107 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +81 -0
- package/src/components/alert-dialog/alert-dialog.tsx +136 -0
- package/src/components/anchor/anchor.meta.md +87 -0
- package/src/components/anchor/anchor.stories.tsx +74 -0
- package/src/components/anchor/anchor.tsx +130 -0
- package/src/components/app/app.meta.md +86 -0
- package/src/components/app/app.stories.tsx +62 -0
- package/src/components/app/app.tsx +58 -0
- package/src/components/aspect-ratio/aspect-ratio.meta.md +81 -0
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +59 -0
- package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
- package/src/components/auto-complete/auto-complete.meta.md +102 -0
- package/src/components/auto-complete/auto-complete.stories.tsx +93 -0
- package/src/components/auto-complete/auto-complete.tsx +205 -0
- package/src/components/avatar/avatar.meta.md +94 -0
- package/src/components/avatar/avatar.stories.tsx +80 -0
- package/src/components/avatar/avatar.tsx +126 -0
- package/src/components/badge/badge.meta.md +119 -0
- package/src/components/badge/badge.stories.tsx +153 -0
- package/src/components/badge/badge.tsx +210 -0
- package/src/components/breadcrumb/breadcrumb.meta.md +107 -0
- package/src/components/breadcrumb/breadcrumb.stories.tsx +84 -0
- package/src/components/breadcrumb/breadcrumb.tsx +122 -0
- package/src/components/button/button.meta.md +98 -0
- package/src/components/button/button.stories.tsx +235 -0
- package/src/components/button/button.tsx +160 -0
- package/src/components/button-group/button-group.meta.md +92 -0
- package/src/components/button-group/button-group.stories.tsx +90 -0
- package/src/components/button-group/button-group.tsx +75 -0
- package/src/components/calendar/calendar.meta.md +118 -0
- package/src/components/calendar/calendar.stories.tsx +68 -0
- package/src/components/calendar/calendar.tsx +107 -0
- package/src/components/card/card.meta.md +117 -0
- package/src/components/card/card.stories.tsx +112 -0
- package/src/components/card/card.tsx +222 -0
- package/src/components/carousel/carousel.meta.md +117 -0
- package/src/components/carousel/carousel.stories.tsx +84 -0
- package/src/components/carousel/carousel.tsx +224 -0
- package/src/components/cascader/cascader.meta.md +110 -0
- package/src/components/cascader/cascader.stories.tsx +108 -0
- package/src/components/cascader/cascader.tsx +198 -0
- package/src/components/checkbox/checkbox.meta.md +99 -0
- package/src/components/checkbox/checkbox.stories.tsx +130 -0
- package/src/components/checkbox/checkbox.tsx +125 -0
- package/src/components/collapsible/collapsible.meta.md +80 -0
- package/src/components/collapsible/collapsible.stories.tsx +35 -0
- package/src/components/collapsible/collapsible.tsx +18 -0
- package/src/components/color-picker/color-picker.meta.md +84 -0
- package/src/components/color-picker/color-picker.stories.tsx +80 -0
- package/src/components/color-picker/color-picker.tsx +160 -0
- package/src/components/combobox/combobox.meta.md +93 -0
- package/src/components/combobox/combobox.stories.tsx +55 -0
- package/src/components/combobox/combobox.tsx +130 -0
- package/src/components/command/command.meta.md +104 -0
- package/src/components/command/command.stories.tsx +59 -0
- package/src/components/command/command.tsx +147 -0
- package/src/components/context-menu/context-menu.meta.md +90 -0
- package/src/components/context-menu/context-menu.stories.tsx +46 -0
- package/src/components/context-menu/context-menu.tsx +191 -0
- package/src/components/data-table/data-table.meta.md +149 -0
- package/src/components/data-table/data-table.stories.tsx +125 -0
- package/src/components/data-table/data-table.tsx +185 -0
- package/src/components/date-picker/date-picker.meta.md +106 -0
- package/src/components/date-picker/date-picker.stories.tsx +58 -0
- package/src/components/date-picker/date-picker.tsx +156 -0
- package/src/components/descriptions/descriptions.meta.md +78 -0
- package/src/components/descriptions/descriptions.stories.tsx +60 -0
- package/src/components/descriptions/descriptions.tsx +129 -0
- package/src/components/dialog/dialog.meta.md +105 -0
- package/src/components/dialog/dialog.stories.tsx +93 -0
- package/src/components/dialog/dialog.tsx +128 -0
- package/src/components/drawer/drawer.meta.md +96 -0
- package/src/components/drawer/drawer.stories.tsx +54 -0
- package/src/components/drawer/drawer.tsx +114 -0
- package/src/components/dropdown-menu/dropdown-menu.meta.md +103 -0
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +112 -0
- package/src/components/dropdown-menu/dropdown-menu.tsx +195 -0
- package/src/components/empty/empty.meta.md +81 -0
- package/src/components/empty/empty.stories.tsx +46 -0
- package/src/components/empty/empty.tsx +47 -0
- package/src/components/field/field.meta.md +116 -0
- package/src/components/field/field.stories.tsx +117 -0
- package/src/components/field/field.tsx +164 -0
- package/src/components/flex/flex.meta.md +94 -0
- package/src/components/flex/flex.stories.tsx +112 -0
- package/src/components/flex/flex.tsx +122 -0
- package/src/components/float-button/float-button.meta.md +87 -0
- package/src/components/float-button/float-button.stories.tsx +78 -0
- package/src/components/float-button/float-button.tsx +143 -0
- package/src/components/form/form.meta.md +131 -0
- package/src/components/form/form.stories.tsx +122 -0
- package/src/components/form/form.tsx +194 -0
- package/src/components/grid/grid.meta.md +87 -0
- package/src/components/grid/grid.stories.tsx +99 -0
- package/src/components/grid/grid.tsx +130 -0
- package/src/components/hover-card/hover-card.meta.md +92 -0
- package/src/components/hover-card/hover-card.stories.tsx +68 -0
- package/src/components/hover-card/hover-card.tsx +29 -0
- package/src/components/image/image.meta.md +94 -0
- package/src/components/image/image.stories.tsx +55 -0
- package/src/components/image/image.tsx +138 -0
- package/src/components/input/input.meta.md +109 -0
- package/src/components/input/input.stories.tsx +117 -0
- package/src/components/input/input.tsx +213 -0
- package/src/components/input-group/input-group.meta.md +92 -0
- package/src/components/input-group/input-group.stories.tsx +88 -0
- package/src/components/input-group/input-group.tsx +107 -0
- package/src/components/input-number/input-number.meta.md +91 -0
- package/src/components/input-number/input-number.stories.tsx +87 -0
- package/src/components/input-number/input-number.tsx +210 -0
- package/src/components/input-otp/input-otp.meta.md +105 -0
- package/src/components/input-otp/input-otp.stories.tsx +65 -0
- package/src/components/input-otp/input-otp.tsx +97 -0
- package/src/components/item/item.meta.md +116 -0
- package/src/components/item/item.stories.tsx +113 -0
- package/src/components/item/item.tsx +171 -0
- package/src/components/kbd/kbd.meta.md +85 -0
- package/src/components/kbd/kbd.stories.tsx +70 -0
- package/src/components/kbd/kbd.tsx +81 -0
- package/src/components/label/label.meta.md +91 -0
- package/src/components/label/label.stories.tsx +87 -0
- package/src/components/label/label.tsx +66 -0
- package/src/components/masonry/masonry.meta.md +85 -0
- package/src/components/masonry/masonry.stories.tsx +66 -0
- package/src/components/masonry/masonry.tsx +59 -0
- package/src/components/mentions/mentions.meta.md +89 -0
- package/src/components/mentions/mentions.stories.tsx +75 -0
- package/src/components/mentions/mentions.tsx +237 -0
- package/src/components/menubar/menubar.meta.md +100 -0
- package/src/components/menubar/menubar.stories.tsx +81 -0
- package/src/components/menubar/menubar.tsx +232 -0
- package/src/components/native-select/native-select.meta.md +88 -0
- package/src/components/native-select/native-select.stories.tsx +80 -0
- package/src/components/native-select/native-select.tsx +54 -0
- package/src/components/navigation-menu/navigation-menu.meta.md +108 -0
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -0
- package/src/components/navigation-menu/navigation-menu.tsx +125 -0
- package/src/components/notification/notification.meta.md +91 -0
- package/src/components/notification/notification.stories.tsx +96 -0
- package/src/components/notification/notification.tsx +84 -0
- package/src/components/pagination/pagination.meta.md +127 -0
- package/src/components/pagination/pagination.stories.tsx +62 -0
- package/src/components/pagination/pagination.tsx +285 -0
- package/src/components/popconfirm/popconfirm.meta.md +109 -0
- package/src/components/popconfirm/popconfirm.stories.tsx +76 -0
- package/src/components/popconfirm/popconfirm.tsx +134 -0
- package/src/components/popover/popover.meta.md +97 -0
- package/src/components/popover/popover.stories.tsx +82 -0
- package/src/components/popover/popover.tsx +55 -0
- package/src/components/progress/progress.meta.md +86 -0
- package/src/components/progress/progress.stories.tsx +75 -0
- package/src/components/progress/progress.tsx +195 -0
- package/src/components/radio-group/radio-group.meta.md +103 -0
- package/src/components/radio-group/radio-group.stories.tsx +77 -0
- package/src/components/radio-group/radio-group.tsx +78 -0
- package/src/components/rate/rate.meta.md +87 -0
- package/src/components/rate/rate.stories.tsx +81 -0
- package/src/components/rate/rate.tsx +153 -0
- package/src/components/resizable/resizable.meta.md +92 -0
- package/src/components/resizable/resizable.stories.tsx +104 -0
- package/src/components/resizable/resizable.tsx +56 -0
- package/src/components/result/result.meta.md +90 -0
- package/src/components/result/result.stories.tsx +71 -0
- package/src/components/result/result.tsx +91 -0
- package/src/components/scroll-area/scroll-area.meta.md +84 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +41 -0
- package/src/components/scroll-area/scroll-area.tsx +51 -0
- package/src/components/segmented/segmented.meta.md +103 -0
- package/src/components/segmented/segmented.stories.tsx +101 -0
- package/src/components/segmented/segmented.tsx +138 -0
- package/src/components/select/select.meta.md +110 -0
- package/src/components/select/select.stories.tsx +100 -0
- package/src/components/select/select.tsx +188 -0
- package/src/components/separator/separator.meta.md +74 -0
- package/src/components/separator/separator.stories.tsx +71 -0
- package/src/components/separator/separator.tsx +104 -0
- package/src/components/sheet/sheet.meta.md +97 -0
- package/src/components/sheet/sheet.stories.tsx +82 -0
- package/src/components/sheet/sheet.tsx +139 -0
- package/src/components/sidebar/sidebar.meta.md +131 -0
- package/src/components/sidebar/sidebar.stories.tsx +82 -0
- package/src/components/sidebar/sidebar.tsx +351 -0
- package/src/components/skeleton/skeleton.meta.md +95 -0
- package/src/components/skeleton/skeleton.stories.tsx +79 -0
- package/src/components/skeleton/skeleton.tsx +144 -0
- package/src/components/slider/slider.meta.md +94 -0
- package/src/components/slider/slider.stories.tsx +69 -0
- package/src/components/slider/slider.tsx +86 -0
- package/src/components/sonner/sonner.meta.md +96 -0
- package/src/components/sonner/sonner.stories.tsx +91 -0
- package/src/components/sonner/sonner.tsx +40 -0
- package/src/components/space/space.meta.md +94 -0
- package/src/components/space/space.stories.tsx +94 -0
- package/src/components/space/space.tsx +106 -0
- package/src/components/spinner/spinner.meta.md +76 -0
- package/src/components/spinner/spinner.stories.tsx +71 -0
- package/src/components/spinner/spinner.tsx +64 -0
- package/src/components/statistic/statistic.meta.md +99 -0
- package/src/components/statistic/statistic.stories.tsx +71 -0
- package/src/components/statistic/statistic.tsx +197 -0
- package/src/components/steps/steps.meta.md +102 -0
- package/src/components/steps/steps.stories.tsx +75 -0
- package/src/components/steps/steps.tsx +170 -0
- package/src/components/switch/switch.meta.md +92 -0
- package/src/components/switch/switch.stories.tsx +75 -0
- package/src/components/switch/switch.tsx +101 -0
- package/src/components/table/table.meta.md +95 -0
- package/src/components/table/table.stories.tsx +75 -0
- package/src/components/table/table.tsx +122 -0
- package/src/components/tabs/tabs.meta.md +98 -0
- package/src/components/tabs/tabs.stories.tsx +70 -0
- package/src/components/tabs/tabs.tsx +119 -0
- package/src/components/tag/tag.meta.md +94 -0
- package/src/components/tag/tag.stories.tsx +77 -0
- package/src/components/tag/tag.tsx +185 -0
- package/src/components/textarea/textarea.meta.md +83 -0
- package/src/components/textarea/textarea.stories.tsx +63 -0
- package/src/components/textarea/textarea.tsx +113 -0
- package/src/components/time-picker/time-picker.meta.md +83 -0
- package/src/components/time-picker/time-picker.stories.tsx +59 -0
- package/src/components/time-picker/time-picker.tsx +94 -0
- package/src/components/timeline/timeline.meta.md +102 -0
- package/src/components/timeline/timeline.stories.tsx +104 -0
- package/src/components/timeline/timeline.tsx +147 -0
- package/src/components/toggle/toggle.meta.md +88 -0
- package/src/components/toggle/toggle.stories.tsx +66 -0
- package/src/components/toggle/toggle.tsx +53 -0
- package/src/components/toggle-group/toggle-group.meta.md +90 -0
- package/src/components/toggle-group/toggle-group.stories.tsx +83 -0
- package/src/components/toggle-group/toggle-group.tsx +78 -0
- package/src/components/tooltip/tooltip.meta.md +99 -0
- package/src/components/tooltip/tooltip.stories.tsx +71 -0
- package/src/components/tooltip/tooltip.tsx +93 -0
- package/src/components/tour/tour.meta.md +116 -0
- package/src/components/tour/tour.stories.tsx +66 -0
- package/src/components/tour/tour.tsx +242 -0
- package/src/components/transfer/transfer.meta.md +90 -0
- package/src/components/transfer/transfer.stories.tsx +68 -0
- package/src/components/transfer/transfer.tsx +251 -0
- package/src/components/tree/tree.meta.md +111 -0
- package/src/components/tree/tree.stories.tsx +109 -0
- package/src/components/tree/tree.tsx +367 -0
- package/src/components/tree-select/tree-select.meta.md +100 -0
- package/src/components/tree-select/tree-select.stories.tsx +80 -0
- package/src/components/tree-select/tree-select.tsx +171 -0
- package/src/components/typography/typography.meta.md +102 -0
- package/src/components/typography/typography.stories.tsx +115 -0
- package/src/components/typography/typography.tsx +245 -0
- package/src/components/upload/upload.meta.md +111 -0
- package/src/components/upload/upload.stories.tsx +75 -0
- package/src/components/upload/upload.tsx +265 -0
- package/src/components/watermark/watermark.meta.md +95 -0
- package/src/components/watermark/watermark.stories.tsx +78 -0
- package/src/components/watermark/watermark.tsx +165 -0
- package/src/utils/cn.ts +6 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: steps
|
|
3
|
+
name: Steps
|
|
4
|
+
type: component
|
|
5
|
+
category: navigation
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: "@teamix-evo/ui"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Steps
|
|
11
|
+
|
|
12
|
+
步骤条 — antd 独有补足。引导用户完成多步流程(注册 / 下单 / 部署),清晰展示当前 / 已完成 / 待完成 / 错误状态。
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- 多步表单(注册 / 入驻 / 配置)
|
|
17
|
+
- 流程进度可视化(订单状态 / CI/CD)
|
|
18
|
+
- 长任务的阶段反馈
|
|
19
|
+
|
|
20
|
+
## When NOT to use
|
|
21
|
+
|
|
22
|
+
- 平级 tab → `Tabs`
|
|
23
|
+
- 简单进度 → `Progress` / `ProgressCircle`
|
|
24
|
+
- 历史时间线 → 自定义 timeline(暂无组件)
|
|
25
|
+
|
|
26
|
+
<!-- auto:props:begin -->
|
|
27
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
28
|
+
| --- | --- | --- | --- | --- |
|
|
29
|
+
| `items` | `StepItem[]` | – | ✓ | 步骤列表。 |
|
|
30
|
+
| `current` | `number` | `0` | – | 当前步骤(0-based)。`current` 之前的为 finish,`current` 为 process,之后为 wait。 |
|
|
31
|
+
| `status` | `'process' \| 'error'` | `"process"` | – | 当前步骤的状态(只影响 `current` 那一步;`process` 默认,`error` 标红)。 |
|
|
32
|
+
| `direction` | `'horizontal' \| 'vertical'` | `"horizontal"` | – | 排列方向。 |
|
|
33
|
+
| `size` | `'sm' \| 'default'` | `"default"` | – | 尺寸。 |
|
|
34
|
+
<!-- auto:props:end -->
|
|
35
|
+
|
|
36
|
+
<!-- auto:deps:begin -->
|
|
37
|
+
### 同库依赖
|
|
38
|
+
|
|
39
|
+
> `teamix-evo ui add steps` 时,以下 entry 会被自动连带安装(无需手动 add)。
|
|
40
|
+
|
|
41
|
+
| Entry | 类型 | 描述 |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
|
|
44
|
+
|
|
45
|
+
### npm 依赖
|
|
46
|
+
|
|
47
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm add lucide-react@^0.460.0
|
|
51
|
+
```
|
|
52
|
+
<!-- auto:deps:end -->
|
|
53
|
+
|
|
54
|
+
> `StepItem` 类型:`{ title; description?; status?: 'wait' | 'process' | 'finish' | 'error'; icon? }`。
|
|
55
|
+
|
|
56
|
+
## AI 生成纪律
|
|
57
|
+
|
|
58
|
+
- **`current` 0-based**:第一步是 0,与数组索引一致
|
|
59
|
+
- **`status="error"` 仅当前步**:把整体状态置错;**不要**对历史已完成步标 error
|
|
60
|
+
- **`description` 简短**:不超过 1 行,长说明放主区
|
|
61
|
+
- **vertical 用于侧栏**:横向用于宽屏 / 顶部
|
|
62
|
+
- **不要堆叠超过 6 步**:认知超载;> 6 步改用 Wizard 模式 + 折叠
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Steps } from '@/components/ui/steps';
|
|
68
|
+
|
|
69
|
+
// 横向
|
|
70
|
+
<Steps
|
|
71
|
+
current={1}
|
|
72
|
+
items={[
|
|
73
|
+
{ title: '账户信息', description: '填写基本信息' },
|
|
74
|
+
{ title: '验证邮箱', description: '收取验证码' },
|
|
75
|
+
{ title: '完成', description: '注册成功' },
|
|
76
|
+
]}
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
// 当前步出错
|
|
80
|
+
<Steps
|
|
81
|
+
current={1}
|
|
82
|
+
status="error"
|
|
83
|
+
items={[
|
|
84
|
+
{ title: '上传文件' },
|
|
85
|
+
{ title: '解析数据' },
|
|
86
|
+
{ title: '导入完成' },
|
|
87
|
+
]}
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
// 纵向
|
|
91
|
+
<Steps direction="vertical" current={2} items={[...]} />
|
|
92
|
+
|
|
93
|
+
// 自定义 icon
|
|
94
|
+
<Steps
|
|
95
|
+
current={0}
|
|
96
|
+
items={[
|
|
97
|
+
{ title: '提交', icon: <UploadIcon /> },
|
|
98
|
+
{ title: '审核', icon: <ShieldCheck /> },
|
|
99
|
+
{ title: '通过', icon: <Check /> },
|
|
100
|
+
]}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Steps } from './steps';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Steps> = {
|
|
5
|
+
title: '导航 · Navigation/Steps',
|
|
6
|
+
component: Steps,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
'步骤条 — 引导多步流程(注册 / 下单 / 部署)。状态自动推导(wait/process/finish/error),支持横向 / 纵向、两种尺寸、自定义图标。OpenTrek tokens 适配。',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
current: { control: 'number' },
|
|
18
|
+
status: { control: 'inline-radio', options: ['process', 'error'] },
|
|
19
|
+
direction: { control: 'inline-radio', options: ['horizontal', 'vertical'] },
|
|
20
|
+
size: { control: 'inline-radio', options: ['sm', 'default'] },
|
|
21
|
+
},
|
|
22
|
+
args: { current: 1, status: 'process', direction: 'horizontal', size: 'default' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof Steps>;
|
|
27
|
+
|
|
28
|
+
const items = [
|
|
29
|
+
{ title: '账户信息', description: '填写基本信息' },
|
|
30
|
+
{ title: '验证邮箱', description: '收取验证码' },
|
|
31
|
+
{ title: '完成', description: '注册成功' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export const Horizontal: Story = {
|
|
35
|
+
render: (args) => (
|
|
36
|
+
<div className="w-[600px]">
|
|
37
|
+
<Steps {...args} items={items} />
|
|
38
|
+
</div>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Vertical: Story = {
|
|
43
|
+
args: { direction: 'vertical', current: 1 },
|
|
44
|
+
render: (args) => (
|
|
45
|
+
<div className="w-72">
|
|
46
|
+
<Steps {...args} items={items} />
|
|
47
|
+
</div>
|
|
48
|
+
),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const ErrorState: Story = {
|
|
52
|
+
parameters: { controls: { disable: true } },
|
|
53
|
+
render: () => (
|
|
54
|
+
<div className="w-[600px]">
|
|
55
|
+
<Steps
|
|
56
|
+
current={1}
|
|
57
|
+
status="error"
|
|
58
|
+
items={[
|
|
59
|
+
{ title: '上传文件' },
|
|
60
|
+
{ title: '解析数据', description: '解析失败,请重试' },
|
|
61
|
+
{ title: '导入完成' },
|
|
62
|
+
]}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Small: Story = {
|
|
69
|
+
parameters: { controls: { disable: true } },
|
|
70
|
+
render: () => (
|
|
71
|
+
<div className="w-96">
|
|
72
|
+
<Steps size="sm" current={1} items={items} />
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Check, X } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/utils/cn';
|
|
5
|
+
|
|
6
|
+
export type StepStatus = 'wait' | 'process' | 'finish' | 'error';
|
|
7
|
+
|
|
8
|
+
export interface StepItem {
|
|
9
|
+
/** 步骤标题(必填)。 */
|
|
10
|
+
title: React.ReactNode;
|
|
11
|
+
/** 描述(可选)。 */
|
|
12
|
+
description?: React.ReactNode;
|
|
13
|
+
/** 自定义状态(覆盖父级根据 current 推导出的状态)。 */
|
|
14
|
+
status?: StepStatus;
|
|
15
|
+
/** 自定义图标(覆盖默认数字 / 勾选 / 错误)。 */
|
|
16
|
+
icon?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface StepsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
20
|
+
/** 步骤列表。 */
|
|
21
|
+
items: StepItem[];
|
|
22
|
+
/**
|
|
23
|
+
* 当前步骤(0-based)。`current` 之前的为 finish,`current` 为 process,之后为 wait。
|
|
24
|
+
* @default 0
|
|
25
|
+
*/
|
|
26
|
+
current?: number;
|
|
27
|
+
/**
|
|
28
|
+
* 当前步骤的状态(只影响 `current` 那一步;`process` 默认,`error` 标红)。
|
|
29
|
+
* @default "process"
|
|
30
|
+
*/
|
|
31
|
+
status?: 'process' | 'error';
|
|
32
|
+
/**
|
|
33
|
+
* 排列方向。
|
|
34
|
+
* @default "horizontal"
|
|
35
|
+
*/
|
|
36
|
+
direction?: 'horizontal' | 'vertical';
|
|
37
|
+
/**
|
|
38
|
+
* 尺寸。
|
|
39
|
+
* @default "default"
|
|
40
|
+
*/
|
|
41
|
+
size?: 'sm' | 'default';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function statusFor(
|
|
45
|
+
i: number,
|
|
46
|
+
current: number,
|
|
47
|
+
topStatus: 'process' | 'error',
|
|
48
|
+
itemStatus?: StepStatus,
|
|
49
|
+
): StepStatus {
|
|
50
|
+
if (itemStatus) return itemStatus;
|
|
51
|
+
if (i < current) return 'finish';
|
|
52
|
+
if (i === current) return topStatus;
|
|
53
|
+
return 'wait';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const dotColor: Record<StepStatus, string> = {
|
|
57
|
+
wait: 'border-border bg-background text-muted-foreground',
|
|
58
|
+
process: 'border-primary bg-primary text-primary-foreground',
|
|
59
|
+
finish: 'border-primary bg-primary text-primary-foreground',
|
|
60
|
+
error: 'border-destructive bg-destructive text-destructive-foreground',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const lineColor: Record<StepStatus, string> = {
|
|
64
|
+
wait: 'bg-border',
|
|
65
|
+
process: 'bg-border',
|
|
66
|
+
finish: 'bg-primary',
|
|
67
|
+
error: 'bg-border',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const titleColor: Record<StepStatus, string> = {
|
|
71
|
+
wait: 'text-muted-foreground',
|
|
72
|
+
process: 'text-foreground font-medium',
|
|
73
|
+
finish: 'text-foreground',
|
|
74
|
+
error: 'text-destructive',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const Steps = React.forwardRef<HTMLDivElement, StepsProps>(
|
|
78
|
+
(
|
|
79
|
+
{
|
|
80
|
+
items,
|
|
81
|
+
current = 0,
|
|
82
|
+
status = 'process',
|
|
83
|
+
direction = 'horizontal',
|
|
84
|
+
size = 'default',
|
|
85
|
+
className,
|
|
86
|
+
...props
|
|
87
|
+
},
|
|
88
|
+
ref,
|
|
89
|
+
) => {
|
|
90
|
+
const isVertical = direction === 'vertical';
|
|
91
|
+
const dotSize = size === 'sm' ? 'size-6 text-xs' : 'size-8 text-sm';
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
ref={ref}
|
|
95
|
+
className={cn(
|
|
96
|
+
'flex',
|
|
97
|
+
isVertical ? 'flex-col gap-0' : 'flex-row items-start gap-0',
|
|
98
|
+
className,
|
|
99
|
+
)}
|
|
100
|
+
{...props}
|
|
101
|
+
>
|
|
102
|
+
{items.map((it, i) => {
|
|
103
|
+
const s = statusFor(i, current, status, it.status);
|
|
104
|
+
const isLast = i === items.length - 1;
|
|
105
|
+
const icon =
|
|
106
|
+
it.icon ??
|
|
107
|
+
(s === 'finish' ? (
|
|
108
|
+
<Check className="size-4" />
|
|
109
|
+
) : s === 'error' ? (
|
|
110
|
+
<X className="size-4" />
|
|
111
|
+
) : (
|
|
112
|
+
i + 1
|
|
113
|
+
));
|
|
114
|
+
return (
|
|
115
|
+
<div
|
|
116
|
+
key={i}
|
|
117
|
+
className={cn(
|
|
118
|
+
'flex flex-1',
|
|
119
|
+
isVertical ? 'flex-row gap-3' : 'flex-col gap-2',
|
|
120
|
+
)}
|
|
121
|
+
>
|
|
122
|
+
<div
|
|
123
|
+
className={cn(
|
|
124
|
+
'flex',
|
|
125
|
+
isVertical
|
|
126
|
+
? 'flex-col items-center gap-2'
|
|
127
|
+
: 'flex-row items-center gap-2',
|
|
128
|
+
)}
|
|
129
|
+
>
|
|
130
|
+
<div
|
|
131
|
+
className={cn(
|
|
132
|
+
'flex shrink-0 items-center justify-center rounded-full border-2 font-medium tabular-nums',
|
|
133
|
+
dotSize,
|
|
134
|
+
dotColor[s],
|
|
135
|
+
)}
|
|
136
|
+
>
|
|
137
|
+
{icon}
|
|
138
|
+
</div>
|
|
139
|
+
{!isLast ? (
|
|
140
|
+
<div
|
|
141
|
+
className={cn(
|
|
142
|
+
lineColor[s],
|
|
143
|
+
isVertical ? 'min-h-8 w-px flex-1' : 'h-px flex-1',
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
) : null}
|
|
147
|
+
</div>
|
|
148
|
+
<div
|
|
149
|
+
className={cn(
|
|
150
|
+
'flex flex-col gap-0.5',
|
|
151
|
+
!isVertical && 'pr-3',
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
<div className={cn('text-sm', titleColor[s])}>{it.title}</div>
|
|
155
|
+
{it.description ? (
|
|
156
|
+
<div className="text-xs text-muted-foreground">
|
|
157
|
+
{it.description}
|
|
158
|
+
</div>
|
|
159
|
+
) : null}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
})}
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
Steps.displayName = 'Steps';
|
|
169
|
+
|
|
170
|
+
export { Steps };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: switch
|
|
3
|
+
name: Switch
|
|
4
|
+
type: component
|
|
5
|
+
category: form
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: "@teamix-evo/ui"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Switch
|
|
11
|
+
|
|
12
|
+
开关 — Radix Switch + antd 的 `loading / checkedChildren / unCheckedChildren / size`。
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- 立即生效的二元状态切换(订阅 / 通知 / 主题)
|
|
17
|
+
- 对比 Checkbox:Switch 表示**立即应用**,Checkbox 表示**待提交**
|
|
18
|
+
|
|
19
|
+
## When NOT to use
|
|
20
|
+
|
|
21
|
+
- 表单内需提交后生效 → 用 `Checkbox`
|
|
22
|
+
- 多个互斥选项 → 用 `Radio Group` 或 `Toggle Group`
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
|
|
27
|
+
|
|
28
|
+
<!-- auto:props:begin -->
|
|
29
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
30
|
+
| --- | --- | --- | --- | --- |
|
|
31
|
+
| `loading` | `boolean` | `false` | – | 加载态 — 自动 `disabled`,thumb 上叠加自旋 spinner(antd 并集)。 |
|
|
32
|
+
| `checkedChildren` | `React.ReactNode` | – | – | 选中态显示的内文(antd `checkedChildren` 并集)。 |
|
|
33
|
+
| `unCheckedChildren` | `React.ReactNode` | – | – | 未选中态显示的内文(antd `unCheckedChildren` 并集)。 |
|
|
34
|
+
| `size` | `'sm' \| 'default'` | `"default"` | – | 尺寸。 |
|
|
35
|
+
<!-- auto:props:end -->
|
|
36
|
+
|
|
37
|
+
## 依赖
|
|
38
|
+
|
|
39
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
|
|
40
|
+
|
|
41
|
+
<!-- auto:deps:begin -->
|
|
42
|
+
### 同库依赖
|
|
43
|
+
|
|
44
|
+
> `teamix-evo ui add switch` 时,以下 entry 会被自动连带安装(无需手动 add)。
|
|
45
|
+
|
|
46
|
+
| Entry | 类型 | 描述 |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
|
|
49
|
+
|
|
50
|
+
### npm 依赖
|
|
51
|
+
|
|
52
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm add @radix-ui/react-switch@^1.1.0 lucide-react@^0.460.0
|
|
56
|
+
```
|
|
57
|
+
<!-- auto:deps:end -->
|
|
58
|
+
|
|
59
|
+
> 透传所有 Radix Switch.Root props(`checked` / `defaultChecked` / `onCheckedChange` / `name` / `value` 等)。
|
|
60
|
+
|
|
61
|
+
## AI 生成纪律
|
|
62
|
+
|
|
63
|
+
- **`loading` 期间禁止操作**:已自动 `disabled`,**不要**手动叠加 disabled 影响 UX 一致
|
|
64
|
+
- **`checkedChildren` 文字 ≤ 2 个汉字**:仅"开/关"或"On/Off",过长会溢出胶囊
|
|
65
|
+
- **配 Label 无障碍**:Switch 不带可见文字时,必须用 `aria-label` 或外部 `<Label>` + `id` 关联
|
|
66
|
+
- **size="sm" 用于密集列表**:常规表单用 `default`
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Switch } from '@/components/ui/switch';
|
|
72
|
+
import { Label } from '@/components/ui/label';
|
|
73
|
+
|
|
74
|
+
// 基础(可控)
|
|
75
|
+
const [on, setOn] = React.useState(false);
|
|
76
|
+
<Switch checked={on} onCheckedChange={setOn} aria-label="启用通知" />
|
|
77
|
+
|
|
78
|
+
// 配 Label
|
|
79
|
+
<div className="flex items-center gap-2">
|
|
80
|
+
<Switch id="notify" />
|
|
81
|
+
<Label htmlFor="notify">订阅邮件提醒</Label>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
// 加载态
|
|
85
|
+
<Switch loading defaultChecked />
|
|
86
|
+
|
|
87
|
+
// 内文(开/关 / On/Off)
|
|
88
|
+
<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked />
|
|
89
|
+
|
|
90
|
+
// 小尺寸
|
|
91
|
+
<Switch size="sm" />
|
|
92
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Switch } from './switch';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Switch> = {
|
|
5
|
+
title: '表单与输入 · Form/Switch',
|
|
6
|
+
component: Switch,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
'开关 — 在开 / 关两种状态间切换,适用于需要即时生效的二值设置。Radix Switch 实现 + antd Switch 的并集能力:支持 `size` (sm / default)、`loading` 异步状态、以及 `checkedChildren` / `unCheckedChildren` 在轨道内渲染状态文本(对齐 antd)。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
size: { control: 'inline-radio', options: ['sm', 'default'] },
|
|
18
|
+
loading: { control: 'boolean' },
|
|
19
|
+
disabled: { control: 'boolean' },
|
|
20
|
+
checkedChildren: { control: 'text' },
|
|
21
|
+
unCheckedChildren: { control: 'text' },
|
|
22
|
+
},
|
|
23
|
+
args: { size: 'default', loading: false, disabled: false },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
type Story = StoryObj<typeof Switch>;
|
|
28
|
+
|
|
29
|
+
export const Playground: Story = {};
|
|
30
|
+
|
|
31
|
+
export const Sizes: Story = {
|
|
32
|
+
parameters: { controls: { disable: true } },
|
|
33
|
+
render: () => (
|
|
34
|
+
<div className="flex items-center gap-4">
|
|
35
|
+
<Switch size="sm" defaultChecked />
|
|
36
|
+
<Switch size="default" defaultChecked />
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const States: Story = {
|
|
42
|
+
parameters: { controls: { disable: true } },
|
|
43
|
+
render: () => (
|
|
44
|
+
<div className="flex items-center gap-4">
|
|
45
|
+
<Switch />
|
|
46
|
+
<Switch defaultChecked />
|
|
47
|
+
<Switch disabled />
|
|
48
|
+
<Switch disabled defaultChecked />
|
|
49
|
+
<Switch loading />
|
|
50
|
+
<Switch loading defaultChecked />
|
|
51
|
+
</div>
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const WithLabel: Story = {
|
|
56
|
+
parameters: { controls: { disable: true } },
|
|
57
|
+
render: () => (
|
|
58
|
+
<div className="flex items-center gap-2">
|
|
59
|
+
<Switch id="notify-stories" />
|
|
60
|
+
<label htmlFor="notify-stories" className="text-sm font-medium">
|
|
61
|
+
订阅邮件提醒
|
|
62
|
+
</label>
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const InnerText: Story = {
|
|
68
|
+
parameters: { controls: { disable: true } },
|
|
69
|
+
render: () => (
|
|
70
|
+
<div className="flex items-center gap-4">
|
|
71
|
+
<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked />
|
|
72
|
+
<Switch checkedChildren="On" unCheckedChildren="Off" />
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
|
3
|
+
import { Loader2 } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/utils/cn';
|
|
6
|
+
|
|
7
|
+
export interface SwitchProps
|
|
8
|
+
extends React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> {
|
|
9
|
+
/**
|
|
10
|
+
* 加载态 — 自动 `disabled`,thumb 上叠加自旋 spinner(antd 并集)。
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
/** 选中态显示的内文(antd `checkedChildren` 并集)。 */
|
|
15
|
+
checkedChildren?: React.ReactNode;
|
|
16
|
+
/** 未选中态显示的内文(antd `unCheckedChildren` 并集)。 */
|
|
17
|
+
unCheckedChildren?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* 尺寸。
|
|
20
|
+
* @default "default"
|
|
21
|
+
*/
|
|
22
|
+
size?: 'sm' | 'default';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Switch = React.forwardRef<
|
|
26
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
27
|
+
SwitchProps
|
|
28
|
+
>(
|
|
29
|
+
(
|
|
30
|
+
{
|
|
31
|
+
className,
|
|
32
|
+
loading = false,
|
|
33
|
+
checkedChildren,
|
|
34
|
+
unCheckedChildren,
|
|
35
|
+
size = 'default',
|
|
36
|
+
disabled,
|
|
37
|
+
...props
|
|
38
|
+
},
|
|
39
|
+
ref,
|
|
40
|
+
) => {
|
|
41
|
+
const isSm = size === 'sm';
|
|
42
|
+
return (
|
|
43
|
+
<SwitchPrimitive.Root
|
|
44
|
+
ref={ref}
|
|
45
|
+
disabled={disabled || loading}
|
|
46
|
+
className={cn(
|
|
47
|
+
'peer relative inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
|
48
|
+
isSm ? 'h-5 w-9' : 'h-6 w-11',
|
|
49
|
+
className,
|
|
50
|
+
)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{/* Inner labels (checkedChildren / unCheckedChildren) */}
|
|
54
|
+
{checkedChildren != null || unCheckedChildren != null ? (
|
|
55
|
+
<>
|
|
56
|
+
<span
|
|
57
|
+
className={cn(
|
|
58
|
+
'pointer-events-none absolute select-none text-[10px] font-medium leading-none text-primary-foreground transition-opacity peer-data-[state=unchecked]:opacity-0',
|
|
59
|
+
isSm ? 'left-1.5' : 'left-2',
|
|
60
|
+
)}
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
>
|
|
63
|
+
{checkedChildren}
|
|
64
|
+
</span>
|
|
65
|
+
<span
|
|
66
|
+
className={cn(
|
|
67
|
+
'pointer-events-none absolute select-none text-[10px] font-medium leading-none text-muted-foreground transition-opacity peer-data-[state=checked]:opacity-0',
|
|
68
|
+
isSm ? 'right-1.5' : 'right-2',
|
|
69
|
+
)}
|
|
70
|
+
aria-hidden="true"
|
|
71
|
+
>
|
|
72
|
+
{unCheckedChildren}
|
|
73
|
+
</span>
|
|
74
|
+
</>
|
|
75
|
+
) : null}
|
|
76
|
+
|
|
77
|
+
<SwitchPrimitive.Thumb
|
|
78
|
+
className={cn(
|
|
79
|
+
'pointer-events-none flex items-center justify-center rounded-full bg-background shadow-lg ring-0 transition-transform',
|
|
80
|
+
isSm
|
|
81
|
+
? 'size-4 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0'
|
|
82
|
+
: 'size-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
{loading ? (
|
|
86
|
+
<Loader2
|
|
87
|
+
className={cn(
|
|
88
|
+
'animate-spin text-muted-foreground motion-reduce:animate-none',
|
|
89
|
+
isSm ? 'size-3' : 'size-3.5',
|
|
90
|
+
)}
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
/>
|
|
93
|
+
) : null}
|
|
94
|
+
</SwitchPrimitive.Thumb>
|
|
95
|
+
</SwitchPrimitive.Root>
|
|
96
|
+
);
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
Switch.displayName = 'Switch';
|
|
100
|
+
|
|
101
|
+
export { Switch };
|