@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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +1176 -0
  3. package/dist/index.js +3939 -0
  4. package/package.json +106 -0
  5. package/src/components/accordion.tsx +52 -0
  6. package/src/components/alert-dialog.tsx +115 -0
  7. package/src/components/alert.tsx +47 -0
  8. package/src/components/aspect-ratio.tsx +7 -0
  9. package/src/components/audio-player.tsx +150 -0
  10. package/src/components/avatar.tsx +76 -0
  11. package/src/components/badge.tsx +40 -0
  12. package/src/components/breadcrumb.tsx +77 -0
  13. package/src/components/button.tsx +119 -0
  14. package/src/components/calendar.tsx +60 -0
  15. package/src/components/card.tsx +50 -0
  16. package/src/components/carousel.tsx +181 -0
  17. package/src/components/chart.tsx +177 -0
  18. package/src/components/checkbox.tsx +38 -0
  19. package/src/components/circular-progress.tsx +67 -0
  20. package/src/components/code-block.tsx +96 -0
  21. package/src/components/collapsible.tsx +9 -0
  22. package/src/components/command.tsx +123 -0
  23. package/src/components/context-menu.tsx +132 -0
  24. package/src/components/data-table.tsx +92 -0
  25. package/src/components/date-picker.tsx +68 -0
  26. package/src/components/dialog.tsx +101 -0
  27. package/src/components/drawer.tsx +82 -0
  28. package/src/components/dropdown-menu.tsx +132 -0
  29. package/src/components/fab.tsx +58 -0
  30. package/src/components/field.tsx +128 -0
  31. package/src/components/file-upload.tsx +183 -0
  32. package/src/components/footer.tsx +62 -0
  33. package/src/components/form.tsx +130 -0
  34. package/src/components/hover-card.tsx +28 -0
  35. package/src/components/image.tsx +87 -0
  36. package/src/components/inform.tsx +82 -0
  37. package/src/components/input-group.tsx +96 -0
  38. package/src/components/input-otp.tsx +63 -0
  39. package/src/components/input.tsx +72 -0
  40. package/src/components/label.tsx +20 -0
  41. package/src/components/list.tsx +69 -0
  42. package/src/components/menubar.tsx +168 -0
  43. package/src/components/metric.tsx +51 -0
  44. package/src/components/modal.tsx +126 -0
  45. package/src/components/navigation-bar.tsx +49 -0
  46. package/src/components/navigation-menu.tsx +117 -0
  47. package/src/components/number-input.tsx +86 -0
  48. package/src/components/pagination.tsx +77 -0
  49. package/src/components/password-input.tsx +36 -0
  50. package/src/components/popover.tsx +32 -0
  51. package/src/components/progress.tsx +24 -0
  52. package/src/components/quote.tsx +34 -0
  53. package/src/components/radio-group.tsx +44 -0
  54. package/src/components/rating.tsx +88 -0
  55. package/src/components/resizable.tsx +40 -0
  56. package/src/components/scroll-area.tsx +39 -0
  57. package/src/components/select.tsx +124 -0
  58. package/src/components/separator.tsx +25 -0
  59. package/src/components/sheet.tsx +104 -0
  60. package/src/components/sidebar.tsx +390 -0
  61. package/src/components/skeleton.tsx +9 -0
  62. package/src/components/slider.tsx +24 -0
  63. package/src/components/sonner.tsx +30 -0
  64. package/src/components/spinner.tsx +43 -0
  65. package/src/components/stepper.tsx +75 -0
  66. package/src/components/switch.tsx +37 -0
  67. package/src/components/tab-bar.tsx +58 -0
  68. package/src/components/table-of-contents.tsx +46 -0
  69. package/src/components/table.tsx +70 -0
  70. package/src/components/tabs.tsx +54 -0
  71. package/src/components/tag.tsx +56 -0
  72. package/src/components/textarea.tsx +56 -0
  73. package/src/components/toggle-group.tsx +41 -0
  74. package/src/components/toggle.tsx +34 -0
  75. package/src/components/tooltip.tsx +31 -0
  76. package/src/index.ts +305 -0
  77. package/src/lib/utils.ts +7 -0
  78. package/src/stories/Button.stories.tsx +155 -0
  79. package/src/stories/Input.stories.tsx +80 -0
  80. package/src/stories/Textarea.stories.tsx +69 -0
  81. package/tailwind.config.ts +107 -0
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+
6
+ /**
7
+ * CircularProgress — a ring progress indicator. Track = `--muted`, indicator
8
+ * = `--primary`. Optionally shows the value in the centre.
9
+ */
10
+ export interface CircularProgressProps extends React.HTMLAttributes<HTMLDivElement> {
11
+ value?: number;
12
+ /** diameter in px */
13
+ size?: number;
14
+ strokeWidth?: number;
15
+ showValue?: boolean;
16
+ /** override the visible label (defaults to `${value}%`) */
17
+ label?: React.ReactNode;
18
+ }
19
+
20
+ const CircularProgress = React.forwardRef<HTMLDivElement, CircularProgressProps>(
21
+ ({ value = 0, size = 48, strokeWidth = 4, showValue = false, label, className, ...props }, ref) => {
22
+ const v = Math.max(0, Math.min(100, value));
23
+ const r = (size - strokeWidth) / 2;
24
+ const c = 2 * Math.PI * r;
25
+
26
+ return (
27
+ <div
28
+ ref={ref}
29
+ role="progressbar"
30
+ aria-valuemin={0}
31
+ aria-valuemax={100}
32
+ aria-valuenow={v}
33
+ className={cn("relative inline-grid place-items-center font-sans", className)}
34
+ style={{ width: size, height: size }}
35
+ {...props}
36
+ >
37
+ <svg width={size} height={size} className="-rotate-90" aria-hidden>
38
+ <circle
39
+ cx={size / 2}
40
+ cy={size / 2}
41
+ r={r}
42
+ fill="none"
43
+ strokeWidth={strokeWidth}
44
+ className="stroke-muted"
45
+ />
46
+ <circle
47
+ cx={size / 2}
48
+ cy={size / 2}
49
+ r={r}
50
+ fill="none"
51
+ strokeWidth={strokeWidth}
52
+ strokeLinecap="round"
53
+ strokeDasharray={c}
54
+ strokeDashoffset={c - (v / 100) * c}
55
+ className="stroke-primary transition-[stroke-dashoffset] duration-300"
56
+ />
57
+ </svg>
58
+ {(showValue || label != null) && (
59
+ <span className="absolute text-label-md text-foreground">{label ?? `${Math.round(v)}%`}</span>
60
+ )}
61
+ </div>
62
+ );
63
+ },
64
+ );
65
+ CircularProgress.displayName = "CircularProgress";
66
+
67
+ export { CircularProgress };
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Check, Copy } from "lucide-react";
5
+ import { cn } from "../lib/utils";
6
+
7
+ /**
8
+ * CodeBlock — a code display with a copy button and, for more than one
9
+ * file, a tab strip. Presentational only: bring your own syntax
10
+ * highlighting by rendering highlighted markup as `code`/`files[].code`.
11
+ */
12
+ export interface CodeBlockFile {
13
+ name: string;
14
+ code: string;
15
+ language?: string;
16
+ }
17
+
18
+ export interface CodeBlockProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
19
+ code?: string;
20
+ language?: string;
21
+ filename?: string;
22
+ files?: CodeBlockFile[];
23
+ hideCopy?: boolean;
24
+ }
25
+
26
+ const CodeBlock = React.forwardRef<HTMLDivElement, CodeBlockProps>(
27
+ ({ className, code, language, filename, files, hideCopy, ...props }, ref) => {
28
+ const tabs = files && files.length > 0 ? files : [{ name: filename ?? language ?? "", code: code ?? "" }];
29
+ const [active, setActive] = React.useState(0);
30
+ const [copied, setCopied] = React.useState(false);
31
+ const current = tabs[active] ?? tabs[0]!;
32
+ const hasHeader = tabs.length > 1 || !!current.name;
33
+
34
+ const copy = async () => {
35
+ try {
36
+ await navigator.clipboard.writeText(current.code);
37
+ setCopied(true);
38
+ window.setTimeout(() => setCopied(false), 1500);
39
+ } catch {
40
+ // clipboard unavailable — no-op
41
+ }
42
+ };
43
+
44
+ return (
45
+ <div ref={ref} className={cn("overflow-hidden rounded-md border border-input bg-muted font-sans", className)} {...props}>
46
+ {hasHeader && (
47
+ <div className="flex items-center justify-between border-b border-input bg-background px-2">
48
+ <div className="flex">
49
+ {tabs.map((t, i) => (
50
+ <button
51
+ key={t.name || i}
52
+ type="button"
53
+ onClick={() => setActive(i)}
54
+ className={cn(
55
+ "border-b-2 px-3 py-2 text-body-sm transition-colors outline-none",
56
+ i === active ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground",
57
+ )}
58
+ >
59
+ {t.name || "code"}
60
+ </button>
61
+ ))}
62
+ </div>
63
+ {!hideCopy && (
64
+ <button
65
+ type="button"
66
+ onClick={copy}
67
+ aria-label="Copy code"
68
+ className="rounded-[2px] p-1.5 text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-current"
69
+ >
70
+ {copied ? <Check className="size-4" /> : <Copy className="size-4" />}
71
+ </button>
72
+ )}
73
+ </div>
74
+ )}
75
+ <div className="relative">
76
+ {!hasHeader && !hideCopy && (
77
+ <button
78
+ type="button"
79
+ onClick={copy}
80
+ aria-label="Copy code"
81
+ className="absolute right-2 top-2 rounded-[2px] p-1.5 text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-current"
82
+ >
83
+ {copied ? <Check className="size-4" /> : <Copy className="size-4" />}
84
+ </button>
85
+ )}
86
+ <pre className="overflow-x-auto p-4 text-body-sm text-foreground">
87
+ <code>{current.code}</code>
88
+ </pre>
89
+ </div>
90
+ </div>
91
+ );
92
+ },
93
+ );
94
+ CodeBlock.displayName = "CodeBlock";
95
+
96
+ export { CodeBlock };
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
4
+
5
+ const Collapsible = CollapsiblePrimitive.Root;
6
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
7
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
8
+
9
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,123 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Command as CommandPrimitive } from "cmdk";
5
+ import { Search } from "lucide-react";
6
+ import { cn } from "../lib/utils";
7
+ import { Dialog, DialogContent } from "./dialog";
8
+
9
+ const Command = React.forwardRef<
10
+ React.ElementRef<typeof CommandPrimitive>,
11
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
12
+ >(({ className, ...props }, ref) => (
13
+ <CommandPrimitive
14
+ ref={ref}
15
+ className={cn(
16
+ "flex size-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground font-sans",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ ));
22
+ Command.displayName = CommandPrimitive.displayName;
23
+
24
+ const CommandDialog = ({ children, ...props }: React.ComponentProps<typeof Dialog>) => (
25
+ <Dialog {...props}>
26
+ <DialogContent className="overflow-hidden p-0">
27
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:size-5">
28
+ {children}
29
+ </Command>
30
+ </DialogContent>
31
+ </Dialog>
32
+ );
33
+
34
+ const CommandInput = React.forwardRef<
35
+ React.ElementRef<typeof CommandPrimitive.Input>,
36
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
37
+ >(({ className, ...props }, ref) => (
38
+ <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
39
+ <Search className="mr-2 size-4 shrink-0 opacity-50" />
40
+ <CommandPrimitive.Input
41
+ ref={ref}
42
+ 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-50",
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ </div>
49
+ ));
50
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
51
+
52
+ const CommandList = React.forwardRef<
53
+ React.ElementRef<typeof CommandPrimitive.List>,
54
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
55
+ >(({ className, ...props }, ref) => (
56
+ <CommandPrimitive.List
57
+ ref={ref}
58
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
59
+ {...props}
60
+ />
61
+ ));
62
+ CommandList.displayName = CommandPrimitive.List.displayName;
63
+
64
+ const CommandEmpty = React.forwardRef<
65
+ React.ElementRef<typeof CommandPrimitive.Empty>,
66
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
67
+ >((props, ref) => <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />);
68
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
69
+
70
+ const CommandGroup = React.forwardRef<
71
+ React.ElementRef<typeof CommandPrimitive.Group>,
72
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
73
+ >(({ className, ...props }, ref) => (
74
+ <CommandPrimitive.Group
75
+ ref={ref}
76
+ className={cn(
77
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
78
+ className,
79
+ )}
80
+ {...props}
81
+ />
82
+ ));
83
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
84
+
85
+ const CommandSeparator = React.forwardRef<
86
+ React.ElementRef<typeof CommandPrimitive.Separator>,
87
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
88
+ >(({ className, ...props }, ref) => (
89
+ <CommandPrimitive.Separator ref={ref} className={cn("-mx-1 h-px bg-border", className)} {...props} />
90
+ ));
91
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
92
+
93
+ const CommandItem = React.forwardRef<
94
+ React.ElementRef<typeof CommandPrimitive.Item>,
95
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
96
+ >(({ className, ...props }, ref) => (
97
+ <CommandPrimitive.Item
98
+ ref={ref}
99
+ 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-50 [&_svg]:size-4 [&_svg]:shrink-0",
101
+ className,
102
+ )}
103
+ {...props}
104
+ />
105
+ ));
106
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
107
+
108
+ const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
109
+ <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />
110
+ );
111
+ CommandShortcut.displayName = "CommandShortcut";
112
+
113
+ export {
114
+ Command,
115
+ CommandDialog,
116
+ CommandInput,
117
+ CommandList,
118
+ CommandEmpty,
119
+ CommandGroup,
120
+ CommandItem,
121
+ CommandShortcut,
122
+ CommandSeparator,
123
+ };
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
5
+ import { Check, ChevronRight, Circle } from "lucide-react";
6
+ import { cn } from "../lib/utils";
7
+
8
+ const ContextMenu = ContextMenuPrimitive.Root;
9
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
10
+ const ContextMenuGroup = ContextMenuPrimitive.Group;
11
+ const ContextMenuPortal = ContextMenuPrimitive.Portal;
12
+ const ContextMenuSub = ContextMenuPrimitive.Sub;
13
+ const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
14
+
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 data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
17
+ const contentCls =
18
+ "z-50 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
+
20
+ const ContextMenuSubTrigger = React.forwardRef<
21
+ React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
22
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean }
23
+ >(({ className, inset, children, ...props }, ref) => (
24
+ <ContextMenuPrimitive.SubTrigger
25
+ ref={ref}
26
+ className={cn(itemCls, "data-[state=open]:bg-accent", inset && "pl-8", className)}
27
+ {...props}
28
+ >
29
+ {children}
30
+ <ChevronRight className="ml-auto size-4" />
31
+ </ContextMenuPrimitive.SubTrigger>
32
+ ));
33
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
34
+
35
+ const ContextMenuSubContent = React.forwardRef<
36
+ React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
37
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
38
+ >(({ className, ...props }, ref) => (
39
+ <ContextMenuPrimitive.SubContent ref={ref} className={cn(contentCls, className)} {...props} />
40
+ ));
41
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
42
+
43
+ const ContextMenuContent = React.forwardRef<
44
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
45
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
46
+ >(({ className, ...props }, ref) => (
47
+ <ContextMenuPrimitive.Portal>
48
+ <ContextMenuPrimitive.Content ref={ref} className={cn(contentCls, className)} {...props} />
49
+ </ContextMenuPrimitive.Portal>
50
+ ));
51
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
52
+
53
+ const ContextMenuItem = React.forwardRef<
54
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
55
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { inset?: boolean }
56
+ >(({ className, inset, ...props }, ref) => (
57
+ <ContextMenuPrimitive.Item ref={ref} className={cn(itemCls, inset && "pl-8", className)} {...props} />
58
+ ));
59
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
60
+
61
+ const ContextMenuCheckboxItem = React.forwardRef<
62
+ React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
63
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
64
+ >(({ className, children, checked, ...props }, ref) => (
65
+ <ContextMenuPrimitive.CheckboxItem ref={ref} className={cn(itemCls, "pl-8", className)} checked={checked} {...props}>
66
+ <span className="absolute left-2 flex size-3.5 items-center justify-center">
67
+ <ContextMenuPrimitive.ItemIndicator>
68
+ <Check className="size-4" />
69
+ </ContextMenuPrimitive.ItemIndicator>
70
+ </span>
71
+ {children}
72
+ </ContextMenuPrimitive.CheckboxItem>
73
+ ));
74
+ ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
75
+
76
+ const ContextMenuRadioItem = React.forwardRef<
77
+ React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
78
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
79
+ >(({ className, children, ...props }, ref) => (
80
+ <ContextMenuPrimitive.RadioItem ref={ref} className={cn(itemCls, "pl-8", className)} {...props}>
81
+ <span className="absolute left-2 flex size-3.5 items-center justify-center">
82
+ <ContextMenuPrimitive.ItemIndicator>
83
+ <Circle className="size-2 fill-current" />
84
+ </ContextMenuPrimitive.ItemIndicator>
85
+ </span>
86
+ {children}
87
+ </ContextMenuPrimitive.RadioItem>
88
+ ));
89
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
90
+
91
+ const ContextMenuLabel = React.forwardRef<
92
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
93
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { inset?: boolean }
94
+ >(({ className, inset, ...props }, ref) => (
95
+ <ContextMenuPrimitive.Label
96
+ ref={ref}
97
+ className={cn("px-2 py-1.5 text-sm font-semibold text-foreground", inset && "pl-8", className)}
98
+ {...props}
99
+ />
100
+ ));
101
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
102
+
103
+ const ContextMenuSeparator = React.forwardRef<
104
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
105
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
106
+ >(({ className, ...props }, ref) => (
107
+ <ContextMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} />
108
+ ));
109
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
110
+
111
+ const ContextMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
112
+ <span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />
113
+ );
114
+ ContextMenuShortcut.displayName = "ContextMenuShortcut";
115
+
116
+ export {
117
+ ContextMenu,
118
+ ContextMenuTrigger,
119
+ ContextMenuContent,
120
+ ContextMenuItem,
121
+ ContextMenuCheckboxItem,
122
+ ContextMenuRadioItem,
123
+ ContextMenuLabel,
124
+ ContextMenuSeparator,
125
+ ContextMenuShortcut,
126
+ ContextMenuGroup,
127
+ ContextMenuPortal,
128
+ ContextMenuSub,
129
+ ContextMenuSubContent,
130
+ ContextMenuSubTrigger,
131
+ ContextMenuRadioGroup,
132
+ };
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import {
5
+ flexRender,
6
+ getCoreRowModel,
7
+ getPaginationRowModel,
8
+ getSortedRowModel,
9
+ useReactTable,
10
+ type ColumnDef,
11
+ type SortingState,
12
+ } from "@tanstack/react-table";
13
+ import { cn } from "../lib/utils";
14
+ import { Button } from "./button";
15
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
16
+
17
+ /**
18
+ * DataTable — a thin wrapper over @tanstack/react-table + the KinetixUI Table.
19
+ * Pass `columns` and `data`; sorting + pagination are on by default. For full
20
+ * control, drop down to `useReactTable` directly.
21
+ */
22
+ export function DataTable<TData, TValue>({
23
+ columns,
24
+ data,
25
+ className,
26
+ pageSize = 10,
27
+ }: {
28
+ columns: ColumnDef<TData, TValue>[];
29
+ data: TData[];
30
+ className?: string;
31
+ pageSize?: number;
32
+ }) {
33
+ const [sorting, setSorting] = React.useState<SortingState>([]);
34
+
35
+ const table = useReactTable({
36
+ data,
37
+ columns,
38
+ state: { sorting },
39
+ onSortingChange: setSorting,
40
+ getCoreRowModel: getCoreRowModel(),
41
+ getSortedRowModel: getSortedRowModel(),
42
+ getPaginationRowModel: getPaginationRowModel(),
43
+ initialState: { pagination: { pageSize } },
44
+ });
45
+
46
+ return (
47
+ <div className={cn("space-y-3", className)}>
48
+ <div className="rounded-md border">
49
+ <Table>
50
+ <TableHeader>
51
+ {table.getHeaderGroups().map((hg) => (
52
+ <TableRow key={hg.id}>
53
+ {hg.headers.map((header) => (
54
+ <TableHead key={header.id}>
55
+ {header.isPlaceholder
56
+ ? null
57
+ : flexRender(header.column.columnDef.header, header.getContext())}
58
+ </TableHead>
59
+ ))}
60
+ </TableRow>
61
+ ))}
62
+ </TableHeader>
63
+ <TableBody>
64
+ {table.getRowModel().rows.length ? (
65
+ table.getRowModel().rows.map((row) => (
66
+ <TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
67
+ {row.getVisibleCells().map((cell) => (
68
+ <TableCell key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>
69
+ ))}
70
+ </TableRow>
71
+ ))
72
+ ) : (
73
+ <TableRow>
74
+ <TableCell colSpan={columns.length} className="h-24 text-center">
75
+ No results.
76
+ </TableCell>
77
+ </TableRow>
78
+ )}
79
+ </TableBody>
80
+ </Table>
81
+ </div>
82
+ <div className="flex items-center justify-end gap-2">
83
+ <Button variant="Outline" size="sm" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
84
+ Previous
85
+ </Button>
86
+ <Button variant="Outline" size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
87
+ Next
88
+ </Button>
89
+ </div>
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { CalendarIcon } from "lucide-react";
5
+ import { format } from "date-fns";
6
+ import { cn } from "../lib/utils";
7
+ import { Button } from "./button";
8
+ import { Calendar, type CalendarProps } from "./calendar";
9
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover";
10
+
11
+ /**
12
+ * DatePicker — a text-field trigger + calendar popover. Composes the
13
+ * existing `Popover` + `Calendar`; single-date only — for a range, compose
14
+ * `Calendar mode="range"` directly.
15
+ */
16
+ export interface DatePickerProps {
17
+ value?: Date;
18
+ onChange?: (date: Date | undefined) => void;
19
+ placeholder?: string;
20
+ label?: React.ReactNode;
21
+ helperText?: React.ReactNode;
22
+ error?: boolean;
23
+ disabled?: boolean;
24
+ className?: string;
25
+ formatStr?: string;
26
+ calendarProps?: Omit<CalendarProps, "mode" | "selected" | "onSelect">;
27
+ }
28
+
29
+ const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(
30
+ ({ value, onChange, placeholder = "Select date", label, helperText, error, disabled, className, formatStr = "PP", calendarProps }, ref) => {
31
+ const [open, setOpen] = React.useState(false);
32
+
33
+ return (
34
+ <div className={cn("flex flex-col gap-1.5 font-sans", className)}>
35
+ {label && <label className="text-label-md font-medium text-foreground">{label}</label>}
36
+ <Popover open={open} onOpenChange={setOpen}>
37
+ <PopoverTrigger asChild>
38
+ <Button
39
+ ref={ref}
40
+ type="button"
41
+ variant="Outline"
42
+ disabled={disabled}
43
+ className={cn("w-full justify-start px-3 font-normal", !value && "text-muted-foreground", error && "border-destructive text-destructive")}
44
+ >
45
+ <CalendarIcon className="size-4" />
46
+ {value ? format(value, formatStr) : placeholder}
47
+ </Button>
48
+ </PopoverTrigger>
49
+ <PopoverContent className="w-auto p-0" align="start">
50
+ <Calendar
51
+ mode="single"
52
+ selected={value}
53
+ onSelect={(d) => {
54
+ onChange?.(d);
55
+ setOpen(false);
56
+ }}
57
+ {...calendarProps}
58
+ />
59
+ </PopoverContent>
60
+ </Popover>
61
+ {helperText && <p className={cn("text-body-sm", error ? "text-destructive" : "text-muted-foreground")}>{helperText}</p>}
62
+ </div>
63
+ );
64
+ },
65
+ );
66
+ DatePicker.displayName = "DatePicker";
67
+
68
+ export { DatePicker };