react-core-ts 2.1.4 → 2.1.6
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/AutoCompleteWithTabFilter.js +41 -0
- package/dist/AutoCompleteWithTabFilter.js.map +1 -1
- package/dist/ReactAutoComplete.js +17 -2
- package/dist/ReactAutoComplete.js.map +1 -1
- package/dist/ReactAutoCompleteDropdown.js +15 -0
- package/dist/ReactAutoCompleteDropdown.js.map +1 -1
- package/dist/ReactAutoCompleteSuggections.d.ts +1 -0
- package/dist/ReactAutoCompleteSuggections.js +51 -17
- package/dist/ReactAutoCompleteSuggections.js.map +1 -1
- package/package.json +1 -1
- package/src/AutoCompleteWithTabFilter.tsx +45 -0
- package/src/ReactAutoComplete.tsx +46 -30
- package/src/ReactAutoCompleteDropdown.tsx +16 -0
- package/src/ReactAutoCompleteSuggections.tsx +61 -13
|
@@ -107,6 +107,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
107
107
|
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
|
|
108
108
|
const scrollContainerRef = useRef<HTMLUListElement | null>(null);
|
|
109
109
|
const isSelectingRef = useRef(false);
|
|
110
|
+
const hasUserChangedInputRef = useRef(false); // Track if user has actually typed/changed input
|
|
110
111
|
const [dropPosition, setDropPosition] = useState<any>({
|
|
111
112
|
top: 0,
|
|
112
113
|
left: 0,
|
|
@@ -119,11 +120,11 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
119
120
|
param1?: string | number,
|
|
120
121
|
param2?: string | number,
|
|
121
122
|
param3?: string | number,
|
|
122
|
-
param4?: string | number
|
|
123
|
+
param4?: string | number,
|
|
123
124
|
): boolean => {
|
|
124
125
|
const checkIncludesIfExists = (
|
|
125
126
|
mainString?: string | number,
|
|
126
|
-
substring?: string
|
|
127
|
+
substring?: string,
|
|
127
128
|
) => {
|
|
128
129
|
return substring &&
|
|
129
130
|
mainString &&
|
|
@@ -137,7 +138,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
137
138
|
};
|
|
138
139
|
const checkArrayContains = (
|
|
139
140
|
strArray: string[] | undefined,
|
|
140
|
-
param: string
|
|
141
|
+
param: string,
|
|
141
142
|
): boolean => strArray?.includes(param) ?? false;
|
|
142
143
|
|
|
143
144
|
return (
|
|
@@ -166,6 +167,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
166
167
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
167
168
|
const { value } = event.target;
|
|
168
169
|
timerRef.current = 0;
|
|
170
|
+
hasUserChangedInputRef.current = true; // Mark that user has changed the input
|
|
169
171
|
|
|
170
172
|
setInputValue(value);
|
|
171
173
|
handleValChange(value);
|
|
@@ -194,9 +196,9 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
194
196
|
timerRef.current = 1;
|
|
195
197
|
}, 200);
|
|
196
198
|
},
|
|
197
|
-
isStaticList ? 0 : 1000
|
|
199
|
+
isStaticList ? 0 : 1000,
|
|
198
200
|
),
|
|
199
|
-
[]
|
|
201
|
+
[],
|
|
200
202
|
);
|
|
201
203
|
const handleDropData = (value?: string) => {
|
|
202
204
|
if (type === 'auto_complete') {
|
|
@@ -210,8 +212,8 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
210
212
|
value,
|
|
211
213
|
item.param1,
|
|
212
214
|
item.param2,
|
|
213
|
-
item.param3
|
|
214
|
-
)
|
|
215
|
+
item.param3,
|
|
216
|
+
),
|
|
215
217
|
)
|
|
216
218
|
: data;
|
|
217
219
|
setSuggestions(filteredData);
|
|
@@ -230,8 +232,8 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
230
232
|
value,
|
|
231
233
|
item.param1,
|
|
232
234
|
item.param2,
|
|
233
|
-
item.param3
|
|
234
|
-
)
|
|
235
|
+
item.param3,
|
|
236
|
+
),
|
|
235
237
|
)
|
|
236
238
|
: data;
|
|
237
239
|
setSuggestions(filteredData);
|
|
@@ -331,6 +333,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
331
333
|
}, [disabled]);
|
|
332
334
|
const handleSuggestionClick = (suggestion: valueProps, index: number) => {
|
|
333
335
|
isSelectingRef.current = true;
|
|
336
|
+
hasUserChangedInputRef.current = false; // Reset flag when selecting from dropdown
|
|
334
337
|
onChange({
|
|
335
338
|
...suggestion,
|
|
336
339
|
id: suggestion?.id,
|
|
@@ -369,7 +372,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
369
372
|
mainElement?.addEventListener('scroll', handleScroll);
|
|
370
373
|
|
|
371
374
|
const gridElements = document.querySelectorAll(
|
|
372
|
-
'.k-grid-content, .overflow-auto, .overflow-y-auto, .overflow-x-auto'
|
|
375
|
+
'.k-grid-content, .overflow-auto, .overflow-y-auto, .overflow-x-auto',
|
|
373
376
|
);
|
|
374
377
|
gridElements.forEach((gridElement: any) => {
|
|
375
378
|
gridElement.addEventListener('scroll', handleScroll);
|
|
@@ -394,11 +397,13 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
394
397
|
setDropOpen(false);
|
|
395
398
|
setInputValue('');
|
|
396
399
|
onChange({ id: undefined, name: '', from: 3 });
|
|
400
|
+
hasUserChangedInputRef.current = false; // Reset flag when clearing
|
|
397
401
|
onLabelClick();
|
|
398
402
|
};
|
|
399
403
|
|
|
400
404
|
useEffect(() => {
|
|
401
405
|
setInputValue(value?.name ?? '');
|
|
406
|
+
hasUserChangedInputRef.current = false; // Reset flag when value prop changes (e.g., coming back to page)
|
|
402
407
|
}, [value?.name]);
|
|
403
408
|
|
|
404
409
|
useEffect(() => {
|
|
@@ -417,7 +422,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
417
422
|
return errMsg;
|
|
418
423
|
};
|
|
419
424
|
const generateClassName = (
|
|
420
|
-
type: 'input' | 'label' | 'message' | 'adorement'
|
|
425
|
+
type: 'input' | 'label' | 'message' | 'adorement',
|
|
421
426
|
): string => {
|
|
422
427
|
let className = propsClassName;
|
|
423
428
|
switch (type) {
|
|
@@ -446,16 +451,16 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
446
451
|
? 'cursor-pointer'
|
|
447
452
|
: 'cursor-text peer-focus:cursor-pointer'
|
|
448
453
|
} ${
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
isDisabled && !checkIsEmptyField()
|
|
455
|
+
? 'disabled-input-label-bg'
|
|
456
|
+
: !isDisabled || !checkIsEmptyField()
|
|
457
|
+
? 'active-input-label-bg'
|
|
458
|
+
: ''
|
|
459
|
+
} absolute duration-300 transform -translate-y-4 top-2 z-1 origin-[0] px-0 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:-translate-y-4 start-[14px] rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto ${
|
|
460
|
+
isDisabled
|
|
461
|
+
? 'cursor-pointer'
|
|
462
|
+
: 'cursor-text peer-focus:cursor-pointer'
|
|
463
|
+
}
|
|
459
464
|
${
|
|
460
465
|
checkIsEmptyField()
|
|
461
466
|
? 'modern-input-label-size'
|
|
@@ -499,16 +504,24 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
499
504
|
// If user is clicking on dropdown, don't execute exact match logic
|
|
500
505
|
if (isSelectingRef.current) {
|
|
501
506
|
isSelectingRef.current = false;
|
|
507
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Only proceed if user has actually changed the input value
|
|
512
|
+
if (!hasUserChangedInputRef.current) {
|
|
502
513
|
return;
|
|
503
514
|
}
|
|
504
515
|
|
|
505
516
|
if (!inputValue) {
|
|
517
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
506
518
|
return;
|
|
507
519
|
}
|
|
508
520
|
|
|
509
521
|
if (!filteredData || filteredData.length === 0) {
|
|
510
522
|
// No suggestions to match against – notify parent with raw value
|
|
511
523
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
524
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
512
525
|
return;
|
|
513
526
|
}
|
|
514
527
|
|
|
@@ -527,6 +540,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
527
540
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
528
541
|
setInputValue('');
|
|
529
542
|
}
|
|
543
|
+
hasUserChangedInputRef.current = false; // Reset flag after handling
|
|
530
544
|
};
|
|
531
545
|
|
|
532
546
|
// Handle Tab key on input - works even when dropdown is closed or loading
|
|
@@ -554,11 +568,13 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
554
568
|
// No exact match and no highlighted item - trigger invalid value action
|
|
555
569
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
556
570
|
setInputValue('');
|
|
571
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
557
572
|
}
|
|
558
|
-
} else if (inputValue && (!filteredData || filteredData.length === 0)) {
|
|
573
|
+
} else if (inputValue && (!filteredData || filteredData.length === 0 ) && hasUserChangedInputRef.current) {
|
|
559
574
|
// No suggestions available - trigger invalid value action
|
|
560
575
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
561
576
|
setInputValue('');
|
|
577
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
562
578
|
}
|
|
563
579
|
// Don't prevent default - allow Tab to move focus
|
|
564
580
|
};
|
|
@@ -574,8 +590,8 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
574
590
|
item.param1 ?? '',
|
|
575
591
|
item.param2 ?? '',
|
|
576
592
|
item.param3 ?? '',
|
|
577
|
-
item.param4 ?? ''
|
|
578
|
-
)
|
|
593
|
+
item.param4 ?? '',
|
|
594
|
+
),
|
|
579
595
|
)
|
|
580
596
|
: suggestions;
|
|
581
597
|
|
|
@@ -642,8 +658,8 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
642
658
|
e.key === 'ArrowDown'
|
|
643
659
|
? (selectedIndex + 1) % filteredData.length
|
|
644
660
|
: e.key === 'ArrowUp'
|
|
645
|
-
|
|
646
|
-
|
|
661
|
+
? (selectedIndex - 1 + filteredData.length) % filteredData.length
|
|
662
|
+
: selectedIndex;
|
|
647
663
|
|
|
648
664
|
switch (e.key) {
|
|
649
665
|
case 'ArrowDown':
|
|
@@ -883,7 +899,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
883
899
|
onBlur={(e) => {
|
|
884
900
|
// Check immediately if user is selecting (Tab or click)
|
|
885
901
|
const wasSelecting = isSelectingRef.current;
|
|
886
|
-
|
|
902
|
+
|
|
887
903
|
// Delay to allow click events on dropdown to process first
|
|
888
904
|
setTimeout(() => {
|
|
889
905
|
// Check if focus moved to dropdown or if user is selecting
|
|
@@ -928,7 +944,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
928
944
|
? placeholder && isCustomPlaceholder
|
|
929
945
|
? placeholder
|
|
930
946
|
: 'Type to search'
|
|
931
|
-
: placeholder ?? '--Select--'
|
|
947
|
+
: (placeholder ?? '--Select--')
|
|
932
948
|
}
|
|
933
949
|
onFocus={onInputFocus}
|
|
934
950
|
onClick={(e) => {
|
|
@@ -955,7 +971,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
955
971
|
<div
|
|
956
972
|
ref={adorementRef}
|
|
957
973
|
className={`${generateClassName(
|
|
958
|
-
'adorement'
|
|
974
|
+
'adorement',
|
|
959
975
|
)} qbs-autocomplete-adorement custom-dorpdown-adorement mr-[1px] ${
|
|
960
976
|
isLoading ? 'bg-white' : ''
|
|
961
977
|
}`}
|
|
@@ -996,7 +1012,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
|
|
|
996
1012
|
{errors && errors[name] && (
|
|
997
1013
|
<div
|
|
998
1014
|
className={` text-error-label relative cursor-pointer ${generateClassName(
|
|
999
|
-
'message'
|
|
1015
|
+
'message',
|
|
1000
1016
|
)}`}
|
|
1001
1017
|
onMouseEnter={() => setIsHovered(true)}
|
|
1002
1018
|
onMouseLeave={() => setIsHovered(false)}
|
|
@@ -111,6 +111,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
111
111
|
const [refetchData, setRefetchData] = useState(false);
|
|
112
112
|
|
|
113
113
|
const isSelectingRef = useRef(false);
|
|
114
|
+
const hasUserChangedInputRef = useRef(false); // Track if user has actually typed/changed input
|
|
114
115
|
const [dropPosition, setDropPosition] = useState<any>({
|
|
115
116
|
top: 0,
|
|
116
117
|
left: 0,
|
|
@@ -170,6 +171,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
170
171
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
171
172
|
const { value } = event.target;
|
|
172
173
|
timerRef.current = 0;
|
|
174
|
+
hasUserChangedInputRef.current = true; // Mark that user has changed the input
|
|
173
175
|
|
|
174
176
|
setInputValue(value);
|
|
175
177
|
handleValChange(value);
|
|
@@ -340,6 +342,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
340
342
|
}, [disabled]);
|
|
341
343
|
const handleSuggestionClick = (suggestion: valueProps, index: number) => {
|
|
342
344
|
isSelectingRef.current = true;
|
|
345
|
+
hasUserChangedInputRef.current = false; // Reset flag when selecting from dropdown
|
|
343
346
|
onChange({
|
|
344
347
|
...suggestion,
|
|
345
348
|
id: suggestion?.id,
|
|
@@ -414,6 +417,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
414
417
|
setDropOpen(false);
|
|
415
418
|
setInputValue('');
|
|
416
419
|
onChange({ id: undefined, name: '', from: 3 });
|
|
420
|
+
hasUserChangedInputRef.current = false; // Reset flag when clearing
|
|
417
421
|
onLabelClick();
|
|
418
422
|
if (autoDropdown) {
|
|
419
423
|
setRefetchData(true);
|
|
@@ -422,6 +426,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
422
426
|
|
|
423
427
|
useEffect(() => {
|
|
424
428
|
setInputValue(value?.name ?? '');
|
|
429
|
+
hasUserChangedInputRef.current = false; // Reset flag when value prop changes (e.g., coming back to page)
|
|
425
430
|
}, [value?.name]);
|
|
426
431
|
|
|
427
432
|
useEffect(() => {
|
|
@@ -522,16 +527,24 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
522
527
|
// If user is clicking on dropdown, don't execute exact match logic
|
|
523
528
|
if (isSelectingRef.current) {
|
|
524
529
|
isSelectingRef.current = false;
|
|
530
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Only proceed if user has actually changed the input value
|
|
535
|
+
if (!hasUserChangedInputRef.current) {
|
|
525
536
|
return;
|
|
526
537
|
}
|
|
527
538
|
|
|
528
539
|
if (!inputValue) {
|
|
540
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
529
541
|
return;
|
|
530
542
|
}
|
|
531
543
|
|
|
532
544
|
if (!filteredData || filteredData.length === 0) {
|
|
533
545
|
// No suggestions to match against – notify parent with raw value
|
|
534
546
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
547
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
535
548
|
return;
|
|
536
549
|
}
|
|
537
550
|
|
|
@@ -550,6 +563,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
550
563
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
551
564
|
setInputValue('');
|
|
552
565
|
}
|
|
566
|
+
hasUserChangedInputRef.current = false; // Reset flag after handling
|
|
553
567
|
};
|
|
554
568
|
|
|
555
569
|
// Handle Tab key on input - works even when dropdown is closed or loading
|
|
@@ -577,11 +591,13 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
|
|
|
577
591
|
// No exact match and no highlighted item - trigger invalid value action
|
|
578
592
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
579
593
|
setInputValue('');
|
|
594
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
580
595
|
}
|
|
581
596
|
} else if (inputValue && (!filteredData || filteredData.length === 0)) {
|
|
582
597
|
// No suggestions available - trigger invalid value action
|
|
583
598
|
onChange({ id: undefined, name: inputValue, from: 4 });
|
|
584
599
|
setInputValue('');
|
|
600
|
+
hasUserChangedInputRef.current = false; // Reset flag
|
|
585
601
|
}
|
|
586
602
|
// Don't prevent default - allow Tab to move focus
|
|
587
603
|
};
|
|
@@ -59,6 +59,7 @@ type ModernAutoCompleteSuggectionsProps = {
|
|
|
59
59
|
desc?: string;
|
|
60
60
|
/** Field name that contains the unique identifier (default: 'id'). */
|
|
61
61
|
descId?: string;
|
|
62
|
+
showWarningIcon?: boolean;
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -74,7 +75,7 @@ const SuggestionItem: React.FC<{ name: string; count: number; showCount: boolean
|
|
|
74
75
|
}) => {
|
|
75
76
|
return (
|
|
76
77
|
<div className="py-0.5 flex items-center justify-between gap-2 group relative">
|
|
77
|
-
<span className="flex-1 whitespace-normal break-words">
|
|
78
|
+
<span className="flex-1 whitespace-normal break-words suggection-label">
|
|
78
79
|
{name}
|
|
79
80
|
</span>
|
|
80
81
|
{showCount && (
|
|
@@ -115,8 +116,10 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
115
116
|
labelTitle,
|
|
116
117
|
desc = 'name',
|
|
117
118
|
descId = 'id',
|
|
119
|
+
showWarningIcon = true,
|
|
118
120
|
}) => {
|
|
119
121
|
const [inputValue, setInputValue] = useState(value ?? '');
|
|
122
|
+
const [debouncedInputValue, setDebouncedInputValue] = useState('');
|
|
120
123
|
const [items, setItems] = useState<SuggestionItem[]>(data ?? []);
|
|
121
124
|
const [isLoading, setIsLoading] = useState(false);
|
|
122
125
|
const [showDropdown, setShowDropdown] = useState(false);
|
|
@@ -152,13 +155,26 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
152
155
|
}
|
|
153
156
|
}, [data]);
|
|
154
157
|
|
|
155
|
-
//
|
|
158
|
+
// Debounce inputValue so we don't call API on every keystroke
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (!hasTyped) return;
|
|
161
|
+
const handle = window.setTimeout(() => {
|
|
162
|
+
setDebouncedInputValue(inputValue);
|
|
163
|
+
}, 500); // 500ms debounce
|
|
164
|
+
|
|
165
|
+
return () => {
|
|
166
|
+
window.clearTimeout(handle);
|
|
167
|
+
};
|
|
168
|
+
}, [inputValue, hasTyped]);
|
|
169
|
+
|
|
170
|
+
// Load from getData only when user types some text (no initial preload),
|
|
171
|
+
// and only after debounce delay.
|
|
156
172
|
useEffect(() => {
|
|
157
173
|
// Do not call API until the user has actually typed at least once
|
|
158
174
|
if (!hasTyped) return;
|
|
159
175
|
|
|
160
176
|
let cancelled = false;
|
|
161
|
-
const query =
|
|
177
|
+
const query = debouncedInputValue.trim();
|
|
162
178
|
if (!getDataRef.current || query === '') {
|
|
163
179
|
// Clear items when input is cleared so dropdown also clears
|
|
164
180
|
if (!cancelled) {
|
|
@@ -189,7 +205,14 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
189
205
|
return () => {
|
|
190
206
|
cancelled = true;
|
|
191
207
|
};
|
|
192
|
-
}, [
|
|
208
|
+
}, [debouncedInputValue, data]);
|
|
209
|
+
|
|
210
|
+
// Notify parent via onChange when debounced value changes
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!hasTyped) return;
|
|
213
|
+
if (!onChange) return;
|
|
214
|
+
onChange(debouncedInputValue);
|
|
215
|
+
}, [debouncedInputValue, onChange]);
|
|
193
216
|
|
|
194
217
|
// Group by desc field and compute counts
|
|
195
218
|
const grouped = useMemo(() => {
|
|
@@ -232,7 +255,14 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
232
255
|
return grouped.filter((g) => g.name.toLowerCase().includes(term));
|
|
233
256
|
}, [grouped, inputValue, hasTyped]);
|
|
234
257
|
|
|
235
|
-
|
|
258
|
+
const effectiveVisible = useMemo(() => {
|
|
259
|
+
if (!hasTyped && items.length > 0) {
|
|
260
|
+
return grouped;
|
|
261
|
+
}
|
|
262
|
+
return visible;
|
|
263
|
+
}, [hasTyped, items.length, grouped, visible]);
|
|
264
|
+
|
|
265
|
+
// Update dropdown visibility based on visible items from typing
|
|
236
266
|
useEffect(() => {
|
|
237
267
|
setShowDropdown(visible.length > 0);
|
|
238
268
|
}, [visible.length]);
|
|
@@ -317,7 +347,7 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
317
347
|
};
|
|
318
348
|
|
|
319
349
|
const generateClassName = (
|
|
320
|
-
type: 'input' | 'label' | 'message' | 'adorement'
|
|
350
|
+
type: 'input' | 'label' | 'message' | 'adorement' | 'warning'
|
|
321
351
|
): string => {
|
|
322
352
|
let className = propsClassName || '';
|
|
323
353
|
switch (type) {
|
|
@@ -370,6 +400,9 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
370
400
|
case 'message':
|
|
371
401
|
className = ' text-error-icon ';
|
|
372
402
|
break;
|
|
403
|
+
case 'warning':
|
|
404
|
+
className = ' text-warning-icon ';
|
|
405
|
+
break;
|
|
373
406
|
case 'adorement':
|
|
374
407
|
className += ' absolute right-0 adorement gap-1 flex items-center ';
|
|
375
408
|
break;
|
|
@@ -464,11 +497,16 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
464
497
|
value={inputValue ? inputValue : ''}
|
|
465
498
|
ref={inputRef}
|
|
466
499
|
disabled={disabled}
|
|
500
|
+
onFocus={() => {
|
|
501
|
+
// show suggestions when the field gains focus.
|
|
502
|
+
if (!disabled && effectiveVisible.length > 0) {
|
|
503
|
+
setShowDropdown(true);
|
|
504
|
+
}
|
|
505
|
+
}}
|
|
467
506
|
onChange={(e) => {
|
|
468
507
|
const val = e.target.value;
|
|
469
508
|
setInputValue(val);
|
|
470
509
|
setHasTyped(true);
|
|
471
|
-
onChange?.(val);
|
|
472
510
|
}}
|
|
473
511
|
onBlur={() => {}}
|
|
474
512
|
placeholder={placeholder}
|
|
@@ -510,16 +548,26 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
510
548
|
<CustomIcons name="alert" type="medium" />
|
|
511
549
|
</div>
|
|
512
550
|
)}
|
|
551
|
+
|
|
552
|
+
{showWarningIcon && effectiveVisible.length > 0 && (
|
|
553
|
+
<div
|
|
554
|
+
className={`text-warning-label relative mr-1 ${generateClassName(
|
|
555
|
+
'warning'
|
|
556
|
+
)}`}
|
|
557
|
+
>
|
|
558
|
+
<CustomIcons name="alert" type="medium" />
|
|
559
|
+
</div>
|
|
560
|
+
)}
|
|
513
561
|
</div>
|
|
514
562
|
</div>
|
|
515
563
|
</div>
|
|
516
564
|
</div>
|
|
517
|
-
{showDropdown &&
|
|
565
|
+
{showDropdown && effectiveVisible.length > 0 && (
|
|
518
566
|
<>
|
|
519
567
|
{insideOpen ? (
|
|
520
568
|
<div className="suggection-list">
|
|
521
569
|
<div className="bg-white border border-grey-light shadow-gray-300 shadow-md rounded-sm max-h-40 overflow-auto text-[11px] text-gray-700 px-3.5 py-1.5">
|
|
522
|
-
{
|
|
570
|
+
{effectiveVisible.map((g) => (
|
|
523
571
|
<SuggestionItem
|
|
524
572
|
key={g.name}
|
|
525
573
|
name={g.name}
|
|
@@ -532,16 +580,16 @@ const ModernAutoCompleteSuggections: React.FC<
|
|
|
532
580
|
) : (
|
|
533
581
|
<Portal>
|
|
534
582
|
<div
|
|
535
|
-
className="autocomplete-suggections suggection-list bg-white shadow-gray-300 shadow-md border border-grey-light z-50"
|
|
583
|
+
className="autocomplete-suggections autosuggection-list suggection-list bg-white shadow-gray-300 shadow-md border border-grey-light z-50"
|
|
536
584
|
style={dropPosition}
|
|
537
585
|
>
|
|
538
586
|
<div className="h-auto max-h-40 overflow-auto w-full py-1.5">
|
|
539
|
-
{
|
|
540
|
-
<div key={g.name} className="px-3.5 text-
|
|
587
|
+
{effectiveVisible.map((g) => (
|
|
588
|
+
<div key={g.name} className="px-3.5 text-gray-700 suggection-item">
|
|
541
589
|
<SuggestionItem
|
|
542
590
|
name={g.name}
|
|
543
591
|
count={g.count}
|
|
544
|
-
showCount={g.count >
|
|
592
|
+
showCount={g.count > 0}
|
|
545
593
|
/>
|
|
546
594
|
</div>
|
|
547
595
|
))}
|