@playfast/reform-better-hub-ui-primitives 0.0.2 → 0.0.3
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/package.json +2 -2
- package/src/components/agent-icon.tsx +21 -0
- package/src/components/badge.tsx +54 -0
- package/src/components/button.tsx +72 -0
- package/src/components/combobox.tsx +290 -0
- package/src/components/command.tsx +170 -0
- package/src/components/dialog.tsx +129 -0
- package/src/components/dropdown-menu.tsx +149 -0
- package/src/components/github-background.tsx +280 -0
- package/src/components/halftone-background.tsx +289 -0
- package/src/components/input-group.tsx +175 -0
- package/src/components/input.tsx +25 -0
- package/src/components/live-duration.tsx +70 -0
- package/src/components/logo.tsx +20 -0
- package/src/components/popover.tsx +82 -0
- package/src/components/resize-handle.tsx +86 -0
- package/src/components/sheet.tsx +147 -0
- package/src/components/textarea.tsx +21 -0
- package/src/components/time-ago.tsx +57 -0
- package/src/components/tooltip.tsx +64 -0
- package/src/foundation/cn.ts +8 -0
- package/src/foundation/styles.css +156 -0
- package/{dist/index.js → src/index.tsx} +2 -1
- package/dist/components/agent-icon.d.ts +0 -5
- package/dist/components/agent-icon.d.ts.map +0 -1
- package/dist/components/agent-icon.js +0 -5
- package/dist/components/agent-icon.js.map +0 -1
- package/dist/components/badge.d.ts +0 -11
- package/dist/components/badge.d.ts.map +0 -1
- package/dist/components/badge.js +0 -28
- package/dist/components/badge.js.map +0 -1
- package/dist/components/button.d.ts +0 -12
- package/dist/components/button.d.ts.map +0 -1
- package/dist/components/button.js +0 -34
- package/dist/components/button.js.map +0 -1
- package/dist/components/combobox.d.ts +0 -25
- package/dist/components/combobox.d.ts.map +0 -1
- package/dist/components/combobox.js +0 -60
- package/dist/components/combobox.js.map +0 -1
- package/dist/components/command.d.ts +0 -19
- package/dist/components/command.d.ts.map +0 -1
- package/dist/components/command.js +0 -36
- package/dist/components/command.js.map +0 -1
- package/dist/components/dialog.d.ts +0 -16
- package/dist/components/dialog.d.ts.map +0 -1
- package/dist/components/dialog.js +0 -38
- package/dist/components/dialog.js.map +0 -1
- package/dist/components/dropdown-menu.d.ts +0 -17
- package/dist/components/dropdown-menu.d.ts.map +0 -1
- package/dist/components/dropdown-menu.js +0 -35
- package/dist/components/dropdown-menu.js.map +0 -1
- package/dist/components/github-background.d.ts +0 -3
- package/dist/components/github-background.d.ts.map +0 -1
- package/dist/components/github-background.js +0 -51
- package/dist/components/github-background.js.map +0 -1
- package/dist/components/halftone-background.d.ts +0 -3
- package/dist/components/halftone-background.d.ts.map +0 -1
- package/dist/components/halftone-background.js +0 -248
- package/dist/components/halftone-background.js.map +0 -1
- package/dist/components/input-group.d.ts +0 -19
- package/dist/components/input-group.d.ts.map +0 -1
- package/dist/components/input-group.js +0 -63
- package/dist/components/input-group.js.map +0 -1
- package/dist/components/input.d.ts +0 -5
- package/dist/components/input.d.ts.map +0 -1
- package/dist/components/input.js +0 -7
- package/dist/components/input.js.map +0 -1
- package/dist/components/live-duration.d.ts +0 -9
- package/dist/components/live-duration.d.ts.map +0 -1
- package/dist/components/live-duration.js +0 -55
- package/dist/components/live-duration.js.map +0 -1
- package/dist/components/logo.d.ts +0 -8
- package/dist/components/logo.d.ts.map +0 -1
- package/dist/components/logo.js +0 -9
- package/dist/components/logo.js.map +0 -1
- package/dist/components/popover.d.ts +0 -12
- package/dist/components/popover.d.ts.map +0 -1
- package/dist/components/popover.js +0 -26
- package/dist/components/popover.js.map +0 -1
- package/dist/components/resize-handle.d.ts +0 -15
- package/dist/components/resize-handle.d.ts.map +0 -1
- package/dist/components/resize-handle.js +0 -36
- package/dist/components/resize-handle.js.map +0 -1
- package/dist/components/sheet.d.ts +0 -18
- package/dist/components/sheet.d.ts.map +0 -1
- package/dist/components/sheet.js +0 -44
- package/dist/components/sheet.js.map +0 -1
- package/dist/components/textarea.d.ts +0 -5
- package/dist/components/textarea.d.ts.map +0 -1
- package/dist/components/textarea.js +0 -7
- package/dist/components/textarea.js.map +0 -1
- package/dist/components/time-ago.d.ts +0 -8
- package/dist/components/time-ago.d.ts.map +0 -1
- package/dist/components/time-ago.js +0 -40
- package/dist/components/time-ago.js.map +0 -1
- package/dist/components/tooltip.d.ts +0 -10
- package/dist/components/tooltip.d.ts.map +0 -1
- package/dist/components/tooltip.js +0 -25
- package/dist/components/tooltip.js.map +0 -1
- package/dist/foundation/cn.d.ts +0 -7
- package/dist/foundation/cn.d.ts.map +0 -1
- package/dist/foundation/cn.js +0 -8
- package/dist/foundation/cn.js.map +0 -1
- package/dist/index.d.ts +0 -21
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playfast/reform-better-hub-ui-primitives",
|
|
3
3
|
"playbook": "./playbook",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "React UI primitives adapted from better-hub by Better Auth (https://github.com/better-auth/better-hub). Full credit to the Better Auth team.",
|
|
7
7
|
"keywords": [
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"./*": "./src/*.tsx"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
|
-
"
|
|
37
|
+
"src",
|
|
38
38
|
"README.md"
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/agent-icon.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
|
|
6
|
+
export function AgentIcon({ className }: { className?: string }): React.JSX.Element {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
width="1em"
|
|
11
|
+
height="1em"
|
|
12
|
+
viewBox="0 0 28 28"
|
|
13
|
+
className={className}
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
fill="currentColor"
|
|
17
|
+
d="M10.06 18.701a1.628 1.628 0 0 0 2.43-.676l.77-2.34a3.82 3.82 0 0 1 2.416-2.418l2.238-.727a1.6 1.6 0 0 0 .786-.595a1.62 1.62 0 0 0-.849-2.489l-2.215-.72a3.82 3.82 0 0 1-2.42-2.414l-.727-2.237a1.62 1.62 0 0 0-.594-.785a1.655 1.655 0 0 0-1.879 0a1.63 1.63 0 0 0-.6.8L8.68 6.365a3.82 3.82 0 0 1-2.359 2.37l-2.24.726a1.626 1.626 0 0 0 .02 3.073l2.216.72a3.86 3.86 0 0 1 1.816 1.286c.266.343.471.728.606 1.14l.728 2.234c.112.318.32.593.595.787m-3.273-6.873l-2.22-.72s-.085-.038-.085-.108a.14.14 0 0 1 .084-.115L6.8 10.16a5.34 5.34 0 0 0 3.32-3.375l.721-2.219s.043-.084.114-.084s.113.084.113.084l.723 2.223a5.32 5.32 0 0 0 3.377 3.372l2.27.735a.13.13 0 0 1 .078.11a.15.15 0 0 1-.083.113l-2.225.722a5.32 5.32 0 0 0-3.374 3.374l-.715 2.2a.11.11 0 0 1-.115.093c-.104 0-.118-.075-.118-.075l-.723-2.22a5.32 5.32 0 0 0-3.376-3.385m13.017 12.954A1.2 1.2 0 0 0 20.5 25a1.2 1.2 0 0 0 .692-.216a1.2 1.2 0 0 0 .446-.6l.372-1.143a1.61 1.61 0 0 1 1.017-1.02l1.166-.378A1.21 1.21 0 0 0 25 20.505a1.2 1.2 0 0 0-.844-1.146l-1.144-.37a1.61 1.61 0 0 1-1.02-1.018l-.38-1.163a1.2 1.2 0 0 0-2.274.016l-.374 1.146a1.61 1.61 0 0 1-.993 1.017l-1.166.378a1.21 1.21 0 0 0-.807 1.139a1.2 1.2 0 0 0 .823 1.138l1.144.372a1.6 1.6 0 0 1 1.02 1.023l.379 1.163a1.2 1.2 0 0 0 .44.582m-1.371-4.194l-.27-.088l.283-.096a3.12 3.12 0 0 0 1.943-1.974l.088-.27l.089.272a3.11 3.11 0 0 0 1.974 1.975l.293.1l-.271.087a3.12 3.12 0 0 0-1.974 1.977l-.088.27l-.088-.272a3.12 3.12 0 0 0-1.98-1.98"
|
|
18
|
+
/>
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/badge.tsx). Full credit to the Better Auth team.
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import type { ClassProp } from "class-variance-authority/types";
|
|
6
|
+
import { Slot } from "radix-ui";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../foundation/cn";
|
|
9
|
+
|
|
10
|
+
const badgeVariants: (
|
|
11
|
+
props?:
|
|
12
|
+
| ({
|
|
13
|
+
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
|
|
14
|
+
} & ClassProp)
|
|
15
|
+
| undefined,
|
|
16
|
+
) => string = cva(
|
|
17
|
+
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
22
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
23
|
+
destructive:
|
|
24
|
+
"bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
|
|
25
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
26
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
27
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
function Badge({
|
|
37
|
+
className,
|
|
38
|
+
variant = "default",
|
|
39
|
+
asChild = false,
|
|
40
|
+
...props
|
|
41
|
+
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }): React.JSX.Element {
|
|
42
|
+
const Comp = asChild ? Slot.Root : "span";
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Comp
|
|
46
|
+
data-slot="badge"
|
|
47
|
+
data-variant={variant}
|
|
48
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/button.tsx). Full credit to the Better Auth team.
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import type { VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import type { ClassProp } from "class-variance-authority/types";
|
|
7
|
+
import type * as React from "react";
|
|
8
|
+
import { cn } from "../foundation/cn";
|
|
9
|
+
|
|
10
|
+
const buttonVariants: (
|
|
11
|
+
props?:
|
|
12
|
+
| ({
|
|
13
|
+
variant?:
|
|
14
|
+
| "default"
|
|
15
|
+
| "destructive"
|
|
16
|
+
| "outline"
|
|
17
|
+
| "secondary"
|
|
18
|
+
| "ghost"
|
|
19
|
+
| "link"
|
|
20
|
+
| null
|
|
21
|
+
| undefined;
|
|
22
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
23
|
+
} & ClassProp)
|
|
24
|
+
| undefined,
|
|
25
|
+
) => string = cva(
|
|
26
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
variant: {
|
|
30
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
31
|
+
destructive:
|
|
32
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90",
|
|
33
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
34
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
35
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
36
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
37
|
+
},
|
|
38
|
+
size: {
|
|
39
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
40
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
41
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
42
|
+
icon: "size-9",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
variant: "default",
|
|
47
|
+
size: "default",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
function Button({
|
|
53
|
+
className,
|
|
54
|
+
variant,
|
|
55
|
+
size,
|
|
56
|
+
asChild = false,
|
|
57
|
+
...props
|
|
58
|
+
}: React.ComponentProps<"button"> &
|
|
59
|
+
VariantProps<typeof buttonVariants> & {
|
|
60
|
+
asChild?: boolean;
|
|
61
|
+
}): React.JSX.Element {
|
|
62
|
+
const Comp = asChild ? Slot : "button";
|
|
63
|
+
return (
|
|
64
|
+
<Comp
|
|
65
|
+
data-slot="button"
|
|
66
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/combobox.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { Combobox as ComboboxPrimitive } from '@base-ui/react'
|
|
6
|
+
import { CheckIcon, ChevronDownIcon, XIcon } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
import { cn } from '../foundation/cn'
|
|
9
|
+
import { Button } from './button'
|
|
10
|
+
import {
|
|
11
|
+
InputGroup,
|
|
12
|
+
InputGroupAddon,
|
|
13
|
+
InputGroupButton,
|
|
14
|
+
InputGroupInput,
|
|
15
|
+
} from './input-group'
|
|
16
|
+
|
|
17
|
+
const Combobox: typeof ComboboxPrimitive.Root = ComboboxPrimitive.Root
|
|
18
|
+
|
|
19
|
+
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props): React.JSX.Element {
|
|
20
|
+
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Trigger.Props): React.JSX.Element {
|
|
24
|
+
return (
|
|
25
|
+
<ComboboxPrimitive.Trigger
|
|
26
|
+
data-slot="combobox-trigger"
|
|
27
|
+
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
<ChevronDownIcon
|
|
32
|
+
data-slot="combobox-trigger-icon"
|
|
33
|
+
className="pointer-events-none size-4 text-muted-foreground"
|
|
34
|
+
/>
|
|
35
|
+
</ComboboxPrimitive.Trigger>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props): React.JSX.Element {
|
|
40
|
+
return (
|
|
41
|
+
<ComboboxPrimitive.Clear
|
|
42
|
+
data-slot="combobox-clear"
|
|
43
|
+
render={<InputGroupButton variant="ghost" size="icon-xs" />}
|
|
44
|
+
className={cn(className)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<XIcon className="pointer-events-none" />
|
|
48
|
+
</ComboboxPrimitive.Clear>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ComboboxInput({
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
disabled = false,
|
|
56
|
+
showTrigger = true,
|
|
57
|
+
showClear = false,
|
|
58
|
+
...props
|
|
59
|
+
}: ComboboxPrimitive.Input.Props & {
|
|
60
|
+
showTrigger?: boolean
|
|
61
|
+
showClear?: boolean
|
|
62
|
+
}): React.JSX.Element {
|
|
63
|
+
return (
|
|
64
|
+
<InputGroup className={cn('w-auto', className)}>
|
|
65
|
+
<ComboboxPrimitive.Input
|
|
66
|
+
render={<InputGroupInput disabled={disabled} />}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
<InputGroupAddon align="inline-end">
|
|
70
|
+
{showTrigger && (
|
|
71
|
+
<InputGroupButton
|
|
72
|
+
size="icon-xs"
|
|
73
|
+
variant="ghost"
|
|
74
|
+
asChild
|
|
75
|
+
data-slot="input-group-button"
|
|
76
|
+
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
|
|
77
|
+
disabled={disabled}
|
|
78
|
+
>
|
|
79
|
+
<ComboboxTrigger />
|
|
80
|
+
</InputGroupButton>
|
|
81
|
+
)}
|
|
82
|
+
{showClear && <ComboboxClear disabled={disabled} />}
|
|
83
|
+
</InputGroupAddon>
|
|
84
|
+
{children}
|
|
85
|
+
</InputGroup>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ComboboxContent({
|
|
90
|
+
className,
|
|
91
|
+
side = 'bottom',
|
|
92
|
+
sideOffset = 6,
|
|
93
|
+
align = 'start',
|
|
94
|
+
alignOffset = 0,
|
|
95
|
+
anchor,
|
|
96
|
+
...props
|
|
97
|
+
}: ComboboxPrimitive.Popup.Props &
|
|
98
|
+
Pick<
|
|
99
|
+
ComboboxPrimitive.Positioner.Props,
|
|
100
|
+
'side' | 'align' | 'sideOffset' | 'alignOffset' | 'anchor'
|
|
101
|
+
>): React.JSX.Element {
|
|
102
|
+
return (
|
|
103
|
+
<ComboboxPrimitive.Portal>
|
|
104
|
+
<ComboboxPrimitive.Positioner
|
|
105
|
+
side={side}
|
|
106
|
+
sideOffset={sideOffset}
|
|
107
|
+
align={align}
|
|
108
|
+
alignOffset={alignOffset}
|
|
109
|
+
anchor={anchor}
|
|
110
|
+
className="isolate z-50"
|
|
111
|
+
>
|
|
112
|
+
<ComboboxPrimitive.Popup
|
|
113
|
+
data-slot="combobox-content"
|
|
114
|
+
data-chips={!!anchor}
|
|
115
|
+
className={cn(
|
|
116
|
+
"group/combobox-content relative max-h-96 w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-card text-card-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) 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 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
117
|
+
className,
|
|
118
|
+
)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
</ComboboxPrimitive.Positioner>
|
|
122
|
+
</ComboboxPrimitive.Portal>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props): React.JSX.Element {
|
|
127
|
+
return (
|
|
128
|
+
<ComboboxPrimitive.List
|
|
129
|
+
data-slot="combobox-list"
|
|
130
|
+
className={cn(
|
|
131
|
+
'max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0',
|
|
132
|
+
className,
|
|
133
|
+
)}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props): React.JSX.Element {
|
|
140
|
+
return (
|
|
141
|
+
<ComboboxPrimitive.Item
|
|
142
|
+
data-slot="combobox-item"
|
|
143
|
+
className={cn(
|
|
144
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
145
|
+
className,
|
|
146
|
+
)}
|
|
147
|
+
{...props}
|
|
148
|
+
>
|
|
149
|
+
{children}
|
|
150
|
+
<ComboboxPrimitive.ItemIndicator
|
|
151
|
+
data-slot="combobox-item-indicator"
|
|
152
|
+
render={
|
|
153
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
<CheckIcon className="pointer-events-none size-4 pointer-coarse:size-5" />
|
|
157
|
+
</ComboboxPrimitive.ItemIndicator>
|
|
158
|
+
</ComboboxPrimitive.Item>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props): React.JSX.Element {
|
|
163
|
+
return (
|
|
164
|
+
<ComboboxPrimitive.Group
|
|
165
|
+
data-slot="combobox-group"
|
|
166
|
+
className={cn(className)}
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props): React.JSX.Element {
|
|
173
|
+
return (
|
|
174
|
+
<ComboboxPrimitive.GroupLabel
|
|
175
|
+
data-slot="combobox-label"
|
|
176
|
+
className={cn(
|
|
177
|
+
'px-2 py-1.5 text-xs text-muted-foreground pointer-coarse:px-3 pointer-coarse:py-2 pointer-coarse:text-sm',
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...props}
|
|
181
|
+
/>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props): React.JSX.Element {
|
|
186
|
+
return <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props): React.JSX.Element {
|
|
190
|
+
return (
|
|
191
|
+
<ComboboxPrimitive.Empty
|
|
192
|
+
data-slot="combobox-empty"
|
|
193
|
+
className={cn(
|
|
194
|
+
'hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex',
|
|
195
|
+
className,
|
|
196
|
+
)}
|
|
197
|
+
{...props}
|
|
198
|
+
/>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props): React.JSX.Element {
|
|
203
|
+
return (
|
|
204
|
+
<ComboboxPrimitive.Separator
|
|
205
|
+
data-slot="combobox-separator"
|
|
206
|
+
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
|
207
|
+
{...props}
|
|
208
|
+
/>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function ComboboxChips({
|
|
213
|
+
className,
|
|
214
|
+
...props
|
|
215
|
+
}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> & ComboboxPrimitive.Chips.Props): React.JSX.Element {
|
|
216
|
+
return (
|
|
217
|
+
<ComboboxPrimitive.Chips
|
|
218
|
+
data-slot="combobox-chips"
|
|
219
|
+
className={cn(
|
|
220
|
+
'flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-[3px] has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40',
|
|
221
|
+
className,
|
|
222
|
+
)}
|
|
223
|
+
{...props}
|
|
224
|
+
/>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function ComboboxChip({
|
|
229
|
+
className,
|
|
230
|
+
children,
|
|
231
|
+
showRemove = true,
|
|
232
|
+
...props
|
|
233
|
+
}: ComboboxPrimitive.Chip.Props & {
|
|
234
|
+
showRemove?: boolean
|
|
235
|
+
}): React.JSX.Element {
|
|
236
|
+
return (
|
|
237
|
+
<ComboboxPrimitive.Chip
|
|
238
|
+
data-slot="combobox-chip"
|
|
239
|
+
className={cn(
|
|
240
|
+
'flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0',
|
|
241
|
+
className,
|
|
242
|
+
)}
|
|
243
|
+
{...props}
|
|
244
|
+
>
|
|
245
|
+
{children}
|
|
246
|
+
{showRemove && (
|
|
247
|
+
<ComboboxPrimitive.ChipRemove
|
|
248
|
+
render={<Button variant="ghost" size="icon" />}
|
|
249
|
+
className="-ml-1 opacity-50 hover:opacity-100"
|
|
250
|
+
data-slot="combobox-chip-remove"
|
|
251
|
+
>
|
|
252
|
+
<XIcon className="pointer-events-none" />
|
|
253
|
+
</ComboboxPrimitive.ChipRemove>
|
|
254
|
+
)}
|
|
255
|
+
</ComboboxPrimitive.Chip>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function ComboboxChipsInput({ className, children, ...props }: ComboboxPrimitive.Input.Props): React.JSX.Element {
|
|
260
|
+
return (
|
|
261
|
+
<ComboboxPrimitive.Input
|
|
262
|
+
data-slot="combobox-chip-input"
|
|
263
|
+
className={cn('min-w-16 flex-1 outline-none', className)}
|
|
264
|
+
{...props}
|
|
265
|
+
/>
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function useComboboxAnchor(): React.RefObject<HTMLDivElement | null> {
|
|
270
|
+
return React.useRef<HTMLDivElement | null>(null)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export {
|
|
274
|
+
Combobox,
|
|
275
|
+
ComboboxInput,
|
|
276
|
+
ComboboxContent,
|
|
277
|
+
ComboboxList,
|
|
278
|
+
ComboboxItem,
|
|
279
|
+
ComboboxGroup,
|
|
280
|
+
ComboboxLabel,
|
|
281
|
+
ComboboxCollection,
|
|
282
|
+
ComboboxEmpty,
|
|
283
|
+
ComboboxSeparator,
|
|
284
|
+
ComboboxChips,
|
|
285
|
+
ComboboxChip,
|
|
286
|
+
ComboboxChipsInput,
|
|
287
|
+
ComboboxTrigger,
|
|
288
|
+
ComboboxValue,
|
|
289
|
+
useComboboxAnchor,
|
|
290
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/command.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
5
|
+
import { SearchIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
import {
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogContent,
|
|
10
|
+
DialogDescription,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from "./dialog";
|
|
14
|
+
import { cn } from "../foundation/cn";
|
|
15
|
+
|
|
16
|
+
function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>): React.JSX.Element {
|
|
17
|
+
return (
|
|
18
|
+
<CommandPrimitive
|
|
19
|
+
data-slot="command"
|
|
20
|
+
className={cn(
|
|
21
|
+
"bg-background text-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function CommandDialog({
|
|
30
|
+
title = "Command Palette",
|
|
31
|
+
description = "Search for a command to run...",
|
|
32
|
+
children,
|
|
33
|
+
className,
|
|
34
|
+
showCloseButton = true,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof Dialog> & {
|
|
37
|
+
title?: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
showCloseButton?: boolean;
|
|
41
|
+
}): React.JSX.Element {
|
|
42
|
+
return (
|
|
43
|
+
<Dialog {...props}>
|
|
44
|
+
<DialogContent
|
|
45
|
+
className={cn("overflow-hidden p-0", className)}
|
|
46
|
+
showCloseButton={showCloseButton}
|
|
47
|
+
>
|
|
48
|
+
<DialogHeader className="sr-only">
|
|
49
|
+
<DialogTitle>{title}</DialogTitle>
|
|
50
|
+
<DialogDescription>{description}</DialogDescription>
|
|
51
|
+
</DialogHeader>
|
|
52
|
+
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
|
53
|
+
{children}
|
|
54
|
+
</Command>
|
|
55
|
+
</DialogContent>
|
|
56
|
+
</Dialog>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function CommandInput({
|
|
61
|
+
className,
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<typeof CommandPrimitive.Input>): React.JSX.Element {
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
data-slot="command-input-wrapper"
|
|
67
|
+
className="flex h-9 items-center gap-2 border-b px-3"
|
|
68
|
+
>
|
|
69
|
+
<SearchIcon className="size-4 shrink-0 opacity-50" />
|
|
70
|
+
<CommandPrimitive.Input
|
|
71
|
+
data-slot="command-input"
|
|
72
|
+
className={cn(
|
|
73
|
+
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>): React.JSX.Element {
|
|
83
|
+
return (
|
|
84
|
+
<CommandPrimitive.List
|
|
85
|
+
data-slot="command-list"
|
|
86
|
+
className={cn(
|
|
87
|
+
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
|
|
88
|
+
className,
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>): React.JSX.Element {
|
|
96
|
+
return (
|
|
97
|
+
<CommandPrimitive.Empty
|
|
98
|
+
data-slot="command-empty"
|
|
99
|
+
className="py-6 text-center text-sm"
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function CommandGroup({
|
|
106
|
+
className,
|
|
107
|
+
...props
|
|
108
|
+
}: React.ComponentProps<typeof CommandPrimitive.Group>): React.JSX.Element {
|
|
109
|
+
return (
|
|
110
|
+
<CommandPrimitive.Group
|
|
111
|
+
data-slot="command-group"
|
|
112
|
+
className={cn(
|
|
113
|
+
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
|
114
|
+
className,
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function CommandSeparator({
|
|
122
|
+
className,
|
|
123
|
+
...props
|
|
124
|
+
}: React.ComponentProps<typeof CommandPrimitive.Separator>): React.JSX.Element {
|
|
125
|
+
return (
|
|
126
|
+
<CommandPrimitive.Separator
|
|
127
|
+
data-slot="command-separator"
|
|
128
|
+
className={cn("bg-border -mx-1 h-px", className)}
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>): React.JSX.Element {
|
|
135
|
+
return (
|
|
136
|
+
<CommandPrimitive.Item
|
|
137
|
+
data-slot="command-item"
|
|
138
|
+
className={cn(
|
|
139
|
+
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
140
|
+
className,
|
|
141
|
+
)}
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function CommandShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element {
|
|
148
|
+
return (
|
|
149
|
+
<span
|
|
150
|
+
data-slot="command-shortcut"
|
|
151
|
+
className={cn(
|
|
152
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
153
|
+
className,
|
|
154
|
+
)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
Command,
|
|
162
|
+
CommandDialog,
|
|
163
|
+
CommandInput,
|
|
164
|
+
CommandList,
|
|
165
|
+
CommandEmpty,
|
|
166
|
+
CommandGroup,
|
|
167
|
+
CommandItem,
|
|
168
|
+
CommandShortcut,
|
|
169
|
+
CommandSeparator,
|
|
170
|
+
};
|