@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.
Files changed (106) hide show
  1. package/package.json +2 -2
  2. package/src/components/agent-icon.tsx +21 -0
  3. package/src/components/badge.tsx +54 -0
  4. package/src/components/button.tsx +72 -0
  5. package/src/components/combobox.tsx +290 -0
  6. package/src/components/command.tsx +170 -0
  7. package/src/components/dialog.tsx +129 -0
  8. package/src/components/dropdown-menu.tsx +149 -0
  9. package/src/components/github-background.tsx +280 -0
  10. package/src/components/halftone-background.tsx +289 -0
  11. package/src/components/input-group.tsx +175 -0
  12. package/src/components/input.tsx +25 -0
  13. package/src/components/live-duration.tsx +70 -0
  14. package/src/components/logo.tsx +20 -0
  15. package/src/components/popover.tsx +82 -0
  16. package/src/components/resize-handle.tsx +86 -0
  17. package/src/components/sheet.tsx +147 -0
  18. package/src/components/textarea.tsx +21 -0
  19. package/src/components/time-ago.tsx +57 -0
  20. package/src/components/tooltip.tsx +64 -0
  21. package/src/foundation/cn.ts +8 -0
  22. package/src/foundation/styles.css +156 -0
  23. package/{dist/index.js → src/index.tsx} +2 -1
  24. package/dist/components/agent-icon.d.ts +0 -5
  25. package/dist/components/agent-icon.d.ts.map +0 -1
  26. package/dist/components/agent-icon.js +0 -5
  27. package/dist/components/agent-icon.js.map +0 -1
  28. package/dist/components/badge.d.ts +0 -11
  29. package/dist/components/badge.d.ts.map +0 -1
  30. package/dist/components/badge.js +0 -28
  31. package/dist/components/badge.js.map +0 -1
  32. package/dist/components/button.d.ts +0 -12
  33. package/dist/components/button.d.ts.map +0 -1
  34. package/dist/components/button.js +0 -34
  35. package/dist/components/button.js.map +0 -1
  36. package/dist/components/combobox.d.ts +0 -25
  37. package/dist/components/combobox.d.ts.map +0 -1
  38. package/dist/components/combobox.js +0 -60
  39. package/dist/components/combobox.js.map +0 -1
  40. package/dist/components/command.d.ts +0 -19
  41. package/dist/components/command.d.ts.map +0 -1
  42. package/dist/components/command.js +0 -36
  43. package/dist/components/command.js.map +0 -1
  44. package/dist/components/dialog.d.ts +0 -16
  45. package/dist/components/dialog.d.ts.map +0 -1
  46. package/dist/components/dialog.js +0 -38
  47. package/dist/components/dialog.js.map +0 -1
  48. package/dist/components/dropdown-menu.d.ts +0 -17
  49. package/dist/components/dropdown-menu.d.ts.map +0 -1
  50. package/dist/components/dropdown-menu.js +0 -35
  51. package/dist/components/dropdown-menu.js.map +0 -1
  52. package/dist/components/github-background.d.ts +0 -3
  53. package/dist/components/github-background.d.ts.map +0 -1
  54. package/dist/components/github-background.js +0 -51
  55. package/dist/components/github-background.js.map +0 -1
  56. package/dist/components/halftone-background.d.ts +0 -3
  57. package/dist/components/halftone-background.d.ts.map +0 -1
  58. package/dist/components/halftone-background.js +0 -248
  59. package/dist/components/halftone-background.js.map +0 -1
  60. package/dist/components/input-group.d.ts +0 -19
  61. package/dist/components/input-group.d.ts.map +0 -1
  62. package/dist/components/input-group.js +0 -63
  63. package/dist/components/input-group.js.map +0 -1
  64. package/dist/components/input.d.ts +0 -5
  65. package/dist/components/input.d.ts.map +0 -1
  66. package/dist/components/input.js +0 -7
  67. package/dist/components/input.js.map +0 -1
  68. package/dist/components/live-duration.d.ts +0 -9
  69. package/dist/components/live-duration.d.ts.map +0 -1
  70. package/dist/components/live-duration.js +0 -55
  71. package/dist/components/live-duration.js.map +0 -1
  72. package/dist/components/logo.d.ts +0 -8
  73. package/dist/components/logo.d.ts.map +0 -1
  74. package/dist/components/logo.js +0 -9
  75. package/dist/components/logo.js.map +0 -1
  76. package/dist/components/popover.d.ts +0 -12
  77. package/dist/components/popover.d.ts.map +0 -1
  78. package/dist/components/popover.js +0 -26
  79. package/dist/components/popover.js.map +0 -1
  80. package/dist/components/resize-handle.d.ts +0 -15
  81. package/dist/components/resize-handle.d.ts.map +0 -1
  82. package/dist/components/resize-handle.js +0 -36
  83. package/dist/components/resize-handle.js.map +0 -1
  84. package/dist/components/sheet.d.ts +0 -18
  85. package/dist/components/sheet.d.ts.map +0 -1
  86. package/dist/components/sheet.js +0 -44
  87. package/dist/components/sheet.js.map +0 -1
  88. package/dist/components/textarea.d.ts +0 -5
  89. package/dist/components/textarea.d.ts.map +0 -1
  90. package/dist/components/textarea.js +0 -7
  91. package/dist/components/textarea.js.map +0 -1
  92. package/dist/components/time-ago.d.ts +0 -8
  93. package/dist/components/time-ago.d.ts.map +0 -1
  94. package/dist/components/time-ago.js +0 -40
  95. package/dist/components/time-ago.js.map +0 -1
  96. package/dist/components/tooltip.d.ts +0 -10
  97. package/dist/components/tooltip.d.ts.map +0 -1
  98. package/dist/components/tooltip.js +0 -25
  99. package/dist/components/tooltip.js.map +0 -1
  100. package/dist/foundation/cn.d.ts +0 -7
  101. package/dist/foundation/cn.d.ts.map +0 -1
  102. package/dist/foundation/cn.js +0 -8
  103. package/dist/foundation/cn.js.map +0 -1
  104. package/dist/index.d.ts +0 -21
  105. package/dist/index.d.ts.map +0 -1
  106. 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.2",
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
- "dist",
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
+ };