react-core-ts 2.0.2 → 2.0.4

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.
@@ -24,6 +24,9 @@ const DropdownFilterTabs = forwardRef<
24
24
  AutoSuggestionInputProps & {
25
25
  dropdownRef?: React.RefObject<HTMLDivElement>;
26
26
  open?: boolean;
27
+ applyTabFilter?: () => void;
28
+ onToolClose?: () => void;
29
+
27
30
  }
28
31
  >(
29
32
  (
@@ -81,6 +84,8 @@ const DropdownFilterTabs = forwardRef<
81
84
  enableSelectAll = false,
82
85
  dropdownRef,
83
86
  open = false,
87
+ applyTabFilter,
88
+ onToolClose
84
89
  },
85
90
  ref
86
91
  ) => {
@@ -121,6 +126,7 @@ const DropdownFilterTabs = forwardRef<
121
126
  const [selectAll, setSelectAll] = useState(false);
122
127
  const [refetchData, setRefetchData] = useState(false);
123
128
  const [allDataLoaded, setAllDataLoaded] = useState(false);
129
+ const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);
124
130
 
125
131
  // Filter selected items based on current active tab
126
132
  const currentTabId = tab.length > 0 ? tab[activeTab].id : undefined;
@@ -128,8 +134,6 @@ const DropdownFilterTabs = forwardRef<
128
134
  return selectedItems.filter(item => item.tabId === currentTabId);
129
135
  }, [selectedItems, currentTabId]);
130
136
 
131
- let originalOverflow = '';
132
- let hasScrollbar = false;
133
137
 
134
138
  useEffect(() => {
135
139
  setDropOpen(open);
@@ -347,10 +351,6 @@ const DropdownFilterTabs = forwardRef<
347
351
  if (async && type === 'auto_suggestion' && tabInlineSearch) resetSuggections?.();
348
352
  };
349
353
 
350
- const generateClassName = useCallback(() => {
351
- return `qbs-textfield-default ${className} ${errors && errors?.message ? 'textfield-error' : 'textfield'
352
- } ${expandable ? 'expandable' : ''}`;
353
- }, [errors, name]);
354
354
  const handleRemoveSelectedItem = useCallback((index: number) => {
355
355
  const itemToRemove = filteredSelectedItems[index];
356
356
  setSelectedItems((prev) => {
@@ -361,48 +361,130 @@ const DropdownFilterTabs = forwardRef<
361
361
  }, [filteredSelectedItems, descId]);
362
362
 
363
363
  const uniqueDropArrowId = `${name}-drop-arrow-selected-list-icon`;
364
+ const isKeyboardOpenRef = useRef(false);
365
+
366
+ // Keep ref in sync with state for use in event handlers
367
+ useEffect(() => {
368
+ isKeyboardOpenRef.current = isKeyboardOpen;
369
+ }, [isKeyboardOpen]);
370
+
371
+ // Detect keyboard open on mobile using Visual Viewport API
364
372
  useEffect(() => {
373
+ const vv = (window as any).visualViewport;
374
+ if (!vv) return;
375
+ const onResize = () => {
376
+ // Heuristic: if visual viewport height shrinks a lot, keyboard is likely open
377
+ const open = vv.height < window.innerHeight - 100;
378
+ setIsKeyboardOpen(open);
379
+ };
380
+ vv.addEventListener("resize", onResize);
381
+ vv.addEventListener("scroll", onResize); // iOS sometimes fires scroll instead
382
+ onResize();
383
+ return () => {
384
+ vv.removeEventListener("resize", onResize);
385
+ vv.removeEventListener("scroll", onResize);
386
+ };
387
+ }, []);
388
+
389
+ useEffect(() => {
390
+ const handleScroll = (event: any) => {
391
+ // On mobile: Don't close dropdown on scroll when keyboard is open
392
+ // On desktop: Always allow closing on scroll
393
+ if (isKeyboardOpenRef.current) {
394
+ // Only prevent closing if keyboard is actually open (mobile scenario)
395
+ // Also check if input is focused to avoid closing while typing
396
+ const activeElement = document.activeElement;
397
+ if (activeElement && (
398
+ activeElement === inputRef.current ||
399
+ activeElement === inputSearchRef.current ||
400
+ (dropRef.current && dropRef.current.contains(activeElement))
401
+ )) {
402
+ return;
403
+ }
404
+ }
405
+
406
+ // Check if scroll is happening within the dropdown itself - if so, don't close
407
+ const scrollTarget = event.target;
408
+ if (dropRef.current && scrollTarget instanceof Node && dropRef.current.contains(scrollTarget)) {
409
+ return;
410
+ }
411
+
412
+ // On desktop (keyboard not open), close on scroll
413
+ if (dropRef.current && dropOpen) {
414
+ setTimeout(() => {
415
+ setDropOpen(false);
416
+ onToolClose?.();
417
+ if (!typeOnlyFetch) setSearchValue('');
418
+ }, 200);
419
+ }
420
+ };
421
+
365
422
  const handleClickOutside = (event: any) => {
423
+ const target = event.target as HTMLElement;
424
+
425
+ // When keyboard is open, be more lenient - only close on clear outside clicks
426
+ if (isKeyboardOpenRef.current) {
427
+ // Only close if it's a clear click outside, not on input/textarea elements
428
+ if (
429
+ dropRef.current &&
430
+ target instanceof Node &&
431
+ !dropRef.current.contains(target) &&
432
+ target?.id !== uniqueDropArrowId &&
433
+ event.type === 'mousedown' &&
434
+ target.closest &&
435
+ !target.closest('input') &&
436
+ !target.closest('textarea')
437
+ ) {
438
+ setTimeout(() => {
439
+ setDropOpen(false);
440
+ onToolClose?.();
441
+ if (!typeOnlyFetch) setSearchValue('');
442
+ }, 200);
443
+ }
444
+ return;
445
+ }
446
+
447
+ // Normal behavior when keyboard is not open
366
448
  if (
367
449
  dropRef.current &&
368
- event.target instanceof Node &&
369
- !dropRef.current.contains(event.target) &&
370
- event.target?.id !== uniqueDropArrowId
450
+ target instanceof Node &&
451
+ !dropRef.current.contains(target) &&
452
+ target?.id !== uniqueDropArrowId
371
453
  ) {
372
454
  setTimeout(() => {
373
455
  setDropOpen(false);
456
+ onToolClose?.();
374
457
  if (!typeOnlyFetch) setSearchValue('');
375
458
  }, 200);
376
- //setSearchValue("");
377
459
  }
378
460
  };
379
461
 
380
462
  document.addEventListener('mousedown', handleClickOutside as any);
381
- window.addEventListener('scroll', handleClickOutside as any);
463
+ window.addEventListener('scroll', handleScroll as any);
382
464
 
383
465
  const scrollableDivs = document.querySelectorAll(
384
466
  'div[style*="overflow"], .overflow-auto, .overflow-y-auto, .overflow-x-auto'
385
467
  );
386
468
  scrollableDivs.forEach((div) =>
387
- div.addEventListener('scroll', handleClickOutside as any)
469
+ div.addEventListener('scroll', handleScroll as any)
388
470
  );
389
471
  if (scrollRef && scrollRef.current && scrollRef.current !== null) {
390
- scrollRef.current.addEventListener('scroll', handleClickOutside as any);
472
+ scrollRef.current.addEventListener('scroll', handleScroll as any);
391
473
  }
392
474
  return () => {
393
475
  document.removeEventListener('mousedown', handleClickOutside as any);
394
- window.removeEventListener('scroll', handleClickOutside as any);
476
+ window.removeEventListener('scroll', handleScroll as any);
395
477
  scrollableDivs.forEach((div) =>
396
- div.removeEventListener('scroll', handleClickOutside as any)
478
+ div.removeEventListener('scroll', handleScroll as any)
397
479
  );
398
480
  if (scrollRef && scrollRef.current && scrollRef.current !== null) {
399
481
  scrollRef.current.removeEventListener(
400
482
  'scroll',
401
- handleClickOutside as any
483
+ handleScroll as any
402
484
  );
403
485
  }
404
486
  };
405
- }, []);
487
+ }, [dropOpen, typeOnlyFetch, uniqueDropArrowId, onToolClose]);
406
488
 
407
489
  // Filtering suggestions based on type and search value
408
490
  const selected: any = isMultiple ? selectedItems : inputValue;
@@ -745,6 +827,11 @@ const DropdownFilterTabs = forwardRef<
745
827
  }
746
828
  };
747
829
 
830
+ const handleApplySelected = () => {
831
+ setDropOpen(false);
832
+ applyTabFilter?.();
833
+ };
834
+
748
835
  useEffect(() => {
749
836
  const allSelected =
750
837
  filteredData?.length > 0 && filteredSelectedItems?.length > 0 &&
@@ -959,10 +1046,10 @@ const DropdownFilterTabs = forwardRef<
959
1046
  className={`qbs-done-link qbs-text-right qbs-cursor-pointer ${viewMode ? 'hidden' : ''
960
1047
  }`}
961
1048
  onClick={() => {
962
- setDropOpen(false);
1049
+ handleApplySelected();
963
1050
  }}
964
1051
  >
965
- Done
1052
+ Apply
966
1053
  </div>
967
1054
  </div>
968
1055
 
@@ -113,7 +113,6 @@ const AutoCompleteWithSelectedList = forwardRef<
113
113
  if (tab.length > 0) {
114
114
  if (tabRef?.current) dropdownHeight += tabRef?.current?.clientHeight;
115
115
  }
116
- console.log(dropdownHeight);
117
116
  if (spaceBelow >= dropdownHeight) {
118
117
  dropdownPosition.top =
119
118
  inputRect.top + window.scrollY + inputRect.height;
@@ -382,6 +382,21 @@ ul {
382
382
  color: #e76458;
383
383
  }
384
384
 
385
+ .qbs-inline-all-dropdown-btn {
386
+ display: flex;
387
+ gap: 12px;
388
+ align-items: center;
389
+ position: absolute;
390
+ right: 25px;
391
+ z-index: 99;
392
+ top: 18px;
393
+ flex-wrap: wrap;
394
+ }
395
+
396
+ .qbs-inline-all-direct-dropdown-btn {
397
+ right: 0px;
398
+ }
399
+
385
400
  .selected-items-container {
386
401
  display: flex;
387
402
  gap: 12px;
@@ -416,6 +431,18 @@ ul {
416
431
  color: #fff;
417
432
  }
418
433
 
434
+ .selected-item-inline-counter {
435
+ right: 30px;
436
+ }
437
+
438
+ .selected-item-tool-direct-counter {
439
+ right: 60px;
440
+ }
441
+
442
+ .selected-item-nontool-direct-counter {
443
+ right: 35px;
444
+ }
445
+
419
446
  .selected-item-more {
420
447
  display: flex;
421
448
  align-items: center;
@@ -11,7 +11,6 @@ const ToolTip: React.FC<any> = ({ title, children }) => {
11
11
 
12
12
  const spaceAbove = inputBoxRect.top;
13
13
  const spaceBelow = viewportHeight - inputBoxRect.bottom;
14
- console.log(spaceAbove, spaceBelow);
15
14
  if (spaceAbove > spaceBelow) {
16
15
  if (spaceAbove > 90 && spaceBelow < 120) {
17
16
  setDropdownPosition('top-position');