@kinetixui/ui 0.6.5 → 0.8.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.
- package/dist/index.d.ts +47 -2
- package/dist/index.js +472 -380
- package/package.json +2 -2
- package/src/components/accordion.tsx +1 -1
- package/src/components/alert-dialog.tsx +2 -2
- package/src/components/app-bar.tsx +1 -1
- package/src/components/audio-player.tsx +3 -0
- package/src/components/badge.tsx +13 -4
- package/src/components/button.tsx +2 -2
- package/src/components/calendar.tsx +9 -5
- package/src/components/checkbox.tsx +1 -1
- package/src/components/circular-progress.tsx +1 -1
- package/src/components/command.tsx +4 -2
- package/src/components/context-menu.tsx +2 -2
- package/src/components/dialog.tsx +3 -3
- package/src/components/drawer.tsx +2 -2
- package/src/components/dropdown-menu.tsx +4 -2
- package/src/components/empty.tsx +76 -0
- package/src/components/fab.tsx +1 -1
- package/src/components/file-upload.tsx +1 -1
- package/src/components/hover-card.tsx +1 -1
- package/src/components/image.tsx +2 -2
- package/src/components/inform.tsx +1 -1
- package/src/components/input-group.tsx +2 -2
- package/src/components/input-otp.tsx +3 -3
- package/src/components/input.tsx +2 -2
- package/src/components/kbd.tsx +40 -0
- package/src/components/label.tsx +1 -1
- package/src/components/list.tsx +1 -1
- package/src/components/menubar.tsx +2 -2
- package/src/components/modal.tsx +2 -2
- package/src/components/navigation-menu.tsx +4 -4
- package/src/components/number-input.tsx +3 -3
- package/src/components/popover.tsx +1 -1
- package/src/components/radio-group.tsx +1 -1
- package/src/components/resizable.tsx +1 -1
- package/src/components/select.tsx +5 -3
- package/src/components/sheet.tsx +3 -3
- package/src/components/sidebar.tsx +5 -5
- package/src/components/slider.tsx +1 -1
- package/src/components/switch.tsx +1 -1
- package/src/components/tabs.tsx +1 -1
- package/src/components/tag.tsx +27 -15
- package/src/components/textarea.tsx +2 -2
- package/src/components/toggle.tsx +1 -1
- package/src/components/tooltip.tsx +1 -1
- package/src/components-behavior.test.tsx +37 -0
- package/src/index.ts +13 -0
- package/src/stories/Empty.stories.tsx +35 -0
- package/src/stories/Kbd.stories.tsx +27 -0
- package/tailwind.config.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinetixui/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KinetixUI React components — CVA + Radix + Tailwind on the KinetixUI token contract. Also distributed via the kinetixui CLI as a component registry.",
|
|
6
6
|
"keywords": [
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"react-dom": ">=18"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@kinetixui/tokens": "0.
|
|
93
|
+
"@kinetixui/tokens": "0.8.0",
|
|
94
94
|
"@testing-library/jest-dom": "^7.0.1",
|
|
95
95
|
"@testing-library/react": "^16.3.3",
|
|
96
96
|
"@testing-library/user-event": "^14.6.7",
|
|
@@ -29,7 +29,7 @@ const AccordionTrigger = React.forwardRef<
|
|
|
29
29
|
{...props}
|
|
30
30
|
>
|
|
31
31
|
{children}
|
|
32
|
-
<ChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-
|
|
32
|
+
<ChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-fast" />
|
|
33
33
|
</AccordionPrimitive.Trigger>
|
|
34
34
|
</AccordionPrimitive.Header>
|
|
35
35
|
));
|
|
@@ -16,7 +16,7 @@ const AlertDialogOverlay = React.forwardRef<
|
|
|
16
16
|
<AlertDialogPrimitive.Overlay
|
|
17
17
|
ref={ref}
|
|
18
18
|
className={cn(
|
|
19
|
-
"fixed inset-0 z-
|
|
19
|
+
"fixed inset-0 z-overlay bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
20
20
|
className,
|
|
21
21
|
)}
|
|
22
22
|
{...props}
|
|
@@ -33,7 +33,7 @@ const AlertDialogContent = React.forwardRef<
|
|
|
33
33
|
<AlertDialogPrimitive.Content
|
|
34
34
|
ref={ref}
|
|
35
35
|
className={cn(
|
|
36
|
-
"fixed left-1/2 top-1/2 z-
|
|
36
|
+
"fixed left-1/2 top-1/2 z-overlay grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
|
|
37
37
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
38
38
|
className,
|
|
39
39
|
)}
|
|
@@ -48,7 +48,7 @@ const AppBar = React.forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement>>(
|
|
|
48
48
|
<header
|
|
49
49
|
ref={ref}
|
|
50
50
|
className={cn(
|
|
51
|
-
"sticky top-0 z-
|
|
51
|
+
"sticky top-0 z-sticky w-full border-b border-border bg-background/95 font-sans",
|
|
52
52
|
"backdrop-blur supports-[backdrop-filter]:bg-background/75",
|
|
53
53
|
className,
|
|
54
54
|
)}
|
|
@@ -73,6 +73,9 @@ const AudioPlayer = React.forwardRef<HTMLDivElement, AudioPlayerProps>(
|
|
|
73
73
|
</button>
|
|
74
74
|
);
|
|
75
75
|
|
|
76
|
+
// disabled:opacity-40 (not --opacity-disabled's 0.5) is a real, pre-existing
|
|
77
|
+
// inconsistency — left as a literal rather than silently changing the
|
|
78
|
+
// rendered value; a design call, not a rename.
|
|
76
79
|
const iconBtn =
|
|
77
80
|
"grid size-8 shrink-0 place-items-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:text-foreground disabled:opacity-40 [&>svg]:size-4";
|
|
78
81
|
|
package/src/components/badge.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
5
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
6
|
import { cn } from "../lib/utils";
|
|
6
7
|
|
|
@@ -31,10 +32,18 @@ const badgeVariants = cva(
|
|
|
31
32
|
|
|
32
33
|
export interface BadgeProps
|
|
33
34
|
extends React.HTMLAttributes<HTMLDivElement>,
|
|
34
|
-
VariantProps<typeof badgeVariants> {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
VariantProps<typeof badgeVariants> {
|
|
36
|
+
/** render as the single child element (Radix Slot) instead of <div> —
|
|
37
|
+
* e.g. `<Badge asChild><a href="/new">New</a></Badge>` for a linked badge */
|
|
38
|
+
asChild?: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
42
|
+
({ className, variant, asChild = false, ...props }, ref) => {
|
|
43
|
+
const Comp = asChild ? Slot : "div";
|
|
44
|
+
return <Comp ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
Badge.displayName = "Badge";
|
|
48
|
+
|
|
40
49
|
export { Badge, badgeVariants };
|
|
@@ -28,7 +28,7 @@ const buttonVariants = cva(
|
|
|
28
28
|
"font-sans font-medium whitespace-nowrap select-none",
|
|
29
29
|
"rounded-md transition-colors outline-none",
|
|
30
30
|
"focus-visible:shadow-focus",
|
|
31
|
-
"disabled:pointer-events-none disabled:opacity-
|
|
31
|
+
"disabled:pointer-events-none disabled:opacity-disabled",
|
|
32
32
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-[18px]",
|
|
33
33
|
],
|
|
34
34
|
{
|
|
@@ -72,7 +72,7 @@ const buttonVariants = cva(
|
|
|
72
72
|
Hover: "",
|
|
73
73
|
Focus: "shadow-focus",
|
|
74
74
|
Active: "",
|
|
75
|
-
Disabled: "pointer-events-none opacity-
|
|
75
|
+
Disabled: "pointer-events-none opacity-disabled",
|
|
76
76
|
},
|
|
77
77
|
/** corner style — matches the design source's Corners property */
|
|
78
78
|
corners: {
|
|
@@ -19,13 +19,17 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
|
|
19
19
|
month_caption: "flex justify-center pt-1 relative items-center w-full",
|
|
20
20
|
caption_label: "text-sm font-medium",
|
|
21
21
|
nav: "flex items-center gap-1",
|
|
22
|
+
// Resting-dim/hover-brighten nav icons — numerically the disabled
|
|
23
|
+
// value (0.5) but not a disabled state, so left as a literal rather
|
|
24
|
+
// than forced into --opacity-disabled or --opacity-muted (0.7, which
|
|
25
|
+
// would change the rendered value).
|
|
22
26
|
button_previous: cn(
|
|
23
27
|
buttonVariants({ variant: "Outline", size: "icon" }),
|
|
24
|
-
"absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-
|
|
28
|
+
"absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-visible",
|
|
25
29
|
),
|
|
26
30
|
button_next: cn(
|
|
27
31
|
buttonVariants({ variant: "Outline", size: "icon" }),
|
|
28
|
-
"absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-
|
|
32
|
+
"absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-visible",
|
|
29
33
|
),
|
|
30
34
|
month_grid: "w-full border-collapse space-x-1",
|
|
31
35
|
weekdays: "flex",
|
|
@@ -36,19 +40,19 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
|
|
36
40
|
// accent background + corner rounding and hands the primary-colored
|
|
37
41
|
// "selected" circle down to `day_button` via a `group` class.
|
|
38
42
|
day: cn(
|
|
39
|
-
"group relative p-0 text-center text-sm focus-within:relative focus-within:z-
|
|
43
|
+
"group relative p-0 text-center text-sm focus-within:relative focus-within:z-focus aria-selected:bg-accent",
|
|
40
44
|
props.mode === "range" ? "" : "aria-selected:rounded-md",
|
|
41
45
|
),
|
|
42
46
|
day_button: cn(
|
|
43
47
|
buttonVariants({ variant: "Ghost", size: "icon" }),
|
|
44
|
-
"size-8 p-0 font-normal group-aria-selected:opacity-
|
|
48
|
+
"size-8 p-0 font-normal group-aria-selected:opacity-visible group-data-[selected=true]:bg-primary group-data-[selected=true]:text-primary-foreground group-data-[selected=true]:hover:bg-primary group-data-[selected=true]:hover:text-primary-foreground group-data-[selected=true]:focus:bg-primary group-data-[selected=true]:focus:text-primary-foreground",
|
|
45
49
|
),
|
|
46
50
|
range_start: "day-range-start rounded-l-md",
|
|
47
51
|
range_end: "day-range-end rounded-r-md",
|
|
48
52
|
range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
|
|
49
53
|
today: "bg-accent text-accent-foreground",
|
|
50
54
|
outside: "day-outside text-muted-foreground aria-selected:text-muted-foreground",
|
|
51
|
-
disabled: "text-muted-foreground opacity-
|
|
55
|
+
disabled: "text-muted-foreground opacity-disabled",
|
|
52
56
|
hidden: "invisible",
|
|
53
57
|
...classNames,
|
|
54
58
|
}}
|
|
@@ -20,7 +20,7 @@ const Checkbox = React.forwardRef<
|
|
|
20
20
|
className={cn(
|
|
21
21
|
"peer size-[18px] shrink-0 rounded-sm border-2 border-input outline-none transition-colors",
|
|
22
22
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
23
|
-
"disabled:cursor-not-allowed disabled:opacity-
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
24
24
|
"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
25
25
|
"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
|
|
26
26
|
"aria-invalid:border-destructive aria-invalid:data-[state=checked]:border-destructive aria-invalid:data-[state=checked]:bg-destructive",
|
|
@@ -52,7 +52,7 @@ const CircularProgress = React.forwardRef<HTMLDivElement, CircularProgressProps>
|
|
|
52
52
|
strokeLinecap="round"
|
|
53
53
|
strokeDasharray={c}
|
|
54
54
|
strokeDashoffset={c - (v / 100) * c}
|
|
55
|
-
className="stroke-primary transition-[stroke-dashoffset] duration-
|
|
55
|
+
className="stroke-primary transition-[stroke-dashoffset] duration-base"
|
|
56
56
|
/>
|
|
57
57
|
</svg>
|
|
58
58
|
{(showValue || label != null) && (
|
|
@@ -36,11 +36,13 @@ const CommandInput = React.forwardRef<
|
|
|
36
36
|
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
|
37
37
|
>(({ className, ...props }, ref) => (
|
|
38
38
|
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
|
39
|
+
{/* Resting-dim icon, no hover/disabled state — numerically the disabled
|
|
40
|
+
value (0.5) but not that role; left as a literal, see calendar.tsx */}
|
|
39
41
|
<Search className="mr-2 size-4 shrink-0 opacity-50" />
|
|
40
42
|
<CommandPrimitive.Input
|
|
41
43
|
ref={ref}
|
|
42
44
|
className={cn(
|
|
43
|
-
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-
|
|
45
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
44
46
|
className,
|
|
45
47
|
)}
|
|
46
48
|
{...props}
|
|
@@ -97,7 +99,7 @@ const CommandItem = React.forwardRef<
|
|
|
97
99
|
<CommandPrimitive.Item
|
|
98
100
|
ref={ref}
|
|
99
101
|
className={cn(
|
|
100
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-
|
|
102
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-disabled [&_svg]:size-4 [&_svg]:shrink-0",
|
|
101
103
|
className,
|
|
102
104
|
)}
|
|
103
105
|
{...props}
|
|
@@ -13,9 +13,9 @@ const ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
|
13
13
|
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
14
14
|
|
|
15
15
|
const itemCls =
|
|
16
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-
|
|
16
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
|
|
17
17
|
const contentCls =
|
|
18
|
-
"z-
|
|
18
|
+
"z-overlay min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
19
19
|
|
|
20
20
|
const ContextMenuSubTrigger = React.forwardRef<
|
|
21
21
|
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
|
|
@@ -17,7 +17,7 @@ const DialogOverlay = React.forwardRef<
|
|
|
17
17
|
<DialogPrimitive.Overlay
|
|
18
18
|
ref={ref}
|
|
19
19
|
className={cn(
|
|
20
|
-
"fixed inset-0 z-
|
|
20
|
+
"fixed inset-0 z-overlay bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
21
21
|
className,
|
|
22
22
|
)}
|
|
23
23
|
{...props}
|
|
@@ -34,14 +34,14 @@ const DialogContent = React.forwardRef<
|
|
|
34
34
|
<DialogPrimitive.Content
|
|
35
35
|
ref={ref}
|
|
36
36
|
className={cn(
|
|
37
|
-
"fixed left-1/2 top-1/2 z-
|
|
37
|
+
"fixed left-1/2 top-1/2 z-overlay grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
|
|
38
38
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
39
39
|
className,
|
|
40
40
|
)}
|
|
41
41
|
{...props}
|
|
42
42
|
>
|
|
43
43
|
{children}
|
|
44
|
-
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-
|
|
44
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-muted transition-opacity hover:opacity-visible focus:outline-none focus:ring-2 focus:ring-ring disabled:pointer-events-none">
|
|
45
45
|
<X className="size-4" />
|
|
46
46
|
<span className="sr-only">Close</span>
|
|
47
47
|
</DialogPrimitive.Close>
|
|
@@ -17,7 +17,7 @@ const DrawerOverlay = React.forwardRef<
|
|
|
17
17
|
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
|
18
18
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
|
19
19
|
>(({ className, ...props }, ref) => (
|
|
20
|
-
<DrawerPrimitive.Overlay ref={ref} className={cn("fixed inset-0 z-
|
|
20
|
+
<DrawerPrimitive.Overlay ref={ref} className={cn("fixed inset-0 z-overlay bg-foreground/40", className)} {...props} />
|
|
21
21
|
));
|
|
22
22
|
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
|
23
23
|
|
|
@@ -30,7 +30,7 @@ const DrawerContent = React.forwardRef<
|
|
|
30
30
|
<DrawerPrimitive.Content
|
|
31
31
|
ref={ref}
|
|
32
32
|
className={cn(
|
|
33
|
-
"fixed inset-x-0 bottom-0 z-
|
|
33
|
+
"fixed inset-x-0 bottom-0 z-overlay mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background font-sans",
|
|
34
34
|
className,
|
|
35
35
|
)}
|
|
36
36
|
{...props}
|
|
@@ -13,9 +13,9 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
|
13
13
|
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
14
14
|
|
|
15
15
|
const contentCls =
|
|
16
|
-
"z-
|
|
16
|
+
"z-overlay min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95";
|
|
17
17
|
const itemCls =
|
|
18
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-
|
|
18
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
|
|
19
19
|
|
|
20
20
|
const DropdownMenuContent = React.forwardRef<
|
|
21
21
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
@@ -85,6 +85,8 @@ const DropdownMenuSeparator = React.forwardRef<
|
|
|
85
85
|
));
|
|
86
86
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
87
87
|
|
|
88
|
+
// opacity-60 (not --opacity-muted's 0.7) is a pre-existing inconsistency —
|
|
89
|
+
// left as a literal rather than silently changing the rendered value.
|
|
88
90
|
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
|
|
89
91
|
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
|
|
90
92
|
);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Empty — a placeholder for a zero-results state (an empty table, an empty
|
|
9
|
+
* search, a fresh workspace with nothing in it yet). Composition-based, like
|
|
10
|
+
* Card: `Empty` is an unstyled-beyond-layout root so it composes cleanly
|
|
11
|
+
* inside whatever already has a border (a Table cell, a Card, a bare page
|
|
12
|
+
* section) instead of forcing its own. Gap-fill addition (not in the
|
|
13
|
+
* original Figma source) — matches the shadcn/ui Empty API shape.
|
|
14
|
+
*/
|
|
15
|
+
const Empty = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
16
|
+
({ className, ...props }, ref) => (
|
|
17
|
+
<div
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn("flex min-w-0 flex-1 flex-col items-center justify-center gap-6 p-10 text-center font-sans", className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
Empty.displayName = "Empty";
|
|
25
|
+
|
|
26
|
+
const EmptyHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
27
|
+
({ className, ...props }, ref) => (
|
|
28
|
+
<div ref={ref} className={cn("flex max-w-sm flex-col items-center gap-2", className)} {...props} />
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
EmptyHeader.displayName = "EmptyHeader";
|
|
32
|
+
|
|
33
|
+
const emptyMediaVariants = cva("flex shrink-0 items-center justify-center", {
|
|
34
|
+
variants: {
|
|
35
|
+
/** `icon` gives a muted circular badge behind the icon; `default` renders the child as-is (an illustration, an avatar, …) */
|
|
36
|
+
variant: {
|
|
37
|
+
default: "",
|
|
38
|
+
icon: "size-10 rounded-full bg-muted text-muted-foreground [&_svg]:size-5",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: { variant: "default" },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export interface EmptyMediaProps
|
|
45
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
46
|
+
VariantProps<typeof emptyMediaVariants> {}
|
|
47
|
+
|
|
48
|
+
const EmptyMedia = React.forwardRef<HTMLDivElement, EmptyMediaProps>(
|
|
49
|
+
({ className, variant, ...props }, ref) => (
|
|
50
|
+
<div ref={ref} className={cn(emptyMediaVariants({ variant }), className)} {...props} />
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
EmptyMedia.displayName = "EmptyMedia";
|
|
54
|
+
|
|
55
|
+
const EmptyTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
56
|
+
({ className, ...props }, ref) => (
|
|
57
|
+
<p ref={ref} className={cn("text-title-sm font-medium text-foreground", className)} {...props} />
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
EmptyTitle.displayName = "EmptyTitle";
|
|
61
|
+
|
|
62
|
+
const EmptyDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
63
|
+
({ className, ...props }, ref) => (
|
|
64
|
+
<p ref={ref} className={cn("text-body-sm text-muted-foreground", className)} {...props} />
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
EmptyDescription.displayName = "EmptyDescription";
|
|
68
|
+
|
|
69
|
+
const EmptyContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
70
|
+
({ className, ...props }, ref) => (
|
|
71
|
+
<div ref={ref} className={cn("flex flex-col items-center gap-3", className)} {...props} />
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
EmptyContent.displayName = "EmptyContent";
|
|
75
|
+
|
|
76
|
+
export { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent, emptyMediaVariants };
|
package/src/components/fab.tsx
CHANGED
|
@@ -13,7 +13,7 @@ const fabVariants = cva(
|
|
|
13
13
|
[
|
|
14
14
|
"inline-flex shrink-0 items-center justify-center gap-2 rounded-full font-sans font-medium",
|
|
15
15
|
"shadow-lg outline-none transition-colors",
|
|
16
|
-
"focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-
|
|
16
|
+
"focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-disabled",
|
|
17
17
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
18
18
|
],
|
|
19
19
|
{
|
|
@@ -84,7 +84,7 @@ const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
|
|
|
84
84
|
"flex flex-col items-center gap-2 rounded-md border border-dashed border-input p-6 text-center outline-none transition-colors",
|
|
85
85
|
!disabled && "cursor-pointer hover:bg-accent/50 focus-visible:shadow-focus",
|
|
86
86
|
dragging && "border-primary bg-accent",
|
|
87
|
-
disabled && "pointer-events-none opacity-
|
|
87
|
+
disabled && "pointer-events-none opacity-disabled",
|
|
88
88
|
)}
|
|
89
89
|
>
|
|
90
90
|
<Upload className="size-6 text-muted-foreground" aria-hidden />
|
|
@@ -16,7 +16,7 @@ const HoverCardContent = React.forwardRef<
|
|
|
16
16
|
align={align}
|
|
17
17
|
sideOffset={sideOffset}
|
|
18
18
|
className={cn(
|
|
19
|
-
"z-
|
|
19
|
+
"z-overlay w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
|
|
20
20
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
21
21
|
className,
|
|
22
22
|
)}
|
package/src/components/image.tsx
CHANGED
|
@@ -64,8 +64,8 @@ const Image = React.forwardRef<HTMLImageElement, ImageProps>(
|
|
|
64
64
|
ref={setRef}
|
|
65
65
|
alt={alt}
|
|
66
66
|
className={cn(
|
|
67
|
-
"size-full object-cover transition-opacity duration-
|
|
68
|
-
status === "loaded" ? "opacity-
|
|
67
|
+
"size-full object-cover transition-opacity duration-base",
|
|
68
|
+
status === "loaded" ? "opacity-visible" : "opacity-hidden",
|
|
69
69
|
)}
|
|
70
70
|
onLoad={(e) => {
|
|
71
71
|
setStatus("loaded");
|
|
@@ -68,7 +68,7 @@ const Inform = React.forwardRef<HTMLDivElement, InformProps>(
|
|
|
68
68
|
type="button"
|
|
69
69
|
onClick={onDismiss}
|
|
70
70
|
aria-label="Dismiss"
|
|
71
|
-
className="-m-0.5 shrink-0 rounded-[2px] p-0.5 opacity-
|
|
71
|
+
className="-m-0.5 shrink-0 rounded-[2px] p-0.5 opacity-muted outline-none transition-opacity hover:opacity-visible focus-visible:opacity-visible focus-visible:ring-1 focus-visible:ring-current"
|
|
72
72
|
>
|
|
73
73
|
<X className="size-3.5" />
|
|
74
74
|
</button>
|
|
@@ -24,7 +24,7 @@ const InputGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
|
|
|
24
24
|
"flex w-full items-center overflow-hidden rounded-sm border border-input bg-background font-sans transition-colors",
|
|
25
25
|
"focus-within:border-primary focus-within:shadow-focus",
|
|
26
26
|
"has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:focus-within:shadow-focus-destructive",
|
|
27
|
-
"has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-
|
|
27
|
+
"has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-disabled",
|
|
28
28
|
className,
|
|
29
29
|
)}
|
|
30
30
|
{...props}
|
|
@@ -85,7 +85,7 @@ const InputGroupButton = React.forwardRef<
|
|
|
85
85
|
className={cn(
|
|
86
86
|
"flex h-full shrink-0 items-center gap-1.5 self-stretch border-l border-input bg-muted px-3 text-label-md text-foreground",
|
|
87
87
|
// base is bg-muted and muted==accent on dark, so the fill is a no-op there — the ring carries it
|
|
88
|
-
"outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-
|
|
88
|
+
"outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
89
89
|
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
90
90
|
className,
|
|
91
91
|
)}
|
|
@@ -11,7 +11,7 @@ const InputOTP = React.forwardRef<
|
|
|
11
11
|
>(({ className, containerClassName, ...props }, ref) => (
|
|
12
12
|
<OTPInput
|
|
13
13
|
ref={ref}
|
|
14
|
-
containerClassName={cn("flex items-center gap-2 has-[:disabled]:opacity-
|
|
14
|
+
containerClassName={cn("flex items-center gap-2 has-[:disabled]:opacity-disabled", containerClassName)}
|
|
15
15
|
className={cn("disabled:cursor-not-allowed", className)}
|
|
16
16
|
{...props}
|
|
17
17
|
/>
|
|
@@ -35,7 +35,7 @@ const InputOTPSlot = React.forwardRef<
|
|
|
35
35
|
ref={ref}
|
|
36
36
|
className={cn(
|
|
37
37
|
"relative flex size-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
|
|
38
|
-
isActive && "z-
|
|
38
|
+
isActive && "z-docked ring-1 ring-ring",
|
|
39
39
|
className,
|
|
40
40
|
)}
|
|
41
41
|
{...props}
|
|
@@ -43,7 +43,7 @@ const InputOTPSlot = React.forwardRef<
|
|
|
43
43
|
{char}
|
|
44
44
|
{hasFakeCaret && (
|
|
45
45
|
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
46
|
-
<div className="h-4 w-px animate-caret-blink bg-foreground duration-
|
|
46
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-slower" />
|
|
47
47
|
</div>
|
|
48
48
|
)}
|
|
49
49
|
</div>
|
package/src/components/input.tsx
CHANGED
|
@@ -26,7 +26,7 @@ const inputVariants = cva(
|
|
|
26
26
|
"placeholder:text-muted-foreground",
|
|
27
27
|
"outline-none transition-colors",
|
|
28
28
|
"focus-visible:border-primary focus-visible:shadow-focus",
|
|
29
|
-
"disabled:cursor-not-allowed disabled:opacity-
|
|
29
|
+
"disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
30
30
|
"aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive aria-[invalid=true]:focus-visible:shadow-focus-destructive",
|
|
31
31
|
],
|
|
32
32
|
{
|
|
@@ -35,7 +35,7 @@ const inputVariants = cva(
|
|
|
35
35
|
Default: "",
|
|
36
36
|
Focus: "border-primary shadow-focus",
|
|
37
37
|
Error: "border-destructive",
|
|
38
|
-
Disabled: "opacity-
|
|
38
|
+
Disabled: "opacity-disabled pointer-events-none",
|
|
39
39
|
},
|
|
40
40
|
/** corner style — matches the design source's Corners property */
|
|
41
41
|
corners: {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Kbd — a single keyboard key glyph, e.g. `<Kbd>⌘</Kbd>`. Wrap several in
|
|
9
|
+
* `KbdGroup` for a shortcut combo (`⌘` `K`). Gap-fill addition (not in the
|
|
10
|
+
* original Figma source) — matches the shadcn/ui Kbd/KbdGroup API shape.
|
|
11
|
+
*/
|
|
12
|
+
export interface KbdProps extends React.HTMLAttributes<HTMLElement> {
|
|
13
|
+
/** render as the single child element (Radix Slot) instead of <kbd> */
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Kbd = React.forwardRef<HTMLElement, KbdProps>(({ className, asChild = false, ...props }, ref) => {
|
|
18
|
+
const Comp = asChild ? Slot : "kbd";
|
|
19
|
+
return (
|
|
20
|
+
<Comp
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn(
|
|
23
|
+
"inline-flex h-5 min-w-5 items-center justify-center rounded-sm border border-input bg-muted px-1.5 font-sans text-[11px] font-medium text-muted-foreground",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
Kbd.displayName = "Kbd";
|
|
31
|
+
|
|
32
|
+
export interface KbdGroupProps extends React.HTMLAttributes<HTMLElement> {}
|
|
33
|
+
|
|
34
|
+
/** Lays out multiple `Kbd` for a shortcut combo — `<KbdGroup><Kbd>⌘</Kbd><Kbd>K</Kbd></KbdGroup>` */
|
|
35
|
+
const KbdGroup = React.forwardRef<HTMLElement, KbdGroupProps>(({ className, ...props }, ref) => (
|
|
36
|
+
<kbd ref={ref} className={cn("inline-flex items-center gap-1", className)} {...props} />
|
|
37
|
+
));
|
|
38
|
+
KbdGroup.displayName = "KbdGroup";
|
|
39
|
+
|
|
40
|
+
export { Kbd, KbdGroup };
|
package/src/components/label.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
7
|
|
|
8
8
|
const labelVariants = cva(
|
|
9
|
-
"text-sm font-medium leading-none font-sans peer-disabled:cursor-not-allowed peer-disabled:opacity-
|
|
9
|
+
"text-sm font-medium leading-none font-sans peer-disabled:cursor-not-allowed peer-disabled:opacity-muted",
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
const Label = React.forwardRef<
|
package/src/components/list.tsx
CHANGED
|
@@ -51,7 +51,7 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
|
|
|
51
51
|
// ring keeps the highlighted row perceptible where the accent fill is <3:1 (dark)
|
|
52
52
|
interactive &&
|
|
53
53
|
"cursor-pointer hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
54
|
-
disabled && "pointer-events-none opacity-
|
|
54
|
+
disabled && "pointer-events-none opacity-disabled",
|
|
55
55
|
className,
|
|
56
56
|
)}
|
|
57
57
|
{...props}
|
|
@@ -22,9 +22,9 @@ const MenubarRadioGroup = (props: React.ComponentProps<typeof MenubarPrimitive.R
|
|
|
22
22
|
);
|
|
23
23
|
|
|
24
24
|
const itemCls =
|
|
25
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-
|
|
25
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
|
|
26
26
|
const contentCls =
|
|
27
|
-
"z-
|
|
27
|
+
"z-overlay min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95";
|
|
28
28
|
|
|
29
29
|
const Menubar = React.forwardRef<
|
|
30
30
|
React.ElementRef<typeof MenubarPrimitive.Root>,
|
package/src/components/modal.tsx
CHANGED
|
@@ -68,7 +68,7 @@ function Modal({
|
|
|
68
68
|
<DialogOverlay />
|
|
69
69
|
<DialogPrimitive.Content
|
|
70
70
|
className={cn(
|
|
71
|
-
"fixed left-1/2 top-1/2 z-
|
|
71
|
+
"fixed left-1/2 top-1/2 z-overlay flex w-full max-w-[480px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-background font-sans",
|
|
72
72
|
"shadow-[0_4px_24px_rgba(0,0,0,0.12)]",
|
|
73
73
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
74
74
|
className,
|
|
@@ -79,7 +79,7 @@ function Modal({
|
|
|
79
79
|
{title}
|
|
80
80
|
</DialogPrimitive.Title>
|
|
81
81
|
<DialogPrimitive.Close
|
|
82
|
-
className="rounded-sm opacity-
|
|
82
|
+
className="rounded-sm opacity-muted outline-none transition-opacity hover:opacity-visible focus-visible:ring-2 focus-visible:ring-ring"
|
|
83
83
|
aria-label="Close"
|
|
84
84
|
>
|
|
85
85
|
<X className="size-5" />
|
|
@@ -12,7 +12,7 @@ const NavigationMenu = React.forwardRef<
|
|
|
12
12
|
>(({ className, children, ...props }, ref) => (
|
|
13
13
|
<NavigationMenuPrimitive.Root
|
|
14
14
|
ref={ref}
|
|
15
|
-
className={cn("relative z-
|
|
15
|
+
className={cn("relative z-docked flex max-w-max flex-1 items-center justify-center font-sans", className)}
|
|
16
16
|
{...props}
|
|
17
17
|
>
|
|
18
18
|
{children}
|
|
@@ -36,7 +36,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
|
|
|
36
36
|
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
|
37
37
|
|
|
38
38
|
const navigationMenuTriggerStyle = cva(
|
|
39
|
-
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-
|
|
39
|
+
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-disabled data-[state=open]:bg-accent/50",
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
const NavigationMenuTrigger = React.forwardRef<
|
|
@@ -46,7 +46,7 @@ const NavigationMenuTrigger = React.forwardRef<
|
|
|
46
46
|
<NavigationMenuPrimitive.Trigger ref={ref} className={cn(navigationMenuTriggerStyle(), "group", className)} {...props}>
|
|
47
47
|
{children}
|
|
48
48
|
<ChevronDown
|
|
49
|
-
className="relative top-px ml-1 size-3 transition-transform duration-
|
|
49
|
+
className="relative top-px ml-1 size-3 transition-transform duration-fast group-data-[state=open]:rotate-180"
|
|
50
50
|
aria-hidden
|
|
51
51
|
/>
|
|
52
52
|
</NavigationMenuPrimitive.Trigger>
|
|
@@ -94,7 +94,7 @@ const NavigationMenuIndicator = React.forwardRef<
|
|
|
94
94
|
<NavigationMenuPrimitive.Indicator
|
|
95
95
|
ref={ref}
|
|
96
96
|
className={cn(
|
|
97
|
-
"top-full z-
|
|
97
|
+
"top-full z-raised flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
|
98
98
|
className,
|
|
99
99
|
)}
|
|
100
100
|
{...props}
|