react-core-ts 2.0.24 → 2.1.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.
@@ -144,6 +144,7 @@ const DropdownFilterTabs = forwardRef<
144
144
  const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);
145
145
  const [hasMoreOptionValues, setHasMoreOptionValues] = useState(false);
146
146
  const [tabsScrollable, setTabsScrollable] = useState(false);
147
+ const scrollableDivsRef = useRef<NodeListOf<Element> | null>(null);
147
148
 
148
149
  // Use external reset key if provided, otherwise default to 0
149
150
  const currentResetKey = externalResetKey !== undefined ? externalResetKey : 0;
@@ -170,9 +171,16 @@ const DropdownFilterTabs = forwardRef<
170
171
  setActiveTab(currentTab);
171
172
  }, [currentTab]);
172
173
 
174
+ // Reset focused index when active tab changes
175
+ useEffect(() => {
176
+ setFocusedIndex(0);
177
+ }, [activeTab]);
178
+
173
179
  const adjustDropdownPosition = () => {
174
- if (dropdownRef?.current) {
175
- const inputRect = dropdownRef?.current?.getBoundingClientRect();
180
+ // Use requestAnimationFrame to ensure DOM is ready
181
+ requestAnimationFrame(() => {
182
+ if (dropdownRef?.current) {
183
+ const inputRect = dropdownRef?.current?.getBoundingClientRect();
176
184
  const dropdownPosition = {
177
185
  left: inputRect.left + window.scrollX,
178
186
  width: inputRect.width,
@@ -248,14 +256,14 @@ const DropdownFilterTabs = forwardRef<
248
256
  // setDropdownStyle({
249
257
  // ...dropdownPosition,
250
258
  // });
251
- setDropdownStyle((prevStyle) => ({
252
- ...prevStyle,
253
- transform: `translateY(${dropdownPosition.top}px)`,
254
- left: desiredLeft,
255
- width: desiredWidth,
256
- }));
257
-
258
- }
259
+ setDropdownStyle((prevStyle) => ({
260
+ ...prevStyle,
261
+ transform: `translateY(${dropdownPosition.top}px)`,
262
+ left: desiredLeft,
263
+ width: desiredWidth,
264
+ }));
265
+ }
266
+ });
259
267
  };
260
268
 
261
269
  useEffect(() => {
@@ -552,22 +560,44 @@ const DropdownFilterTabs = forwardRef<
552
560
  // Capture-phase scroll catches scrolls from any scroll container (scroll doesn't bubble).
553
561
  document.addEventListener('scroll', handleScroll as any, true);
554
562
 
555
- const scrollableDivs = document.querySelectorAll(
556
- 'div[style*="overflow"], .overflow-auto, .overflow-y-auto, .overflow-x-auto'
557
- );
558
- scrollableDivs.forEach((div) =>
563
+ // Query scrollable divs and store in ref for cleanup
564
+ // Re-query when dropdown opens to catch dynamically added containers
565
+ const queryScrollableDivs = () => {
566
+ return document.querySelectorAll(
567
+ 'div[style*="overflow"], .overflow-auto, .overflow-y-auto, .overflow-x-auto'
568
+ );
569
+ };
570
+
571
+ // Clean up previous listeners if any
572
+ if (scrollableDivsRef.current) {
573
+ scrollableDivsRef.current.forEach((div) =>
574
+ div.removeEventListener('scroll', handleScroll as any)
575
+ );
576
+ }
577
+
578
+ // Query and attach listeners to scrollable divs
579
+ scrollableDivsRef.current = queryScrollableDivs();
580
+ scrollableDivsRef.current.forEach((div) =>
559
581
  div.addEventListener('scroll', handleScroll as any)
560
582
  );
583
+
561
584
  if (scrollRef && scrollRef.current && scrollRef.current !== null) {
562
585
  scrollRef.current.addEventListener('scroll', handleScroll as any);
563
586
  }
587
+
564
588
  return () => {
565
589
  document.removeEventListener('mousedown', handleClickOutside as any);
566
590
  window.removeEventListener('scroll', handleScroll as any);
567
591
  document.removeEventListener('scroll', handleScroll as any, true);
568
- scrollableDivs.forEach((div) =>
569
- div.removeEventListener('scroll', handleScroll as any)
570
- );
592
+
593
+ // Clean up scrollable divs listeners
594
+ if (scrollableDivsRef.current) {
595
+ scrollableDivsRef.current.forEach((div) =>
596
+ div.removeEventListener('scroll', handleScroll as any)
597
+ );
598
+ scrollableDivsRef.current = null;
599
+ }
600
+
571
601
  if (closeOnScrollTimeoutRef.current) {
572
602
  window.clearTimeout(closeOnScrollTimeoutRef.current);
573
603
  closeOnScrollTimeoutRef.current = null;
@@ -582,7 +612,10 @@ const DropdownFilterTabs = forwardRef<
582
612
  }, [dropOpen, typeOnlyFetch, uniqueDropArrowId, onToolClose]);
583
613
 
584
614
  // Filtering suggestions based on type and search value
585
- const selected: any = isMultiple ? selectedItems : inputValue;
615
+ // Use filteredSelectedItems when tabs are present, otherwise use selectedItems
616
+ const selected: any = isMultiple
617
+ ? (tab.length > 0 ? filteredSelectedItems : selectedItems)
618
+ : inputValue;
586
619
  const filteredData: ValueProps[] = filterSuggestions(
587
620
  suggestions,
588
621
  searchValue,
@@ -607,18 +640,22 @@ const DropdownFilterTabs = forwardRef<
607
640
  prevSelectedItemsRef.current = selectedItems;
608
641
  }
609
642
  }
610
- adjustDropdownPosition();
611
- setTimeout(() => {
612
- adjustDropdownPosition();
613
- }, 200);
643
+ // Use double requestAnimationFrame to ensure DOM updates are complete
644
+ requestAnimationFrame(() => {
645
+ requestAnimationFrame(() => {
646
+ adjustDropdownPosition();
647
+ });
648
+ });
614
649
  // eslint-disable-next-line react-hooks/exhaustive-deps
615
650
  }, [selectedItems]);
616
651
 
617
652
  useEffect(() => {
618
- adjustDropdownPosition();
619
- setTimeout(() => {
620
- adjustDropdownPosition();
621
- }, 200);
653
+ // Use double requestAnimationFrame to ensure DOM updates are complete
654
+ requestAnimationFrame(() => {
655
+ requestAnimationFrame(() => {
656
+ adjustDropdownPosition();
657
+ });
658
+ });
622
659
  // eslint-disable-next-line react-hooks/exhaustive-deps
623
660
  }, [filteredData]);
624
661
 
@@ -681,19 +718,19 @@ const DropdownFilterTabs = forwardRef<
681
718
  };
682
719
  }, [filteredData, dropOpen, isLoading, focusedIndex]);
683
720
 
684
- const isSelected = (
721
+ const isSelected = useCallback((
685
722
  item: ValueProps,
686
723
  selectedItems: ValueProps[] | string
687
724
  ): boolean => {
688
725
  if (Array.isArray(selectedItems)) {
689
726
  return selectedItems.some((selectedItem) => {
690
- // If tabId is present, ensure the selected item belongs to the current tab
691
- if (
692
- currentTabId !== undefined &&
693
- selectedItem?.tabId !== undefined &&
694
- selectedItem.tabId !== currentTabId
695
- ) {
696
- return false;
727
+ // When tabs are present, ensure the selected item belongs to the current tab
728
+ if (tab.length > 0 && currentTabId !== undefined) {
729
+ // Only match if the selectedItem's tabId matches the current tab
730
+ // If selectedItem doesn't have tabId, don't match it (it belongs to no tab or old data)
731
+ if (selectedItem?.tabId === undefined || selectedItem.tabId !== currentTabId) {
732
+ return false;
733
+ }
697
734
  }
698
735
 
699
736
  return (
@@ -709,7 +746,7 @@ const DropdownFilterTabs = forwardRef<
709
746
  getKeyValue(item, descId, 'id') === safeToLowerString(selectedItems)
710
747
  );
711
748
  }
712
- };
749
+ }, [tab, currentTabId, desc, descId]);
713
750
 
714
751
  const handleLoadMore = () => {
715
752
  if (paginationEnabled) {
@@ -761,6 +798,7 @@ const DropdownFilterTabs = forwardRef<
761
798
  handlePickSuggestions(searchValue, 1, false, activeTabVal);
762
799
  }
763
800
  setActiveTab(index);
801
+ setFocusedIndex(0); // Reset focused index when switching tabs
764
802
  }
765
803
  }, [activeTab, clearTabSwitch, handleClearSelected, tabInlineSearch, tab, searchValue, handlePickSuggestions, resetSuggections]);
766
804
 
package/src/index.tsx CHANGED
@@ -10,6 +10,7 @@ export { default as AutoCompleteWithTreeStructure } from './AutoCompleteWithTree
10
10
  export type { AutoSuggestionInputProps } from './commontypes';
11
11
  export { default as ExpandableAutoComplete } from './ExpandableAutoComplete';
12
12
  export { default as ModernAutoComplete } from './ReactAutoComplete';
13
+ export { default as ModernAutoCompleteSuggections } from './ReactAutoCompleteSuggections';
13
14
  export { default as ModernCheckBox } from './ReactCheckbox';
14
15
  export { default as ModernOtpInput } from './ReactOTPInput';
15
16
  export { default as ModernRadio } from './ReactRadioButton';