@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.
Files changed (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +336 -0
  3. package/_data.json +12 -0
  4. package/manifest.json +1688 -0
  5. package/package.json +90 -0
  6. package/src/components/accordion/accordion.meta.md +87 -0
  7. package/src/components/accordion/accordion.stories.tsx +67 -0
  8. package/src/components/accordion/accordion.tsx +58 -0
  9. package/src/components/affix/affix.meta.md +80 -0
  10. package/src/components/affix/affix.stories.tsx +57 -0
  11. package/src/components/affix/affix.tsx +97 -0
  12. package/src/components/alert/alert.meta.md +101 -0
  13. package/src/components/alert/alert.stories.tsx +93 -0
  14. package/src/components/alert/alert.tsx +132 -0
  15. package/src/components/alert-dialog/alert-dialog.meta.md +107 -0
  16. package/src/components/alert-dialog/alert-dialog.stories.tsx +81 -0
  17. package/src/components/alert-dialog/alert-dialog.tsx +136 -0
  18. package/src/components/anchor/anchor.meta.md +87 -0
  19. package/src/components/anchor/anchor.stories.tsx +74 -0
  20. package/src/components/anchor/anchor.tsx +130 -0
  21. package/src/components/app/app.meta.md +86 -0
  22. package/src/components/app/app.stories.tsx +62 -0
  23. package/src/components/app/app.tsx +58 -0
  24. package/src/components/aspect-ratio/aspect-ratio.meta.md +81 -0
  25. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +59 -0
  26. package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
  27. package/src/components/auto-complete/auto-complete.meta.md +102 -0
  28. package/src/components/auto-complete/auto-complete.stories.tsx +93 -0
  29. package/src/components/auto-complete/auto-complete.tsx +205 -0
  30. package/src/components/avatar/avatar.meta.md +94 -0
  31. package/src/components/avatar/avatar.stories.tsx +80 -0
  32. package/src/components/avatar/avatar.tsx +126 -0
  33. package/src/components/badge/badge.meta.md +119 -0
  34. package/src/components/badge/badge.stories.tsx +153 -0
  35. package/src/components/badge/badge.tsx +210 -0
  36. package/src/components/breadcrumb/breadcrumb.meta.md +107 -0
  37. package/src/components/breadcrumb/breadcrumb.stories.tsx +84 -0
  38. package/src/components/breadcrumb/breadcrumb.tsx +122 -0
  39. package/src/components/button/button.meta.md +98 -0
  40. package/src/components/button/button.stories.tsx +235 -0
  41. package/src/components/button/button.tsx +160 -0
  42. package/src/components/button-group/button-group.meta.md +92 -0
  43. package/src/components/button-group/button-group.stories.tsx +90 -0
  44. package/src/components/button-group/button-group.tsx +75 -0
  45. package/src/components/calendar/calendar.meta.md +118 -0
  46. package/src/components/calendar/calendar.stories.tsx +68 -0
  47. package/src/components/calendar/calendar.tsx +107 -0
  48. package/src/components/card/card.meta.md +117 -0
  49. package/src/components/card/card.stories.tsx +112 -0
  50. package/src/components/card/card.tsx +222 -0
  51. package/src/components/carousel/carousel.meta.md +117 -0
  52. package/src/components/carousel/carousel.stories.tsx +84 -0
  53. package/src/components/carousel/carousel.tsx +224 -0
  54. package/src/components/cascader/cascader.meta.md +110 -0
  55. package/src/components/cascader/cascader.stories.tsx +108 -0
  56. package/src/components/cascader/cascader.tsx +198 -0
  57. package/src/components/checkbox/checkbox.meta.md +99 -0
  58. package/src/components/checkbox/checkbox.stories.tsx +130 -0
  59. package/src/components/checkbox/checkbox.tsx +125 -0
  60. package/src/components/collapsible/collapsible.meta.md +80 -0
  61. package/src/components/collapsible/collapsible.stories.tsx +35 -0
  62. package/src/components/collapsible/collapsible.tsx +18 -0
  63. package/src/components/color-picker/color-picker.meta.md +84 -0
  64. package/src/components/color-picker/color-picker.stories.tsx +80 -0
  65. package/src/components/color-picker/color-picker.tsx +160 -0
  66. package/src/components/combobox/combobox.meta.md +93 -0
  67. package/src/components/combobox/combobox.stories.tsx +55 -0
  68. package/src/components/combobox/combobox.tsx +130 -0
  69. package/src/components/command/command.meta.md +104 -0
  70. package/src/components/command/command.stories.tsx +59 -0
  71. package/src/components/command/command.tsx +147 -0
  72. package/src/components/context-menu/context-menu.meta.md +90 -0
  73. package/src/components/context-menu/context-menu.stories.tsx +46 -0
  74. package/src/components/context-menu/context-menu.tsx +191 -0
  75. package/src/components/data-table/data-table.meta.md +149 -0
  76. package/src/components/data-table/data-table.stories.tsx +125 -0
  77. package/src/components/data-table/data-table.tsx +185 -0
  78. package/src/components/date-picker/date-picker.meta.md +106 -0
  79. package/src/components/date-picker/date-picker.stories.tsx +58 -0
  80. package/src/components/date-picker/date-picker.tsx +156 -0
  81. package/src/components/descriptions/descriptions.meta.md +78 -0
  82. package/src/components/descriptions/descriptions.stories.tsx +60 -0
  83. package/src/components/descriptions/descriptions.tsx +129 -0
  84. package/src/components/dialog/dialog.meta.md +105 -0
  85. package/src/components/dialog/dialog.stories.tsx +93 -0
  86. package/src/components/dialog/dialog.tsx +128 -0
  87. package/src/components/drawer/drawer.meta.md +96 -0
  88. package/src/components/drawer/drawer.stories.tsx +54 -0
  89. package/src/components/drawer/drawer.tsx +114 -0
  90. package/src/components/dropdown-menu/dropdown-menu.meta.md +103 -0
  91. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +112 -0
  92. package/src/components/dropdown-menu/dropdown-menu.tsx +195 -0
  93. package/src/components/empty/empty.meta.md +81 -0
  94. package/src/components/empty/empty.stories.tsx +46 -0
  95. package/src/components/empty/empty.tsx +47 -0
  96. package/src/components/field/field.meta.md +116 -0
  97. package/src/components/field/field.stories.tsx +117 -0
  98. package/src/components/field/field.tsx +164 -0
  99. package/src/components/flex/flex.meta.md +94 -0
  100. package/src/components/flex/flex.stories.tsx +112 -0
  101. package/src/components/flex/flex.tsx +122 -0
  102. package/src/components/float-button/float-button.meta.md +87 -0
  103. package/src/components/float-button/float-button.stories.tsx +78 -0
  104. package/src/components/float-button/float-button.tsx +143 -0
  105. package/src/components/form/form.meta.md +131 -0
  106. package/src/components/form/form.stories.tsx +122 -0
  107. package/src/components/form/form.tsx +194 -0
  108. package/src/components/grid/grid.meta.md +87 -0
  109. package/src/components/grid/grid.stories.tsx +99 -0
  110. package/src/components/grid/grid.tsx +130 -0
  111. package/src/components/hover-card/hover-card.meta.md +92 -0
  112. package/src/components/hover-card/hover-card.stories.tsx +68 -0
  113. package/src/components/hover-card/hover-card.tsx +29 -0
  114. package/src/components/image/image.meta.md +94 -0
  115. package/src/components/image/image.stories.tsx +55 -0
  116. package/src/components/image/image.tsx +138 -0
  117. package/src/components/input/input.meta.md +109 -0
  118. package/src/components/input/input.stories.tsx +117 -0
  119. package/src/components/input/input.tsx +213 -0
  120. package/src/components/input-group/input-group.meta.md +92 -0
  121. package/src/components/input-group/input-group.stories.tsx +88 -0
  122. package/src/components/input-group/input-group.tsx +107 -0
  123. package/src/components/input-number/input-number.meta.md +91 -0
  124. package/src/components/input-number/input-number.stories.tsx +87 -0
  125. package/src/components/input-number/input-number.tsx +210 -0
  126. package/src/components/input-otp/input-otp.meta.md +105 -0
  127. package/src/components/input-otp/input-otp.stories.tsx +65 -0
  128. package/src/components/input-otp/input-otp.tsx +97 -0
  129. package/src/components/item/item.meta.md +116 -0
  130. package/src/components/item/item.stories.tsx +113 -0
  131. package/src/components/item/item.tsx +171 -0
  132. package/src/components/kbd/kbd.meta.md +85 -0
  133. package/src/components/kbd/kbd.stories.tsx +70 -0
  134. package/src/components/kbd/kbd.tsx +81 -0
  135. package/src/components/label/label.meta.md +91 -0
  136. package/src/components/label/label.stories.tsx +87 -0
  137. package/src/components/label/label.tsx +66 -0
  138. package/src/components/masonry/masonry.meta.md +85 -0
  139. package/src/components/masonry/masonry.stories.tsx +66 -0
  140. package/src/components/masonry/masonry.tsx +59 -0
  141. package/src/components/mentions/mentions.meta.md +89 -0
  142. package/src/components/mentions/mentions.stories.tsx +75 -0
  143. package/src/components/mentions/mentions.tsx +237 -0
  144. package/src/components/menubar/menubar.meta.md +100 -0
  145. package/src/components/menubar/menubar.stories.tsx +81 -0
  146. package/src/components/menubar/menubar.tsx +232 -0
  147. package/src/components/native-select/native-select.meta.md +88 -0
  148. package/src/components/native-select/native-select.stories.tsx +80 -0
  149. package/src/components/native-select/native-select.tsx +54 -0
  150. package/src/components/navigation-menu/navigation-menu.meta.md +108 -0
  151. package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -0
  152. package/src/components/navigation-menu/navigation-menu.tsx +125 -0
  153. package/src/components/notification/notification.meta.md +91 -0
  154. package/src/components/notification/notification.stories.tsx +96 -0
  155. package/src/components/notification/notification.tsx +84 -0
  156. package/src/components/pagination/pagination.meta.md +127 -0
  157. package/src/components/pagination/pagination.stories.tsx +62 -0
  158. package/src/components/pagination/pagination.tsx +285 -0
  159. package/src/components/popconfirm/popconfirm.meta.md +109 -0
  160. package/src/components/popconfirm/popconfirm.stories.tsx +76 -0
  161. package/src/components/popconfirm/popconfirm.tsx +134 -0
  162. package/src/components/popover/popover.meta.md +97 -0
  163. package/src/components/popover/popover.stories.tsx +82 -0
  164. package/src/components/popover/popover.tsx +55 -0
  165. package/src/components/progress/progress.meta.md +86 -0
  166. package/src/components/progress/progress.stories.tsx +75 -0
  167. package/src/components/progress/progress.tsx +195 -0
  168. package/src/components/radio-group/radio-group.meta.md +103 -0
  169. package/src/components/radio-group/radio-group.stories.tsx +77 -0
  170. package/src/components/radio-group/radio-group.tsx +78 -0
  171. package/src/components/rate/rate.meta.md +87 -0
  172. package/src/components/rate/rate.stories.tsx +81 -0
  173. package/src/components/rate/rate.tsx +153 -0
  174. package/src/components/resizable/resizable.meta.md +92 -0
  175. package/src/components/resizable/resizable.stories.tsx +104 -0
  176. package/src/components/resizable/resizable.tsx +56 -0
  177. package/src/components/result/result.meta.md +90 -0
  178. package/src/components/result/result.stories.tsx +71 -0
  179. package/src/components/result/result.tsx +91 -0
  180. package/src/components/scroll-area/scroll-area.meta.md +84 -0
  181. package/src/components/scroll-area/scroll-area.stories.tsx +41 -0
  182. package/src/components/scroll-area/scroll-area.tsx +51 -0
  183. package/src/components/segmented/segmented.meta.md +103 -0
  184. package/src/components/segmented/segmented.stories.tsx +101 -0
  185. package/src/components/segmented/segmented.tsx +138 -0
  186. package/src/components/select/select.meta.md +110 -0
  187. package/src/components/select/select.stories.tsx +100 -0
  188. package/src/components/select/select.tsx +188 -0
  189. package/src/components/separator/separator.meta.md +74 -0
  190. package/src/components/separator/separator.stories.tsx +71 -0
  191. package/src/components/separator/separator.tsx +104 -0
  192. package/src/components/sheet/sheet.meta.md +97 -0
  193. package/src/components/sheet/sheet.stories.tsx +82 -0
  194. package/src/components/sheet/sheet.tsx +139 -0
  195. package/src/components/sidebar/sidebar.meta.md +131 -0
  196. package/src/components/sidebar/sidebar.stories.tsx +82 -0
  197. package/src/components/sidebar/sidebar.tsx +351 -0
  198. package/src/components/skeleton/skeleton.meta.md +95 -0
  199. package/src/components/skeleton/skeleton.stories.tsx +79 -0
  200. package/src/components/skeleton/skeleton.tsx +144 -0
  201. package/src/components/slider/slider.meta.md +94 -0
  202. package/src/components/slider/slider.stories.tsx +69 -0
  203. package/src/components/slider/slider.tsx +86 -0
  204. package/src/components/sonner/sonner.meta.md +96 -0
  205. package/src/components/sonner/sonner.stories.tsx +91 -0
  206. package/src/components/sonner/sonner.tsx +40 -0
  207. package/src/components/space/space.meta.md +94 -0
  208. package/src/components/space/space.stories.tsx +94 -0
  209. package/src/components/space/space.tsx +106 -0
  210. package/src/components/spinner/spinner.meta.md +76 -0
  211. package/src/components/spinner/spinner.stories.tsx +71 -0
  212. package/src/components/spinner/spinner.tsx +64 -0
  213. package/src/components/statistic/statistic.meta.md +99 -0
  214. package/src/components/statistic/statistic.stories.tsx +71 -0
  215. package/src/components/statistic/statistic.tsx +197 -0
  216. package/src/components/steps/steps.meta.md +102 -0
  217. package/src/components/steps/steps.stories.tsx +75 -0
  218. package/src/components/steps/steps.tsx +170 -0
  219. package/src/components/switch/switch.meta.md +92 -0
  220. package/src/components/switch/switch.stories.tsx +75 -0
  221. package/src/components/switch/switch.tsx +101 -0
  222. package/src/components/table/table.meta.md +95 -0
  223. package/src/components/table/table.stories.tsx +75 -0
  224. package/src/components/table/table.tsx +122 -0
  225. package/src/components/tabs/tabs.meta.md +98 -0
  226. package/src/components/tabs/tabs.stories.tsx +70 -0
  227. package/src/components/tabs/tabs.tsx +119 -0
  228. package/src/components/tag/tag.meta.md +94 -0
  229. package/src/components/tag/tag.stories.tsx +77 -0
  230. package/src/components/tag/tag.tsx +185 -0
  231. package/src/components/textarea/textarea.meta.md +83 -0
  232. package/src/components/textarea/textarea.stories.tsx +63 -0
  233. package/src/components/textarea/textarea.tsx +113 -0
  234. package/src/components/time-picker/time-picker.meta.md +83 -0
  235. package/src/components/time-picker/time-picker.stories.tsx +59 -0
  236. package/src/components/time-picker/time-picker.tsx +94 -0
  237. package/src/components/timeline/timeline.meta.md +102 -0
  238. package/src/components/timeline/timeline.stories.tsx +104 -0
  239. package/src/components/timeline/timeline.tsx +147 -0
  240. package/src/components/toggle/toggle.meta.md +88 -0
  241. package/src/components/toggle/toggle.stories.tsx +66 -0
  242. package/src/components/toggle/toggle.tsx +53 -0
  243. package/src/components/toggle-group/toggle-group.meta.md +90 -0
  244. package/src/components/toggle-group/toggle-group.stories.tsx +83 -0
  245. package/src/components/toggle-group/toggle-group.tsx +78 -0
  246. package/src/components/tooltip/tooltip.meta.md +99 -0
  247. package/src/components/tooltip/tooltip.stories.tsx +71 -0
  248. package/src/components/tooltip/tooltip.tsx +93 -0
  249. package/src/components/tour/tour.meta.md +116 -0
  250. package/src/components/tour/tour.stories.tsx +66 -0
  251. package/src/components/tour/tour.tsx +242 -0
  252. package/src/components/transfer/transfer.meta.md +90 -0
  253. package/src/components/transfer/transfer.stories.tsx +68 -0
  254. package/src/components/transfer/transfer.tsx +251 -0
  255. package/src/components/tree/tree.meta.md +111 -0
  256. package/src/components/tree/tree.stories.tsx +109 -0
  257. package/src/components/tree/tree.tsx +367 -0
  258. package/src/components/tree-select/tree-select.meta.md +100 -0
  259. package/src/components/tree-select/tree-select.stories.tsx +80 -0
  260. package/src/components/tree-select/tree-select.tsx +171 -0
  261. package/src/components/typography/typography.meta.md +102 -0
  262. package/src/components/typography/typography.stories.tsx +115 -0
  263. package/src/components/typography/typography.tsx +245 -0
  264. package/src/components/upload/upload.meta.md +111 -0
  265. package/src/components/upload/upload.stories.tsx +75 -0
  266. package/src/components/upload/upload.tsx +265 -0
  267. package/src/components/watermark/watermark.meta.md +95 -0
  268. package/src/components/watermark/watermark.stories.tsx +78 -0
  269. package/src/components/watermark/watermark.tsx +165 -0
  270. 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
+ };