@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
@@ -6,16 +6,37 @@ import {
6
6
  ChevronsRight,
7
7
  MoreHorizontal,
8
8
  } from 'lucide-react';
9
+ import { cva, type VariantProps } from 'class-variance-authority';
9
10
 
10
11
  import { cn } from '@/utils/cn';
11
12
  import { Button, type ButtonProps } from '@/components/button/button';
12
13
 
14
+ // ─── 尺寸映射 — 对齐 Button/Input 的 h-7 / h-8 / h-9 三档 ──────────────────────
15
+
16
+ export type PaginationSize = 'sm' | 'md' | 'lg';
17
+ export type PaginationType = 'normal' | 'simple' | 'mini';
18
+
19
+ const sizeClass: Record<PaginationSize, string> = {
20
+ sm: 'text-xs',
21
+ md: 'text-xs',
22
+ lg: 'text-sm',
23
+ };
24
+
25
+ const minWClass: Record<PaginationSize, string> = {
26
+ sm: 'min-w-7',
27
+ md: 'min-w-8',
28
+ lg: 'min-w-9',
29
+ };
30
+
31
+ const inputHeightClass: Record<PaginationSize, string> = {
32
+ sm: 'h-7 text-xs px-2',
33
+ md: 'h-8 text-xs px-2.5',
34
+ lg: 'h-9 text-sm px-3',
35
+ };
36
+
13
37
  // ─── shadcn-style primitives ──────────────────────────────────────────────────
14
38
 
15
- const Pagination = ({
16
- className,
17
- ...props
18
- }: React.ComponentProps<'nav'>) => (
39
+ const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (
19
40
  <nav
20
41
  role="navigation"
21
42
  aria-label="pagination"
@@ -46,92 +67,189 @@ const PaginationItem = React.forwardRef<
46
67
  PaginationItem.displayName = 'PaginationItem';
47
68
 
48
69
  interface PaginationLinkProps
49
- extends Pick<ButtonProps, 'size'>,
50
- React.ComponentProps<'button'> {
70
+ extends Omit<ButtonProps, 'size' | 'variant' | 'shape'> {
71
+ /** 当前激活状态(高亮选中页)。 */
51
72
  isActive?: boolean;
73
+ /**
74
+ * 尺寸,对齐 Button/Input 三档。
75
+ * @default "md"
76
+ */
77
+ size?: PaginationSize;
52
78
  }
53
79
 
54
80
  const PaginationLink = ({
55
81
  className,
56
82
  isActive,
57
- size = 'icon',
83
+ size = 'md',
58
84
  ...props
59
85
  }: PaginationLinkProps) => (
60
86
  <Button
61
87
  aria-current={isActive ? 'page' : undefined}
62
88
  variant={isActive ? 'outline' : 'ghost'}
63
89
  size={size}
64
- className={cn(className)}
90
+ className={cn(
91
+ // 让数字键看起来近似方块,最小宽度 = 高度
92
+ minWClass[size],
93
+ // 数字键以 padding 收紧(等宽数字)
94
+ 'tabular-nums',
95
+ isActive && 'border-primary text-primary',
96
+ className,
97
+ )}
65
98
  {...props}
66
99
  />
67
100
  );
68
101
  PaginationLink.displayName = 'PaginationLink';
69
102
 
103
+ interface PaginationNavProps extends Omit<PaginationLinkProps, 'isActive'> {
104
+ /**
105
+ * 仅显示图标(不显示「上一页 / 下一页」文字),用于 mini 与图标条形态。
106
+ * @default false
107
+ */
108
+ iconOnly?: boolean;
109
+ }
110
+
70
111
  const PaginationPrevious = ({
71
112
  className,
113
+ size = 'md',
114
+ iconOnly = false,
115
+ children,
72
116
  ...props
73
- }: React.ComponentProps<typeof PaginationLink>) => (
117
+ }: PaginationNavProps) => (
74
118
  <PaginationLink
75
119
  aria-label="Go to previous page"
76
- size="default"
77
- className={cn('gap-1 pl-2.5', className)}
120
+ size={size}
121
+ className={cn(iconOnly ? 'aspect-square px-0' : 'gap-1 px-2.5', className)}
78
122
  {...props}
79
123
  >
80
124
  <ChevronLeft className="size-4" />
81
- <span>上一页</span>
125
+ {!iconOnly && (children ?? <span>上一页</span>)}
82
126
  </PaginationLink>
83
127
  );
84
128
  PaginationPrevious.displayName = 'PaginationPrevious';
85
129
 
86
130
  const PaginationNext = ({
87
131
  className,
132
+ size = 'md',
133
+ iconOnly = false,
134
+ children,
88
135
  ...props
89
- }: React.ComponentProps<typeof PaginationLink>) => (
136
+ }: PaginationNavProps) => (
90
137
  <PaginationLink
91
138
  aria-label="Go to next page"
92
- size="default"
93
- className={cn('gap-1 pr-2.5', className)}
139
+ size={size}
140
+ className={cn(iconOnly ? 'aspect-square px-0' : 'gap-1 px-2.5', className)}
94
141
  {...props}
95
142
  >
96
- <span>下一页</span>
143
+ {!iconOnly && (children ?? <span>下一页</span>)}
97
144
  <ChevronRight className="size-4" />
98
145
  </PaginationLink>
99
146
  );
100
147
  PaginationNext.displayName = 'PaginationNext';
101
148
 
149
+ const ellipsisVariants = cva(
150
+ 'flex items-center justify-center text-muted-foreground',
151
+ {
152
+ variants: {
153
+ size: {
154
+ sm: 'h-7 w-7 [&_svg]:size-3.5',
155
+ md: 'h-8 w-8 [&_svg]:size-4',
156
+ lg: 'h-9 w-9 [&_svg]:size-4',
157
+ },
158
+ },
159
+ defaultVariants: { size: 'md' },
160
+ },
161
+ );
162
+
163
+ interface PaginationEllipsisProps
164
+ extends React.ComponentProps<'span'>,
165
+ VariantProps<typeof ellipsisVariants> {}
166
+
102
167
  const PaginationEllipsis = ({
103
168
  className,
169
+ size,
104
170
  ...props
105
- }: React.ComponentProps<'span'>) => (
171
+ }: PaginationEllipsisProps) => (
106
172
  <span
107
173
  aria-hidden="true"
108
- className={cn('flex size-9 items-center justify-center', className)}
174
+ className={cn(ellipsisVariants({ size }), className)}
109
175
  {...props}
110
176
  >
111
- <MoreHorizontal className="size-4" />
177
+ <MoreHorizontal />
112
178
  <span className="sr-only">More pages</span>
113
179
  </span>
114
180
  );
115
181
  PaginationEllipsis.displayName = 'PaginationEllipsis';
116
182
 
117
- // ─── Higher-level wrapper(antd 风格 props 化分页器)───────────────────────
183
+ // ─── 高阶 SimplePagination — antd 风格 props 化 ───────────────────────────────
118
184
 
119
185
  export interface SimplePaginationProps {
120
- /** 当前页(1-based)。 */
186
+ /**
187
+ * 当前页(1-based,受控)。与 `defaultCurrent` 互斥。
188
+ */
121
189
  current?: number;
122
- /** 受控变化回调。 */
123
- onChange?: (page: number) => void;
190
+ /**
191
+ * 默认当前页(非受控)
192
+ * @default 1
193
+ */
194
+ defaultCurrent?: number;
195
+ /**
196
+ * 页码 / pageSize 变化回调。第二参为变化后的 pageSize。
197
+ */
198
+ onChange?: (page: number, pageSize: number) => void;
124
199
  /** 总条数。 */
125
200
  total: number;
126
- /** 每页大小。 @default 10 */
201
+ /**
202
+ * 每页大小(受控)。与 `defaultPageSize` 互斥。
203
+ */
127
204
  pageSize?: number;
128
- /** 是否显示首末页快捷按钮。 @default false */
205
+ /**
206
+ * 默认每页大小(非受控)。
207
+ * @default 10
208
+ */
209
+ defaultPageSize?: number;
210
+ /**
211
+ * 每页大小可选项(用于 `pageSizeSelector`)。
212
+ * @default [10, 20, 50, 100]
213
+ */
214
+ pageSizeList?: number[];
215
+ /**
216
+ * 显示每页大小选择器(对齐 antd `showSizeChanger` / cloud-design `pageSizeSelector`)。
217
+ * @default false
218
+ */
219
+ pageSizeSelector?: boolean;
220
+ /** pageSize 变化回调。 */
221
+ onPageSizeChange?: (pageSize: number) => void;
222
+ /**
223
+ * 组件大小,对齐 Button / Input 的 sm(h-7) / md(h-8) / lg(h-9) 三档。
224
+ * @default "md"
225
+ */
226
+ size?: PaginationSize;
227
+ /**
228
+ * 组件类型 — `normal` 完整 / `simple` 简化(prev + 页码/总页 + next) / `mini` 极简(仅前后箭头)。
229
+ * @default "normal"
230
+ */
231
+ type?: PaginationType;
232
+ /**
233
+ * `normal` 类型下,显示首末页快捷按钮。
234
+ * @default false
235
+ */
129
236
  showFirstLast?: boolean;
130
- /** 是否显示总数文本(antd `showTotal` 并集,简化版)。 @default true */
237
+ /**
238
+ * 显示总数文本(`mini` 类型默认不显示)。
239
+ * @default true
240
+ */
131
241
  showTotal?: boolean;
132
- /** 自定义总数渲染。返回字符串或 ReactNode。 */
242
+ /** 自定义总数渲染。 */
133
243
  totalRender?: (total: number, range: [number, number]) => React.ReactNode;
134
- /** 显示页号窗口大小(中间显示几个页号)。 @default 5 */
244
+ /**
245
+ * `normal` 类型下,显示跳转输入框。
246
+ * @default false
247
+ */
248
+ showJump?: boolean;
249
+ /**
250
+ * 中间显示几个页号。建议奇数。
251
+ * @default 5
252
+ */
135
253
  siblingCount?: number;
136
254
  /** 容器 className。 */
137
255
  className?: string;
@@ -139,25 +257,61 @@ export interface SimplePaginationProps {
139
257
 
140
258
  /**
141
259
  * 高阶 Pagination — antd 风格 `total / pageSize / current / onChange` props 化用法,
142
- * 自动计算页码窗口 + 折叠 ellipsis
260
+ * 自动计算页码窗口 + 折叠 ellipsis,并支持 `size` / `type` / `pageSizeSelector` 三档语义化扩展。
143
261
  *
144
262
  * 内部组合 `Pagination / PaginationContent / PaginationItem / PaginationLink /
145
263
  * PaginationPrevious / PaginationNext / PaginationEllipsis` 这套 shadcn primitives,
146
264
  * 业务方需要更细粒度时直接使用 primitives 自行拼装。
147
265
  */
148
266
  const SimplePagination = ({
149
- current = 1,
267
+ current,
268
+ defaultCurrent = 1,
150
269
  onChange,
151
270
  total,
152
- pageSize = 10,
271
+ pageSize,
272
+ defaultPageSize = 10,
273
+ pageSizeList = [10, 20, 50, 100],
274
+ pageSizeSelector = false,
275
+ onPageSizeChange,
276
+ size = 'md',
277
+ type = 'normal',
153
278
  showFirstLast = false,
154
279
  showTotal = true,
155
280
  totalRender,
281
+ showJump = false,
156
282
  siblingCount = 5,
157
283
  className,
158
284
  }: SimplePaginationProps) => {
159
- const totalPages = Math.max(1, Math.ceil(total / pageSize));
160
- const page = Math.min(Math.max(1, current), totalPages);
285
+ const [innerCurrent, setInnerCurrent] = React.useState(defaultCurrent);
286
+ const [innerPageSize, setInnerPageSize] = React.useState(defaultPageSize);
287
+
288
+ const finalCurrent = current ?? innerCurrent;
289
+ const finalPageSize = pageSize ?? innerPageSize;
290
+ const totalPages = Math.max(1, Math.ceil(total / finalPageSize));
291
+ const page = Math.min(Math.max(1, finalCurrent), totalPages);
292
+
293
+ const goto = (n: number) => {
294
+ if (n < 1 || n > totalPages || n === page) return;
295
+ if (current === undefined) setInnerCurrent(n);
296
+ onChange?.(n, finalPageSize);
297
+ };
298
+
299
+ const handlePageSizeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
300
+ const v = Number(e.target.value);
301
+ if (Number.isNaN(v)) return;
302
+ if (pageSize === undefined) setInnerPageSize(v);
303
+ if (current === undefined) setInnerCurrent(1);
304
+ onPageSizeChange?.(v);
305
+ onChange?.(1, v);
306
+ };
307
+
308
+ const [jumperVal, setJumperVal] = React.useState('');
309
+ const handleJumpKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
310
+ if (e.key !== 'Enter') return;
311
+ const v = Number(jumperVal);
312
+ if (Number.isFinite(v)) goto(v);
313
+ setJumperVal('');
314
+ };
161
315
 
162
316
  const pages = React.useMemo(() => {
163
317
  const half = Math.floor(siblingCount / 2);
@@ -171,39 +325,106 @@ const SimplePagination = ({
171
325
  return arr;
172
326
  }, [page, siblingCount, totalPages]);
173
327
 
174
- const goto = (n: number) => {
175
- if (n < 1 || n > totalPages || n === page) return;
176
- onChange?.(n);
177
- };
328
+ const rangeStart = total === 0 ? 0 : (page - 1) * finalPageSize + 1;
329
+ const rangeEnd = Math.min(page * finalPageSize, total);
178
330
 
179
- const rangeStart = total === 0 ? 0 : (page - 1) * pageSize + 1;
180
- const rangeEnd = Math.min(page * pageSize, total);
331
+ const showLeftEllipsis = (pages[0] ?? 1) > 2;
332
+ const showRightEllipsis =
333
+ (pages[pages.length - 1] ?? totalPages) < totalPages - 1;
181
334
 
182
- const showLeftEllipsis = pages[0]! > 2;
183
- const showRightEllipsis = pages[pages.length - 1]! < totalPages - 1;
335
+ // ─── 总数文本(mini 类型默认隐藏)─────────────────────────────────────────
336
+ const totalNode =
337
+ showTotal && type !== 'mini' ? (
338
+ <p className={cn('text-muted-foreground tabular-nums', sizeClass[size])}>
339
+ {totalRender
340
+ ? totalRender(total, [rangeStart, rangeEnd])
341
+ : `共 ${total} 条 · 第 ${rangeStart}-${rangeEnd} 条`}
342
+ </p>
343
+ ) : (
344
+ <span />
345
+ );
184
346
 
185
- return (
186
- <div
187
- className={cn(
188
- 'flex w-full flex-wrap items-center justify-between gap-3',
189
- className,
190
- )}
191
- >
192
- {showTotal ? (
193
- <p className="text-sm text-muted-foreground tabular-nums">
194
- {totalRender
195
- ? totalRender(total, [rangeStart, rangeEnd])
196
- : `共 ${total} · 第 ${rangeStart}-${rangeEnd} 条`}
197
- </p>
198
- ) : (
199
- <span />
200
- )}
347
+ // ─── 主体内容 ────────────────────────────────────────────────────────────
348
+ let body: React.ReactNode;
349
+
350
+ if (type === 'mini') {
351
+ body = (
352
+ <Pagination className="mx-0 w-auto">
353
+ <PaginationContent>
354
+ <PaginationItem>
355
+ <PaginationPrevious
356
+ size={size}
357
+ iconOnly
358
+ disabled={page === 1}
359
+ onClick={() => goto(page - 1)}
360
+ />
361
+ </PaginationItem>
362
+ <PaginationItem>
363
+ <span
364
+ className={cn(
365
+ 'px-2 tabular-nums text-muted-foreground',
366
+ sizeClass[size],
367
+ )}
368
+ >
369
+ {page} / {totalPages}
370
+ </span>
371
+ </PaginationItem>
372
+ <PaginationItem>
373
+ <PaginationNext
374
+ size={size}
375
+ iconOnly
376
+ disabled={page === totalPages}
377
+ onClick={() => goto(page + 1)}
378
+ />
379
+ </PaginationItem>
380
+ </PaginationContent>
381
+ </Pagination>
382
+ );
383
+ } else if (type === 'simple') {
384
+ body = (
385
+ <Pagination className="mx-0 w-auto">
386
+ <PaginationContent>
387
+ <PaginationItem>
388
+ <PaginationPrevious
389
+ size={size}
390
+ iconOnly
391
+ disabled={page === 1}
392
+ onClick={() => goto(page - 1)}
393
+ />
394
+ </PaginationItem>
395
+ <PaginationItem>
396
+ <span
397
+ className={cn(
398
+ 'flex items-center gap-1 px-2 tabular-nums',
399
+ sizeClass[size],
400
+ )}
401
+ >
402
+ <strong className="text-foreground">{page}</strong>
403
+ <span className="text-muted-foreground">/</span>
404
+ <span className="text-muted-foreground">{totalPages}</span>
405
+ </span>
406
+ </PaginationItem>
407
+ <PaginationItem>
408
+ <PaginationNext
409
+ size={size}
410
+ iconOnly
411
+ disabled={page === totalPages}
412
+ onClick={() => goto(page + 1)}
413
+ />
414
+ </PaginationItem>
415
+ </PaginationContent>
416
+ </Pagination>
417
+ );
418
+ } else {
419
+ body = (
201
420
  <Pagination className="mx-0 w-auto">
202
421
  <PaginationContent>
203
422
  {showFirstLast ? (
204
423
  <PaginationItem>
205
424
  <PaginationLink
206
425
  aria-label="First"
426
+ size={size}
427
+ className="aspect-square px-0"
207
428
  disabled={page === 1}
208
429
  onClick={() => goto(1)}
209
430
  >
@@ -213,23 +434,28 @@ const SimplePagination = ({
213
434
  ) : null}
214
435
  <PaginationItem>
215
436
  <PaginationPrevious
437
+ size={size}
438
+ iconOnly
216
439
  disabled={page === 1}
217
440
  onClick={() => goto(page - 1)}
218
441
  />
219
442
  </PaginationItem>
220
- {pages[0]! > 1 ? (
443
+ {(pages[0] ?? 1) > 1 ? (
221
444
  <PaginationItem>
222
- <PaginationLink onClick={() => goto(1)}>1</PaginationLink>
445
+ <PaginationLink size={size} onClick={() => goto(1)}>
446
+ 1
447
+ </PaginationLink>
223
448
  </PaginationItem>
224
449
  ) : null}
225
450
  {showLeftEllipsis ? (
226
451
  <PaginationItem>
227
- <PaginationEllipsis />
452
+ <PaginationEllipsis size={size} />
228
453
  </PaginationItem>
229
454
  ) : null}
230
455
  {pages.map((p) => (
231
456
  <PaginationItem key={p}>
232
457
  <PaginationLink
458
+ size={size}
233
459
  isActive={p === page}
234
460
  onClick={() => goto(p)}
235
461
  >
@@ -239,18 +465,20 @@ const SimplePagination = ({
239
465
  ))}
240
466
  {showRightEllipsis ? (
241
467
  <PaginationItem>
242
- <PaginationEllipsis />
468
+ <PaginationEllipsis size={size} />
243
469
  </PaginationItem>
244
470
  ) : null}
245
- {pages[pages.length - 1]! < totalPages ? (
471
+ {(pages[pages.length - 1] ?? totalPages) < totalPages ? (
246
472
  <PaginationItem>
247
- <PaginationLink onClick={() => goto(totalPages)}>
473
+ <PaginationLink size={size} onClick={() => goto(totalPages)}>
248
474
  {totalPages}
249
475
  </PaginationLink>
250
476
  </PaginationItem>
251
477
  ) : null}
252
478
  <PaginationItem>
253
479
  <PaginationNext
480
+ size={size}
481
+ iconOnly
254
482
  disabled={page === totalPages}
255
483
  onClick={() => goto(page + 1)}
256
484
  />
@@ -259,6 +487,8 @@ const SimplePagination = ({
259
487
  <PaginationItem>
260
488
  <PaginationLink
261
489
  aria-label="Last"
490
+ size={size}
491
+ className="aspect-square px-0"
262
492
  disabled={page === totalPages}
263
493
  onClick={() => goto(totalPages)}
264
494
  >
@@ -268,6 +498,68 @@ const SimplePagination = ({
268
498
  ) : null}
269
499
  </PaginationContent>
270
500
  </Pagination>
501
+ );
502
+ }
503
+
504
+ // ─── 附加 — pageSize 选择器 / 跳转(仅 normal)─────────────────────────────
505
+ const showAddons = type === 'normal' && (pageSizeSelector || showJump);
506
+
507
+ const addons = showAddons ? (
508
+ <div className={cn('flex items-center gap-2', sizeClass[size])}>
509
+ {pageSizeSelector ? (
510
+ <select
511
+ aria-label="每页显示"
512
+ value={finalPageSize}
513
+ onChange={handlePageSizeChange}
514
+ className={cn(
515
+ 'cursor-pointer rounded-lg border border-input bg-background shadow-sm',
516
+ 'transition-colors hover:border-ring focus:outline-none focus:ring-1 focus:ring-ring',
517
+ 'tabular-nums text-foreground',
518
+ inputHeightClass[size],
519
+ )}
520
+ >
521
+ {pageSizeList.map((opt) => (
522
+ <option key={opt} value={opt}>
523
+ {opt} 条/页
524
+ </option>
525
+ ))}
526
+ </select>
527
+ ) : null}
528
+ {showJump ? (
529
+ <span className="inline-flex items-center gap-1 text-muted-foreground">
530
+ 前往
531
+ <input
532
+ type="text"
533
+ inputMode="numeric"
534
+ value={jumperVal}
535
+ onChange={(e) => setJumperVal(e.target.value)}
536
+ onKeyDown={handleJumpKey}
537
+ className={cn(
538
+ 'w-12 rounded-lg border border-input bg-background text-center shadow-sm',
539
+ 'transition-colors placeholder:text-muted-foreground',
540
+ 'focus:outline-none focus:ring-1 focus:ring-ring',
541
+ 'tabular-nums text-foreground',
542
+ inputHeightClass[size],
543
+ )}
544
+ />
545
+
546
+ </span>
547
+ ) : null}
548
+ </div>
549
+ ) : null;
550
+
551
+ return (
552
+ <div
553
+ className={cn(
554
+ 'flex w-full flex-wrap items-center justify-between gap-3',
555
+ className,
556
+ )}
557
+ >
558
+ {totalNode}
559
+ <div className="flex flex-wrap items-center gap-3">
560
+ {body}
561
+ {addons}
562
+ </div>
271
563
  </div>
272
564
  );
273
565
  };
@@ -4,10 +4,11 @@ name: Popconfirm
4
4
  type: component
5
5
  category: feedback
6
6
  since: 0.1.0
7
- package: "@teamix-evo/ui"
7
+ package: '@teamix-evo/ui'
8
+ displayName: 气泡确认
8
9
  ---
9
10
 
10
- # Popconfirm
11
+ # Popconfirm 气泡确认
11
12
 
12
13
  轻量级确认弹层 — antd 独有补足。**等价 antd `Popconfirm`**。用于"小风险"操作的二次确认(删除一行、忽略某条通知),视觉比 `AlertDialog` 更轻、不阻塞页面;`onConfirm` 支持返回 Promise(等待时按钮自动 loading)。
13
14
 
@@ -23,6 +24,8 @@ package: "@teamix-evo/ui"
23
24
  - 无后果的纯展示 → `Tooltip` / `Popover`
24
25
  - 长决策路径(需要填写理由)→ `Dialog` / `Drawer`
25
26
 
27
+ ## Props
28
+
26
29
  <!-- auto:props:begin -->
27
30
  | 名称 | 类型 | 默认值 | 必填 | 说明 |
28
31
  | --- | --- | --- | --- | --- |
@@ -40,6 +43,8 @@ package: "@teamix-evo/ui"
40
43
  | `disabled` | `boolean` | – | – | 禁用(子节点点击不弹出)。 |
41
44
  <!-- auto:props:end -->
42
45
 
46
+ ## 依赖
47
+
43
48
  <!-- auto:deps:begin -->
44
49
  ### 同库依赖
45
50
 
@@ -48,8 +53,8 @@ package: "@teamix-evo/ui"
48
53
  | Entry | 类型 | 描述 |
49
54
  | --- | --- | --- |
50
55
  | `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
51
- | `button` | component | 通用按钮 — shadcn 实现 + antd 功能扩展(loading / icon / shape / block / dashed variant) |
52
- | `popover` | component | 可交互浮层 — Radix Popover + antd arrow 并集 |
56
+ | `button` | component | 通用按钮 — shadcn 实现 + cloud-design 能力并集(loading / icon / shape / block / dashed variant / color 语义双 prop / disabledTooltip)。同文件合一导出 ButtonGroup + ButtonGroupText(等价 antd Space.Compact + cd SplitButton)。 |
57
+ | `popover` | component | 可交互浮层 — Radix Popover + antd arrow 并集,使用 showArrow 控制尖角(与 Tooltip / HoverCard 命名统一) |
53
58
 
54
59
  ### npm 依赖
55
60
 
@@ -1,17 +1,17 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { RefreshCcw } from 'lucide-react';
3
3
  import { Popconfirm } from './popconfirm';
4
4
  import { Button } from '@/components/button/button';
5
5
 
6
6
  const meta: Meta<typeof Popconfirm> = {
7
- title: '反馈与浮层 · Feedback/Popconfirm',
7
+ title: '反馈 · Feedback/Popconfirm',
8
8
  component: Popconfirm,
9
9
  tags: ['autodocs'],
10
10
  parameters: {
11
11
  docs: {
12
12
  description: {
13
13
  component:
14
- '轻量级确认弹层 — 小风险操作的二次确认(删除一行 / 忽略一条通知)。视觉比 AlertDialog 更轻、不阻塞页面;onConfirm 支持 Promise(等待时按钮自动 loading)。等价 antd `Popconfirm`。视觉走 OpenTrek tokens,所有样式来自 `@teamix-evo/design`,无 mock。',
14
+ '轻量级确认弹层 — 小风险操作的二次确认(删除一行 / 忽略一条通知)。视觉比 AlertDialog 更轻、不阻塞页面;onConfirm 支持 Promise(等待时按钮自动 loading)。等价 antd `Popconfirm`。',
15
15
  },
16
16
  },
17
17
  },
@@ -57,7 +57,6 @@ export const CustomIcon: Story = {
57
57
  title="重置所有筛选条件?"
58
58
  icon={<RefreshCcw className="size-4 text-primary" />}
59
59
  onConfirm={() => {
60
- // eslint-disable-next-line no-alert
61
60
  alert('已重置');
62
61
  }}
63
62
  >
@@ -94,11 +94,11 @@ const Popconfirm: React.FC<PopconfirmProps> = ({
94
94
  <PopoverTrigger asChild>{children}</PopoverTrigger>
95
95
  <PopoverContent className="w-72 p-3" align="start">
96
96
  <div className="flex items-start gap-2">
97
- <span className="mt-0.5 text-amber-500">
97
+ <span className="mt-0.5 text-warning">
98
98
  {icon ?? <AlertCircle className="size-4" />}
99
99
  </span>
100
100
  <div className="min-w-0 flex-1">
101
- <div className="text-sm font-medium">{title}</div>
101
+ <div className="text-xs font-medium">{title}</div>
102
102
  {description ? (
103
103
  <div className={cn('mt-1 text-xs text-muted-foreground')}>
104
104
  {description}