zcomponents-ui 1.5.12 → 1.5.14

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.
@@ -375,7 +375,6 @@ const getElementVerticalBorders = (el) => {
375
375
  };
376
376
 
377
377
  const minUsableHeight = 50;
378
- const subPixelSafetyBufferPx = 2;
379
378
  const calculateHeightForTop = (scrollHeight, maxHeightLimiter, wrapperMaxHeight) => {
380
379
  const currentHeightLimiter = maxHeightLimiter < wrapperMaxHeight ? maxHeightLimiter : wrapperMaxHeight;
381
380
  return scrollHeight > currentHeightLimiter
@@ -398,14 +397,16 @@ const ZDropListWrapper = (props) => {
398
397
  const wrapperClasses = classNames(styles["zd__list-wrapper"], className);
399
398
  const getWrapperVerticalSpacing = react.useCallback(() => {
400
399
  const wrapper = listWrapperRef.current;
401
- if (!wrapper)
400
+ if (!wrapper) {
402
401
  return 0;
402
+ }
403
403
  return (getElementVerticalMargins(wrapper) + getElementVerticalBorders(wrapper));
404
404
  }, []);
405
405
  const getWrapperVerticalExtrasPx = react.useCallback(() => {
406
406
  const wrapper = listWrapperRef.current;
407
- if (!wrapper)
407
+ if (!wrapper) {
408
408
  return 0;
409
+ }
409
410
  const bordersPx = getElementVerticalBorders(wrapper);
410
411
  const cs = window.getComputedStyle(wrapper);
411
412
  const paddingTopPx = parseFloat(cs.paddingTop || "0") || 0;
@@ -424,10 +425,66 @@ const ZDropListWrapper = (props) => {
424
425
  setMeasuredContentHeight(0);
425
426
  setAnimatedWrapperHeightPx(0);
426
427
  }, [optionsCount]);
428
+ const measureVisualContentHeightPx = react.useCallback((wrapper, wrapperVerticalExtrasPx) => {
429
+ const prevHeight = wrapper.style.height;
430
+ const prevMaxHeight = wrapper.style.maxHeight;
431
+ wrapper.style.height = "auto";
432
+ wrapper.style.maxHeight = "none";
433
+ wrapper.offsetHeight;
434
+ const wrapperRect = wrapper.getBoundingClientRect();
435
+ const childrenElements = Array.from(wrapper.children);
436
+ if (childrenElements.length === 0) {
437
+ const height = (wrapper.scrollHeight || 0) + wrapperVerticalExtrasPx;
438
+ wrapper.style.height = prevHeight;
439
+ wrapper.style.maxHeight = prevMaxHeight;
440
+ return Math.max(0, height);
441
+ }
442
+ let minTop = Number.POSITIVE_INFINITY;
443
+ let maxBottom = Number.NEGATIVE_INFINITY;
444
+ let topMostEl = null;
445
+ let bottomMostEl = null;
446
+ let topMostTop = Number.POSITIVE_INFINITY;
447
+ let bottomMostBottom = Number.NEGATIVE_INFINITY;
448
+ for (const child of childrenElements) {
449
+ const rect = child.getBoundingClientRect();
450
+ const topRel = rect.top - wrapperRect.top;
451
+ const bottomRel = rect.bottom - wrapperRect.top;
452
+ if (topRel < minTop) {
453
+ minTop = topRel;
454
+ }
455
+ if (bottomRel > maxBottom) {
456
+ maxBottom = bottomRel;
457
+ }
458
+ if (rect.top < topMostTop) {
459
+ topMostTop = rect.top;
460
+ topMostEl = child;
461
+ }
462
+ if (rect.bottom > bottomMostBottom) {
463
+ bottomMostBottom = rect.bottom;
464
+ bottomMostEl = child;
465
+ }
466
+ }
467
+ let topMarginPx = 0;
468
+ let bottomMarginPx = 0;
469
+ if (topMostEl) {
470
+ const cs = window.getComputedStyle(topMostEl);
471
+ topMarginPx = parseFloat(cs.marginTop || "0") || 0;
472
+ }
473
+ if (bottomMostEl) {
474
+ const cs = window.getComputedStyle(bottomMostEl);
475
+ bottomMarginPx = parseFloat(cs.marginBottom || "0") || 0;
476
+ }
477
+ const unionHeight = Math.max(0, maxBottom - minTop);
478
+ const total = unionHeight + topMarginPx + bottomMarginPx + wrapperVerticalExtrasPx;
479
+ wrapper.style.height = prevHeight;
480
+ wrapper.style.maxHeight = prevMaxHeight;
481
+ return Math.max(0, total);
482
+ }, []);
427
483
  const measureContentHeightPx = react.useCallback(() => {
428
484
  const wrapper = listWrapperRef.current;
429
- if (!wrapper)
485
+ if (!wrapper) {
430
486
  return 0;
487
+ }
431
488
  const wrapperVerticalExtrasPx = getWrapperVerticalExtrasPx();
432
489
  if (optionsCount > 0) {
433
490
  const firstListItem = wrapper.querySelector("li");
@@ -446,31 +503,8 @@ const ZDropListWrapper = (props) => {
446
503
  }
447
504
  }
448
505
  cachedListItemHeightRef.current = null;
449
- const firstChild = wrapper.firstElementChild;
450
- if (!firstChild) {
451
- return wrapperVerticalExtrasPx;
452
- }
453
- const previousInlineHeight = wrapper.style.height;
454
- const previousInlineMaxHeight = wrapper.style.maxHeight;
455
- wrapper.style.height = "auto";
456
- wrapper.style.maxHeight = "none";
457
- wrapper.offsetHeight;
458
- let contentRawPx = 0;
459
- const childScrollHeight = firstChild.scrollHeight || 0;
460
- if (childScrollHeight > 0) {
461
- contentRawPx = childScrollHeight;
462
- }
463
- else {
464
- const rectHeightPx = firstChild.getBoundingClientRect().height || 0;
465
- const childComputedStyles = window.getComputedStyle(firstChild);
466
- const childMarginTopPx = parseFloat(childComputedStyles.marginTop || "0") || 0;
467
- const childMarginBottomPx = parseFloat(childComputedStyles.marginBottom || "0") || 0;
468
- contentRawPx = rectHeightPx + childMarginTopPx + childMarginBottomPx;
469
- }
470
- wrapper.style.height = previousInlineHeight;
471
- wrapper.style.maxHeight = previousInlineMaxHeight;
472
- return Math.max(0, contentRawPx + wrapperVerticalExtrasPx);
473
- }, [optionsCount, getWrapperVerticalExtrasPx]);
506
+ return measureVisualContentHeightPx(wrapper, wrapperVerticalExtrasPx);
507
+ }, [optionsCount, getWrapperVerticalExtrasPx, measureVisualContentHeightPx]);
474
508
  const updateMaxSpaces = react.useCallback(() => {
475
509
  const wrapper = listWrapperRef.current;
476
510
  if (!wrapper) {
@@ -485,10 +519,26 @@ const ZDropListWrapper = (props) => {
485
519
  return;
486
520
  }
487
521
  const elementVerticalSpacing = getWrapperVerticalSpacing();
488
- const anchorTop = getElementOffsetTop(anchor);
489
- const anchorBottom = anchorTop + anchor.offsetHeight;
490
- const above = Math.max(anchorTop - reference.top - elementVerticalSpacing, 0);
491
- const below = Math.max(reference.bottom - anchorBottom - elementVerticalSpacing, 0);
522
+ const anchorRect = anchor.getBoundingClientRect();
523
+ const referenceEl = referenceElementClassName
524
+ ? anchor.closest(`.${referenceElementClassName}`)
525
+ : null;
526
+ const refRect = referenceEl
527
+ ? referenceEl.getBoundingClientRect()
528
+ : { top: reference.top, bottom: reference.bottom };
529
+ let refContentTop = refRect.top;
530
+ let refContentBottom = refRect.bottom;
531
+ if (referenceEl) {
532
+ const cs = window.getComputedStyle(referenceEl);
533
+ const paddingTop = parseFloat(cs.paddingTop || "0") || 0;
534
+ const paddingBottom = parseFloat(cs.paddingBottom || "0") || 0;
535
+ const borderTop = parseFloat(cs.borderTopWidth || "0") || 0;
536
+ const borderBottom = parseFloat(cs.borderBottomWidth || "0") || 0;
537
+ refContentTop = refRect.top + borderTop + paddingTop;
538
+ refContentBottom = refRect.bottom - borderBottom - paddingBottom;
539
+ }
540
+ const above = Math.max(anchorRect.top - refContentTop - elementVerticalSpacing, 0);
541
+ const below = Math.max(refContentBottom - anchorRect.bottom - elementVerticalSpacing, 0);
492
542
  if (lastAboveRef.current !== above) {
493
543
  lastAboveRef.current = above;
494
544
  setMaxSpaceAbove(above);
@@ -498,16 +548,55 @@ const ZDropListWrapper = (props) => {
498
548
  setMaxSpaceBelow(below);
499
549
  }
500
550
  }, [referenceElementClassName, getWrapperVerticalSpacing]);
551
+ const runMeasureTick = react.useCallback(() => {
552
+ updateMaxSpaces();
553
+ const next = measureContentHeightPx();
554
+ if (lastMeasuredContentHeightRef.current !== next) {
555
+ lastMeasuredContentHeightRef.current = next;
556
+ setMeasuredContentHeight(next);
557
+ }
558
+ }, [updateMaxSpaces, measureContentHeightPx]);
559
+ react.useLayoutEffect(() => {
560
+ const wrapper = listWrapperRef.current;
561
+ if (!wrapper) {
562
+ return;
563
+ }
564
+ let rafId = 0;
565
+ const schedule = () => {
566
+ cancelAnimationFrame(rafId);
567
+ rafId = requestAnimationFrame(() => {
568
+ runMeasureTick();
569
+ });
570
+ };
571
+ let ro = null;
572
+ let mo = null;
573
+ const attachResizeObserver = () => {
574
+ var _a;
575
+ ro === null || ro === void 0 ? void 0 : ro.disconnect();
576
+ ro = new ResizeObserver(() => schedule());
577
+ const target = (_a = wrapper.firstElementChild) !== null && _a !== void 0 ? _a : wrapper;
578
+ ro.observe(target);
579
+ };
580
+ mo = new MutationObserver(() => {
581
+ attachResizeObserver();
582
+ schedule();
583
+ });
584
+ mo.observe(wrapper, { childList: true, subtree: true });
585
+ attachResizeObserver();
586
+ schedule();
587
+ return () => {
588
+ cancelAnimationFrame(rafId);
589
+ ro === null || ro === void 0 ? void 0 : ro.disconnect();
590
+ mo === null || mo === void 0 ? void 0 : mo.disconnect();
591
+ };
592
+ }, [runMeasureTick]);
501
593
  react.useLayoutEffect(() => {
502
594
  resetLocalCachesAndGuards();
503
595
  let raf1 = 0;
504
596
  let raf2 = 0;
505
597
  raf1 = requestAnimationFrame(() => {
506
598
  raf2 = requestAnimationFrame(() => {
507
- updateMaxSpaces();
508
- const nextHeight = measureContentHeightPx();
509
- lastMeasuredContentHeightRef.current = nextHeight;
510
- setMeasuredContentHeight(nextHeight);
599
+ runMeasureTick();
511
600
  });
512
601
  });
513
602
  return () => {
@@ -517,8 +606,9 @@ const ZDropListWrapper = (props) => {
517
606
  // eslint-disable-next-line react-hooks/exhaustive-deps
518
607
  }, []);
519
608
  react.useLayoutEffect(() => {
520
- if (!listWrapperRef.current)
609
+ if (!listWrapperRef.current) {
521
610
  return;
611
+ }
522
612
  const previousCount = previousOptionsCountRef.current;
523
613
  previousOptionsCountRef.current = optionsCount;
524
614
  const isSwitchingBetweenListAndFallback = (previousCount > 0 && optionsCount === 0) ||
@@ -526,47 +616,43 @@ const ZDropListWrapper = (props) => {
526
616
  cachedListItemHeightRef.current = null;
527
617
  let raf1 = 0;
528
618
  let raf2 = 0;
529
- const runMeasure = () => {
530
- const next = measureContentHeightPx();
531
- if (lastMeasuredContentHeightRef.current !== next) {
532
- lastMeasuredContentHeightRef.current = next;
533
- setMeasuredContentHeight(next);
534
- }
535
- };
619
+ let raf3 = 0;
536
620
  raf1 = requestAnimationFrame(() => {
537
- if (isSwitchingBetweenListAndFallback) {
538
- raf2 = requestAnimationFrame(() => runMeasure());
539
- }
540
- else {
541
- runMeasure();
542
- }
621
+ runMeasureTick();
622
+ raf2 = requestAnimationFrame(() => {
623
+ if (isSwitchingBetweenListAndFallback)
624
+ runMeasureTick();
625
+ raf3 = requestAnimationFrame(() => {
626
+ if (isSwitchingBetweenListAndFallback)
627
+ runMeasureTick();
628
+ });
629
+ });
543
630
  });
544
631
  return () => {
545
632
  cancelAnimationFrame(raf1);
546
633
  cancelAnimationFrame(raf2);
634
+ cancelAnimationFrame(raf3);
547
635
  };
548
- }, [optionsCount, measureContentHeightPx]);
636
+ }, [optionsCount, runMeasureTick]);
549
637
  react.useLayoutEffect(() => {
550
- if (!listWrapperRef.current)
638
+ if (!listWrapperRef.current) {
551
639
  return;
552
- if (optionsCount > 0)
640
+ }
641
+ if (optionsCount > 0) {
553
642
  return;
643
+ }
554
644
  let raf1 = 0;
555
645
  let raf2 = 0;
556
646
  raf1 = requestAnimationFrame(() => {
557
647
  raf2 = requestAnimationFrame(() => {
558
- const next = measureContentHeightPx();
559
- if (lastMeasuredContentHeightRef.current !== next) {
560
- lastMeasuredContentHeightRef.current = next;
561
- setMeasuredContentHeight(next);
562
- }
648
+ runMeasureTick();
563
649
  });
564
650
  });
565
651
  return () => {
566
652
  cancelAnimationFrame(raf1);
567
653
  cancelAnimationFrame(raf2);
568
654
  };
569
- }, [children, optionsCount, measureContentHeightPx]);
655
+ }, [children, optionsCount, runMeasureTick]);
570
656
  const hasTopSpace = maxSpaceAbove > minUsableHeight;
571
657
  const hasBottomSpace = maxSpaceBelow > minUsableHeight;
572
658
  let finalPosition;
@@ -590,7 +676,7 @@ const ZDropListWrapper = (props) => {
590
676
  const heightForTop = calculateHeightForTop(measuredContentHeight, listMaxHeightLimiter !== null && listMaxHeightLimiter !== void 0 ? listMaxHeightLimiter : maxSpaceAbove, maxSpaceAbove);
591
677
  const finalHeight = finalPosition === "top" ? heightForTop : heightForBottom;
592
678
  react.useLayoutEffect(() => {
593
- const nextHeightPx = Math.max(0, Math.ceil(finalHeight + subPixelSafetyBufferPx));
679
+ const nextHeightPx = Math.max(0, finalHeight);
594
680
  if (lastAnimatedHeightRef.current !== nextHeightPx) {
595
681
  lastAnimatedHeightRef.current = nextHeightPx;
596
682
  setAnimatedWrapperHeightPx(nextHeightPx);
@@ -627,20 +713,12 @@ const checkIsValueEqualToSelectedValue = (value, selectedValue) => {
627
713
  return keysA.every((key) => checkIsValueEqualToSelectedValue(value[key], selectedValue[key]));
628
714
  };
629
715
 
630
- const getAvailableSpace = (element) => {
631
- if (!element)
632
- return { top: 0, bottom: 0 };
633
- const rect = element.getBoundingClientRect();
634
- const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
635
- const spaceTop = Math.max(0, rect.top);
636
- const spaceBottom = Math.max(0, viewportHeight - rect.bottom);
637
- return {
638
- top: spaceTop,
639
- bottom: spaceBottom,
640
- };
641
- };
642
-
643
716
  const edgeDistance = 10;
717
+ const shallowEqualPos = (prev, next) => {
718
+ var _a, _b, _c, _d;
719
+ return ((_a = prev === null || prev === void 0 ? void 0 : prev.top) !== null && _a !== void 0 ? _a : undefined) === ((_b = next === null || next === void 0 ? void 0 : next.top) !== null && _b !== void 0 ? _b : undefined) &&
720
+ ((_c = prev === null || prev === void 0 ? void 0 : prev.bottom) !== null && _c !== void 0 ? _c : undefined) === ((_d = next === null || next === void 0 ? void 0 : next.bottom) !== null && _d !== void 0 ? _d : undefined);
721
+ };
644
722
  const ZDropListAutoHeightWrapper = (props) => {
645
723
  const { containerRef, position = "bottom", className, optionsCount, children, } = props;
646
724
  const wrapperRef = react.useRef(null);
@@ -649,131 +727,193 @@ const ZDropListAutoHeightWrapper = (props) => {
649
727
  const [maxAllowedHeightPx, setMaxAllowedHeightPx] = react.useState(0);
650
728
  const [measuredContentHeightPx, setMeasuredContentHeightPx] = react.useState(0);
651
729
  const wrapperClasses = classNames(styles["zd__list"], styles["zd__list-auto-height-wrapper"], className);
652
- const calculateContentHeight = react.useCallback(() => {
653
- var _a;
654
- setForcedPositionY(undefined);
655
- if (wrapperRef.current && (containerRef === null || containerRef === void 0 ? void 0 : containerRef.current)) {
656
- const { top, bottom } = getAvailableSpace(containerRef.current);
657
- const availableTopPx = Math.max(0, top - edgeDistance);
658
- const availableBottomPx = Math.max(0, bottom - edgeDistance);
659
- let listItemHeightPx = cachedListItemHeightRef.current;
660
- if (!listItemHeightPx) {
661
- listItemHeightPx =
662
- ((_a = wrapperRef.current.querySelector("li")) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || null;
663
- if (!listItemHeightPx)
664
- return;
665
- cachedListItemHeightRef.current = listItemHeightPx;
730
+ const setForcedPositionYIfChanged = react.useCallback((next) => {
731
+ setForcedPositionY((prev) => (shallowEqualPos(prev, next) ? prev : next));
732
+ }, []);
733
+ const setMaxAllowedHeightIfChanged = react.useCallback((next) => {
734
+ setMaxAllowedHeightPx((prev) => (prev === next ? prev : next));
735
+ }, []);
736
+ const measureVisualContentHeightPx = react.useCallback(() => {
737
+ const el = wrapperRef.current;
738
+ if (!el) {
739
+ return 0;
740
+ }
741
+ const bordersPx = getElementVerticalBorders(el);
742
+ const prevHeight = el.style.height;
743
+ const prevMaxHeight = el.style.maxHeight;
744
+ el.style.height = "auto";
745
+ el.style.maxHeight = "none";
746
+ el.offsetHeight;
747
+ const wrapperRect = el.getBoundingClientRect();
748
+ const childrenElements = Array.from(el.children);
749
+ if (childrenElements.length === 0) {
750
+ const height = (el.scrollHeight || 0) + bordersPx;
751
+ el.style.height = prevHeight;
752
+ el.style.maxHeight = prevMaxHeight;
753
+ return Math.max(0, height);
754
+ }
755
+ let minTop = Number.POSITIVE_INFINITY;
756
+ let maxBottom = Number.NEGATIVE_INFINITY;
757
+ let topMostEl = null;
758
+ let bottomMostEl = null;
759
+ let topMostTop = Number.POSITIVE_INFINITY;
760
+ let bottomMostBottom = Number.NEGATIVE_INFINITY;
761
+ for (const child of childrenElements) {
762
+ const rect = child.getBoundingClientRect();
763
+ const topRel = rect.top - wrapperRect.top;
764
+ const bottomRel = rect.bottom - wrapperRect.top;
765
+ if (topRel < minTop) {
766
+ minTop = topRel;
666
767
  }
667
- const minimumApprovedHeightPx = listItemHeightPx * 2;
668
- if (availableTopPx < minimumApprovedHeightPx &&
669
- availableBottomPx < minimumApprovedHeightPx) {
670
- return;
768
+ if (bottomRel > maxBottom) {
769
+ maxBottom = bottomRel;
671
770
  }
672
- if (position.includes("top") &&
673
- availableTopPx > minimumApprovedHeightPx) {
674
- setMaxAllowedHeightPx(availableTopPx);
675
- return;
771
+ if (rect.top < topMostTop) {
772
+ topMostTop = rect.top;
773
+ topMostEl = child;
676
774
  }
677
- if (position.includes("top") &&
678
- availableTopPx <= minimumApprovedHeightPx) {
679
- setMaxAllowedHeightPx(availableBottomPx);
680
- setForcedPositionY({ top: "100%", bottom: "auto" });
681
- return;
775
+ if (rect.bottom > bottomMostBottom) {
776
+ bottomMostBottom = rect.bottom;
777
+ bottomMostEl = child;
682
778
  }
683
- if (position.includes("bottom") &&
684
- availableBottomPx > minimumApprovedHeightPx) {
685
- setMaxAllowedHeightPx(availableBottomPx);
779
+ }
780
+ let topMarginPx = 0;
781
+ let bottomMarginPx = 0;
782
+ if (topMostEl) {
783
+ const cs = window.getComputedStyle(topMostEl);
784
+ topMarginPx = parseFloat(cs.marginTop || "0") || 0;
785
+ }
786
+ if (bottomMostEl) {
787
+ const cs = window.getComputedStyle(bottomMostEl);
788
+ bottomMarginPx = parseFloat(cs.marginBottom || "0") || 0;
789
+ }
790
+ const unionHeight = Math.max(0, maxBottom - minTop);
791
+ const total = unionHeight + topMarginPx + bottomMarginPx + bordersPx;
792
+ el.style.height = prevHeight;
793
+ el.style.maxHeight = prevMaxHeight;
794
+ return Math.max(0, total);
795
+ }, []);
796
+ const calculateContentHeight = react.useCallback(() => {
797
+ if (!wrapperRef.current || !(containerRef === null || containerRef === void 0 ? void 0 : containerRef.current)) {
798
+ return;
799
+ }
800
+ const triggerRect = containerRef.current.getBoundingClientRect();
801
+ const viewportH = window.innerHeight || document.documentElement.clientHeight;
802
+ const availableTopPx = Math.max(0, triggerRect.top - edgeDistance);
803
+ const availableBottomPx = Math.max(0, viewportH - triggerRect.bottom - edgeDistance);
804
+ const preferTop = position.includes("top");
805
+ if (optionsCount === 0) {
806
+ const preferSpace = preferTop ? availableTopPx : availableBottomPx;
807
+ const otherSpace = preferTop ? availableBottomPx : availableTopPx;
808
+ const minUsable = 20;
809
+ if (preferSpace >= minUsable || otherSpace < minUsable) {
810
+ setMaxAllowedHeightIfChanged(preferSpace);
811
+ setForcedPositionYIfChanged(undefined);
686
812
  return;
687
813
  }
688
- if (position.includes("bottom") &&
689
- availableBottomPx <= minimumApprovedHeightPx) {
690
- setMaxAllowedHeightPx(availableTopPx);
691
- setForcedPositionY({ top: "auto", bottom: "100%" });
692
- return;
814
+ setMaxAllowedHeightIfChanged(otherSpace);
815
+ setForcedPositionYIfChanged(preferTop
816
+ ? { top: "100%", bottom: "auto" }
817
+ : { top: "auto", bottom: "100%" });
818
+ return;
819
+ }
820
+ let liHeight = cachedListItemHeightRef.current;
821
+ if (!liHeight) {
822
+ const li = wrapperRef.current.querySelector("li");
823
+ const height = (li === null || li === void 0 ? void 0 : li.getBoundingClientRect().height) || 0;
824
+ if (height > 0) {
825
+ liHeight = height;
826
+ cachedListItemHeightRef.current = height;
693
827
  }
694
- setForcedPositionY(undefined);
695
828
  }
696
- }, [containerRef, position]);
697
- const preventFromOverflowY = react.useCallback(() => {
698
- if (!(containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) || !wrapperRef.current)
699
- return;
700
- const dropdownHeightPx = containerRef.current.scrollHeight;
701
- const dropdownPositionYPx = containerRef.current.getBoundingClientRect().y;
702
- const viewportHeightPx = window.innerHeight || document.documentElement.clientHeight;
703
- const isOverflowingTop = dropdownPositionYPx < wrapperRef.current.scrollHeight;
704
- const isOverflowingBottom = dropdownPositionYPx + dropdownHeightPx + wrapperRef.current.clientHeight >
705
- viewportHeightPx;
706
- if (!isOverflowingTop && !isOverflowingBottom && !maxAllowedHeightPx) {
707
- calculateContentHeight();
829
+ if (!liHeight) {
830
+ setMaxAllowedHeightIfChanged(preferTop ? availableTopPx : availableBottomPx);
831
+ setForcedPositionYIfChanged(undefined);
708
832
  return;
709
833
  }
710
- if (!isOverflowingTop && isOverflowingBottom) {
711
- setForcedPositionY({ top: "auto", bottom: "100%" });
712
- calculateContentHeight();
834
+ const min2 = liHeight * 2;
835
+ const isTopOk = availableTopPx >= min2;
836
+ const isBottomOk = availableBottomPx >= min2;
837
+ if (!isTopOk && !isBottomOk) {
838
+ setMaxAllowedHeightIfChanged(preferTop ? availableTopPx : availableBottomPx);
839
+ setForcedPositionYIfChanged(undefined);
713
840
  return;
714
841
  }
715
- if (isOverflowingTop && !isOverflowingBottom) {
716
- setForcedPositionY({ top: "100%", bottom: "auto" });
717
- calculateContentHeight();
718
- return;
842
+ if (preferTop) {
843
+ if (isTopOk) {
844
+ setMaxAllowedHeightIfChanged(availableTopPx);
845
+ setForcedPositionYIfChanged(undefined);
846
+ }
847
+ else {
848
+ setMaxAllowedHeightIfChanged(availableBottomPx);
849
+ setForcedPositionYIfChanged({ top: "100%", bottom: "auto" });
850
+ }
719
851
  }
720
- if (isOverflowingTop && isOverflowingBottom) {
721
- calculateContentHeight();
852
+ else {
853
+ if (isBottomOk) {
854
+ setMaxAllowedHeightIfChanged(availableBottomPx);
855
+ setForcedPositionYIfChanged(undefined);
856
+ }
857
+ else {
858
+ setMaxAllowedHeightIfChanged(availableTopPx);
859
+ setForcedPositionYIfChanged({ top: "auto", bottom: "100%" });
860
+ }
722
861
  }
723
- }, [containerRef, calculateContentHeight, maxAllowedHeightPx]);
862
+ }, [
863
+ containerRef,
864
+ position,
865
+ optionsCount,
866
+ setForcedPositionYIfChanged,
867
+ setMaxAllowedHeightIfChanged,
868
+ ]);
869
+ const preventFromOverflowY = react.useCallback(() => {
870
+ if (!(containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) || !wrapperRef.current)
871
+ return;
872
+ calculateContentHeight();
873
+ }, [containerRef, calculateContentHeight]);
724
874
  const reCalcHeightFromContent = react.useCallback(() => {
725
875
  const wrapperElement = wrapperRef.current;
726
- if (!wrapperElement)
876
+ if (!wrapperElement) {
727
877
  return;
728
- const wrapperVerticalBordersPx = getElementVerticalBorders(wrapperElement);
729
- const firstListItemElement = wrapperElement.querySelector("li");
730
- const isListRenderedInDom = !!firstListItemElement;
731
- if (!isListRenderedInDom) {
732
- cachedListItemHeightRef.current = null;
733
878
  }
879
+ const bordersPx = getElementVerticalBorders(wrapperElement);
734
880
  let calculatedContentHeight;
735
- if (isListRenderedInDom && optionsCount > 0) {
736
- let listItemHeightPx = cachedListItemHeightRef.current;
737
- if (!listItemHeightPx) {
738
- listItemHeightPx =
739
- firstListItemElement.getBoundingClientRect().height || null;
740
- if (listItemHeightPx) {
741
- cachedListItemHeightRef.current = listItemHeightPx;
881
+ if (optionsCount > 0) {
882
+ const firstLi = wrapperElement.querySelector("li");
883
+ let liHeight = cachedListItemHeightRef.current;
884
+ if (!liHeight && firstLi) {
885
+ const h = firstLi.getBoundingClientRect().height || 0;
886
+ if (h > 0) {
887
+ liHeight = h;
888
+ cachedListItemHeightRef.current = h;
742
889
  }
743
890
  }
744
- calculatedContentHeight = listItemHeightPx
745
- ? listItemHeightPx * optionsCount + wrapperVerticalBordersPx
746
- : wrapperVerticalBordersPx;
747
- }
748
- else {
749
- const firstChildElement = wrapperElement.firstElementChild;
750
- if (!firstChildElement) {
751
- calculatedContentHeight = wrapperVerticalBordersPx;
891
+ if (liHeight && liHeight > 0) {
892
+ calculatedContentHeight = liHeight * optionsCount + bordersPx;
752
893
  }
753
894
  else {
754
- const previousHeight = wrapperElement.style.height;
755
- const previousMaxHeight = wrapperElement.style.maxHeight;
895
+ const prevHeight = wrapperElement.style.height;
896
+ const prevMaxHeight = wrapperElement.style.maxHeight;
756
897
  wrapperElement.style.height = "auto";
757
898
  wrapperElement.style.maxHeight = "none";
758
899
  wrapperElement.offsetHeight;
759
- const firstChildRectHeight = firstChildElement.getBoundingClientRect().height || 0;
760
- wrapperElement.style.height = previousHeight;
761
- wrapperElement.style.maxHeight = previousMaxHeight;
762
- const firstChildComputedStyles = window.getComputedStyle(firstChildElement);
763
- const marginTopPx = parseFloat(firstChildComputedStyles.marginTop || "0") || 0;
764
- const marginBottomPx = parseFloat(firstChildComputedStyles.marginBottom || "0") || 0;
765
900
  calculatedContentHeight =
766
- firstChildRectHeight +
767
- marginTopPx +
768
- marginBottomPx +
769
- wrapperVerticalBordersPx;
901
+ (wrapperElement.scrollHeight || 0) + bordersPx;
902
+ wrapperElement.style.height = prevHeight;
903
+ wrapperElement.style.maxHeight = prevMaxHeight;
770
904
  }
771
905
  }
772
- const clampedContentHeight = maxAllowedHeightPx > 0
906
+ else {
907
+ calculatedContentHeight = measureVisualContentHeightPx();
908
+ }
909
+ const clamped = maxAllowedHeightPx > 0
773
910
  ? Math.min(calculatedContentHeight, maxAllowedHeightPx)
774
911
  : calculatedContentHeight;
775
- setMeasuredContentHeightPx(Math.max(0, Math.round(clampedContentHeight)));
776
- }, [optionsCount, maxAllowedHeightPx]);
912
+ setMeasuredContentHeightPx((prev) => {
913
+ const next = Math.max(0, Math.round(clamped));
914
+ return prev === next ? prev : next;
915
+ });
916
+ }, [optionsCount, maxAllowedHeightPx, measureVisualContentHeightPx]);
777
917
  react.useLayoutEffect(() => {
778
918
  preventFromOverflowY();
779
919
  requestAnimationFrame(() => {
@@ -783,46 +923,56 @@ const ZDropListAutoHeightWrapper = (props) => {
783
923
  }, [preventFromOverflowY, reCalcHeightFromContent]);
784
924
  react.useLayoutEffect(() => {
785
925
  cachedListItemHeightRef.current = null;
786
- setForcedPositionY(undefined);
787
926
  let raf1 = 0;
788
927
  let raf2 = 0;
928
+ let raf3 = 0;
929
+ preventFromOverflowY();
930
+ reCalcHeightFromContent();
789
931
  raf1 = requestAnimationFrame(() => {
932
+ preventFromOverflowY();
933
+ reCalcHeightFromContent();
790
934
  raf2 = requestAnimationFrame(() => {
791
935
  preventFromOverflowY();
792
936
  reCalcHeightFromContent();
937
+ raf3 = requestAnimationFrame(() => {
938
+ preventFromOverflowY();
939
+ reCalcHeightFromContent();
940
+ });
793
941
  });
794
942
  });
795
943
  return () => {
796
944
  cancelAnimationFrame(raf1);
797
945
  cancelAnimationFrame(raf2);
946
+ cancelAnimationFrame(raf3);
798
947
  };
799
948
  }, [optionsCount, preventFromOverflowY, reCalcHeightFromContent]);
800
949
  react.useLayoutEffect(() => {
801
- let resizeTimeoutId;
950
+ let rafId = 0;
802
951
  const onResize = () => {
803
952
  cachedListItemHeightRef.current = null;
804
- setForcedPositionY(undefined);
805
- clearTimeout(resizeTimeoutId);
806
- resizeTimeoutId = setTimeout(() => {
953
+ cancelAnimationFrame(rafId);
954
+ rafId = requestAnimationFrame(() => {
807
955
  preventFromOverflowY();
808
956
  reCalcHeightFromContent();
809
- }, 50);
957
+ });
810
958
  };
811
959
  window.addEventListener("resize", onResize);
812
960
  return () => {
813
- clearTimeout(resizeTimeoutId);
961
+ cancelAnimationFrame(rafId);
814
962
  window.removeEventListener("resize", onResize);
815
963
  };
816
964
  }, [preventFromOverflowY, reCalcHeightFromContent]);
817
- react.useLayoutEffect(() => {
818
- reCalcHeightFromContent();
819
- }, [maxAllowedHeightPx, forcedPositionY, reCalcHeightFromContent]);
965
+ const baseY = position.includes("top")
966
+ ? { top: "auto", bottom: "100%" }
967
+ : { top: "100%", bottom: "auto" };
820
968
  const positionStyles = {
821
969
  position: "absolute",
822
- ...(position.includes("top") ? { bottom: "100%" } : { top: "100%" }),
823
- ...(forcedPositionY && { ...forcedPositionY }),
824
- ...(maxAllowedHeightPx && { maxHeight: `${maxAllowedHeightPx}px` }),
825
- height: `${measuredContentHeightPx}px`,
970
+ ...baseY,
971
+ ...(forcedPositionY !== null && forcedPositionY !== void 0 ? forcedPositionY : {}),
972
+ ...(maxAllowedHeightPx ? { maxHeight: `${maxAllowedHeightPx}px` } : {}),
973
+ ...(measuredContentHeightPx > 0
974
+ ? { height: `${measuredContentHeightPx}px` }
975
+ : {}),
826
976
  overflowY: "auto",
827
977
  };
828
978
  return (jsxRuntime.jsx("div", { className: wrapperClasses, style: positionStyles, ref: wrapperRef, children: children }));
@@ -1173,7 +1323,6 @@ const ZDropField = (props) => {
1173
1323
  exports.ZDrop = ZDrop;
1174
1324
  exports.ZDropField = ZDropField;
1175
1325
  exports.classNames = classNames;
1176
- exports.getAvailableSpace = getAvailableSpace;
1177
1326
  exports.getElementVerticalBorders = getElementVerticalBorders;
1178
1327
  exports.useOutsideClose = useOutsideClose;
1179
- //# sourceMappingURL=index-BbHVfHYa.js.map
1328
+ //# sourceMappingURL=index-8z2jr57w.js.map