@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,93 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Alert } from './alert';
3
+
4
+ const meta: Meta<typeof Alert> = {
5
+ title: '反馈与浮层 · Feedback/Alert',
6
+ component: Alert,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component:
12
+ '警告提示 — 在不打断用户操作的前提下展示重要信息。四种语义状态(`info` / `success` / `warning` / `error`)对齐 design 语义色;支持 `showIcon` 状态图标、`closable` 一键关闭、`banner` 顶部业务通告梨式。能力对齐 antd Alert,视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ type: {
18
+ control: 'select',
19
+ options: ['info', 'success', 'warning', 'error'],
20
+ },
21
+ showIcon: { control: 'boolean' },
22
+ closable: { control: 'boolean' },
23
+ banner: { control: 'boolean' },
24
+ title: { control: 'text' },
25
+ description: { control: 'text' },
26
+ },
27
+ args: {
28
+ type: 'info',
29
+ showIcon: true,
30
+ closable: false,
31
+ banner: false,
32
+ title: '提示',
33
+ description: '数据将在 10 分钟后刷新。',
34
+ },
35
+ decorators: [
36
+ (Story) => (
37
+ <div className="w-96">
38
+ <Story />
39
+ </div>
40
+ ),
41
+ ],
42
+ };
43
+
44
+ export default meta;
45
+ type Story = StoryObj<typeof Alert>;
46
+
47
+ export const Playground: Story = {};
48
+
49
+ export const Types: Story = {
50
+ parameters: { controls: { disable: true } },
51
+ decorators: [(Story) => <div className="w-96">{Story()}</div>],
52
+ render: () => (
53
+ <div className="flex flex-col gap-3">
54
+ <Alert type="info" title="信息" description="一般性提示。" />
55
+ <Alert type="success" title="成功" description="操作已完成。" />
56
+ <Alert type="warning" title="警告" description="请注意可能的影响。" />
57
+ <Alert type="error" title="错误" description="操作失败,请重试。" />
58
+ </div>
59
+ ),
60
+ };
61
+
62
+ export const Closable: Story = {
63
+ parameters: { controls: { disable: true } },
64
+ render: () => (
65
+ <Alert
66
+ type="warning"
67
+ title="账号即将过期"
68
+ description="请在 7 天内续费。"
69
+ closable
70
+ />
71
+ ),
72
+ };
73
+
74
+ export const Banner: Story = {
75
+ parameters: { controls: { disable: true } },
76
+ decorators: [(Story) => <div className="w-[600px]">{Story()}</div>],
77
+ render: () => (
78
+ <Alert
79
+ type="warning"
80
+ banner
81
+ title="系统维护"
82
+ description="本站将于 02:00 起暂停服务 30 分钟。"
83
+ closable
84
+ />
85
+ ),
86
+ };
87
+
88
+ export const NoIcon: Story = {
89
+ parameters: { controls: { disable: true } },
90
+ render: () => (
91
+ <Alert type="info" showIcon={false} description="纯文本提示,无图标。" />
92
+ ),
93
+ };
@@ -0,0 +1,132 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import {
4
+ AlertCircle,
5
+ AlertTriangle,
6
+ CheckCircle2,
7
+ Info,
8
+ X,
9
+ } from 'lucide-react';
10
+
11
+ import { cn } from '@/utils/cn';
12
+
13
+ const alertVariants = cva(
14
+ 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:size-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7',
15
+ {
16
+ variants: {
17
+ type: {
18
+ info: 'border-blue-200 bg-blue-50 text-blue-900 [&>svg]:text-blue-500 dark:border-blue-900/30 dark:bg-blue-950/40 dark:text-blue-100',
19
+ success:
20
+ 'border-emerald-200 bg-emerald-50 text-emerald-900 [&>svg]:text-emerald-500 dark:border-emerald-900/30 dark:bg-emerald-950/40 dark:text-emerald-100',
21
+ warning:
22
+ 'border-amber-200 bg-amber-50 text-amber-900 [&>svg]:text-amber-500 dark:border-amber-900/30 dark:bg-amber-950/40 dark:text-amber-100',
23
+ error:
24
+ 'border-destructive/30 bg-destructive/10 text-destructive [&>svg]:text-destructive',
25
+ },
26
+ banner: {
27
+ true: 'rounded-none border-x-0',
28
+ false: '',
29
+ },
30
+ },
31
+ defaultVariants: { type: 'info', banner: false },
32
+ },
33
+ );
34
+
35
+ const typeIcon = {
36
+ info: Info,
37
+ success: CheckCircle2,
38
+ warning: AlertTriangle,
39
+ error: AlertCircle,
40
+ } as const;
41
+
42
+ export interface AlertProps
43
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>,
44
+ VariantProps<typeof alertVariants> {
45
+ /**
46
+ * 类型(antd `type` 并集)— 控制语义色与默认图标。
47
+ * @default "info"
48
+ */
49
+ type?: 'info' | 'success' | 'warning' | 'error';
50
+ /**
51
+ * 是否显示图标(antd `showIcon` 并集)。
52
+ * @default true
53
+ */
54
+ showIcon?: boolean;
55
+ /**
56
+ * 是否可关闭,显示右侧 X 按钮(antd `closable` 并集)。
57
+ * @default false
58
+ */
59
+ closable?: boolean;
60
+ /**
61
+ * 关闭按钮点击回调。
62
+ */
63
+ onClose?: () => void;
64
+ /**
65
+ * Banner 模式 — 去除横向圆角,沿屏幕宽度铺满,常用于全局公告条。
66
+ * @default false
67
+ */
68
+ banner?: boolean;
69
+ /** 标题(粗体行)。 */
70
+ title?: React.ReactNode;
71
+ /** 描述(标题下方主体内容)。 */
72
+ description?: React.ReactNode;
73
+ }
74
+
75
+ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
76
+ (
77
+ {
78
+ className,
79
+ type = 'info',
80
+ showIcon = true,
81
+ closable = false,
82
+ onClose,
83
+ banner = false,
84
+ title,
85
+ description,
86
+ children,
87
+ ...props
88
+ },
89
+ ref,
90
+ ) => {
91
+ const [open, setOpen] = React.useState(true);
92
+ if (!open) return null;
93
+ const Icon = typeIcon[type];
94
+ return (
95
+ <div
96
+ ref={ref}
97
+ role="alert"
98
+ className={cn(alertVariants({ type, banner }), className)}
99
+ {...props}
100
+ >
101
+ {showIcon ? <Icon aria-hidden="true" /> : null}
102
+ <div className={cn(showIcon ? '' : 'pl-0')}>
103
+ {title ? (
104
+ <h5 className="mb-1 font-medium leading-none tracking-tight">
105
+ {title}
106
+ </h5>
107
+ ) : null}
108
+ {description ? (
109
+ <div className="text-sm [&_p]:leading-relaxed">{description}</div>
110
+ ) : null}
111
+ {children}
112
+ </div>
113
+ {closable ? (
114
+ <button
115
+ type="button"
116
+ onClick={() => {
117
+ setOpen(false);
118
+ onClose?.();
119
+ }}
120
+ aria-label="Close"
121
+ className="absolute right-3 top-3 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
122
+ >
123
+ <X className="size-4" />
124
+ </button>
125
+ ) : null}
126
+ </div>
127
+ );
128
+ },
129
+ );
130
+ Alert.displayName = 'Alert';
131
+
132
+ export { Alert, alertVariants };
@@ -0,0 +1,107 @@
1
+ ---
2
+ id: alert-dialog
3
+ name: AlertDialog
4
+ type: component
5
+ category: feedback
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # AlertDialog
11
+
12
+ 阻断式确认对话框 — Radix AlertDialog + antd `Modal.confirm` 并集。
13
+ **与 Dialog 关键差异**:
14
+ - 不可点击外部 / ESC 关闭(强制选择 Action / Cancel)
15
+ - 自带 Action / Cancel 子组件,默认走 Button 视觉
16
+ - 无右上角关闭按钮(避免误关)
17
+
18
+ 设计依据:design `principles.md` P4 Safety + `ai-rules/checklist.md` §8 安全 — 危险操作必须二次确认。
19
+
20
+ ## When to use
21
+
22
+ - 删除 / 释放 / 强制重启等**不可逆**操作
23
+ - 离开未保存表单的拦截
24
+ - 高危 SaaS 操作(关闭账号、清空数据)
25
+
26
+ ## When NOT to use
27
+
28
+ - 普通编辑确认 → `Dialog`(可关闭、可取消)
29
+ - 一次性提示 → `Sonner`
30
+ - 非阻断的状态提示 → `Alert`
31
+
32
+ ## Props
33
+
34
+ > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `AlertDialogContent` 的 props;`AlertDialog`(Root)透传 Radix `open / defaultOpen / onOpenChange`。
35
+
36
+ <!-- auto:props:begin -->
37
+ _(no props)_
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 alert-dialog` 时,以下 entry 会被自动连带安装(无需手动 add)。
48
+
49
+ | Entry | 类型 | 描述 |
50
+ | --- | --- | --- |
51
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
52
+ | `button` | component | 通用按钮 — shadcn 实现 + antd 功能扩展(loading / icon / shape / block / dashed variant) |
53
+
54
+ ### npm 依赖
55
+
56
+ > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
57
+
58
+ ```bash
59
+ pnpm add @radix-ui/react-alert-dialog@^1.1.0
60
+ ```
61
+ <!-- auto:deps:end -->
62
+
63
+ > 子组件:`AlertDialog`(Root)/ `AlertDialogTrigger` / `AlertDialogContent` / `AlertDialogHeader` / `AlertDialogFooter` / `AlertDialogTitle` / `AlertDialogDescription` / `AlertDialogAction` / `AlertDialogCancel`。
64
+
65
+ ## AI 生成纪律
66
+
67
+ - **危险动作 Action 必用 destructive variant**:`<AlertDialogAction className={buttonVariants({ variant: 'destructive' })}>...`
68
+ - **Description 列出影响范围**:遵循 design checklist §8 — 删除时列出资源标识 + 影响范围
69
+ - **高危操作要求输入名称校验**(超出 AlertDialog 单次使用范围,但提醒在 Description 引导用户)
70
+ - **Cancel 永远在左,Action 永远在右**:遵循 design P3 Predictability;**不要**为了"突出取消"反置
71
+ - **Title ≤ 1 行**:简短指令式("确认删除?"),Description 才放细节
72
+
73
+ ## Examples
74
+
75
+ ```tsx
76
+ import {
77
+ AlertDialog, AlertDialogTrigger, AlertDialogContent,
78
+ AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
79
+ AlertDialogFooter, AlertDialogAction, AlertDialogCancel,
80
+ } from '@/components/ui/alert-dialog';
81
+ import { Button } from '@/components/ui/button';
82
+ import { buttonVariants } from '@/components/ui/button';
83
+
84
+ <AlertDialog>
85
+ <AlertDialogTrigger asChild>
86
+ <Button variant="destructive">删除项目</Button>
87
+ </AlertDialogTrigger>
88
+ <AlertDialogContent>
89
+ <AlertDialogHeader>
90
+ <AlertDialogTitle>确认删除项目?</AlertDialogTitle>
91
+ <AlertDialogDescription>
92
+ 将永久删除 <b>"运营后台"</b> 项目及其下 142 个资源。<br />
93
+ 此操作不可撤销。
94
+ </AlertDialogDescription>
95
+ </AlertDialogHeader>
96
+ <AlertDialogFooter>
97
+ <AlertDialogCancel>取消</AlertDialogCancel>
98
+ <AlertDialogAction
99
+ className={buttonVariants({ variant: 'destructive' })}
100
+ onClick={() => doDelete()}
101
+ >
102
+ 删除
103
+ </AlertDialogAction>
104
+ </AlertDialogFooter>
105
+ </AlertDialogContent>
106
+ </AlertDialog>
107
+ ```
@@ -0,0 +1,81 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import {
3
+ AlertDialog,
4
+ AlertDialogTrigger,
5
+ AlertDialogContent,
6
+ AlertDialogHeader,
7
+ AlertDialogTitle,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogAction,
11
+ AlertDialogCancel,
12
+ } from './alert-dialog';
13
+ import { Button, buttonVariants } from '@/components/button/button';
14
+
15
+ const meta: Meta<typeof AlertDialogContent> = {
16
+ title: '反馈与浮层 · Feedback/AlertDialog',
17
+ component: AlertDialogContent,
18
+ tags: ['autodocs'],
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component:
23
+ '命令式确认弹窗 — 用于高风险操作(删除、不可恢复变更)的二次确认。基于 Radix AlertDialog,语义角色为 `alertdialog`,自动陷阱焦点 + ESC 关闭;Cancel + Action 双按钮范式对齐 antd `Modal.confirm`。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
24
+ },
25
+ },
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof AlertDialogContent>;
31
+
32
+ export const Destructive: Story = {
33
+ render: () => (
34
+ <AlertDialog>
35
+ <AlertDialogTrigger asChild>
36
+ <Button variant="destructive">删除项目</Button>
37
+ </AlertDialogTrigger>
38
+ <AlertDialogContent>
39
+ <AlertDialogHeader>
40
+ <AlertDialogTitle>确认删除项目?</AlertDialogTitle>
41
+ <AlertDialogDescription>
42
+ 将永久删除 <b>"运营后台"</b> 项目及其下 142 个资源。
43
+ <br />
44
+ 此操作不可撤销。
45
+ </AlertDialogDescription>
46
+ </AlertDialogHeader>
47
+ <AlertDialogFooter>
48
+ <AlertDialogCancel>取消</AlertDialogCancel>
49
+ <AlertDialogAction
50
+ className={buttonVariants({ variant: 'destructive' })}
51
+ >
52
+ 删除
53
+ </AlertDialogAction>
54
+ </AlertDialogFooter>
55
+ </AlertDialogContent>
56
+ </AlertDialog>
57
+ ),
58
+ };
59
+
60
+ export const NormalConfirm: Story = {
61
+ parameters: { controls: { disable: true } },
62
+ render: () => (
63
+ <AlertDialog>
64
+ <AlertDialogTrigger asChild>
65
+ <Button>提交订单</Button>
66
+ </AlertDialogTrigger>
67
+ <AlertDialogContent>
68
+ <AlertDialogHeader>
69
+ <AlertDialogTitle>确认提交?</AlertDialogTitle>
70
+ <AlertDialogDescription>
71
+ 订单提交后 5 分钟内可取消,超时不可撤销。
72
+ </AlertDialogDescription>
73
+ </AlertDialogHeader>
74
+ <AlertDialogFooter>
75
+ <AlertDialogCancel>取消</AlertDialogCancel>
76
+ <AlertDialogAction>确认</AlertDialogAction>
77
+ </AlertDialogFooter>
78
+ </AlertDialogContent>
79
+ </AlertDialog>
80
+ ),
81
+ };
@@ -0,0 +1,136 @@
1
+ import * as React from 'react';
2
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
3
+
4
+ import { cn } from '@/utils/cn';
5
+ import { buttonVariants } from '@/components/button/button';
6
+
7
+ const AlertDialog = AlertDialogPrimitive.Root;
8
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
9
+ const AlertDialogPortal = AlertDialogPrimitive.Portal;
10
+
11
+ const AlertDialogOverlay = React.forwardRef<
12
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
13
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
14
+ >(({ className, ...props }, ref) => (
15
+ <AlertDialogPrimitive.Overlay
16
+ ref={ref}
17
+ className={cn(
18
+ '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',
19
+ className,
20
+ )}
21
+ {...props}
22
+ />
23
+ ));
24
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
25
+
26
+ export interface AlertDialogContentProps
27
+ extends React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> {}
28
+
29
+ const AlertDialogContent = React.forwardRef<
30
+ React.ElementRef<typeof AlertDialogPrimitive.Content>,
31
+ AlertDialogContentProps
32
+ >(({ className, ...props }, ref) => (
33
+ <AlertDialogPortal>
34
+ <AlertDialogOverlay />
35
+ <AlertDialogPrimitive.Content
36
+ ref={ref}
37
+ className={cn(
38
+ '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',
39
+ className,
40
+ )}
41
+ {...props}
42
+ />
43
+ </AlertDialogPortal>
44
+ ));
45
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
46
+
47
+ const AlertDialogHeader = ({
48
+ className,
49
+ ...props
50
+ }: React.HTMLAttributes<HTMLDivElement>) => (
51
+ <div
52
+ className={cn(
53
+ 'flex flex-col space-y-2 text-center sm:text-left',
54
+ className,
55
+ )}
56
+ {...props}
57
+ />
58
+ );
59
+ AlertDialogHeader.displayName = 'AlertDialogHeader';
60
+
61
+ const AlertDialogFooter = ({
62
+ className,
63
+ ...props
64
+ }: React.HTMLAttributes<HTMLDivElement>) => (
65
+ <div
66
+ className={cn(
67
+ 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2',
68
+ className,
69
+ )}
70
+ {...props}
71
+ />
72
+ );
73
+ AlertDialogFooter.displayName = 'AlertDialogFooter';
74
+
75
+ const AlertDialogTitle = React.forwardRef<
76
+ React.ElementRef<typeof AlertDialogPrimitive.Title>,
77
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
78
+ >(({ className, ...props }, ref) => (
79
+ <AlertDialogPrimitive.Title
80
+ ref={ref}
81
+ className={cn('text-lg font-semibold', className)}
82
+ {...props}
83
+ />
84
+ ));
85
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
86
+
87
+ const AlertDialogDescription = React.forwardRef<
88
+ React.ElementRef<typeof AlertDialogPrimitive.Description>,
89
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
90
+ >(({ className, ...props }, ref) => (
91
+ <AlertDialogPrimitive.Description
92
+ ref={ref}
93
+ className={cn('text-sm text-muted-foreground', className)}
94
+ {...props}
95
+ />
96
+ ));
97
+ AlertDialogDescription.displayName =
98
+ AlertDialogPrimitive.Description.displayName;
99
+
100
+ const AlertDialogAction = React.forwardRef<
101
+ React.ElementRef<typeof AlertDialogPrimitive.Action>,
102
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
103
+ >(({ className, ...props }, ref) => (
104
+ <AlertDialogPrimitive.Action
105
+ ref={ref}
106
+ className={cn(buttonVariants(), className)}
107
+ {...props}
108
+ />
109
+ ));
110
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
111
+
112
+ const AlertDialogCancel = React.forwardRef<
113
+ React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
114
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
115
+ >(({ className, ...props }, ref) => (
116
+ <AlertDialogPrimitive.Cancel
117
+ ref={ref}
118
+ className={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
119
+ {...props}
120
+ />
121
+ ));
122
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
123
+
124
+ export {
125
+ AlertDialog,
126
+ AlertDialogPortal,
127
+ AlertDialogOverlay,
128
+ AlertDialogTrigger,
129
+ AlertDialogContent,
130
+ AlertDialogHeader,
131
+ AlertDialogFooter,
132
+ AlertDialogTitle,
133
+ AlertDialogDescription,
134
+ AlertDialogAction,
135
+ AlertDialogCancel,
136
+ };
@@ -0,0 +1,87 @@
1
+ ---
2
+ id: anchor
3
+ name: Anchor
4
+ type: component
5
+ category: navigation
6
+ since: 0.1.0
7
+ package: "@teamix-evo/ui"
8
+ ---
9
+
10
+ # Anchor
11
+
12
+ 锚点导航 — antd 独有补足。**等价 antd `Anchor`**。长文档 / 详情页侧边的目录,点击锚点滚动到对应 `id` 节点,滚动时自动高亮当前可见的锚点(IntersectionObserver)。支持嵌套两层缩进。
13
+
14
+ ## When to use
15
+
16
+ - 文档 / 帮助页 / 设置页的侧边目录
17
+ - 长详情页(订单 / 用户 / 商品)的快捷跳转
18
+ - 协议 / 隐私政策的章节导航
19
+
20
+ ## When NOT to use
21
+
22
+ - 单页应用路由跳转 → `Link` / 路由库
23
+ - 主导航(顶部 / 侧栏菜单)→ `NavigationMenu` / `Sidebar`
24
+ - 步骤导航(顺序操作)→ `Steps`
25
+
26
+ <!-- auto:props:begin -->
27
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
28
+ | --- | --- | --- | --- | --- |
29
+ | `items` | `AnchorItem[]` | – | ✓ | 锚点项树(antd `items` 并集)。 |
30
+ | `offsetTop` | `number` | `0` | – | 当目标元素距视口顶部小于此值时高亮(IntersectionObserver `rootMargin`)。 |
31
+ | `scrollBehavior` | `'smooth' \| 'auto'` | `"smooth"` | – | 点击锚点平滑滚动行为。 |
32
+ | `onChange` | `(key: string) => void` | – | – | 当前高亮变化回调。 |
33
+ <!-- auto:props:end -->
34
+
35
+ <!-- auto:deps:begin -->
36
+ ### 同库依赖
37
+
38
+ > `teamix-evo ui add anchor` 时,以下 entry 会被自动连带安装(无需手动 add)。
39
+
40
+ | Entry | 类型 | 描述 |
41
+ | --- | --- | --- |
42
+ | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
43
+
44
+ ### npm 依赖
45
+
46
+ _无 — 本组件不依赖任何 npm 包。_
47
+ <!-- auto:deps:end -->
48
+
49
+ ## AI 生成纪律
50
+
51
+ - **必须给目标节点设 `id`** — Anchor 用 `document.getElementById(key)` 找元素;**不要**用 `data-*` 或 React ref
52
+ - **`offsetTop` 与你的 Header 高度对齐** — 否则点击锚点后内容会被 header 遮挡
53
+ - **`scrollBehavior="smooth"` 默认开** — 受用户系统 `prefers-reduced-motion` 影响,无需手动判断
54
+ - **嵌套 ≤ 2 层** — 3 层以上侧栏拥挤;深结构改用 Tree 或折叠
55
+ - **不要给 Anchor 包 Affix** — 直接 Tailwind `sticky top-N` 更轻
56
+ - **`onChange`** 是只读回调(同步状态到 URL hash 等),**不要**在里面再调 scrollTo,会无限循环
57
+
58
+ ## Examples
59
+
60
+ ```tsx
61
+ import { Anchor } from '@/components/ui/anchor';
62
+
63
+ const items = [
64
+ { key: 'intro', title: '介绍' },
65
+ {
66
+ key: 'install',
67
+ title: '安装',
68
+ children: [
69
+ { key: 'npm', title: 'npm' },
70
+ { key: 'pnpm', title: 'pnpm' },
71
+ ],
72
+ },
73
+ { key: 'usage', title: '使用' },
74
+ ];
75
+
76
+ <aside className="sticky top-16 w-56">
77
+ <Anchor items={items} offsetTop={64} />
78
+ </aside>
79
+
80
+ // 同步到 URL
81
+ <Anchor
82
+ items={items}
83
+ onChange={(key) => {
84
+ window.history.replaceState(null, '', `#${key}`);
85
+ }}
86
+ />
87
+ ```