@teamix-evo/ui 0.1.1 → 0.3.0

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 (295) hide show
  1. package/README.md +184 -184
  2. package/manifest.json +680 -492
  3. package/package.json +20 -10
  4. package/src/components/accordion/accordion.meta.md +5 -4
  5. package/src/components/accordion/accordion.stories.tsx +14 -9
  6. package/src/components/accordion/accordion.tsx +104 -8
  7. package/src/components/affix/affix.meta.md +20 -2
  8. package/src/components/affix/affix.stories.tsx +102 -25
  9. package/src/components/affix/affix.tsx +79 -9
  10. package/src/components/alert/alert.meta.md +44 -13
  11. package/src/components/alert/alert.stories.tsx +66 -21
  12. package/src/components/alert/alert.tsx +81 -34
  13. package/src/components/alert-dialog/alert-dialog.meta.md +61 -16
  14. package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
  15. package/src/components/alert-dialog/alert-dialog.tsx +60 -13
  16. package/src/components/anchor/anchor.meta.md +8 -3
  17. package/src/components/anchor/anchor.stories.tsx +3 -3
  18. package/src/components/anchor/anchor.tsx +2 -2
  19. package/src/components/app/app.meta.md +9 -4
  20. package/src/components/app/app.stories.tsx +9 -7
  21. package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
  22. package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
  23. package/src/components/auto-complete/auto-complete.meta.md +14 -6
  24. package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
  25. package/src/components/auto-complete/auto-complete.tsx +119 -71
  26. package/src/components/avatar/avatar.meta.md +6 -7
  27. package/src/components/avatar/avatar.stories.tsx +21 -3
  28. package/src/components/avatar/avatar.tsx +24 -23
  29. package/src/components/badge/badge.meta.md +10 -9
  30. package/src/components/badge/badge.stories.tsx +2 -2
  31. package/src/components/badge/badge.tsx +9 -15
  32. package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
  33. package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
  34. package/src/components/breadcrumb/breadcrumb.tsx +22 -8
  35. package/src/components/button/button.meta.md +258 -21
  36. package/src/components/button/button.stories.tsx +549 -41
  37. package/src/components/button/button.tsx +335 -33
  38. package/src/components/button/demo/as-child.tsx +24 -0
  39. package/src/components/button/demo/basic.tsx +8 -0
  40. package/src/components/button/demo/block.tsx +16 -0
  41. package/src/components/button/demo/loading.tsx +19 -0
  42. package/src/components/button/demo/shapes.tsx +18 -0
  43. package/src/components/button/demo/sizes.tsx +19 -0
  44. package/src/components/button/demo/variants.tsx +19 -0
  45. package/src/components/button/demo/with-icon.tsx +20 -0
  46. package/src/components/calendar/calendar.meta.md +13 -3
  47. package/src/components/calendar/calendar.stories.tsx +6 -6
  48. package/src/components/calendar/calendar.tsx +73 -8
  49. package/src/components/card/card.meta.md +27 -5
  50. package/src/components/card/card.stories.tsx +42 -3
  51. package/src/components/card/card.tsx +146 -63
  52. package/src/components/carousel/carousel.meta.md +4 -3
  53. package/src/components/carousel/carousel.stories.tsx +11 -6
  54. package/src/components/cascader/cascader.meta.md +47 -17
  55. package/src/components/cascader/cascader.stories.tsx +22 -10
  56. package/src/components/cascader/cascader.tsx +428 -85
  57. package/src/components/checkbox/checkbox.meta.md +75 -7
  58. package/src/components/checkbox/checkbox.stories.tsx +161 -3
  59. package/src/components/checkbox/checkbox.tsx +77 -9
  60. package/src/components/collapsible/collapsible.meta.md +14 -6
  61. package/src/components/collapsible/collapsible.stories.tsx +10 -2
  62. package/src/components/collapsible/collapsible.tsx +93 -6
  63. package/src/components/color-picker/color-picker.meta.md +12 -7
  64. package/src/components/color-picker/color-picker.stories.tsx +86 -7
  65. package/src/components/color-picker/color-picker.tsx +20 -9
  66. package/src/components/command/command.meta.md +29 -13
  67. package/src/components/command/command.stories.tsx +4 -4
  68. package/src/components/command/command.tsx +19 -8
  69. package/src/components/context-menu/context-menu.meta.md +11 -8
  70. package/src/components/context-menu/context-menu.stories.tsx +11 -3
  71. package/src/components/context-menu/context-menu.tsx +21 -8
  72. package/src/components/data-table/data-table.meta.md +6 -5
  73. package/src/components/data-table/data-table.stories.tsx +13 -6
  74. package/src/components/data-table/data-table.tsx +2 -2
  75. package/src/components/date-picker/date-picker.meta.md +88 -19
  76. package/src/components/date-picker/date-picker.stories.tsx +55 -5
  77. package/src/components/date-picker/date-picker.tsx +1489 -91
  78. package/src/components/descriptions/descriptions.meta.md +10 -5
  79. package/src/components/descriptions/descriptions.stories.tsx +3 -3
  80. package/src/components/descriptions/descriptions.tsx +22 -14
  81. package/src/components/dialog/dialog.meta.md +76 -13
  82. package/src/components/dialog/dialog.stories.tsx +182 -20
  83. package/src/components/dialog/dialog.tsx +67 -15
  84. package/src/components/dialog/imperative.tsx +252 -0
  85. package/src/components/drawer/drawer.meta.md +33 -34
  86. package/src/components/drawer/drawer.stories.tsx +29 -12
  87. package/src/components/drawer/drawer.tsx +22 -113
  88. package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
  89. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
  90. package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
  91. package/src/components/ellipsis/ellipsis.meta.md +87 -0
  92. package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
  93. package/src/components/ellipsis/ellipsis.tsx +153 -0
  94. package/src/components/empty/empty.meta.md +9 -4
  95. package/src/components/empty/empty.stories.tsx +4 -4
  96. package/src/components/empty/empty.tsx +10 -3
  97. package/src/components/field/field.meta.md +47 -9
  98. package/src/components/field/field.stories.tsx +385 -5
  99. package/src/components/field/field.tsx +263 -35
  100. package/src/components/filter-bar/filter-bar.meta.md +92 -0
  101. package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
  102. package/src/components/filter-bar/filter-bar.tsx +568 -0
  103. package/src/components/flex/flex.meta.md +54 -6
  104. package/src/components/flex/flex.stories.tsx +107 -20
  105. package/src/components/flex/flex.tsx +27 -4
  106. package/src/components/float-button/float-button.meta.md +8 -3
  107. package/src/components/float-button/float-button.stories.tsx +9 -7
  108. package/src/components/float-button/float-button.tsx +1 -1
  109. package/src/components/form/form.meta.md +39 -17
  110. package/src/components/form/form.stories.tsx +350 -3
  111. package/src/components/form/form.tsx +101 -35
  112. package/src/components/grid/grid.meta.md +7 -2
  113. package/src/components/grid/grid.stories.tsx +6 -4
  114. package/src/components/hover-card/hover-card.meta.md +20 -9
  115. package/src/components/hover-card/hover-card.stories.tsx +34 -5
  116. package/src/components/hover-card/hover-card.tsx +51 -13
  117. package/src/components/icon/DEVELOPMENT.md +809 -0
  118. package/src/components/icon/icon.meta.md +170 -0
  119. package/src/components/icon/icon.stories.tsx +344 -0
  120. package/src/components/icon/icon.tsx +248 -0
  121. package/src/components/image/image.meta.md +9 -4
  122. package/src/components/image/image.stories.tsx +3 -3
  123. package/src/components/image/image.tsx +6 -4
  124. package/src/components/input/demo/basic.tsx +12 -0
  125. package/src/components/input/demo/clearable.tsx +21 -0
  126. package/src/components/input/demo/show-count.tsx +18 -0
  127. package/src/components/input/demo/sizes.tsx +15 -0
  128. package/src/components/input/input.meta.md +39 -33
  129. package/src/components/input/input.stories.tsx +62 -35
  130. package/src/components/input/input.tsx +97 -98
  131. package/src/components/input-group/input-group.meta.md +54 -22
  132. package/src/components/input-group/input-group.stories.tsx +49 -16
  133. package/src/components/input-group/input-group.tsx +44 -8
  134. package/src/components/input-number/input-number.meta.md +64 -7
  135. package/src/components/input-number/input-number.stories.tsx +46 -8
  136. package/src/components/input-number/input-number.tsx +99 -26
  137. package/src/components/input-otp/input-otp.meta.md +4 -3
  138. package/src/components/input-otp/input-otp.stories.tsx +3 -3
  139. package/src/components/input-otp/input-otp.tsx +1 -1
  140. package/src/components/item/item.meta.md +8 -3
  141. package/src/components/item/item.stories.tsx +8 -5
  142. package/src/components/item/item.tsx +7 -6
  143. package/src/components/kbd/kbd.meta.md +13 -4
  144. package/src/components/kbd/kbd.stories.tsx +4 -4
  145. package/src/components/kbd/kbd.tsx +10 -5
  146. package/src/components/label/label.meta.md +18 -10
  147. package/src/components/label/label.stories.tsx +64 -6
  148. package/src/components/label/label.tsx +91 -19
  149. package/src/components/masonry/masonry.meta.md +8 -3
  150. package/src/components/masonry/masonry.stories.tsx +7 -5
  151. package/src/components/masonry/masonry.tsx +1 -0
  152. package/src/components/mentions/mentions.meta.md +36 -6
  153. package/src/components/mentions/mentions.stories.tsx +120 -6
  154. package/src/components/mentions/mentions.tsx +11 -5
  155. package/src/components/menubar/menubar.meta.md +30 -12
  156. package/src/components/menubar/menubar.stories.tsx +62 -2
  157. package/src/components/menubar/menubar.tsx +9 -9
  158. package/src/components/native-select/native-select.meta.md +8 -3
  159. package/src/components/native-select/native-select.stories.tsx +8 -5
  160. package/src/components/native-select/native-select.tsx +1 -1
  161. package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
  162. package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
  163. package/src/components/navigation-menu/navigation-menu.tsx +8 -4
  164. package/src/components/notification/notification.meta.md +52 -10
  165. package/src/components/notification/notification.stories.tsx +11 -9
  166. package/src/components/notification/notification.tsx +36 -21
  167. package/src/components/page-header/DEVELOPMENT.md +842 -0
  168. package/src/components/page-header/page-header.meta.md +208 -0
  169. package/src/components/page-header/page-header.stories.tsx +421 -0
  170. package/src/components/page-header/page-header.tsx +281 -0
  171. package/src/components/pagination/pagination.meta.md +140 -37
  172. package/src/components/pagination/pagination.stories.tsx +232 -10
  173. package/src/components/pagination/pagination.tsx +355 -63
  174. package/src/components/popconfirm/popconfirm.meta.md +9 -4
  175. package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
  176. package/src/components/popconfirm/popconfirm.tsx +2 -2
  177. package/src/components/popover/popover.meta.md +62 -5
  178. package/src/components/popover/popover.stories.tsx +83 -7
  179. package/src/components/popover/popover.tsx +77 -28
  180. package/src/components/progress/progress.meta.md +38 -6
  181. package/src/components/progress/progress.stories.tsx +3 -3
  182. package/src/components/progress/progress.tsx +24 -16
  183. package/src/components/radio-group/radio-group.meta.md +79 -7
  184. package/src/components/radio-group/radio-group.stories.tsx +39 -3
  185. package/src/components/radio-group/radio-group.tsx +149 -18
  186. package/src/components/rate/rate.meta.md +35 -4
  187. package/src/components/rate/rate.stories.tsx +13 -5
  188. package/src/components/rate/rate.tsx +37 -10
  189. package/src/components/resizable/resizable.meta.md +7 -4
  190. package/src/components/resizable/resizable.stories.tsx +6 -6
  191. package/src/components/resizable/resizable.tsx +1 -1
  192. package/src/components/result/result.meta.md +7 -2
  193. package/src/components/result/result.stories.tsx +4 -8
  194. package/src/components/result/result.tsx +24 -15
  195. package/src/components/scroll-area/scroll-area.meta.md +4 -3
  196. package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
  197. package/src/components/scroll-area/scroll-area.tsx +3 -3
  198. package/src/components/segmented/segmented.meta.md +7 -4
  199. package/src/components/segmented/segmented.stories.tsx +37 -8
  200. package/src/components/segmented/segmented.tsx +15 -7
  201. package/src/components/select/select.meta.md +197 -52
  202. package/src/components/select/select.stories.tsx +238 -63
  203. package/src/components/select/select.tsx +718 -171
  204. package/src/components/separator/separator.meta.md +4 -3
  205. package/src/components/separator/separator.stories.tsx +3 -3
  206. package/src/components/separator/separator.tsx +3 -7
  207. package/src/components/sheet/sheet.meta.md +32 -16
  208. package/src/components/sheet/sheet.stories.tsx +116 -10
  209. package/src/components/sheet/sheet.tsx +116 -29
  210. package/src/components/sidebar/sidebar.meta.md +37 -18
  211. package/src/components/sidebar/sidebar.stories.tsx +701 -29
  212. package/src/components/sidebar/sidebar.tsx +615 -142
  213. package/src/components/skeleton/skeleton.meta.md +4 -5
  214. package/src/components/skeleton/skeleton.stories.tsx +4 -4
  215. package/src/components/skeleton/skeleton.tsx +7 -7
  216. package/src/components/slider/slider.meta.md +57 -5
  217. package/src/components/slider/slider.stories.tsx +58 -6
  218. package/src/components/slider/slider.tsx +154 -13
  219. package/src/components/sonner/sonner.meta.md +58 -7
  220. package/src/components/sonner/sonner.stories.tsx +78 -5
  221. package/src/components/sonner/sonner.tsx +137 -8
  222. package/src/components/spinner/spinner.meta.md +62 -13
  223. package/src/components/spinner/spinner.stories.tsx +66 -14
  224. package/src/components/spinner/spinner.tsx +111 -9
  225. package/src/components/statistic/statistic.meta.md +7 -2
  226. package/src/components/statistic/statistic.stories.tsx +3 -7
  227. package/src/components/statistic/statistic.tsx +5 -6
  228. package/src/components/steps/steps.meta.md +18 -4
  229. package/src/components/steps/steps.stories.tsx +43 -3
  230. package/src/components/steps/steps.tsx +15 -12
  231. package/src/components/switch/switch.meta.md +51 -5
  232. package/src/components/switch/switch.stories.tsx +6 -6
  233. package/src/components/switch/switch.tsx +109 -41
  234. package/src/components/table/table.meta.md +17 -6
  235. package/src/components/table/table.stories.tsx +10 -5
  236. package/src/components/table/table.tsx +4 -4
  237. package/src/components/tabs/tabs.meta.md +38 -25
  238. package/src/components/tabs/tabs.stories.tsx +111 -25
  239. package/src/components/tabs/tabs.tsx +125 -54
  240. package/src/components/tag/tag.meta.md +105 -40
  241. package/src/components/tag/tag.stories.tsx +189 -16
  242. package/src/components/tag/tag.tsx +222 -21
  243. package/src/components/textarea/textarea.meta.md +35 -19
  244. package/src/components/textarea/textarea.stories.tsx +32 -6
  245. package/src/components/textarea/textarea.tsx +33 -9
  246. package/src/components/time-picker/time-picker.meta.md +124 -32
  247. package/src/components/time-picker/time-picker.stories.tsx +85 -15
  248. package/src/components/time-picker/time-picker.tsx +913 -61
  249. package/src/components/timeline/timeline.meta.md +14 -6
  250. package/src/components/timeline/timeline.stories.tsx +37 -7
  251. package/src/components/timeline/timeline.tsx +35 -14
  252. package/src/components/toggle/toggle.meta.md +5 -4
  253. package/src/components/toggle/toggle.stories.tsx +4 -4
  254. package/src/components/toggle/toggle.tsx +4 -3
  255. package/src/components/toggle-group/toggle-group.meta.md +5 -4
  256. package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
  257. package/src/components/toggle-group/toggle-group.tsx +2 -2
  258. package/src/components/tooltip/tooltip.meta.md +55 -5
  259. package/src/components/tooltip/tooltip.stories.tsx +42 -5
  260. package/src/components/tooltip/tooltip.tsx +81 -21
  261. package/src/components/tour/tour.meta.md +9 -4
  262. package/src/components/tour/tour.stories.tsx +3 -3
  263. package/src/components/tour/tour.tsx +4 -4
  264. package/src/components/transfer/transfer.meta.md +11 -6
  265. package/src/components/transfer/transfer.stories.tsx +4 -8
  266. package/src/components/transfer/transfer.tsx +28 -21
  267. package/src/components/tree/tree.meta.md +63 -5
  268. package/src/components/tree/tree.stories.tsx +31 -12
  269. package/src/components/tree/tree.tsx +9 -8
  270. package/src/components/tree-select/tree-select.meta.md +59 -8
  271. package/src/components/tree-select/tree-select.stories.tsx +3 -3
  272. package/src/components/tree-select/tree-select.tsx +42 -7
  273. package/src/components/typography/typography.meta.md +61 -14
  274. package/src/components/typography/typography.stories.tsx +12 -11
  275. package/src/components/typography/typography.tsx +43 -28
  276. package/src/components/upload/upload.meta.md +49 -4
  277. package/src/components/upload/upload.stories.tsx +72 -12
  278. package/src/components/upload/upload.tsx +170 -37
  279. package/src/components/watermark/watermark.meta.md +7 -2
  280. package/src/components/watermark/watermark.stories.tsx +101 -9
  281. package/src/components/watermark/watermark.tsx +1 -0
  282. package/src/hooks/use-breakpoint.ts +117 -0
  283. package/src/hooks/use-debounce-callback.ts +52 -0
  284. package/src/hooks/use-mobile.ts +23 -0
  285. package/src/stories/theme-tokens.stories.tsx +747 -0
  286. package/src/utils/trigger-input.ts +53 -0
  287. package/src/components/button-group/button-group.meta.md +0 -92
  288. package/src/components/button-group/button-group.stories.tsx +0 -90
  289. package/src/components/button-group/button-group.tsx +0 -75
  290. package/src/components/combobox/combobox.meta.md +0 -93
  291. package/src/components/combobox/combobox.stories.tsx +0 -55
  292. package/src/components/combobox/combobox.tsx +0 -130
  293. package/src/components/space/space.meta.md +0 -94
  294. package/src/components/space/space.stories.tsx +0 -94
  295. package/src/components/space/space.tsx +0 -106
@@ -1,13 +1,36 @@
1
1
  import * as React from 'react';
2
2
  import * as DialogPrimitive from '@radix-ui/react-dialog';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
3
4
  import { X } from 'lucide-react';
4
5
 
5
6
  import { cn } from '@/utils/cn';
6
7
 
7
8
  const Dialog = DialogPrimitive.Root;
8
- const DialogTrigger = DialogPrimitive.Trigger;
9
9
  const DialogPortal = DialogPrimitive.Portal;
10
- const DialogClose = DialogPrimitive.Close;
10
+
11
+ const DialogTrigger = React.forwardRef<
12
+ React.ElementRef<typeof DialogPrimitive.Trigger>,
13
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger>
14
+ >(({ className, ...props }, ref) => (
15
+ <DialogPrimitive.Trigger
16
+ ref={ref}
17
+ className={cn('cursor-pointer', className)}
18
+ {...props}
19
+ />
20
+ ));
21
+ DialogTrigger.displayName = DialogPrimitive.Trigger.displayName;
22
+
23
+ const DialogClose = React.forwardRef<
24
+ React.ElementRef<typeof DialogPrimitive.Close>,
25
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>
26
+ >(({ className, ...props }, ref) => (
27
+ <DialogPrimitive.Close
28
+ ref={ref}
29
+ className={cn('cursor-pointer', className)}
30
+ {...props}
31
+ />
32
+ ));
33
+ DialogClose.displayName = DialogPrimitive.Close.displayName;
11
34
 
12
35
  const DialogOverlay = React.forwardRef<
13
36
  React.ElementRef<typeof DialogPrimitive.Overlay>,
@@ -24,32 +47,62 @@ const DialogOverlay = React.forwardRef<
24
47
  ));
25
48
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
26
49
 
50
+ /**
51
+ * Dialog content 变体。
52
+ * - 圆角统一绑定 `--radius-dialog`(16px,OpenTrek)
53
+ * - 移除 border,纯白 + shadow 作为视觉分界
54
+ * - size 绑定 `--layout-dialog-{sm|md|lg|xl}` tokens
55
+ */
56
+ const dialogContentVariants = cva(
57
+ 'fixed left-1/2 top-1/2 z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border-0 bg-background p-6 shadow-lg duration-200 rounded-[var(--radius-dialog)] 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',
58
+ {
59
+ variants: {
60
+ size: {
61
+ sm: 'max-w-[var(--layout-dialog-sm)]',
62
+ md: 'max-w-[var(--layout-dialog-md)]',
63
+ lg: 'max-w-[var(--layout-dialog-lg)]',
64
+ xl: 'max-w-[var(--layout-dialog-xl)]',
65
+ },
66
+ },
67
+ defaultVariants: { size: 'md' },
68
+ },
69
+ );
70
+
71
+ export type DialogSize = NonNullable<
72
+ VariantProps<typeof dialogContentVariants>['size']
73
+ >;
74
+
27
75
  export interface DialogContentProps
28
- extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
76
+ extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,
77
+ VariantProps<typeof dialogContentVariants> {
29
78
  /**
30
- * 是否在右上角显示默认关闭按钮(antd Modal 默认行为)。
79
+ * 是否在标题区右侧显示默认关闭按钮(antd Modal 默认行为)。
31
80
  * @default true
32
81
  */
33
82
  showClose?: boolean;
83
+ /**
84
+ * 对话框尺寸 — 绑定 design tokens `--layout-dialog-{sm|md|lg|xl}`。
85
+ * OpenTrek 基准:sm=400 / md=600 / lg=800 / xl=1200。
86
+ * @default "md"
87
+ */
88
+ size?: DialogSize;
34
89
  }
35
90
 
36
91
  const DialogContent = React.forwardRef<
37
92
  React.ElementRef<typeof DialogPrimitive.Content>,
38
93
  DialogContentProps
39
- >(({ className, children, showClose = true, ...props }, ref) => (
94
+ >(({ className, children, showClose = true, size = 'md', ...props }, ref) => (
40
95
  <DialogPortal>
41
96
  <DialogOverlay />
42
97
  <DialogPrimitive.Content
43
98
  ref={ref}
44
- className={cn(
45
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg',
46
- className,
47
- )}
99
+ data-slot="dialog-content"
100
+ className={cn(dialogContentVariants({ size }), className)}
48
101
  {...props}
49
102
  >
50
103
  {children}
51
104
  {showClose ? (
52
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none">
105
+ <DialogPrimitive.Close className="absolute right-4 top-4 inline-flex size-6 cursor-pointer items-center justify-center rounded-sm text-muted-foreground opacity-70 ring-offset-background transition-opacity hover:bg-accent hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none">
53
106
  <X className="size-4" />
54
107
  <span className="sr-only">Close</span>
55
108
  </DialogPrimitive.Close>
@@ -64,10 +117,7 @@ const DialogHeader = ({
64
117
  ...props
65
118
  }: React.HTMLAttributes<HTMLDivElement>) => (
66
119
  <div
67
- className={cn(
68
- 'flex flex-col space-y-1.5 text-center sm:text-left',
69
- className,
70
- )}
120
+ className={cn('flex flex-col gap-1.5 text-center sm:text-left', className)}
71
121
  {...props}
72
122
  />
73
123
  );
@@ -93,6 +143,7 @@ const DialogTitle = React.forwardRef<
93
143
  >(({ className, ...props }, ref) => (
94
144
  <DialogPrimitive.Title
95
145
  ref={ref}
146
+ data-slot="dialog-title"
96
147
  className={cn(
97
148
  'text-lg font-semibold leading-none tracking-tight',
98
149
  className,
@@ -108,7 +159,7 @@ const DialogDescription = React.forwardRef<
108
159
  >(({ className, ...props }, ref) => (
109
160
  <DialogPrimitive.Description
110
161
  ref={ref}
111
- className={cn('text-sm text-muted-foreground', className)}
162
+ className={cn('text-xs text-muted-foreground', className)}
112
163
  {...props}
113
164
  />
114
165
  ));
@@ -125,4 +176,5 @@ export {
125
176
  DialogFooter,
126
177
  DialogTitle,
127
178
  DialogDescription,
179
+ dialogContentVariants,
128
180
  };
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Dialog 命令式 API — 对齐 antd `Modal.confirm` / `Modal.alert` 的函数式调用范式。
3
+ *
4
+ * 使用方式:
5
+ * ```tsx
6
+ * import { confirm, alert } from '@/components/ui/dialog/imperative';
7
+ *
8
+ * // confirm — 双按钮(取消 + 确认),返回 Promise<boolean>
9
+ * const ok = await confirm({ title: '确认删除?', content: '不可撤销' });
10
+ *
11
+ * // alert — 单按钮(知道了),返回 Promise<void>
12
+ * await alert({ title: '操作完成', content: '已保存成功。' });
13
+ *
14
+ * // tone — 等价 cd `Dialog.confirm({ type: 'error' / 'warning' / 'success' })`
15
+ * await confirm({ tone: 'warning', title: '即将停止服务?', content: '...' });
16
+ * ```
17
+ *
18
+ * 底层通过 `createRoot` 挂载临时 React 节点,关闭后自动卸载 + 清理 DOM。
19
+ */
20
+ import * as React from 'react';
21
+ import { createRoot } from 'react-dom/client';
22
+ import {
23
+ AlertCircle,
24
+ AlertTriangle,
25
+ CheckCircle2,
26
+ Info,
27
+ XCircle,
28
+ } from 'lucide-react';
29
+
30
+ import { cn } from '@/utils/cn';
31
+ import { buttonVariants } from '@/components/button/button';
32
+
33
+ // ─── Types ─────────────────────────────────────────────────────────────────
34
+
35
+ /**
36
+ * 弹窗语气(对齐 cd `Dialog.confirm/alert` 的 `type` preset):
37
+ * - `'info'` — 蓝色 Info icon(默认无 icon 时的"中性提示")
38
+ * - `'success'` — 绿色 CheckCircle2 icon
39
+ * - `'warning'` — 黄色 AlertTriangle icon
40
+ * - `'error'` — 红色 XCircle icon
41
+ *
42
+ * 不传时无 icon,仅纯文字(简化模式)。
43
+ */
44
+ export type DialogTone = 'info' | 'success' | 'warning' | 'error';
45
+
46
+ export interface ConfirmOptions {
47
+ /** 标题(必填)。 */
48
+ title: React.ReactNode;
49
+ /** 描述内容。 */
50
+ content?: React.ReactNode;
51
+ /** 确认按钮文案。@default "确认" */
52
+ okText?: string;
53
+ /** 取消按钮文案。@default "取消" */
54
+ cancelText?: string;
55
+ /** 确认按钮样式变体。@default "default" */
56
+ variant?: 'default' | 'destructive';
57
+ /**
58
+ * 语气 — 控制左侧 icon + icon 色(`tone='error'` 同时把 `variant` 默认为 `destructive`)。
59
+ * 等价 cd `Dialog.confirm({ type: 'error' })`。
60
+ * @default 无(简化模式,无 icon)
61
+ */
62
+ tone?: DialogTone;
63
+ }
64
+
65
+ export interface AlertOptions {
66
+ /** 标题(必填)。 */
67
+ title: React.ReactNode;
68
+ /** 描述内容。 */
69
+ content?: React.ReactNode;
70
+ /** 按钮文案。@default "知道了" */
71
+ okText?: string;
72
+ /**
73
+ * 语气 — 控制左侧 icon + icon 色。等价 cd `Dialog.alert({ type: 'success' })`。
74
+ * @default 无(简化模式,无 icon)
75
+ */
76
+ tone?: DialogTone;
77
+ }
78
+
79
+ // ─── 内部渲染组件 ──────────────────────────────────────────────────────────
80
+
81
+ // 不引入 AlertDialog 组件(避免循环依赖 + 减少打包体积),
82
+ // 直接使用相同的视觉类名构建最小化 modal。
83
+
84
+ const overlayClass = 'fixed inset-0 z-50 bg-black/80 animate-in fade-in-0';
85
+ const contentClass =
86
+ 'fixed left-1/2 top-1/2 z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border-0 bg-background p-6 shadow-lg animate-in fade-in-0 zoom-in-95';
87
+ /** maxWidth / borderRadius 使用 CSS 变量,不能写 arbitrary value(ADR 0008)。 */
88
+ const contentStyle: React.CSSProperties = {
89
+ maxWidth: 'var(--layout-dialog-sm)',
90
+ borderRadius: 'var(--radius-dialog)',
91
+ };
92
+
93
+ // tone → icon + 颜色 token 映射
94
+ const TONE_META: Record<
95
+ DialogTone,
96
+ { Icon: React.ComponentType<{ className?: string }>; colorClass: string }
97
+ > = {
98
+ info: { Icon: Info, colorClass: 'text-primary' },
99
+ success: { Icon: CheckCircle2, colorClass: 'text-success' },
100
+ warning: { Icon: AlertTriangle, colorClass: 'text-warning' },
101
+ error: { Icon: XCircle, colorClass: 'text-destructive' },
102
+ };
103
+
104
+ function ToneIcon({ tone }: { tone?: DialogTone }) {
105
+ if (!tone) return null;
106
+ const meta = TONE_META[tone] ?? { Icon: AlertCircle, colorClass: '' };
107
+ const { Icon, colorClass } = meta;
108
+ return (
109
+ <Icon className={cn('mt-0.5 size-5 shrink-0', colorClass)} aria-hidden />
110
+ );
111
+ }
112
+
113
+ function ConfirmDialog({
114
+ title,
115
+ content,
116
+ okText = '确认',
117
+ cancelText = '取消',
118
+ variant,
119
+ tone,
120
+ onResult,
121
+ }: ConfirmOptions & { onResult: (ok: boolean) => void }) {
122
+ // tone='error' 时默认确认按钮也是 destructive(危险动作语义)
123
+ const effectiveVariant: 'default' | 'destructive' =
124
+ variant ?? (tone === 'error' ? 'destructive' : 'default');
125
+ return (
126
+ <div role="alertdialog" aria-modal="true" aria-labelledby="__imp_title">
127
+ <div className={overlayClass} onClick={() => onResult(false)} />
128
+ <div className={contentClass} style={contentStyle}>
129
+ <div className="flex gap-3">
130
+ <ToneIcon tone={tone} />
131
+ <div className="flex flex-1 flex-col gap-2 text-left">
132
+ <h2 id="__imp_title" className="text-lg font-semibold">
133
+ {title}
134
+ </h2>
135
+ {content && (
136
+ <p className="text-xs text-muted-foreground">{content}</p>
137
+ )}
138
+ </div>
139
+ </div>
140
+ <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2">
141
+ <button
142
+ type="button"
143
+ className={cn(
144
+ buttonVariants({ variant: 'outline' }),
145
+ 'mt-2 sm:mt-0',
146
+ )}
147
+ onClick={() => onResult(false)}
148
+ >
149
+ {cancelText}
150
+ </button>
151
+ <button
152
+ type="button"
153
+ className={cn(buttonVariants({ variant: effectiveVariant }))}
154
+ onClick={() => onResult(true)}
155
+ >
156
+ {okText}
157
+ </button>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ );
162
+ }
163
+
164
+ function AlertDialog({
165
+ title,
166
+ content,
167
+ okText = '知道了',
168
+ tone,
169
+ onResult,
170
+ }: AlertOptions & { onResult: () => void }) {
171
+ return (
172
+ <div role="alertdialog" aria-modal="true" aria-labelledby="__imp_title">
173
+ <div className={overlayClass} />
174
+ <div className={contentClass} style={contentStyle}>
175
+ <div className="flex gap-3">
176
+ <ToneIcon tone={tone} />
177
+ <div className="flex flex-1 flex-col gap-2 text-left">
178
+ <h2 id="__imp_title" className="text-lg font-semibold">
179
+ {title}
180
+ </h2>
181
+ {content && (
182
+ <p className="text-xs text-muted-foreground">{content}</p>
183
+ )}
184
+ </div>
185
+ </div>
186
+ <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2">
187
+ <button
188
+ type="button"
189
+ className={cn(buttonVariants())}
190
+ onClick={() => onResult()}
191
+ >
192
+ {okText}
193
+ </button>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ );
198
+ }
199
+
200
+ // ─── 挂载 / 卸载工具 ───────────────────────────────────────────────────────
201
+
202
+ function mount(element: React.ReactElement): () => void {
203
+ const container = document.createElement('div');
204
+ container.setAttribute('data-dialog-imperative', '');
205
+ document.body.appendChild(container);
206
+ const root = createRoot(container);
207
+ root.render(element);
208
+ return () => {
209
+ // 延迟卸载,确保 state 回调完成后再清理
210
+ setTimeout(() => {
211
+ root.unmount();
212
+ container.remove();
213
+ }, 0);
214
+ };
215
+ }
216
+
217
+ // ─── 公开 API ───────────────────────────────────────────────────────────────
218
+
219
+ /**
220
+ * 命令式确认弹窗 — 返回 `Promise<boolean>`:
221
+ * - 用户点击确认 → `true`
222
+ * - 用户点击取消 / 点击遮罩 → `false`
223
+ *
224
+ * 等价 cd `Dialog.confirm({ ... })` / antd `Modal.confirm({ ... })`。
225
+ */
226
+ export function confirm(options: ConfirmOptions): Promise<boolean> {
227
+ return new Promise<boolean>((resolve) => {
228
+ let cleanup: (() => void) | undefined;
229
+ const handleResult = (ok: boolean) => {
230
+ cleanup?.();
231
+ resolve(ok);
232
+ };
233
+ cleanup = mount(<ConfirmDialog {...options} onResult={handleResult} />);
234
+ });
235
+ }
236
+
237
+ /**
238
+ * 命令式 alert 弹窗 — 返回 `Promise<void>`:
239
+ * 用户点击确认按钮后 resolve。
240
+ *
241
+ * 等价 cd `Dialog.alert({ ... })` / antd `Modal.info|success|warning|error({ ... })`。
242
+ */
243
+ export function alert(options: AlertOptions): Promise<void> {
244
+ return new Promise<void>((resolve) => {
245
+ let cleanup: (() => void) | undefined;
246
+ const handleResult = () => {
247
+ cleanup?.();
248
+ resolve();
249
+ };
250
+ cleanup = mount(<AlertDialog {...options} onResult={handleResult} />);
251
+ });
252
+ }
@@ -1,37 +1,35 @@
1
1
  ---
2
2
  id: drawer
3
3
  name: Drawer
4
+ displayName: 抽屉(Sheet 别名)
4
5
  type: component
5
6
  category: feedback
6
7
  since: 0.1.0
7
- package: "@teamix-evo/ui"
8
+ package: '@teamix-evo/ui'
9
+ alias: sheet
8
10
  ---
9
11
 
10
- # Drawer
12
+ # Drawer 抽屉(Sheet 别名)
11
13
 
12
- 底部可拖拽抽屉 基于 [`vaul`](https://github.com/emilkowalski/vaul),iOS 风格,**移动端体验最佳**。
13
- **与 Sheet 区别**:Sheet 模态阻断 + 边缘滑入(桌面端);Drawer 底部 + 可向下拖拽 dismiss(移动端 / 触屏)。
14
+ Drawer 现在是 Sheet 的 re-export 别名。所有能力(side / size / header / body / footer / 圆角) Sheet 完全一致。
15
+ 保留本 entry 仅为向后兼容 已有的 `import { Drawer, DrawerContent } from '...'` 不会断裂。
16
+
17
+ > **新代码推荐直接使用 Sheet。**
14
18
 
15
19
  ## When to use
16
20
 
17
- - 移动端(< sm 断点)的设置 / 详情面板
18
- - 触屏可拖拽 dismiss 的临时面板
19
- - 需要 iOS / 现代移动 OS 视觉一致性的场景
21
+ Sheet 相同 参见 [Sheet](../sheet/sheet.meta.md)。
20
22
 
21
23
  ## When NOT to use
22
24
 
23
- - 桌面端 `Sheet`(更紧凑 + 多方向支持)
24
- - 阻断式确认 → `AlertDialog`
25
- - 普通模态 → `Dialog`
26
-
27
- > 实际项目通常**响应式切换**:`md` 以下用 Drawer,`md` 以上用 Sheet/Dialog。
25
+ Sheet 相同 — 参见 [Sheet](../sheet/sheet.meta.md)
28
26
 
29
27
  ## Props
30
28
 
31
- > 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成。下表是 `DrawerContent` 的 props;`Drawer`(Root)透传 vaul `open / defaultOpen / onOpenChange / shouldScaleBackground / dismissible / snapPoints / onClose`。
29
+ > DrawerContent 的 props SheetContent 完全一致(类型别名)。
32
30
 
33
31
  <!-- auto:props:begin -->
34
- _(no props)_
32
+ _(组件无 `<Name>Props` interface — props 详见 [`drawer.tsx`](./drawer.tsx))_
35
33
  <!-- auto:props:end -->
36
34
 
37
35
  ## 依赖
@@ -45,52 +43,53 @@ _(no props)_
45
43
 
46
44
  | Entry | 类型 | 描述 |
47
45
  | --- | --- | --- |
46
+ | `sheet` | component | 侧边面板 — Radix Dialog + side(left/right/top/bottom) + size(sm/md/lg/xl) + --radius-dialog 圆角,对齐 antd Drawer 全能力集 |
48
47
  | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
49
48
 
50
49
  ### npm 依赖
51
50
 
52
- > 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
53
-
54
- ```bash
55
- pnpm add vaul@^1.0.0
56
- ```
51
+ _无 本组件不依赖任何 npm 包。_
57
52
  <!-- auto:deps:end -->
58
53
 
59
- > 子组件:`Drawer` / `DrawerTrigger` / `DrawerContent` / `DrawerHeader` / `DrawerFooter` / `DrawerTitle` / `DrawerDescription` / `DrawerClose`。
54
+ > 导出映射:`Drawer=Sheet` / `DrawerContent=SheetContent` / `DrawerHeader=SheetHeader` / `DrawerBody=SheetBody` / `DrawerFooter=SheetFooter` / `DrawerTitle=SheetTitle` / `DrawerDescription=SheetDescription` / `DrawerClose=SheetClose`。
60
55
 
61
56
  ## AI 生成纪律
62
57
 
63
- - **`shouldScaleBackground={true}` 默认**:背景元素轻微缩小 + 圆角,iOS 视觉一致;桌面端可关闭
64
- - **`dismissible={false}` 用于"必选"场景**:阻止下拉关闭,用户必须点 Action / Cancel
65
- - **抽屉顶部把手已自动渲染**:不要再手动加,vaul 自带视觉
66
- - **`snapPoints`**:支持半屏 / 全屏阶段(如 `['148px', '300px', 1]`),vaul 原生
67
- - **不要在 Drawer 内放 Drawer**:嵌套抽屉是反模式
58
+ - **新代码不要使用 Drawer**:统一用 Sheet,Drawer 仅保持已有代码兼容
59
+ - **Drawer Sheet 能力完全等价**:side / size / header / body / footer 行为一致
60
+ - 其余纪律参见 [Sheet meta](../sheet/sheet.meta.md)
68
61
 
69
62
  ## Examples
70
63
 
71
64
  ```tsx
65
+ // 向后兼容写法(不推荐新代码使用)
72
66
  import {
73
- Drawer, DrawerTrigger, DrawerContent, DrawerHeader,
74
- DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose,
67
+ Drawer,
68
+ DrawerTrigger,
69
+ DrawerContent,
70
+ DrawerHeader,
71
+ DrawerBody,
72
+ DrawerTitle,
73
+ DrawerFooter,
74
+ DrawerClose,
75
75
  } from '@/components/ui/drawer';
76
76
  import { Button } from '@/components/ui/button';
77
77
 
78
78
  <Drawer>
79
79
  <DrawerTrigger asChild>
80
- <Button variant="outline">打开抽屉</Button>
80
+ <Button variant="outline">打开</Button>
81
81
  </DrawerTrigger>
82
- <DrawerContent>
82
+ <DrawerContent side="right" size="md">
83
83
  <DrawerHeader>
84
- <DrawerTitle>发起讨论</DrawerTitle>
85
- <DrawerDescription>讨论将通知到该项目所有成员。</DrawerDescription>
84
+ <DrawerTitle>编辑</DrawerTitle>
86
85
  </DrawerHeader>
87
- <div className="px-4 pb-4">{/* form */}</div>
86
+ <DrawerBody>{/* content */}</DrawerBody>
88
87
  <DrawerFooter>
89
- <Button>提交</Button>
90
88
  <DrawerClose asChild>
91
89
  <Button variant="outline">取消</Button>
92
90
  </DrawerClose>
91
+ <Button>确定</Button>
93
92
  </DrawerFooter>
94
93
  </DrawerContent>
95
- </Drawer>
94
+ </Drawer>;
96
95
  ```
@@ -1,9 +1,10 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import {
3
3
  Drawer,
4
4
  DrawerTrigger,
5
5
  DrawerContent,
6
6
  DrawerHeader,
7
+ DrawerBody,
7
8
  DrawerTitle,
8
9
  DrawerDescription,
9
10
  DrawerFooter,
@@ -12,41 +13,57 @@ import {
12
13
  import { Button } from '@/components/button/button';
13
14
 
14
15
  const meta: Meta<typeof DrawerContent> = {
15
- title: '反馈与浮层 · Feedback/Drawer',
16
+ title: '反馈 · Feedback/Drawer (alias)',
16
17
  component: DrawerContent,
17
18
  tags: ['autodocs'],
18
19
  parameters: {
19
20
  docs: {
20
21
  description: {
21
22
  component:
22
- '抽屉(移动优先) 从屏幕边缘滑入的浮层容器,常用于移动端表单 / 详情 / 导航等中等复杂度的二级界面。基于 [Vaul](https://vaul.emilkowal.ski/) 实现,提供原生手势 + 拖拽关闭 + 顶部把手;桌面端可视为 `Sheet` 的移动友好版本(能力对齐 antd Drawer)。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
23
+ '抽屉(Sheet 别名) —— Drawer 现为 Sheet re-export 别名,保持向后兼容。所有能力(`side`/`size`/header/body/footer/圆角) Sheet 完全一致,新代码推荐直接使用 Sheet。Radix Dialog 内核 + cva side/size 双变体,对齐 antd `Drawer`:`side`、`size`、`showClose`。视觉走 OpenTrek semantic tokens,所有样式来自 `@teamix-evo/tokens`,无 mock。',
23
24
  },
24
25
  },
25
26
  },
27
+ argTypes: {
28
+ side: {
29
+ control: 'inline-radio',
30
+ options: ['top', 'right', 'bottom', 'left'],
31
+ },
32
+ size: {
33
+ control: 'inline-radio',
34
+ options: ['sm', 'md', 'lg', 'xl'],
35
+ },
36
+ showClose: {
37
+ control: 'boolean',
38
+ },
39
+ },
40
+ args: { side: 'right', size: 'md', showClose: true },
26
41
  };
27
42
 
28
43
  export default meta;
29
44
  type Story = StoryObj<typeof DrawerContent>;
30
45
 
31
46
  export const Playground: Story = {
32
- render: () => (
47
+ render: (args) => (
33
48
  <Drawer>
34
49
  <DrawerTrigger asChild>
35
- <Button variant="outline">打开 Drawer</Button>
50
+ <Button variant="outline">打开 Drawer (alias)</Button>
36
51
  </DrawerTrigger>
37
- <DrawerContent>
52
+ <DrawerContent {...args}>
38
53
  <DrawerHeader>
39
- <DrawerTitle>发起讨论</DrawerTitle>
40
- <DrawerDescription>讨论将通知到该项目所有成员。</DrawerDescription>
54
+ <DrawerTitle>编辑资料</DrawerTitle>
41
55
  </DrawerHeader>
42
- <div className="px-4 pb-4 text-sm text-muted-foreground">
43
- 表单字段放在这里... 试试向下拖拽抽屉关闭。
44
- </div>
56
+ <DrawerBody>
57
+ <DrawerDescription>Drawer 现在等同于 Sheet。</DrawerDescription>
58
+ <div className="py-4 text-sm text-muted-foreground">
59
+ 内容区域...
60
+ </div>
61
+ </DrawerBody>
45
62
  <DrawerFooter>
46
- <Button>提交</Button>
47
63
  <DrawerClose asChild>
48
64
  <Button variant="outline">取消</Button>
49
65
  </DrawerClose>
66
+ <Button>确定</Button>
50
67
  </DrawerFooter>
51
68
  </DrawerContent>
52
69
  </Drawer>