@altimateai/ui-components 0.0.37-beta.1 → 0.0.37-beta.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.
@@ -0,0 +1,650 @@
1
+ import { Meta, StoryFn } from "@storybook/react";
2
+ import { Combobox } from "../shadcn";
3
+ import { Button } from "../button";
4
+ import { DatabaseIcon } from "@ac-assets/icons";
5
+ import { useState } from "react";
6
+
7
+ export default {
8
+ title: "Shadcn/Components/Combobox",
9
+ component: Combobox,
10
+ } as Meta;
11
+
12
+ export const ComboboxExample: StoryFn = () => {
13
+ const options = [
14
+ { value: "react", label: "React" },
15
+ { value: "vue", label: "Vue" },
16
+ { value: "angular", label: "Angular" },
17
+ { value: "svelte", label: "Svelte" },
18
+ { value: "nextjs", label: "Next.js" },
19
+ ];
20
+
21
+ const [value, setValue] = useState("");
22
+
23
+ const handleChange = (newValue: string) => {
24
+ setValue(newValue);
25
+ };
26
+
27
+ return (
28
+ <div className="al-flex al-flex-col al-gap-4 al-justify-start al-items-start">
29
+ <h3 className="al-text-lg al-font-medium">Single Select</h3>
30
+ <Combobox
31
+ options={options}
32
+ value={value}
33
+ onChange={handleChange}
34
+ placeholder="Select framework..."
35
+ />
36
+
37
+ <Combobox
38
+ options={options}
39
+ value={value}
40
+ onChange={handleChange}
41
+ placeholder="Custom width..."
42
+ buttonProps={{ className: "al-w-[300px]" }}
43
+ />
44
+
45
+ <Combobox
46
+ options={options}
47
+ value={value}
48
+ onChange={handleChange}
49
+ placeholder="With icon..."
50
+ icon={<DatabaseIcon className="al-mr-2" />}
51
+ />
52
+ </div>
53
+ );
54
+ };
55
+
56
+ export const ComboboxMultiSelectExample: StoryFn = () => {
57
+ const options = [
58
+ { value: "react", label: "React" },
59
+ { value: "vue", label: "Vue" },
60
+ { value: "angular", label: "Angular" },
61
+ { value: "svelte", label: "Svelte" },
62
+ { value: "nextjs", label: "Next.js" },
63
+ { value: "remix", label: "Remix" },
64
+ { value: "gatsby", label: "Gatsby" },
65
+ { value: "ember", label: "Ember.js" },
66
+ ];
67
+
68
+ const [selectedValues, setSelectedValues] = useState<string[]>([]);
69
+ const [selectedValuesWithApply, setSelectedValuesWithApply] = useState<string[]>([
70
+ "react",
71
+ "vue",
72
+ ]);
73
+
74
+ const handleMultiSelectChange = (newValue: string[]) => {
75
+ setSelectedValues(Array.isArray(newValue) ? newValue : []);
76
+ };
77
+
78
+ const handleMultiSelectWithApplyChange = (newValue: string | string[]) => {
79
+ setSelectedValuesWithApply(Array.isArray(newValue) ? newValue : []);
80
+ };
81
+
82
+ return (
83
+ <div className="al-flex al-flex-col al-gap-8 al-justify-start al-items-start">
84
+ <div className="al-flex al-flex-col al-gap-4">
85
+ <h3 className="al-text-lg al-font-medium">Multi-Select (Auto-Apply)</h3>
86
+ <p className="al-text-sm al-text-muted-foreground">
87
+ Changes are applied immediately when selecting/deselecting options
88
+ </p>
89
+ <Combobox
90
+ options={options}
91
+ value={selectedValues}
92
+ placeholder="Select frameworks..."
93
+ multiSelect={true}
94
+ buttonProps={{ className: "al-w-[250px]" }}
95
+ onChange={handleMultiSelectChange}
96
+ />
97
+ <div className="al-text-sm">
98
+ Selected: {selectedValues.length > 0 ? selectedValues.join(", ") : "None"}
99
+ </div>
100
+ </div>
101
+
102
+ <div className="al-flex al-flex-col al-gap-4">
103
+ <h3 className="al-text-lg al-font-medium">Multi-Select with Apply Button</h3>
104
+ <p className="al-text-sm al-text-muted-foreground">
105
+ Changes are only applied when clicking the Apply button
106
+ </p>
107
+ <Combobox
108
+ options={options}
109
+ value={selectedValuesWithApply}
110
+ onChange={handleMultiSelectWithApplyChange}
111
+ placeholder="Select frameworks..."
112
+ multiSelect={true}
113
+ showApplyButton={true}
114
+ buttonProps={{ className: "al-w-[250px]" }}
115
+ />
116
+ <div className="al-text-sm">
117
+ Selected:{" "}
118
+ {selectedValuesWithApply.length > 0 ? selectedValuesWithApply.join(", ") : "None"}
119
+ </div>
120
+ </div>
121
+
122
+ <div className="al-flex al-flex-col al-gap-4">
123
+ <h3 className="al-text-lg al-font-medium">Columns Selector Example</h3>
124
+ <p className="al-text-sm al-text-muted-foreground">
125
+ Similar to the UI shown in the screenshot
126
+ </p>
127
+ <ComboboxColumnsExample />
128
+ </div>
129
+ </div>
130
+ );
131
+ };
132
+
133
+ const ComboboxColumnsExample = () => {
134
+ const options = [
135
+ { value: "est_cost", label: "Est. Cost" },
136
+ { value: "exec_time", label: "Exec. Time" },
137
+ { value: "insights", label: "Insights" },
138
+ { value: "query_hash", label: "Query Hash" },
139
+ { value: "query_text", label: "Query Text" },
140
+ { value: "query_type", label: "Query Type" },
141
+ { value: "timestamp", label: "Timestamp" },
142
+ { value: "user", label: "User" },
143
+ ];
144
+
145
+ const [selectedColumns, setSelectedColumns] = useState<string[]>([
146
+ "est_cost",
147
+ "exec_time",
148
+ "insights",
149
+ "query_hash",
150
+ "query_text",
151
+ "query_type",
152
+ "timestamp",
153
+ "user",
154
+ ]);
155
+
156
+ const handleColumnsChange = (newValue: string | string[]) => {
157
+ setSelectedColumns(Array.isArray(newValue) ? newValue : []);
158
+ };
159
+
160
+ return (
161
+ <Combobox
162
+ options={options}
163
+ value={selectedColumns}
164
+ onChange={handleColumnsChange}
165
+ placeholder="Columns"
166
+ multiSelect={true}
167
+ showApplyButton={true}
168
+ buttonProps={{ className: "al-w-[200px]" }}
169
+ />
170
+ );
171
+ };
172
+
173
+ export const ComboboxAdvancedExample: StoryFn = () => {
174
+ const options = [
175
+ { value: "react", label: "React" },
176
+ { value: "vue", label: "Vue" },
177
+ { value: "angular", label: "Angular" },
178
+ { value: "svelte", label: "Svelte" },
179
+ { value: "nextjs", label: "Next.js" },
180
+ { value: "remix", label: "Remix" },
181
+ { value: "gatsby", label: "Gatsby" },
182
+ { value: "ember", label: "Ember.js" },
183
+ ];
184
+
185
+ const [singleValue, setSingleValue] = useState("");
186
+ const [multiValue, setMultiValue] = useState<string[]>(["react", "vue"]);
187
+ const [clearableValue, setClearableValue] = useState("angular");
188
+ const [multiClearableValue, setMultiClearableValue] = useState<string[]>(["nextjs", "remix"]);
189
+ const [multiApplyValue, setMultiApplyValue] = useState<string[]>(["gatsby", "ember"]);
190
+
191
+ return (
192
+ <div className="al-flex al-flex-col al-gap-8 al-justify-start al-items-start">
193
+ <div className="al-flex al-flex-col al-gap-4">
194
+ <h3 className="al-text-lg al-font-medium">Search Placeholders</h3>
195
+ <p className="al-text-sm al-text-muted-foreground">Different search placeholder examples</p>
196
+
197
+ <div className="al-flex al-flex-col al-gap-2">
198
+ <label className="al-text-sm al-font-medium">Default Search Placeholder</label>
199
+ <Combobox
200
+ options={options}
201
+ value={singleValue}
202
+ onChange={value => setSingleValue(value as string)}
203
+ placeholder="Select framework..."
204
+ buttonProps={{ className: "al-w-[250px]" }}
205
+ />
206
+ </div>
207
+
208
+ <div className="al-flex al-flex-col al-gap-2">
209
+ <label className="al-text-sm al-font-medium">Custom Search Placeholder</label>
210
+ <Combobox
211
+ options={options}
212
+ value={singleValue}
213
+ onChange={value => setSingleValue(value as string)}
214
+ placeholder="Select framework..."
215
+ searchPlaceholder="Type to find frameworks..."
216
+ buttonProps={{ className: "al-w-[250px]" }}
217
+ />
218
+ </div>
219
+
220
+ <div className="al-flex al-flex-col al-gap-2">
221
+ <label className="al-text-sm al-font-medium">Empty Search Placeholder</label>
222
+ <Combobox
223
+ options={options}
224
+ value={singleValue}
225
+ onChange={value => setSingleValue(value as string)}
226
+ placeholder="Select framework..."
227
+ searchPlaceholder=""
228
+ buttonProps={{ className: "al-w-[250px]" }}
229
+ />
230
+ </div>
231
+ </div>
232
+
233
+ <div className="al-flex al-flex-col al-gap-4">
234
+ <h3 className="al-text-lg al-font-medium">Clear Button Examples</h3>
235
+ <p className="al-text-sm al-text-muted-foreground">
236
+ Demonstrating showClearButton prop with different configurations
237
+ </p>
238
+
239
+ <div className="al-flex al-flex-col al-gap-2">
240
+ <label className="al-text-sm al-font-medium">
241
+ Single Select - No Clear Button (Default)
242
+ </label>
243
+ <Combobox
244
+ options={options}
245
+ value={clearableValue}
246
+ onChange={value => setClearableValue(value as string)}
247
+ placeholder="Select framework..."
248
+ searchPlaceholder="Search frameworks..."
249
+ showClearButton={false}
250
+ buttonProps={{ className: "al-w-[250px]" }}
251
+ />
252
+ </div>
253
+
254
+ <div className="al-flex al-flex-col al-gap-2">
255
+ <label className="al-text-sm al-font-medium">Single Select - With Clear Button</label>
256
+ <Combobox
257
+ options={options}
258
+ value={clearableValue}
259
+ onChange={value => setClearableValue(value as string)}
260
+ placeholder="Select framework..."
261
+ searchPlaceholder="Search frameworks..."
262
+ showClearButton={true}
263
+ buttonProps={{ className: "al-w-[250px]" }}
264
+ />
265
+ </div>
266
+
267
+ <div className="al-flex al-flex-col al-gap-2">
268
+ <label className="al-text-sm al-font-medium">Multi Select - No Clear Button</label>
269
+ <Combobox
270
+ options={options}
271
+ value={multiValue}
272
+ onChange={value => setMultiValue(value as string[])}
273
+ placeholder="Select frameworks..."
274
+ searchPlaceholder="Find your frameworks..."
275
+ multiSelect={true}
276
+ showClearButton={false}
277
+ buttonProps={{ className: "al-w-[250px]" }}
278
+ />
279
+ <div className="al-text-sm">
280
+ Selected: {multiValue.length > 0 ? multiValue.join(", ") : "None"}
281
+ </div>
282
+ </div>
283
+
284
+ <div className="al-flex al-flex-col al-gap-2">
285
+ <label className="al-text-sm al-font-medium">Multi Select - With Clear Button</label>
286
+ <Combobox
287
+ options={options}
288
+ value={multiClearableValue}
289
+ onChange={value => setMultiClearableValue(value as string[])}
290
+ placeholder="Select frameworks..."
291
+ searchPlaceholder="Find your frameworks..."
292
+ multiSelect={true}
293
+ showClearButton={true}
294
+ buttonProps={{ className: "al-w-[250px]" }}
295
+ />
296
+ <div className="al-text-sm">
297
+ Selected: {multiClearableValue.length > 0 ? multiClearableValue.join(", ") : "None"}
298
+ </div>
299
+ </div>
300
+
301
+ <div className="al-flex al-flex-col al-gap-2">
302
+ <label className="al-text-sm al-font-medium">
303
+ Multi Select - With Apply Button & Clear Button
304
+ </label>
305
+ <Combobox
306
+ options={options}
307
+ value={multiApplyValue}
308
+ onChange={value => setMultiApplyValue(value as string[])}
309
+ placeholder="Select frameworks..."
310
+ searchPlaceholder="Filter options..."
311
+ multiSelect={true}
312
+ showApplyButton={true}
313
+ showClearButton={true}
314
+ buttonProps={{ className: "al-w-[250px]" }}
315
+ />
316
+ <div className="al-text-sm">
317
+ Selected: {multiApplyValue.length > 0 ? multiApplyValue.join(", ") : "None"}
318
+ </div>
319
+ </div>
320
+ </div>
321
+ </div>
322
+ );
323
+ };
324
+
325
+ export const ComboboxInfiniteScrollExample: StoryFn = () => {
326
+ const [allOptions] = useState(() => {
327
+ // Generate a large dataset for demonstration
328
+ return Array.from({ length: 1000 }, (_, i) => ({
329
+ value: `option-${i + 1}`,
330
+ label: `Option ${i + 1}`,
331
+ }));
332
+ });
333
+
334
+ const [displayedOptions, setDisplayedOptions] = useState(() => allOptions.slice(0, 20));
335
+ const [hasMore, setHasMore] = useState(true);
336
+ const [selectedValue, setSelectedValue] = useState("option-500"); // Pre-select a value not in initial load
337
+ const [multiSelectedValue, setMultiSelectedValue] = useState<string[]>([
338
+ "option-100",
339
+ "option-200",
340
+ ]); // Pre-select values not in initial load
341
+
342
+ const loadMore = async () => {
343
+ if (!hasMore) return;
344
+
345
+ // Simulate API call delay
346
+ await new Promise(resolve => setTimeout(resolve, 500));
347
+
348
+ const currentLength = displayedOptions.length;
349
+ const nextBatch = allOptions.slice(currentLength, currentLength + 20);
350
+
351
+ if (nextBatch.length === 0) {
352
+ setHasMore(false);
353
+ } else {
354
+ setDisplayedOptions(prev => [...prev, ...nextBatch]);
355
+ setHasMore(currentLength + nextBatch.length < allOptions.length);
356
+ }
357
+ };
358
+
359
+ const resetData = () => {
360
+ setDisplayedOptions(allOptions.slice(0, 20));
361
+ setHasMore(true);
362
+ setSelectedValue("");
363
+ setMultiSelectedValue([]);
364
+ };
365
+
366
+ return (
367
+ <div className="al-flex al-flex-col al-gap-6 al-justify-start al-items-start">
368
+ <div className="al-flex al-flex-col al-gap-2">
369
+ <div className="al-flex al-gap-4 al-items-center">
370
+ <h3 className="al-text-lg al-font-medium">Infinite Scroll Examples</h3>
371
+ <Button onClick={resetData} size="sm" variant="outline">
372
+ Reset Data
373
+ </Button>
374
+ </div>
375
+ <p className="al-text-sm al-text-muted-foreground">
376
+ Notice how pre-selected values (Option 500, Option 100, Option 200) show proper labels
377
+ even though they&apos;re not in the initial 20 options. This demonstrates the valueLabels
378
+ prop functionality.
379
+ </p>
380
+ </div>
381
+
382
+ <div className="al-flex al-flex-col al-gap-4">
383
+ <div className="al-flex al-flex-col al-gap-2">
384
+ <label className="al-text-sm al-font-medium">Single Select with Infinite Scroll</label>
385
+ <p className="al-text-xs al-text-muted-foreground">
386
+ Showing {displayedOptions.length} of {allOptions.length} options
387
+ </p>
388
+ <Combobox
389
+ options={displayedOptions}
390
+ value={selectedValue}
391
+ onChange={value => setSelectedValue(value as string)}
392
+ placeholder="Select an option..."
393
+ onLoadMore={loadMore}
394
+ hasMore={hasMore}
395
+ valueLabels={{
396
+ "option-500": "Option 500",
397
+ }}
398
+ buttonProps={{ className: "al-w-[250px]" }}
399
+ />
400
+ </div>
401
+
402
+ <div className="al-flex al-flex-col al-gap-2">
403
+ <label className="al-text-sm al-font-medium">Multi-Select with Infinite Scroll</label>
404
+ <p className="al-text-xs al-text-muted-foreground">
405
+ Selected: {multiSelectedValue.length} items
406
+ </p>
407
+ <Combobox
408
+ options={displayedOptions}
409
+ value={multiSelectedValue}
410
+ onChange={value => setMultiSelectedValue(value as string[])}
411
+ placeholder="Select multiple options..."
412
+ multiSelect={true}
413
+ onLoadMore={loadMore}
414
+ hasMore={hasMore}
415
+ valueLabels={{
416
+ "option-100": "Option 100",
417
+ "option-200": "Option 200",
418
+ }}
419
+ buttonProps={{ className: "al-w-[250px]" }}
420
+ />
421
+ </div>
422
+
423
+ <div className="al-flex al-flex-col al-gap-2">
424
+ <label className="al-text-sm al-font-medium">
425
+ Standard Combobox (No Infinite Scroll)
426
+ </label>
427
+ <Combobox
428
+ options={displayedOptions.slice(0, 10)}
429
+ value="option-5" // Use a value that exists in the first 10 options
430
+ onChange={value => setSelectedValue(value as string)}
431
+ placeholder="Standard combobox..."
432
+ buttonProps={{ className: "al-w-[250px]" }}
433
+ />
434
+ </div>
435
+ </div>
436
+ </div>
437
+ );
438
+ };
439
+
440
+ export const ComboboxSearchExample: StoryFn = () => {
441
+ const [options] = useState([
442
+ { value: "react", label: "React" },
443
+ { value: "vue", label: "Vue" },
444
+ { value: "angular", label: "Angular" },
445
+ { value: "svelte", label: "Svelte" },
446
+ { value: "nextjs", label: "Next.js" },
447
+ ]);
448
+
449
+ const [selectedValue, setSelectedValue] = useState("");
450
+ const [searchValue, setSearchValue] = useState("");
451
+ const [searchHistory, setSearchHistory] = useState<string[]>([]);
452
+
453
+ const handleSearch = (searchTerm: string) => {
454
+ setSearchValue(searchTerm);
455
+ if (searchTerm.trim() && !searchHistory.includes(searchTerm.trim())) {
456
+ setSearchHistory(prev => [searchTerm.trim(), ...prev.slice(0, 4)]); // Keep last 5 searches
457
+ }
458
+ };
459
+
460
+ const clearHistory = () => {
461
+ setSearchHistory([]);
462
+ setSearchValue("");
463
+ };
464
+
465
+ return (
466
+ <div className="al-flex al-flex-col al-gap-6 al-justify-start al-items-start">
467
+ <div className="al-flex al-gap-4 al-items-center">
468
+ <h3 className="al-text-lg al-font-medium">Search Callback Example</h3>
469
+ <Button onClick={clearHistory} size="sm" variant="outline">
470
+ Clear History
471
+ </Button>
472
+ </div>
473
+
474
+ <div className="al-flex al-flex-col al-gap-4">
475
+ <div className="al-flex al-flex-col al-gap-2">
476
+ <label className="al-text-sm al-font-medium">Combobox with Search Callback</label>
477
+ <p className="al-text-xs al-text-muted-foreground">
478
+ Type in the search box to see the search values being emitted
479
+ </p>
480
+ <Combobox
481
+ options={options}
482
+ value={selectedValue}
483
+ onChange={value => setSelectedValue(value as string)}
484
+ placeholder="Search frameworks..."
485
+ searchPlaceholder="Type to search..."
486
+ onSearch={handleSearch}
487
+ buttonProps={{ className: "al-w-[250px]" }}
488
+ />
489
+ </div>
490
+
491
+ <div className="al-flex al-flex-col al-gap-2">
492
+ <label className="al-text-sm al-font-medium">Current Search Value:</label>
493
+ <code className="al-text-sm al-bg-muted al-p-2 al-rounded">
494
+ {searchValue || "No search performed yet"}
495
+ </code>
496
+ </div>
497
+
498
+ {searchHistory.length > 0 && (
499
+ <div className="al-flex al-flex-col al-gap-2">
500
+ <label className="al-text-sm al-font-medium">Search History:</label>
501
+ <ul className="al-text-sm al-bg-muted al-p-2 al-rounded al-space-y-1">
502
+ {searchHistory.map((search, index) => (
503
+ <li key={index} className="al-font-mono">
504
+ {index + 1}. &quot;{search}&quot;
505
+ </li>
506
+ ))}
507
+ </ul>
508
+ </div>
509
+ )}
510
+ </div>
511
+ </div>
512
+ );
513
+ };
514
+
515
+ export const ComboboxCallbacksExample: StoryFn = () => {
516
+ const options = [
517
+ { value: "react", label: "React" },
518
+ { value: "vue", label: "Vue" },
519
+ { value: "angular", label: "Angular" },
520
+ { value: "svelte", label: "Svelte" },
521
+ { value: "nextjs", label: "Next.js" },
522
+ ];
523
+
524
+ const [selectedValue, setSelectedValue] = useState("");
525
+ const [isOpen, setIsOpen] = useState(false);
526
+ const [openHistory, setOpenHistory] = useState<string[]>([]);
527
+
528
+ const handleOpenChange = (open: boolean) => {
529
+ setIsOpen(open);
530
+ const timestamp = new Date().toLocaleTimeString();
531
+ setOpenHistory(prev => [`${timestamp}: ${open ? "opened" : "closed"}`, ...prev.slice(0, 4)]);
532
+ };
533
+
534
+ const clearHistory = () => {
535
+ setOpenHistory([]);
536
+ };
537
+
538
+ return (
539
+ <div className="al-flex al-flex-col al-gap-6 al-justify-start al-items-start">
540
+ <div className="al-flex al-gap-4 al-items-center">
541
+ <h3 className="al-text-lg al-font-medium">Open/Close Callback Example</h3>
542
+ <Button onClick={clearHistory} size="sm" variant="outline">
543
+ Clear History
544
+ </Button>
545
+ </div>
546
+
547
+ <div className="al-flex al-flex-col al-gap-4">
548
+ <div className="al-flex al-flex-col al-gap-2">
549
+ <label className="al-text-sm al-font-medium">Combobox with onOpenChange Callback</label>
550
+ <p className="al-text-xs al-text-muted-foreground">
551
+ Open and close the combobox to see the callback events
552
+ </p>
553
+ <Combobox
554
+ options={options}
555
+ value={selectedValue}
556
+ onChange={value => setSelectedValue(value as string)}
557
+ placeholder="Select framework..."
558
+ onOpenChange={handleOpenChange}
559
+ buttonProps={{ className: "al-w-[250px]" }}
560
+ />
561
+ </div>
562
+
563
+ <div className="al-flex al-flex-col al-gap-2">
564
+ <label className="al-text-sm al-font-medium">Current State:</label>
565
+ <code className="al-text-sm al-bg-muted al-p-2 al-rounded">
566
+ Popover is {isOpen ? "open" : "closed"}
567
+ </code>
568
+ </div>
569
+
570
+ {openHistory.length > 0 && (
571
+ <div className="al-flex al-flex-col al-gap-2">
572
+ <label className="al-text-sm al-font-medium">Open/Close History:</label>
573
+ <ul className="al-text-sm al-bg-muted al-p-2 al-rounded al-space-y-1">
574
+ {openHistory.map((event, index) => (
575
+ <li key={index} className="al-font-mono">
576
+ {event}
577
+ </li>
578
+ ))}
579
+ </ul>
580
+ </div>
581
+ )}
582
+ </div>
583
+ </div>
584
+ );
585
+ };
586
+
587
+ export const ComboboxPopoverCustomizationExample: StoryFn = () => {
588
+ const options = [
589
+ { value: "react", label: "React" },
590
+ { value: "vue", label: "Vue" },
591
+ { value: "angular", label: "Angular" },
592
+ { value: "svelte", label: "Svelte" },
593
+ { value: "nextjs", label: "Next.js" },
594
+ ];
595
+
596
+ const [selectedValue1, setSelectedValue1] = useState("");
597
+ const [selectedValue2, setSelectedValue2] = useState("");
598
+ const [selectedValue3, setSelectedValue3] = useState("");
599
+
600
+ return (
601
+ <div className="al-flex al-flex-col al-gap-6 al-justify-start al-items-start">
602
+ <h3 className="al-text-lg al-font-medium">Popover Customization Examples</h3>
603
+
604
+ <div className="al-flex al-flex-col al-gap-4">
605
+ <div className="al-flex al-flex-col al-gap-2">
606
+ <label className="al-text-sm al-font-medium">Default Popover (Bottom)</label>
607
+ <Combobox
608
+ options={options}
609
+ value={selectedValue1}
610
+ onChange={value => setSelectedValue1(value as string)}
611
+ placeholder="Default positioning..."
612
+ buttonProps={{ className: "al-w-[250px]" }}
613
+ />
614
+ </div>
615
+
616
+ <div className="al-flex al-flex-col al-gap-2">
617
+ <label className="al-text-sm al-font-medium">
618
+ Right Side Popover (with right chevron)
619
+ </label>
620
+ <Combobox
621
+ options={options}
622
+ value={selectedValue2}
623
+ onChange={value => setSelectedValue2(value as string)}
624
+ placeholder="Right side popover..."
625
+ popoverContentProps={{ side: "right" }}
626
+ buttonProps={{ className: "al-w-[250px]" }}
627
+ />
628
+ </div>
629
+
630
+ <div className="al-flex al-flex-col al-gap-2">
631
+ <label className="al-text-sm al-font-medium">Custom Styled Popover</label>
632
+ <p className="al-text-xs al-text-muted-foreground">
633
+ Popover with custom styling and wider width
634
+ </p>
635
+ <Combobox
636
+ options={options}
637
+ value={selectedValue3}
638
+ onChange={value => setSelectedValue3(value as string)}
639
+ placeholder="Custom styled popover..."
640
+ popoverContentProps={{
641
+ className: "al-w-[350px] al-border-2 al-border-blue-200",
642
+ side: "top",
643
+ }}
644
+ buttonProps={{ className: "al-w-[250px]" }}
645
+ />
646
+ </div>
647
+ </div>
648
+ </div>
649
+ );
650
+ };