@oppulence/design-system 1.0.2

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 (80) hide show
  1. package/README.md +115 -0
  2. package/components.json +21 -0
  3. package/hooks/use-mobile.tsx +21 -0
  4. package/lib/utils.ts +6 -0
  5. package/package.json +104 -0
  6. package/postcss.config.mjs +8 -0
  7. package/src/components/atoms/aspect-ratio.tsx +21 -0
  8. package/src/components/atoms/avatar.tsx +91 -0
  9. package/src/components/atoms/badge.tsx +47 -0
  10. package/src/components/atoms/button.tsx +128 -0
  11. package/src/components/atoms/checkbox.tsx +24 -0
  12. package/src/components/atoms/container.tsx +42 -0
  13. package/src/components/atoms/heading.tsx +56 -0
  14. package/src/components/atoms/index.ts +21 -0
  15. package/src/components/atoms/input.tsx +18 -0
  16. package/src/components/atoms/kbd.tsx +23 -0
  17. package/src/components/atoms/label.tsx +15 -0
  18. package/src/components/atoms/logo.tsx +52 -0
  19. package/src/components/atoms/progress.tsx +79 -0
  20. package/src/components/atoms/separator.tsx +17 -0
  21. package/src/components/atoms/skeleton.tsx +13 -0
  22. package/src/components/atoms/slider.tsx +56 -0
  23. package/src/components/atoms/spinner.tsx +14 -0
  24. package/src/components/atoms/stack.tsx +126 -0
  25. package/src/components/atoms/switch.tsx +26 -0
  26. package/src/components/atoms/text.tsx +69 -0
  27. package/src/components/atoms/textarea.tsx +19 -0
  28. package/src/components/atoms/toggle.tsx +40 -0
  29. package/src/components/molecules/accordion.tsx +72 -0
  30. package/src/components/molecules/ai-chat.tsx +251 -0
  31. package/src/components/molecules/alert.tsx +131 -0
  32. package/src/components/molecules/breadcrumb.tsx +301 -0
  33. package/src/components/molecules/button-group.tsx +96 -0
  34. package/src/components/molecules/card.tsx +184 -0
  35. package/src/components/molecules/collapsible.tsx +21 -0
  36. package/src/components/molecules/command-search.tsx +148 -0
  37. package/src/components/molecules/empty.tsx +98 -0
  38. package/src/components/molecules/field.tsx +217 -0
  39. package/src/components/molecules/grid.tsx +141 -0
  40. package/src/components/molecules/hover-card.tsx +45 -0
  41. package/src/components/molecules/index.ts +29 -0
  42. package/src/components/molecules/input-group.tsx +151 -0
  43. package/src/components/molecules/input-otp.tsx +74 -0
  44. package/src/components/molecules/item.tsx +194 -0
  45. package/src/components/molecules/page-header.tsx +89 -0
  46. package/src/components/molecules/pagination.tsx +130 -0
  47. package/src/components/molecules/popover.tsx +96 -0
  48. package/src/components/molecules/radio-group.tsx +37 -0
  49. package/src/components/molecules/resizable.tsx +52 -0
  50. package/src/components/molecules/scroll-area.tsx +45 -0
  51. package/src/components/molecules/section.tsx +108 -0
  52. package/src/components/molecules/select.tsx +201 -0
  53. package/src/components/molecules/settings.tsx +197 -0
  54. package/src/components/molecules/table.tsx +111 -0
  55. package/src/components/molecules/tabs.tsx +74 -0
  56. package/src/components/molecules/theme-switcher.tsx +187 -0
  57. package/src/components/molecules/toggle-group.tsx +89 -0
  58. package/src/components/molecules/tooltip.tsx +66 -0
  59. package/src/components/organisms/alert-dialog.tsx +152 -0
  60. package/src/components/organisms/app-shell.tsx +939 -0
  61. package/src/components/organisms/calendar.tsx +212 -0
  62. package/src/components/organisms/carousel.tsx +230 -0
  63. package/src/components/organisms/chart.tsx +333 -0
  64. package/src/components/organisms/combobox.tsx +274 -0
  65. package/src/components/organisms/command.tsx +200 -0
  66. package/src/components/organisms/context-menu.tsx +229 -0
  67. package/src/components/organisms/dialog.tsx +134 -0
  68. package/src/components/organisms/drawer.tsx +123 -0
  69. package/src/components/organisms/dropdown-menu.tsx +256 -0
  70. package/src/components/organisms/index.ts +17 -0
  71. package/src/components/organisms/menubar.tsx +203 -0
  72. package/src/components/organisms/navigation-menu.tsx +143 -0
  73. package/src/components/organisms/page-layout.tsx +105 -0
  74. package/src/components/organisms/sheet.tsx +126 -0
  75. package/src/components/organisms/sidebar.tsx +723 -0
  76. package/src/components/organisms/sonner.tsx +41 -0
  77. package/src/components/ui/index.ts +3 -0
  78. package/src/index.ts +3 -0
  79. package/src/styles/globals.css +297 -0
  80. package/tailwind.config.ts +77 -0
@@ -0,0 +1,333 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as RechartsPrimitive from "recharts";
5
+
6
+ import { cn } from "../../../lib/utils";
7
+
8
+ // Format: { THEME_NAME: CSS_SELECTOR }
9
+ const THEMES = { light: "", dark: ".dark" } as const;
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode;
14
+ icon?: React.ComponentType;
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ );
19
+ };
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig;
23
+ };
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null);
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext);
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />");
32
+ }
33
+
34
+ return context;
35
+ }
36
+
37
+ function ChartContainer({
38
+ id,
39
+ children,
40
+ config,
41
+ ...props
42
+ }: Omit<React.ComponentProps<"div">, "className"> & {
43
+ config: ChartConfig;
44
+ children: React.ComponentProps<
45
+ typeof RechartsPrimitive.ResponsiveContainer
46
+ >["children"];
47
+ }) {
48
+ const uniqueId = React.useId();
49
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
50
+
51
+ return (
52
+ <ChartContext.Provider value={{ config }}>
53
+ <div
54
+ data-slot="chart"
55
+ data-chart={chartId}
56
+ className="[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden"
57
+ {...props}
58
+ >
59
+ <ChartStyle id={chartId} config={config} />
60
+ <RechartsPrimitive.ResponsiveContainer>
61
+ {children}
62
+ </RechartsPrimitive.ResponsiveContainer>
63
+ </div>
64
+ </ChartContext.Provider>
65
+ );
66
+ }
67
+
68
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
69
+ const colorConfig = Object.entries(config).filter(
70
+ ([, config]) => config.theme || config.color,
71
+ );
72
+
73
+ if (!colorConfig.length) {
74
+ return null;
75
+ }
76
+
77
+ return (
78
+ <style
79
+ dangerouslySetInnerHTML={{
80
+ __html: Object.entries(THEMES)
81
+ .map(
82
+ ([theme, prefix]) => `
83
+ ${prefix} [data-chart=${id}] {
84
+ ${colorConfig
85
+ .map(([key, itemConfig]) => {
86
+ const color =
87
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
88
+ itemConfig.color;
89
+ return color ? ` --color-${key}: ${color};` : null;
90
+ })
91
+ .join("\n")}
92
+ }
93
+ `,
94
+ )
95
+ .join("\n"),
96
+ }}
97
+ />
98
+ );
99
+ };
100
+
101
+ const ChartTooltip = RechartsPrimitive.Tooltip;
102
+
103
+ function ChartTooltipContent({
104
+ active,
105
+ payload,
106
+ indicator = "dot",
107
+ hideLabel = false,
108
+ hideIndicator = false,
109
+ label,
110
+ labelFormatter,
111
+ formatter,
112
+ color,
113
+ nameKey,
114
+ labelKey,
115
+ }: Omit<React.ComponentProps<typeof RechartsPrimitive.Tooltip>, "className"> &
116
+ Omit<React.ComponentProps<"div">, "className"> & {
117
+ hideLabel?: boolean;
118
+ hideIndicator?: boolean;
119
+ indicator?: "line" | "dot" | "dashed";
120
+ nameKey?: string;
121
+ labelKey?: string;
122
+ }) {
123
+ const { config } = useChart();
124
+
125
+ const tooltipLabel = React.useMemo(() => {
126
+ if (hideLabel || !payload?.length) {
127
+ return null;
128
+ }
129
+
130
+ const [item] = payload;
131
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
132
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
133
+ const value =
134
+ !labelKey && typeof label === "string"
135
+ ? config[label as keyof typeof config]?.label || label
136
+ : itemConfig?.label;
137
+
138
+ if (labelFormatter) {
139
+ return (
140
+ <div className="font-medium">{labelFormatter(value, payload)}</div>
141
+ );
142
+ }
143
+
144
+ if (!value) {
145
+ return null;
146
+ }
147
+
148
+ return <div className="font-medium">{value}</div>;
149
+ }, [label, labelFormatter, payload, hideLabel, config, labelKey]);
150
+
151
+ if (!active || !payload?.length) {
152
+ return null;
153
+ }
154
+
155
+ const nestLabel = payload.length === 1 && indicator !== "dot";
156
+
157
+ return (
158
+ <div className="border-border/50 bg-background gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl grid min-w-[8rem] items-start">
159
+ {!nestLabel ? tooltipLabel : null}
160
+ <div className="grid gap-1.5">
161
+ {payload
162
+ .filter((item) => item.type !== "none")
163
+ .map((item, index) => {
164
+ const key = `${nameKey || item.name || item.dataKey || "value"}`;
165
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
166
+ const indicatorColor = color || item.payload.fill || item.color;
167
+
168
+ return (
169
+ <div
170
+ key={item.dataKey}
171
+ className={cn(
172
+ "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
173
+ indicator === "dot" && "items-center",
174
+ )}
175
+ >
176
+ {formatter && item?.value !== undefined && item.name ? (
177
+ formatter(item.value, item.name, item, index, item.payload)
178
+ ) : (
179
+ <>
180
+ {itemConfig?.icon ? (
181
+ <itemConfig.icon />
182
+ ) : (
183
+ !hideIndicator && (
184
+ <div
185
+ className={cn(
186
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
187
+ {
188
+ "h-2.5 w-2.5": indicator === "dot",
189
+ "w-1": indicator === "line",
190
+ "w-0 border-[1.5px] border-dashed bg-transparent":
191
+ indicator === "dashed",
192
+ "my-0.5": nestLabel && indicator === "dashed",
193
+ },
194
+ )}
195
+ style={
196
+ {
197
+ "--color-bg": indicatorColor,
198
+ "--color-border": indicatorColor,
199
+ } as React.CSSProperties
200
+ }
201
+ />
202
+ )
203
+ )}
204
+ <div
205
+ className={cn(
206
+ "flex flex-1 justify-between leading-none",
207
+ nestLabel ? "items-end" : "items-center",
208
+ )}
209
+ >
210
+ <div className="grid gap-1.5">
211
+ {nestLabel ? tooltipLabel : null}
212
+ <span className="text-muted-foreground">
213
+ {itemConfig?.label || item.name}
214
+ </span>
215
+ </div>
216
+ {item.value && (
217
+ <span className="text-foreground font-mono font-medium tabular-nums">
218
+ {item.value.toLocaleString()}
219
+ </span>
220
+ )}
221
+ </div>
222
+ </>
223
+ )}
224
+ </div>
225
+ );
226
+ })}
227
+ </div>
228
+ </div>
229
+ );
230
+ }
231
+
232
+ const ChartLegend = RechartsPrimitive.Legend;
233
+
234
+ function ChartLegendContent({
235
+ hideIcon = false,
236
+ payload,
237
+ verticalAlign = "bottom",
238
+ nameKey,
239
+ }: Omit<React.ComponentProps<"div">, "className"> &
240
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
241
+ hideIcon?: boolean;
242
+ nameKey?: string;
243
+ }) {
244
+ const { config } = useChart();
245
+
246
+ if (!payload?.length) {
247
+ return null;
248
+ }
249
+
250
+ return (
251
+ <div
252
+ className={cn(
253
+ "flex items-center justify-center gap-4",
254
+ verticalAlign === "top" ? "pb-3" : "pt-3",
255
+ )}
256
+ >
257
+ {payload
258
+ .filter((item) => item.type !== "none")
259
+ .map((item) => {
260
+ const key = `${nameKey || item.dataKey || "value"}`;
261
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
262
+
263
+ return (
264
+ <div
265
+ key={item.value}
266
+ className={cn(
267
+ "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
268
+ )}
269
+ >
270
+ {itemConfig?.icon && !hideIcon ? (
271
+ <itemConfig.icon />
272
+ ) : (
273
+ <div
274
+ className="h-2 w-2 shrink-0 rounded-[2px]"
275
+ style={{
276
+ backgroundColor: item.color,
277
+ }}
278
+ />
279
+ )}
280
+ {itemConfig?.label}
281
+ </div>
282
+ );
283
+ })}
284
+ </div>
285
+ );
286
+ }
287
+
288
+ function getPayloadConfigFromPayload(
289
+ config: ChartConfig,
290
+ payload: unknown,
291
+ key: string,
292
+ ) {
293
+ if (typeof payload !== "object" || payload === null) {
294
+ return undefined;
295
+ }
296
+
297
+ const payloadPayload =
298
+ "payload" in payload &&
299
+ typeof payload.payload === "object" &&
300
+ payload.payload !== null
301
+ ? payload.payload
302
+ : undefined;
303
+
304
+ let configLabelKey: string = key;
305
+
306
+ if (
307
+ key in payload &&
308
+ typeof payload[key as keyof typeof payload] === "string"
309
+ ) {
310
+ configLabelKey = payload[key as keyof typeof payload] as string;
311
+ } else if (
312
+ payloadPayload &&
313
+ key in payloadPayload &&
314
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
315
+ ) {
316
+ configLabelKey = payloadPayload[
317
+ key as keyof typeof payloadPayload
318
+ ] as string;
319
+ }
320
+
321
+ return configLabelKey in config
322
+ ? config[configLabelKey]
323
+ : config[key as keyof typeof config];
324
+ }
325
+
326
+ export {
327
+ ChartContainer,
328
+ ChartLegend,
329
+ ChartLegendContent,
330
+ ChartStyle,
331
+ ChartTooltip,
332
+ ChartTooltipContent,
333
+ };
@@ -0,0 +1,274 @@
1
+ "use client";
2
+
3
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react";
4
+ import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { Button } from "../atoms/button";
8
+ import {
9
+ InputGroup,
10
+ InputGroupAddon,
11
+ InputGroupButton,
12
+ InputGroupInput,
13
+ } from "../molecules/input-group";
14
+
15
+ const Combobox = ComboboxPrimitive.Root;
16
+
17
+ function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
18
+ return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;
19
+ }
20
+
21
+ function ComboboxTrigger({
22
+ children,
23
+ ...props
24
+ }: Omit<ComboboxPrimitive.Trigger.Props, "className">) {
25
+ return (
26
+ <ComboboxPrimitive.Trigger
27
+ data-slot="combobox-trigger"
28
+ className="[&_svg:not([class*='size-'])]:size-4"
29
+ {...props}
30
+ >
31
+ {children}
32
+ <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
33
+ </ComboboxPrimitive.Trigger>
34
+ );
35
+ }
36
+
37
+ function ComboboxClear({
38
+ ...props
39
+ }: Omit<ComboboxPrimitive.Clear.Props, "className">) {
40
+ return (
41
+ <ComboboxPrimitive.Clear
42
+ data-slot="combobox-clear"
43
+ render={<InputGroupButton variant="ghost" size="icon-xs" />}
44
+ {...props}
45
+ >
46
+ <XIcon className="pointer-events-none" />
47
+ </ComboboxPrimitive.Clear>
48
+ );
49
+ }
50
+
51
+ function ComboboxInput({
52
+ children,
53
+ disabled = false,
54
+ showTrigger = true,
55
+ showClear = false,
56
+ ...props
57
+ }: Omit<ComboboxPrimitive.Input.Props, "className"> & {
58
+ showTrigger?: boolean;
59
+ showClear?: boolean;
60
+ }) {
61
+ return (
62
+ <InputGroup>
63
+ <ComboboxPrimitive.Input
64
+ render={<InputGroupInput disabled={disabled} />}
65
+ {...props}
66
+ />
67
+ <InputGroupAddon align="inline-end">
68
+ {showTrigger && (
69
+ <InputGroupButton
70
+ size="icon-xs"
71
+ variant="ghost"
72
+ render={<ComboboxTrigger />}
73
+ data-slot="input-group-button"
74
+ disabled={disabled}
75
+ />
76
+ )}
77
+ {showClear && <ComboboxClear disabled={disabled} />}
78
+ </InputGroupAddon>
79
+ {children}
80
+ </InputGroup>
81
+ );
82
+ }
83
+
84
+ function ComboboxContent({
85
+ side = "bottom",
86
+ sideOffset = 6,
87
+ align = "start",
88
+ alignOffset = 0,
89
+ anchor,
90
+ ...props
91
+ }: Omit<ComboboxPrimitive.Popup.Props, "className"> &
92
+ Pick<
93
+ ComboboxPrimitive.Positioner.Props,
94
+ "side" | "align" | "sideOffset" | "alignOffset" | "anchor"
95
+ >) {
96
+ return (
97
+ <ComboboxPrimitive.Portal>
98
+ <ComboboxPrimitive.Positioner
99
+ side={side}
100
+ sideOffset={sideOffset}
101
+ align={align}
102
+ alignOffset={alignOffset}
103
+ anchor={anchor}
104
+ className="isolate z-50"
105
+ >
106
+ <ComboboxPrimitive.Popup
107
+ data-slot="combobox-content"
108
+ data-chips={!!anchor}
109
+ className="bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:border-input/30 max-h-72 min-w-36 overflow-hidden rounded-lg shadow-md ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:shadow-none group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)"
110
+ {...props}
111
+ />
112
+ </ComboboxPrimitive.Positioner>
113
+ </ComboboxPrimitive.Portal>
114
+ );
115
+ }
116
+
117
+ function ComboboxList({
118
+ ...props
119
+ }: Omit<ComboboxPrimitive.List.Props, "className">) {
120
+ return (
121
+ <ComboboxPrimitive.List
122
+ data-slot="combobox-list"
123
+ className="no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overscroll-contain"
124
+ {...props}
125
+ />
126
+ );
127
+ }
128
+
129
+ function ComboboxItem({
130
+ children,
131
+ ...props
132
+ }: Omit<ComboboxPrimitive.Item.Props, "className">) {
133
+ return (
134
+ <ComboboxPrimitive.Item
135
+ data-slot="combobox-item"
136
+ className="data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0"
137
+ {...props}
138
+ >
139
+ {children}
140
+ <ComboboxPrimitive.ItemIndicator
141
+ render={
142
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
143
+ }
144
+ >
145
+ <CheckIcon className="pointer-events-none" />
146
+ </ComboboxPrimitive.ItemIndicator>
147
+ </ComboboxPrimitive.Item>
148
+ );
149
+ }
150
+
151
+ function ComboboxGroup({ ...props }: ComboboxPrimitive.Group.Props) {
152
+ return <ComboboxPrimitive.Group data-slot="combobox-group" {...props} />;
153
+ }
154
+
155
+ function ComboboxLabel({
156
+ ...props
157
+ }: Omit<ComboboxPrimitive.GroupLabel.Props, "className">) {
158
+ return (
159
+ <ComboboxPrimitive.GroupLabel
160
+ data-slot="combobox-label"
161
+ className="text-muted-foreground px-1.5 py-1 text-xs"
162
+ {...props}
163
+ />
164
+ );
165
+ }
166
+
167
+ function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
168
+ return (
169
+ <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
170
+ );
171
+ }
172
+
173
+ function ComboboxEmpty({
174
+ ...props
175
+ }: Omit<ComboboxPrimitive.Empty.Props, "className">) {
176
+ return (
177
+ <ComboboxPrimitive.Empty
178
+ data-slot="combobox-empty"
179
+ className="text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex"
180
+ {...props}
181
+ />
182
+ );
183
+ }
184
+
185
+ function ComboboxSeparator({
186
+ ...props
187
+ }: Omit<ComboboxPrimitive.Separator.Props, "className">) {
188
+ return (
189
+ <ComboboxPrimitive.Separator
190
+ data-slot="combobox-separator"
191
+ className="bg-border -mx-1 my-1 h-px"
192
+ {...props}
193
+ />
194
+ );
195
+ }
196
+
197
+ function ComboboxChips({
198
+ ...props
199
+ }: Omit<
200
+ React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
201
+ ComboboxPrimitive.Chips.Props,
202
+ "className"
203
+ >) {
204
+ return (
205
+ <ComboboxPrimitive.Chips
206
+ data-slot="combobox-chips"
207
+ className="dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-8 flex-wrap items-center gap-1 rounded-lg border bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1"
208
+ {...props}
209
+ />
210
+ );
211
+ }
212
+
213
+ function ComboboxChip({
214
+ children,
215
+ showRemove = true,
216
+ ...props
217
+ }: Omit<ComboboxPrimitive.Chip.Props, "className"> & {
218
+ showRemove?: boolean;
219
+ }) {
220
+ return (
221
+ <ComboboxPrimitive.Chip
222
+ data-slot="combobox-chip"
223
+ className="bg-muted text-foreground flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm px-1.5 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50"
224
+ {...props}
225
+ >
226
+ {children}
227
+ {showRemove && (
228
+ <ComboboxPrimitive.ChipRemove
229
+ render={<Button variant="ghost" size="icon-xs" />}
230
+ className="-ml-1 opacity-50 hover:opacity-100"
231
+ data-slot="combobox-chip-remove"
232
+ >
233
+ <XIcon className="pointer-events-none" />
234
+ </ComboboxPrimitive.ChipRemove>
235
+ )}
236
+ </ComboboxPrimitive.Chip>
237
+ );
238
+ }
239
+
240
+ function ComboboxChipsInput({
241
+ ...props
242
+ }: Omit<ComboboxPrimitive.Input.Props, "className">) {
243
+ return (
244
+ <ComboboxPrimitive.Input
245
+ data-slot="combobox-chip-input"
246
+ className="min-w-16 flex-1 outline-none"
247
+ {...props}
248
+ />
249
+ );
250
+ }
251
+
252
+ function useComboboxAnchor() {
253
+ return React.useRef<HTMLDivElement | null>(null);
254
+ }
255
+
256
+ export {
257
+ Combobox,
258
+ ComboboxChip,
259
+ ComboboxChips,
260
+ ComboboxChipsInput,
261
+ ComboboxClear,
262
+ ComboboxCollection,
263
+ ComboboxContent,
264
+ ComboboxEmpty,
265
+ ComboboxGroup,
266
+ ComboboxInput,
267
+ ComboboxItem,
268
+ ComboboxLabel,
269
+ ComboboxList,
270
+ ComboboxSeparator,
271
+ ComboboxTrigger,
272
+ ComboboxValue,
273
+ useComboboxAnchor,
274
+ };