@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,116 @@
1
+ ---
2
+ id: item
3
+ name: Item
4
+ type: component
5
+ category: layout
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Item
11
+
12
+ 通用列表项 / 卡片项容器 — shadcn 2025-10 新增。**取代我们之前自实现的 `List.Item` + `List.Item.Meta`**(antd 老 List 在 v6 已 deprecated)。用 5 个语义槽组合:`ItemMedia` 媒体 / `ItemContent` 主体 / `ItemTitle` 标题 / `ItemDescription` 描述 / `ItemActions` 操作。排列用 `ItemGroup`(纵向 + 分隔)或自行 div / ul 自由布局。
13
+
14
+ ## When to use
15
+
16
+ - 消息流 / 通知列表的单条
17
+ - 设置项(标题 + 描述 + 开关 / 按钮)
18
+ - 文件 / 联系人 / 任务卡片化展示
19
+ - 配 Flex / Grid / Card / Sidebar 自由组合排列容器
20
+
21
+ ## When NOT to use
22
+
23
+ - 结构化数据(有列对齐 / 排序)→ `Table` / `DataTable`
24
+ - 完整业务卡片(带 Header / Cover / Footer)→ `Card`
25
+ - 树形嵌套 → `Tree`
26
+
27
+ <!-- auto:props:begin -->
28
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
29
+ | --- | --- | --- | --- | --- |
30
+ | `variant` | `'default' \| 'outline' \| 'muted'` | `"default"` | – | 视觉变体 — `default` 透明无边框(嵌入 List / Group 用);`outline` 卡片化(独立卡片);`muted` 灰底卡片。 |
31
+ | `interactive` | `boolean` | `false` | – | 是否带 hover 高亮 + cursor-pointer(行可点击场景)。 |
32
+ <!-- auto:props:end -->
33
+
34
+ <!-- auto:deps:begin -->
35
+ ### 同库依赖
36
+
37
+ > `teamix-evo ui add item` 时,以下 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
49
+ ```
50
+ <!-- auto:deps:end -->
51
+
52
+ ## AI 生成纪律
53
+
54
+ - **5 个槽是约定不是强制**:`Item` 内子节点顺序就是渲染顺序,可以只用 `ItemContent + ItemActions`,也可以五个都用
55
+ - **`ItemContent`** 必须放主体内容,它有 `min-w-0 flex-1` 用来对抗 truncate;不要外面再裹 div
56
+ - **`ItemTitle / ItemDescription` 自带 `truncate`** — 不希望截断的场景请覆盖 `className="whitespace-normal"`
57
+ - **`variant="default"`(默认)透明无边框** — 嵌入 `ItemGroup` / `List` / `Sidebar` 容器使用
58
+ - **`variant="outline"` 卡片化** — 独立单 Item 卡片(关注小白卡 / 消息中心整条)
59
+ - **`interactive=true` 等价 cursor-pointer + hover 高亮** — 配合 `onClick` 或外层 `<a>` 包裹;**不要**给 Item 加 `role="button"`,完整按钮请用 Button
60
+
61
+ ## Examples
62
+
63
+ ```tsx
64
+ import {
65
+ Item,
66
+ ItemMedia,
67
+ ItemContent,
68
+ ItemTitle,
69
+ ItemDescription,
70
+ ItemActions,
71
+ ItemGroup,
72
+ } from '@/components/ui/item';
73
+ import { Avatar, AvatarFallback } from '@/components/ui/avatar';
74
+ import { Button } from '@/components/ui/button';
75
+ import { Switch } from '@/components/ui/switch';
76
+
77
+ // 设置项
78
+ <Item>
79
+ <ItemContent>
80
+ <ItemTitle>邮件通知</ItemTitle>
81
+ <ItemDescription>有重要事件时给我发邮件</ItemDescription>
82
+ </ItemContent>
83
+ <ItemActions>
84
+ <Switch defaultChecked />
85
+ </ItemActions>
86
+ </Item>
87
+
88
+ // 联系人列表
89
+ <ItemGroup>
90
+ {users.map((u) => (
91
+ <Item key={u.id} interactive>
92
+ <ItemMedia>
93
+ <Avatar><AvatarFallback>{u.initials}</AvatarFallback></Avatar>
94
+ </ItemMedia>
95
+ <ItemContent>
96
+ <ItemTitle>{u.name}</ItemTitle>
97
+ <ItemDescription>{u.email}</ItemDescription>
98
+ </ItemContent>
99
+ <ItemActions>
100
+ <Button variant="ghost" size="sm">邀请</Button>
101
+ </ItemActions>
102
+ </Item>
103
+ ))}
104
+ </ItemGroup>
105
+
106
+ // 独立卡片化 Item
107
+ <Item variant="outline" interactive>
108
+ <ItemContent>
109
+ <ItemTitle>升级到 Pro</ItemTitle>
110
+ <ItemDescription>解锁更多组件与高级模板</ItemDescription>
111
+ </ItemContent>
112
+ <ItemActions>
113
+ <Button>升级</Button>
114
+ </ItemActions>
115
+ </Item>
116
+ ```
@@ -0,0 +1,113 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import {
3
+ Item,
4
+ ItemMedia,
5
+ ItemContent,
6
+ ItemTitle,
7
+ ItemDescription,
8
+ ItemActions,
9
+ ItemGroup,
10
+ } from './item';
11
+ import { Avatar, AvatarFallback } from '@/components/avatar/avatar';
12
+ import { Button } from '@/components/button/button';
13
+ import { Switch } from '@/components/switch/switch';
14
+
15
+ const meta: Meta<typeof Item> = {
16
+ title: '布局与容器 · Layout/Item',
17
+ component: Item,
18
+ tags: ['autodocs'],
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component:
23
+ '通用列表项 / 卡片项 — 5 个语义槽组合(ItemMedia / ItemContent / ItemTitle / ItemDescription / ItemActions),配 ItemGroup 纵向排列。shadcn 2025-10 新增,取代之前自实现的 List.Item(antd 老 List 在 v6 已 deprecated)。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
24
+ },
25
+ },
26
+ },
27
+ argTypes: {
28
+ variant: { control: 'inline-radio', options: ['default', 'outline', 'muted'] },
29
+ interactive: { control: 'boolean' },
30
+ },
31
+ args: { variant: 'default', interactive: false },
32
+ };
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof Item>;
36
+
37
+ export const Playground: Story = {
38
+ render: (args) => (
39
+ <Item {...args}>
40
+ <ItemContent>
41
+ <ItemTitle>邮件通知</ItemTitle>
42
+ <ItemDescription>有重要事件时给我发邮件</ItemDescription>
43
+ </ItemContent>
44
+ <ItemActions>
45
+ <Switch defaultChecked />
46
+ </ItemActions>
47
+ </Item>
48
+ ),
49
+ };
50
+
51
+ const users = [
52
+ { id: 1, name: 'Alice Wong', email: 'alice@example.com', initials: 'AW' },
53
+ { id: 2, name: 'Bob Lin', email: 'bob@example.com', initials: 'BL' },
54
+ { id: 3, name: 'Carol Sun', email: 'carol@example.com', initials: 'CS' },
55
+ ];
56
+
57
+ export const ContactList: Story = {
58
+ parameters: { controls: { disable: true } },
59
+ render: () => (
60
+ <ItemGroup className="w-96 rounded-md border">
61
+ {users.map((u) => (
62
+ <Item key={u.id} interactive className="px-3 py-2">
63
+ <ItemMedia>
64
+ <Avatar>
65
+ <AvatarFallback>{u.initials}</AvatarFallback>
66
+ </Avatar>
67
+ </ItemMedia>
68
+ <ItemContent>
69
+ <ItemTitle>{u.name}</ItemTitle>
70
+ <ItemDescription>{u.email}</ItemDescription>
71
+ </ItemContent>
72
+ <ItemActions>
73
+ <Button variant="ghost" size="sm">
74
+ 邀请
75
+ </Button>
76
+ </ItemActions>
77
+ </Item>
78
+ ))}
79
+ </ItemGroup>
80
+ ),
81
+ };
82
+
83
+ export const OutlineCard: Story = {
84
+ parameters: { controls: { disable: true } },
85
+ render: () => (
86
+ <Item variant="outline" interactive className="w-96">
87
+ <ItemContent>
88
+ <ItemTitle>升级到 Pro</ItemTitle>
89
+ <ItemDescription>解锁更多组件与高级模板</ItemDescription>
90
+ </ItemContent>
91
+ <ItemActions>
92
+ <Button>升级</Button>
93
+ </ItemActions>
94
+ </Item>
95
+ ),
96
+ };
97
+
98
+ export const Muted: Story = {
99
+ parameters: { controls: { disable: true } },
100
+ render: () => (
101
+ <Item variant="muted" className="w-96">
102
+ <ItemContent>
103
+ <ItemTitle>提示</ItemTitle>
104
+ <ItemDescription>该 workspace 还没有任何项目</ItemDescription>
105
+ </ItemContent>
106
+ <ItemActions>
107
+ <Button variant="outline" size="sm">
108
+ 新建
109
+ </Button>
110
+ </ItemActions>
111
+ </Item>
112
+ ),
113
+ };
@@ -0,0 +1,171 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+
4
+ import { cn } from '@/utils/cn';
5
+
6
+ const itemVariants = cva(
7
+ 'flex w-full items-start gap-3 text-sm transition-colors',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: '',
12
+ outline: 'rounded-md border bg-card p-3 text-card-foreground shadow-sm',
13
+ muted: 'rounded-md bg-muted p-3',
14
+ },
15
+ interactive: {
16
+ true: 'cursor-pointer hover:bg-accent',
17
+ false: '',
18
+ },
19
+ },
20
+ defaultVariants: { variant: 'default', interactive: false },
21
+ },
22
+ );
23
+
24
+ export interface ItemProps
25
+ extends React.HTMLAttributes<HTMLDivElement>,
26
+ VariantProps<typeof itemVariants> {
27
+ /**
28
+ * 视觉变体 — `default` 透明无边框(嵌入 List / Group 用);`outline` 卡片化(独立卡片);`muted` 灰底卡片。
29
+ * @default "default"
30
+ */
31
+ variant?: 'default' | 'outline' | 'muted';
32
+ /**
33
+ * 是否带 hover 高亮 + cursor-pointer(行可点击场景)。
34
+ * @default false
35
+ */
36
+ interactive?: boolean;
37
+ }
38
+
39
+ /**
40
+ * 通用列表项 / 卡片项容器 — shadcn 2025-10 新增。
41
+ * 用 `<ItemMedia>` `<ItemContent>` `<ItemTitle>` `<ItemDescription>` `<ItemActions>`
42
+ * 五个语义槽组合,替代之前 antd `List.Item` + `List.Item.Meta` 的强结构。
43
+ *
44
+ * 排列容器请用 `<ItemGroup>`(纵向 + divider)或直接用 div / ul 自行布局。
45
+ */
46
+ const Item = React.forwardRef<HTMLDivElement, ItemProps>(
47
+ ({ variant, interactive, className, ...props }, ref) => (
48
+ <div
49
+ ref={ref}
50
+ className={cn(itemVariants({ variant, interactive }), className)}
51
+ {...props}
52
+ />
53
+ ),
54
+ );
55
+ Item.displayName = 'Item';
56
+
57
+ // ─── ItemMedia(头像 / 图标 / 缩略图槽)──────────────────────────────────
58
+
59
+ export interface ItemMediaProps extends React.HTMLAttributes<HTMLDivElement> {
60
+ /**
61
+ * 媒体尺寸 — `default` 用于头像 / 图标;`lg` 用于缩略图。
62
+ * @default "default"
63
+ */
64
+ size?: 'default' | 'lg';
65
+ }
66
+
67
+ const ItemMedia = React.forwardRef<HTMLDivElement, ItemMediaProps>(
68
+ ({ size = 'default', className, ...props }, ref) => (
69
+ <div
70
+ ref={ref}
71
+ className={cn(
72
+ 'flex shrink-0 items-center justify-center text-muted-foreground',
73
+ size === 'lg' && 'size-16',
74
+ className,
75
+ )}
76
+ {...props}
77
+ />
78
+ ),
79
+ );
80
+ ItemMedia.displayName = 'ItemMedia';
81
+
82
+ // ─── ItemContent(主体内容容器)─────────────────────────────────────────
83
+
84
+ const ItemContent = React.forwardRef<
85
+ HTMLDivElement,
86
+ React.HTMLAttributes<HTMLDivElement>
87
+ >(({ className, ...props }, ref) => (
88
+ <div
89
+ ref={ref}
90
+ className={cn('flex min-w-0 flex-1 flex-col gap-1', className)}
91
+ {...props}
92
+ />
93
+ ));
94
+ ItemContent.displayName = 'ItemContent';
95
+
96
+ // ─── ItemTitle / ItemDescription ───────────────────────────────────────
97
+
98
+ const ItemTitle = React.forwardRef<
99
+ HTMLDivElement,
100
+ React.HTMLAttributes<HTMLDivElement>
101
+ >(({ className, ...props }, ref) => (
102
+ <div
103
+ ref={ref}
104
+ className={cn('truncate text-sm font-semibold leading-none', className)}
105
+ {...props}
106
+ />
107
+ ));
108
+ ItemTitle.displayName = 'ItemTitle';
109
+
110
+ const ItemDescription = React.forwardRef<
111
+ HTMLParagraphElement,
112
+ React.HTMLAttributes<HTMLParagraphElement>
113
+ >(({ className, ...props }, ref) => (
114
+ <p
115
+ ref={ref}
116
+ className={cn('truncate text-sm text-muted-foreground', className)}
117
+ {...props}
118
+ />
119
+ ));
120
+ ItemDescription.displayName = 'ItemDescription';
121
+
122
+ // ─── ItemActions(右侧操作槽)──────────────────────────────────────────
123
+
124
+ const ItemActions = React.forwardRef<
125
+ HTMLDivElement,
126
+ React.HTMLAttributes<HTMLDivElement>
127
+ >(({ className, ...props }, ref) => (
128
+ <div
129
+ ref={ref}
130
+ className={cn('flex shrink-0 items-center gap-2', className)}
131
+ {...props}
132
+ />
133
+ ));
134
+ ItemActions.displayName = 'ItemActions';
135
+
136
+ // ─── ItemGroup(纵向排列容器,自带分隔线)──────────────────────────────
137
+
138
+ export interface ItemGroupProps extends React.HTMLAttributes<HTMLDivElement> {
139
+ /**
140
+ * 项之间是否显示分隔线。
141
+ * @default true
142
+ */
143
+ divided?: boolean;
144
+ }
145
+
146
+ const ItemGroup = React.forwardRef<HTMLDivElement, ItemGroupProps>(
147
+ ({ divided = true, className, ...props }, ref) => (
148
+ <div
149
+ ref={ref}
150
+ role="list"
151
+ className={cn(
152
+ 'flex w-full flex-col',
153
+ divided && 'divide-y divide-border',
154
+ className,
155
+ )}
156
+ {...props}
157
+ />
158
+ ),
159
+ );
160
+ ItemGroup.displayName = 'ItemGroup';
161
+
162
+ export {
163
+ Item,
164
+ ItemMedia,
165
+ ItemContent,
166
+ ItemTitle,
167
+ ItemDescription,
168
+ ItemActions,
169
+ ItemGroup,
170
+ itemVariants,
171
+ };
@@ -0,0 +1,85 @@
1
+ ---
2
+ id: kbd
3
+ name: Kbd
4
+ type: component
5
+ category: foundation
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Kbd
11
+
12
+ 键位展示 — shadcn 2025-10 新增。语义化的 `<kbd>` 元素 + `KbdGroup` 组合容器,用于在 UI 中标注**快捷键**(`⌘K` / `Ctrl + Enter` / `Esc`)。常见于 Command Palette、Tooltip、文档 / 帮助提示。
13
+
14
+ ## When to use
15
+
16
+ - Command Palette 项右侧的快捷键提示
17
+ - Tooltip / 帮助气泡里指明键盘快捷键
18
+ - 文档 / 帮助页中表达"按某键"
19
+
20
+ ## When NOT to use
21
+
22
+ - 引用代码片段 → 用 `<code>` / Typography Code(`<Kbd>` 语义是物理键位)
23
+ - 普通行内强调 → 用 `<strong>` / Typography Text strong
24
+
25
+ <!-- auto:props:begin -->
26
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
27
+ | --- | --- | --- | --- | --- |
28
+ | `size` | `'sm' \| 'default' \| 'lg'` | `"default"` | – | 尺寸 — 与文字行高匹配,大尺寸用于 Command Palette 等突出展示。 |
29
+ <!-- auto:props:end -->
30
+
31
+ <!-- auto:deps:begin -->
32
+ ### 同库依赖
33
+
34
+ > `teamix-evo ui add kbd` 时,以下 entry 会被自动连带安装(无需手动 add)。
35
+
36
+ | Entry | 类型 | 描述 |
37
+ | --- | --- | --- |
38
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
39
+
40
+ ### npm 依赖
41
+
42
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
43
+
44
+ ```bash
45
+ pnpm add class-variance-authority@^0.7.0
46
+ ```
47
+ <!-- auto:deps:end -->
48
+
49
+ ## AI 生成纪律
50
+
51
+ - **单键用 `<Kbd>`,组合键必须用 `<KbdGroup>`** — 不要直接用空格连接多个 Kbd,会丢失语义
52
+ - **`separator` 仅做视觉**(`+` / `→`),按键间距已默认 `gap-1`,常见 mac 风格留空即可
53
+ - **键名规范**:macOS 用 ⌘ ⇧ ⌥ ⌃ ⏎ 符号;Windows 用 `Ctrl` `Shift` `Alt` 缩写。同一界面保持一致
54
+ - **不要嵌套 `<Kbd>` 在按钮内**做"按这个键"的提示 — 按钮已表达可点击,语义重复;改用 `aria-keyshortcuts` + Tooltip
55
+ - **`size="lg"`** 仅用于 Command Palette / 教程页等视觉高亮,默认与正文同高
56
+
57
+ ## Examples
58
+
59
+ ```tsx
60
+ import { Kbd, KbdGroup } from '@/components/ui/kbd';
61
+
62
+ // 单键
63
+ <Kbd>Esc</Kbd>
64
+
65
+ // 组合(mac 风格)
66
+ <KbdGroup>
67
+ <Kbd>⌘</Kbd>
68
+ <Kbd>K</Kbd>
69
+ </KbdGroup>
70
+
71
+ // 组合(Win 风格,带分隔)
72
+ <KbdGroup separator="+">
73
+ <Kbd>Ctrl</Kbd>
74
+ <Kbd>Shift</Kbd>
75
+ <Kbd>P</Kbd>
76
+ </KbdGroup>
77
+
78
+ // 配 Command Palette
79
+ <CommandItem>
80
+ 打开搜索
81
+ <KbdGroup className="ml-auto">
82
+ <Kbd>⌘</Kbd><Kbd>K</Kbd>
83
+ </KbdGroup>
84
+ </CommandItem>
85
+ ```
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Kbd, KbdGroup } from './kbd';
3
+
4
+ const meta: Meta<typeof Kbd> = {
5
+ title: '基础原语 · Foundation/Kbd',
6
+ component: Kbd,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component:
12
+ '键位展示 — 语义化 `<kbd>` 元素,标注快捷键(⌘K / Ctrl + Enter)。单键 + KbdGroup 组合容器,常用于 Command Palette、Tooltip、文档帮助。shadcn 2025-10 新增,antd 体系无对应原子。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ size: { control: 'inline-radio', options: ['sm', 'default', 'lg'] },
18
+ },
19
+ args: { size: 'default', children: 'K' },
20
+ };
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof Kbd>;
24
+
25
+ export const Playground: Story = {};
26
+
27
+ export const Sizes: Story = {
28
+ parameters: { controls: { disable: true } },
29
+ render: () => (
30
+ <div className="flex items-end gap-3">
31
+ <Kbd size="sm">K</Kbd>
32
+ <Kbd size="default">K</Kbd>
33
+ <Kbd size="lg">K</Kbd>
34
+ </div>
35
+ ),
36
+ };
37
+
38
+ export const MacCombo: Story = {
39
+ parameters: { controls: { disable: true } },
40
+ render: () => (
41
+ <KbdGroup>
42
+ <Kbd>⌘</Kbd>
43
+ <Kbd>K</Kbd>
44
+ </KbdGroup>
45
+ ),
46
+ };
47
+
48
+ export const WinCombo: Story = {
49
+ parameters: { controls: { disable: true } },
50
+ render: () => (
51
+ <KbdGroup separator="+">
52
+ <Kbd>Ctrl</Kbd>
53
+ <Kbd>Shift</Kbd>
54
+ <Kbd>P</Kbd>
55
+ </KbdGroup>
56
+ ),
57
+ };
58
+
59
+ export const InContext: Story = {
60
+ parameters: { controls: { disable: true } },
61
+ render: () => (
62
+ <div className="flex items-center justify-between rounded-md border bg-card p-3 text-sm">
63
+ <span>打开命令面板</span>
64
+ <KbdGroup>
65
+ <Kbd>⌘</Kbd>
66
+ <Kbd>K</Kbd>
67
+ </KbdGroup>
68
+ </div>
69
+ ),
70
+ };
@@ -0,0 +1,81 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+
4
+ import { cn } from '@/utils/cn';
5
+
6
+ const kbdVariants = cva(
7
+ 'pointer-events-none inline-flex select-none items-center gap-1 rounded border bg-muted font-mono tabular-nums text-muted-foreground shadow-[0_1px_0_0_rgba(0,0,0,0.08)]',
8
+ {
9
+ variants: {
10
+ size: {
11
+ sm: 'h-4 px-1 text-[10px]',
12
+ default: 'h-5 px-1.5 text-xs',
13
+ lg: 'h-6 px-2 text-sm',
14
+ },
15
+ },
16
+ defaultVariants: { size: 'default' },
17
+ },
18
+ );
19
+
20
+ export interface KbdProps
21
+ extends React.HTMLAttributes<HTMLElement>,
22
+ VariantProps<typeof kbdVariants> {
23
+ /**
24
+ * 尺寸 — 与文字行高匹配,大尺寸用于 Command Palette 等突出展示。
25
+ * @default "default"
26
+ */
27
+ size?: 'sm' | 'default' | 'lg';
28
+ }
29
+
30
+ /**
31
+ * 单个键位展示 — shadcn 2025-10 新增。语义化的 `<kbd>` 元素,
32
+ * 配 `KbdGroup` 表达"⌘ + K"等组合键。
33
+ */
34
+ const Kbd = React.forwardRef<HTMLElement, KbdProps>(
35
+ ({ size, className, ...props }, ref) => (
36
+ <kbd
37
+ ref={ref as React.Ref<HTMLElement>}
38
+ className={cn(kbdVariants({ size }), className)}
39
+ {...props}
40
+ />
41
+ ),
42
+ );
43
+ Kbd.displayName = 'Kbd';
44
+
45
+ export interface KbdGroupProps extends React.HTMLAttributes<HTMLSpanElement> {
46
+ /**
47
+ * 组合分隔符(纯视觉) — 默认 ` `(键位间距即可),如 "Ctrl + K" 风格传 "+";"⌘K" 风格传空串。
48
+ * @default ""
49
+ */
50
+ separator?: string;
51
+ }
52
+
53
+ /**
54
+ * 组合键容器 — 渲染多个 `<Kbd>` 之间的间距与可选分隔符(`Ctrl + K`、`⇧⌘P`)。
55
+ */
56
+ const KbdGroup = React.forwardRef<HTMLSpanElement, KbdGroupProps>(
57
+ ({ separator = '', className, children, ...props }, ref) => {
58
+ const items = React.Children.toArray(children).filter(Boolean);
59
+ return (
60
+ <span
61
+ ref={ref}
62
+ className={cn('inline-flex items-center gap-1', className)}
63
+ {...props}
64
+ >
65
+ {items.map((child, i) => (
66
+ <React.Fragment key={i}>
67
+ {i > 0 && separator ? (
68
+ <span aria-hidden="true" className="text-xs text-muted-foreground">
69
+ {separator}
70
+ </span>
71
+ ) : null}
72
+ {child}
73
+ </React.Fragment>
74
+ ))}
75
+ </span>
76
+ );
77
+ },
78
+ );
79
+ KbdGroup.displayName = 'KbdGroup';
80
+
81
+ export { Kbd, KbdGroup, kbdVariants };