@adea-ai/ui 0.10.8

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 (40) hide show
  1. package/README.md +5 -0
  2. package/components.json +19 -0
  3. package/package.json +56 -0
  4. package/src/components/account-drawer.tsx +133 -0
  5. package/src/components/on-screen-controls.tsx +156 -0
  6. package/src/components/prop-catalog.tsx +583 -0
  7. package/src/components/scene-settings.tsx +202 -0
  8. package/src/components/theme-provider.tsx +30 -0
  9. package/src/components/theme-toggle.tsx +73 -0
  10. package/src/components/ui/badge.tsx +49 -0
  11. package/src/components/ui/button.tsx +60 -0
  12. package/src/components/ui/card.tsx +18 -0
  13. package/src/components/ui/dialog.tsx +129 -0
  14. package/src/components/ui/drawer.tsx +209 -0
  15. package/src/components/ui/dropdown-menu.tsx +258 -0
  16. package/src/components/ui/empty.tsx +94 -0
  17. package/src/components/ui/field.tsx +224 -0
  18. package/src/components/ui/input.tsx +20 -0
  19. package/src/components/ui/label.tsx +20 -0
  20. package/src/components/ui/radio-group.tsx +38 -0
  21. package/src/components/ui/separator.tsx +21 -0
  22. package/src/components/ui/skeleton.tsx +13 -0
  23. package/src/components/ui/spinner.tsx +16 -0
  24. package/src/components/ui/switch.tsx +32 -0
  25. package/src/components/ui/tabs.tsx +75 -0
  26. package/src/components/ui/toggle-group.tsx +87 -0
  27. package/src/components/ui/toggle.tsx +45 -0
  28. package/src/components/ui/tooltip.tsx +56 -0
  29. package/src/components/version-dialog.tsx +366 -0
  30. package/src/components/workspace-brand.tsx +18 -0
  31. package/src/components/workspace-logo.tsx +17 -0
  32. package/src/index.tsx +55 -0
  33. package/src/lib/utils.ts +6 -0
  34. package/src/lib/version-notes.ts +30 -0
  35. package/src/styles/auth-shell.css +67 -0
  36. package/src/styles/conventional-workspace.css +2846 -0
  37. package/src/styles/globals.css +93 -0
  38. package/src/styles/theme.css +94 -0
  39. package/src/styles/workspace-shell.css +145 -0
  40. package/tsconfig.json +10 -0
@@ -0,0 +1,224 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ import { cn } from "#lib/utils";
7
+ import { Label } from "#components/ui/label";
8
+ import { Separator } from "#components/ui/separator";
9
+
10
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
11
+ return (
12
+ <fieldset
13
+ data-slot="field-set"
14
+ className={cn(
15
+ "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ function FieldLegend({
24
+ className,
25
+ variant = "legend",
26
+ ...props
27
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
28
+ return (
29
+ <legend
30
+ data-slot="field-legend"
31
+ data-variant={variant}
32
+ className={cn(
33
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="field-group"
45
+ className={cn(
46
+ "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+
54
+ const fieldVariants = cva("group/field flex w-full gap-2 data-[invalid=true]:text-destructive", {
55
+ variants: {
56
+ orientation: {
57
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
58
+ horizontal:
59
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
60
+ responsive:
61
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
62
+ },
63
+ },
64
+ defaultVariants: {
65
+ orientation: "vertical",
66
+ },
67
+ });
68
+
69
+ function Field({
70
+ className,
71
+ orientation = "vertical",
72
+ ...props
73
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
74
+ return (
75
+ <div
76
+ role="group"
77
+ data-slot="field"
78
+ data-orientation={orientation}
79
+ className={cn(fieldVariants({ orientation }), className)}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
86
+ return (
87
+ <div
88
+ data-slot="field-content"
89
+ className={cn("group/field-content flex flex-1 flex-col gap-0.5 leading-snug", className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
96
+ return (
97
+ <Label
98
+ data-slot="field-label"
99
+ className={cn(
100
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
101
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
102
+ className
103
+ )}
104
+ {...props}
105
+ />
106
+ );
107
+ }
108
+
109
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
110
+ return (
111
+ <div
112
+ data-slot="field-label"
113
+ className={cn(
114
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
115
+ className
116
+ )}
117
+ {...props}
118
+ />
119
+ );
120
+ }
121
+
122
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
123
+ return (
124
+ <p
125
+ data-slot="field-description"
126
+ className={cn(
127
+ "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
128
+ "last:mt-0 nth-last-2:-mt-1",
129
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
130
+ className
131
+ )}
132
+ {...props}
133
+ />
134
+ );
135
+ }
136
+
137
+ function FieldSeparator({
138
+ children,
139
+ className,
140
+ ...props
141
+ }: React.ComponentProps<"div"> & {
142
+ children?: React.ReactNode;
143
+ }) {
144
+ return (
145
+ <div
146
+ data-slot="field-separator"
147
+ data-content={!!children}
148
+ className={cn(
149
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
150
+ className
151
+ )}
152
+ {...props}
153
+ >
154
+ <Separator className="absolute inset-0 top-1/2" />
155
+ {children && (
156
+ <span
157
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
158
+ data-slot="field-separator-content"
159
+ >
160
+ {children}
161
+ </span>
162
+ )}
163
+ </div>
164
+ );
165
+ }
166
+
167
+ function FieldError({
168
+ className,
169
+ children,
170
+ errors,
171
+ ...props
172
+ }: React.ComponentProps<"div"> & {
173
+ errors?: Array<{ message?: string } | undefined>;
174
+ }) {
175
+ const content = useMemo(() => {
176
+ if (children) {
177
+ return children;
178
+ }
179
+
180
+ if (!errors?.length) {
181
+ return null;
182
+ }
183
+
184
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
185
+
186
+ if (uniqueErrors?.length == 1) {
187
+ return uniqueErrors[0]?.message;
188
+ }
189
+
190
+ return (
191
+ <ul className="ml-4 flex list-disc flex-col gap-1">
192
+ {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
193
+ </ul>
194
+ );
195
+ }, [children, errors]);
196
+
197
+ if (!content) {
198
+ return null;
199
+ }
200
+
201
+ return (
202
+ <div
203
+ role="alert"
204
+ data-slot="field-error"
205
+ className={cn("text-sm font-normal text-destructive", className)}
206
+ {...props}
207
+ >
208
+ {content}
209
+ </div>
210
+ );
211
+ }
212
+
213
+ export {
214
+ Field,
215
+ FieldLabel,
216
+ FieldDescription,
217
+ FieldError,
218
+ FieldGroup,
219
+ FieldLegend,
220
+ FieldSeparator,
221
+ FieldSet,
222
+ FieldContent,
223
+ FieldTitle,
224
+ };
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import { Input as InputPrimitive } from "@base-ui/react/input";
3
+
4
+ import { cn } from "#lib/utils";
5
+
6
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
7
+ return (
8
+ <InputPrimitive
9
+ type={type}
10
+ data-slot="input"
11
+ className={cn(
12
+ "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ export { Input };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "#lib/utils";
6
+
7
+ function Label({ className, ...props }: React.ComponentProps<"label">) {
8
+ return (
9
+ <label
10
+ data-slot="label"
11
+ className={cn(
12
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ export { Label };
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import { Radio as RadioPrimitive } from "@base-ui/react/radio";
4
+ import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
5
+
6
+ import { cn } from "#lib/utils";
7
+
8
+ function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
9
+ return (
10
+ <RadioGroupPrimitive
11
+ data-slot="radio-group"
12
+ className={cn("grid w-full gap-2", className)}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
19
+ return (
20
+ <RadioPrimitive.Root
21
+ data-slot="radio-group-item"
22
+ className={cn(
23
+ "group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
24
+ className
25
+ )}
26
+ {...props}
27
+ >
28
+ <RadioPrimitive.Indicator
29
+ data-slot="radio-group-indicator"
30
+ className="flex size-4 items-center justify-center"
31
+ >
32
+ <span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
33
+ </RadioPrimitive.Indicator>
34
+ </RadioPrimitive.Root>
35
+ );
36
+ }
37
+
38
+ export { RadioGroup, RadioGroupItem };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
4
+
5
+ import { cn } from "#lib/utils";
6
+
7
+ function Separator({ className, orientation = "horizontal", ...props }: SeparatorPrimitive.Props) {
8
+ return (
9
+ <SeparatorPrimitive
10
+ data-slot="separator"
11
+ orientation={orientation}
12
+ className={cn(
13
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Separator };
@@ -0,0 +1,13 @@
1
+ import { cn } from "#lib/utils";
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-muted", className)}
8
+ {...props}
9
+ />
10
+ );
11
+ }
12
+
13
+ export { Skeleton };
@@ -0,0 +1,16 @@
1
+ import { LoaderCircle } from "lucide-react";
2
+ import type { ComponentProps } from "react";
3
+ import { cn } from "#lib/utils";
4
+
5
+ function Spinner({ className, ...props }: ComponentProps<typeof LoaderCircle>) {
6
+ return (
7
+ <LoaderCircle
8
+ role="status"
9
+ aria-label="Loading"
10
+ className={cn("animate-spin", className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ export { Spinner };
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
4
+
5
+ import { cn } from "#lib/utils";
6
+
7
+ function Switch({
8
+ className,
9
+ size = "default",
10
+ ...props
11
+ }: SwitchPrimitive.Root.Props & {
12
+ size?: "sm" | "default";
13
+ }) {
14
+ return (
15
+ <SwitchPrimitive.Root
16
+ data-slot="switch"
17
+ data-size={size}
18
+ className={cn(
19
+ "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
20
+ className
21
+ )}
22
+ {...props}
23
+ >
24
+ <SwitchPrimitive.Thumb
25
+ data-slot="switch-thumb"
26
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
27
+ />
28
+ </SwitchPrimitive.Root>
29
+ );
30
+ }
31
+
32
+ export { Switch };
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ import { cn } from "#lib/utils";
7
+
8
+ function Tabs({ className, orientation = "horizontal", ...props }: TabsPrimitive.Root.Props) {
9
+ return (
10
+ <TabsPrimitive.Root
11
+ data-slot="tabs"
12
+ data-orientation={orientation}
13
+ className={cn("group/tabs flex gap-2 data-horizontal:flex-col", className)}
14
+ {...props}
15
+ />
16
+ );
17
+ }
18
+
19
+ const tabsListVariants = cva(
20
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
21
+ {
22
+ variants: {
23
+ variant: {
24
+ default: "bg-muted",
25
+ line: "gap-1 bg-transparent",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ },
31
+ }
32
+ );
33
+
34
+ function TabsList({
35
+ className,
36
+ variant = "default",
37
+ ...props
38
+ }: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
39
+ return (
40
+ <TabsPrimitive.List
41
+ data-slot="tabs-list"
42
+ data-variant={variant}
43
+ className={cn(tabsListVariants({ variant }), className)}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
50
+ return (
51
+ <TabsPrimitive.Tab
52
+ data-slot="tabs-trigger"
53
+ className={cn(
54
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
55
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
56
+ "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
57
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
66
+ return (
67
+ <TabsPrimitive.Panel
68
+ data-slot="tabs-content"
69
+ className={cn("flex-1 text-sm outline-none", className)}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
@@ -0,0 +1,87 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
5
+ import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
6
+ import { type VariantProps } from "class-variance-authority";
7
+
8
+ import { cn } from "#lib/utils";
9
+ import { toggleVariants } from "#components/ui/toggle";
10
+
11
+ const ToggleGroupContext = React.createContext<
12
+ VariantProps<typeof toggleVariants> & {
13
+ spacing?: number;
14
+ orientation?: "horizontal" | "vertical";
15
+ }
16
+ >({
17
+ size: "default",
18
+ variant: "default",
19
+ spacing: 2,
20
+ orientation: "horizontal",
21
+ });
22
+
23
+ function ToggleGroup({
24
+ className,
25
+ variant,
26
+ size,
27
+ spacing = 2,
28
+ orientation = "horizontal",
29
+ children,
30
+ ...props
31
+ }: ToggleGroupPrimitive.Props &
32
+ VariantProps<typeof toggleVariants> & {
33
+ spacing?: number;
34
+ orientation?: "horizontal" | "vertical";
35
+ }) {
36
+ return (
37
+ <ToggleGroupPrimitive
38
+ data-slot="toggle-group"
39
+ data-variant={variant}
40
+ data-size={size}
41
+ data-spacing={spacing}
42
+ data-orientation={orientation}
43
+ style={{ "--gap": spacing } as React.CSSProperties}
44
+ className={cn(
45
+ "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch",
46
+ className
47
+ )}
48
+ {...props}
49
+ >
50
+ <ToggleGroupContext.Provider value={{ variant, size, spacing, orientation }}>
51
+ {children}
52
+ </ToggleGroupContext.Provider>
53
+ </ToggleGroupPrimitive>
54
+ );
55
+ }
56
+
57
+ function ToggleGroupItem({
58
+ className,
59
+ children,
60
+ variant = "default",
61
+ size = "default",
62
+ ...props
63
+ }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
64
+ const context = React.useContext(ToggleGroupContext);
65
+
66
+ return (
67
+ <TogglePrimitive
68
+ data-slot="toggle-group-item"
69
+ data-variant={context.variant || variant}
70
+ data-size={context.size || size}
71
+ data-spacing={context.spacing}
72
+ className={cn(
73
+ "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
74
+ toggleVariants({
75
+ variant: context.variant || variant,
76
+ size: context.size || size,
77
+ }),
78
+ className
79
+ )}
80
+ {...props}
81
+ >
82
+ {children}
83
+ </TogglePrimitive>
84
+ );
85
+ }
86
+
87
+ export { ToggleGroup, ToggleGroupItem };
@@ -0,0 +1,45 @@
1
+ "use client";
2
+
3
+ import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ import { cn } from "#lib/utils";
7
+
8
+ const toggleVariants = cva(
9
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "bg-transparent",
14
+ outline: "border border-input bg-transparent hover:bg-muted",
15
+ },
16
+ size: {
17
+ default:
18
+ "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
19
+ sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
20
+ lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ size: "default",
26
+ },
27
+ }
28
+ );
29
+
30
+ function Toggle({
31
+ className,
32
+ variant = "default",
33
+ size = "default",
34
+ ...props
35
+ }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
36
+ return (
37
+ <TogglePrimitive
38
+ data-slot="toggle"
39
+ className={cn(toggleVariants({ variant, size, className }))}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+
45
+ export { Toggle, toggleVariants };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
5
+
6
+ import { cn } from "#lib/utils";
7
+
8
+ const TOOLTIP_DELAY = 200;
9
+
10
+ function TooltipProvider({ delay = TOOLTIP_DELAY, ...props }: TooltipPrimitive.Provider.Props) {
11
+ return <TooltipPrimitive.Provider data-slot="tooltip-provider" delay={delay} {...props} />;
12
+ }
13
+
14
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
15
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
16
+ }
17
+
18
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
19
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
20
+ }
21
+
22
+ function TooltipContent({
23
+ className,
24
+ side = "top",
25
+ sideOffset = 4,
26
+ align,
27
+ alignOffset,
28
+ children,
29
+ ...props
30
+ }: TooltipPrimitive.Popup.Props &
31
+ Pick<TooltipPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) {
32
+ return (
33
+ <TooltipPrimitive.Portal>
34
+ <TooltipPrimitive.Positioner
35
+ className="isolate z-[160] outline-none"
36
+ align={align}
37
+ alignOffset={alignOffset}
38
+ side={side}
39
+ sideOffset={sideOffset}
40
+ >
41
+ <TooltipPrimitive.Popup
42
+ data-slot="tooltip-content"
43
+ className={cn(
44
+ "max-w-72 rounded-md bg-popover px-2.5 py-1.5 text-sm leading-5 text-popover-foreground shadow-md ring-1 ring-foreground/10",
45
+ className
46
+ )}
47
+ {...props}
48
+ >
49
+ {children}
50
+ </TooltipPrimitive.Popup>
51
+ </TooltipPrimitive.Positioner>
52
+ </TooltipPrimitive.Portal>
53
+ );
54
+ }
55
+
56
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TOOLTIP_DELAY };