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.
Files changed (78) hide show
  1. package/dist/index.js +59 -0
  2. package/dist/index.js.map +1 -0
  3. package/package.json +39 -0
  4. package/template/README.md +290 -0
  5. package/template/components.json +24 -0
  6. package/template/package.json +56 -0
  7. package/template/public/favicon.ico +0 -0
  8. package/template/public/google-logo.svg +6 -0
  9. package/template/public/logo-dark.ico +0 -0
  10. package/template/public/logo-dark.webp +0 -0
  11. package/template/public/logo-light.ico +0 -0
  12. package/template/public/logo-light.webp +0 -0
  13. package/template/public/manifest.json +16 -0
  14. package/template/public/robots.txt +3 -0
  15. package/template/src/components/Header.tsx +76 -0
  16. package/template/src/components/language-switcher.tsx +38 -0
  17. package/template/src/components/theme-provider.tsx +14 -0
  18. package/template/src/components/themed-logo.tsx +44 -0
  19. package/template/src/components/ui/accordion.tsx +69 -0
  20. package/template/src/components/ui/alert-dialog.tsx +169 -0
  21. package/template/src/components/ui/alert.tsx +80 -0
  22. package/template/src/components/ui/autocomplete.tsx +301 -0
  23. package/template/src/components/ui/avatar.tsx +46 -0
  24. package/template/src/components/ui/badge.tsx +60 -0
  25. package/template/src/components/ui/breadcrumb.tsx +112 -0
  26. package/template/src/components/ui/button.tsx +73 -0
  27. package/template/src/components/ui/card.tsx +244 -0
  28. package/template/src/components/ui/checkbox-group.tsx +16 -0
  29. package/template/src/components/ui/checkbox.tsx +60 -0
  30. package/template/src/components/ui/collapsible.tsx +45 -0
  31. package/template/src/components/ui/combobox.tsx +415 -0
  32. package/template/src/components/ui/command.tsx +264 -0
  33. package/template/src/components/ui/dialog.tsx +196 -0
  34. package/template/src/components/ui/empty.tsx +127 -0
  35. package/template/src/components/ui/field.tsx +74 -0
  36. package/template/src/components/ui/fieldset.tsx +29 -0
  37. package/template/src/components/ui/form.tsx +17 -0
  38. package/template/src/components/ui/frame.tsx +82 -0
  39. package/template/src/components/ui/group.tsx +97 -0
  40. package/template/src/components/ui/input-group.tsx +101 -0
  41. package/template/src/components/ui/input.tsx +66 -0
  42. package/template/src/components/ui/kbd.tsx +28 -0
  43. package/template/src/components/ui/label.tsx +28 -0
  44. package/template/src/components/ui/menu.tsx +310 -0
  45. package/template/src/components/ui/meter.tsx +67 -0
  46. package/template/src/components/ui/number-field.tsx +160 -0
  47. package/template/src/components/ui/pagination.tsx +136 -0
  48. package/template/src/components/ui/popover.tsx +104 -0
  49. package/template/src/components/ui/preview-card.tsx +55 -0
  50. package/template/src/components/ui/progress.tsx +81 -0
  51. package/template/src/components/ui/radio-group.tsx +36 -0
  52. package/template/src/components/ui/scroll-area.tsx +64 -0
  53. package/template/src/components/ui/select.tsx +180 -0
  54. package/template/src/components/ui/separator.tsx +23 -0
  55. package/template/src/components/ui/sheet.tsx +203 -0
  56. package/template/src/components/ui/sidebar.tsx +743 -0
  57. package/template/src/components/ui/skeleton.tsx +16 -0
  58. package/template/src/components/ui/slider.tsx +74 -0
  59. package/template/src/components/ui/spinner.tsx +18 -0
  60. package/template/src/components/ui/switch.tsx +27 -0
  61. package/template/src/components/ui/table.tsx +126 -0
  62. package/template/src/components/ui/tabs.tsx +87 -0
  63. package/template/src/components/ui/textarea.tsx +51 -0
  64. package/template/src/components/ui/toast.tsx +269 -0
  65. package/template/src/components/ui/toggle-group.tsx +102 -0
  66. package/template/src/components/ui/toggle.tsx +45 -0
  67. package/template/src/components/ui/toolbar.tsx +83 -0
  68. package/template/src/components/ui/tooltip.tsx +65 -0
  69. package/template/src/hooks/use-mobile.ts +21 -0
  70. package/template/src/lib/i18n.ts +70 -0
  71. package/template/src/lib/utils.ts +6 -0
  72. package/template/src/routeTree.gen.ts +68 -0
  73. package/template/src/router.tsx +17 -0
  74. package/template/src/routes/__root.tsx +62 -0
  75. package/template/src/routes/index.tsx +71 -0
  76. package/template/src/styles.css +121 -0
  77. package/template/tsconfig.json +28 -0
  78. package/template/vite.config.ts +30 -0
@@ -0,0 +1,44 @@
1
+ import { useTheme } from "next-themes"
2
+ import { useEffect, useState } from "react"
3
+
4
+ interface ThemedLogoProps {
5
+ width?: number
6
+ height?: number
7
+ className?: string
8
+ alt?: string
9
+ inverted?: boolean
10
+ }
11
+
12
+ export function ThemedLogo({
13
+ width = 24,
14
+ height = 24,
15
+ className = "",
16
+ alt = "Strayl Logo",
17
+ inverted = false,
18
+ }: ThemedLogoProps) {
19
+ const { resolvedTheme } = useTheme()
20
+ const [mounted, setMounted] = useState(false)
21
+
22
+ useEffect(() => {
23
+ setMounted(true)
24
+ }, [])
25
+
26
+ if (!mounted) {
27
+ return <div style={{ width, height }} className={className} />
28
+ }
29
+
30
+ const isDark = resolvedTheme === "dark"
31
+ const logoSrc = inverted
32
+ ? (isDark ? "/logo-dark.webp" : "/logo-light.webp")
33
+ : (isDark ? "/logo-light.webp" : "/logo-dark.webp")
34
+
35
+ return (
36
+ <img
37
+ src={logoSrc}
38
+ alt={alt}
39
+ width={width}
40
+ height={height}
41
+ className={className}
42
+ />
43
+ )
44
+ }
@@ -0,0 +1,69 @@
1
+ "use client";
2
+
3
+ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
4
+ import { ChevronDownIcon } from "lucide-react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Accordion(props: AccordionPrimitive.Root.Props) {
9
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
10
+ }
11
+
12
+ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
13
+ return (
14
+ <AccordionPrimitive.Item
15
+ className={cn("border-b last:border-b-0", className)}
16
+ data-slot="accordion-item"
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ function AccordionTrigger({
23
+ className,
24
+ children,
25
+ ...props
26
+ }: AccordionPrimitive.Trigger.Props) {
27
+ return (
28
+ <AccordionPrimitive.Header className="flex">
29
+ <AccordionPrimitive.Trigger
30
+ className={cn(
31
+ "flex flex-1 cursor-pointer items-start justify-between gap-4 rounded-md py-4 text-left font-medium text-sm outline-none transition-all focus-visible:ring-[3px] focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 data-panel-open:*:data-[slot=accordion-indicator]:rotate-180",
32
+ className,
33
+ )}
34
+ data-slot="accordion-trigger"
35
+ {...props}
36
+ >
37
+ {children}
38
+ <ChevronDownIcon
39
+ className="pointer-events-none size-4 shrink-0 translate-y-0.5 opacity-80 transition-transform duration-200 ease-in-out"
40
+ data-slot="accordion-indicator"
41
+ />
42
+ </AccordionPrimitive.Trigger>
43
+ </AccordionPrimitive.Header>
44
+ );
45
+ }
46
+
47
+ function AccordionPanel({
48
+ className,
49
+ children,
50
+ ...props
51
+ }: AccordionPrimitive.Panel.Props) {
52
+ return (
53
+ <AccordionPrimitive.Panel
54
+ className="h-(--accordion-panel-height) overflow-hidden text-muted-foreground text-sm transition-[height] duration-200 ease-in-out data-ending-style:h-0 data-starting-style:h-0"
55
+ data-slot="accordion-panel"
56
+ {...props}
57
+ >
58
+ <div className={cn("pt-0 pb-4", className)}>{children}</div>
59
+ </AccordionPrimitive.Panel>
60
+ );
61
+ }
62
+
63
+ export {
64
+ Accordion,
65
+ AccordionItem,
66
+ AccordionTrigger,
67
+ AccordionPanel,
68
+ AccordionPanel as AccordionContent,
69
+ };
@@ -0,0 +1,169 @@
1
+ "use client";
2
+
3
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const AlertDialogCreateHandle = AlertDialogPrimitive.createHandle;
8
+
9
+ const AlertDialog = AlertDialogPrimitive.Root;
10
+
11
+ const AlertDialogPortal = AlertDialogPrimitive.Portal;
12
+
13
+ function AlertDialogTrigger(props: AlertDialogPrimitive.Trigger.Props) {
14
+ return (
15
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
16
+ );
17
+ }
18
+
19
+ function AlertDialogBackdrop({
20
+ className,
21
+ ...props
22
+ }: AlertDialogPrimitive.Backdrop.Props) {
23
+ return (
24
+ <AlertDialogPrimitive.Backdrop
25
+ className={cn(
26
+ "fixed inset-0 z-50 bg-black/32 backdrop-blur-sm transition-all duration-200 ease-out data-ending-style:opacity-0 data-starting-style:opacity-0",
27
+ className,
28
+ )}
29
+ data-slot="alert-dialog-backdrop"
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ function AlertDialogViewport({
36
+ className,
37
+ ...props
38
+ }: AlertDialogPrimitive.Viewport.Props) {
39
+ return (
40
+ <AlertDialogPrimitive.Viewport
41
+ className={cn(
42
+ "fixed inset-0 z-50 grid grid-rows-[1fr_auto_3fr] justify-items-center p-4",
43
+ className,
44
+ )}
45
+ data-slot="alert-dialog-viewport"
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ function AlertDialogPopup({
52
+ className,
53
+ bottomStickOnMobile = true,
54
+ ...props
55
+ }: AlertDialogPrimitive.Popup.Props & {
56
+ bottomStickOnMobile?: boolean;
57
+ }) {
58
+ return (
59
+ <AlertDialogPortal>
60
+ <AlertDialogBackdrop />
61
+ <AlertDialogViewport
62
+ className={cn(
63
+ bottomStickOnMobile &&
64
+ "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12",
65
+ )}
66
+ >
67
+ <AlertDialogPrimitive.Popup
68
+ className={cn(
69
+ "-translate-y-[calc(1.25rem*var(--nested-dialogs))] relative row-start-2 flex max-h-full min-h-0 w-full min-w-0 max-w-lg 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 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: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 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
70
+ bottomStickOnMobile &&
71
+ "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4 max-sm:before:hidden max-sm:before:rounded-none",
72
+ className,
73
+ )}
74
+ data-slot="alert-dialog-popup"
75
+ {...props}
76
+ />
77
+ </AlertDialogViewport>
78
+ </AlertDialogPortal>
79
+ );
80
+ }
81
+
82
+ function AlertDialogHeader({
83
+ className,
84
+ ...props
85
+ }: React.ComponentProps<"div">) {
86
+ return (
87
+ <div
88
+ className={cn(
89
+ "flex flex-col gap-2 p-6 text-center max-sm:pb-4 sm:text-left",
90
+ className,
91
+ )}
92
+ data-slot="alert-dialog-header"
93
+ {...props}
94
+ />
95
+ );
96
+ }
97
+
98
+ function AlertDialogFooter({
99
+ className,
100
+ variant = "default",
101
+ ...props
102
+ }: React.ComponentProps<"div"> & {
103
+ variant?: "default" | "bare";
104
+ }) {
105
+ return (
106
+ <div
107
+ className={cn(
108
+ "flex flex-col-reverse gap-2 px-6 sm:flex-row sm:justify-end sm:rounded-b-[calc(var(--radius-2xl)-1px)]",
109
+ variant === "default" && "border-t bg-muted/72 py-4",
110
+ variant === "bare" && "pb-6",
111
+ className,
112
+ )}
113
+ data-slot="alert-dialog-footer"
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ function AlertDialogTitle({
120
+ className,
121
+ ...props
122
+ }: AlertDialogPrimitive.Title.Props) {
123
+ return (
124
+ <AlertDialogPrimitive.Title
125
+ className={cn(
126
+ "font-heading font-semibold text-xl leading-none",
127
+ className,
128
+ )}
129
+ data-slot="alert-dialog-title"
130
+ {...props}
131
+ />
132
+ );
133
+ }
134
+
135
+ function AlertDialogDescription({
136
+ className,
137
+ ...props
138
+ }: AlertDialogPrimitive.Description.Props) {
139
+ return (
140
+ <AlertDialogPrimitive.Description
141
+ className={cn("text-muted-foreground text-sm", className)}
142
+ data-slot="alert-dialog-description"
143
+ {...props}
144
+ />
145
+ );
146
+ }
147
+
148
+ function AlertDialogClose(props: AlertDialogPrimitive.Close.Props) {
149
+ return (
150
+ <AlertDialogPrimitive.Close data-slot="alert-dialog-close" {...props} />
151
+ );
152
+ }
153
+
154
+ export {
155
+ AlertDialogCreateHandle,
156
+ AlertDialog,
157
+ AlertDialogPortal,
158
+ AlertDialogBackdrop,
159
+ AlertDialogBackdrop as AlertDialogOverlay,
160
+ AlertDialogTrigger,
161
+ AlertDialogPopup,
162
+ AlertDialogPopup as AlertDialogContent,
163
+ AlertDialogHeader,
164
+ AlertDialogFooter,
165
+ AlertDialogTitle,
166
+ AlertDialogDescription,
167
+ AlertDialogClose,
168
+ AlertDialogViewport,
169
+ };
@@ -0,0 +1,80 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import type * as React from "react";
3
+
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const alertVariants = cva(
7
+ "relative grid w-full items-start gap-x-2 gap-y-0.5 rounded-xl border px-3.5 py-3 text-card-foreground text-sm has-[>svg]:has-data-[slot=alert-action]:grid-cols-[calc(var(--spacing)*4)_1fr_auto] has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-data-[slot=alert-action]:grid-cols-[1fr_auto] has-[>svg]:gap-x-2 [&>svg]:h-lh [&>svg]:w-4",
8
+ {
9
+ defaultVariants: {
10
+ variant: "default",
11
+ },
12
+ variants: {
13
+ variant: {
14
+ default:
15
+ "bg-transparent dark:bg-input/32 [&>svg]:text-muted-foreground",
16
+ error:
17
+ "border-destructive/32 bg-destructive/4 [&>svg]:text-destructive",
18
+ info: "border-info/32 bg-info/4 [&>svg]:text-info",
19
+ success: "border-success/32 bg-success/4 [&>svg]:text-success",
20
+ warning: "border-warning/32 bg-warning/4 [&>svg]:text-warning",
21
+ },
22
+ },
23
+ },
24
+ );
25
+
26
+ function Alert({
27
+ className,
28
+ variant,
29
+ ...props
30
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
31
+ return (
32
+ <div
33
+ className={cn(alertVariants({ variant }), className)}
34
+ data-slot="alert"
35
+ role="alert"
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ className={cn("font-medium [svg~&]:col-start-2", className)}
45
+ data-slot="alert-title"
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ function AlertDescription({
52
+ className,
53
+ ...props
54
+ }: React.ComponentProps<"div">) {
55
+ return (
56
+ <div
57
+ className={cn(
58
+ "flex flex-col gap-2.5 text-muted-foreground [svg~&]:col-start-2",
59
+ className,
60
+ )}
61
+ data-slot="alert-description"
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ className={cn(
71
+ "flex gap-1 max-sm:col-start-2 max-sm:mt-2 sm:row-start-1 sm:row-end-3 sm:self-center sm:[[data-slot=alert-description]~&]:col-start-2 sm:[[data-slot=alert-title]~&]:col-start-2 sm:[svg~&]:col-start-2 sm:[svg~[data-slot=alert-description]~&]:col-start-3 sm:[svg~[data-slot=alert-title]~&]:col-start-3",
72
+ className,
73
+ )}
74
+ data-slot="alert-action"
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
@@ -0,0 +1,301 @@
1
+ "use client";
2
+
3
+ import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete";
4
+ import { ChevronsUpDownIcon, XIcon } from "lucide-react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+ import { Input } from "@/components/ui/input";
8
+ import { ScrollArea } from "@/components/ui/scroll-area";
9
+
10
+ const Autocomplete = AutocompletePrimitive.Root;
11
+
12
+ function AutocompleteInput({
13
+ className,
14
+ showTrigger = false,
15
+ showClear = false,
16
+ startAddon,
17
+ size,
18
+ ...props
19
+ }: Omit<AutocompletePrimitive.Input.Props, "size"> & {
20
+ showTrigger?: boolean;
21
+ showClear?: boolean;
22
+ startAddon?: React.ReactNode;
23
+ size?: "sm" | "default" | "lg" | number;
24
+ ref?: React.Ref<HTMLInputElement>;
25
+ }) {
26
+ const sizeValue = (size ?? "default") as "sm" | "default" | "lg" | number;
27
+
28
+ return (
29
+ <div className="relative not-has-[>*.w-full]:w-fit w-full text-foreground has-disabled:opacity-64">
30
+ {startAddon && (
31
+ <div
32
+ aria-hidden="true"
33
+ 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"
34
+ data-slot="autocomplete-start-addon"
35
+ >
36
+ {startAddon}
37
+ </div>
38
+ )}
39
+ <AutocompletePrimitive.Input
40
+ className={cn(
41
+ startAddon &&
42
+ "data-[size=sm]:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(7.5)-1px)] *:data-[slot=autocomplete-input]:ps-[calc(--spacing(8.5)-1px)] sm:data-[size=sm]:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(7)-1px)] sm:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(8)-1px)]",
43
+ sizeValue === "sm"
44
+ ? "has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=autocomplete-input]:pe-6.5"
45
+ : "has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=autocomplete-input]:pe-7",
46
+ className,
47
+ )}
48
+ data-slot="autocomplete-input"
49
+ render={<Input nativeInput size={sizeValue} />}
50
+ {...props}
51
+ />
52
+ {showTrigger && (
53
+ <AutocompleteTrigger
54
+ className={cn(
55
+ "-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-colors pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=autocomplete-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",
56
+ sizeValue === "sm" ? "end-0" : "end-0.5",
57
+ )}
58
+ >
59
+ <ChevronsUpDownIcon />
60
+ </AutocompleteTrigger>
61
+ )}
62
+ {showClear && (
63
+ <AutocompleteClear
64
+ className={cn(
65
+ "-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-colors pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=autocomplete-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",
66
+ sizeValue === "sm" ? "end-0" : "end-0.5",
67
+ )}
68
+ >
69
+ <XIcon />
70
+ </AutocompleteClear>
71
+ )}
72
+ </div>
73
+ );
74
+ }
75
+
76
+ function AutocompletePopup({
77
+ className,
78
+ children,
79
+ sideOffset = 4,
80
+ ...props
81
+ }: AutocompletePrimitive.Popup.Props & {
82
+ sideOffset?: number;
83
+ }) {
84
+ return (
85
+ <AutocompletePrimitive.Portal>
86
+ <AutocompletePrimitive.Positioner
87
+ className="z-50 select-none"
88
+ data-slot="autocomplete-positioner"
89
+ sideOffset={sideOffset}
90
+ >
91
+ <span
92
+ className={cn(
93
+ "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%)]",
94
+ className,
95
+ )}
96
+ >
97
+ <AutocompletePrimitive.Popup
98
+ className="flex max-h-[min(var(--available-height),23rem)] w-(--anchor-width) max-w-(--available-width) flex-col text-foreground"
99
+ data-slot="autocomplete-popup"
100
+ {...props}
101
+ >
102
+ {children}
103
+ </AutocompletePrimitive.Popup>
104
+ </span>
105
+ </AutocompletePrimitive.Positioner>
106
+ </AutocompletePrimitive.Portal>
107
+ );
108
+ }
109
+
110
+ function AutocompleteItem({
111
+ className,
112
+ children,
113
+ ...props
114
+ }: AutocompletePrimitive.Item.Props) {
115
+ return (
116
+ <AutocompletePrimitive.Item
117
+ className={cn(
118
+ "flex min-h-8 cursor-default select-none items-center rounded-sm px-2 py-1 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",
119
+ className,
120
+ )}
121
+ data-slot="autocomplete-item"
122
+ {...props}
123
+ >
124
+ {children}
125
+ </AutocompletePrimitive.Item>
126
+ );
127
+ }
128
+
129
+ function AutocompleteSeparator({
130
+ className,
131
+ ...props
132
+ }: AutocompletePrimitive.Separator.Props) {
133
+ return (
134
+ <AutocompletePrimitive.Separator
135
+ className={cn("mx-2 my-1 h-px bg-border last:hidden", className)}
136
+ data-slot="autocomplete-separator"
137
+ {...props}
138
+ />
139
+ );
140
+ }
141
+
142
+ function AutocompleteGroup({
143
+ className,
144
+ ...props
145
+ }: AutocompletePrimitive.Group.Props) {
146
+ return (
147
+ <AutocompletePrimitive.Group
148
+ className={cn("[[role=group]+&]:mt-1.5", className)}
149
+ data-slot="autocomplete-group"
150
+ {...props}
151
+ />
152
+ );
153
+ }
154
+
155
+ function AutocompleteGroupLabel({
156
+ className,
157
+ ...props
158
+ }: AutocompletePrimitive.GroupLabel.Props) {
159
+ return (
160
+ <AutocompletePrimitive.GroupLabel
161
+ className={cn(
162
+ "px-2 py-1.5 font-medium text-muted-foreground text-xs",
163
+ className,
164
+ )}
165
+ data-slot="autocomplete-group-label"
166
+ {...props}
167
+ />
168
+ );
169
+ }
170
+
171
+ function AutocompleteEmpty({
172
+ className,
173
+ ...props
174
+ }: AutocompletePrimitive.Empty.Props) {
175
+ return (
176
+ <AutocompletePrimitive.Empty
177
+ className={cn(
178
+ "not-empty:p-2 text-center text-base text-muted-foreground sm:text-sm",
179
+ className,
180
+ )}
181
+ data-slot="autocomplete-empty"
182
+ {...props}
183
+ />
184
+ );
185
+ }
186
+
187
+ function AutocompleteRow({
188
+ className,
189
+ ...props
190
+ }: AutocompletePrimitive.Row.Props) {
191
+ return (
192
+ <AutocompletePrimitive.Row
193
+ className={className}
194
+ data-slot="autocomplete-row"
195
+ {...props}
196
+ />
197
+ );
198
+ }
199
+
200
+ function AutocompleteValue({ ...props }: AutocompletePrimitive.Value.Props) {
201
+ return (
202
+ <AutocompletePrimitive.Value data-slot="autocomplete-value" {...props} />
203
+ );
204
+ }
205
+
206
+ function AutocompleteList({
207
+ className,
208
+ ...props
209
+ }: AutocompletePrimitive.List.Props) {
210
+ return (
211
+ <ScrollArea scrollbarGutter scrollFade>
212
+ <AutocompletePrimitive.List
213
+ className={cn(
214
+ "not-empty:scroll-py-1 not-empty:p-1 in-data-has-overflow-y:pe-3",
215
+ className,
216
+ )}
217
+ data-slot="autocomplete-list"
218
+ {...props}
219
+ />
220
+ </ScrollArea>
221
+ );
222
+ }
223
+
224
+ function AutocompleteClear({
225
+ className,
226
+ ...props
227
+ }: AutocompletePrimitive.Clear.Props) {
228
+ return (
229
+ <AutocompletePrimitive.Clear
230
+ className={cn(
231
+ "-translate-y-1/2 absolute end-0.5 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-[color,background-color,box-shadow,opacity] pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
232
+ className,
233
+ )}
234
+ data-slot="autocomplete-clear"
235
+ {...props}
236
+ >
237
+ <XIcon />
238
+ </AutocompletePrimitive.Clear>
239
+ );
240
+ }
241
+
242
+ function AutocompleteStatus({
243
+ className,
244
+ ...props
245
+ }: AutocompletePrimitive.Status.Props) {
246
+ return (
247
+ <AutocompletePrimitive.Status
248
+ className={cn(
249
+ "px-3 py-2 font-medium text-muted-foreground text-xs empty:m-0 empty:p-0",
250
+ className,
251
+ )}
252
+ data-slot="autocomplete-status"
253
+ {...props}
254
+ />
255
+ );
256
+ }
257
+
258
+ function AutocompleteCollection({
259
+ ...props
260
+ }: AutocompletePrimitive.Collection.Props) {
261
+ return (
262
+ <AutocompletePrimitive.Collection
263
+ data-slot="autocomplete-collection"
264
+ {...props}
265
+ />
266
+ );
267
+ }
268
+
269
+ function AutocompleteTrigger({
270
+ className,
271
+ ...props
272
+ }: AutocompletePrimitive.Trigger.Props) {
273
+ return (
274
+ <AutocompletePrimitive.Trigger
275
+ className={className}
276
+ data-slot="autocomplete-trigger"
277
+ {...props}
278
+ />
279
+ );
280
+ }
281
+
282
+ const useAutocompleteFilter = AutocompletePrimitive.useFilter;
283
+
284
+ export {
285
+ Autocomplete,
286
+ AutocompleteInput,
287
+ AutocompleteTrigger,
288
+ AutocompletePopup,
289
+ AutocompleteItem,
290
+ AutocompleteSeparator,
291
+ AutocompleteGroup,
292
+ AutocompleteGroupLabel,
293
+ AutocompleteEmpty,
294
+ AutocompleteValue,
295
+ AutocompleteList,
296
+ AutocompleteClear,
297
+ AutocompleteStatus,
298
+ AutocompleteRow,
299
+ AutocompleteCollection,
300
+ useAutocompleteFilter,
301
+ };