@teamix-evo/ui 0.7.1 → 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.
- package/manifest.json +16 -7
- package/package.json +4 -4
- package/src/_design-system/theme-tokens/stories.tsx +2 -2
- package/src/components/accordion/index.tsx +1 -1
- package/src/components/affix/meta.md +26 -0
- package/src/components/alert/index.tsx +2 -2
- package/src/components/alert-dialog/index.tsx +3 -3
- package/src/components/alert-dialog/meta.md +52 -0
- package/src/components/alert-dialog/stories.tsx +45 -48
- package/src/components/avatar/index.tsx +1 -1
- package/src/components/badge/index.tsx +2 -2
- package/src/components/badge/meta.md +48 -0
- package/src/components/button/index.tsx +2 -2
- package/src/components/button/meta.md +15 -0
- package/src/components/button/stories.tsx +1 -1
- package/src/components/calendar/index.tsx +2 -2
- package/src/components/card/index.tsx +1 -1
- package/src/components/carousel/index.tsx +2 -2
- package/src/components/carousel/meta.md +34 -2
- package/src/components/carousel/stories.tsx +2 -2
- package/src/components/cascader-select/index.tsx +2 -1
- package/src/components/cascader-select/meta.md +46 -0
- package/src/components/checkbox/meta.md +47 -0
- package/src/components/color-picker/index.tsx +3 -3
- package/src/components/color-picker/meta.md +80 -0
- package/src/components/combobox/index.tsx +2 -2
- package/src/components/combobox/meta.md +130 -0
- package/src/components/data-table/index.tsx +2 -2
- package/src/components/data-table/meta.md +419 -0
- package/src/components/date-picker/meta.md +91 -0
- package/src/components/descriptions/index.tsx +1 -1
- package/src/components/descriptions/meta.md +245 -0
- package/src/components/dialog/index.tsx +4 -4
- package/src/components/dialog/meta.md +47 -1
- package/src/components/dialog/stories.tsx +38 -41
- package/src/components/dropdown-menu/index.tsx +5 -5
- package/src/components/empty/index.tsx +2 -2
- package/src/components/field/index.tsx +4 -4
- package/src/components/filter-bar/index.tsx +6 -6
- package/src/components/filter-bar/meta.md +323 -0
- package/src/components/float-button/index.tsx +2 -2
- package/src/components/form/index.tsx +1 -1
- package/src/components/form/meta.md +119 -0
- package/src/components/hover-card/index.tsx +1 -1
- package/src/components/hover-card/meta.md +21 -0
- package/src/components/input/meta.md +16 -0
- package/src/components/input-group/index.tsx +1 -1
- package/src/components/input-group/meta.md +118 -0
- package/src/components/input-group/stories.tsx +6 -6
- package/src/components/input-ip/index.tsx +2 -2
- package/src/components/input-ip/meta.md +30 -0
- package/src/components/input-ip/stories.tsx +2 -2
- package/src/components/input-number/index.tsx +3 -2
- package/src/components/input-number/meta.md +67 -0
- package/src/components/input-number/stories.tsx +2 -2
- package/src/components/item/index.tsx +4 -4
- package/src/components/label/meta.md +8 -0
- package/src/components/mentions/meta.md +15 -0
- package/src/components/menubar/index.tsx +4 -4
- package/src/components/navigation-menu/index.tsx +4 -4
- package/src/components/page-header/index.tsx +1 -1
- package/src/components/page-header/meta.md +145 -0
- package/src/components/page-shell/index.tsx +2 -2
- package/src/components/pagination/index.tsx +1 -1
- package/src/components/pagination/meta.md +203 -0
- package/src/components/popconfirm/meta.md +45 -0
- package/src/components/popover/index.tsx +2 -2
- package/src/components/popover/meta.md +47 -0
- package/src/components/progress/index.tsx +1 -1
- package/src/components/progress/meta.md +36 -0
- package/src/components/progress/stories.tsx +1 -1
- package/src/components/radio-group/meta.md +69 -0
- package/src/components/rate/index.tsx +1 -1
- package/src/components/rate/meta.md +50 -0
- package/src/components/resizable/index.tsx +1 -1
- package/src/components/select/index.tsx +2 -2
- package/src/components/select/meta.md +20 -0
- package/src/components/separator/index.tsx +1 -1
- package/src/components/sheet/index.tsx +13 -14
- package/src/components/sheet/meta.md +124 -0
- package/src/components/sheet/stories.tsx +110 -119
- package/src/components/sidebar/index.tsx +5 -5
- package/src/components/sidebar/meta.md +383 -0
- package/src/components/skeleton/meta.md +13 -0
- package/src/components/slider/index.tsx +2 -2
- package/src/components/sonner/meta.md +86 -0
- package/src/components/spinner/meta.md +46 -0
- package/src/components/spinner/stories.tsx +2 -2
- package/src/components/steps/meta.md +20 -0
- package/src/components/steps/stories.tsx +1 -1
- package/src/components/switch/index.tsx +2 -2
- package/src/components/switch/meta.md +33 -0
- package/src/components/table/index.tsx +2 -2
- package/src/components/table/meta.md +11 -0
- package/src/components/tabs/index.tsx +7 -7
- package/src/components/tabs/meta.md +52 -0
- package/src/components/tag/index.tsx +8 -8
- package/src/components/tag/meta.md +194 -0
- package/src/components/textarea/index.tsx +1 -1
- package/src/components/textarea/meta.md +27 -0
- package/src/components/textarea/stories.tsx +1 -1
- package/src/components/time-picker/index.tsx +3 -3
- package/src/components/time-picker/meta.md +76 -0
- package/src/components/timeline/index.tsx +1 -0
- package/src/components/toggle/index.tsx +1 -1
- package/src/components/toggle-group/index.tsx +1 -1
- package/src/components/tooltip/index.tsx +1 -1
- package/src/components/tooltip/meta.md +23 -0
- package/src/components/transfer/index.tsx +2 -2
- package/src/components/transfer/meta.md +97 -0
- package/src/components/tree/index.tsx +245 -15
- package/src/components/tree/meta.md +151 -0
- package/src/components/tree-select/index.tsx +16 -2
- package/src/components/tree-select/meta.md +150 -0
- package/src/components/typography/index.tsx +3 -3
- package/src/components/upload/index.tsx +3 -3
- package/src/components/upload/meta.md +82 -0
- package/src/components/tree/utils.ts +0 -269
- package/src/examples/built-in-assets/stories.tsx +0 -572
- package/src/examples/evaluators/stories.tsx +0 -502
package/manifest.json
CHANGED
|
@@ -507,7 +507,7 @@
|
|
|
507
507
|
}
|
|
508
508
|
],
|
|
509
509
|
"meta": "src/components/dialog/meta.md",
|
|
510
|
-
"registryDependencies": ["cn", "button"],
|
|
510
|
+
"registryDependencies": ["cn", "button", "use-radix-popper-guard"],
|
|
511
511
|
"dependencies": {
|
|
512
512
|
"radix-ui": "^1.4.3",
|
|
513
513
|
"class-variance-authority": "^0.7.0",
|
|
@@ -551,7 +551,7 @@
|
|
|
551
551
|
}
|
|
552
552
|
],
|
|
553
553
|
"meta": "src/components/sheet/meta.md",
|
|
554
|
-
"registryDependencies": ["cn"],
|
|
554
|
+
"registryDependencies": ["cn", "use-radix-popper-guard"],
|
|
555
555
|
"dependencies": {
|
|
556
556
|
"radix-ui": "^1.4.3",
|
|
557
557
|
"class-variance-authority": "^0.7.0",
|
|
@@ -812,6 +812,20 @@
|
|
|
812
812
|
],
|
|
813
813
|
"updateStrategy": "frozen"
|
|
814
814
|
},
|
|
815
|
+
{
|
|
816
|
+
"id": "use-radix-popper-guard",
|
|
817
|
+
"name": "useRadixPopperGuard",
|
|
818
|
+
"type": "hook",
|
|
819
|
+
"description": "防止 Radix DismissableLayer 级联关闭 — Dialog/Sheet 内嵌 Select/Popover 时阻止误关宿主弹窗",
|
|
820
|
+
"files": [
|
|
821
|
+
{
|
|
822
|
+
"source": "src/hooks/use-radix-popper-guard.ts",
|
|
823
|
+
"targetAlias": "hooks",
|
|
824
|
+
"targetName": "use-radix-popper-guard.ts"
|
|
825
|
+
}
|
|
826
|
+
],
|
|
827
|
+
"updateStrategy": "frozen"
|
|
828
|
+
},
|
|
815
829
|
{
|
|
816
830
|
"id": "sidebar",
|
|
817
831
|
"name": "Sidebar",
|
|
@@ -1150,11 +1164,6 @@
|
|
|
1150
1164
|
"source": "src/components/tree/index.tsx",
|
|
1151
1165
|
"targetAlias": "components",
|
|
1152
1166
|
"targetName": "tree.tsx"
|
|
1153
|
-
},
|
|
1154
|
-
{
|
|
1155
|
-
"source": "src/components/tree/utils.ts",
|
|
1156
|
-
"targetAlias": "components",
|
|
1157
|
-
"targetName": "utils.ts"
|
|
1158
1167
|
}
|
|
1159
1168
|
],
|
|
1160
1169
|
"meta": "src/components/tree/meta.md",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamix-evo/ui",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Source-injected UI components for Teamix Evo (shadcn-based, antd capabilities)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"vite": "^5.4.0",
|
|
52
52
|
"vite-tsconfig-paths": "^6.1.1",
|
|
53
53
|
"zod": "^3",
|
|
54
|
-
"@teamix-evo/
|
|
55
|
-
"@teamix-evo/
|
|
56
|
-
"@teamix-evo/registry": "0.
|
|
54
|
+
"@teamix-evo/eslint-config": "0.3.0",
|
|
55
|
+
"@teamix-evo/tokens": "^0.8.2",
|
|
56
|
+
"@teamix-evo/registry": "0.14.0"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public",
|
|
@@ -655,7 +655,7 @@ function RadiusRow({ k, cls }: { k: string; cls: string }) {
|
|
|
655
655
|
{/* 隐形 probe 用来取值,与可见样本同一 class 同一渲染路径 */}
|
|
656
656
|
<div ref={probeRef} className={cls} style={{ display: 'none' }} />
|
|
657
657
|
<div
|
|
658
|
-
className={`flex
|
|
658
|
+
className={`flex items-center justify-center border-2 border-foreground size-16 ${cls}`}
|
|
659
659
|
aria-label={`rounded-${k} sample`}
|
|
660
660
|
>
|
|
661
661
|
<span className="font-mono text-xxs uppercase tracking-widest">
|
|
@@ -1140,7 +1140,7 @@ function SpacingRow({
|
|
|
1140
1140
|
element-A
|
|
1141
1141
|
</div>
|
|
1142
1142
|
<div
|
|
1143
|
-
className="w-full border-
|
|
1143
|
+
className="w-full border-primary border-x-2"
|
|
1144
1144
|
style={{ height: `var(${varName})`, minHeight: '2px' }}
|
|
1145
1145
|
aria-label={`gap ${varName}`}
|
|
1146
1146
|
/>
|
|
@@ -121,7 +121,7 @@ function AccordionContent({
|
|
|
121
121
|
return (
|
|
122
122
|
<AccordionPrimitive.Content
|
|
123
123
|
data-slot="accordion-content"
|
|
124
|
-
className="overflow-hidden text-sm data-[state=
|
|
124
|
+
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
125
125
|
{...props}
|
|
126
126
|
>
|
|
127
127
|
<div
|
|
@@ -21,3 +21,29 @@
|
|
|
21
21
|
| `offsetBottom` | `number` | – | – | 距离视口底部指定偏移量后吸底(px)— 与 `offsetTop` 互斥。 |
|
|
22
22
|
| `container` | `() => HTMLElement \| Window` | `() => window` | – | 滚动容器 — 默认 `window`。传入自定义容器时使用 `position: sticky`。 |
|
|
23
23
|
| `onAffixChange` | `(affixed: boolean) => void` | – | – | 吸附状态变化回调。 |
|
|
24
|
+
|
|
25
|
+
## 示例
|
|
26
|
+
|
|
27
|
+
### Bottom
|
|
28
|
+
|
|
29
|
+
吸底 — 滚动时固定在容器底部。
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<div
|
|
33
|
+
ref={ref}
|
|
34
|
+
className="relative h-[400px] overflow-auto rounded-md border border-border p-4"
|
|
35
|
+
>
|
|
36
|
+
<div className="h-[500px] rounded-md border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
37
|
+
向下滚动
|
|
38
|
+
</div>
|
|
39
|
+
<div className="mt-3" />
|
|
40
|
+
<Affix offsetBottom={16} container={() => ref.current!}>
|
|
41
|
+
<div className="rounded-md border border-border bg-background p-3 shadow-sm">
|
|
42
|
+
<Button block>立即提交(吸底)</Button>
|
|
43
|
+
</div>
|
|
44
|
+
</Affix>
|
|
45
|
+
<div className="h-[500px] rounded-md border border-border bg-card p-4 mt-3 text-sm text-muted-foreground">
|
|
46
|
+
底部区域
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from '@/components/icon';
|
|
26
26
|
|
|
27
27
|
const alertVariants = cva(
|
|
28
|
-
'group/alert relative grid w-full grid-cols-[auto_1fr] items-center gap-x-2 rounded-md p-2 text-left text-foreground has-data-[slot=alert-
|
|
28
|
+
'group/alert relative grid w-full grid-cols-[auto_1fr] items-center gap-x-2 rounded-md p-2 text-left text-foreground has-data-[slot=alert-action]:pr-18 has-data-[slot=alert-close]:pr-7 has-data-[slot=alert-title]:items-start',
|
|
29
29
|
{
|
|
30
30
|
variants: {
|
|
31
31
|
variant: {
|
|
@@ -33,7 +33,7 @@ const alertVariants = cva(
|
|
|
33
33
|
success: 'border border-success-bg bg-success-bg',
|
|
34
34
|
warning: 'border border-warning-bg bg-warning-bg',
|
|
35
35
|
destructive: 'border border-destructive-bg bg-destructive-bg',
|
|
36
|
-
help: 'border border-
|
|
36
|
+
help: 'border border-help-bg bg-help-bg',
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
defaultVariants: {
|
|
@@ -68,7 +68,7 @@ function AlertDialogOverlay({
|
|
|
68
68
|
<AlertDialogPrimitive.Overlay
|
|
69
69
|
data-slot="alert-dialog-overlay"
|
|
70
70
|
className={cn(
|
|
71
|
-
'fixed inset-0 z-50 bg-black/10 duration-100
|
|
71
|
+
'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',
|
|
72
72
|
className,
|
|
73
73
|
)}
|
|
74
74
|
{...props}
|
|
@@ -101,7 +101,7 @@ function AlertDialogContent({
|
|
|
101
101
|
data-size={size}
|
|
102
102
|
className={cn(
|
|
103
103
|
// eslint-disable-next-line teamix-evo/no-arbitrary-tw-value -- mobile viewport needs calc(100% - 2rem) for safe inset; box-shadow drives via component-level token --dialog-shadow per ADR 0026.
|
|
104
|
-
'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-
|
|
104
|
+
'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-1/2 rounded-md bg-popover text-sm text-popover-foreground shadow-[var(--dialog-shadow,0px_6px_24px_0px_rgba(0,0,0,0.1))] duration-100 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=lg]:sm:max-w-3xl data-[size=md]:sm:max-w-xl data-[size=sm]:sm:max-w-md data-[size=xl]:sm:max-w-6xl',
|
|
105
105
|
className,
|
|
106
106
|
)}
|
|
107
107
|
{...props}
|
|
@@ -139,7 +139,7 @@ function AlertDialogBody({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
139
139
|
return (
|
|
140
140
|
<div
|
|
141
141
|
data-slot="alert-dialog-body"
|
|
142
|
-
className={cn('pr-5 pl-11
|
|
142
|
+
className={cn('pt-3 pr-5 pl-11', className)}
|
|
143
143
|
{...props}
|
|
144
144
|
/>
|
|
145
145
|
);
|
|
@@ -178,3 +178,55 @@
|
|
|
178
178
|
</AlertDialogContent>
|
|
179
179
|
</AlertDialog>
|
|
180
180
|
```
|
|
181
|
+
|
|
182
|
+
### Async
|
|
183
|
+
|
|
184
|
+
异步确认 — onAction 返回 Promise,loading 期间禁止关闭。
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
const [open, setOpen] = React.useState(false);
|
|
188
|
+
const [loading, setLoading] = React.useState(false);
|
|
189
|
+
const onConfirm = async (e: React.MouseEvent) => {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
setLoading(true);
|
|
192
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
193
|
+
setLoading(false);
|
|
194
|
+
setOpen(false);
|
|
195
|
+
};
|
|
196
|
+
return (
|
|
197
|
+
<AlertDialog
|
|
198
|
+
open={open}
|
|
199
|
+
onOpenChange={(next) => {
|
|
200
|
+
if (loading) return;
|
|
201
|
+
setOpen(next);
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
<AlertDialogTrigger asChild>
|
|
205
|
+
<Button variant="destructive">删除</Button>
|
|
206
|
+
</AlertDialogTrigger>
|
|
207
|
+
<AlertDialogContent>
|
|
208
|
+
<AlertDialogHeader>
|
|
209
|
+
<AlertDialogMedia>
|
|
210
|
+
<ErrorFilledIcon className="text-destructive" />
|
|
211
|
+
</AlertDialogMedia>
|
|
212
|
+
<AlertDialogTitle>确认删除?</AlertDialogTitle>
|
|
213
|
+
</AlertDialogHeader>
|
|
214
|
+
<AlertDialogBody>
|
|
215
|
+
<AlertDialogDescription>
|
|
216
|
+
onAction 返回 Promise,loading 期间无法关闭对话框。
|
|
217
|
+
</AlertDialogDescription>
|
|
218
|
+
</AlertDialogBody>
|
|
219
|
+
<AlertDialogFooter>
|
|
220
|
+
<AlertDialogCancel disabled={loading}>取消</AlertDialogCancel>
|
|
221
|
+
<Button
|
|
222
|
+
variant="destructive"
|
|
223
|
+
loading={loading}
|
|
224
|
+
onClick={onConfirm}
|
|
225
|
+
>
|
|
226
|
+
确认删除
|
|
227
|
+
</Button>
|
|
228
|
+
</AlertDialogFooter>
|
|
229
|
+
</AlertDialogContent>
|
|
230
|
+
</AlertDialog>
|
|
231
|
+
);
|
|
232
|
+
```
|
|
@@ -221,53 +221,50 @@ export const Warning: Story = {
|
|
|
221
221
|
export const Async: Story = {
|
|
222
222
|
name: '异步确认',
|
|
223
223
|
render: () => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
>
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
<
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
return <Demo />;
|
|
224
|
+
const [open, setOpen] = React.useState(false);
|
|
225
|
+
const [loading, setLoading] = React.useState(false);
|
|
226
|
+
const onConfirm = async (e: React.MouseEvent) => {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
setLoading(true);
|
|
229
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
230
|
+
setLoading(false);
|
|
231
|
+
setOpen(false);
|
|
232
|
+
};
|
|
233
|
+
return (
|
|
234
|
+
<AlertDialog
|
|
235
|
+
open={open}
|
|
236
|
+
onOpenChange={(next) => {
|
|
237
|
+
if (loading) return;
|
|
238
|
+
setOpen(next);
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
<AlertDialogTrigger asChild>
|
|
242
|
+
<Button variant="destructive">删除</Button>
|
|
243
|
+
</AlertDialogTrigger>
|
|
244
|
+
<AlertDialogContent>
|
|
245
|
+
<AlertDialogHeader>
|
|
246
|
+
<AlertDialogMedia>
|
|
247
|
+
<ErrorFilledIcon className="text-destructive" />
|
|
248
|
+
</AlertDialogMedia>
|
|
249
|
+
<AlertDialogTitle>确认删除?</AlertDialogTitle>
|
|
250
|
+
</AlertDialogHeader>
|
|
251
|
+
<AlertDialogBody>
|
|
252
|
+
<AlertDialogDescription>
|
|
253
|
+
onAction 返回 Promise,loading 期间无法关闭对话框。
|
|
254
|
+
</AlertDialogDescription>
|
|
255
|
+
</AlertDialogBody>
|
|
256
|
+
<AlertDialogFooter>
|
|
257
|
+
<AlertDialogCancel disabled={loading}>取消</AlertDialogCancel>
|
|
258
|
+
<Button
|
|
259
|
+
variant="destructive"
|
|
260
|
+
loading={loading}
|
|
261
|
+
onClick={onConfirm}
|
|
262
|
+
>
|
|
263
|
+
确认删除
|
|
264
|
+
</Button>
|
|
265
|
+
</AlertDialogFooter>
|
|
266
|
+
</AlertDialogContent>
|
|
267
|
+
</AlertDialog>
|
|
268
|
+
);
|
|
272
269
|
},
|
|
273
270
|
};
|
|
@@ -91,7 +91,7 @@ function AvatarFallback({
|
|
|
91
91
|
className={cn(
|
|
92
92
|
'flex size-full items-center justify-center bg-border text-muted-foreground',
|
|
93
93
|
'group-data-[shape=circle]/avatar:rounded-full group-data-[shape=square]/avatar:rounded-md',
|
|
94
|
-
'group-data-[size=
|
|
94
|
+
'group-data-[size=default]/avatar:text-xs group-data-[size=lg]/avatar:text-sm group-data-[size=sm]/avatar:text-xs',
|
|
95
95
|
'[&>svg]:size-1/2',
|
|
96
96
|
className,
|
|
97
97
|
)}
|
|
@@ -144,7 +144,7 @@ function Indicator({
|
|
|
144
144
|
data-indicator-kind={kind}
|
|
145
145
|
data-size={size}
|
|
146
146
|
className={cn(
|
|
147
|
-
'pointer-events-none inline-flex items-center justify-center rounded-full font-medium
|
|
147
|
+
'pointer-events-none inline-flex items-center justify-center rounded-full leading-none font-medium select-none',
|
|
148
148
|
// 默认色(content 形态走透明,由 color / 自定义 className 决定)
|
|
149
149
|
!isContent && 'bg-destructive text-destructive-foreground',
|
|
150
150
|
// content 形态 + 有色非透明底 → 白色文字
|
|
@@ -162,7 +162,7 @@ function Indicator({
|
|
|
162
162
|
: 'h-4 min-w-4 px-1 text-xs',
|
|
163
163
|
// wrapped 模式:绝对定位 + ring 描边
|
|
164
164
|
wrapped &&
|
|
165
|
-
'absolute top-0 right-0 z-10 origin-center
|
|
165
|
+
'absolute top-0 right-0 z-10 origin-center translate-x-1/2 -translate-y-1/2 ring-2 ring-background',
|
|
166
166
|
)}
|
|
167
167
|
style={style}
|
|
168
168
|
>
|
|
@@ -235,3 +235,51 @@
|
|
|
235
235
|
</Badge>
|
|
236
236
|
</div>
|
|
237
237
|
```
|
|
238
|
+
|
|
239
|
+
### Dynamic
|
|
240
|
+
|
|
241
|
+
受控示例:count 与 dot 动态切换。
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
function DynamicDemo() {
|
|
245
|
+
const [count, setCount] = React.useState(0);
|
|
246
|
+
const [show, setShow] = React.useState(true);
|
|
247
|
+
return (
|
|
248
|
+
<div className="flex flex-col gap-4">
|
|
249
|
+
<div className="flex items-center gap-3">
|
|
250
|
+
<Badge count={count}>
|
|
251
|
+
<Avatar>
|
|
252
|
+
<AvatarFallback>N</AvatarFallback>
|
|
253
|
+
</Avatar>
|
|
254
|
+
</Badge>
|
|
255
|
+
<Badge count={count} showZero>
|
|
256
|
+
<Avatar>
|
|
257
|
+
<AvatarFallback>Z</AvatarFallback>
|
|
258
|
+
</Avatar>
|
|
259
|
+
</Badge>
|
|
260
|
+
<Button size="sm" onClick={() => setCount((c) => c + 1)}>
|
|
261
|
+
+
|
|
262
|
+
</Button>
|
|
263
|
+
<Button
|
|
264
|
+
size="sm"
|
|
265
|
+
variant="outline"
|
|
266
|
+
onClick={() => setCount((c) => Math.max(0, c - 1))}
|
|
267
|
+
>
|
|
268
|
+
-
|
|
269
|
+
</Button>
|
|
270
|
+
</div>
|
|
271
|
+
<div className="flex items-center gap-3">
|
|
272
|
+
<Badge dot={show}>
|
|
273
|
+
<Avatar>
|
|
274
|
+
<AvatarFallback>D</AvatarFallback>
|
|
275
|
+
</Avatar>
|
|
276
|
+
</Badge>
|
|
277
|
+
<Button size="sm" onClick={() => setShow((s) => !s)}>
|
|
278
|
+
Toggle Dot
|
|
279
|
+
</Button>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return <DynamicDemo />;
|
|
285
|
+
```
|
|
@@ -24,7 +24,7 @@ import { Spinner } from '@/components/spinner';
|
|
|
24
24
|
// - shadow:组件不写,uni-manager 通过 theme.css scoped CSS [data-slot='button'] 注入
|
|
25
25
|
|
|
26
26
|
const buttonVariants = cva(
|
|
27
|
-
'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md font-normal whitespace-nowrap transition-colors select-none focus-visible:
|
|
27
|
+
'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md font-normal whitespace-nowrap transition-colors select-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',
|
|
28
28
|
{
|
|
29
29
|
variants: {
|
|
30
30
|
variant: {
|
|
@@ -54,7 +54,7 @@ const buttonVariants = cva(
|
|
|
54
54
|
shape: {
|
|
55
55
|
default: '',
|
|
56
56
|
round: 'rounded-full',
|
|
57
|
-
circle: 'rounded-full
|
|
57
|
+
circle: 'aspect-square rounded-full p-0',
|
|
58
58
|
square: 'aspect-square p-0',
|
|
59
59
|
},
|
|
60
60
|
block: {
|
|
@@ -96,6 +96,21 @@ Button 只有图标时,应该显示为正方形。
|
|
|
96
96
|
</div>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
### Loading
|
|
100
|
+
|
|
101
|
+
通过设置 `loading` 属性即可以让按钮处于加载状态。
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<div className="flex gap-2">
|
|
105
|
+
<Button loading variant="secondary">
|
|
106
|
+
Loading
|
|
107
|
+
</Button>
|
|
108
|
+
<Button loading={loading} onClick={() => setLoading(true)}>
|
|
109
|
+
Click to loading
|
|
110
|
+
</Button>
|
|
111
|
+
</div>
|
|
112
|
+
```
|
|
113
|
+
|
|
99
114
|
### Disabled
|
|
100
115
|
|
|
101
116
|
添加 `disabled` 属性即可让按钮处于不可用状态,同时按钮样式也会改变。
|
|
@@ -93,7 +93,7 @@ export const IconOnly: Story = {
|
|
|
93
93
|
|
|
94
94
|
/** 通过设置 `loading` 属性即可以让按钮处于加载状态。 */
|
|
95
95
|
export const Loading: Story = {
|
|
96
|
-
render:
|
|
96
|
+
render: () => {
|
|
97
97
|
const [loading, setLoading] = useState(false);
|
|
98
98
|
return (
|
|
99
99
|
<div className="flex gap-2">
|
|
@@ -117,7 +117,7 @@ function Calendar({
|
|
|
117
117
|
defaultClassNames.week_number,
|
|
118
118
|
),
|
|
119
119
|
day: cn(
|
|
120
|
-
'group/day relative aspect-square
|
|
120
|
+
'group/day relative aspect-square size-full rounded-md p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md',
|
|
121
121
|
props.showWeekNumber
|
|
122
122
|
? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-md'
|
|
123
123
|
: '[&:first-child[data-selected=true]_button]:rounded-l-md',
|
|
@@ -231,7 +231,7 @@ function CalendarDayButton({
|
|
|
231
231
|
data-range-end={modifiers.range_end}
|
|
232
232
|
data-range-middle={modifiers.range_middle}
|
|
233
233
|
className={cn(
|
|
234
|
-
'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 text-xs leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-1 group-data-[focused=true]/day:ring-ring data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md
|
|
234
|
+
'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 text-xs leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-1 group-data-[focused=true]/day:ring-ring hover:text-foreground data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70',
|
|
235
235
|
defaultClassNames.day,
|
|
236
236
|
className,
|
|
237
237
|
isActive &&
|
|
@@ -28,7 +28,7 @@ import { Skeleton } from '@/components/skeleton';
|
|
|
28
28
|
// - 垂直 padding 20px (`py-5`) / sm 16px (`py-4`)
|
|
29
29
|
// - normal 无阴影,hover 抬升
|
|
30
30
|
const cardVariants = cva(
|
|
31
|
-
'group/card flex flex-col overflow-hidden rounded-md bg-card text-card-foreground
|
|
31
|
+
'group/card flex flex-col overflow-hidden rounded-md bg-card text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-md *:[img:last-child]:rounded-b-md',
|
|
32
32
|
{
|
|
33
33
|
variants: {
|
|
34
34
|
variant: {
|
|
@@ -344,7 +344,7 @@ function CarouselPrevious({
|
|
|
344
344
|
className={cn(
|
|
345
345
|
'absolute z-10 touch-manipulation',
|
|
346
346
|
arrowPosition === 'inner' &&
|
|
347
|
-
'bg-background/80 backdrop-blur-sm
|
|
347
|
+
'bg-background/80 shadow-sm backdrop-blur-sm',
|
|
348
348
|
positionClass,
|
|
349
349
|
className,
|
|
350
350
|
)}
|
|
@@ -390,7 +390,7 @@ function CarouselNext({
|
|
|
390
390
|
className={cn(
|
|
391
391
|
'absolute z-10 touch-manipulation',
|
|
392
392
|
arrowPosition === 'inner' &&
|
|
393
|
-
'bg-background/80 backdrop-blur-sm
|
|
393
|
+
'bg-background/80 shadow-sm backdrop-blur-sm',
|
|
394
394
|
positionClass,
|
|
395
395
|
className,
|
|
396
396
|
)}
|
|
@@ -172,6 +172,38 @@
|
|
|
172
172
|
</Carousel>
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
+
### Controlled
|
|
176
|
+
|
|
177
|
+
受控当前页:外部按钮跳转。
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<div className="flex w-full max-w-xs flex-col gap-3 px-12">
|
|
181
|
+
<Carousel activeIndex={active} onChange={setActive}>
|
|
182
|
+
<CarouselContent>
|
|
183
|
+
{slides.map((i) => (
|
|
184
|
+
<CarouselItem key={i}>
|
|
185
|
+
<Slide index={i - 1}>{i}</Slide>
|
|
186
|
+
</CarouselItem>
|
|
187
|
+
))}
|
|
188
|
+
</CarouselContent>
|
|
189
|
+
<CarouselPrevious />
|
|
190
|
+
<CarouselNext />
|
|
191
|
+
</Carousel>
|
|
192
|
+
<div className="flex flex-wrap gap-2">
|
|
193
|
+
{slides.map((_, i) => (
|
|
194
|
+
<Button
|
|
195
|
+
key={i}
|
|
196
|
+
size="sm"
|
|
197
|
+
variant={i === active ? 'default' : 'outline'}
|
|
198
|
+
onClick={() => setActive(i)}
|
|
199
|
+
>
|
|
200
|
+
{i + 1}
|
|
201
|
+
</Button>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
```
|
|
206
|
+
|
|
175
207
|
### CustomDots
|
|
176
208
|
|
|
177
209
|
自定义指示点渲染。
|
|
@@ -195,8 +227,8 @@
|
|
|
195
227
|
onClick={onClick}
|
|
196
228
|
className={
|
|
197
229
|
selected
|
|
198
|
-
? '
|
|
199
|
-
: '
|
|
230
|
+
? 'cursor-pointer rounded-md bg-primary text-xs text-primary-foreground size-6'
|
|
231
|
+
: 'cursor-pointer rounded-md bg-muted text-xs text-muted-foreground hover:bg-accent size-6'
|
|
200
232
|
}
|
|
201
233
|
>
|
|
202
234
|
{index + 1}
|
|
@@ -257,8 +257,8 @@ export const CustomDots: Story = {
|
|
|
257
257
|
onClick={onClick}
|
|
258
258
|
className={
|
|
259
259
|
selected
|
|
260
|
-
? '
|
|
261
|
-
: '
|
|
260
|
+
? 'cursor-pointer rounded-md bg-primary text-xs text-primary-foreground size-6'
|
|
261
|
+
: 'cursor-pointer rounded-md bg-muted text-xs text-muted-foreground hover:bg-accent size-6'
|
|
262
262
|
}
|
|
263
263
|
>
|
|
264
264
|
{index + 1}
|
|
@@ -72,6 +72,7 @@ function CascaderPanel({
|
|
|
72
72
|
size?: 'sm' | 'default';
|
|
73
73
|
}) {
|
|
74
74
|
return (
|
|
75
|
+
// eslint-disable-next-line tailwindcss/no-contradicting-classname -- divide-color ≠ border-color
|
|
75
76
|
<div className="flex max-h-56 divide-x divide-border overflow-hidden rounded-md border border-border bg-popover shadow-md">
|
|
76
77
|
{columns.map((column, depth) => (
|
|
77
78
|
<div key={depth} className="min-w-32 overflow-y-auto p-1">
|
|
@@ -291,7 +292,7 @@ function CascaderSelect({
|
|
|
291
292
|
|
|
292
293
|
{/* Dropdown */}
|
|
293
294
|
{open && (
|
|
294
|
-
<div className="absolute left-0
|
|
295
|
+
<div className="absolute top-full left-0 z-50 mt-1">
|
|
295
296
|
<CascaderPanel
|
|
296
297
|
columns={columns}
|
|
297
298
|
activeValues={activeValues}
|
|
@@ -26,3 +26,49 @@
|
|
|
26
26
|
| `size` | `'sm' \| 'default'` | – | – | 尺寸。@default 'default' |
|
|
27
27
|
| `allowClear` | `boolean` | `true` | – | 是否允许清空:有选中值且未加载/未禁用时,右侧 chevron 切为 × 按钮。 与 Select 一致:右侧图标优先级 loading > clear > chevron,三者互斥、占同一位置。 |
|
|
28
28
|
| `loading` | `boolean` | `false` | – | 加载态:将右侧图标替换为旋转 spinner,并标记 `data-loading="true"` 供外部样式穿透。 |
|
|
29
|
+
|
|
30
|
+
## 示例
|
|
31
|
+
|
|
32
|
+
### Basic
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<CascaderSelect options={options} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### DefaultValue
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<CascaderSelect options={options} defaultValue={['zhejiang'} 'hangzhou'={'hangzhou'} 'xihu']={'xihu']} />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### SmallSize
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<CascaderSelect options={options} size="sm" />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Disabled
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<CascaderSelect options={options} disabled defaultValue={['jiangsu'} 'nanjing'={'nanjing'} 'xuanwu']={'xuanwu']} />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### WithClear
|
|
57
|
+
|
|
58
|
+
有选中值时右侧 chevron 切为 ×,点击清空。优先级:loading > clear > chevron。
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<CascaderSelect
|
|
62
|
+
options={options}
|
|
63
|
+
value={value}
|
|
64
|
+
onChange={(next) => setValue(next)}
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Loading
|
|
69
|
+
|
|
70
|
+
加载态:`loading` 为 `true` 时右侧图标替换为旋转 spinner。
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<CascaderSelect options={options} loading />
|
|
74
|
+
```
|