@djangocfg/layouts 1.2.48 โ 1.2.50
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.50",
|
|
4
4
|
"description": "Layout system and components for Unrealon applications",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "DjangoCFG",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"check": "tsc --noEmit"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@djangocfg/api": "^1.2.
|
|
67
|
-
"@djangocfg/og-image": "^1.2.
|
|
68
|
-
"@djangocfg/ui": "^1.2.
|
|
66
|
+
"@djangocfg/api": "^1.2.50",
|
|
67
|
+
"@djangocfg/og-image": "^1.2.50",
|
|
68
|
+
"@djangocfg/ui": "^1.2.50",
|
|
69
69
|
"@hookform/resolvers": "^5.2.0",
|
|
70
70
|
"consola": "^3.4.2",
|
|
71
71
|
"lucide-react": "^0.468.0",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"vidstack": "0.6.15"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@djangocfg/typescript-config": "^1.2.
|
|
89
|
+
"@djangocfg/typescript-config": "^1.2.50",
|
|
90
90
|
"@types/node": "^24.7.2",
|
|
91
91
|
"@types/react": "19.2.2",
|
|
92
92
|
"@types/react-dom": "19.2.1",
|
|
@@ -16,36 +16,36 @@ export interface PackageInfo {
|
|
|
16
16
|
/**
|
|
17
17
|
* Package versions registry
|
|
18
18
|
* Auto-synced from package.json files
|
|
19
|
-
* Last updated: 2025-11-
|
|
19
|
+
* Last updated: 2025-11-21T06:17:17.842Z
|
|
20
20
|
*/
|
|
21
21
|
const PACKAGE_VERSIONS: PackageInfo[] = [
|
|
22
22
|
{
|
|
23
23
|
"name": "@djangocfg/ui",
|
|
24
|
-
"version": "1.2.
|
|
24
|
+
"version": "1.2.50"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "@djangocfg/api",
|
|
28
|
-
"version": "1.2.
|
|
28
|
+
"version": "1.2.50"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"name": "@djangocfg/layouts",
|
|
32
|
-
"version": "1.2.
|
|
32
|
+
"version": "1.2.50"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"name": "@djangocfg/markdown",
|
|
36
|
-
"version": "1.2.
|
|
36
|
+
"version": "1.2.50"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"name": "@djangocfg/og-image",
|
|
40
|
-
"version": "1.2.
|
|
40
|
+
"version": "1.2.50"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"name": "@djangocfg/eslint-config",
|
|
44
|
-
"version": "1.2.
|
|
44
|
+
"version": "1.2.50"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"name": "@djangocfg/typescript-config",
|
|
48
|
-
"version": "1.2.
|
|
48
|
+
"version": "1.2.50"
|
|
49
49
|
}
|
|
50
50
|
];
|
|
51
51
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Form Components Configuration
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import React from 'react';
|
|
5
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
6
6
|
import {
|
|
7
7
|
Button,
|
|
8
8
|
ButtonLink,
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
Slider,
|
|
23
23
|
Combobox,
|
|
24
24
|
MultiSelect,
|
|
25
|
+
MultiSelectPro,
|
|
26
|
+
MultiSelectProAsync,
|
|
25
27
|
InputOTP,
|
|
26
28
|
InputOTPGroup,
|
|
27
29
|
InputOTPSlot,
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
FormLabel,
|
|
35
37
|
FormMessage,
|
|
36
38
|
Field,
|
|
39
|
+
useDebounce,
|
|
37
40
|
} from '@djangocfg/ui';
|
|
38
41
|
import { JsonSchemaForm } from '@djangocfg/ui/tools';
|
|
39
42
|
import type { ComponentConfig } from './types';
|
|
@@ -391,6 +394,353 @@ export const FORM_COMPONENTS: ComponentConfig[] = [
|
|
|
391
394
|
/>
|
|
392
395
|
),
|
|
393
396
|
},
|
|
397
|
+
{
|
|
398
|
+
name: 'MultiSelectPro',
|
|
399
|
+
category: 'forms',
|
|
400
|
+
description: 'Advanced multi-select with animations, custom styling, grouped options, and comprehensive accessibility. Supports variants, icons, gradients, responsive design, and imperative control via ref.',
|
|
401
|
+
importPath: "import { MultiSelectPro } from '@djangocfg/ui';",
|
|
402
|
+
example: `import { MultiSelectPro } from '@djangocfg/ui';
|
|
403
|
+
import type { MultiSelectProOption } from '@djangocfg/ui';
|
|
404
|
+
import { useState } from 'react';
|
|
405
|
+
|
|
406
|
+
// Basic usage
|
|
407
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
408
|
+
|
|
409
|
+
<MultiSelectPro
|
|
410
|
+
options={[
|
|
411
|
+
{ value: "react", label: "React" },
|
|
412
|
+
{ value: "vue", label: "Vue.js" },
|
|
413
|
+
{ value: "angular", label: "Angular" },
|
|
414
|
+
]}
|
|
415
|
+
onValueChange={setSelected}
|
|
416
|
+
defaultValue={selected}
|
|
417
|
+
placeholder="Select frameworks..."
|
|
418
|
+
/>
|
|
419
|
+
|
|
420
|
+
// With custom styling and icons
|
|
421
|
+
const styledOptions = [
|
|
422
|
+
{
|
|
423
|
+
value: "react",
|
|
424
|
+
label: "React",
|
|
425
|
+
style: {
|
|
426
|
+
badgeColor: "#61DAFB",
|
|
427
|
+
iconColor: "#282C34",
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
value: "vue",
|
|
432
|
+
label: "Vue.js",
|
|
433
|
+
style: {
|
|
434
|
+
gradient: "linear-gradient(135deg, #4FC08D 0%, #42B883 100%)",
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
<MultiSelectPro
|
|
440
|
+
options={styledOptions}
|
|
441
|
+
onValueChange={setSelected}
|
|
442
|
+
variant="secondary"
|
|
443
|
+
animationConfig={{
|
|
444
|
+
badgeAnimation: "bounce",
|
|
445
|
+
popoverAnimation: "scale",
|
|
446
|
+
duration: 0.3,
|
|
447
|
+
}}
|
|
448
|
+
maxCount={3}
|
|
449
|
+
closeOnSelect={false}
|
|
450
|
+
/>
|
|
451
|
+
|
|
452
|
+
// With grouped options
|
|
453
|
+
const groupedOptions = [
|
|
454
|
+
{
|
|
455
|
+
heading: "Frontend Frameworks",
|
|
456
|
+
options: [
|
|
457
|
+
{ value: "react", label: "React" },
|
|
458
|
+
{ value: "vue", label: "Vue.js" },
|
|
459
|
+
{ value: "angular", label: "Angular", disabled: true },
|
|
460
|
+
],
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
heading: "Backend Technologies",
|
|
464
|
+
options: [
|
|
465
|
+
{ value: "node", label: "Node.js" },
|
|
466
|
+
{ value: "python", label: "Python" },
|
|
467
|
+
],
|
|
468
|
+
},
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
<MultiSelectPro
|
|
472
|
+
options={groupedOptions}
|
|
473
|
+
onValueChange={setSelected}
|
|
474
|
+
placeholder="Select technologies..."
|
|
475
|
+
searchable={true}
|
|
476
|
+
responsive={true}
|
|
477
|
+
minWidth="200px"
|
|
478
|
+
maxWidth="500px"
|
|
479
|
+
/>
|
|
480
|
+
|
|
481
|
+
// With imperative control via ref
|
|
482
|
+
import { useRef } from 'react';
|
|
483
|
+
import type { MultiSelectProRef } from '@djangocfg/ui';
|
|
484
|
+
|
|
485
|
+
const ref = useRef<MultiSelectProRef>(null);
|
|
486
|
+
|
|
487
|
+
// Later in code:
|
|
488
|
+
ref.current?.clear();
|
|
489
|
+
ref.current?.reset();
|
|
490
|
+
ref.current?.setSelectedValues(['react', 'vue']);
|
|
491
|
+
const values = ref.current?.getSelectedValues();`,
|
|
492
|
+
preview: (
|
|
493
|
+
<div className="space-y-6">
|
|
494
|
+
<div className="space-y-2">
|
|
495
|
+
<p className="text-sm font-medium">Basic with animations:</p>
|
|
496
|
+
<MultiSelectPro
|
|
497
|
+
options={[
|
|
498
|
+
{ value: "react", label: "React" },
|
|
499
|
+
{ value: "vue", label: "Vue" },
|
|
500
|
+
{ value: "angular", label: "Angular" },
|
|
501
|
+
{ value: "svelte", label: "Svelte" },
|
|
502
|
+
]}
|
|
503
|
+
defaultValue={[]}
|
|
504
|
+
onValueChange={() => {}}
|
|
505
|
+
placeholder="Select frameworks..."
|
|
506
|
+
animationConfig={{
|
|
507
|
+
badgeAnimation: "bounce",
|
|
508
|
+
popoverAnimation: "scale",
|
|
509
|
+
duration: 0.3,
|
|
510
|
+
}}
|
|
511
|
+
maxCount={2}
|
|
512
|
+
className="w-[350px]"
|
|
513
|
+
/>
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
<div className="space-y-2">
|
|
517
|
+
<p className="text-sm font-medium">With variants and styles:</p>
|
|
518
|
+
<div className="grid grid-cols-2 gap-2">
|
|
519
|
+
<MultiSelectPro
|
|
520
|
+
options={[
|
|
521
|
+
{ value: "1", label: "Option 1" },
|
|
522
|
+
{ value: "2", label: "Option 2" },
|
|
523
|
+
]}
|
|
524
|
+
defaultValue={["1"]}
|
|
525
|
+
onValueChange={() => {}}
|
|
526
|
+
variant="secondary"
|
|
527
|
+
placeholder="Secondary"
|
|
528
|
+
maxCount={1}
|
|
529
|
+
/>
|
|
530
|
+
<MultiSelectPro
|
|
531
|
+
options={[
|
|
532
|
+
{ value: "1", label: "Option 1" },
|
|
533
|
+
{ value: "2", label: "Option 2" },
|
|
534
|
+
]}
|
|
535
|
+
defaultValue={["2"]}
|
|
536
|
+
onValueChange={() => {}}
|
|
537
|
+
variant="destructive"
|
|
538
|
+
placeholder="Destructive"
|
|
539
|
+
maxCount={1}
|
|
540
|
+
/>
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
|
|
544
|
+
<div className="space-y-2">
|
|
545
|
+
<p className="text-sm font-medium">Grouped options:</p>
|
|
546
|
+
<MultiSelectPro
|
|
547
|
+
options={[
|
|
548
|
+
{
|
|
549
|
+
heading: "Frontend",
|
|
550
|
+
options: [
|
|
551
|
+
{ value: "react", label: "React" },
|
|
552
|
+
{ value: "vue", label: "Vue" },
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
heading: "Backend",
|
|
557
|
+
options: [
|
|
558
|
+
{ value: "node", label: "Node.js" },
|
|
559
|
+
{ value: "python", label: "Python" },
|
|
560
|
+
],
|
|
561
|
+
},
|
|
562
|
+
]}
|
|
563
|
+
defaultValue={[]}
|
|
564
|
+
onValueChange={() => {}}
|
|
565
|
+
placeholder="Select from groups..."
|
|
566
|
+
className="w-[350px]"
|
|
567
|
+
maxCount={2}
|
|
568
|
+
/>
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div className="p-4 border rounded-md bg-muted/50">
|
|
572
|
+
<p className="text-sm font-medium mb-2">Features:</p>
|
|
573
|
+
<ul className="grid grid-cols-2 gap-x-4 gap-y-1 text-sm text-muted-foreground">
|
|
574
|
+
<li>โจ Multiple variants (default, secondary, destructive, inverted)</li>
|
|
575
|
+
<li>๐ Custom badge colors & gradients</li>
|
|
576
|
+
<li>๐ Grouped options with separators</li>
|
|
577
|
+
<li>๐ซ Disabled options support</li>
|
|
578
|
+
<li>๐จ Badge animations (bounce, pulse, wiggle, fade, slide)</li>
|
|
579
|
+
<li>๐ Built-in search & filter</li>
|
|
580
|
+
<li>๐ฑ Responsive design (mobile/tablet/desktop)</li>
|
|
581
|
+
<li>๐ Width constraints (min/max)</li>
|
|
582
|
+
<li>โฟ Full accessibility (ARIA, keyboard nav)</li>
|
|
583
|
+
<li>๐ง Imperative control via ref</li>
|
|
584
|
+
<li>๐ Duplicate handling</li>
|
|
585
|
+
<li>๐๏ธ Auto-close, single-line, auto-size modes</li>
|
|
586
|
+
</ul>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
),
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
name: 'MultiSelectProAsync',
|
|
593
|
+
category: 'forms',
|
|
594
|
+
description: 'Async multi-select with external API search, debouncing, and loading states. Perfect for large datasets and server-side filtering.',
|
|
595
|
+
importPath: "import { MultiSelectProAsync, useDebounce } from '@djangocfg/ui';",
|
|
596
|
+
example: `import { MultiSelectProAsync, useDebounce } from '@djangocfg/ui';
|
|
597
|
+
import { useState, useEffect } from 'react';
|
|
598
|
+
|
|
599
|
+
// Mock API function (replace with your actual API)
|
|
600
|
+
const searchAPI = async (query: string) => {
|
|
601
|
+
const response = await fetch(\`/api/search?q=\${query}\`);
|
|
602
|
+
return response.json();
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
function AsyncExample() {
|
|
606
|
+
const [searchValue, setSearchValue] = useState('');
|
|
607
|
+
const [options, setOptions] = useState([]);
|
|
608
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
609
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
610
|
+
|
|
611
|
+
// Debounce search to reduce API calls
|
|
612
|
+
const debouncedSearch = useDebounce(searchValue, 300);
|
|
613
|
+
|
|
614
|
+
// Fetch options when debounced search changes
|
|
615
|
+
useEffect(() => {
|
|
616
|
+
if (!debouncedSearch) {
|
|
617
|
+
setOptions([]);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const fetchOptions = async () => {
|
|
622
|
+
setIsLoading(true);
|
|
623
|
+
try {
|
|
624
|
+
const results = await searchAPI(debouncedSearch);
|
|
625
|
+
setOptions(results);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
console.error('Search failed:', error);
|
|
628
|
+
} finally {
|
|
629
|
+
setIsLoading(false);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
fetchOptions();
|
|
634
|
+
}, [debouncedSearch]);
|
|
635
|
+
|
|
636
|
+
return (
|
|
637
|
+
<MultiSelectProAsync
|
|
638
|
+
// Search control (managed by parent)
|
|
639
|
+
searchValue={searchValue}
|
|
640
|
+
onSearchChange={setSearchValue}
|
|
641
|
+
isLoading={isLoading}
|
|
642
|
+
|
|
643
|
+
// Options from API
|
|
644
|
+
options={options}
|
|
645
|
+
|
|
646
|
+
// Selection
|
|
647
|
+
onValueChange={setSelected}
|
|
648
|
+
defaultValue={selected}
|
|
649
|
+
|
|
650
|
+
// UI
|
|
651
|
+
placeholder="Search and select..."
|
|
652
|
+
searchPlaceholder="Type to search..."
|
|
653
|
+
emptyText="No results found"
|
|
654
|
+
loadingText="Searching..."
|
|
655
|
+
|
|
656
|
+
// Features
|
|
657
|
+
variant="default"
|
|
658
|
+
maxCount={3}
|
|
659
|
+
closeOnSelect={false}
|
|
660
|
+
/>
|
|
661
|
+
);
|
|
662
|
+
}`,
|
|
663
|
+
preview: (() => {
|
|
664
|
+
// Demo component with mock data
|
|
665
|
+
const DemoAsync = () => {
|
|
666
|
+
const [searchValue, setSearchValue] = useState('');
|
|
667
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
668
|
+
const debouncedSearch = useDebounce(searchValue, 300);
|
|
669
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
670
|
+
|
|
671
|
+
// Mock database of countries
|
|
672
|
+
const allCountries = useMemo(() => [
|
|
673
|
+
{ value: 'us', label: 'United States' },
|
|
674
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
675
|
+
{ value: 'ca', label: 'Canada' },
|
|
676
|
+
{ value: 'au', label: 'Australia' },
|
|
677
|
+
{ value: 'de', label: 'Germany' },
|
|
678
|
+
{ value: 'fr', label: 'France' },
|
|
679
|
+
{ value: 'it', label: 'Italy' },
|
|
680
|
+
{ value: 'es', label: 'Spain' },
|
|
681
|
+
{ value: 'jp', label: 'Japan' },
|
|
682
|
+
{ value: 'cn', label: 'China' },
|
|
683
|
+
{ value: 'in', label: 'India' },
|
|
684
|
+
{ value: 'br', label: 'Brazil' },
|
|
685
|
+
{ value: 'mx', label: 'Mexico' },
|
|
686
|
+
{ value: 'ru', label: 'Russia' },
|
|
687
|
+
{ value: 'za', label: 'South Africa' },
|
|
688
|
+
], []);
|
|
689
|
+
|
|
690
|
+
// Filtered options based on debounced search
|
|
691
|
+
const filteredOptions = useMemo(() => {
|
|
692
|
+
if (!debouncedSearch) return [];
|
|
693
|
+
return allCountries.filter(country =>
|
|
694
|
+
country.label.toLowerCase().includes(debouncedSearch.toLowerCase())
|
|
695
|
+
);
|
|
696
|
+
}, [debouncedSearch, allCountries]);
|
|
697
|
+
|
|
698
|
+
// Simulate API loading
|
|
699
|
+
useEffect(() => {
|
|
700
|
+
if (debouncedSearch) {
|
|
701
|
+
setIsLoading(true);
|
|
702
|
+
const timer = setTimeout(() => setIsLoading(false), 300);
|
|
703
|
+
return () => clearTimeout(timer);
|
|
704
|
+
}
|
|
705
|
+
}, [debouncedSearch]);
|
|
706
|
+
|
|
707
|
+
return (
|
|
708
|
+
<div className="space-y-4">
|
|
709
|
+
<MultiSelectProAsync
|
|
710
|
+
searchValue={searchValue}
|
|
711
|
+
onSearchChange={setSearchValue}
|
|
712
|
+
isLoading={isLoading}
|
|
713
|
+
options={filteredOptions}
|
|
714
|
+
onValueChange={setSelected}
|
|
715
|
+
defaultValue={selected}
|
|
716
|
+
placeholder="Search countries..."
|
|
717
|
+
searchPlaceholder="Type to search countries..."
|
|
718
|
+
emptyText="No countries found"
|
|
719
|
+
loadingText="Searching..."
|
|
720
|
+
maxCount={2}
|
|
721
|
+
className="w-[350px]"
|
|
722
|
+
/>
|
|
723
|
+
|
|
724
|
+
<div className="p-4 border rounded-md bg-muted/50">
|
|
725
|
+
<p className="text-sm font-medium mb-2">Features:</p>
|
|
726
|
+
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
727
|
+
<li>๐ <strong>Async search</strong> - Controlled search value for API integration</li>
|
|
728
|
+
<li>โฑ๏ธ <strong>Debouncing</strong> - Use useDebounce hook to reduce API calls</li>
|
|
729
|
+
<li>๐ <strong>Loading states</strong> - Shows spinner during data fetching</li>
|
|
730
|
+
<li>๐งน <strong>Auto-clear</strong> - Clears search when popover closes</li>
|
|
731
|
+
<li>โจ <strong>All MultiSelectPro features</strong> - Animations, variants, groups, etc.</li>
|
|
732
|
+
</ul>
|
|
733
|
+
<div className="mt-3 p-2 bg-muted rounded text-xs">
|
|
734
|
+
<strong>Try it:</strong> Type "united" or "india" to see async filtering
|
|
735
|
+
</div>
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
738
|
+
);
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
return <DemoAsync />;
|
|
742
|
+
})(),
|
|
743
|
+
},
|
|
394
744
|
{
|
|
395
745
|
name: 'InputOTP',
|
|
396
746
|
category: 'forms',
|
|
@@ -92,20 +92,57 @@ const countdown = useCountdown(targetDate);
|
|
|
92
92
|
{
|
|
93
93
|
name: 'useDebounce',
|
|
94
94
|
category: 'hooks',
|
|
95
|
-
description: 'Debounce value changes',
|
|
95
|
+
description: 'Debounce value changes to reduce API calls and improve performance. Perfect for search inputs and form fields.',
|
|
96
96
|
importPath: "import { useDebounce } from '@djangocfg/ui';",
|
|
97
|
-
example:
|
|
98
|
-
const
|
|
97
|
+
example: `// Basic search debouncing
|
|
98
|
+
const [search, setSearch] = useState('');
|
|
99
|
+
const debouncedSearch = useDebounce(search, 300); // Default 300ms
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (debouncedSearch) {
|
|
103
|
+
// API call only fires 300ms after user stops typing
|
|
104
|
+
fetchResults(debouncedSearch);
|
|
105
|
+
}
|
|
106
|
+
}, [debouncedSearch]);
|
|
107
|
+
|
|
108
|
+
// With MultiSelectPro (has built-in search)
|
|
109
|
+
const [searchValue, setSearchValue] = useState('');
|
|
110
|
+
const debouncedSearchValue = useDebounce(searchValue, 500);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
// Fetch options from API with debounced search
|
|
114
|
+
if (debouncedSearchValue) {
|
|
115
|
+
fetchOptions(debouncedSearchValue);
|
|
116
|
+
}
|
|
117
|
+
}, [debouncedSearchValue]);
|
|
118
|
+
|
|
119
|
+
<MultiSelectPro
|
|
120
|
+
options={filteredOptions}
|
|
121
|
+
onValueChange={setSelected}
|
|
122
|
+
placeholder="Search and select..."
|
|
123
|
+
searchable={true}
|
|
124
|
+
// ... other props
|
|
125
|
+
/>`,
|
|
101
126
|
preview: (
|
|
102
127
|
<div className="p-4 border rounded-md">
|
|
103
128
|
<code className="text-sm">
|
|
104
|
-
useDebounce(value, delay)
|
|
129
|
+
const debouncedValue = useDebounce(value, delay);
|
|
105
130
|
</code>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
131
|
+
<div className="mt-3 space-y-2 text-sm text-muted-foreground">
|
|
132
|
+
<p><strong>Parameters:</strong></p>
|
|
133
|
+
<ul className="list-disc list-inside space-y-1 ml-2">
|
|
134
|
+
<li><code>value</code>: Any value to debounce</li>
|
|
135
|
+
<li><code>delay</code>: Delay in milliseconds (default: 300ms)</li>
|
|
136
|
+
</ul>
|
|
137
|
+
<p className="mt-2"><strong>Returns:</strong> Debounced value</p>
|
|
138
|
+
<p className="mt-2"><strong>Use Cases:</strong></p>
|
|
139
|
+
<ul className="list-disc list-inside space-y-1 ml-2">
|
|
140
|
+
<li>Search inputs (reduce API calls)</li>
|
|
141
|
+
<li>Form validation</li>
|
|
142
|
+
<li>Auto-save functionality</li>
|
|
143
|
+
<li>Scroll/resize event handlers</li>
|
|
144
|
+
</ul>
|
|
145
|
+
</div>
|
|
109
146
|
</div>
|
|
110
147
|
),
|
|
111
148
|
},
|