@teamix-evo/ui 0.7.0 → 0.7.2

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 (121) hide show
  1. package/manifest.json +16 -7
  2. package/package.json +4 -4
  3. package/src/_design-system/theme-tokens/stories.tsx +2 -2
  4. package/src/components/accordion/index.tsx +1 -1
  5. package/src/components/affix/meta.md +26 -0
  6. package/src/components/alert/index.tsx +2 -2
  7. package/src/components/alert-dialog/index.tsx +3 -3
  8. package/src/components/alert-dialog/meta.md +52 -0
  9. package/src/components/alert-dialog/stories.tsx +45 -48
  10. package/src/components/avatar/index.tsx +1 -1
  11. package/src/components/badge/index.tsx +2 -2
  12. package/src/components/badge/meta.md +48 -0
  13. package/src/components/button/index.tsx +2 -2
  14. package/src/components/button/meta.md +15 -0
  15. package/src/components/button/stories.tsx +1 -1
  16. package/src/components/calendar/index.tsx +2 -2
  17. package/src/components/card/index.tsx +1 -1
  18. package/src/components/carousel/index.tsx +2 -2
  19. package/src/components/carousel/meta.md +34 -2
  20. package/src/components/carousel/stories.tsx +2 -2
  21. package/src/components/cascader-select/index.tsx +2 -1
  22. package/src/components/cascader-select/meta.md +46 -0
  23. package/src/components/checkbox/meta.md +47 -0
  24. package/src/components/color-picker/index.tsx +3 -3
  25. package/src/components/color-picker/meta.md +80 -0
  26. package/src/components/combobox/index.tsx +2 -2
  27. package/src/components/combobox/meta.md +130 -0
  28. package/src/components/data-table/index.tsx +3 -3
  29. package/src/components/data-table/meta.md +419 -0
  30. package/src/components/data-table/stories.tsx +4 -4
  31. package/src/components/date-picker/meta.md +91 -0
  32. package/src/components/descriptions/index.tsx +1 -1
  33. package/src/components/descriptions/meta.md +245 -0
  34. package/src/components/dialog/index.tsx +4 -4
  35. package/src/components/dialog/meta.md +47 -1
  36. package/src/components/dialog/stories.tsx +38 -41
  37. package/src/components/dropdown-menu/index.tsx +5 -5
  38. package/src/components/empty/index.tsx +2 -2
  39. package/src/components/field/index.tsx +4 -4
  40. package/src/components/filter-bar/index.tsx +6 -6
  41. package/src/components/filter-bar/meta.md +323 -0
  42. package/src/components/float-button/index.tsx +2 -2
  43. package/src/components/form/index.tsx +1 -1
  44. package/src/components/form/meta.md +119 -0
  45. package/src/components/hover-card/index.tsx +1 -1
  46. package/src/components/hover-card/meta.md +21 -0
  47. package/src/components/input/meta.md +16 -0
  48. package/src/components/input-group/index.tsx +1 -1
  49. package/src/components/input-group/meta.md +118 -0
  50. package/src/components/input-group/stories.tsx +6 -6
  51. package/src/components/input-ip/index.tsx +2 -2
  52. package/src/components/input-ip/meta.md +30 -0
  53. package/src/components/input-ip/stories.tsx +2 -2
  54. package/src/components/input-number/index.tsx +3 -2
  55. package/src/components/input-number/meta.md +67 -0
  56. package/src/components/input-number/stories.tsx +2 -2
  57. package/src/components/item/index.tsx +4 -4
  58. package/src/components/label/meta.md +8 -0
  59. package/src/components/mentions/meta.md +15 -0
  60. package/src/components/menubar/index.tsx +4 -4
  61. package/src/components/navigation-menu/index.tsx +4 -4
  62. package/src/components/page-header/index.tsx +2 -2
  63. package/src/components/page-header/meta.md +145 -0
  64. package/src/components/page-shell/index.tsx +3 -3
  65. package/src/components/pagination/index.tsx +1 -1
  66. package/src/components/pagination/meta.md +203 -0
  67. package/src/components/popconfirm/meta.md +45 -0
  68. package/src/components/popover/index.tsx +2 -2
  69. package/src/components/popover/meta.md +47 -0
  70. package/src/components/progress/index.tsx +1 -1
  71. package/src/components/progress/meta.md +36 -0
  72. package/src/components/progress/stories.tsx +1 -1
  73. package/src/components/radio-group/meta.md +69 -0
  74. package/src/components/rate/index.tsx +1 -1
  75. package/src/components/rate/meta.md +50 -0
  76. package/src/components/resizable/index.tsx +1 -1
  77. package/src/components/select/index.tsx +2 -2
  78. package/src/components/select/meta.md +20 -0
  79. package/src/components/separator/index.tsx +1 -1
  80. package/src/components/sheet/index.tsx +13 -14
  81. package/src/components/sheet/meta.md +124 -0
  82. package/src/components/sheet/stories.tsx +110 -119
  83. package/src/components/sidebar/index.tsx +5 -5
  84. package/src/components/sidebar/meta.md +383 -0
  85. package/src/components/skeleton/meta.md +13 -0
  86. package/src/components/slider/index.tsx +2 -2
  87. package/src/components/sonner/meta.md +86 -0
  88. package/src/components/spinner/meta.md +46 -0
  89. package/src/components/spinner/stories.tsx +2 -2
  90. package/src/components/steps/meta.md +20 -0
  91. package/src/components/steps/stories.tsx +1 -1
  92. package/src/components/switch/index.tsx +2 -2
  93. package/src/components/switch/meta.md +33 -0
  94. package/src/components/table/index.tsx +4 -4
  95. package/src/components/table/meta.md +11 -0
  96. package/src/components/tabs/index.tsx +7 -7
  97. package/src/components/tabs/meta.md +52 -0
  98. package/src/components/tag/index.tsx +8 -8
  99. package/src/components/tag/meta.md +194 -0
  100. package/src/components/textarea/index.tsx +1 -1
  101. package/src/components/textarea/meta.md +27 -0
  102. package/src/components/textarea/stories.tsx +1 -1
  103. package/src/components/time-picker/index.tsx +3 -3
  104. package/src/components/time-picker/meta.md +76 -0
  105. package/src/components/timeline/index.tsx +1 -0
  106. package/src/components/toggle/index.tsx +1 -1
  107. package/src/components/toggle-group/index.tsx +1 -1
  108. package/src/components/tooltip/index.tsx +1 -1
  109. package/src/components/tooltip/meta.md +23 -0
  110. package/src/components/transfer/index.tsx +2 -2
  111. package/src/components/transfer/meta.md +97 -0
  112. package/src/components/tree/index.tsx +245 -15
  113. package/src/components/tree/meta.md +151 -0
  114. package/src/components/tree-select/index.tsx +16 -2
  115. package/src/components/tree-select/meta.md +150 -0
  116. package/src/components/typography/index.tsx +3 -3
  117. package/src/components/upload/index.tsx +3 -3
  118. package/src/components/upload/meta.md +82 -0
  119. package/src/components/tree/utils.ts +0 -269
  120. package/src/examples/built-in-assets/stories.tsx +0 -572
  121. package/src/examples/evaluators/stories.tsx +0 -502
@@ -76,7 +76,7 @@ function SheetOverlay({
76
76
  <SheetPrimitive.Overlay
77
77
  data-slot="sheet-overlay"
78
78
  className={cn(
79
- 'fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0',
79
+ 'fixed inset-0 z-50 bg-black/10 duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 supports-backdrop-filter:backdrop-blur-xs',
80
80
  className,
81
81
  )}
82
82
  {...props}
@@ -131,26 +131,26 @@ function SheetContent({
131
131
  className={cn(
132
132
  // 基础布局:fixed 定位 + flex 列容器 + token 化阴影(抽屉与背景的边界由阴影表达,不画 border)
133
133
  // eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- box-shadow 走 component-level token --drawer-shadow,按 ADR 0026 在源码内显式声明。
134
- 'fixed z-50 flex flex-col bg-popover text-xs text-popover-foreground shadow-[var(--drawer-shadow,0px_0px_24px_0px_rgba(0,0,0,0.1))] transition ease-in-out outline-none data-[state=open]:animate-in data-[state=open]:duration-300 data-[state=closed]:animate-out data-[state=closed]:duration-200',
134
+ 'fixed z-50 flex flex-col bg-popover text-xs text-popover-foreground shadow-[var(--drawer-shadow,0px_0px_24px_0px_rgba(0,0,0,0.1))] transition ease-in-out outline-none data-[state=closed]:animate-out data-[state=closed]:duration-200 data-[state=open]:animate-in data-[state=open]:duration-300',
135
135
  // 4 向定位(不加 border)
136
- 'data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-full',
137
- 'data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-full',
136
+ 'data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:size-full',
137
+ 'data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:size-full',
138
138
  'data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:w-full',
139
139
  'data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:w-full',
140
140
  // 4 向滑入滑出动画
141
- 'data-[side=right]:data-[state=open]:slide-in-from-right data-[side=right]:data-[state=closed]:slide-out-to-right',
142
- 'data-[side=left]:data-[state=open]:slide-in-from-left data-[side=left]:data-[state=closed]:slide-out-to-left',
143
- 'data-[side=top]:data-[state=open]:slide-in-from-top data-[side=top]:data-[state=closed]:slide-out-to-top',
144
- 'data-[side=bottom]:data-[state=open]:slide-in-from-bottom data-[side=bottom]:data-[state=closed]:slide-out-to-bottom',
141
+ 'data-[side=right]:data-[state=closed]:slide-out-to-right data-[side=right]:data-[state=open]:slide-in-from-right',
142
+ 'data-[side=left]:data-[state=closed]:slide-out-to-left data-[side=left]:data-[state=open]:slide-in-from-left',
143
+ 'data-[side=top]:data-[state=closed]:slide-out-to-top data-[side=top]:data-[state=open]:slide-in-from-top',
144
+ 'data-[side=bottom]:data-[state=closed]:slide-out-to-bottom data-[side=bottom]:data-[state=open]:slide-in-from-bottom',
145
145
  // size 档位:left/right 控宽,top/bottom 控高(sm 断点以上生效,移动端默认贴边全屏)
146
146
  // eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- 像素值对齐 cloud-design Drawer mini/small/medium/large 视觉档位 (400/600/800/1200);通过 component-level token 表达过于碎片化,按 ADR 0026 在源码内显式声明。
147
- 'data-[side=left]:data-[size=sm]:sm:max-w-[400px] data-[side=left]:data-[size=md]:sm:max-w-[600px] data-[side=left]:data-[size=lg]:sm:max-w-[800px] data-[side=left]:data-[size=xl]:sm:max-w-[1200px]',
147
+ 'data-[side=left]:data-[size=lg]:sm:max-w-[800px] data-[side=left]:data-[size=md]:sm:max-w-[600px] data-[side=left]:data-[size=sm]:sm:max-w-[400px] data-[side=left]:data-[size=xl]:sm:max-w-[1200px]',
148
148
  // eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- 同上,对齐 cloud-design Drawer 视觉档位。
149
- 'data-[side=right]:data-[size=sm]:sm:max-w-[400px] data-[side=right]:data-[size=md]:sm:max-w-[600px] data-[side=right]:data-[size=lg]:sm:max-w-[800px] data-[side=right]:data-[size=xl]:sm:max-w-[1200px]',
149
+ 'data-[side=right]:data-[size=lg]:sm:max-w-[800px] data-[side=right]:data-[size=md]:sm:max-w-[600px] data-[side=right]:data-[size=sm]:sm:max-w-[400px] data-[side=right]:data-[size=xl]:sm:max-w-[1200px]',
150
150
  // eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- top/bottom 方向 size 控高度,对齐视觉档位。
151
- 'data-[side=top]:data-[size=sm]:max-h-[280px] data-[side=top]:data-[size=md]:max-h-[400px] data-[side=top]:data-[size=lg]:max-h-[560px] data-[side=top]:data-[size=xl]:max-h-[80vh]',
151
+ 'data-[side=top]:data-[size=lg]:max-h-[560px] data-[side=top]:data-[size=md]:max-h-[400px] data-[side=top]:data-[size=sm]:max-h-[280px] data-[side=top]:data-[size=xl]:max-h-[80vh]',
152
152
  // eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- 同上。
153
- 'data-[side=bottom]:data-[size=sm]:max-h-[280px] data-[side=bottom]:data-[size=md]:max-h-[400px] data-[side=bottom]:data-[size=lg]:max-h-[560px] data-[side=bottom]:data-[size=xl]:max-h-[80vh]',
153
+ 'data-[side=bottom]:data-[size=lg]:max-h-[560px] data-[side=bottom]:data-[size=md]:max-h-[400px] data-[side=bottom]:data-[size=sm]:max-h-[280px] data-[side=bottom]:data-[size=xl]:max-h-[80vh]',
154
154
  className,
155
155
  )}
156
156
  {...props}
@@ -182,7 +182,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
182
182
  <div
183
183
  data-slot="sheet-header"
184
184
  className={cn(
185
- 'flex flex-col gap-1.5 border-b border-border pt-3 pr-5 pb-3 pl-5 has-data-[slot=sheet-header-extra]:grid has-data-[slot=sheet-header-extra]:grid-cols-[1fr_auto] has-data-[slot=sheet-header-extra]:items-start has-data-[slot=sheet-header-extra]:gap-x-3 has-data-[slot=sheet-header-extra]:pr-14',
185
+ 'flex flex-col gap-1.5 border-b border-border px-5 py-3 has-data-[slot=sheet-header-extra]:grid has-data-[slot=sheet-header-extra]:grid-cols-[1fr_auto] has-data-[slot=sheet-header-extra]:items-start has-data-[slot=sheet-header-extra]:gap-x-3 has-data-[slot=sheet-header-extra]:pr-14',
186
186
  className,
187
187
  )}
188
188
  {...props}
@@ -245,7 +245,6 @@ function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
245
245
  <div
246
246
  data-slot="sheet-footer"
247
247
  className={cn(
248
- // eslint-disable-next-line teamix-evo/no-bare-border -- border-footer-separator 是 @theme 声明的语义 token,已加入 lint-core 白名单
249
248
  'flex flex-col gap-2 border-t border-footer-separator px-4 py-2.5 sm:flex-row sm:justify-start',
250
249
  className,
251
250
  )}
@@ -66,6 +66,40 @@
66
66
  </div>
67
67
  ```
68
68
 
69
+ ### Placement
70
+
71
+ 自定义弹出方向 — 4 向 side 切换。
72
+
73
+ ```tsx
74
+ <div className="flex flex-col gap-3">
75
+ <RadioGroup
76
+ value={side}
77
+ onValueChange={(v) => setSide(v as typeof side)}
78
+ className="flex flex-row gap-4"
79
+ >
80
+ {(['right', 'bottom', 'left', 'top'] as const).map((p) => (
81
+ <div key={p} className="flex items-center gap-1.5">
82
+ <RadioGroupItem value={p} id={`side-${p}`} />
83
+ <Label htmlFor={`side-${p}`}>{p}</Label>
84
+ </div>
85
+ ))}
86
+ </RadioGroup>
87
+ <Sheet>
88
+ <SheetTrigger asChild>
89
+ <Button>打开 {side} 方向</Button>
90
+ </SheetTrigger>
91
+ <SheetContent side={side}>
92
+ <SheetHeader>
93
+ <SheetTitle>标题</SheetTitle>
94
+ </SheetHeader>
95
+ <SheetBody>
96
+ Start your business here by searching a popular product
97
+ </SheetBody>
98
+ </SheetContent>
99
+ </Sheet>
100
+ </div>
101
+ ```
102
+
69
103
  ### WithExtra
70
104
 
71
105
  Header 附加内容 — 通过 SheetHeaderExtra 放置帮助链接 / 次要操作。
@@ -199,6 +233,96 @@ Header 附加内容 — 通过 SheetHeaderExtra 放置帮助链接 / 次要操
199
233
  </Sheet>
200
234
  ```
201
235
 
236
+ ### Nested
237
+
238
+ 双层抽屉 — 抽屉内打开新的抽屉,对应 cloud-design Drawer 的 double demo。
239
+
240
+ ```tsx
241
+ <>
242
+ <Button onClick={() => setOuter(true)}>Open drawer</Button>
243
+ <Sheet open={outer} onOpenChange={setOuter}>
244
+ <SheetContent size="md">
245
+ <SheetHeader>
246
+ <SheetTitle>Multi-level drawer</SheetTitle>
247
+ </SheetHeader>
248
+ <SheetBody>
249
+ <Button onClick={() => setInner(true)}>Two-level drawer</Button>
250
+ <div className="mt-10 flex flex-col gap-2">
251
+ {Array.from({ length: 20 }).map((_, i) => (
252
+ <p key={i}>很长的内容 {i + 1}</p>
253
+ ))}
254
+ <p>底部的内容</p>
255
+ </div>
256
+ </SheetBody>
257
+ <SheetFooter className="border-border">
258
+ <Button onClick={() => setOuter(false)}>Submit</Button>
259
+ <Button variant="outline" onClick={() => setOuter(false)}>
260
+ Cancel
261
+ </Button>
262
+ </SheetFooter>
263
+ <Sheet open={inner} onOpenChange={setInner}>
264
+ <SheetContent size="sm">
265
+ <SheetHeader>
266
+ <SheetTitle>Two-level Drawer</SheetTitle>
267
+ </SheetHeader>
268
+ <SheetBody>This is two-level drawer</SheetBody>
269
+ </SheetContent>
270
+ </Sheet>
271
+ </SheetContent>
272
+ </Sheet>
273
+ </>
274
+ ```
275
+
276
+ ### Controlled
277
+
278
+ 受控异步关闭 — onOpenChange 在 loading 期间拦截关闭,按钮 loading 期间禁止外部交互。
279
+
280
+ ```tsx
281
+ const [open, setOpen] = React.useState(false);
282
+ const [loading, setLoading] = React.useState(false);
283
+ const onConfirm = async () => {
284
+ setLoading(true);
285
+ await new Promise((r) => setTimeout(r, 1500));
286
+ setLoading(false);
287
+ setOpen(false);
288
+ };
289
+ return (
290
+ <Sheet
291
+ open={open}
292
+ onOpenChange={(next) => {
293
+ if (loading) return;
294
+ setOpen(next);
295
+ }}
296
+ >
297
+ <SheetTrigger asChild>
298
+ <Button>打开</Button>
299
+ </SheetTrigger>
300
+ <SheetContent
301
+ size="sm"
302
+ onPointerDownOutside={(e) => loading && e.preventDefault()}
303
+ onEscapeKeyDown={(e) => loading && e.preventDefault()}
304
+ >
305
+ <SheetHeader>
306
+ <SheetTitle>确认提交?</SheetTitle>
307
+ </SheetHeader>
308
+ <SheetBody>
309
+ <p>提交期间无法点击遮罩或按 ESC 关闭。</p>
310
+ </SheetBody>
311
+ <SheetFooter>
312
+ <Button loading={loading} onClick={onConfirm}>
313
+ 确认
314
+ </Button>
315
+ <SheetClose asChild>
316
+ <Button variant="outline" disabled={loading}>
317
+ 取消
318
+ </Button>
319
+ </SheetClose>
320
+ </SheetFooter>
321
+ </SheetContent>
322
+ </Sheet>
323
+ );
324
+ ```
325
+
202
326
  ### NoCloseButton
203
327
 
204
328
  隐藏关闭按钮 — 强制通过底部操作关闭,常用于流程类强引导。
@@ -107,41 +107,38 @@ export const Sizes: Story = {
107
107
  export const Placement: Story = {
108
108
  name: '弹出方向 side',
109
109
  render: () => {
110
- function Demo() {
111
- const [side, setSide] = React.useState<
112
- 'top' | 'right' | 'bottom' | 'left'
113
- >('right');
114
- return (
115
- <div className="flex flex-col gap-3">
116
- <RadioGroup
117
- value={side}
118
- onValueChange={(v) => setSide(v as typeof side)}
119
- className="flex flex-row gap-4"
120
- >
121
- {(['right', 'bottom', 'left', 'top'] as const).map((p) => (
122
- <div key={p} className="flex items-center gap-1.5">
123
- <RadioGroupItem value={p} id={`side-${p}`} />
124
- <Label htmlFor={`side-${p}`}>{p}</Label>
125
- </div>
126
- ))}
127
- </RadioGroup>
128
- <Sheet>
129
- <SheetTrigger asChild>
130
- <Button>打开 {side} 方向</Button>
131
- </SheetTrigger>
132
- <SheetContent side={side}>
133
- <SheetHeader>
134
- <SheetTitle>标题</SheetTitle>
135
- </SheetHeader>
136
- <SheetBody>
137
- Start your business here by searching a popular product
138
- </SheetBody>
139
- </SheetContent>
140
- </Sheet>
141
- </div>
142
- );
143
- }
144
- return <Demo />;
110
+ const [side, setSide] = React.useState<
111
+ 'top' | 'right' | 'bottom' | 'left'
112
+ >('right');
113
+ return (
114
+ <div className="flex flex-col gap-3">
115
+ <RadioGroup
116
+ value={side}
117
+ onValueChange={(v) => setSide(v as typeof side)}
118
+ className="flex flex-row gap-4"
119
+ >
120
+ {(['right', 'bottom', 'left', 'top'] as const).map((p) => (
121
+ <div key={p} className="flex items-center gap-1.5">
122
+ <RadioGroupItem value={p} id={`side-${p}`} />
123
+ <Label htmlFor={`side-${p}`}>{p}</Label>
124
+ </div>
125
+ ))}
126
+ </RadioGroup>
127
+ <Sheet>
128
+ <SheetTrigger asChild>
129
+ <Button>打开 {side} 方向</Button>
130
+ </SheetTrigger>
131
+ <SheetContent side={side}>
132
+ <SheetHeader>
133
+ <SheetTitle>标题</SheetTitle>
134
+ </SheetHeader>
135
+ <SheetBody>
136
+ Start your business here by searching a popular product
137
+ </SheetBody>
138
+ </SheetContent>
139
+ </Sheet>
140
+ </div>
141
+ );
145
142
  },
146
143
  };
147
144
 
@@ -282,46 +279,43 @@ export const LargeContent: Story = {
282
279
  export const Nested: Story = {
283
280
  name: '双层抽屉',
284
281
  render: () => {
285
- function Demo() {
286
- const [outer, setOuter] = React.useState(false);
287
- const [inner, setInner] = React.useState(false);
288
- return (
289
- <>
290
- <Button onClick={() => setOuter(true)}>Open drawer</Button>
291
- <Sheet open={outer} onOpenChange={setOuter}>
292
- <SheetContent size="md">
293
- <SheetHeader>
294
- <SheetTitle>Multi-level drawer</SheetTitle>
295
- </SheetHeader>
296
- <SheetBody>
297
- <Button onClick={() => setInner(true)}>Two-level drawer</Button>
298
- <div className="mt-10 flex flex-col gap-2">
299
- {Array.from({ length: 20 }).map((_, i) => (
300
- <p key={i}>很长的内容 {i + 1}</p>
301
- ))}
302
- <p>底部的内容</p>
303
- </div>
304
- </SheetBody>
305
- <SheetFooter className="border-border">
306
- <Button onClick={() => setOuter(false)}>Submit</Button>
307
- <Button variant="outline" onClick={() => setOuter(false)}>
308
- Cancel
309
- </Button>
310
- </SheetFooter>
311
- <Sheet open={inner} onOpenChange={setInner}>
312
- <SheetContent size="sm">
313
- <SheetHeader>
314
- <SheetTitle>Two-level Drawer</SheetTitle>
315
- </SheetHeader>
316
- <SheetBody>This is two-level drawer</SheetBody>
317
- </SheetContent>
318
- </Sheet>
319
- </SheetContent>
320
- </Sheet>
321
- </>
322
- );
323
- }
324
- return <Demo />;
282
+ const [outer, setOuter] = React.useState(false);
283
+ const [inner, setInner] = React.useState(false);
284
+ return (
285
+ <>
286
+ <Button onClick={() => setOuter(true)}>Open drawer</Button>
287
+ <Sheet open={outer} onOpenChange={setOuter}>
288
+ <SheetContent size="md">
289
+ <SheetHeader>
290
+ <SheetTitle>Multi-level drawer</SheetTitle>
291
+ </SheetHeader>
292
+ <SheetBody>
293
+ <Button onClick={() => setInner(true)}>Two-level drawer</Button>
294
+ <div className="mt-10 flex flex-col gap-2">
295
+ {Array.from({ length: 20 }).map((_, i) => (
296
+ <p key={i}>很长的内容 {i + 1}</p>
297
+ ))}
298
+ <p>底部的内容</p>
299
+ </div>
300
+ </SheetBody>
301
+ <SheetFooter className="border-border">
302
+ <Button onClick={() => setOuter(false)}>Submit</Button>
303
+ <Button variant="outline" onClick={() => setOuter(false)}>
304
+ Cancel
305
+ </Button>
306
+ </SheetFooter>
307
+ <Sheet open={inner} onOpenChange={setInner}>
308
+ <SheetContent size="sm">
309
+ <SheetHeader>
310
+ <SheetTitle>Two-level Drawer</SheetTitle>
311
+ </SheetHeader>
312
+ <SheetBody>This is two-level drawer</SheetBody>
313
+ </SheetContent>
314
+ </Sheet>
315
+ </SheetContent>
316
+ </Sheet>
317
+ </>
318
+ );
325
319
  },
326
320
  };
327
321
 
@@ -329,52 +323,49 @@ export const Nested: Story = {
329
323
  export const Controlled: Story = {
330
324
  name: '受控异步',
331
325
  render: () => {
332
- function Demo() {
333
- const [open, setOpen] = React.useState(false);
334
- const [loading, setLoading] = React.useState(false);
335
- const onConfirm = async () => {
336
- setLoading(true);
337
- await new Promise((r) => setTimeout(r, 1500));
338
- setLoading(false);
339
- setOpen(false);
340
- };
341
- return (
342
- <Sheet
343
- open={open}
344
- onOpenChange={(next) => {
345
- if (loading) return;
346
- setOpen(next);
347
- }}
326
+ const [open, setOpen] = React.useState(false);
327
+ const [loading, setLoading] = React.useState(false);
328
+ const onConfirm = async () => {
329
+ setLoading(true);
330
+ await new Promise((r) => setTimeout(r, 1500));
331
+ setLoading(false);
332
+ setOpen(false);
333
+ };
334
+ return (
335
+ <Sheet
336
+ open={open}
337
+ onOpenChange={(next) => {
338
+ if (loading) return;
339
+ setOpen(next);
340
+ }}
341
+ >
342
+ <SheetTrigger asChild>
343
+ <Button>打开</Button>
344
+ </SheetTrigger>
345
+ <SheetContent
346
+ size="sm"
347
+ onPointerDownOutside={(e) => loading && e.preventDefault()}
348
+ onEscapeKeyDown={(e) => loading && e.preventDefault()}
348
349
  >
349
- <SheetTrigger asChild>
350
- <Button>打开</Button>
351
- </SheetTrigger>
352
- <SheetContent
353
- size="sm"
354
- onPointerDownOutside={(e) => loading && e.preventDefault()}
355
- onEscapeKeyDown={(e) => loading && e.preventDefault()}
356
- >
357
- <SheetHeader>
358
- <SheetTitle>确认提交?</SheetTitle>
359
- </SheetHeader>
360
- <SheetBody>
361
- <p>提交期间无法点击遮罩或按 ESC 关闭。</p>
362
- </SheetBody>
363
- <SheetFooter>
364
- <Button loading={loading} onClick={onConfirm}>
365
- 确认
350
+ <SheetHeader>
351
+ <SheetTitle>确认提交?</SheetTitle>
352
+ </SheetHeader>
353
+ <SheetBody>
354
+ <p>提交期间无法点击遮罩或按 ESC 关闭。</p>
355
+ </SheetBody>
356
+ <SheetFooter>
357
+ <Button loading={loading} onClick={onConfirm}>
358
+ 确认
359
+ </Button>
360
+ <SheetClose asChild>
361
+ <Button variant="outline" disabled={loading}>
362
+ 取消
366
363
  </Button>
367
- <SheetClose asChild>
368
- <Button variant="outline" disabled={loading}>
369
- 取消
370
- </Button>
371
- </SheetClose>
372
- </SheetFooter>
373
- </SheetContent>
374
- </Sheet>
375
- );
376
- }
377
- return <Demo />;
364
+ </SheetClose>
365
+ </SheetFooter>
366
+ </SheetContent>
367
+ </Sheet>
368
+ );
378
369
  },
379
370
  };
380
371
 
@@ -211,7 +211,7 @@ function Sidebar({
211
211
  <SheetTitle>Sidebar</SheetTitle>
212
212
  <SheetDescription>Displays the mobile sidebar.</SheetDescription>
213
213
  </SheetHeader>
214
- <div className="flex h-full w-full flex-col">{children}</div>
214
+ <div className="flex size-full flex-col">{children}</div>
215
215
  </SheetContent>
216
216
  </Sheet>
217
217
  );
@@ -246,7 +246,7 @@ function Sidebar({
246
246
  // Adjust the padding for floating and inset variants.
247
247
  variant === 'floating' || variant === 'inset'
248
248
  ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
249
- : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l border-sidebar-border',
249
+ : 'border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
250
250
  className,
251
251
  )}
252
252
  {...props}
@@ -479,7 +479,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
479
479
  }
480
480
 
481
481
  const sidebarMenuButtonVariants = cva(
482
- 'peer/menu-button group/menu-button flex w-full cursor-pointer items-center gap-2 overflow-hidden rounded-md p-2 text-left text-xs ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-50 data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate',
482
+ 'peer/menu-button group/menu-button flex w-full cursor-pointer items-center gap-2 overflow-hidden rounded-md p-2 text-left text-xs ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate',
483
483
  {
484
484
  variants: {
485
485
  variant: {
@@ -585,7 +585,7 @@ function SidebarMenuBadge({
585
585
  data-slot="sidebar-menu-badge"
586
586
  data-sidebar="menu-badge"
587
587
  className={cn(
588
- 'pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
588
+ 'pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1',
589
589
  className,
590
590
  )}
591
591
  {...props}
@@ -637,7 +637,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
637
637
  data-slot="sidebar-menu-sub"
638
638
  data-sidebar="menu-sub"
639
639
  className={cn(
640
- 'ml-4 flex min-w-0 flex-col gap-1 pl-4 py-0.5 group-data-[collapsible=icon]:hidden',
640
+ 'ml-4 flex min-w-0 flex-col gap-1 py-0.5 pl-4 group-data-[collapsible=icon]:hidden',
641
641
  className,
642
642
  )}
643
643
  {...props}