@alpic-ai/ui 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion-card.d.mts +41 -0
- package/dist/components/accordion-card.mjs +61 -0
- package/dist/components/accordion.d.mts +29 -0
- package/dist/components/accordion.mjs +48 -0
- package/dist/components/alert.d.mts +40 -0
- package/dist/components/alert.mjs +63 -0
- package/dist/components/attachment-tile.d.mts +26 -0
- package/dist/components/attachment-tile.mjs +35 -0
- package/dist/components/avatar.d.mts +52 -0
- package/dist/components/avatar.mjs +81 -0
- package/dist/components/badge.d.mts +20 -0
- package/dist/components/badge.mjs +36 -0
- package/dist/components/breadcrumb.d.mts +42 -0
- package/dist/components/breadcrumb.mjs +79 -0
- package/dist/components/button.d.mts +29 -0
- package/dist/components/button.mjs +67 -0
- package/dist/components/card.d.mts +37 -0
- package/dist/components/card.mjs +54 -0
- package/dist/components/checkbox.d.mts +11 -0
- package/dist/components/checkbox.mjs +26 -0
- package/dist/components/collapsible.d.mts +16 -0
- package/dist/components/collapsible.mjs +24 -0
- package/dist/components/combobox.d.mts +86 -0
- package/dist/components/combobox.mjs +207 -0
- package/dist/components/command.d.mts +38 -0
- package/dist/components/command.mjs +68 -0
- package/dist/components/copyable.d.mts +22 -0
- package/dist/components/copyable.mjs +33 -0
- package/dist/components/description-list.d.mts +22 -0
- package/dist/components/description-list.mjs +34 -0
- package/dist/components/dialog.d.mts +61 -0
- package/dist/components/dialog.mjs +110 -0
- package/dist/components/dropdown-menu.d.mts +72 -0
- package/dist/components/dropdown-menu.mjs +122 -0
- package/dist/components/input-group.d.mts +25 -0
- package/dist/components/input-group.mjs +43 -0
- package/dist/components/input.d.mts +27 -0
- package/dist/components/input.mjs +90 -0
- package/dist/components/label.d.mts +11 -0
- package/dist/components/label.mjs +14 -0
- package/dist/components/pagination.d.mts +18 -0
- package/dist/components/pagination.mjs +42 -0
- package/dist/components/popover.d.mts +22 -0
- package/dist/components/popover.mjs +34 -0
- package/dist/components/radio-group.d.mts +15 -0
- package/dist/components/radio-group.mjs +26 -0
- package/dist/components/scroll-area.d.mts +17 -0
- package/dist/components/scroll-area.mjs +35 -0
- package/dist/components/select-trigger-variants.d.mts +13 -0
- package/dist/components/select-trigger-variants.mjs +23 -0
- package/dist/components/select.d.mts +51 -0
- package/dist/components/select.mjs +95 -0
- package/dist/components/separator.d.mts +13 -0
- package/dist/components/separator.mjs +16 -0
- package/dist/components/sheet.d.mts +43 -0
- package/dist/components/sheet.mjs +74 -0
- package/dist/components/sidebar.d.mts +163 -0
- package/dist/components/sidebar.mjs +378 -0
- package/dist/components/skeleton.d.mts +16 -0
- package/dist/components/skeleton.mjs +21 -0
- package/dist/components/sonner.d.mts +29 -0
- package/dist/components/sonner.mjs +76 -0
- package/dist/components/spinner.d.mts +30 -0
- package/dist/components/spinner.mjs +46 -0
- package/dist/components/status-dot.d.mts +19 -0
- package/dist/components/status-dot.mjs +34 -0
- package/dist/components/switch.d.mts +11 -0
- package/dist/components/switch.mjs +18 -0
- package/dist/components/table.d.mts +38 -0
- package/dist/components/table.mjs +65 -0
- package/dist/components/tabs.d.mts +58 -0
- package/dist/components/tabs.mjs +119 -0
- package/dist/components/tag.d.mts +35 -0
- package/dist/components/tag.mjs +65 -0
- package/dist/components/textarea.d.mts +21 -0
- package/dist/components/textarea.mjs +44 -0
- package/dist/components/toggle-group.d.mts +28 -0
- package/dist/components/toggle-group.mjs +72 -0
- package/dist/components/tooltip-icon-button.d.mts +12 -0
- package/dist/components/tooltip-icon-button.mjs +27 -0
- package/dist/components/tooltip.d.mts +23 -0
- package/dist/components/tooltip.mjs +35 -0
- package/dist/hooks/use-copy-to-clipboard.d.mts +11 -0
- package/dist/hooks/use-copy-to-clipboard.mjs +24 -0
- package/dist/hooks/use-mobile.d.mts +4 -0
- package/dist/hooks/use-mobile.mjs +16 -0
- package/dist/lib/cn.d.mts +6 -0
- package/dist/lib/cn.mjs +8 -0
- package/package.json +88 -0
- package/src/components/accordion-card.tsx +103 -0
- package/src/components/accordion.tsx +63 -0
- package/src/components/alert.tsx +74 -0
- package/src/components/attachment-tile.tsx +68 -0
- package/src/components/avatar.tsx +127 -0
- package/src/components/badge.tsx +41 -0
- package/src/components/breadcrumb.tsx +98 -0
- package/src/components/button.tsx +106 -0
- package/src/components/card.tsx +62 -0
- package/src/components/checkbox.tsx +35 -0
- package/src/components/collapsible.tsx +18 -0
- package/src/components/combobox.tsx +393 -0
- package/src/components/command.tsx +112 -0
- package/src/components/copyable.tsx +47 -0
- package/src/components/description-list.tsx +36 -0
- package/src/components/dialog.tsx +161 -0
- package/src/components/dropdown-menu.tsx +234 -0
- package/src/components/input-group.tsx +97 -0
- package/src/components/input.tsx +145 -0
- package/src/components/label.tsx +20 -0
- package/src/components/pagination.tsx +53 -0
- package/src/components/popover.tsx +49 -0
- package/src/components/radio-group.tsx +33 -0
- package/src/components/scroll-area.tsx +48 -0
- package/src/components/select-trigger-variants.ts +28 -0
- package/src/components/select.tsx +186 -0
- package/src/components/separator.tsx +30 -0
- package/src/components/sheet.tsx +112 -0
- package/src/components/sidebar.tsx +682 -0
- package/src/components/skeleton.tsx +24 -0
- package/src/components/sonner.tsx +91 -0
- package/src/components/spinner.tsx +62 -0
- package/src/components/status-dot.tsx +33 -0
- package/src/components/switch.tsx +33 -0
- package/src/components/table.tsx +89 -0
- package/src/components/tabs.tsx +226 -0
- package/src/components/tag.tsx +82 -0
- package/src/components/textarea.tsx +70 -0
- package/src/components/toggle-group.tsx +96 -0
- package/src/components/tooltip-icon-button.tsx +33 -0
- package/src/components/tooltip.tsx +54 -0
- package/src/hooks/use-copy-to-clipboard.ts +27 -0
- package/src/hooks/use-mobile.ts +17 -0
- package/src/lib/cn.ts +6 -0
- package/src/styles/tokens.css +352 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { VariantProps } from "class-variance-authority";
|
|
4
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
5
|
+
import { CheckIcon, ChevronDownIcon, SearchIcon } from "lucide-react";
|
|
6
|
+
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../lib/cn";
|
|
9
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
|
|
10
|
+
import { selectTriggerVariants } from "./select-trigger-variants";
|
|
11
|
+
import { TagDismissible } from "./tag";
|
|
12
|
+
|
|
13
|
+
/* ── Context ──────────────────────────────────────────────────────────────── */
|
|
14
|
+
|
|
15
|
+
interface ComboboxContextValue {
|
|
16
|
+
multiple: boolean;
|
|
17
|
+
value: string | null;
|
|
18
|
+
values: string[];
|
|
19
|
+
onSelect: (itemValue: string) => void;
|
|
20
|
+
onDeselect: (itemValue: string) => void;
|
|
21
|
+
isSelected: (itemValue: string) => boolean;
|
|
22
|
+
open: boolean;
|
|
23
|
+
onOpenChange: (open: boolean) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ComboboxContext = createContext<ComboboxContextValue | null>(null);
|
|
27
|
+
|
|
28
|
+
function useComboboxContext() {
|
|
29
|
+
const context = useContext(ComboboxContext);
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("Combobox compound components must be used within <Combobox>");
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ── Root ─────────────────────────────────────────────────────────────────── */
|
|
37
|
+
|
|
38
|
+
interface ComboboxBaseProps {
|
|
39
|
+
children: ReactNode;
|
|
40
|
+
open?: boolean;
|
|
41
|
+
defaultOpen?: boolean;
|
|
42
|
+
onOpenChange?: (open: boolean) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ComboboxSingleProps extends ComboboxBaseProps {
|
|
46
|
+
multiple?: false;
|
|
47
|
+
value?: string | null;
|
|
48
|
+
defaultValue?: string | null;
|
|
49
|
+
onValueChange?: (value: string | null) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ComboboxMultipleProps extends ComboboxBaseProps {
|
|
53
|
+
multiple: true;
|
|
54
|
+
value?: string[];
|
|
55
|
+
defaultValue?: string[];
|
|
56
|
+
onValueChange?: (value: string[]) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;
|
|
60
|
+
|
|
61
|
+
function Combobox(props: ComboboxProps) {
|
|
62
|
+
const {
|
|
63
|
+
children,
|
|
64
|
+
multiple = false,
|
|
65
|
+
open: controlledOpen,
|
|
66
|
+
defaultOpen = false,
|
|
67
|
+
onOpenChange: controlledOnOpenChange,
|
|
68
|
+
} = props;
|
|
69
|
+
|
|
70
|
+
// Single mode state
|
|
71
|
+
const [uncontrolledSingleValue, setUncontrolledSingleValue] = useState<string | null>(
|
|
72
|
+
!multiple ? ((props as ComboboxSingleProps).defaultValue ?? null) : null,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Multi mode state
|
|
76
|
+
const [uncontrolledMultiValue, setUncontrolledMultiValue] = useState<string[]>(
|
|
77
|
+
multiple ? ((props as ComboboxMultipleProps).defaultValue ?? []) : [],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
81
|
+
|
|
82
|
+
const isOpenControlled = controlledOpen !== undefined;
|
|
83
|
+
const open = isOpenControlled ? controlledOpen : uncontrolledOpen;
|
|
84
|
+
|
|
85
|
+
const onOpenChange = useCallback(
|
|
86
|
+
(newOpen: boolean) => {
|
|
87
|
+
if (!isOpenControlled) setUncontrolledOpen(newOpen);
|
|
88
|
+
controlledOnOpenChange?.(newOpen);
|
|
89
|
+
},
|
|
90
|
+
[isOpenControlled, controlledOnOpenChange],
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Resolve current values
|
|
94
|
+
const singleValue = multiple
|
|
95
|
+
? null
|
|
96
|
+
: (((props as ComboboxSingleProps).value !== undefined
|
|
97
|
+
? (props as ComboboxSingleProps).value
|
|
98
|
+
: uncontrolledSingleValue) ?? null);
|
|
99
|
+
|
|
100
|
+
const multiValues = multiple
|
|
101
|
+
? (props as ComboboxMultipleProps).value !== undefined
|
|
102
|
+
? ((props as ComboboxMultipleProps).value as string[])
|
|
103
|
+
: uncontrolledMultiValue
|
|
104
|
+
: [];
|
|
105
|
+
|
|
106
|
+
const isSelected = useCallback(
|
|
107
|
+
(itemValue: string) => {
|
|
108
|
+
if (multiple) return multiValues.includes(itemValue);
|
|
109
|
+
return singleValue === itemValue;
|
|
110
|
+
},
|
|
111
|
+
[multiple, singleValue, multiValues],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Extract specific callbacks to avoid depending on the entire props object
|
|
115
|
+
const onValueChangeSingle = !multiple ? (props as ComboboxSingleProps).onValueChange : undefined;
|
|
116
|
+
const onValueChangeMulti = multiple ? (props as ComboboxMultipleProps).onValueChange : undefined;
|
|
117
|
+
const isControlledSingle = !multiple && (props as ComboboxSingleProps).value !== undefined;
|
|
118
|
+
const isControlledMulti = multiple && (props as ComboboxMultipleProps).value !== undefined;
|
|
119
|
+
|
|
120
|
+
const onSelect = useCallback(
|
|
121
|
+
(itemValue: string) => {
|
|
122
|
+
if (multiple) {
|
|
123
|
+
const next = multiValues.includes(itemValue)
|
|
124
|
+
? multiValues.filter((val) => val !== itemValue)
|
|
125
|
+
: [...multiValues, itemValue];
|
|
126
|
+
if (!isControlledMulti) setUncontrolledMultiValue(next);
|
|
127
|
+
onValueChangeMulti?.(next);
|
|
128
|
+
// Stay open in multi mode
|
|
129
|
+
} else {
|
|
130
|
+
if (!isControlledSingle) setUncontrolledSingleValue(itemValue);
|
|
131
|
+
onValueChangeSingle?.(itemValue);
|
|
132
|
+
onOpenChange(false);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
[
|
|
136
|
+
multiple,
|
|
137
|
+
isControlledMulti,
|
|
138
|
+
isControlledSingle,
|
|
139
|
+
onValueChangeMulti,
|
|
140
|
+
onValueChangeSingle,
|
|
141
|
+
multiValues,
|
|
142
|
+
onOpenChange,
|
|
143
|
+
],
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const onDeselect = useCallback(
|
|
147
|
+
(itemValue: string) => {
|
|
148
|
+
if (!multiple) return;
|
|
149
|
+
const next = multiValues.filter((val) => val !== itemValue);
|
|
150
|
+
if (!isControlledMulti) setUncontrolledMultiValue(next);
|
|
151
|
+
onValueChangeMulti?.(next);
|
|
152
|
+
},
|
|
153
|
+
[multiple, isControlledMulti, onValueChangeMulti, multiValues],
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const contextValue = useMemo(
|
|
157
|
+
() => ({
|
|
158
|
+
multiple,
|
|
159
|
+
value: singleValue,
|
|
160
|
+
values: multiValues,
|
|
161
|
+
onSelect,
|
|
162
|
+
onDeselect,
|
|
163
|
+
isSelected,
|
|
164
|
+
open,
|
|
165
|
+
onOpenChange,
|
|
166
|
+
}),
|
|
167
|
+
[multiple, singleValue, multiValues, onSelect, onDeselect, isSelected, open, onOpenChange],
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<ComboboxContext.Provider value={contextValue}>
|
|
172
|
+
<Popover open={open} onOpenChange={onOpenChange}>
|
|
173
|
+
{children}
|
|
174
|
+
</Popover>
|
|
175
|
+
</ComboboxContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* ── Trigger ──────────────────────────────────────────────────────────────── */
|
|
180
|
+
|
|
181
|
+
interface ComboboxTriggerProps
|
|
182
|
+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "size">,
|
|
183
|
+
VariantProps<typeof selectTriggerVariants> {
|
|
184
|
+
placeholder?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function ComboboxTrigger({ className, size, placeholder, children, ...props }: ComboboxTriggerProps) {
|
|
188
|
+
const { multiple, value, values, open, onDeselect } = useComboboxContext();
|
|
189
|
+
|
|
190
|
+
const isEmpty = multiple ? values.length === 0 : value === null || value === undefined;
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<PopoverTrigger asChild>
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
data-slot="combobox-trigger"
|
|
197
|
+
role="combobox"
|
|
198
|
+
aria-expanded={open}
|
|
199
|
+
className={cn(selectTriggerVariants({ size }), className)}
|
|
200
|
+
{...props}
|
|
201
|
+
>
|
|
202
|
+
<span
|
|
203
|
+
className={cn(
|
|
204
|
+
"flex flex-1 items-center gap-1.5 text-left",
|
|
205
|
+
multiple && "flex-wrap",
|
|
206
|
+
isEmpty && "text-placeholder",
|
|
207
|
+
)}
|
|
208
|
+
>
|
|
209
|
+
{isEmpty ? placeholder : multiple ? <ComboboxTags values={values} onDeselect={onDeselect} /> : children}
|
|
210
|
+
</span>
|
|
211
|
+
<ChevronDownIcon className="size-5 shrink-0 text-muted-foreground" />
|
|
212
|
+
</button>
|
|
213
|
+
</PopoverTrigger>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* ── Tags (internal, for multi-select trigger) ────────────────────────────── */
|
|
218
|
+
|
|
219
|
+
function ComboboxTags({ values, onDeselect }: { values: string[]; onDeselect: (value: string) => void }) {
|
|
220
|
+
return (
|
|
221
|
+
<>
|
|
222
|
+
{values.map((tagValue) => (
|
|
223
|
+
<TagDismissible
|
|
224
|
+
key={tagValue}
|
|
225
|
+
onClick={(event) => event.stopPropagation()}
|
|
226
|
+
onDismiss={() => onDeselect(tagValue)}
|
|
227
|
+
>
|
|
228
|
+
{tagValue}
|
|
229
|
+
</TagDismissible>
|
|
230
|
+
))}
|
|
231
|
+
</>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* ── Content ──────────────────────────────────────────────────────────────── */
|
|
236
|
+
|
|
237
|
+
interface ComboboxContentProps extends React.ComponentProps<typeof PopoverContent> {
|
|
238
|
+
className?: string;
|
|
239
|
+
children?: ReactNode;
|
|
240
|
+
filter?: React.ComponentProps<typeof CommandPrimitive>["filter"];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function ComboboxContent({ className, children, filter, ...props }: ComboboxContentProps) {
|
|
244
|
+
const { multiple } = useComboboxContext();
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<PopoverContent
|
|
248
|
+
className={cn("w-[var(--radix-popover-trigger-width)] p-0", className)}
|
|
249
|
+
align="start"
|
|
250
|
+
onOpenAutoFocus={multiple ? (event: Event) => event.preventDefault() : undefined}
|
|
251
|
+
{...props}
|
|
252
|
+
>
|
|
253
|
+
<CommandPrimitive
|
|
254
|
+
data-slot="combobox-command"
|
|
255
|
+
filter={filter}
|
|
256
|
+
className="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover"
|
|
257
|
+
>
|
|
258
|
+
{children}
|
|
259
|
+
</CommandPrimitive>
|
|
260
|
+
</PopoverContent>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* ── Search ───────────────────────────────────────────────────────────────── */
|
|
265
|
+
|
|
266
|
+
function ComboboxSearch({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
|
267
|
+
return (
|
|
268
|
+
<div data-slot="combobox-search" className="flex items-center gap-2 border-b border-border-secondary px-3 py-2.5">
|
|
269
|
+
<SearchIcon className="size-5 shrink-0 text-muted-foreground" />
|
|
270
|
+
<CommandPrimitive.Input
|
|
271
|
+
data-slot="combobox-search-input"
|
|
272
|
+
className={cn(
|
|
273
|
+
"flex h-full w-full bg-transparent type-text-md font-medium text-foreground outline-hidden",
|
|
274
|
+
"placeholder:text-placeholder",
|
|
275
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
276
|
+
className,
|
|
277
|
+
)}
|
|
278
|
+
{...props}
|
|
279
|
+
/>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* ── List ─────────────────────────────────────────────────────────────────── */
|
|
285
|
+
|
|
286
|
+
function ComboboxList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
|
|
287
|
+
return (
|
|
288
|
+
<CommandPrimitive.List
|
|
289
|
+
data-slot="combobox-list"
|
|
290
|
+
className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto py-1", className)}
|
|
291
|
+
{...props}
|
|
292
|
+
/>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ── Empty ────────────────────────────────────────────────────────────────── */
|
|
297
|
+
|
|
298
|
+
function ComboboxEmpty({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
|
299
|
+
return (
|
|
300
|
+
<CommandPrimitive.Empty
|
|
301
|
+
data-slot="combobox-empty"
|
|
302
|
+
className={cn("py-6 text-center type-text-sm text-muted-foreground", className)}
|
|
303
|
+
{...props}
|
|
304
|
+
/>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* ── Group ────────────────────────────────────────────────────────────────── */
|
|
309
|
+
|
|
310
|
+
function ComboboxGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
|
311
|
+
return (
|
|
312
|
+
<CommandPrimitive.Group
|
|
313
|
+
data-slot="combobox-group"
|
|
314
|
+
className={cn(
|
|
315
|
+
"overflow-hidden [&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:type-text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-subtle-foreground",
|
|
316
|
+
className,
|
|
317
|
+
)}
|
|
318
|
+
{...props}
|
|
319
|
+
/>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* ── Separator ────────────────────────────────────────────────────────────── */
|
|
324
|
+
|
|
325
|
+
function ComboboxSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
|
326
|
+
return (
|
|
327
|
+
<CommandPrimitive.Separator
|
|
328
|
+
data-slot="combobox-separator"
|
|
329
|
+
className={cn("bg-border-secondary -mx-1 my-1 h-px", className)}
|
|
330
|
+
{...props}
|
|
331
|
+
/>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* ── Item ─────────────────────────────────────────────────────────────────── */
|
|
336
|
+
|
|
337
|
+
interface ComboboxItemProps extends Omit<React.ComponentProps<typeof CommandPrimitive.Item>, "onSelect"> {
|
|
338
|
+
/** The value stored when this item is selected */
|
|
339
|
+
itemValue: string;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function ComboboxItem({ className, children, itemValue, ...props }: ComboboxItemProps) {
|
|
343
|
+
const { onSelect, isSelected } = useComboboxContext();
|
|
344
|
+
const selected = isSelected(itemValue);
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<CommandPrimitive.Item
|
|
348
|
+
data-slot="combobox-item"
|
|
349
|
+
data-selected-item={selected || undefined}
|
|
350
|
+
onSelect={() => onSelect(itemValue)}
|
|
351
|
+
className={cn(
|
|
352
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-2.5 outline-hidden select-none",
|
|
353
|
+
"type-text-md font-medium text-foreground mx-1.5 my-px",
|
|
354
|
+
"data-[selected=true]:bg-background-hover",
|
|
355
|
+
"data-[selected-item]:bg-accent",
|
|
356
|
+
"data-[disabled=true]:pointer-events-none data-[disabled=true]:text-disabled-foreground",
|
|
357
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5",
|
|
358
|
+
className,
|
|
359
|
+
)}
|
|
360
|
+
{...props}
|
|
361
|
+
>
|
|
362
|
+
<span className="flex flex-1 items-center gap-2 truncate">{children}</span>
|
|
363
|
+
{selected && <CheckIcon className="size-5 shrink-0 text-primary" />}
|
|
364
|
+
</CommandPrimitive.Item>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* ── Item supporting text ─────────────────────────────────────────────────── */
|
|
369
|
+
|
|
370
|
+
function ComboboxItemText({ className, children, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
371
|
+
return (
|
|
372
|
+
<span
|
|
373
|
+
data-slot="combobox-item-text"
|
|
374
|
+
className={cn("type-text-md font-normal text-subtle-foreground", className)}
|
|
375
|
+
{...props}
|
|
376
|
+
>
|
|
377
|
+
{children}
|
|
378
|
+
</span>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export {
|
|
383
|
+
Combobox,
|
|
384
|
+
ComboboxContent,
|
|
385
|
+
ComboboxEmpty,
|
|
386
|
+
ComboboxGroup,
|
|
387
|
+
ComboboxItem,
|
|
388
|
+
ComboboxItemText,
|
|
389
|
+
ComboboxList,
|
|
390
|
+
ComboboxSearch,
|
|
391
|
+
ComboboxSeparator,
|
|
392
|
+
ComboboxTrigger,
|
|
393
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@alpic-ai/ui/lib/cn";
|
|
4
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
5
|
+
import { SearchIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
|
|
8
|
+
function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
|
|
9
|
+
return (
|
|
10
|
+
<CommandPrimitive
|
|
11
|
+
data-slot="command"
|
|
12
|
+
className={cn("bg-popover text-foreground flex h-full w-full flex-col overflow-hidden rounded-md", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
|
19
|
+
return (
|
|
20
|
+
<div data-slot="command-input-wrapper" className="flex h-14 items-center gap-2 border-b p-3">
|
|
21
|
+
<SearchIcon className="size-5 shrink-0 text-muted-foreground" />
|
|
22
|
+
<CommandPrimitive.Input
|
|
23
|
+
data-slot="command-input"
|
|
24
|
+
className={cn(
|
|
25
|
+
"placeholder:text-placeholder flex h-full w-full bg-transparent type-text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
|
26
|
+
className,
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
|
|
35
|
+
return (
|
|
36
|
+
<CommandPrimitive.List
|
|
37
|
+
data-slot="command-list"
|
|
38
|
+
className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
|
45
|
+
return (
|
|
46
|
+
<CommandPrimitive.Empty
|
|
47
|
+
data-slot="command-empty"
|
|
48
|
+
className="py-6 text-center type-text-sm text-muted-foreground"
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
|
55
|
+
return (
|
|
56
|
+
<CommandPrimitive.Group
|
|
57
|
+
data-slot="command-group"
|
|
58
|
+
className={cn(
|
|
59
|
+
"text-foreground [&_[cmdk-group-heading]]:text-subtle-foreground overflow-hidden px-2 py-1 [&_[cmdk-group-heading]]:p-2 [&_[cmdk-group-heading]]:type-text-xs [&_[cmdk-group-heading]]:font-medium",
|
|
60
|
+
className,
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
|
68
|
+
return (
|
|
69
|
+
<CommandPrimitive.Separator
|
|
70
|
+
data-slot="command-separator"
|
|
71
|
+
className={cn("bg-border -mx-1 h-px", className)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
|
78
|
+
return (
|
|
79
|
+
<CommandPrimitive.Item
|
|
80
|
+
data-slot="command-item"
|
|
81
|
+
className={cn(
|
|
82
|
+
"data-[selected=true]:bg-background-hover [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-xs px-2 py-3 type-text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:text-disabled-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5",
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
|
91
|
+
return (
|
|
92
|
+
<span
|
|
93
|
+
data-slot="command-shortcut"
|
|
94
|
+
className={cn(
|
|
95
|
+
"ml-auto shrink-0 rounded-xs border border-border-secondary px-1 py-px type-text-xs font-medium text-placeholder",
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
Command,
|
|
105
|
+
CommandEmpty,
|
|
106
|
+
CommandGroup,
|
|
107
|
+
CommandInput,
|
|
108
|
+
CommandItem,
|
|
109
|
+
CommandList,
|
|
110
|
+
CommandSeparator,
|
|
111
|
+
CommandShortcut,
|
|
112
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Check, Copy } from "lucide-react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard";
|
|
6
|
+
import { cn } from "../lib/cn";
|
|
7
|
+
import { Button } from "./button";
|
|
8
|
+
|
|
9
|
+
interface CopyableProps {
|
|
10
|
+
content: string;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Copyable({ content, children, className }: CopyableProps) {
|
|
16
|
+
const { copy, isCopied } = useCopyToClipboard();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className={cn("flex items-center gap-1", className)}>
|
|
20
|
+
{children}
|
|
21
|
+
<Button
|
|
22
|
+
variant="tertiary"
|
|
23
|
+
size="icon"
|
|
24
|
+
onClick={(event) => {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
copy(content);
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{isCopied ? <Check className="size-4 text-success" /> : <Copy className="size-4 text-muted-foreground" />}
|
|
30
|
+
</Button>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface CopyableUrlProps {
|
|
36
|
+
url: URL;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function CopyableUrl({ url }: CopyableUrlProps) {
|
|
40
|
+
return (
|
|
41
|
+
<Copyable content={url.href}>
|
|
42
|
+
<span className="font-semibold">{url.hostname}</span>
|
|
43
|
+
</Copyable>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { Copyable, type CopyableProps, CopyableUrl, type CopyableUrlProps };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@alpic-ai/ui/lib/cn";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
|
|
6
|
+
function DescriptionList({ className, ...props }: React.ComponentProps<"dl">) {
|
|
7
|
+
return (
|
|
8
|
+
<dl
|
|
9
|
+
data-slot="description-list"
|
|
10
|
+
className={cn("grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4", className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DescriptionItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
17
|
+
return <div data-slot="description-item" className={cn("flex flex-col gap-1 xl:gap-1.5", className)} {...props} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DescriptionTitle({ className, ...props }: React.ComponentProps<"dt">) {
|
|
21
|
+
return (
|
|
22
|
+
<dt
|
|
23
|
+
data-slot="description-title"
|
|
24
|
+
className={cn("type-text-xs font-medium text-subtle-foreground", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function DescriptionValue({ className, ...props }: React.ComponentProps<"dd">) {
|
|
31
|
+
return (
|
|
32
|
+
<dd data-slot="description-value" className={cn("type-text-sm text-foreground truncate", className)} {...props} />
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { DescriptionItem, DescriptionList, DescriptionTitle, DescriptionValue };
|