@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,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: slider
|
|
3
|
+
name: Slider
|
|
4
|
+
type: component
|
|
5
|
+
category: form
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: '@teamix-evo/ui'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Slider
|
|
11
|
+
|
|
12
|
+
滑块 — Radix Slider + antd 的 `marks` 刻度标签。**单 / 双滑块由 `value`/`defaultValue` 数组长度决定**(传 `[lo, hi]` 自动变范围滑块)。
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- 数值范围调节(音量 / 缩放 / 亮度)
|
|
17
|
+
- 区间筛选(价格区间、日期区间)
|
|
18
|
+
- 配合实时预览的微调控件
|
|
19
|
+
|
|
20
|
+
## When NOT to use
|
|
21
|
+
|
|
22
|
+
- 离散选项 → 用 `Radio Group` 或 `Select`
|
|
23
|
+
- 精确数值输入 → 用 `Input type="number"`
|
|
24
|
+
- 多于 2 个滑块 → 改用专门的多段滑块组件(暂未提供)
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
|
|
29
|
+
|
|
30
|
+
<!-- auto:props:begin -->
|
|
31
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `marks` | `Record<number, React.ReactNode>` | – | – | 刻度标记(antd `marks` 并集)。键为 0~max 的数值,值为标签文本。 渲染时小标记点 + 文字标签出现在轨道下方。 |
|
|
34
|
+
<!-- auto:props:end -->
|
|
35
|
+
|
|
36
|
+
## 依赖
|
|
37
|
+
|
|
38
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
|
|
39
|
+
|
|
40
|
+
<!-- auto:deps:begin -->
|
|
41
|
+
### 同库依赖
|
|
42
|
+
|
|
43
|
+
> `teamix-evo ui add slider` 时,以下 entry 会被自动连带安装(无需手动 add)。
|
|
44
|
+
|
|
45
|
+
| Entry | 类型 | 描述 |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
|
|
48
|
+
|
|
49
|
+
### npm 依赖
|
|
50
|
+
|
|
51
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add @radix-ui/react-slider@^1.2.0
|
|
55
|
+
```
|
|
56
|
+
<!-- auto:deps:end -->
|
|
57
|
+
|
|
58
|
+
> 透传所有 Radix Slider.Root props(`min` / `max` / `step` / `disabled` / `onValueChange` / `name` / `inverted` 等)。
|
|
59
|
+
|
|
60
|
+
## AI 生成纪律
|
|
61
|
+
|
|
62
|
+
- **`value` 永远是数组**:即使单滑块,Radix 期望 `[42]` 而非 `42`,初学者最易踩坑
|
|
63
|
+
- **`marks` 键必须在 `[min, max]` 内**:超出范围的标记仍会渲染但位置错乱
|
|
64
|
+
- **范围滑块由数据决定形态**:不要用单独的 `range` prop,**直接** `defaultValue={[20, 80]}` 即可
|
|
65
|
+
- **配 Label**:Slider 不带可见数值时,必须 `aria-label` 或外部 Label 关联
|
|
66
|
+
- **`step` 跨度的可访问性**:键盘 Page Up/Down 步进 = step × 10,设计 step 时考虑这一点
|
|
67
|
+
- **marks 的"圆点"和"文字"必须分两层渲染,不要打包成一个 box**:
|
|
68
|
+
- **圆点**:`absolute top-1/2 -translate-x-1/2 -translate-y-1/2` 居中压在 track 中线上,渲染在 `<SliderPrimitive.Root>` 内、`<Track>` 之外(放进 Track 内会被 `overflow-hidden` 裁掉或被 `Range` 高亮覆盖)
|
|
69
|
+
- **文字**:独立放在父容器 `bottom-0` 处,通过父容器 `pb-6` 留出 ~14px 间距;不要用 `mt-1` 把文字直接挂在圆点下方,会出现"点和文字粘在一起"的视觉 bug(对照 antd 标准:点压 track、文字距 track ~12px)
|
|
70
|
+
- **`<Thumb>` 必须加 `relative z-10`**:让滑块视觉层级高于 marks 圆点,避免拖动时圆点突兀露在 thumb 之上
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { Slider } from '@/components/ui/slider';
|
|
76
|
+
|
|
77
|
+
// 单滑块
|
|
78
|
+
<Slider defaultValue={[42]} max={100} step={1} />
|
|
79
|
+
|
|
80
|
+
// 范围滑块
|
|
81
|
+
<Slider defaultValue={[20, 80]} max={100} step={1} />
|
|
82
|
+
|
|
83
|
+
// 带刻度标签
|
|
84
|
+
<Slider
|
|
85
|
+
defaultValue={[40]}
|
|
86
|
+
max={100}
|
|
87
|
+
step={20}
|
|
88
|
+
marks={{ 0: '0°C', 20: '20', 40: '40', 60: '60', 80: '80', 100: '100°C' }}
|
|
89
|
+
/>
|
|
90
|
+
|
|
91
|
+
// 受控
|
|
92
|
+
const [v, setV] = React.useState([50]);
|
|
93
|
+
<Slider value={v} onValueChange={setV} max={100} />
|
|
94
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Slider } from './slider';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Slider> = {
|
|
5
|
+
title: '表单与输入 · Form/Slider',
|
|
6
|
+
component: Slider,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
'滑块 — 在给定范围内选取一个数值或一段区间。Radix Slider 实现 + antd 的 `marks` 刻度能力:`value` / `defaultValue` 为数组,长度为 1 时为单滑块、为 2 时自动切换为区间滑块;可配 `marks` 渲染轨道下方的刻度点与标签。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
min: { control: 'number' },
|
|
18
|
+
max: { control: 'number' },
|
|
19
|
+
step: { control: 'number' },
|
|
20
|
+
disabled: { control: 'boolean' },
|
|
21
|
+
},
|
|
22
|
+
args: { defaultValue: [42], min: 0, max: 100, step: 1 },
|
|
23
|
+
decorators: [
|
|
24
|
+
(Story) => (
|
|
25
|
+
<div className="w-80">
|
|
26
|
+
<Story />
|
|
27
|
+
</div>
|
|
28
|
+
),
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default meta;
|
|
33
|
+
type Story = StoryObj<typeof Slider>;
|
|
34
|
+
|
|
35
|
+
export const Playground: Story = {};
|
|
36
|
+
|
|
37
|
+
export const Range: Story = {
|
|
38
|
+
parameters: { controls: { disable: true } },
|
|
39
|
+
render: () => <Slider defaultValue={[20, 80]} max={100} />,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const WithMarks: Story = {
|
|
43
|
+
parameters: { controls: { disable: true } },
|
|
44
|
+
render: () => (
|
|
45
|
+
<Slider
|
|
46
|
+
defaultValue={[40]}
|
|
47
|
+
max={100}
|
|
48
|
+
step={20}
|
|
49
|
+
marks={{
|
|
50
|
+
0: '0°C',
|
|
51
|
+
20: '20',
|
|
52
|
+
40: '40',
|
|
53
|
+
60: '60',
|
|
54
|
+
80: '80',
|
|
55
|
+
100: '100°C',
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Disabled: Story = {
|
|
62
|
+
parameters: { controls: { disable: true } },
|
|
63
|
+
render: () => (
|
|
64
|
+
<div className="flex flex-col gap-4">
|
|
65
|
+
<Slider defaultValue={[42]} disabled />
|
|
66
|
+
<Slider defaultValue={[20, 80]} disabled />
|
|
67
|
+
</div>
|
|
68
|
+
),
|
|
69
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/utils/cn';
|
|
5
|
+
|
|
6
|
+
export interface SliderProps
|
|
7
|
+
extends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {
|
|
8
|
+
/**
|
|
9
|
+
* 刻度标记(antd `marks` 并集)。键为 0~max 的数值,值为标签文本。
|
|
10
|
+
* 渲染时小标记点 + 文字标签出现在轨道下方。
|
|
11
|
+
*/
|
|
12
|
+
marks?: Record<number, React.ReactNode>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Slider = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof SliderPrimitive.Root>,
|
|
17
|
+
SliderProps
|
|
18
|
+
>(
|
|
19
|
+
(
|
|
20
|
+
{ className, marks, value, defaultValue, min = 0, max = 100, ...props },
|
|
21
|
+
ref,
|
|
22
|
+
) => {
|
|
23
|
+
// Radix Slider supports range natively when `value` / `defaultValue` is array of length 2.
|
|
24
|
+
const thumbCount = (value ?? defaultValue ?? [0]).length;
|
|
25
|
+
return (
|
|
26
|
+
<div className={cn('relative w-full', marks ? 'pb-6' : '')}>
|
|
27
|
+
<SliderPrimitive.Root
|
|
28
|
+
ref={ref}
|
|
29
|
+
value={value}
|
|
30
|
+
defaultValue={defaultValue}
|
|
31
|
+
min={min}
|
|
32
|
+
max={max}
|
|
33
|
+
className={cn(
|
|
34
|
+
'relative flex w-full touch-none select-none items-center',
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-secondary">
|
|
40
|
+
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
|
41
|
+
</SliderPrimitive.Track>
|
|
42
|
+
{marks
|
|
43
|
+
? Object.entries(marks).map(([k]) => {
|
|
44
|
+
const num = Number(k);
|
|
45
|
+
const pct = ((num - min) / (max - min)) * 100;
|
|
46
|
+
return (
|
|
47
|
+
<span
|
|
48
|
+
key={`dot-${k}`}
|
|
49
|
+
aria-hidden
|
|
50
|
+
className="pointer-events-none absolute top-1/2 size-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-muted-foreground"
|
|
51
|
+
style={{ left: `${pct}%` }}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
})
|
|
55
|
+
: null}
|
|
56
|
+
{Array.from({ length: thumbCount }).map((_, i) => (
|
|
57
|
+
<SliderPrimitive.Thumb
|
|
58
|
+
key={i}
|
|
59
|
+
className="relative z-10 block size-4 rounded-full border-2 border-primary bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
62
|
+
</SliderPrimitive.Root>
|
|
63
|
+
{marks ? (
|
|
64
|
+
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-4">
|
|
65
|
+
{Object.entries(marks).map(([k, label]) => {
|
|
66
|
+
const num = Number(k);
|
|
67
|
+
const pct = ((num - min) / (max - min)) * 100;
|
|
68
|
+
return (
|
|
69
|
+
<span
|
|
70
|
+
key={k}
|
|
71
|
+
className="absolute -translate-x-1/2 whitespace-nowrap text-xs text-muted-foreground"
|
|
72
|
+
style={{ left: `${pct}%` }}
|
|
73
|
+
>
|
|
74
|
+
{label}
|
|
75
|
+
</span>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</div>
|
|
79
|
+
) : null}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
Slider.displayName = 'Slider';
|
|
85
|
+
|
|
86
|
+
export { Slider };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: sonner
|
|
3
|
+
name: Sonner
|
|
4
|
+
type: component
|
|
5
|
+
category: feedback
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: "@teamix-evo/ui"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Sonner
|
|
11
|
+
|
|
12
|
+
Toast 通知 — 基于 [`sonner`](https://sonner.emilkowal.ski/),已**同时覆盖** antd `message`(行内简短)+ `notification`(标题 + 描述 + 行动)的并集。
|
|
13
|
+
|
|
14
|
+
**应用根挂 `<Toaster />` 一次**,在任意地方 `import { toast } from '@/components/ui/sonner'` 触发。
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- 异步操作结果反馈(保存成功 / 网络错误)
|
|
19
|
+
- 系统通知(新消息 / 协作动作)
|
|
20
|
+
- Promise 状态自动切换(`toast.promise(saving, { loading, success, error })`)
|
|
21
|
+
- 带行动按钮的提示(`toast(... { action: { label: '撤销', onClick } })`)
|
|
22
|
+
|
|
23
|
+
## When NOT to use
|
|
24
|
+
|
|
25
|
+
- 阻断式确认 → `AlertDialog`
|
|
26
|
+
- 静态常驻提示 → `Alert`
|
|
27
|
+
- 表单字段错误 → 字段下方文本
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `<Toaster />` 的 props。`toast()` 函数式 API 见下方 Examples。
|
|
32
|
+
|
|
33
|
+
<!-- auto:props:begin -->
|
|
34
|
+
_(no props)_
|
|
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
|
+
_无 — 本组件不依赖其他 ui entry。_
|
|
45
|
+
|
|
46
|
+
### npm 依赖
|
|
47
|
+
|
|
48
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm add sonner@^1.5.0
|
|
52
|
+
```
|
|
53
|
+
<!-- auto:deps:end -->
|
|
54
|
+
|
|
55
|
+
> 透传所有 sonner `<Toaster>` 原生属性(`position` / `richColors` / `expand` / `closeButton` 等)。
|
|
56
|
+
|
|
57
|
+
## AI 生成纪律
|
|
58
|
+
|
|
59
|
+
- **应用根只挂一次**:不要在多处 `<Toaster />`,触发会重复
|
|
60
|
+
- **不要替代必读信息**:Toast 会自动消失,**不要**用它呈现"用户必须知道"的关键信息(用 `Alert` / `AlertDialog`)
|
|
61
|
+
- **错误用 `toast.error()`**:语义化方法比 `toast({ type: 'error' })` 优先
|
|
62
|
+
- **长任务用 `toast.promise()`**:自动显示 loading → success / error,避免手写状态切换
|
|
63
|
+
- **Action 按钮用一个就够**:不要塞多个,过载
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { Toaster, toast } from '@/components/ui/sonner';
|
|
69
|
+
|
|
70
|
+
// 应用根
|
|
71
|
+
<Toaster position="top-right" richColors />
|
|
72
|
+
|
|
73
|
+
// 简单消息(等价 antd message)
|
|
74
|
+
toast('已复制到剪贴板');
|
|
75
|
+
|
|
76
|
+
// 带标题 + 描述(等价 antd notification)
|
|
77
|
+
toast.success('保存成功', {
|
|
78
|
+
description: '所有更改已生效。',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
toast.error('提交失败', {
|
|
82
|
+
description: '请检查网络后重试。',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 异步任务(自动 loading → success / error)
|
|
86
|
+
toast.promise(saveProject(), {
|
|
87
|
+
loading: '保存中...',
|
|
88
|
+
success: '保存成功',
|
|
89
|
+
error: (err) => `保存失败: ${err.message}`,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 带行动按钮
|
|
93
|
+
toast('文件已删除', {
|
|
94
|
+
action: { label: '撤销', onClick: () => undo() },
|
|
95
|
+
});
|
|
96
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Toaster, toast } from './sonner';
|
|
3
|
+
import { Button } from '@/components/button/button';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Toaster> = {
|
|
6
|
+
title: '反馈与浮层 · Feedback/Sonner',
|
|
7
|
+
component: Toaster,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
position: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: [
|
|
13
|
+
'top-left',
|
|
14
|
+
'top-center',
|
|
15
|
+
'top-right',
|
|
16
|
+
'bottom-left',
|
|
17
|
+
'bottom-center',
|
|
18
|
+
'bottom-right',
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
richColors: { control: 'boolean' },
|
|
22
|
+
expand: { control: 'boolean' },
|
|
23
|
+
closeButton: { control: 'boolean' },
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
position: 'top-right',
|
|
27
|
+
richColors: true,
|
|
28
|
+
closeButton: true,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default meta;
|
|
33
|
+
type Story = StoryObj<typeof Toaster>;
|
|
34
|
+
|
|
35
|
+
export const Playground: Story = {
|
|
36
|
+
render: (args) => (
|
|
37
|
+
<div className="flex flex-col gap-2">
|
|
38
|
+
<Toaster {...args} />
|
|
39
|
+
<Button onClick={() => toast('已复制到剪贴板')}>普通</Button>
|
|
40
|
+
<Button
|
|
41
|
+
variant="outline"
|
|
42
|
+
onClick={() =>
|
|
43
|
+
toast.success('保存成功', { description: '所有更改已生效。' })
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
Success
|
|
47
|
+
</Button>
|
|
48
|
+
<Button
|
|
49
|
+
variant="outline"
|
|
50
|
+
onClick={() =>
|
|
51
|
+
toast.error('提交失败', { description: '请检查网络后重试。' })
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
Error
|
|
55
|
+
</Button>
|
|
56
|
+
<Button
|
|
57
|
+
variant="outline"
|
|
58
|
+
onClick={() =>
|
|
59
|
+
toast.warning('注意', { description: '账号 7 天后过期。' })
|
|
60
|
+
}
|
|
61
|
+
>
|
|
62
|
+
Warning
|
|
63
|
+
</Button>
|
|
64
|
+
<Button
|
|
65
|
+
variant="outline"
|
|
66
|
+
onClick={() =>
|
|
67
|
+
toast.promise(
|
|
68
|
+
new Promise((res) => setTimeout(res, 1500)),
|
|
69
|
+
{
|
|
70
|
+
loading: '保存中...',
|
|
71
|
+
success: '保存成功',
|
|
72
|
+
error: '保存失败',
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
>
|
|
77
|
+
Promise
|
|
78
|
+
</Button>
|
|
79
|
+
<Button
|
|
80
|
+
variant="outline"
|
|
81
|
+
onClick={() =>
|
|
82
|
+
toast('文件已删除', {
|
|
83
|
+
action: { label: '撤销', onClick: () => toast('已撤销') },
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
With Action
|
|
88
|
+
</Button>
|
|
89
|
+
</div>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Toaster as Sonner, type ToasterProps as SonnerProps } from 'sonner';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Toaster 容器 — 应用根挂一次,后续在任意地方调 `toast()` / `toast.success()` / `toast.error()` /
|
|
5
|
+
* `toast.warning()` / `toast.loading()` / `toast.promise()` 等触发 toast。
|
|
6
|
+
*
|
|
7
|
+
* 默认主题跟随系统;视觉走 design tokens(`--background` / `--border` / `--foreground`)。
|
|
8
|
+
*
|
|
9
|
+
* 来自 antd `message` + `notification` 的并集 — sonner 的 toast API 已经覆盖了:
|
|
10
|
+
* - 短文字提示(对应 message)
|
|
11
|
+
* - 标题 + 描述(对应 notification)
|
|
12
|
+
* - 操作按钮(action)
|
|
13
|
+
* - Promise 状态切换(loading → success/error)
|
|
14
|
+
*/
|
|
15
|
+
export interface ToasterProps extends SonnerProps {}
|
|
16
|
+
|
|
17
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<Sonner
|
|
20
|
+
theme="system"
|
|
21
|
+
className="toaster group"
|
|
22
|
+
toastOptions={{
|
|
23
|
+
classNames: {
|
|
24
|
+
toast:
|
|
25
|
+
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
|
26
|
+
description: 'group-[.toast]:text-muted-foreground',
|
|
27
|
+
actionButton:
|
|
28
|
+
'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
|
29
|
+
cancelButton:
|
|
30
|
+
'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
|
|
31
|
+
},
|
|
32
|
+
}}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Re-export the imperative API so consumers only depend on this module.
|
|
39
|
+
export { toast } from 'sonner';
|
|
40
|
+
export { Toaster };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: space
|
|
3
|
+
name: Space
|
|
4
|
+
type: component
|
|
5
|
+
category: layout
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: "@teamix-evo/ui"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Space
|
|
11
|
+
|
|
12
|
+
间距容器 — antd 独有补足。**等价 antd `Space`**:把同语义并排子项统一间距、对齐与可选换行,可选 `split` 节点(常用 Separator)。与 `Flex` 区别:Space 偏向**小集合 inline 间距**(按钮组、tag 组、表单 label-value 对),Flex 偏向**完整布局容器**。
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- 多个按钮 / 标签 / 链接并排(避免直接写 `flex gap-2`)
|
|
17
|
+
- 表单某一行的"标签 + 值"对
|
|
18
|
+
- 列表行末的多个操作链接(配 `split=<Separator orientation="vertical" />`)
|
|
19
|
+
|
|
20
|
+
## When NOT to use
|
|
21
|
+
|
|
22
|
+
- 完整页面布局 → `Flex` / `Grid`
|
|
23
|
+
- 按钮粘连共享边线 → `ButtonGroup`
|
|
24
|
+
- 大段排版分段 → `Typography Prose`
|
|
25
|
+
|
|
26
|
+
<!-- auto:props:begin -->
|
|
27
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
28
|
+
| --- | --- | --- | --- | --- |
|
|
29
|
+
| `direction` | `'horizontal' \| 'vertical'` | `"horizontal"` | – | 排列方向(antd `direction` 并集)。 |
|
|
30
|
+
| `size` | `'sm' \| 'default' \| 'lg'` | `"default"` | – | 间距档位(antd `size` 并集) — 走 design 间距刻度,不接受 number。 |
|
|
31
|
+
| `wrap` | `boolean` | `false` | – | 是否自动换行(antd `wrap` 并集) — horizontal 模式生效。 |
|
|
32
|
+
| `align` | `'start' \| 'center' \| 'end' \| 'baseline' \| 'stretch'` | `"center"` | – | 主轴对齐方式(antd `align` 并集)。 |
|
|
33
|
+
| `justify` | `'start' \| 'center' \| 'end' \| 'between' \| 'around'` | – | – | 副轴对齐(antd `Space` 无,Space.Compact 也无 — 但中后台需求高,补)。 |
|
|
34
|
+
| `split` | `React.ReactNode` | – | – | 分隔节点(antd `split` 并集) — 在每两个子项之间插入(常用 `<Separator>`)。 |
|
|
35
|
+
<!-- auto:props:end -->
|
|
36
|
+
|
|
37
|
+
<!-- auto:deps:begin -->
|
|
38
|
+
### 同库依赖
|
|
39
|
+
|
|
40
|
+
> `teamix-evo ui add space` 时,以下 entry 会被自动连带安装(无需手动 add)。
|
|
41
|
+
|
|
42
|
+
| Entry | 类型 | 描述 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
|
|
45
|
+
|
|
46
|
+
### npm 依赖
|
|
47
|
+
|
|
48
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm add class-variance-authority@^0.7.0
|
|
52
|
+
```
|
|
53
|
+
<!-- auto:deps:end -->
|
|
54
|
+
|
|
55
|
+
## AI 生成纪律
|
|
56
|
+
|
|
57
|
+
- **`size` 是档位枚举**(sm/default/lg),**不接受 number** — 与 antd 偏离,因为我们走 design 间距刻度
|
|
58
|
+
- **`wrap` 仅 horizontal 生效** — vertical 自动换行无意义
|
|
59
|
+
- **`split` 应是无状态轻节点**(Separator / `|` / dot),不要传 Button 等复杂组件,语义混乱
|
|
60
|
+
- **`justify`** 是 antd 缺失但中后台高频补的能力 — 列表行末右对齐操作就用 `justify="end"`
|
|
61
|
+
- **不要嵌套 Space**:嵌套用 Flex 表达父子结构
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { Space } from '@/components/ui/space';
|
|
67
|
+
import { Button } from '@/components/ui/button';
|
|
68
|
+
import { Separator } from '@/components/ui/separator';
|
|
69
|
+
|
|
70
|
+
// 按钮组
|
|
71
|
+
<Space>
|
|
72
|
+
<Button variant="outline">取消</Button>
|
|
73
|
+
<Button>确定</Button>
|
|
74
|
+
</Space>
|
|
75
|
+
|
|
76
|
+
// 链接组 + split
|
|
77
|
+
<Space split={<Separator orientation="vertical" className="h-4" />} size="sm">
|
|
78
|
+
<a href="#">查看</a>
|
|
79
|
+
<a href="#">编辑</a>
|
|
80
|
+
<a href="#">删除</a>
|
|
81
|
+
</Space>
|
|
82
|
+
|
|
83
|
+
// 表单行
|
|
84
|
+
<Space justify="between">
|
|
85
|
+
<span className="text-sm text-muted-foreground">总计</span>
|
|
86
|
+
<span className="text-lg font-semibold">¥ 1,299</span>
|
|
87
|
+
</Space>
|
|
88
|
+
|
|
89
|
+
// 纵向
|
|
90
|
+
<Space direction="vertical" align="start">
|
|
91
|
+
<Title>标题</Title>
|
|
92
|
+
<Paragraph>描述</Paragraph>
|
|
93
|
+
</Space>
|
|
94
|
+
```
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Space } from './space';
|
|
3
|
+
import { Button } from '@/components/button/button';
|
|
4
|
+
import { Separator } from '@/components/separator/separator';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Space> = {
|
|
7
|
+
title: '布局与容器 · Layout/Space',
|
|
8
|
+
component: Space,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'间距容器 — 把同语义并排子项统一间距、对齐与可选换行(按钮组、tag 组、表单 label-value 对)。等价 antd `Space`,与 Flex 互补:Space 偏 inline 小集合,Flex 偏完整布局。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
direction: { control: 'inline-radio', options: ['horizontal', 'vertical'] },
|
|
20
|
+
size: { control: 'inline-radio', options: ['sm', 'default', 'lg'] },
|
|
21
|
+
wrap: { control: 'boolean' },
|
|
22
|
+
align: {
|
|
23
|
+
control: 'inline-radio',
|
|
24
|
+
options: ['start', 'center', 'end', 'baseline', 'stretch'],
|
|
25
|
+
},
|
|
26
|
+
justify: {
|
|
27
|
+
control: 'inline-radio',
|
|
28
|
+
options: ['start', 'center', 'end', 'between', 'around'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
args: { direction: 'horizontal', size: 'default', wrap: false, align: 'center' },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
type Story = StoryObj<typeof Space>;
|
|
36
|
+
|
|
37
|
+
export const Playground: Story = {
|
|
38
|
+
render: (args) => (
|
|
39
|
+
<Space {...args}>
|
|
40
|
+
<Button variant="outline">取消</Button>
|
|
41
|
+
<Button>确定</Button>
|
|
42
|
+
<Button variant="ghost">更多</Button>
|
|
43
|
+
</Space>
|
|
44
|
+
),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const Vertical: Story = {
|
|
48
|
+
parameters: { controls: { disable: true } },
|
|
49
|
+
render: () => (
|
|
50
|
+
<Space direction="vertical" align="start">
|
|
51
|
+
<span className="text-base font-semibold">标题</span>
|
|
52
|
+
<span className="text-sm text-muted-foreground">这是描述文字</span>
|
|
53
|
+
<Button size="sm">操作</Button>
|
|
54
|
+
</Space>
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const WithSplit: Story = {
|
|
59
|
+
parameters: { controls: { disable: true } },
|
|
60
|
+
render: () => (
|
|
61
|
+
<Space split={<Separator orientation="vertical" className="h-4" />} size="sm">
|
|
62
|
+
<a href="#" className="text-sm text-primary hover:underline">查看</a>
|
|
63
|
+
<a href="#" className="text-sm text-primary hover:underline">编辑</a>
|
|
64
|
+
<a href="#" className="text-sm text-destructive hover:underline">删除</a>
|
|
65
|
+
</Space>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Between: Story = {
|
|
70
|
+
parameters: { controls: { disable: true } },
|
|
71
|
+
render: () => (
|
|
72
|
+
<div className="w-80 rounded-md border p-3">
|
|
73
|
+
<Space justify="between">
|
|
74
|
+
<span className="text-sm text-muted-foreground">总计</span>
|
|
75
|
+
<span className="text-lg font-semibold">¥ 1,299</span>
|
|
76
|
+
</Space>
|
|
77
|
+
</div>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const Wrap: Story = {
|
|
82
|
+
parameters: { controls: { disable: true } },
|
|
83
|
+
render: () => (
|
|
84
|
+
<div className="w-80 rounded-md border p-3">
|
|
85
|
+
<Space wrap>
|
|
86
|
+
{Array.from({ length: 12 }).map((_, i) => (
|
|
87
|
+
<Button key={i} variant="outline" size="sm">
|
|
88
|
+
标签 {i + 1}
|
|
89
|
+
</Button>
|
|
90
|
+
))}
|
|
91
|
+
</Space>
|
|
92
|
+
</div>
|
|
93
|
+
),
|
|
94
|
+
};
|