@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,78 @@
1
+ ---
2
+ id: descriptions
3
+ name: Descriptions
4
+ type: component
5
+ category: layout
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Descriptions
11
+
12
+ 描述列表 — antd 独有补足。**键值对详情**(资源详情 / 用户信息 / 配置预览),声明式 `items` 数组驱动,自动布局到等分列。
13
+
14
+ ## When to use
15
+
16
+ - 资源 / 用户 / 订单详情页的元信息块
17
+ - 弹窗 / 抽屉里的当前状态展示
18
+ - 静态配置预览(以读为主,无编辑)
19
+
20
+ ## When NOT to use
21
+
22
+ - 可编辑表单 → `Form`
23
+ - 表格(行 = 同质对象)→ `Table`
24
+ - 富文本 → `Prose`
25
+
26
+ <!-- auto:props:begin -->
27
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
28
+ | --- | --- | --- | --- | --- |
29
+ | `title` | `React.ReactNode` | – | – | 顶部标题。 |
30
+ | `items` | `DescriptionsItem[]` | – | ✓ | 字段列表。 |
31
+ | `column` | `1 \| 2 \| 3 \| 4` | `3` | – | 列数(antd `column` 并集) — 等分。响应式可在容器侧加 className 处理。 |
32
+ | `layout` | `'horizontal' \| 'vertical'` | `"horizontal"` | – | 布局方向。`horizontal`(默认):label 在左,value 在右;`vertical`:label 在上,value 在下。 |
33
+ | `bordered` | `boolean` | `false` | – | 带边框(每项独立单元格)。 |
34
+ | `size` | `'sm' \| 'default'` | `"default"` | – | 尺寸。 |
35
+ <!-- auto:props:end -->
36
+
37
+ <!-- auto:deps:begin -->
38
+ ### 同库依赖
39
+
40
+ > `teamix-evo ui add descriptions` 时,以下 entry 会被自动连带安装(无需手动 add)。
41
+
42
+ | Entry | 类型 | 描述 |
43
+ | --- | --- | --- |
44
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
45
+
46
+ ### npm 依赖
47
+
48
+ _无 — 本组件不依赖任何 npm 包。_
49
+ <!-- auto:deps:end -->
50
+
51
+ > `DescriptionsItem`:`{ label; value; span?: number }`(`span > 1` 让该项跨多列)。
52
+
53
+ ## AI 生成纪律
54
+
55
+ - **`column` 与窗口宽度匹配**:窄屏 1~2 列,宽屏 3~4 列
56
+ - **关键字段加 span**:重要内容跨 2 列让它更醒目
57
+ - **`bordered={true}` 用于详情页 / Card 内**:有边框更清晰;`bordered={false}` 适合 Sheet / Drawer
58
+ - **value 用 Text 子组件**:状态用 `<Badge>`,链接用 `<Link>`,**不要**纯字符串硬塞颜色
59
+ - **不要超过 12 项**:多了认知负担;> 12 项考虑分组(多个 `Descriptions` 配 Title)
60
+
61
+ ## Examples
62
+
63
+ ```tsx
64
+ import { Descriptions } from '@/components/ui/descriptions';
65
+ import { Badge } from '@/components/ui/badge';
66
+
67
+ <Descriptions
68
+ title="项目信息"
69
+ bordered
70
+ items={[
71
+ { label: '项目名', value: '运营后台' },
72
+ { label: '状态', value: <Badge variant="success">运行中</Badge> },
73
+ { label: '所有者', value: 'lyca' },
74
+ { label: '创建时间', value: '2024-12-01' },
75
+ { label: '描述', value: '中后台运营管理系统', span: 3 },
76
+ ]}
77
+ />
78
+ ```
@@ -0,0 +1,60 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Descriptions } from './descriptions';
3
+ import { Badge } from '@/components/badge/badge';
4
+
5
+ const meta: Meta<typeof Descriptions> = {
6
+ title: '布局与容器 · Layout/Descriptions',
7
+ component: Descriptions,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ '描述列表 — 键值对详情(资源详情 / 用户信息 / 配置预览)。声明式 items 数组驱动,自动布局,支持横/纵布局、bordered、span 跨列。OpenTrek tokens 适配,等价 antd Descriptions。',
14
+ },
15
+ },
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof Descriptions>;
21
+
22
+ const items = [
23
+ { label: '项目名', value: '运营后台' },
24
+ { label: '状态', value: <Badge variant="success">运行中</Badge> },
25
+ { label: '所有者', value: 'lyca' },
26
+ { label: '创建时间', value: '2024-12-01' },
27
+ { label: '更新时间', value: '2026-05-15' },
28
+ { label: '描述', value: '中后台运营管理系统,服务于内部产品团队', span: 3 },
29
+ ];
30
+
31
+ export const Bordered: Story = {
32
+ render: () => (
33
+ <div className="w-[720px]">
34
+ <Descriptions title="项目信息" items={items} bordered />
35
+ </div>
36
+ ),
37
+ };
38
+
39
+ export const Plain: Story = {
40
+ parameters: { controls: { disable: true } },
41
+ render: () => (
42
+ <div className="w-[720px]">
43
+ <Descriptions title="项目信息" items={items} />
44
+ </div>
45
+ ),
46
+ };
47
+
48
+ export const Vertical: Story = {
49
+ parameters: { controls: { disable: true } },
50
+ render: () => (
51
+ <div className="w-[720px]">
52
+ <Descriptions
53
+ title="项目信息(垂直布局)"
54
+ items={items}
55
+ layout="vertical"
56
+ bordered
57
+ />
58
+ </div>
59
+ ),
60
+ };
@@ -0,0 +1,129 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from '@/utils/cn';
4
+
5
+ export interface DescriptionsItem {
6
+ /** 字段标签。 */
7
+ label: React.ReactNode;
8
+ /** 字段值。 */
9
+ value: React.ReactNode;
10
+ /** 跨列数(默认 1)。 */
11
+ span?: number;
12
+ }
13
+
14
+ export interface DescriptionsProps
15
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
16
+ /** 顶部标题。 */
17
+ title?: React.ReactNode;
18
+ /** 字段列表。 */
19
+ items: DescriptionsItem[];
20
+ /**
21
+ * 列数(antd `column` 并集) — 等分。响应式可在容器侧加 className 处理。
22
+ * @default 3
23
+ */
24
+ column?: 1 | 2 | 3 | 4;
25
+ /**
26
+ * 布局方向。`horizontal`(默认):label 在左,value 在右;`vertical`:label 在上,value 在下。
27
+ * @default "horizontal"
28
+ */
29
+ layout?: 'horizontal' | 'vertical';
30
+ /**
31
+ * 带边框(每项独立单元格)。
32
+ * @default false
33
+ */
34
+ bordered?: boolean;
35
+ /**
36
+ * 尺寸。
37
+ * @default "default"
38
+ */
39
+ size?: 'sm' | 'default';
40
+ }
41
+
42
+ const Descriptions = React.forwardRef<HTMLDivElement, DescriptionsProps>(
43
+ (
44
+ {
45
+ title,
46
+ items,
47
+ column = 3,
48
+ layout = 'horizontal',
49
+ bordered = false,
50
+ size = 'default',
51
+ className,
52
+ ...props
53
+ },
54
+ ref,
55
+ ) => {
56
+ const isSm = size === 'sm';
57
+ return (
58
+ <div
59
+ ref={ref}
60
+ className={cn(
61
+ bordered && 'overflow-hidden rounded-md border',
62
+ className,
63
+ )}
64
+ {...props}
65
+ >
66
+ {title ? (
67
+ <div
68
+ className={cn(
69
+ 'font-semibold',
70
+ bordered ? 'border-b bg-muted/50 px-4 py-2 text-sm' : 'mb-3 text-base',
71
+ )}
72
+ >
73
+ {title}
74
+ </div>
75
+ ) : null}
76
+ <div
77
+ className={cn(
78
+ 'grid',
79
+ { 1: 'grid-cols-1', 2: 'grid-cols-2', 3: 'grid-cols-3', 4: 'grid-cols-4' }[column],
80
+ bordered ? 'divide-x divide-y divide-border' : 'gap-x-6 gap-y-3 p-0',
81
+ )}
82
+ >
83
+ {items.map((it, i) => (
84
+ <div
85
+ key={i}
86
+ style={{
87
+ gridColumn: it.span && it.span > 1 ? `span ${it.span}` : undefined,
88
+ }}
89
+ className={cn(
90
+ bordered
91
+ ? layout === 'horizontal'
92
+ ? 'flex divide-x divide-border'
93
+ : 'flex flex-col'
94
+ : layout === 'horizontal'
95
+ ? 'flex gap-3'
96
+ : 'flex flex-col gap-1',
97
+ )}
98
+ >
99
+ <div
100
+ className={cn(
101
+ 'shrink-0 text-muted-foreground',
102
+ isSm ? 'text-xs' : 'text-sm',
103
+ bordered &&
104
+ (layout === 'horizontal'
105
+ ? 'w-32 bg-muted/30 px-3 py-2'
106
+ : 'border-b bg-muted/30 px-3 py-2'),
107
+ !bordered && layout === 'horizontal' && 'min-w-24',
108
+ )}
109
+ >
110
+ {it.label}
111
+ </div>
112
+ <div
113
+ className={cn(
114
+ isSm ? 'text-xs' : 'text-sm',
115
+ bordered ? 'flex-1 px-3 py-2' : 'flex-1',
116
+ )}
117
+ >
118
+ {it.value}
119
+ </div>
120
+ </div>
121
+ ))}
122
+ </div>
123
+ </div>
124
+ );
125
+ },
126
+ );
127
+ Descriptions.displayName = 'Descriptions';
128
+
129
+ export { Descriptions };
@@ -0,0 +1,105 @@
1
+ ---
2
+ id: dialog
3
+ name: Dialog
4
+ type: component
5
+ category: feedback
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Dialog
11
+
12
+ 模态对话框 — Radix Dialog + antd Modal 并集。**组合式 API**(shadcn 风格,非 antd 的 props 配置式),通过 `DialogHeader` / `DialogFooter` / `DialogTitle` / `DialogDescription` 子组件灵活组装内容。
13
+
14
+ > antd 用户对照:`title` → `<DialogTitle>`,`children` → `<DialogContent>`,`footer` → `<DialogFooter>`,`onOk/onCancel` → 自行接 `<DialogClose>` 与回调。
15
+
16
+ ## When to use
17
+
18
+ - 需要用户确认或输入的阻断式交互(表单提交、详情展示)
19
+ - 多步骤向导(Step / 表单分步)
20
+ - 复杂内容(无法用 Popover 装下)
21
+
22
+ ## When NOT to use
23
+
24
+ - 危险确认 → `AlertDialog`(语义更强,无关闭按钮)
25
+ - 侧边滑出 → `Sheet`
26
+ - 移动端底部弹出 → `Drawer`
27
+ - 短暂提示 → `Sonner`(toast)
28
+ - 行内编辑 → `Popover`
29
+
30
+ ## Props
31
+
32
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `DialogContent` 的 props;`Dialog`(Root)透传 Radix `open / defaultOpen / onOpenChange / modal`。
33
+
34
+ <!-- auto:props:begin -->
35
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
36
+ | --- | --- | --- | --- | --- |
37
+ | `showClose` | `boolean` | `true` | – | 是否在右上角显示默认关闭按钮(antd Modal 默认行为)。 |
38
+ <!-- auto:props:end -->
39
+
40
+ ## 依赖
41
+
42
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
43
+
44
+ <!-- auto:deps:begin -->
45
+ ### 同库依赖
46
+
47
+ > `teamix-evo ui add dialog` 时,以下 entry 会被自动连带安装(无需手动 add)。
48
+
49
+ | Entry | 类型 | 描述 |
50
+ | --- | --- | --- |
51
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
52
+
53
+ ### npm 依赖
54
+
55
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
56
+
57
+ ```bash
58
+ pnpm add @radix-ui/react-dialog@^1.1.0 lucide-react@^0.460.0
59
+ ```
60
+ <!-- auto:deps:end -->
61
+
62
+ > 子组件:`Dialog`(Root)/ `DialogTrigger` / `DialogContent` / `DialogHeader` / `DialogFooter` / `DialogTitle` / `DialogDescription` / `DialogClose` / `DialogOverlay` / `DialogPortal`。
63
+
64
+ ## AI 生成纪律
65
+
66
+ - **`DialogTitle` 必有**:无障碍要求(屏幕阅读器朗读),即使视觉上不显示也要 `<VisuallyHidden>` 包一层
67
+ - **`DialogDescription` 推荐有**:不强制但能显著改善可达性
68
+ - **不嵌套 Dialog**:嵌套对话框是反模式 — 一次只展示一个,用步骤导航替代
69
+ - **`onOpenChange` 关闭后清表单**:用户可能 ESC 关闭,需要清理状态
70
+ - **Footer 主操作右对齐**:遵循 design `principles.md` P3 Predictability(主操作位置一致)
71
+ - **危险动作单独走 `AlertDialog`**:Dialog 不强制二次确认,destructive 场景必须切换组件
72
+
73
+ ## Examples
74
+
75
+ ```tsx
76
+ import {
77
+ Dialog, DialogTrigger, DialogContent, DialogHeader,
78
+ DialogFooter, DialogTitle, DialogDescription, DialogClose,
79
+ } from '@/components/ui/dialog';
80
+ import { Button } from '@/components/ui/button';
81
+
82
+ // 基础确认
83
+ <Dialog>
84
+ <DialogTrigger asChild>
85
+ <Button variant="outline">编辑</Button>
86
+ </DialogTrigger>
87
+ <DialogContent className="sm:max-w-md">
88
+ <DialogHeader>
89
+ <DialogTitle>编辑资料</DialogTitle>
90
+ <DialogDescription>保存后立即生效。</DialogDescription>
91
+ </DialogHeader>
92
+ {/* form fields */}
93
+ <DialogFooter>
94
+ <DialogClose asChild>
95
+ <Button variant="outline">取消</Button>
96
+ </DialogClose>
97
+ <Button>保存</Button>
98
+ </DialogFooter>
99
+ </DialogContent>
100
+ </Dialog>
101
+
102
+ // 受控
103
+ const [open, setOpen] = React.useState(false);
104
+ <Dialog open={open} onOpenChange={setOpen}>...</Dialog>
105
+ ```
@@ -0,0 +1,93 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import {
3
+ Dialog,
4
+ DialogTrigger,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogFooter,
8
+ DialogTitle,
9
+ DialogDescription,
10
+ DialogClose,
11
+ } from './dialog';
12
+ import { Button } from '@/components/button/button';
13
+ import { Input } from '@/components/input/input';
14
+ import { Label } from '@/components/label/label';
15
+
16
+ const meta: Meta<typeof DialogContent> = {
17
+ title: '反馈与浮层 · Feedback/Dialog',
18
+ component: DialogContent,
19
+ tags: ['autodocs'],
20
+ parameters: {
21
+ docs: {
22
+ description: {
23
+ component:
24
+ '模态对话框 — 用于承载需要用户聚焦完成的任务(表单 / 确认 / 查看详情)。基于 Radix Dialog(焦点管理、ESC 关闭、`aria-modal` 自动) + antd Modal 范式(`DialogHeader` / `DialogFooter` / `DialogTitle` / `DialogDescription` 子组件组合)。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
25
+ },
26
+ },
27
+ },
28
+ argTypes: {
29
+ showClose: { control: 'boolean' },
30
+ },
31
+ args: { showClose: true },
32
+ };
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof DialogContent>;
36
+
37
+ export const Playground: Story = {
38
+ render: (args) => (
39
+ <Dialog>
40
+ <DialogTrigger asChild>
41
+ <Button variant="outline">打开对话框</Button>
42
+ </DialogTrigger>
43
+ <DialogContent {...args} className="sm:max-w-md">
44
+ <DialogHeader>
45
+ <DialogTitle>编辑资料</DialogTitle>
46
+ <DialogDescription>保存后立即对所有页面生效。</DialogDescription>
47
+ </DialogHeader>
48
+ <div className="grid gap-3 py-2">
49
+ <div className="grid grid-cols-4 items-center gap-3">
50
+ <Label htmlFor="name" className="text-right">
51
+ 名称
52
+ </Label>
53
+ <Input id="name" defaultValue="lyca" className="col-span-3" />
54
+ </div>
55
+ <div className="grid grid-cols-4 items-center gap-3">
56
+ <Label htmlFor="bio" className="text-right">
57
+ 简介
58
+ </Label>
59
+ <Input id="bio" defaultValue="Frontend" className="col-span-3" />
60
+ </div>
61
+ </div>
62
+ <DialogFooter>
63
+ <DialogClose asChild>
64
+ <Button variant="outline">取消</Button>
65
+ </DialogClose>
66
+ <Button>保存</Button>
67
+ </DialogFooter>
68
+ </DialogContent>
69
+ </Dialog>
70
+ ),
71
+ };
72
+
73
+ export const NoCloseButton: Story = {
74
+ parameters: { controls: { disable: true } },
75
+ render: () => (
76
+ <Dialog>
77
+ <DialogTrigger asChild>
78
+ <Button variant="outline">隐藏右上角关闭</Button>
79
+ </DialogTrigger>
80
+ <DialogContent showClose={false}>
81
+ <DialogHeader>
82
+ <DialogTitle>必须选择操作</DialogTitle>
83
+ <DialogDescription>必须点击下方按钮才能关闭。</DialogDescription>
84
+ </DialogHeader>
85
+ <DialogFooter>
86
+ <DialogClose asChild>
87
+ <Button>明白</Button>
88
+ </DialogClose>
89
+ </DialogFooter>
90
+ </DialogContent>
91
+ </Dialog>
92
+ ),
93
+ };
@@ -0,0 +1,128 @@
1
+ import * as React from 'react';
2
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
3
+ import { X } from 'lucide-react';
4
+
5
+ import { cn } from '@/utils/cn';
6
+
7
+ const Dialog = DialogPrimitive.Root;
8
+ const DialogTrigger = DialogPrimitive.Trigger;
9
+ const DialogPortal = DialogPrimitive.Portal;
10
+ const DialogClose = DialogPrimitive.Close;
11
+
12
+ const DialogOverlay = React.forwardRef<
13
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
14
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
15
+ >(({ className, ...props }, ref) => (
16
+ <DialogPrimitive.Overlay
17
+ ref={ref}
18
+ className={cn(
19
+ 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ ));
25
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
26
+
27
+ export interface DialogContentProps
28
+ extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
29
+ /**
30
+ * 是否在右上角显示默认关闭按钮(antd Modal 默认行为)。
31
+ * @default true
32
+ */
33
+ showClose?: boolean;
34
+ }
35
+
36
+ const DialogContent = React.forwardRef<
37
+ React.ElementRef<typeof DialogPrimitive.Content>,
38
+ DialogContentProps
39
+ >(({ className, children, showClose = true, ...props }, ref) => (
40
+ <DialogPortal>
41
+ <DialogOverlay />
42
+ <DialogPrimitive.Content
43
+ ref={ref}
44
+ className={cn(
45
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg',
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ {showClose ? (
52
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none">
53
+ <X className="size-4" />
54
+ <span className="sr-only">Close</span>
55
+ </DialogPrimitive.Close>
56
+ ) : null}
57
+ </DialogPrimitive.Content>
58
+ </DialogPortal>
59
+ ));
60
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
61
+
62
+ const DialogHeader = ({
63
+ className,
64
+ ...props
65
+ }: React.HTMLAttributes<HTMLDivElement>) => (
66
+ <div
67
+ className={cn(
68
+ 'flex flex-col space-y-1.5 text-center sm:text-left',
69
+ className,
70
+ )}
71
+ {...props}
72
+ />
73
+ );
74
+ DialogHeader.displayName = 'DialogHeader';
75
+
76
+ const DialogFooter = ({
77
+ className,
78
+ ...props
79
+ }: React.HTMLAttributes<HTMLDivElement>) => (
80
+ <div
81
+ className={cn(
82
+ 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2',
83
+ className,
84
+ )}
85
+ {...props}
86
+ />
87
+ );
88
+ DialogFooter.displayName = 'DialogFooter';
89
+
90
+ const DialogTitle = React.forwardRef<
91
+ React.ElementRef<typeof DialogPrimitive.Title>,
92
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
93
+ >(({ className, ...props }, ref) => (
94
+ <DialogPrimitive.Title
95
+ ref={ref}
96
+ className={cn(
97
+ 'text-lg font-semibold leading-none tracking-tight',
98
+ className,
99
+ )}
100
+ {...props}
101
+ />
102
+ ));
103
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
104
+
105
+ const DialogDescription = React.forwardRef<
106
+ React.ElementRef<typeof DialogPrimitive.Description>,
107
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
108
+ >(({ className, ...props }, ref) => (
109
+ <DialogPrimitive.Description
110
+ ref={ref}
111
+ className={cn('text-sm text-muted-foreground', className)}
112
+ {...props}
113
+ />
114
+ ));
115
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
116
+
117
+ export {
118
+ Dialog,
119
+ DialogPortal,
120
+ DialogOverlay,
121
+ DialogTrigger,
122
+ DialogClose,
123
+ DialogContent,
124
+ DialogHeader,
125
+ DialogFooter,
126
+ DialogTitle,
127
+ DialogDescription,
128
+ };
@@ -0,0 +1,96 @@
1
+ ---
2
+ id: drawer
3
+ name: Drawer
4
+ type: component
5
+ category: feedback
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Drawer
11
+
12
+ 底部可拖拽抽屉 — 基于 [`vaul`](https://github.com/emilkowalski/vaul),iOS 风格,**移动端体验最佳**。
13
+ **与 Sheet 区别**:Sheet 模态阻断 + 边缘滑入(桌面端);Drawer 底部 + 可向下拖拽 dismiss(移动端 / 触屏)。
14
+
15
+ ## When to use
16
+
17
+ - 移动端(< sm 断点)的设置 / 详情面板
18
+ - 触屏可拖拽 dismiss 的临时面板
19
+ - 需要 iOS / 现代移动 OS 视觉一致性的场景
20
+
21
+ ## When NOT to use
22
+
23
+ - 桌面端 → `Sheet`(更紧凑 + 多方向支持)
24
+ - 阻断式确认 → `AlertDialog`
25
+ - 普通模态 → `Dialog`
26
+
27
+ > 实际项目通常**响应式切换**:`md` 以下用 Drawer,`md` 以上用 Sheet/Dialog。
28
+
29
+ ## Props
30
+
31
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `DrawerContent` 的 props;`Drawer`(Root)透传 vaul `open / defaultOpen / onOpenChange / shouldScaleBackground / dismissible / snapPoints / onClose`。
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
+ > `teamix-evo ui add drawer` 时,以下 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 vaul@^1.0.0
56
+ ```
57
+ <!-- auto:deps:end -->
58
+
59
+ > 子组件:`Drawer` / `DrawerTrigger` / `DrawerContent` / `DrawerHeader` / `DrawerFooter` / `DrawerTitle` / `DrawerDescription` / `DrawerClose`。
60
+
61
+ ## AI 生成纪律
62
+
63
+ - **`shouldScaleBackground={true}` 默认**:背景元素轻微缩小 + 圆角,iOS 视觉一致;桌面端可关闭
64
+ - **`dismissible={false}` 用于"必选"场景**:阻止下拉关闭,用户必须点 Action / Cancel
65
+ - **抽屉顶部把手已自动渲染**:不要再手动加,vaul 自带视觉
66
+ - **`snapPoints`**:支持半屏 / 全屏阶段(如 `['148px', '300px', 1]`),vaul 原生
67
+ - **不要在 Drawer 内放 Drawer**:嵌套抽屉是反模式
68
+
69
+ ## Examples
70
+
71
+ ```tsx
72
+ import {
73
+ Drawer, DrawerTrigger, DrawerContent, DrawerHeader,
74
+ DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose,
75
+ } from '@/components/ui/drawer';
76
+ import { Button } from '@/components/ui/button';
77
+
78
+ <Drawer>
79
+ <DrawerTrigger asChild>
80
+ <Button variant="outline">打开抽屉</Button>
81
+ </DrawerTrigger>
82
+ <DrawerContent>
83
+ <DrawerHeader>
84
+ <DrawerTitle>发起讨论</DrawerTitle>
85
+ <DrawerDescription>讨论将通知到该项目所有成员。</DrawerDescription>
86
+ </DrawerHeader>
87
+ <div className="px-4 pb-4">{/* form */}</div>
88
+ <DrawerFooter>
89
+ <Button>提交</Button>
90
+ <DrawerClose asChild>
91
+ <Button variant="outline">取消</Button>
92
+ </DrawerClose>
93
+ </DrawerFooter>
94
+ </DrawerContent>
95
+ </Drawer>
96
+ ```