@kinetixui/ui 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +1176 -0
- package/dist/index.js +3939 -0
- package/package.json +106 -0
- package/src/components/accordion.tsx +52 -0
- package/src/components/alert-dialog.tsx +115 -0
- package/src/components/alert.tsx +47 -0
- package/src/components/aspect-ratio.tsx +7 -0
- package/src/components/audio-player.tsx +150 -0
- package/src/components/avatar.tsx +76 -0
- package/src/components/badge.tsx +40 -0
- package/src/components/breadcrumb.tsx +77 -0
- package/src/components/button.tsx +119 -0
- package/src/components/calendar.tsx +60 -0
- package/src/components/card.tsx +50 -0
- package/src/components/carousel.tsx +181 -0
- package/src/components/chart.tsx +177 -0
- package/src/components/checkbox.tsx +38 -0
- package/src/components/circular-progress.tsx +67 -0
- package/src/components/code-block.tsx +96 -0
- package/src/components/collapsible.tsx +9 -0
- package/src/components/command.tsx +123 -0
- package/src/components/context-menu.tsx +132 -0
- package/src/components/data-table.tsx +92 -0
- package/src/components/date-picker.tsx +68 -0
- package/src/components/dialog.tsx +101 -0
- package/src/components/drawer.tsx +82 -0
- package/src/components/dropdown-menu.tsx +132 -0
- package/src/components/fab.tsx +58 -0
- package/src/components/field.tsx +128 -0
- package/src/components/file-upload.tsx +183 -0
- package/src/components/footer.tsx +62 -0
- package/src/components/form.tsx +130 -0
- package/src/components/hover-card.tsx +28 -0
- package/src/components/image.tsx +87 -0
- package/src/components/inform.tsx +82 -0
- package/src/components/input-group.tsx +96 -0
- package/src/components/input-otp.tsx +63 -0
- package/src/components/input.tsx +72 -0
- package/src/components/label.tsx +20 -0
- package/src/components/list.tsx +69 -0
- package/src/components/menubar.tsx +168 -0
- package/src/components/metric.tsx +51 -0
- package/src/components/modal.tsx +126 -0
- package/src/components/navigation-bar.tsx +49 -0
- package/src/components/navigation-menu.tsx +117 -0
- package/src/components/number-input.tsx +86 -0
- package/src/components/pagination.tsx +77 -0
- package/src/components/password-input.tsx +36 -0
- package/src/components/popover.tsx +32 -0
- package/src/components/progress.tsx +24 -0
- package/src/components/quote.tsx +34 -0
- package/src/components/radio-group.tsx +44 -0
- package/src/components/rating.tsx +88 -0
- package/src/components/resizable.tsx +40 -0
- package/src/components/scroll-area.tsx +39 -0
- package/src/components/select.tsx +124 -0
- package/src/components/separator.tsx +25 -0
- package/src/components/sheet.tsx +104 -0
- package/src/components/sidebar.tsx +390 -0
- package/src/components/skeleton.tsx +9 -0
- package/src/components/slider.tsx +24 -0
- package/src/components/sonner.tsx +30 -0
- package/src/components/spinner.tsx +43 -0
- package/src/components/stepper.tsx +75 -0
- package/src/components/switch.tsx +37 -0
- package/src/components/tab-bar.tsx +58 -0
- package/src/components/table-of-contents.tsx +46 -0
- package/src/components/table.tsx +70 -0
- package/src/components/tabs.tsx +54 -0
- package/src/components/tag.tsx +56 -0
- package/src/components/textarea.tsx +56 -0
- package/src/components/toggle-group.tsx +41 -0
- package/src/components/toggle.tsx +34 -0
- package/src/components/tooltip.tsx +31 -0
- package/src/index.ts +305 -0
- package/src/lib/utils.ts +7 -0
- package/src/stories/Button.stories.tsx +155 -0
- package/src/stories/Input.stories.tsx +80 -0
- package/src/stories/Textarea.stories.tsx +69 -0
- package/tailwind.config.ts +107 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
import { buttonVariants } from "./button";
|
|
7
|
+
|
|
8
|
+
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
|
9
|
+
<nav
|
|
10
|
+
role="navigation"
|
|
11
|
+
aria-label="pagination"
|
|
12
|
+
className={cn("mx-auto flex w-full justify-center font-sans", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
Pagination.displayName = "Pagination";
|
|
17
|
+
|
|
18
|
+
const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentProps<"ul">>(
|
|
19
|
+
({ className, ...props }, ref) => (
|
|
20
|
+
<ul ref={ref} className={cn("flex flex-row items-center gap-1", className)} {...props} />
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
PaginationContent.displayName = "PaginationContent";
|
|
24
|
+
|
|
25
|
+
const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentProps<"li">>(
|
|
26
|
+
({ className, ...props }, ref) => <li ref={ref} className={cn("", className)} {...props} />,
|
|
27
|
+
);
|
|
28
|
+
PaginationItem.displayName = "PaginationItem";
|
|
29
|
+
|
|
30
|
+
type PaginationLinkProps = { isActive?: boolean } & React.ComponentProps<"a">;
|
|
31
|
+
|
|
32
|
+
const PaginationLink = ({ className, isActive, ...props }: PaginationLinkProps) => (
|
|
33
|
+
<a
|
|
34
|
+
aria-current={isActive ? "page" : undefined}
|
|
35
|
+
className={cn(
|
|
36
|
+
buttonVariants({ variant: isActive ? "Outline" : "Ghost", size: "icon" }),
|
|
37
|
+
"size-9",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
PaginationLink.displayName = "PaginationLink";
|
|
44
|
+
|
|
45
|
+
const PaginationPrevious = ({ className, ...props }: React.ComponentProps<typeof PaginationLink>) => (
|
|
46
|
+
<PaginationLink aria-label="Go to previous page" className={cn("w-auto gap-1 px-2.5", className)} {...props}>
|
|
47
|
+
<ChevronLeft className="size-4" />
|
|
48
|
+
<span>Previous</span>
|
|
49
|
+
</PaginationLink>
|
|
50
|
+
);
|
|
51
|
+
PaginationPrevious.displayName = "PaginationPrevious";
|
|
52
|
+
|
|
53
|
+
const PaginationNext = ({ className, ...props }: React.ComponentProps<typeof PaginationLink>) => (
|
|
54
|
+
<PaginationLink aria-label="Go to next page" className={cn("w-auto gap-1 px-2.5", className)} {...props}>
|
|
55
|
+
<span>Next</span>
|
|
56
|
+
<ChevronRight className="size-4" />
|
|
57
|
+
</PaginationLink>
|
|
58
|
+
);
|
|
59
|
+
PaginationNext.displayName = "PaginationNext";
|
|
60
|
+
|
|
61
|
+
const PaginationEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => (
|
|
62
|
+
<span aria-hidden className={cn("flex size-9 items-center justify-center", className)} {...props}>
|
|
63
|
+
<MoreHorizontal className="size-4" />
|
|
64
|
+
<span className="sr-only">More pages</span>
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
PaginationEllipsis.displayName = "PaginationEllipsis";
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
Pagination,
|
|
71
|
+
PaginationContent,
|
|
72
|
+
PaginationItem,
|
|
73
|
+
PaginationLink,
|
|
74
|
+
PaginationPrevious,
|
|
75
|
+
PaginationNext,
|
|
76
|
+
PaginationEllipsis,
|
|
77
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Eye, EyeOff } from "lucide-react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from "./input-group";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PasswordInput — an InputGroup recipe: a password field with a show / hide
|
|
10
|
+
* toggle. Forwards its ref to the underlying <input>.
|
|
11
|
+
*/
|
|
12
|
+
const PasswordInput = React.forwardRef<
|
|
13
|
+
HTMLInputElement,
|
|
14
|
+
Omit<React.InputHTMLAttributes<HTMLInputElement>, "type">
|
|
15
|
+
>(({ className, ...props }, ref) => {
|
|
16
|
+
const [visible, setVisible] = React.useState(false);
|
|
17
|
+
return (
|
|
18
|
+
<InputGroup className={className}>
|
|
19
|
+
<InputGroupInput ref={ref} type={visible ? "text" : "password"} {...props} />
|
|
20
|
+
<InputGroupAddon align="end">
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
aria-label={visible ? "Hide password" : "Show password"}
|
|
24
|
+
aria-pressed={visible}
|
|
25
|
+
onClick={() => setVisible((v) => !v)}
|
|
26
|
+
className={cn("rounded-[2px] outline-none transition-colors hover:text-foreground focus-visible:text-foreground")}
|
|
27
|
+
>
|
|
28
|
+
{visible ? <EyeOff /> : <Eye />}
|
|
29
|
+
</button>
|
|
30
|
+
</InputGroupAddon>
|
|
31
|
+
</InputGroup>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
PasswordInput.displayName = "PasswordInput";
|
|
35
|
+
|
|
36
|
+
export { PasswordInput };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Popover = PopoverPrimitive.Root;
|
|
8
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
9
|
+
const PopoverAnchor = PopoverPrimitive.Anchor;
|
|
10
|
+
|
|
11
|
+
const PopoverContent = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
14
|
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
|
15
|
+
<PopoverPrimitive.Portal>
|
|
16
|
+
<PopoverPrimitive.Content
|
|
17
|
+
ref={ref}
|
|
18
|
+
align={align}
|
|
19
|
+
sideOffset={sideOffset}
|
|
20
|
+
className={cn(
|
|
21
|
+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
|
|
22
|
+
"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",
|
|
23
|
+
"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",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
</PopoverPrimitive.Portal>
|
|
29
|
+
));
|
|
30
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
31
|
+
|
|
32
|
+
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Progress = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
10
|
+
>(({ className, value, ...props }, ref) => (
|
|
11
|
+
<ProgressPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn("relative h-2 w-full overflow-hidden rounded-full bg-muted", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
>
|
|
16
|
+
<ProgressPrimitive.Indicator
|
|
17
|
+
className="size-full flex-1 bg-primary transition-transform"
|
|
18
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
19
|
+
/>
|
|
20
|
+
</ProgressPrimitive.Root>
|
|
21
|
+
));
|
|
22
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
23
|
+
|
|
24
|
+
export { Progress };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Quote — a blockquote with an optional attributed author (name, title,
|
|
8
|
+
* avatar slot).
|
|
9
|
+
*/
|
|
10
|
+
export interface QuoteProps extends React.HTMLAttributes<HTMLElement> {
|
|
11
|
+
author?: React.ReactNode;
|
|
12
|
+
authorTitle?: React.ReactNode;
|
|
13
|
+
avatar?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Quote = React.forwardRef<HTMLElement, QuoteProps>(
|
|
17
|
+
({ className, author, authorTitle, avatar, children, ...props }, ref) => (
|
|
18
|
+
<figure ref={ref} className={cn("flex flex-col gap-4 font-sans", className)} {...props}>
|
|
19
|
+
<blockquote className="text-title-md font-medium text-foreground">“{children}”</blockquote>
|
|
20
|
+
{(author || authorTitle) && (
|
|
21
|
+
<figcaption className="flex items-center gap-3">
|
|
22
|
+
{avatar}
|
|
23
|
+
<div>
|
|
24
|
+
{author && <p className="text-body-md font-medium text-foreground">{author}</p>}
|
|
25
|
+
{authorTitle && <p className="text-body-sm text-muted-foreground">{authorTitle}</p>}
|
|
26
|
+
</div>
|
|
27
|
+
</figcaption>
|
|
28
|
+
)}
|
|
29
|
+
</figure>
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
Quote.displayName = "Quote";
|
|
33
|
+
|
|
34
|
+
export { Quote };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
5
|
+
import { Circle } from "lucide-react";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* RadioGroup — reconciled 1:1 with the KinetixUI design source, node 54863:536.
|
|
10
|
+
* 18px circle, 2px border. Unchecked border = `--input`; checked border + dot =
|
|
11
|
+
* `--primary`. Focus = 2px `--ring` offset. `aria-invalid` → `--destructive`.
|
|
12
|
+
*/
|
|
13
|
+
const RadioGroup = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
|
16
|
+
>(({ className, ...props }, ref) => (
|
|
17
|
+
<RadioGroupPrimitive.Root ref={ref} className={cn("grid gap-3", className)} {...props} />
|
|
18
|
+
));
|
|
19
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
20
|
+
|
|
21
|
+
const RadioGroupItem = React.forwardRef<
|
|
22
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
23
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
|
24
|
+
>(({ className, ...props }, ref) => (
|
|
25
|
+
<RadioGroupPrimitive.Item
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn(
|
|
28
|
+
"aspect-square size-[18px] rounded-full border-2 border-input text-primary outline-none transition-colors",
|
|
29
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
30
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
31
|
+
"data-[state=checked]:border-primary",
|
|
32
|
+
"aria-invalid:border-destructive",
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
38
|
+
<Circle className="size-2.5 fill-primary text-primary" />
|
|
39
|
+
</RadioGroupPrimitive.Indicator>
|
|
40
|
+
</RadioGroupPrimitive.Item>
|
|
41
|
+
));
|
|
42
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
43
|
+
|
|
44
|
+
export { RadioGroup, RadioGroupItem };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Star } from "lucide-react";
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Rating — a star rating input / display. Controlled (`value`) or
|
|
10
|
+
* uncontrolled (`defaultValue`); `readOnly` renders a static display, e.g.
|
|
11
|
+
* inside a Card.
|
|
12
|
+
*/
|
|
13
|
+
const starVariants = cva("transition-colors", {
|
|
14
|
+
variants: {
|
|
15
|
+
size: {
|
|
16
|
+
sm: "size-4",
|
|
17
|
+
md: "size-5",
|
|
18
|
+
lg: "size-6",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: { size: "md" },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export interface RatingProps
|
|
25
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
|
|
26
|
+
VariantProps<typeof starVariants> {
|
|
27
|
+
value?: number;
|
|
28
|
+
defaultValue?: number;
|
|
29
|
+
max?: number;
|
|
30
|
+
readOnly?: boolean;
|
|
31
|
+
onChange?: (value: number) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const Rating = React.forwardRef<HTMLDivElement, RatingProps>(
|
|
35
|
+
({ className, value, defaultValue = 0, max = 5, size, readOnly = false, onChange, ...props }, ref) => {
|
|
36
|
+
const [internal, setInternal] = React.useState(defaultValue);
|
|
37
|
+
const [hovered, setHovered] = React.useState<number | null>(null);
|
|
38
|
+
const current = value ?? internal;
|
|
39
|
+
const display = hovered ?? current;
|
|
40
|
+
|
|
41
|
+
const set = (n: number) => {
|
|
42
|
+
if (readOnly) return;
|
|
43
|
+
setInternal(n);
|
|
44
|
+
onChange?.(n);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
ref={ref}
|
|
50
|
+
role={readOnly ? "img" : "radiogroup"}
|
|
51
|
+
aria-label={readOnly ? `Rated ${current} out of ${max}` : "Rating"}
|
|
52
|
+
className={cn("inline-flex items-center gap-0.5", className)}
|
|
53
|
+
onMouseLeave={() => setHovered(null)}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
{Array.from({ length: max }, (_, i) => {
|
|
57
|
+
const n = i + 1;
|
|
58
|
+
const filled = n <= display;
|
|
59
|
+
return (
|
|
60
|
+
<button
|
|
61
|
+
key={n}
|
|
62
|
+
type="button"
|
|
63
|
+
disabled={readOnly}
|
|
64
|
+
aria-label={`${n} star${n > 1 ? "s" : ""}`}
|
|
65
|
+
aria-pressed={n <= current}
|
|
66
|
+
onMouseEnter={() => !readOnly && setHovered(n)}
|
|
67
|
+
onClick={() => set(n)}
|
|
68
|
+
className={cn(
|
|
69
|
+
"rounded-[2px] outline-none disabled:cursor-default",
|
|
70
|
+
!readOnly && "cursor-pointer focus-visible:ring-2 focus-visible:ring-ring",
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
<Star
|
|
74
|
+
className={cn(
|
|
75
|
+
starVariants({ size }),
|
|
76
|
+
filled ? "fill-warning text-warning" : "fill-transparent text-muted-foreground",
|
|
77
|
+
)}
|
|
78
|
+
/>
|
|
79
|
+
</button>
|
|
80
|
+
);
|
|
81
|
+
})}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
Rating.displayName = "Rating";
|
|
87
|
+
|
|
88
|
+
export { Rating };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { GripVertical } from "lucide-react";
|
|
5
|
+
import * as ResizablePrimitive from "react-resizable-panels";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
const ResizablePanelGroup = ({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
|
|
12
|
+
<ResizablePrimitive.PanelGroup
|
|
13
|
+
className={cn("flex h-full w-full data-[panel-group-direction=vertical]:flex-col", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const ResizablePanel = ResizablePrimitive.Panel;
|
|
19
|
+
|
|
20
|
+
const ResizableHandle = ({
|
|
21
|
+
withHandle,
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & { withHandle?: boolean }) => (
|
|
25
|
+
<ResizablePrimitive.PanelResizeHandle
|
|
26
|
+
className={cn(
|
|
27
|
+
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
>
|
|
32
|
+
{withHandle && (
|
|
33
|
+
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
|
|
34
|
+
<GripVertical className="size-2.5" />
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
37
|
+
</ResizablePrimitive.PanelResizeHandle>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const ScrollArea = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
10
|
+
>(({ className, children, ...props }, ref) => (
|
|
11
|
+
<ScrollAreaPrimitive.Root ref={ref} className={cn("relative overflow-hidden", className)} {...props}>
|
|
12
|
+
<ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
|
|
13
|
+
<ScrollBar />
|
|
14
|
+
<ScrollAreaPrimitive.Corner />
|
|
15
|
+
</ScrollAreaPrimitive.Root>
|
|
16
|
+
));
|
|
17
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
const ScrollBar = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
22
|
+
>(({ className, orientation = "vertical", ...props }, ref) => (
|
|
23
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
24
|
+
ref={ref}
|
|
25
|
+
orientation={orientation}
|
|
26
|
+
className={cn(
|
|
27
|
+
"flex touch-none select-none transition-colors",
|
|
28
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-px",
|
|
29
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-px",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
|
35
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
36
|
+
));
|
|
37
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
38
|
+
|
|
39
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
5
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Select — reconciled 1:1 with the KinetixUI design source, node 54855:13882.
|
|
10
|
+
* The trigger mirrors Input: `rounded-sm`, `border-input`, `--spacing-3` pad,
|
|
11
|
+
* Body Medium text, a soft `--shadow-focus` glow, `--destructive` on aria-invalid.
|
|
12
|
+
*/
|
|
13
|
+
const Select = SelectPrimitive.Root;
|
|
14
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
15
|
+
const SelectValue = SelectPrimitive.Value;
|
|
16
|
+
|
|
17
|
+
const SelectTrigger = React.forwardRef<
|
|
18
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
19
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
20
|
+
>(({ className, children, ...props }, ref) => (
|
|
21
|
+
<SelectPrimitive.Trigger
|
|
22
|
+
ref={ref}
|
|
23
|
+
className={cn(
|
|
24
|
+
"flex min-h-[44px] w-full items-center justify-between rounded-sm border border-input bg-background px-3 py-3",
|
|
25
|
+
"font-sans text-[14px] leading-5 tracking-[0.25px] text-foreground",
|
|
26
|
+
"data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1",
|
|
27
|
+
"outline-none transition-colors",
|
|
28
|
+
"focus:border-primary focus:shadow-focus",
|
|
29
|
+
"data-[state=open]:border-primary",
|
|
30
|
+
"aria-[invalid=true]:border-destructive aria-[invalid=true]:focus:border-destructive aria-[invalid=true]:focus:shadow-focus-destructive",
|
|
31
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
<SelectPrimitive.Icon asChild>
|
|
38
|
+
<ChevronDown className="size-4 shrink-0 opacity-60" />
|
|
39
|
+
</SelectPrimitive.Icon>
|
|
40
|
+
</SelectPrimitive.Trigger>
|
|
41
|
+
));
|
|
42
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
43
|
+
|
|
44
|
+
const SelectContent = React.forwardRef<
|
|
45
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
46
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
47
|
+
>(({ className, children, position = "popper", ...props }, ref) => (
|
|
48
|
+
<SelectPrimitive.Portal>
|
|
49
|
+
<SelectPrimitive.Content
|
|
50
|
+
ref={ref}
|
|
51
|
+
position={position}
|
|
52
|
+
className={cn(
|
|
53
|
+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md font-sans",
|
|
54
|
+
"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",
|
|
55
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
<SelectPrimitive.ScrollUpButton className="flex h-6 items-center justify-center">
|
|
61
|
+
<ChevronUp className="size-4" />
|
|
62
|
+
</SelectPrimitive.ScrollUpButton>
|
|
63
|
+
<SelectPrimitive.Viewport
|
|
64
|
+
className={cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</SelectPrimitive.Viewport>
|
|
68
|
+
<SelectPrimitive.ScrollDownButton className="flex h-6 items-center justify-center">
|
|
69
|
+
<ChevronDown className="size-4" />
|
|
70
|
+
</SelectPrimitive.ScrollDownButton>
|
|
71
|
+
</SelectPrimitive.Content>
|
|
72
|
+
</SelectPrimitive.Portal>
|
|
73
|
+
));
|
|
74
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
75
|
+
|
|
76
|
+
const SelectLabel = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
79
|
+
>(({ className, ...props }, ref) => (
|
|
80
|
+
<SelectPrimitive.Label ref={ref} className={cn("px-2 py-1.5 text-sm font-semibold", className)} {...props} />
|
|
81
|
+
));
|
|
82
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
83
|
+
|
|
84
|
+
const SelectItem = React.forwardRef<
|
|
85
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
86
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
87
|
+
>(({ className, children, ...props }, ref) => (
|
|
88
|
+
<SelectPrimitive.Item
|
|
89
|
+
ref={ref}
|
|
90
|
+
className={cn(
|
|
91
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
92
|
+
"focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
93
|
+
className,
|
|
94
|
+
)}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
|
98
|
+
<SelectPrimitive.ItemIndicator>
|
|
99
|
+
<Check className="size-4" />
|
|
100
|
+
</SelectPrimitive.ItemIndicator>
|
|
101
|
+
</span>
|
|
102
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
103
|
+
</SelectPrimitive.Item>
|
|
104
|
+
));
|
|
105
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
106
|
+
|
|
107
|
+
const SelectSeparator = React.forwardRef<
|
|
108
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
109
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
110
|
+
>(({ className, ...props }, ref) => (
|
|
111
|
+
<SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
112
|
+
));
|
|
113
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
Select,
|
|
117
|
+
SelectGroup,
|
|
118
|
+
SelectValue,
|
|
119
|
+
SelectTrigger,
|
|
120
|
+
SelectContent,
|
|
121
|
+
SelectLabel,
|
|
122
|
+
SelectItem,
|
|
123
|
+
SelectSeparator,
|
|
124
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
const Separator = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
10
|
+
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
|
11
|
+
<SeparatorPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
decorative={decorative}
|
|
14
|
+
orientation={orientation}
|
|
15
|
+
className={cn(
|
|
16
|
+
"shrink-0 bg-border",
|
|
17
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
));
|
|
23
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
24
|
+
|
|
25
|
+
export { Separator };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
+
import { X } from "lucide-react";
|
|
7
|
+
import { cn } from "../lib/utils";
|
|
8
|
+
|
|
9
|
+
const Sheet = SheetPrimitive.Root;
|
|
10
|
+
const SheetTrigger = SheetPrimitive.Trigger;
|
|
11
|
+
const SheetClose = SheetPrimitive.Close;
|
|
12
|
+
const SheetPortal = SheetPrimitive.Portal;
|
|
13
|
+
|
|
14
|
+
const SheetOverlay = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
17
|
+
>(({ className, ...props }, ref) => (
|
|
18
|
+
<SheetPrimitive.Overlay
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(
|
|
21
|
+
"fixed inset-0 z-50 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
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
));
|
|
27
|
+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
28
|
+
|
|
29
|
+
const sheetVariants = cva(
|
|
30
|
+
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 font-sans",
|
|
31
|
+
{
|
|
32
|
+
variants: {
|
|
33
|
+
side: {
|
|
34
|
+
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
35
|
+
bottom:
|
|
36
|
+
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
37
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
38
|
+
right:
|
|
39
|
+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
defaultVariants: { side: "right" },
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
interface SheetContentProps
|
|
47
|
+
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
|
48
|
+
VariantProps<typeof sheetVariants> {}
|
|
49
|
+
|
|
50
|
+
const SheetContent = React.forwardRef<
|
|
51
|
+
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
52
|
+
SheetContentProps
|
|
53
|
+
>(({ side = "right", className, children, ...props }, ref) => (
|
|
54
|
+
<SheetPortal>
|
|
55
|
+
<SheetOverlay />
|
|
56
|
+
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
|
57
|
+
{children}
|
|
58
|
+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring">
|
|
59
|
+
<X className="size-4" />
|
|
60
|
+
<span className="sr-only">Close</span>
|
|
61
|
+
</SheetPrimitive.Close>
|
|
62
|
+
</SheetPrimitive.Content>
|
|
63
|
+
</SheetPortal>
|
|
64
|
+
));
|
|
65
|
+
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
66
|
+
|
|
67
|
+
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
68
|
+
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
|
69
|
+
);
|
|
70
|
+
SheetHeader.displayName = "SheetHeader";
|
|
71
|
+
|
|
72
|
+
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
73
|
+
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
|
|
74
|
+
);
|
|
75
|
+
SheetFooter.displayName = "SheetFooter";
|
|
76
|
+
|
|
77
|
+
const SheetTitle = React.forwardRef<
|
|
78
|
+
React.ElementRef<typeof SheetPrimitive.Title>,
|
|
79
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
80
|
+
>(({ className, ...props }, ref) => (
|
|
81
|
+
<SheetPrimitive.Title ref={ref} className={cn("text-lg font-semibold text-foreground", className)} {...props} />
|
|
82
|
+
));
|
|
83
|
+
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
84
|
+
|
|
85
|
+
const SheetDescription = React.forwardRef<
|
|
86
|
+
React.ElementRef<typeof SheetPrimitive.Description>,
|
|
87
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
|
88
|
+
>(({ className, ...props }, ref) => (
|
|
89
|
+
<SheetPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
90
|
+
));
|
|
91
|
+
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
Sheet,
|
|
95
|
+
SheetPortal,
|
|
96
|
+
SheetOverlay,
|
|
97
|
+
SheetTrigger,
|
|
98
|
+
SheetClose,
|
|
99
|
+
SheetContent,
|
|
100
|
+
SheetHeader,
|
|
101
|
+
SheetFooter,
|
|
102
|
+
SheetTitle,
|
|
103
|
+
SheetDescription,
|
|
104
|
+
};
|