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