@kinetixui/ui 0.6.4 → 0.7.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 +11 -1
- package/dist/index.js +395 -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/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/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/tailwind.config.ts +29 -0
package/src/components/sheet.tsx
CHANGED
|
@@ -18,7 +18,7 @@ const SheetOverlay = React.forwardRef<
|
|
|
18
18
|
<SheetPrimitive.Overlay
|
|
19
19
|
ref={ref}
|
|
20
20
|
className={cn(
|
|
21
|
-
"fixed inset-0 z-
|
|
21
|
+
"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",
|
|
22
22
|
className,
|
|
23
23
|
)}
|
|
24
24
|
{...props}
|
|
@@ -27,7 +27,7 @@ const SheetOverlay = React.forwardRef<
|
|
|
27
27
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
28
28
|
|
|
29
29
|
const sheetVariants = cva(
|
|
30
|
-
"fixed z-
|
|
30
|
+
"fixed z-overlay gap-4 bg-background p-6 shadow-lg transition ease-standard data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-base data-[state=open]:duration-slow font-sans",
|
|
31
31
|
{
|
|
32
32
|
variants: {
|
|
33
33
|
side: {
|
|
@@ -55,7 +55,7 @@ const SheetContent = React.forwardRef<
|
|
|
55
55
|
<SheetOverlay />
|
|
56
56
|
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
|
57
57
|
{children}
|
|
58
|
-
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-
|
|
58
|
+
<SheetPrimitive.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">
|
|
59
59
|
<X className="size-4" />
|
|
60
60
|
<span className="sr-only">Close</span>
|
|
61
61
|
</SheetPrimitive.Close>
|
|
@@ -159,7 +159,7 @@ const Sidebar = React.forwardRef<
|
|
|
159
159
|
>
|
|
160
160
|
<div
|
|
161
161
|
className={cn(
|
|
162
|
-
"relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-
|
|
162
|
+
"relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-fast ease-linear",
|
|
163
163
|
"group-data-[collapsible=offcanvas]:w-0",
|
|
164
164
|
variant === "floating" || variant === "inset"
|
|
165
165
|
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4))]"
|
|
@@ -168,7 +168,7 @@ const Sidebar = React.forwardRef<
|
|
|
168
168
|
/>
|
|
169
169
|
<div
|
|
170
170
|
className={cn(
|
|
171
|
-
"fixed inset-y-0 z-
|
|
171
|
+
"fixed inset-y-0 z-docked hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-fast ease-linear md:flex",
|
|
172
172
|
side === "left"
|
|
173
173
|
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
|
174
174
|
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
@@ -269,8 +269,8 @@ const SidebarGroupLabel = React.forwardRef<
|
|
|
269
269
|
ref={ref}
|
|
270
270
|
data-sidebar="group-label"
|
|
271
271
|
className={cn(
|
|
272
|
-
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none transition-[margin,opacity] duration-
|
|
273
|
-
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-
|
|
272
|
+
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none transition-[margin,opacity] duration-fast ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
273
|
+
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-hidden",
|
|
274
274
|
className,
|
|
275
275
|
)}
|
|
276
276
|
{...props}
|
|
@@ -297,7 +297,7 @@ const SidebarMenuItem = React.forwardRef<HTMLLIElement, React.ComponentProps<"li
|
|
|
297
297
|
SidebarMenuItem.displayName = "SidebarMenuItem";
|
|
298
298
|
|
|
299
299
|
const sidebarMenuButtonVariants = cva(
|
|
300
|
-
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] 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:opacity-
|
|
300
|
+
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] 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:opacity-disabled group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-disabled 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 group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
301
301
|
{
|
|
302
302
|
variants: {
|
|
303
303
|
variant: {
|
|
@@ -16,7 +16,7 @@ const Slider = React.forwardRef<
|
|
|
16
16
|
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-muted">
|
|
17
17
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
|
18
18
|
</SliderPrimitive.Track>
|
|
19
|
-
<SliderPrimitive.Thumb className="block size-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-
|
|
19
|
+
<SliderPrimitive.Thumb className="block size-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-disabled" />
|
|
20
20
|
</SliderPrimitive.Root>
|
|
21
21
|
));
|
|
22
22
|
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
@@ -18,7 +18,7 @@ const Switch = React.forwardRef<
|
|
|
18
18
|
className={cn(
|
|
19
19
|
"peer inline-flex h-6 w-12 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
|
|
20
20
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
21
|
-
"disabled:cursor-not-allowed disabled:opacity-
|
|
21
|
+
"disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
22
22
|
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-tertiary",
|
|
23
23
|
className,
|
|
24
24
|
)}
|
package/src/components/tabs.tsx
CHANGED
|
@@ -30,7 +30,7 @@ const TabsTrigger = React.forwardRef<
|
|
|
30
30
|
className={cn(
|
|
31
31
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all",
|
|
32
32
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
33
|
-
"disabled:pointer-events-none disabled:opacity-
|
|
33
|
+
"disabled:pointer-events-none disabled:opacity-disabled",
|
|
34
34
|
"data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
|
35
35
|
className,
|
|
36
36
|
)}
|
package/src/components/tag.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
4
5
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
6
|
import { X } from "lucide-react";
|
|
6
7
|
import { cn } from "../lib/utils";
|
|
@@ -32,24 +33,35 @@ export interface TagProps
|
|
|
32
33
|
VariantProps<typeof tagVariants> {
|
|
33
34
|
/** show a dismiss button; called when it's clicked */
|
|
34
35
|
onRemove?: () => void;
|
|
36
|
+
/** render as the single child element (Radix Slot) instead of <span> —
|
|
37
|
+
* e.g. `<Tag asChild><a href="/filter">Active</a></Tag>` for a linked tag.
|
|
38
|
+
* Combined with `onRemove`, the remove button nests inside the slotted
|
|
39
|
+
* element (Slot can only render one root node) — fine for a <div>/<span>,
|
|
40
|
+
* but avoid pairing asChild+onRemove with an <a>, since a nested <button>
|
|
41
|
+
* inside an anchor is invalid HTML. */
|
|
42
|
+
asChild?: boolean;
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
38
|
-
({ className, variant, onRemove, children, ...props }, ref) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
({ className, variant, onRemove, children, asChild = false, ...props }, ref) => {
|
|
47
|
+
const Comp = asChild ? Slot : "span";
|
|
48
|
+
const removeButton = onRemove && (
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
onClick={onRemove}
|
|
52
|
+
aria-label="Remove"
|
|
53
|
+
className="-mr-0.5 ml-0.5 rounded-[2px] opacity-muted outline-none transition-opacity hover:opacity-visible focus-visible:ring-1 focus-visible:ring-current"
|
|
54
|
+
>
|
|
55
|
+
<X className="size-3.5" />
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
return (
|
|
59
|
+
<Comp ref={ref} className={cn(tagVariants({ variant }), className)} {...props}>
|
|
60
|
+
{asChild ? <Slottable>{children}</Slottable> : children}
|
|
61
|
+
{removeButton}
|
|
62
|
+
</Comp>
|
|
63
|
+
);
|
|
64
|
+
},
|
|
53
65
|
);
|
|
54
66
|
Tag.displayName = "Tag";
|
|
55
67
|
|
|
@@ -18,7 +18,7 @@ const textareaVariants = cva(
|
|
|
18
18
|
"placeholder:text-muted-foreground",
|
|
19
19
|
"outline-none transition-colors",
|
|
20
20
|
"focus-visible:border-primary focus-visible:shadow-focus",
|
|
21
|
-
"disabled:cursor-not-allowed disabled:opacity-
|
|
21
|
+
"disabled:cursor-not-allowed disabled:opacity-disabled",
|
|
22
22
|
"aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive aria-[invalid=true]:focus-visible:shadow-focus-destructive",
|
|
23
23
|
],
|
|
24
24
|
{
|
|
@@ -27,7 +27,7 @@ const textareaVariants = cva(
|
|
|
27
27
|
Default: "",
|
|
28
28
|
Focus: "border-primary shadow-focus",
|
|
29
29
|
Error: "border-destructive",
|
|
30
|
-
Disabled: "opacity-
|
|
30
|
+
Disabled: "opacity-disabled pointer-events-none resize-none",
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
defaultVariants: { state: "Default" },
|
|
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
7
|
|
|
8
8
|
const toggleVariants = cva(
|
|
9
|
-
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-
|
|
9
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium font-sans transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-disabled data-[state=on]:bg-accent data-[state=on]:text-accent-foreground data-[state=on]:ring-1 data-[state=on]:ring-inset data-[state=on]:ring-ring [&_svg]:size-4 [&_svg]:shrink-0",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
@@ -17,7 +17,7 @@ const TooltipContent = React.forwardRef<
|
|
|
17
17
|
ref={ref}
|
|
18
18
|
sideOffset={sideOffset}
|
|
19
19
|
className={cn(
|
|
20
|
-
"z-
|
|
20
|
+
"z-overlay overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground font-sans",
|
|
21
21
|
"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
22
22
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
23
23
|
className,
|
|
@@ -5,6 +5,7 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
5
5
|
import { Badge } from "./components/badge";
|
|
6
6
|
import { Button } from "./components/button";
|
|
7
7
|
import { Input } from "./components/input";
|
|
8
|
+
import { Tag } from "./components/tag";
|
|
8
9
|
import {
|
|
9
10
|
Accordion,
|
|
10
11
|
AccordionContent,
|
|
@@ -55,6 +56,42 @@ describe("Badge", () => {
|
|
|
55
56
|
expect(screen.getByText("tag").className).not.toBe(def);
|
|
56
57
|
expect(screen.getByText("tag")).toHaveClass("text-secondary");
|
|
57
58
|
});
|
|
59
|
+
|
|
60
|
+
it("renders as a child element with asChild", () => {
|
|
61
|
+
render(
|
|
62
|
+
<Badge asChild>
|
|
63
|
+
<a href="/new">New</a>
|
|
64
|
+
</Badge>,
|
|
65
|
+
);
|
|
66
|
+
const link = screen.getByRole("link", { name: "New" });
|
|
67
|
+
expect(link).toHaveAttribute("href", "/new");
|
|
68
|
+
expect(link).toHaveClass("bg-primary"); // badge classes merged onto the <a>
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("Tag", () => {
|
|
73
|
+
it("renders as a child element with asChild", () => {
|
|
74
|
+
render(
|
|
75
|
+
<Tag asChild>
|
|
76
|
+
<a href="/filter">Active</a>
|
|
77
|
+
</Tag>,
|
|
78
|
+
);
|
|
79
|
+
const link = screen.getByRole("link", { name: "Active" });
|
|
80
|
+
expect(link).toHaveAttribute("href", "/filter");
|
|
81
|
+
expect(link).toHaveClass("bg-accent"); // tag classes merged onto the <a>
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("nests the remove button inside the slotted element when combined with onRemove", async () => {
|
|
85
|
+
const onRemove = vi.fn();
|
|
86
|
+
render(
|
|
87
|
+
<Tag asChild onRemove={onRemove}>
|
|
88
|
+
<div>Active</div>
|
|
89
|
+
</Tag>,
|
|
90
|
+
);
|
|
91
|
+
const remove = screen.getByRole("button", { name: "Remove" });
|
|
92
|
+
await userEvent.click(remove);
|
|
93
|
+
expect(onRemove).toHaveBeenCalledOnce();
|
|
94
|
+
});
|
|
58
95
|
});
|
|
59
96
|
|
|
60
97
|
describe("Accordion", () => {
|
package/tailwind.config.ts
CHANGED
|
@@ -94,6 +94,35 @@ export default {
|
|
|
94
94
|
"2xl": "calc(var(--radius-lg) + 8px)",
|
|
95
95
|
full: "var(--radius-full)",
|
|
96
96
|
},
|
|
97
|
+
// Motion + scale — tokens/primitives/{motion,opacity,z-index}.json.
|
|
98
|
+
// Added alongside Tailwind's own numeric scales (z-10, opacity-50, …),
|
|
99
|
+
// not replacing them — these are the token-backed semantic names.
|
|
100
|
+
transitionDuration: {
|
|
101
|
+
fast: "var(--duration-fast)",
|
|
102
|
+
base: "var(--duration-base)",
|
|
103
|
+
slow: "var(--duration-slow)",
|
|
104
|
+
slower: "var(--duration-slower)",
|
|
105
|
+
},
|
|
106
|
+
transitionTimingFunction: {
|
|
107
|
+
// overrides Tailwind's built-in `ease-linear` with the identical
|
|
108
|
+
// cubic-bezier(0,0,1,1) value, now token-backed instead of hardcoded
|
|
109
|
+
linear: "var(--easing-linear)",
|
|
110
|
+
standard: "var(--easing-standard)",
|
|
111
|
+
},
|
|
112
|
+
zIndex: {
|
|
113
|
+
base: "var(--z-index-base)",
|
|
114
|
+
raised: "var(--z-index-raised)",
|
|
115
|
+
docked: "var(--z-index-docked)",
|
|
116
|
+
focus: "var(--z-index-focus)",
|
|
117
|
+
sticky: "var(--z-index-sticky)",
|
|
118
|
+
overlay: "var(--z-index-overlay)",
|
|
119
|
+
},
|
|
120
|
+
opacity: {
|
|
121
|
+
hidden: "var(--opacity-hidden)",
|
|
122
|
+
disabled: "var(--opacity-disabled)",
|
|
123
|
+
muted: "var(--opacity-muted)",
|
|
124
|
+
visible: "var(--opacity-visible)",
|
|
125
|
+
},
|
|
97
126
|
keyframes: {
|
|
98
127
|
"accordion-down": { from: { height: "0" }, to: { height: "var(--radix-accordion-content-height)" } },
|
|
99
128
|
"accordion-up": { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" } },
|