@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,95 @@
1
+ ---
2
+ id: table
3
+ name: Table
4
+ type: component
5
+ category: data-display
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Table
11
+
12
+ 基础表格 — 原生 HTML 元素薄包装。**仅视觉与可访问性骨架**(列宽 / 边框 / hover / 选中态);**无排序 / 筛选 / 分页 / 数据流**(那是 `DataTable` 的范畴)。
13
+
14
+ ## When to use
15
+
16
+ - 简单数据展示(< 50 行,无交互)
17
+ - 静态信息表(键值对 / 配置项)
18
+ - 自定义"业务表"的视觉骨架(自己写排序/筛选/选择)
19
+
20
+ ## When NOT to use
21
+
22
+ - 大量数据 / 排序 / 筛选 / 分页 → `DataTable`(基于 TanStack Table)
23
+ - 键值对详情 → `Descriptions`(antd 风格,v0.x)
24
+ - 树结构 → `Tree`(v0.x)
25
+
26
+ ## Props
27
+
28
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。
29
+
30
+ <!-- auto:props:begin -->
31
+ _(组件无 `<Name>Props` interface — props 详见 [`table.tsx`](./table.tsx))_
32
+ <!-- auto:props:end -->
33
+
34
+ ## 依赖
35
+
36
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
37
+
38
+ <!-- auto:deps:begin -->
39
+ ### 同库依赖
40
+
41
+ > `teamix-evo ui add table` 时,以下 entry 会被自动连带安装(无需手动 add)。
42
+
43
+ | Entry | 类型 | 描述 |
44
+ | --- | --- | --- |
45
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
46
+
47
+ ### npm 依赖
48
+
49
+ _无 — 本组件不依赖任何 npm 包。_
50
+ <!-- auto:deps:end -->
51
+
52
+ > 子组件:`Table` / `TableHeader`(thead)/ `TableBody`(tbody)/ `TableFooter`(tfoot)/ `TableHead`(th)/ `TableRow`(tr)/ `TableCell`(td)/ `TableCaption`(caption)。
53
+
54
+ ## AI 生成纪律
55
+
56
+ - **行选中态**:配 `data-state="selected"` 在 `<TableRow>`,自动套高亮样式
57
+ - **列宽自适应**:不要硬编码 `width: 100px`,用 `<col>` 或 className 控制
58
+ - **数字列右对齐**:数字 / 金额 / 时长用 `className="text-right tabular-nums"`
59
+ - **不嵌套 Table**:嵌套表格语义破碎;改用展开行(`expandable`,DataTable 提供)
60
+ - **大数据集 → DataTable**:Table 不做虚拟滚动;> 200 行用 DataTable + virtualizer
61
+
62
+ ## Examples
63
+
64
+ ```tsx
65
+ import {
66
+ Table, TableHeader, TableBody, TableHead, TableRow, TableCell, TableCaption,
67
+ } from '@/components/ui/table';
68
+ import { Badge } from '@/components/ui/badge';
69
+
70
+ <Table>
71
+ <TableCaption>近期发票</TableCaption>
72
+ <TableHeader>
73
+ <TableRow>
74
+ <TableHead className="w-32">编号</TableHead>
75
+ <TableHead>状态</TableHead>
76
+ <TableHead>方式</TableHead>
77
+ <TableHead className="text-right">金额</TableHead>
78
+ </TableRow>
79
+ </TableHeader>
80
+ <TableBody>
81
+ <TableRow>
82
+ <TableCell className="font-medium">INV001</TableCell>
83
+ <TableCell><Badge variant="success">已支付</Badge></TableCell>
84
+ <TableCell>信用卡</TableCell>
85
+ <TableCell className="text-right">¥250.00</TableCell>
86
+ </TableRow>
87
+ <TableRow data-state="selected">
88
+ <TableCell className="font-medium">INV002</TableCell>
89
+ <TableCell><Badge>待支付</Badge></TableCell>
90
+ <TableCell>转账</TableCell>
91
+ <TableCell className="text-right">¥150.00</TableCell>
92
+ </TableRow>
93
+ </TableBody>
94
+ </Table>
95
+ ```
@@ -0,0 +1,75 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import {
3
+ Table,
4
+ TableHeader,
5
+ TableBody,
6
+ TableHead,
7
+ TableRow,
8
+ TableCell,
9
+ TableCaption,
10
+ TableFooter,
11
+ } from './table';
12
+ import { Badge } from '@/components/badge/badge';
13
+
14
+ const meta: Meta<typeof Table> = {
15
+ title: '数据展示 · Data Display/Table',
16
+ component: Table,
17
+ tags: ['autodocs'],
18
+ };
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof Table>;
22
+
23
+ const invoices = [
24
+ { id: 'INV001', status: 'paid', method: '信用卡', amount: 250 },
25
+ { id: 'INV002', status: 'pending', method: '转账', amount: 150 },
26
+ { id: 'INV003', status: 'paid', method: '支付宝', amount: 350 },
27
+ { id: 'INV004', status: 'failed', method: '信用卡', amount: 450 },
28
+ ];
29
+
30
+ export const Default: Story = {
31
+ render: () => (
32
+ <Table>
33
+ <TableCaption>近期发票</TableCaption>
34
+ <TableHeader>
35
+ <TableRow>
36
+ <TableHead className="w-32">编号</TableHead>
37
+ <TableHead>状态</TableHead>
38
+ <TableHead>方式</TableHead>
39
+ <TableHead className="text-right">金额</TableHead>
40
+ </TableRow>
41
+ </TableHeader>
42
+ <TableBody>
43
+ {invoices.map((inv) => (
44
+ <TableRow key={inv.id}>
45
+ <TableCell className="font-medium">{inv.id}</TableCell>
46
+ <TableCell>
47
+ {inv.status === 'paid' ? (
48
+ <Badge variant="success">已支付</Badge>
49
+ ) : inv.status === 'failed' ? (
50
+ <Badge variant="destructive">失败</Badge>
51
+ ) : (
52
+ <Badge>待支付</Badge>
53
+ )}
54
+ </TableCell>
55
+ <TableCell>{inv.method}</TableCell>
56
+ <TableCell className="text-right tabular-nums">
57
+ ¥{inv.amount.toFixed(2)}
58
+ </TableCell>
59
+ </TableRow>
60
+ ))}
61
+ </TableBody>
62
+ <TableFooter>
63
+ <TableRow>
64
+ <TableCell colSpan={3}>合计</TableCell>
65
+ <TableCell className="text-right tabular-nums">
66
+ ¥
67
+ {invoices
68
+ .reduce((sum, x) => sum + x.amount, 0)
69
+ .toFixed(2)}
70
+ </TableCell>
71
+ </TableRow>
72
+ </TableFooter>
73
+ </Table>
74
+ ),
75
+ };
@@ -0,0 +1,122 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from '@/utils/cn';
4
+
5
+ const Table = React.forwardRef<
6
+ HTMLTableElement,
7
+ React.HTMLAttributes<HTMLTableElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div className="relative w-full overflow-auto">
10
+ <table
11
+ ref={ref}
12
+ className={cn('w-full caption-bottom text-sm', className)}
13
+ {...props}
14
+ />
15
+ </div>
16
+ ));
17
+ Table.displayName = 'Table';
18
+
19
+ const TableHeader = React.forwardRef<
20
+ HTMLTableSectionElement,
21
+ React.HTMLAttributes<HTMLTableSectionElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
24
+ ));
25
+ TableHeader.displayName = 'TableHeader';
26
+
27
+ const TableBody = React.forwardRef<
28
+ HTMLTableSectionElement,
29
+ React.HTMLAttributes<HTMLTableSectionElement>
30
+ >(({ className, ...props }, ref) => (
31
+ <tbody
32
+ ref={ref}
33
+ className={cn('[&_tr:last-child]:border-0', className)}
34
+ {...props}
35
+ />
36
+ ));
37
+ TableBody.displayName = 'TableBody';
38
+
39
+ const TableFooter = React.forwardRef<
40
+ HTMLTableSectionElement,
41
+ React.HTMLAttributes<HTMLTableSectionElement>
42
+ >(({ className, ...props }, ref) => (
43
+ <tfoot
44
+ ref={ref}
45
+ className={cn(
46
+ 'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
47
+ className,
48
+ )}
49
+ {...props}
50
+ />
51
+ ));
52
+ TableFooter.displayName = 'TableFooter';
53
+
54
+ const TableRow = React.forwardRef<
55
+ HTMLTableRowElement,
56
+ React.HTMLAttributes<HTMLTableRowElement>
57
+ >(({ className, ...props }, ref) => (
58
+ <tr
59
+ ref={ref}
60
+ className={cn(
61
+ 'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
62
+ className,
63
+ )}
64
+ {...props}
65
+ />
66
+ ));
67
+ TableRow.displayName = 'TableRow';
68
+
69
+ const TableHead = React.forwardRef<
70
+ HTMLTableCellElement,
71
+ React.ThHTMLAttributes<HTMLTableCellElement>
72
+ >(({ className, ...props }, ref) => (
73
+ <th
74
+ ref={ref}
75
+ className={cn(
76
+ 'h-10 px-3 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
77
+ className,
78
+ )}
79
+ {...props}
80
+ />
81
+ ));
82
+ TableHead.displayName = 'TableHead';
83
+
84
+ const TableCell = React.forwardRef<
85
+ HTMLTableCellElement,
86
+ React.TdHTMLAttributes<HTMLTableCellElement>
87
+ >(({ className, ...props }, ref) => (
88
+ <td
89
+ ref={ref}
90
+ className={cn(
91
+ 'p-3 align-middle [&:has([role=checkbox])]:pr-0',
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ ));
97
+ TableCell.displayName = 'TableCell';
98
+
99
+ const TableCaption = React.forwardRef<
100
+ HTMLTableCaptionElement,
101
+ React.HTMLAttributes<HTMLTableCaptionElement>
102
+ >(({ className, ...props }, ref) => (
103
+ <caption
104
+ ref={ref}
105
+ className={cn('mt-4 text-sm text-muted-foreground', className)}
106
+ {...props}
107
+ />
108
+ ));
109
+ TableCaption.displayName = 'TableCaption';
110
+
111
+ export {
112
+ Table,
113
+ TableHeader,
114
+ TableBody,
115
+ TableFooter,
116
+ TableHead,
117
+ TableRow,
118
+ TableCell,
119
+ TableCaption,
120
+ };
121
+
122
+ export type TableProps = React.HTMLAttributes<HTMLTableElement>;
@@ -0,0 +1,98 @@
1
+ ---
2
+ id: tabs
3
+ name: Tabs
4
+ type: component
5
+ category: navigation
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Tabs
11
+
12
+ 标签页 — Radix Tabs + antd `type` (line/card) + `tabBarExtraContent`(`extra`) 并集。
13
+ 两种风格切换:`type="card"`(shadcn 默认胶囊),`type="line"`(antd 默认下划线)。
14
+
15
+ ## When to use
16
+
17
+ - 平级内容切换(详情页的 Overview / Logs / Settings 多视图)
18
+ - 表单分组(基本信息 / 高级配置)
19
+ - 工具栏顶部多视图
20
+
21
+ ## When NOT to use
22
+
23
+ - 多步流程 → `Steps`(v0.x)
24
+ - 内容互斥但需对照 → `RadioGroup` + 切换内容区
25
+ - 折叠展开 → `Accordion` / `Collapsible`
26
+
27
+ ## Props
28
+
29
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `TabsList` 的 props;`Tabs`(Root)透传 Radix `value / defaultValue / onValueChange / orientation / activationMode`。
30
+
31
+ <!-- auto:props:begin -->
32
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
33
+ | --- | --- | --- | --- | --- |
34
+ | `type` | `'line' \| 'card'` | `"card"` | – | 视觉风格(antd `type` 并集)。`card` 是 shadcn 默认胶囊;`line` 是 antd 默认下划线。 |
35
+ | `extra` | `React.ReactNode` | – | – | 右侧附加内容(antd `tabBarExtraContent` 并集);仅 `type="line"` 显式支持(布局灵活)。 用法:把节点放进来即可,组件用 `flex justify-between` 自动撑开。 |
36
+ <!-- auto:props:end -->
37
+
38
+ ## 依赖
39
+
40
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
41
+
42
+ <!-- auto:deps:begin -->
43
+ ### 同库依赖
44
+
45
+ > `teamix-evo ui add tabs` 时,以下 entry 会被自动连带安装(无需手动 add)。
46
+
47
+ | Entry | 类型 | 描述 |
48
+ | --- | --- | --- |
49
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
50
+
51
+ ### npm 依赖
52
+
53
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
54
+
55
+ ```bash
56
+ pnpm add @radix-ui/react-tabs@^1.1.0 class-variance-authority@^0.7.0
57
+ ```
58
+ <!-- auto:deps:end -->
59
+
60
+ > 子组件:`Tabs` / `TabsList` / `TabsTrigger` / `TabsContent`。
61
+
62
+ ## AI 生成纪律
63
+
64
+ - **`type="card"` 默认**:用于嵌入卡片内 / 紧凑场景(shadcn 胶囊视觉)
65
+ - **`type="line"` 用于页面级**:大区块切换、主导航(antd 风格)
66
+ - **`extra` 仅 `type="line"` 视觉对齐**:右侧附加按钮(刷新 / 新建)
67
+ - **value 必稳定**:切换 tab 间不改变 value 字符串
68
+ - **orientation="vertical"** 罕用,改用 Sidebar 模式
69
+
70
+ ## Examples
71
+
72
+ ```tsx
73
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
74
+ import { Button } from '@/components/ui/button';
75
+ import { RefreshCw } from 'lucide-react';
76
+
77
+ // shadcn 胶囊风格
78
+ <Tabs defaultValue="overview" className="w-96">
79
+ <TabsList>
80
+ <TabsTrigger value="overview">概览</TabsTrigger>
81
+ <TabsTrigger value="logs">日志</TabsTrigger>
82
+ <TabsTrigger value="settings">设置</TabsTrigger>
83
+ </TabsList>
84
+ <TabsContent value="overview">...</TabsContent>
85
+ <TabsContent value="logs">...</TabsContent>
86
+ <TabsContent value="settings">...</TabsContent>
87
+ </Tabs>
88
+
89
+ // antd 下划线风格 + extra
90
+ <Tabs defaultValue="all">
91
+ <TabsList type="line" extra={<Button size="sm" icon={<RefreshCw />}>刷新</Button>}>
92
+ <TabsTrigger value="all">全部</TabsTrigger>
93
+ <TabsTrigger value="active">活跃</TabsTrigger>
94
+ <TabsTrigger value="archived">已归档</TabsTrigger>
95
+ </TabsList>
96
+ <TabsContent value="all">...</TabsContent>
97
+ </Tabs>
98
+ ```
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { RefreshCw } from 'lucide-react';
3
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs';
4
+ import { Button } from '@/components/button/button';
5
+
6
+ const meta: Meta<typeof TabsList> = {
7
+ title: '导航 · Navigation/Tabs',
8
+ component: TabsList,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ type: { control: 'inline-radio', options: ['card', 'line'] },
12
+ },
13
+ args: { type: 'card' },
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof TabsList>;
18
+
19
+ export const Card: Story = {
20
+ args: { type: 'card' },
21
+ render: (args) => (
22
+ <Tabs defaultValue="overview" className="w-96">
23
+ <TabsList {...args}>
24
+ <TabsTrigger value="overview">概览</TabsTrigger>
25
+ <TabsTrigger value="logs">日志</TabsTrigger>
26
+ <TabsTrigger value="settings">设置</TabsTrigger>
27
+ </TabsList>
28
+ <TabsContent value="overview">概览内容</TabsContent>
29
+ <TabsContent value="logs">日志内容</TabsContent>
30
+ <TabsContent value="settings">设置内容</TabsContent>
31
+ </Tabs>
32
+ ),
33
+ };
34
+
35
+ export const Line: Story = {
36
+ args: { type: 'line' },
37
+ render: (args) => (
38
+ <Tabs defaultValue="all" className="w-96">
39
+ <TabsList {...args}>
40
+ <TabsTrigger value="all">全部</TabsTrigger>
41
+ <TabsTrigger value="active">活跃</TabsTrigger>
42
+ <TabsTrigger value="archived">已归档</TabsTrigger>
43
+ </TabsList>
44
+ <TabsContent value="all">全部内容</TabsContent>
45
+ <TabsContent value="active">活跃内容</TabsContent>
46
+ <TabsContent value="archived">已归档</TabsContent>
47
+ </Tabs>
48
+ ),
49
+ };
50
+
51
+ export const WithExtra: Story = {
52
+ parameters: { controls: { disable: true } },
53
+ render: () => (
54
+ <Tabs defaultValue="all" className="w-[480px]">
55
+ <TabsList
56
+ type="line"
57
+ extra={
58
+ <Button size="sm" variant="outline" icon={<RefreshCw />}>
59
+ 刷新
60
+ </Button>
61
+ }
62
+ >
63
+ <TabsTrigger value="all">全部</TabsTrigger>
64
+ <TabsTrigger value="active">活跃</TabsTrigger>
65
+ <TabsTrigger value="archived">已归档</TabsTrigger>
66
+ </TabsList>
67
+ <TabsContent value="all">全部内容...</TabsContent>
68
+ </Tabs>
69
+ ),
70
+ };
@@ -0,0 +1,119 @@
1
+ import * as React from 'react';
2
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '@/utils/cn';
6
+
7
+ const Tabs = TabsPrimitive.Root;
8
+
9
+ const tabsListVariants = cva('inline-flex items-center justify-center', {
10
+ variants: {
11
+ type: {
12
+ line: 'border-b border-border w-full justify-start gap-4',
13
+ card: 'h-9 rounded-md bg-muted p-1 text-muted-foreground',
14
+ },
15
+ },
16
+ defaultVariants: { type: 'card' },
17
+ });
18
+
19
+ const tabsTriggerVariants = cva(
20
+ 'inline-flex items-center justify-center whitespace-nowrap text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
21
+ {
22
+ variants: {
23
+ type: {
24
+ line: 'relative px-1 py-2 -mb-px border-b-2 border-transparent text-muted-foreground hover:text-foreground data-[state=active]:border-primary data-[state=active]:text-foreground',
25
+ card: 'rounded-sm px-3 py-1 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
26
+ },
27
+ },
28
+ defaultVariants: { type: 'card' },
29
+ },
30
+ );
31
+
32
+ export interface TabsListProps
33
+ extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>,
34
+ VariantProps<typeof tabsListVariants> {
35
+ /**
36
+ * 视觉风格(antd `type` 并集)。`card` 是 shadcn 默认胶囊;`line` 是 antd 默认下划线。
37
+ * @default "card"
38
+ */
39
+ type?: 'line' | 'card';
40
+ /**
41
+ * 右侧附加内容(antd `tabBarExtraContent` 并集);仅 `type="line"` 显式支持(布局灵活)。
42
+ * 用法:把节点放进来即可,组件用 `flex justify-between` 自动撑开。
43
+ */
44
+ extra?: React.ReactNode;
45
+ }
46
+
47
+ const TabsListContext = React.createContext<{ type: 'line' | 'card' }>({
48
+ type: 'card',
49
+ });
50
+
51
+ const TabsList = React.forwardRef<
52
+ React.ElementRef<typeof TabsPrimitive.List>,
53
+ TabsListProps
54
+ >(({ className, type = 'card', extra, children, ...props }, ref) => {
55
+ const list = (
56
+ <TabsPrimitive.List
57
+ ref={ref}
58
+ className={cn(tabsListVariants({ type }), className)}
59
+ {...props}
60
+ >
61
+ <TabsListContext.Provider value={{ type }}>
62
+ {children}
63
+ </TabsListContext.Provider>
64
+ </TabsPrimitive.List>
65
+ );
66
+ if (extra) {
67
+ return (
68
+ <div
69
+ className={cn(
70
+ 'flex items-center justify-between',
71
+ type === 'line' && 'border-b border-border',
72
+ )}
73
+ >
74
+ {React.cloneElement(list, {
75
+ className: cn(
76
+ tabsListVariants({ type }),
77
+ type === 'line' && 'border-b-0',
78
+ className,
79
+ ),
80
+ })}
81
+ <div className="shrink-0">{extra}</div>
82
+ </div>
83
+ );
84
+ }
85
+ return list;
86
+ });
87
+ TabsList.displayName = TabsPrimitive.List.displayName;
88
+
89
+ const TabsTrigger = React.forwardRef<
90
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
91
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
92
+ >(({ className, ...props }, ref) => {
93
+ const { type } = React.useContext(TabsListContext);
94
+ return (
95
+ <TabsPrimitive.Trigger
96
+ ref={ref}
97
+ className={cn(tabsTriggerVariants({ type }), className)}
98
+ {...props}
99
+ />
100
+ );
101
+ });
102
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
103
+
104
+ const TabsContent = React.forwardRef<
105
+ React.ElementRef<typeof TabsPrimitive.Content>,
106
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
107
+ >(({ className, ...props }, ref) => (
108
+ <TabsPrimitive.Content
109
+ ref={ref}
110
+ className={cn(
111
+ 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
112
+ className,
113
+ )}
114
+ {...props}
115
+ />
116
+ ));
117
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
118
+
119
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
@@ -0,0 +1,94 @@
1
+ ---
2
+ id: tag
3
+ name: Tag
4
+ type: component
5
+ category: foundation
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Tag
11
+
12
+ 标签 — antd 独有补足。**与 Badge 区别**:Tag 偏向**用户可关闭的关键词标签**(分类 / 标签 / 筛选条件),支持 `closable` + 6 种语义色;Badge 偏向**状态徽标**(数字 / 红点 / 状态条)。
13
+
14
+ ## When to use
15
+
16
+ - 关键词 / 标签 / 分类(博客标签、用户兴趣)
17
+ - 当前筛选条件展示(可关闭以移除条件)
18
+ - 列表项的次级标签(优先级 / 类型)
19
+
20
+ ## When NOT to use
21
+
22
+ - 状态指示 → `Badge`
23
+ - 切换按钮 → `Toggle` / `ToggleGroup`
24
+ - 选择类输入 → `Combobox` / `Select`
25
+
26
+ <!-- auto:props:begin -->
27
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
28
+ | --- | --- | --- | --- | --- |
29
+ | `color` | `'default' \| 'primary' \| 'success' \| 'warning' \| 'error' \| 'info'` | `"default"` | – | 语义颜色(antd `color` 并集) — 与 Badge 不同,Tag 偏向**用户可关闭的标签**(关键词 / 分类),Badge 偏向状态徽标。 |
30
+ | `closable` | `boolean` | `false` | – | 显示关闭按钮(antd `closable` 并集)。 |
31
+ | `onClose` | `() => void` | – | – | 关闭回调。 |
32
+ <!-- auto:props:end -->
33
+
34
+ <!-- auto:deps:begin -->
35
+ ### 同库依赖
36
+
37
+ > `teamix-evo ui add tag` 时,以下 entry 会被自动连带安装(无需手动 add)。
38
+
39
+ | Entry | 类型 | 描述 |
40
+ | --- | --- | --- |
41
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
42
+
43
+ ### npm 依赖
44
+
45
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
46
+
47
+ ```bash
48
+ pnpm add class-variance-authority@^0.7.0 lucide-react@^0.460.0
49
+ ```
50
+ <!-- auto:deps:end -->
51
+
52
+ ## AI 生成纪律
53
+
54
+ - **`color` 反映语义**:不要随便选 — `error` 仅用于真错误,`success` 仅用于已通过
55
+ - **`closable` 必配 `onClose`**:让父组件知道哪个 tag 被移除,业务侧维护数组
56
+ - **文字 ≤ 4 个汉字 / 8 字符**:过长的标签视觉破碎
57
+ - **不要嵌套交互**:Tag 内不要放 Button / Link
58
+ - **筛选条件场景**:配 `closable` 让用户一键移除条件
59
+
60
+ ## Examples
61
+
62
+ ```tsx
63
+ import { Tag } from '@/components/ui/tag';
64
+ import * as React from 'react';
65
+
66
+ // 基础
67
+ <Tag>React</Tag>
68
+ <Tag color="primary">Vue</Tag>
69
+ <Tag color="success">已通过</Tag>
70
+ <Tag color="warning">待审核</Tag>
71
+ <Tag color="error">失败</Tag>
72
+
73
+ // 可关闭
74
+ const [tags, setTags] = React.useState(['React', 'Vue', 'Angular']);
75
+ <div className="flex gap-2">
76
+ {tags.map((t) => (
77
+ <Tag
78
+ key={t}
79
+ color="primary"
80
+ closable
81
+ onClose={() => setTags(tags.filter((x) => x !== t))}
82
+ >
83
+ {t}
84
+ </Tag>
85
+ ))}
86
+ </div>
87
+
88
+ // 筛选条件
89
+ <div className="flex flex-wrap gap-2">
90
+ <Tag closable>状态: 运行中</Tag>
91
+ <Tag closable>类型: 服务</Tag>
92
+ <Tag closable>地区: 杭州</Tag>
93
+ </div>
94
+ ```