react-core-ts 2.1.1 → 2.1.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,97 @@
1
+ .qbs-tree-list-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ }
5
+ .qbs-tree-list-container-sub {
6
+ display: flex;
7
+ flex-direction: row;
8
+ align-items: flex-start;
9
+ gap: 7px;
10
+ }
11
+
12
+ .qbs-tree-list-container-sub .no-children {
13
+ margin-left: 1.75rem; /* Corresponds to ml-7 in Tailwind */
14
+ }
15
+ .qbs-tree-list-container-sub-icon {
16
+ margin-top: 0.25rem; /* Corresponds to mt-1 in Tailwind */
17
+ cursor: pointer;
18
+ padding-top: 0.5rem; /* Corresponds to py-2 in Tailwind */
19
+ padding-bottom: 0.5rem;
20
+ transition: transform 0.3s ease; /* Smooth rotation transition */
21
+ color: #003c71;
22
+ }
23
+
24
+ .qbs-tree-list-container-sub-icon.expanded {
25
+ transform: rotate(90deg); /* Corresponds to rotate-90 in Tailwind */
26
+ }
27
+ .qbs-tree-list-container-sub-container {
28
+ display: flex;
29
+ gap: 15px; /* Corresponds to gap-2 in Tailwind */
30
+ padding: 0.5rem; /* Corresponds to p-2 in Tailwind */
31
+ flex-direction: row; /* Corresponds to flex-row in Tailwind */
32
+ width: 100%; /* Corresponds to w-full in Tailwind */
33
+ color: #3d414a;
34
+ }
35
+ .qbs-tree-list-container-sub-container-hideicon {
36
+ display: flex;
37
+ gap: 6px; /* Corresponds to gap-2 in Tailwind */
38
+ padding: 0.5rem; /* Corresponds to p-2 in Tailwind */
39
+ flex-direction: row; /* Corresponds to flex-row in Tailwind */
40
+ width: 100%; /* Corresponds to w-full in Tailwind */
41
+ color: #3d414a;
42
+ }
43
+ .qbs-tree-list-container-sub-container-hideicon:hover {
44
+ color: #003c71;
45
+ background-color: rgba(141, 194, 240, 0.15) !important;
46
+ }
47
+ .qbs-tree-list-container-sub-container:hover {
48
+ color: #003c71;
49
+ background-color: rgba(141, 194, 240, 0.15) !important;
50
+ }
51
+ .qbs-tree-list-container-sub-container.qbs-expanded-child {
52
+ color: #003c71;
53
+ }
54
+ .qbs-tree-list-container-sub-text.qbs-expanded-child {
55
+ color: #003c71;
56
+ }
57
+ .qbs-tree-list-container-sub-text:hover {
58
+ color: #003c71;
59
+ /* background-color: rgba(141, 194, 240, 0.15) !important; */
60
+ }
61
+
62
+ .qbs-tree-list-container-sub-container.checked,
63
+ .qbs-tree-list-container-sub-container-hideicon.checked {
64
+ color: #003c71;
65
+ background-color: rgba(
66
+ 141,
67
+ 194,
68
+ 240,
69
+ 0.15
70
+ ) !important; /* Corresponds to bg-green-200 in Tailwind */
71
+ }
72
+
73
+ .marginLeft {
74
+ margin-left: 28px; /* Corresponds to ml-2 in Tailwind */
75
+ }
76
+ .lightmarginLeft {
77
+ margin-left: 19px; /* Corresponds to ml-2 in Tailwind */
78
+ }
79
+ .qbs-tree-list-container-sub- {
80
+ line-height: 18px; /* Corresponds to leading-[18px] in Tailwind */
81
+ font-size: 0.875rem; /* Corresponds to text-sm in Tailwind (14px) */
82
+ }
83
+ .qbs-tree-list-container-child {
84
+ margin-left: 1rem; /* Corresponds to ml-4 in Tailwind */
85
+ margin-top: 0.5rem; /* Corresponds to mt-2 in Tailwind */
86
+ display: flex; /* Corresponds to flex in Tailwind */
87
+ flex-direction: column; /* Corresponds to flex-col in Tailwind */
88
+ gap: 0.5rem; /* Corresponds to gap-2 in Tailwind */
89
+ }
90
+ .qbs-tree-list-container-sub-text {
91
+ line-height: 18px; /* Corresponds to leading-[18px] in Tailwind */
92
+ font-size: 14px; /* Corresponds to text-sm in Tailwind (14px) */
93
+ font-weight: 500;
94
+ letter-spacing: 0.28px;
95
+ font-family: 'Noto Sans';
96
+ /* color: #3d414a; */
97
+ }
@@ -0,0 +1,8 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @import './components/autocomplete.css';
6
+ @import './components/treedropdown.css';
7
+ @import './components/selectedautocomplete.css';
8
+ @import './components/tooltip.css';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-core-ts",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "React Components",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,11 @@
1
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
2
9
 
3
10
  import { AutoSuggestionInputProps } from './commontypes';
4
11
  import { useSuggestions } from './utilities/autosuggestions';
@@ -43,7 +50,7 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
43
50
  notDataMessage,
44
51
  onFocus,
45
52
  },
46
- ref
53
+ ref,
47
54
  ) => {
48
55
  const dropdownRef = useRef<HTMLDivElement>(null);
49
56
  // State Hooks Section
@@ -94,16 +101,17 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
94
101
  inputValue,
95
102
  isMultiple,
96
103
  setNextPage,
97
- selectedItems
104
+ selectedItems,
98
105
  // nextBlock
99
106
  );
100
107
 
101
108
  // Handling the selection of a suggestion
102
109
  const handleSuggestionClick = useCallback((suggestion: ValueProps) => {
110
+ isSelectingRef.current = true;
103
111
  if (isMultiple) {
104
112
  setSelectedItems((prev) => {
105
113
  const isAdded = prev.some(
106
- (item) => item[descId] === suggestion[descId]
114
+ (item) => item[descId] === suggestion[descId],
107
115
  );
108
116
  if (isAdded) {
109
117
  return prev.filter((item) => item[descId] !== suggestion[descId]);
@@ -134,7 +142,7 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
134
142
  } else {
135
143
  setSelectedItems((prev) => {
136
144
  return prev.filter(
137
- (item, i) => item[descId] !== suggestion[descId]
145
+ (item, i) => item[descId] !== suggestion[descId],
138
146
  );
139
147
  });
140
148
  }
@@ -156,7 +164,15 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
156
164
  setInputValue(value ?? '');
157
165
  }, [value]);
158
166
 
167
+ const isSelectingRef = useRef(false);
168
+
159
169
  const selectExactMatchFromSuggestions = () => {
170
+ // If user is clicking on dropdown, don't execute exact match logic
171
+ if (isSelectingRef.current) {
172
+ isSelectingRef.current = false;
173
+ return;
174
+ }
175
+
160
176
  if (!inputValue) {
161
177
  return;
162
178
  }
@@ -260,15 +276,15 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
260
276
  type,
261
277
  desc,
262
278
  selected,
263
- async
279
+ async,
264
280
  );
265
281
  const isSelected = (
266
282
  item: ValueProps,
267
- selectedItems: ValueProps[] | string
283
+ selectedItems: ValueProps[] | string,
268
284
  ): boolean => {
269
285
  if (Array.isArray(selectedItems)) {
270
286
  return selectedItems.some(
271
- (selectedItem) => selectedItem[desc] === item[desc]
287
+ (selectedItem) => selectedItem[desc] === item[desc],
272
288
  );
273
289
  } else {
274
290
  return item[desc] === selectedItems;
@@ -353,14 +369,26 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
353
369
  : searchValue || inputValue
354
370
  }
355
371
  onChange={handleChange}
356
- onBlur={() => {
357
- handleBlur();
358
- selectExactMatchFromSuggestions();
372
+ onBlur={(e) => {
373
+ // Delay to allow click events on dropdown to process first
374
+ setTimeout(() => {
375
+ // Check if focus moved to dropdown or if user is selecting
376
+ const relatedTarget = e.relatedTarget as Node;
377
+ if (
378
+ dropdownRef.current &&
379
+ relatedTarget &&
380
+ dropdownRef.current.contains(relatedTarget)
381
+ ) {
382
+ return; // Focus moved to dropdown, don't execute exact match
383
+ }
384
+ handleBlur();
385
+ selectExactMatchFromSuggestions();
386
+ }, 200);
359
387
  }}
360
388
  onFocus={onFocus}
361
389
  onClick={() => handleOnClick()}
362
390
  className={generateClassName()}
363
- placeholder={selectedItems?.length > 0 ? '' : placeholder ?? ''}
391
+ placeholder={selectedItems?.length > 0 ? '' : (placeholder ?? '')}
364
392
  readOnly={
365
393
  readOnly || type === 'custom_select' || type == 'auto_suggestion'
366
394
  }
@@ -469,6 +497,10 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
469
497
  className={`qbs-autocomplete-suggestions-item ${
470
498
  isSelected(suggestion, selected) ? 'is-selected' : ''
471
499
  }`}
500
+ onMouseDown={(e) => {
501
+ e.preventDefault(); // Prevent input blur before click
502
+ isSelectingRef.current = true;
503
+ }}
472
504
  onClick={() => handleSuggestionClick(suggestion)}
473
505
  data-testid={suggestion[desc]}
474
506
  >
@@ -523,7 +555,7 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
523
555
  )}
524
556
  </div>
525
557
  );
526
- }
558
+ },
527
559
  );
528
560
 
529
561
  export default React.memo(AutoComplete);
@@ -106,6 +106,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
106
106
  const [selectedIndex, setSelectedIndex] = useState(0);
107
107
  const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
108
108
  const scrollContainerRef = useRef<HTMLUListElement | null>(null);
109
+ const isSelectingRef = useRef(false);
109
110
  const [dropPosition, setDropPosition] = useState<any>({
110
111
  top: 0,
111
112
  left: 0,
@@ -329,6 +330,7 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
329
330
  setIsDisabled(disabled);
330
331
  }, [disabled]);
331
332
  const handleSuggestionClick = (suggestion: valueProps, index: number) => {
333
+ isSelectingRef.current = true;
332
334
  onChange({
333
335
  ...suggestion,
334
336
  id: suggestion?.id,
@@ -338,6 +340,10 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
338
340
  if (type !== 'custom_select') setInputValue(suggestion.name);
339
341
  setDropOpen(false);
340
342
  setSelectedIndex(index);
343
+ // Reset flag after a short delay to allow blur handler to check it
344
+ setTimeout(() => {
345
+ isSelectingRef.current = false;
346
+ }, 100);
341
347
  };
342
348
  const handleOpen = (e: any) => {
343
349
  if (!suggestions || suggestions?.length === 0) {
@@ -490,6 +496,12 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
490
496
  // return 'bottom'
491
497
  // }
492
498
  const selectExactMatchFromSuggestions = () => {
499
+ // If user is clicking on dropdown, don't execute exact match logic
500
+ if (isSelectingRef.current) {
501
+ isSelectingRef.current = false;
502
+ return;
503
+ }
504
+
493
505
  if (!inputValue) {
494
506
  return;
495
507
  }
@@ -630,7 +642,18 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
630
642
 
631
643
  case 'Enter':
632
644
  e.preventDefault();
633
- handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
645
+ if (filteredData[selectedIndex]) {
646
+ handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
647
+ }
648
+ break;
649
+
650
+ case 'Tab':
651
+ // Tab should select highlighted item if dropdown is open
652
+ if (filteredData[selectedIndex]) {
653
+ isSelectingRef.current = true;
654
+ handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
655
+ // Don't prevent default - allow Tab to move focus
656
+ }
634
657
  break;
635
658
 
636
659
  case 'Escape':
@@ -682,6 +705,10 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
682
705
  } cursor-pointer p-1 text-xxs ps-3.5 pl-[10px] qbs-autocomplete-suggections-items`}
683
706
  key={suggestion?.id}
684
707
  data-testid={suggestion.name}
708
+ onMouseDown={(e) => {
709
+ e.preventDefault(); // Prevent input blur before click
710
+ isSelectingRef.current = true;
711
+ }}
685
712
  onClick={() => handleSuggestionClick(suggestion, index)}
686
713
  tabIndex={index}
687
714
  ref={(el) => (itemRefs.current[index] = el)}
@@ -821,9 +848,25 @@ const ModernAutoComplete: React.FC<AutoSuggestionInputProps> = ({
821
848
  type="text"
822
849
  readOnly={readOnly ?? type === 'custom_select'}
823
850
  value={inputValue ? inputValue : ''}
824
- onBlur={() => {
825
- handleClearInputValue();
826
- selectExactMatchFromSuggestions();
851
+ onBlur={(e) => {
852
+ // Check immediately if user is selecting (Tab or click)
853
+ const wasSelecting = isSelectingRef.current;
854
+
855
+ // Delay to allow click events on dropdown to process first
856
+ setTimeout(() => {
857
+ // Check if focus moved to dropdown or if user is selecting
858
+ const relatedTarget = e.relatedTarget as Node;
859
+ if (
860
+ wasSelecting ||
861
+ (dropdownref.current &&
862
+ relatedTarget &&
863
+ dropdownref.current.contains(relatedTarget))
864
+ ) {
865
+ return; // User selected an item or focus moved to dropdown, don't execute exact match
866
+ }
867
+ handleClearInputValue();
868
+ selectExactMatchFromSuggestions();
869
+ }, 200);
827
870
  }}
828
871
  autoComplete="off"
829
872
  disabled={disabled}
@@ -109,6 +109,8 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
109
109
  const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
110
110
  const scrollContainerRef = useRef<HTMLUListElement | null>(null);
111
111
  const [refetchData, setRefetchData] = useState(false);
112
+
113
+ const isSelectingRef = useRef(false);
112
114
  const [dropPosition, setDropPosition] = useState<any>({
113
115
  top: 0,
114
116
  left: 0,
@@ -337,6 +339,7 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
337
339
  setIsDisabled(disabled);
338
340
  }, [disabled]);
339
341
  const handleSuggestionClick = (suggestion: valueProps, index: number) => {
342
+ isSelectingRef.current = true;
340
343
  onChange({
341
344
  ...suggestion,
342
345
  id: suggestion?.id,
@@ -346,6 +349,10 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
346
349
  if (type !== 'custom_select') setInputValue(suggestion.name);
347
350
  setDropOpen(false);
348
351
  setSelectedIndex(index);
352
+ // Reset flag after a short delay to allow blur handler to check it
353
+ setTimeout(() => {
354
+ isSelectingRef.current = false;
355
+ }, 100);
349
356
  };
350
357
  const handleOpen = (e: any) => {
351
358
  if (!suggestions || suggestions?.length === 0) handleDropData();
@@ -512,6 +519,12 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
512
519
  // return 'bottom'
513
520
  // }
514
521
  const selectExactMatchFromSuggestions = () => {
522
+ // If user is clicking on dropdown, don't execute exact match logic
523
+ if (isSelectingRef.current) {
524
+ isSelectingRef.current = false;
525
+ return;
526
+ }
527
+
515
528
  if (!inputValue) {
516
529
  return;
517
530
  }
@@ -652,7 +665,18 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
652
665
 
653
666
  case 'Enter':
654
667
  e.preventDefault();
655
- handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
668
+ if (filteredData[selectedIndex]) {
669
+ handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
670
+ }
671
+ break;
672
+
673
+ case 'Tab':
674
+ // Tab should select highlighted item if dropdown is open
675
+ if (filteredData[selectedIndex]) {
676
+ isSelectingRef.current = true;
677
+ handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
678
+ // Don't prevent default - allow Tab to move focus
679
+ }
656
680
  break;
657
681
 
658
682
  case 'Escape':
@@ -704,6 +728,10 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
704
728
  } cursor-pointer p-1 text-xxs ps-3.5 pl-[10px] qbs-autocomplete-suggections-items`}
705
729
  key={suggestion?.id}
706
730
  data-testid={suggestion.name}
731
+ onMouseDown={(e) => {
732
+ e.preventDefault(); // Prevent input blur before click
733
+ isSelectingRef.current = true;
734
+ }}
707
735
  onClick={() => handleSuggestionClick(suggestion, index)}
708
736
  tabIndex={index}
709
737
  ref={(el) => (itemRefs.current[index] = el)}
@@ -844,9 +872,25 @@ const ModernAutoCompleteDropdown: React.FC<AutoSuggestionInputProps> = ({
844
872
  type="text"
845
873
  readOnly={readOnly ?? type === 'custom_select'}
846
874
  value={inputValue ? inputValue : ''}
847
- onBlur={() => {
848
- handleClearInputValue();
849
- selectExactMatchFromSuggestions();
875
+ onBlur={(e) => {
876
+ // Check immediately if user is selecting (Tab or click)
877
+ const wasSelecting = isSelectingRef.current;
878
+
879
+ // Delay to allow click events on dropdown to process first
880
+ setTimeout(() => {
881
+ // Check if focus moved to dropdown or if user is selecting
882
+ const relatedTarget = e.relatedTarget as Node;
883
+ if (
884
+ wasSelecting ||
885
+ (dropdownref.current &&
886
+ relatedTarget &&
887
+ dropdownref.current.contains(relatedTarget))
888
+ ) {
889
+ return; // User selected an item or focus moved to dropdown, don't execute exact match
890
+ }
891
+ handleClearInputValue();
892
+ selectExactMatchFromSuggestions();
893
+ }, 200);
850
894
  }}
851
895
  autoComplete="off"
852
896
  disabled={disabled}