create-strayl-web-app 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
- package/template/README.md +290 -0
- package/template/components.json +24 -0
- package/template/package.json +56 -0
- package/template/public/favicon.ico +0 -0
- package/template/public/google-logo.svg +6 -0
- package/template/public/logo-dark.ico +0 -0
- package/template/public/logo-dark.webp +0 -0
- package/template/public/logo-light.ico +0 -0
- package/template/public/logo-light.webp +0 -0
- package/template/public/manifest.json +16 -0
- package/template/public/robots.txt +3 -0
- package/template/src/components/Header.tsx +76 -0
- package/template/src/components/language-switcher.tsx +38 -0
- package/template/src/components/theme-provider.tsx +14 -0
- package/template/src/components/themed-logo.tsx +44 -0
- package/template/src/components/ui/accordion.tsx +69 -0
- package/template/src/components/ui/alert-dialog.tsx +169 -0
- package/template/src/components/ui/alert.tsx +80 -0
- package/template/src/components/ui/autocomplete.tsx +301 -0
- package/template/src/components/ui/avatar.tsx +46 -0
- package/template/src/components/ui/badge.tsx +60 -0
- package/template/src/components/ui/breadcrumb.tsx +112 -0
- package/template/src/components/ui/button.tsx +73 -0
- package/template/src/components/ui/card.tsx +244 -0
- package/template/src/components/ui/checkbox-group.tsx +16 -0
- package/template/src/components/ui/checkbox.tsx +60 -0
- package/template/src/components/ui/collapsible.tsx +45 -0
- package/template/src/components/ui/combobox.tsx +415 -0
- package/template/src/components/ui/command.tsx +264 -0
- package/template/src/components/ui/dialog.tsx +196 -0
- package/template/src/components/ui/empty.tsx +127 -0
- package/template/src/components/ui/field.tsx +74 -0
- package/template/src/components/ui/fieldset.tsx +29 -0
- package/template/src/components/ui/form.tsx +17 -0
- package/template/src/components/ui/frame.tsx +82 -0
- package/template/src/components/ui/group.tsx +97 -0
- package/template/src/components/ui/input-group.tsx +101 -0
- package/template/src/components/ui/input.tsx +66 -0
- package/template/src/components/ui/kbd.tsx +28 -0
- package/template/src/components/ui/label.tsx +28 -0
- package/template/src/components/ui/menu.tsx +310 -0
- package/template/src/components/ui/meter.tsx +67 -0
- package/template/src/components/ui/number-field.tsx +160 -0
- package/template/src/components/ui/pagination.tsx +136 -0
- package/template/src/components/ui/popover.tsx +104 -0
- package/template/src/components/ui/preview-card.tsx +55 -0
- package/template/src/components/ui/progress.tsx +81 -0
- package/template/src/components/ui/radio-group.tsx +36 -0
- package/template/src/components/ui/scroll-area.tsx +64 -0
- package/template/src/components/ui/select.tsx +180 -0
- package/template/src/components/ui/separator.tsx +23 -0
- package/template/src/components/ui/sheet.tsx +203 -0
- package/template/src/components/ui/sidebar.tsx +743 -0
- package/template/src/components/ui/skeleton.tsx +16 -0
- package/template/src/components/ui/slider.tsx +74 -0
- package/template/src/components/ui/spinner.tsx +18 -0
- package/template/src/components/ui/switch.tsx +27 -0
- package/template/src/components/ui/table.tsx +126 -0
- package/template/src/components/ui/tabs.tsx +87 -0
- package/template/src/components/ui/textarea.tsx +51 -0
- package/template/src/components/ui/toast.tsx +269 -0
- package/template/src/components/ui/toggle-group.tsx +102 -0
- package/template/src/components/ui/toggle.tsx +45 -0
- package/template/src/components/ui/toolbar.tsx +83 -0
- package/template/src/components/ui/tooltip.tsx +65 -0
- package/template/src/hooks/use-mobile.ts +21 -0
- package/template/src/lib/i18n.ts +70 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/src/routeTree.gen.ts +68 -0
- package/template/src/router.tsx +17 -0
- package/template/src/routes/__root.tsx +62 -0
- package/template/src/routes/index.tsx +71 -0
- package/template/src/styles.css +121 -0
- package/template/tsconfig.json +28 -0
- package/template/vite.config.ts +30 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
|
|
4
|
+
import { ChevronsUpDownIcon, XIcon } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
import { Input } from "@/components/ui/input";
|
|
9
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
10
|
+
|
|
11
|
+
const ComboboxContext = React.createContext<{
|
|
12
|
+
chipsRef: React.RefObject<Element | null> | null;
|
|
13
|
+
multiple: boolean;
|
|
14
|
+
}>({
|
|
15
|
+
chipsRef: null,
|
|
16
|
+
multiple: false,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
type ComboboxRootProps<
|
|
20
|
+
ItemValue,
|
|
21
|
+
Multiple extends boolean | undefined,
|
|
22
|
+
> = Parameters<typeof ComboboxPrimitive.Root<ItemValue, Multiple>>[0];
|
|
23
|
+
|
|
24
|
+
function Combobox<ItemValue, Multiple extends boolean | undefined = false>(
|
|
25
|
+
props: ComboboxPrimitive.Root.Props<ItemValue, Multiple>,
|
|
26
|
+
) {
|
|
27
|
+
const chipsRef = React.useRef<Element | null>(null);
|
|
28
|
+
return (
|
|
29
|
+
<ComboboxContext.Provider value={{ chipsRef, multiple: !!props.multiple }}>
|
|
30
|
+
<ComboboxPrimitive.Root
|
|
31
|
+
{...(props as ComboboxRootProps<ItemValue, Multiple>)}
|
|
32
|
+
/>
|
|
33
|
+
</ComboboxContext.Provider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ComboboxInput({
|
|
38
|
+
className,
|
|
39
|
+
showTrigger = true,
|
|
40
|
+
showClear = false,
|
|
41
|
+
startAddon,
|
|
42
|
+
size,
|
|
43
|
+
...props
|
|
44
|
+
}: Omit<ComboboxPrimitive.Input.Props, "size"> & {
|
|
45
|
+
showTrigger?: boolean;
|
|
46
|
+
showClear?: boolean;
|
|
47
|
+
startAddon?: React.ReactNode;
|
|
48
|
+
size?: "sm" | "default" | "lg" | number;
|
|
49
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
50
|
+
}) {
|
|
51
|
+
const { multiple } = React.useContext(ComboboxContext);
|
|
52
|
+
const sizeValue = (size ?? "default") as "sm" | "default" | "lg" | number;
|
|
53
|
+
|
|
54
|
+
// multiple mode
|
|
55
|
+
if (multiple) {
|
|
56
|
+
return (
|
|
57
|
+
<ComboboxPrimitive.Input
|
|
58
|
+
className={cn(
|
|
59
|
+
"min-w-12 flex-1 text-base outline-none sm:text-sm [[data-slot=combobox-chip]+&]:ps-0.5",
|
|
60
|
+
sizeValue === "sm" ? "ps-1.5" : "ps-2",
|
|
61
|
+
className,
|
|
62
|
+
)}
|
|
63
|
+
data-size={typeof sizeValue === "string" ? sizeValue : undefined}
|
|
64
|
+
data-slot="combobox-input"
|
|
65
|
+
size={typeof sizeValue === "number" ? sizeValue : undefined}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// single mode
|
|
72
|
+
return (
|
|
73
|
+
<div className="relative not-has-[>*.w-full]:w-fit w-full text-foreground has-disabled:opacity-64">
|
|
74
|
+
{startAddon && (
|
|
75
|
+
<div
|
|
76
|
+
aria-hidden="true"
|
|
77
|
+
className="[&_svg]:-mx-0.5 pointer-events-none absolute inset-y-0 start-px z-10 flex items-center ps-[calc(--spacing(3)-1px)] opacity-80 has-[+[data-size=sm]]:ps-[calc(--spacing(2.5)-1px)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4"
|
|
78
|
+
data-slot="combobox-start-addon"
|
|
79
|
+
>
|
|
80
|
+
{startAddon}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
<ComboboxPrimitive.Input
|
|
84
|
+
className={cn(
|
|
85
|
+
startAddon &&
|
|
86
|
+
"data-[size=sm]:*:data-[slot=combobox-input]:ps-[calc(--spacing(7.5)-1px)] *:data-[slot=combobox-input]:ps-[calc(--spacing(8.5)-1px)] sm:data-[size=sm]:*:data-[slot=combobox-input]:ps-[calc(--spacing(7)-1px)] sm:*:data-[slot=combobox-input]:ps-[calc(--spacing(8)-1px)]",
|
|
87
|
+
sizeValue === "sm"
|
|
88
|
+
? "has-[+[data-slot=combobox-trigger],+[data-slot=combobox-clear]]:*:data-[slot=combobox-input]:pe-6.5"
|
|
89
|
+
: "has-[+[data-slot=combobox-trigger],+[data-slot=combobox-clear]]:*:data-[slot=combobox-input]:pe-7",
|
|
90
|
+
className,
|
|
91
|
+
)}
|
|
92
|
+
data-slot="combobox-input"
|
|
93
|
+
render={
|
|
94
|
+
<Input
|
|
95
|
+
className="has-disabled:opacity-100"
|
|
96
|
+
nativeInput
|
|
97
|
+
size={sizeValue}
|
|
98
|
+
/>
|
|
99
|
+
}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
{showTrigger && (
|
|
103
|
+
<ComboboxTrigger
|
|
104
|
+
className={cn(
|
|
105
|
+
"-translate-y-1/2 absolute top-1/2 inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-opacity pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=combobox-clear]]:hidden sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
106
|
+
sizeValue === "sm" ? "end-0" : "end-0.5",
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
<ChevronsUpDownIcon />
|
|
110
|
+
</ComboboxTrigger>
|
|
111
|
+
)}
|
|
112
|
+
{showClear && (
|
|
113
|
+
<ComboboxClear
|
|
114
|
+
className={cn(
|
|
115
|
+
"-translate-y-1/2 absolute top-1/2 inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-opacity pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=combobox-clear]]:hidden sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
116
|
+
sizeValue === "sm" ? "end-0" : "end-0.5",
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
<XIcon />
|
|
120
|
+
</ComboboxClear>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function ComboboxTrigger({
|
|
127
|
+
className,
|
|
128
|
+
...props
|
|
129
|
+
}: ComboboxPrimitive.Trigger.Props) {
|
|
130
|
+
return (
|
|
131
|
+
<ComboboxPrimitive.Trigger
|
|
132
|
+
className={className}
|
|
133
|
+
data-slot="combobox-trigger"
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function ComboboxPopup({
|
|
140
|
+
className,
|
|
141
|
+
children,
|
|
142
|
+
sideOffset = 4,
|
|
143
|
+
...props
|
|
144
|
+
}: ComboboxPrimitive.Popup.Props & {
|
|
145
|
+
sideOffset?: number;
|
|
146
|
+
}) {
|
|
147
|
+
const { chipsRef } = React.useContext(ComboboxContext);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<ComboboxPrimitive.Portal>
|
|
151
|
+
<ComboboxPrimitive.Positioner
|
|
152
|
+
anchor={chipsRef}
|
|
153
|
+
className="z-50 select-none"
|
|
154
|
+
data-slot="combobox-positioner"
|
|
155
|
+
sideOffset={sideOffset}
|
|
156
|
+
>
|
|
157
|
+
<span
|
|
158
|
+
className={cn(
|
|
159
|
+
"relative flex max-h-full origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 transition-[scale,opacity] before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
|
|
160
|
+
className,
|
|
161
|
+
)}
|
|
162
|
+
>
|
|
163
|
+
<ComboboxPrimitive.Popup
|
|
164
|
+
className="flex max-h-[min(var(--available-height),23rem)] w-(--anchor-width) max-w-(--available-width) flex-col text-foreground"
|
|
165
|
+
data-slot="combobox-popup"
|
|
166
|
+
{...props}
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</ComboboxPrimitive.Popup>
|
|
170
|
+
</span>
|
|
171
|
+
</ComboboxPrimitive.Positioner>
|
|
172
|
+
</ComboboxPrimitive.Portal>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function ComboboxItem({
|
|
177
|
+
className,
|
|
178
|
+
children,
|
|
179
|
+
...props
|
|
180
|
+
}: ComboboxPrimitive.Item.Props) {
|
|
181
|
+
return (
|
|
182
|
+
<ComboboxPrimitive.Item
|
|
183
|
+
className={cn(
|
|
184
|
+
"grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
185
|
+
className,
|
|
186
|
+
)}
|
|
187
|
+
data-slot="combobox-item"
|
|
188
|
+
{...props}
|
|
189
|
+
>
|
|
190
|
+
<ComboboxPrimitive.ItemIndicator className="col-start-1">
|
|
191
|
+
<svg
|
|
192
|
+
fill="none"
|
|
193
|
+
height="24"
|
|
194
|
+
stroke="currentColor"
|
|
195
|
+
strokeLinecap="round"
|
|
196
|
+
strokeLinejoin="round"
|
|
197
|
+
strokeWidth="2"
|
|
198
|
+
viewBox="0 0 24 24"
|
|
199
|
+
width="24"
|
|
200
|
+
xmlns="http://www.w3.org/1500/svg"
|
|
201
|
+
>
|
|
202
|
+
<path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
|
|
203
|
+
</svg>
|
|
204
|
+
</ComboboxPrimitive.ItemIndicator>
|
|
205
|
+
<div className="col-start-2">{children}</div>
|
|
206
|
+
</ComboboxPrimitive.Item>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function ComboboxSeparator({
|
|
211
|
+
className,
|
|
212
|
+
...props
|
|
213
|
+
}: ComboboxPrimitive.Separator.Props) {
|
|
214
|
+
return (
|
|
215
|
+
<ComboboxPrimitive.Separator
|
|
216
|
+
className={cn("mx-2 my-1 h-px bg-border last:hidden", className)}
|
|
217
|
+
data-slot="combobox-separator"
|
|
218
|
+
{...props}
|
|
219
|
+
/>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
|
|
224
|
+
return (
|
|
225
|
+
<ComboboxPrimitive.Group
|
|
226
|
+
className={cn("[[role=group]+&]:mt-1.5", className)}
|
|
227
|
+
data-slot="combobox-group"
|
|
228
|
+
{...props}
|
|
229
|
+
/>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function ComboboxGroupLabel({
|
|
234
|
+
className,
|
|
235
|
+
...props
|
|
236
|
+
}: ComboboxPrimitive.GroupLabel.Props) {
|
|
237
|
+
return (
|
|
238
|
+
<ComboboxPrimitive.GroupLabel
|
|
239
|
+
className={cn(
|
|
240
|
+
"px-2 py-1.5 font-medium text-muted-foreground text-xs",
|
|
241
|
+
className,
|
|
242
|
+
)}
|
|
243
|
+
data-slot="combobox-group-label"
|
|
244
|
+
{...props}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
|
|
250
|
+
return (
|
|
251
|
+
<ComboboxPrimitive.Empty
|
|
252
|
+
className={cn(
|
|
253
|
+
"not-empty:p-2 text-center text-base text-muted-foreground sm:text-sm",
|
|
254
|
+
className,
|
|
255
|
+
)}
|
|
256
|
+
data-slot="combobox-empty"
|
|
257
|
+
{...props}
|
|
258
|
+
/>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function ComboboxRow({ className, ...props }: ComboboxPrimitive.Row.Props) {
|
|
263
|
+
return (
|
|
264
|
+
<ComboboxPrimitive.Row
|
|
265
|
+
className={className}
|
|
266
|
+
data-slot="combobox-row"
|
|
267
|
+
{...props}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
|
|
273
|
+
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
|
|
277
|
+
return (
|
|
278
|
+
<ScrollArea scrollbarGutter scrollFade>
|
|
279
|
+
<ComboboxPrimitive.List
|
|
280
|
+
className={cn(
|
|
281
|
+
"not-empty:scroll-py-1 not-empty:px-1 not-empty:py-1 in-data-has-overflow-y:pe-3",
|
|
282
|
+
className,
|
|
283
|
+
)}
|
|
284
|
+
data-slot="combobox-list"
|
|
285
|
+
{...props}
|
|
286
|
+
/>
|
|
287
|
+
</ScrollArea>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
|
|
292
|
+
return (
|
|
293
|
+
<ComboboxPrimitive.Clear
|
|
294
|
+
className={className}
|
|
295
|
+
data-slot="combobox-clear"
|
|
296
|
+
{...props}
|
|
297
|
+
/>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function ComboboxStatus({
|
|
302
|
+
className,
|
|
303
|
+
...props
|
|
304
|
+
}: ComboboxPrimitive.Status.Props) {
|
|
305
|
+
return (
|
|
306
|
+
<ComboboxPrimitive.Status
|
|
307
|
+
className={cn(
|
|
308
|
+
"px-3 py-2 font-medium text-muted-foreground text-xs empty:m-0 empty:p-0",
|
|
309
|
+
className,
|
|
310
|
+
)}
|
|
311
|
+
data-slot="combobox-status"
|
|
312
|
+
{...props}
|
|
313
|
+
/>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function ComboboxCollection(props: ComboboxPrimitive.Collection.Props) {
|
|
318
|
+
return (
|
|
319
|
+
<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function ComboboxChips({
|
|
324
|
+
className,
|
|
325
|
+
children,
|
|
326
|
+
startAddon,
|
|
327
|
+
...props
|
|
328
|
+
}: ComboboxPrimitive.Chips.Props & {
|
|
329
|
+
startAddon?: React.ReactNode;
|
|
330
|
+
}) {
|
|
331
|
+
const { chipsRef } = React.useContext(ComboboxContext);
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<ComboboxPrimitive.Chips
|
|
335
|
+
className={cn(
|
|
336
|
+
"relative inline-flex min-h-9 w-full flex-wrap gap-1 rounded-lg border border-input bg-background not-dark:bg-clip-padding p-[calc(--spacing(1)-1px)] text-base shadow-xs/5 outline-none ring-ring/24 transition-shadow *:min-h-7 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/6%)] focus-within:border-ring focus-within:ring-[3px] has-disabled:pointer-events-none has-data-[size=lg]:min-h-10 has-data-[size=sm]:min-h-8 has-aria-invalid:border-destructive/36 has-disabled:opacity-64 has-[:disabled,:focus-within,[aria-invalid]]:shadow-none focus-within:has-aria-invalid:border-destructive/64 focus-within:has-aria-invalid:ring-destructive/16 has-data-[size=lg]:*:min-h-8 has-data-[size=sm]:*:min-h-6 sm:min-h-8 sm:text-sm sm:has-data-[size=lg]:min-h-9 sm:has-data-[size=sm]:min-h-7 sm:*:min-h-6 sm:has-data-[size=lg]:*:min-h-7 sm:has-data-[size=sm]:*:min-h-5 dark:not-has-disabled:bg-input/32 dark:has-aria-invalid:ring-destructive/24 dark:not-has-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]",
|
|
337
|
+
className,
|
|
338
|
+
)}
|
|
339
|
+
data-slot="combobox-chips"
|
|
340
|
+
onMouseDown={(e) => {
|
|
341
|
+
const target = e.target as HTMLElement;
|
|
342
|
+
const isChip = target.closest('[data-slot="combobox-chip"]');
|
|
343
|
+
if (isChip || !chipsRef?.current) return;
|
|
344
|
+
e.preventDefault();
|
|
345
|
+
const input: HTMLInputElement | null =
|
|
346
|
+
chipsRef.current.querySelector("input");
|
|
347
|
+
if (input && !chipsRef.current.querySelector("input:focus")) {
|
|
348
|
+
input.focus();
|
|
349
|
+
}
|
|
350
|
+
}}
|
|
351
|
+
ref={chipsRef as React.Ref<HTMLDivElement> | null}
|
|
352
|
+
{...props}
|
|
353
|
+
>
|
|
354
|
+
{startAddon && (
|
|
355
|
+
<div
|
|
356
|
+
aria-hidden="true"
|
|
357
|
+
className="[&_svg]:-ms-0.5 [&_svg]:-me-1.5 flex shrink-0 items-center ps-2 opacity-80 has-[~[data-size=sm]]:has-[+[data-slot=combobox-chip]]:pe-1.5 has-[~[data-size=sm]]:ps-1.5 has-[+[data-slot=combobox-chip]]:pe-2 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none"
|
|
358
|
+
data-slot="combobox-start-addon"
|
|
359
|
+
>
|
|
360
|
+
{startAddon}
|
|
361
|
+
</div>
|
|
362
|
+
)}
|
|
363
|
+
{children}
|
|
364
|
+
</ComboboxPrimitive.Chips>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function ComboboxChip({ children, ...props }: ComboboxPrimitive.Chip.Props) {
|
|
369
|
+
return (
|
|
370
|
+
<ComboboxPrimitive.Chip
|
|
371
|
+
className="flex items-center rounded-[calc(var(--radius-md)-1px)] bg-accent ps-2 font-medium text-accent-foreground text-sm outline-none sm:text-xs/(--text-xs--line-height) [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5"
|
|
372
|
+
data-slot="combobox-chip"
|
|
373
|
+
{...props}
|
|
374
|
+
>
|
|
375
|
+
{children}
|
|
376
|
+
<ComboboxChipRemove />
|
|
377
|
+
</ComboboxPrimitive.Chip>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function ComboboxChipRemove(props: ComboboxPrimitive.ChipRemove.Props) {
|
|
382
|
+
return (
|
|
383
|
+
<ComboboxPrimitive.ChipRemove
|
|
384
|
+
aria-label="Remove"
|
|
385
|
+
className="h-full shrink-0 cursor-pointer px-1.5 opacity-80 hover:opacity-100 [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5"
|
|
386
|
+
data-slot="combobox-chip-remove"
|
|
387
|
+
{...props}
|
|
388
|
+
>
|
|
389
|
+
<XIcon />
|
|
390
|
+
</ComboboxPrimitive.ChipRemove>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const useComboboxFilter = ComboboxPrimitive.useFilter;
|
|
395
|
+
|
|
396
|
+
export {
|
|
397
|
+
Combobox,
|
|
398
|
+
ComboboxInput,
|
|
399
|
+
ComboboxTrigger,
|
|
400
|
+
ComboboxPopup,
|
|
401
|
+
ComboboxItem,
|
|
402
|
+
ComboboxSeparator,
|
|
403
|
+
ComboboxGroup,
|
|
404
|
+
ComboboxGroupLabel,
|
|
405
|
+
ComboboxEmpty,
|
|
406
|
+
ComboboxValue,
|
|
407
|
+
ComboboxList,
|
|
408
|
+
ComboboxClear,
|
|
409
|
+
ComboboxStatus,
|
|
410
|
+
ComboboxRow,
|
|
411
|
+
ComboboxCollection,
|
|
412
|
+
ComboboxChips,
|
|
413
|
+
ComboboxChip,
|
|
414
|
+
useComboboxFilter,
|
|
415
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Dialog as CommandDialogPrimitive } from "@base-ui/react/dialog";
|
|
4
|
+
import { SearchIcon } from "lucide-react";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
import {
|
|
8
|
+
Autocomplete,
|
|
9
|
+
AutocompleteCollection,
|
|
10
|
+
AutocompleteEmpty,
|
|
11
|
+
AutocompleteGroup,
|
|
12
|
+
AutocompleteGroupLabel,
|
|
13
|
+
AutocompleteInput,
|
|
14
|
+
AutocompleteItem,
|
|
15
|
+
AutocompleteList,
|
|
16
|
+
AutocompleteSeparator,
|
|
17
|
+
} from "@/components/ui/autocomplete";
|
|
18
|
+
|
|
19
|
+
const CommandDialog = CommandDialogPrimitive.Root;
|
|
20
|
+
|
|
21
|
+
const CommandDialogPortal = CommandDialogPrimitive.Portal;
|
|
22
|
+
|
|
23
|
+
const CommandCreateHandle = CommandDialogPrimitive.createHandle;
|
|
24
|
+
|
|
25
|
+
function CommandDialogTrigger(props: CommandDialogPrimitive.Trigger.Props) {
|
|
26
|
+
return (
|
|
27
|
+
<CommandDialogPrimitive.Trigger
|
|
28
|
+
data-slot="command-dialog-trigger"
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function CommandDialogBackdrop({
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}: CommandDialogPrimitive.Backdrop.Props) {
|
|
38
|
+
return (
|
|
39
|
+
<CommandDialogPrimitive.Backdrop
|
|
40
|
+
className={cn(
|
|
41
|
+
"fixed inset-0 z-50 bg-black/32 backdrop-blur-sm transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0",
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
data-slot="command-dialog-backdrop"
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function CommandDialogViewport({
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: CommandDialogPrimitive.Viewport.Props) {
|
|
54
|
+
return (
|
|
55
|
+
<CommandDialogPrimitive.Viewport
|
|
56
|
+
className={cn(
|
|
57
|
+
"fixed inset-0 z-50 flex flex-col items-center px-4 py-[max(--spacing(4),4vh)] sm:py-[10vh]",
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
data-slot="command-dialog-viewport"
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function CommandDialogPopup({
|
|
67
|
+
className,
|
|
68
|
+
children,
|
|
69
|
+
...props
|
|
70
|
+
}: CommandDialogPrimitive.Popup.Props) {
|
|
71
|
+
return (
|
|
72
|
+
<CommandDialogPortal>
|
|
73
|
+
<CommandDialogBackdrop />
|
|
74
|
+
<CommandDialogViewport>
|
|
75
|
+
<CommandDialogPrimitive.Popup
|
|
76
|
+
className={cn(
|
|
77
|
+
"-translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-105 min-h-0 w-full min-w-0 max-w-xl scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border bg-popover not-dark:bg-clip-padding text-popover-foreground opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 outline-none transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:bg-muted/72 before:shadow-[0_1px_--theme(--color-black/6%)] data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=scroll-area-viewport]:data-has-overflow-y:pe-1 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
|
|
78
|
+
className,
|
|
79
|
+
)}
|
|
80
|
+
data-slot="command-dialog-popup"
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</CommandDialogPrimitive.Popup>
|
|
85
|
+
</CommandDialogViewport>
|
|
86
|
+
</CommandDialogPortal>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function Command({
|
|
91
|
+
autoHighlight = "always",
|
|
92
|
+
keepHighlight = true,
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentProps<typeof Autocomplete>) {
|
|
95
|
+
return (
|
|
96
|
+
<Autocomplete
|
|
97
|
+
autoHighlight={autoHighlight}
|
|
98
|
+
inline
|
|
99
|
+
keepHighlight={keepHighlight}
|
|
100
|
+
open
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function CommandInput({
|
|
107
|
+
className,
|
|
108
|
+
placeholder = undefined,
|
|
109
|
+
...props
|
|
110
|
+
}: React.ComponentProps<typeof AutocompleteInput>) {
|
|
111
|
+
return (
|
|
112
|
+
<div className="px-2.5 py-1.5">
|
|
113
|
+
<AutocompleteInput
|
|
114
|
+
autoFocus
|
|
115
|
+
className={cn(
|
|
116
|
+
"border-transparent! bg-transparent! shadow-none before:hidden has-focus-visible:ring-0",
|
|
117
|
+
className,
|
|
118
|
+
)}
|
|
119
|
+
placeholder={placeholder}
|
|
120
|
+
size="lg"
|
|
121
|
+
startAddon={<SearchIcon />}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function CommandList({
|
|
129
|
+
className,
|
|
130
|
+
...props
|
|
131
|
+
}: React.ComponentProps<typeof AutocompleteList>) {
|
|
132
|
+
return (
|
|
133
|
+
<AutocompleteList
|
|
134
|
+
className={cn("not-empty:scroll-py-2 not-empty:p-2", className)}
|
|
135
|
+
data-slot="command-list"
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function CommandEmpty({
|
|
142
|
+
className,
|
|
143
|
+
...props
|
|
144
|
+
}: React.ComponentProps<typeof AutocompleteEmpty>) {
|
|
145
|
+
return (
|
|
146
|
+
<AutocompleteEmpty
|
|
147
|
+
className={cn("not-empty:py-6", className)}
|
|
148
|
+
data-slot="command-empty"
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function CommandPanel({ className, ...props }: React.ComponentProps<"div">) {
|
|
155
|
+
return (
|
|
156
|
+
<div
|
|
157
|
+
className="-mx-px not-has-[+[data-slot=command-footer]]:-mb-px relative min-h-0 rounded-t-xl not-has-[+[data-slot=command-footer]]:rounded-b-2xl border border-b-0 bg-popover bg-clip-padding shadow-xs/5 [clip-path:inset(0_1px)] not-has-[+[data-slot=command-footer]]:[clip-path:inset(0_1px_1px_1px_round_0_0_calc(var(--radius-2xl)-1px)_calc(var(--radius-2xl)-1px))] before:pointer-events-none before:absolute before:inset-0 before:rounded-t-[calc(var(--radius-xl)-1px)] **:data-[slot=scroll-area-scrollbar]:mt-2"
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function CommandGroup({
|
|
164
|
+
className,
|
|
165
|
+
...props
|
|
166
|
+
}: React.ComponentProps<typeof AutocompleteGroup>) {
|
|
167
|
+
return (
|
|
168
|
+
<AutocompleteGroup
|
|
169
|
+
className={className}
|
|
170
|
+
data-slot="command-group"
|
|
171
|
+
{...props}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function CommandGroupLabel({
|
|
177
|
+
className,
|
|
178
|
+
...props
|
|
179
|
+
}: React.ComponentProps<typeof AutocompleteGroupLabel>) {
|
|
180
|
+
return (
|
|
181
|
+
<AutocompleteGroupLabel
|
|
182
|
+
className={className}
|
|
183
|
+
data-slot="command-group-label"
|
|
184
|
+
{...props}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function CommandCollection({
|
|
190
|
+
...props
|
|
191
|
+
}: React.ComponentProps<typeof AutocompleteCollection>) {
|
|
192
|
+
return <AutocompleteCollection data-slot="command-collection" {...props} />;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function CommandItem({
|
|
196
|
+
className,
|
|
197
|
+
...props
|
|
198
|
+
}: React.ComponentProps<typeof AutocompleteItem>) {
|
|
199
|
+
return (
|
|
200
|
+
<AutocompleteItem
|
|
201
|
+
className={cn("py-1.5", className)}
|
|
202
|
+
data-slot="command-item"
|
|
203
|
+
{...props}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function CommandSeparator({
|
|
209
|
+
className,
|
|
210
|
+
...props
|
|
211
|
+
}: React.ComponentProps<typeof AutocompleteSeparator>) {
|
|
212
|
+
return (
|
|
213
|
+
<AutocompleteSeparator
|
|
214
|
+
className={cn("my-2", className)}
|
|
215
|
+
data-slot="command-separator"
|
|
216
|
+
{...props}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function CommandShortcut({ className, ...props }: React.ComponentProps<"kbd">) {
|
|
222
|
+
return (
|
|
223
|
+
<kbd
|
|
224
|
+
className={cn(
|
|
225
|
+
"ms-auto font-medium font-sans text-muted-foreground/72 text-xs tracking-widest",
|
|
226
|
+
className,
|
|
227
|
+
)}
|
|
228
|
+
data-slot="command-shortcut"
|
|
229
|
+
{...props}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function CommandFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
235
|
+
return (
|
|
236
|
+
<div
|
|
237
|
+
className={cn(
|
|
238
|
+
"flex items-center justify-between gap-2 rounded-b-[calc(var(--radius-2xl)-1px)] border-t px-5 py-3 text-muted-foreground text-xs",
|
|
239
|
+
className,
|
|
240
|
+
)}
|
|
241
|
+
data-slot="command-footer"
|
|
242
|
+
{...props}
|
|
243
|
+
/>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
CommandCreateHandle,
|
|
249
|
+
Command,
|
|
250
|
+
CommandCollection,
|
|
251
|
+
CommandDialog,
|
|
252
|
+
CommandDialogPopup,
|
|
253
|
+
CommandDialogTrigger,
|
|
254
|
+
CommandEmpty,
|
|
255
|
+
CommandFooter,
|
|
256
|
+
CommandGroup,
|
|
257
|
+
CommandGroupLabel,
|
|
258
|
+
CommandInput,
|
|
259
|
+
CommandItem,
|
|
260
|
+
CommandList,
|
|
261
|
+
CommandPanel,
|
|
262
|
+
CommandSeparator,
|
|
263
|
+
CommandShortcut,
|
|
264
|
+
};
|