@trackunit/react-components 1.10.40 → 1.10.43

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/index.cjs.js CHANGED
@@ -1334,34 +1334,40 @@ const useElevatedState = (initialState, customState) => {
1334
1334
  * @returns {object} The object containing the onMouseEnter, onMouseLeave and hovering props
1335
1335
  */
1336
1336
  const useHover = ({ debounced = false, delay = 100, direction = "out" } = { debounced: false }) => {
1337
- const [hovering, setHovering] = react.useState(false);
1338
- const [debouncedHovering, setDebouncedHovering] = react.useState(false);
1339
- react.useEffect(() => {
1340
- if (!debounced) {
1341
- setDebouncedHovering(hovering);
1342
- return undefined;
1343
- }
1344
- const shouldDebounce = direction === "both" || (direction === "in" && hovering) || (direction === "out" && !hovering);
1345
- if (shouldDebounce) {
1346
- const timer = setTimeout(() => {
1347
- setDebouncedHovering(hovering);
1348
- }, delay);
1349
- return () => clearTimeout(timer);
1350
- }
1351
- setDebouncedHovering(hovering);
1352
- return undefined;
1353
- }, [debounced, direction, delay, hovering]);
1337
+ const [isHovering, setIsHovering] = react.useState(false);
1338
+ const [debouncedIsHovering, setDebouncedIsHovering] = react.useState(false);
1354
1339
  const onMouseEnter = react.useCallback(() => {
1355
- setHovering(true);
1340
+ setIsHovering(true);
1356
1341
  }, []);
1357
1342
  const onMouseLeave = react.useCallback(() => {
1358
- setHovering(false);
1343
+ setIsHovering(false);
1359
1344
  }, []);
1345
+ // Determine if the current transition should be debounced based on direction
1346
+ const isTransitionDebounced = debounced && (direction === "both" || (direction === "in" && isHovering) || (direction === "out" && !isHovering));
1347
+ // Sync debouncedIsHovering immediately for non-debounced transitions
1348
+ react.useLayoutEffect(() => {
1349
+ if (!debounced || isTransitionDebounced) {
1350
+ return undefined;
1351
+ }
1352
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- Synchronizing derived state for directional debouncing
1353
+ setDebouncedIsHovering(isHovering);
1354
+ }, [debounced, isTransitionDebounced, isHovering]);
1355
+ // Apply delay for debounced transitions
1356
+ react.useEffect(() => {
1357
+ if (!isTransitionDebounced) {
1358
+ return undefined;
1359
+ }
1360
+ const timer = setTimeout(() => {
1361
+ setDebouncedIsHovering(isHovering);
1362
+ }, delay);
1363
+ return () => clearTimeout(timer);
1364
+ }, [isTransitionDebounced, delay, isHovering]);
1365
+ const value = debounced ? (isTransitionDebounced ? debouncedIsHovering : isHovering) : isHovering;
1360
1366
  return react.useMemo(() => ({
1361
1367
  onMouseEnter,
1362
1368
  onMouseLeave,
1363
- hovering: debouncedHovering,
1364
- }), [onMouseEnter, onMouseLeave, debouncedHovering]);
1369
+ hovering: value,
1370
+ }), [onMouseEnter, onMouseLeave, value]);
1365
1371
  };
1366
1372
 
1367
1373
  const OVERSCAN = 10;
@@ -1391,10 +1397,13 @@ const DEFAULT_ROW_HEIGHT = 50;
1391
1397
  * });
1392
1398
  */
1393
1399
  const useInfiniteScroll = ({ pagination, scrollElementRef, count, estimateSize, overscan, onChange, skip = false, }) => {
1400
+ "use no memo"; //! TODO: remove this once Tanstack Virtual is updated to support React Compiler
1394
1401
  const handleChange = react.useCallback((virtualizer) => {
1395
1402
  onChange?.(virtualizer);
1396
1403
  }, [onChange]);
1397
1404
  const handleEstimateSize = react.useCallback((index) => estimateSize?.(index) ?? DEFAULT_ROW_HEIGHT, [estimateSize]);
1405
+ //! TODO: remove this once Tanstack Virtual is updated to support React Compiler
1406
+ // eslint-disable-next-line react-hooks/incompatible-library
1398
1407
  const rowVirtualizer = reactVirtual.useVirtualizer({
1399
1408
  count,
1400
1409
  getScrollElement: () => scrollElementRef.current,
@@ -1443,8 +1452,10 @@ const useInfiniteScroll = ({ pagination, scrollElementRef, count, estimateSize,
1443
1452
  */
1444
1453
  const useIsFirstRender = () => {
1445
1454
  const [isFirstRender, setIsFirstRender] = react.useState(true);
1446
- react.useEffect(() => {
1447
- setIsFirstRender(false);
1455
+ react.useLayoutEffect(() => {
1456
+ queueMicrotask(() => {
1457
+ setIsFirstRender(false);
1458
+ });
1448
1459
  }, []);
1449
1460
  return isFirstRender;
1450
1461
  };
@@ -1516,27 +1527,51 @@ const useIsTextTruncated = (text, { skip = false } = {}) => {
1516
1527
  };
1517
1528
 
1518
1529
  /**
1519
- * Shared measurement logic used by both useMeasure and useMeasureElement.
1520
- * This hook observes an element and measures its geometry.
1530
+ * Custom hook to measure the geometry of an element using a callback ref.
1531
+ * Measures the size and position of the element relative to the viewport.
1532
+ *
1533
+ * @template TElement extends HTMLElement
1534
+ * @returns {UseMeasureResult<HTMLElement>} An object containing `geometry`, `ref` callback, and `element`.
1535
+ * @example
1536
+ * ```tsx
1537
+ * const { geometry, ref } = useMeasure();
1538
+ * return <div ref={ref}>Width: {geometry.width}</div>;
1539
+ * ```
1521
1540
  */
1522
- const useMeasureShared = (element, { skip = false, onChange } = {}) => {
1541
+ const useMeasure = ({ skip = false, onChange, } = {}) => {
1542
+ const [element, setElement] = react.useState(null);
1523
1543
  const [geometry, setGeometry] = react.useState(undefined);
1524
1544
  const observerRef = react.useRef(null);
1525
1545
  const onChangeRef = react.useRef(onChange);
1526
1546
  const isInitialRender = react.useRef(true);
1527
- onChangeRef.current = onChange;
1547
+ // Callback ref to track the element
1548
+ const ref = react.useCallback((node) => {
1549
+ setElement(node);
1550
+ }, []);
1528
1551
  const disconnectObserver = () => {
1529
1552
  if (observerRef.current !== null) {
1530
1553
  observerRef.current.disconnect();
1531
1554
  observerRef.current = null;
1532
1555
  }
1533
1556
  };
1557
+ react.useEffect(() => {
1558
+ onChangeRef.current = onChange;
1559
+ }, [onChange]);
1534
1560
  react.useEffect(() => {
1535
1561
  if (skip || element === null) {
1536
1562
  return;
1537
1563
  }
1538
1564
  const updateGeometry = (rect) => {
1539
- const newGeometry = esToolkit.omit(rect, ["toJSON"]);
1565
+ const newGeometry = {
1566
+ width: rect.width,
1567
+ height: rect.height,
1568
+ top: rect.top,
1569
+ bottom: rect.bottom,
1570
+ left: rect.left,
1571
+ right: rect.right,
1572
+ x: rect.x,
1573
+ y: rect.y,
1574
+ };
1540
1575
  setGeometry(prevGeometry => {
1541
1576
  if (esToolkit.isEqual(prevGeometry, newGeometry)) {
1542
1577
  return prevGeometry;
@@ -1562,48 +1597,72 @@ const useMeasureShared = (element, { skip = false, onChange } = {}) => {
1562
1597
  updateGeometry(initialRect);
1563
1598
  return disconnectObserver;
1564
1599
  }, [element, skip]);
1565
- return geometry;
1600
+ return react.useMemo(() => ({ geometry, ref, element }), [geometry, ref, element]);
1566
1601
  };
1567
1602
 
1603
+ // This hook is _heavily_ inspired by floating-ui's useMergeRefs
1568
1604
  /**
1569
- * Custom hook to measure the geometry of an element using a callback ref.
1570
- * Measures the size and position of the element relative to the viewport.
1605
+ * Merges an array of refs into a single memoized callback ref or `null`.
1606
+ * Useful when you need to attach multiple refs to the same element,
1607
+ * such as when composing multiple hooks that each need a ref.
1571
1608
  *
1572
- * @returns {UseMeasureResult<HTMLElement>} An object containing `geometry`, `ref` callback, and `element`.
1573
- * @template TElement extends HTMLElement
1609
+ * @template TInstance - The type of the element instance
1610
+ * @param {ReadonlyArray<MergeableRef<TInstance> | undefined>} refs - Array of refs to merge (can be RefObjects, RefCallbacks, or undefined)
1611
+ * @returns {null | RefCallback<TInstance>} A single ref callback that will update all provided refs, or null if all refs are null
1574
1612
  * @example
1575
1613
  * ```tsx
1576
- * const { geometry, ref } = useMeasure();
1577
- * return <div ref={ref}>Width: {geometry.width}</div>;
1578
- * ```
1579
- */
1580
- const useMeasure = ({ skip = false, onChange, } = {}) => {
1581
- const [element, setElement] = react.useState(null);
1582
- // Callback ref to track the element
1583
- const ref = react.useCallback((node) => {
1584
- setElement(node);
1585
- }, []);
1586
- const geometry = useMeasureShared(element, { skip, onChange });
1587
- return { geometry, ref, element };
1588
- };
1589
-
1590
- /**
1591
- * Custom hook to measure the geometry of an element from a RefObject.
1592
- * Use this when you already have a ref (e.g., from useRef or for composition with other hooks).
1593
- * Measures the size and position of the element relative to the viewport.
1614
+ * const { ref: measureRef } = useMeasure();
1615
+ * const { ref: scrollRef } = useScrollDetection();
1616
+ * const mergedRef = useMergeRefs([measureRef, scrollRef]);
1594
1617
  *
1595
- * @param ref - RefObject pointing to the element to measure
1596
- * @returns {Geometry} The geometry of the element
1597
- * @example
1598
- * ```tsx
1599
- * const ref = useRef<HTMLDivElement>(null);
1600
- * const geometry = useMeasureElement(ref);
1601
- * return <div ref={ref}>Width: {geometry.width}</div>;
1618
+ * return <div ref={mergedRef}>Content</div>;
1602
1619
  * ```
1603
1620
  */
1604
- const useMeasureElement = (ref, { skip = false, onChange } = {}) => {
1605
- return useMeasureShared(ref.current, { skip, onChange });
1606
- };
1621
+ function useMergeRefs(refs) {
1622
+ const cleanupRef = react.useRef(undefined);
1623
+ const refsRef = react.useRef(refs);
1624
+ react.useEffect(() => {
1625
+ refsRef.current = refs;
1626
+ }, [refs]);
1627
+ const refEffect = react.useCallback((instance) => {
1628
+ const cleanups = refsRef.current.map(ref => {
1629
+ if (ref === null || ref === undefined) {
1630
+ return;
1631
+ }
1632
+ if (typeof ref === "function") {
1633
+ const refCallback = ref;
1634
+ const refCleanup = refCallback(instance);
1635
+ return typeof refCleanup === "function"
1636
+ ? refCleanup
1637
+ : () => {
1638
+ refCallback(null);
1639
+ };
1640
+ }
1641
+ ref.current = instance;
1642
+ return () => {
1643
+ ref.current = null;
1644
+ };
1645
+ });
1646
+ return () => {
1647
+ cleanups.forEach(refCleanup => refCleanup?.());
1648
+ };
1649
+ }, []);
1650
+ return react.useMemo(() => {
1651
+ // eslint-disable-next-line react-hooks/refs
1652
+ if (refsRef.current.every(ref => ref === null)) {
1653
+ return null;
1654
+ }
1655
+ return (value) => {
1656
+ if (cleanupRef.current) {
1657
+ cleanupRef.current();
1658
+ cleanupRef.current = undefined;
1659
+ }
1660
+ if (value !== null) {
1661
+ cleanupRef.current = refEffect(value);
1662
+ }
1663
+ };
1664
+ }, [refEffect]);
1665
+ }
1607
1666
 
1608
1667
  /**
1609
1668
  * Hook that returns true if any modifier key (Ctrl, Alt, Shift, Meta/Cmd) is pressed
@@ -1643,22 +1702,24 @@ const useRelayPagination = ({ onReset, pageSize } = { pageSize: defaultPageSize
1643
1702
  const [variables, setVariables] = react.useState({ first: pageSize });
1644
1703
  const [pageInfo, setPageInfo] = react.useState();
1645
1704
  const [isLoading, setIsLoading] = react.useState(true);
1705
+ // Destructure pageInfo properties early to avoid depending on the entire object
1706
+ const { hasNextPage, endCursor, hasPreviousPage, startCursor } = pageInfo ?? {};
1646
1707
  const nextPage = react.useCallback(() => {
1647
- if (pageInfo?.hasNextPage === true) {
1708
+ if (hasNextPage === true) {
1648
1709
  setVariables({
1649
- after: pageInfo.endCursor === null ? undefined : pageInfo.endCursor,
1710
+ after: endCursor === null ? undefined : endCursor,
1650
1711
  first: pageSize,
1651
1712
  });
1652
1713
  }
1653
- }, [pageInfo?.endCursor, pageInfo?.hasNextPage, pageSize]);
1714
+ }, [hasNextPage, endCursor, pageSize]);
1654
1715
  const previousPage = react.useCallback(() => {
1655
- if (pageInfo?.hasPreviousPage === true) {
1716
+ if (hasPreviousPage === true) {
1656
1717
  setVariables({
1657
- before: pageInfo.startCursor === null ? undefined : pageInfo.startCursor,
1718
+ before: startCursor === null ? undefined : startCursor,
1658
1719
  last: pageSize,
1659
1720
  });
1660
1721
  }
1661
- }, [pageInfo?.hasPreviousPage, pageInfo?.startCursor, pageSize]);
1722
+ }, [hasPreviousPage, startCursor, pageSize]);
1662
1723
  const reset = react.useCallback(() => {
1663
1724
  setVariables({
1664
1725
  last: undefined,
@@ -1873,21 +1934,31 @@ const useScrollBlock = (scrollContainer = typeof document !== "undefined" ? docu
1873
1934
  const SCROLL_DEBOUNCE_TIME = 50;
1874
1935
  /**
1875
1936
  * Hook for detecting scroll values in horizontal or vertical direction.
1937
+ * Returns a ref callback to attach to the element you want to observe.
1876
1938
  *
1877
- * @param {useRef} elementRef - Ref hook holding the element that needs to be observed during scrolling
1878
1939
  * @param {object} options - Options object containing direction, onScrollStateChange callback, and skip
1879
- * @returns {object} An object containing if the element is scrollable, is at the beginning, is at the end, and its current scroll position.
1940
+ * @returns {object} An object containing ref callback, element, and scroll state (isScrollable, isAtBeginning, isAtEnd, scrollPosition)
1941
+ * @template TElement extends HTMLElement
1942
+ * @example
1943
+ * ```tsx
1944
+ * const { ref, isScrollable, isAtBeginning } = useScrollDetection({ direction: "horizontal" });
1945
+ * return <div ref={ref}>Scrollable content</div>;
1946
+ * ```
1880
1947
  */
1881
- const useScrollDetection = (elementRef, options) => {
1948
+ const useScrollDetection = (options) => {
1882
1949
  const { direction = "vertical", onScrollStateChange, skip = false } = options ?? {};
1950
+ const [element, setElement] = react.useState(null);
1883
1951
  const [isScrollable, setIsScrollable] = react.useState(false);
1884
1952
  const [isAtBeginning, setIsAtBeginning] = react.useState(true);
1885
1953
  const [isAtEnd, setIsAtEnd] = react.useState(false);
1886
1954
  const [scrollPosition, setScrollPosition] = react.useState({ start: 0, end: 0 });
1887
1955
  const observerRef = react.useRef(null);
1888
1956
  const isFirstRender = useIsFirstRender();
1957
+ // Callback ref to track the element
1958
+ const ref = react.useCallback((node) => {
1959
+ setElement(node);
1960
+ }, []);
1889
1961
  const checkScrollable = react.useCallback(() => {
1890
- const element = elementRef?.current;
1891
1962
  if (!element) {
1892
1963
  return;
1893
1964
  }
@@ -1901,9 +1972,8 @@ const useScrollDetection = (elementRef, options) => {
1901
1972
  }
1902
1973
  return prev;
1903
1974
  });
1904
- }, [elementRef, direction]);
1975
+ }, [element, direction]);
1905
1976
  const checkScrollPosition = react.useCallback(() => {
1906
- const element = elementRef?.current;
1907
1977
  if (!element) {
1908
1978
  return;
1909
1979
  }
@@ -1925,8 +1995,16 @@ const useScrollDetection = (elementRef, options) => {
1925
1995
  setIsAtEnd(newIsAtEnd);
1926
1996
  setScrollPosition(newScrollPosition);
1927
1997
  }
1928
- }, [elementRef, direction]);
1998
+ }, [element, direction]);
1929
1999
  const debouncedCheckScrollPosition = usehooksTs.useDebounceCallback(checkScrollPosition, SCROLL_DEBOUNCE_TIME);
2000
+ const checkScrollableRef = react.useRef(checkScrollable);
2001
+ react.useEffect(() => {
2002
+ checkScrollableRef.current = checkScrollable;
2003
+ }, [checkScrollable]);
2004
+ const checkScrollPositionRef = react.useRef(checkScrollPosition);
2005
+ react.useEffect(() => {
2006
+ checkScrollPositionRef.current = checkScrollPosition;
2007
+ }, [checkScrollPosition]);
1930
2008
  // Notify about state changes whenever any state value changes
1931
2009
  react.useEffect(() => {
1932
2010
  if (isFirstRender) {
@@ -1940,19 +2018,15 @@ const useScrollDetection = (elementRef, options) => {
1940
2018
  });
1941
2019
  }, [isScrollable, isAtBeginning, isAtEnd, scrollPosition, onScrollStateChange, isFirstRender]);
1942
2020
  react.useEffect(() => {
1943
- if (skip) {
1944
- return;
1945
- }
1946
- const element = elementRef?.current;
1947
- if (!element) {
2021
+ if (skip || !element) {
1948
2022
  return;
1949
2023
  }
1950
2024
  // Initial checks
1951
- checkScrollable();
1952
- checkScrollPosition();
2025
+ checkScrollableRef.current();
2026
+ checkScrollPositionRef.current();
1953
2027
  observerRef.current = new ResizeObserver(() => {
1954
- checkScrollable();
1955
- checkScrollPosition();
2028
+ checkScrollableRef.current();
2029
+ checkScrollPositionRef.current();
1956
2030
  });
1957
2031
  observerRef.current.observe(element);
1958
2032
  element.addEventListener("scroll", debouncedCheckScrollPosition);
@@ -1962,8 +2036,8 @@ const useScrollDetection = (elementRef, options) => {
1962
2036
  }
1963
2037
  element.removeEventListener("scroll", debouncedCheckScrollPosition);
1964
2038
  };
1965
- }, [elementRef, checkScrollable, checkScrollPosition, debouncedCheckScrollPosition, skip]);
1966
- return react.useMemo(() => ({ isScrollable, isAtBeginning, isAtEnd, scrollPosition }), [isScrollable, isAtBeginning, isAtEnd, scrollPosition]);
2039
+ }, [element, debouncedCheckScrollPosition, skip]);
2040
+ return react.useMemo(() => ({ ref, element, isScrollable, isAtBeginning, isAtEnd, scrollPosition }), [ref, element, isScrollable, isAtBeginning, isAtEnd, scrollPosition]);
1967
2041
  };
1968
2042
 
1969
2043
  /**
@@ -2016,24 +2090,28 @@ const useViewportBreakpoints = (options = {}) => {
2016
2090
  }), { ...defaultBreakpointState });
2017
2091
  setViewportSize(newViewportSize);
2018
2092
  }, []);
2093
+ const updateViewportSizeRef = react.useRef(updateViewportSize);
2094
+ react.useEffect(() => {
2095
+ updateViewportSizeRef.current = updateViewportSize;
2096
+ }, [updateViewportSize]);
2019
2097
  react.useEffect(() => {
2020
2098
  if (skip) {
2021
2099
  return;
2022
2100
  }
2023
2101
  // Initial check
2024
- updateViewportSize();
2102
+ updateViewportSizeRef.current();
2025
2103
  // Set up listeners for each breakpoint
2026
2104
  const mediaQueryLists = sharedUtils.objectEntries(uiDesignTokens.themeScreenSizeAsNumber).map(([_, minWidth]) => window.matchMedia(`(min-width: ${minWidth}px)`));
2027
2105
  mediaQueryLists.forEach(mql => {
2028
- mql.addEventListener("change", updateViewportSize);
2106
+ mql.addEventListener("change", updateViewportSizeRef.current);
2029
2107
  });
2030
2108
  // Cleanup
2031
2109
  return () => {
2032
2110
  mediaQueryLists.forEach(mql => {
2033
- mql.removeEventListener("change", updateViewportSize);
2111
+ mql.removeEventListener("change", updateViewportSizeRef.current);
2034
2112
  });
2035
2113
  };
2036
- }, [updateViewportSize, skip]);
2114
+ }, [skip]);
2037
2115
  return viewportSize;
2038
2116
  };
2039
2117
 
@@ -2051,11 +2129,15 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
2051
2129
  setFocused(hasFocus());
2052
2130
  onBlur?.();
2053
2131
  }, [onBlur]);
2132
+ const setFocusedRef = react.useRef(setFocused);
2133
+ react.useEffect(() => {
2134
+ setFocusedRef.current = setFocused;
2135
+ }, [setFocused]);
2054
2136
  react.useEffect(() => {
2055
2137
  if (skip) {
2056
2138
  return;
2057
2139
  }
2058
- setFocused(hasFocus()); // Focus for additional renders
2140
+ setFocusedRef.current(hasFocus());
2059
2141
  window.addEventListener("focus", onFocusInternal);
2060
2142
  window.addEventListener("blur", onBlurInternal);
2061
2143
  return () => {
@@ -2969,16 +3051,20 @@ const OverflowIndicator = ({ className, dataTestId, direction, onClickScroll, })
2969
3051
  };
2970
3052
 
2971
3053
  /**
2972
- * Container for displaying components in a horizontal layout with overflow detection
3054
+ * Container for displaying components in a horizontal layout with overflow detection.
3055
+ * Provides visual indicators when content overflows and can be scrolled.
2973
3056
  *
2974
- * @param {HorizontalOverflowScrollerProps} props - The props for the component
2975
- * @returns {Element} HorizontalOverflowScroller component
3057
+ * @param props - Component properties
3058
+ * @param props.children - The content to display in the horizontal scroller
3059
+ * @param props.className - Optional CSS class name for styling
3060
+ * @param props.dataTestId - Optional test ID for testing purposes
3061
+ * @param props.onScrollStateChange - Optional callback fired when scroll state changes
3062
+ * @returns {ReactElement} A horizontal overflow scroller component with visual indicators
2976
3063
  */
2977
3064
  const HorizontalOverflowScroller = ({ className, dataTestId, children, onScrollStateChange, }) => {
2978
3065
  const childrenArray = react.Children.toArray(children);
2979
- const containerRef = react.useRef(null);
2980
- const containerGeometry = useMeasureElement(containerRef);
2981
- const { isScrollable, isAtBeginning, isAtEnd } = useScrollDetection(containerRef, {
3066
+ const { geometry: containerGeometry, ref: measureRef, element } = useMeasure();
3067
+ const { ref: scrollRef, isScrollable, isAtBeginning, isAtEnd, } = useScrollDetection({
2982
3068
  direction: "horizontal",
2983
3069
  onScrollStateChange: onScrollStateChange
2984
3070
  ? (state) => onScrollStateChange({
@@ -2988,23 +3074,24 @@ const HorizontalOverflowScroller = ({ className, dataTestId, children, onScrollS
2988
3074
  })
2989
3075
  : undefined,
2990
3076
  });
3077
+ const mergedRef = useMergeRefs([measureRef, scrollRef]);
2991
3078
  const handleScrollLeft = () => {
2992
- if (!containerRef.current || containerGeometry?.width === undefined)
3079
+ if (!element || containerGeometry?.width === undefined)
2993
3080
  return;
2994
- containerRef.current.scrollBy({
3081
+ element.scrollBy({
2995
3082
  left: -containerGeometry.width,
2996
3083
  behavior: "smooth",
2997
3084
  });
2998
3085
  };
2999
3086
  const handleScrollRight = () => {
3000
- if (!containerRef.current || containerGeometry?.width === undefined)
3087
+ if (!element || containerGeometry?.width === undefined)
3001
3088
  return;
3002
- containerRef.current.scrollBy({
3089
+ element.scrollBy({
3003
3090
  left: containerGeometry.width,
3004
3091
  behavior: "smooth",
3005
3092
  });
3006
3093
  };
3007
- return (jsxRuntime.jsxs(ZStack, { className: cvaHorizontalOverflowScrollerAndIndicatorsContainer({ className }), children: [jsxRuntime.jsx("div", { className: cvaHorizontalOverflowScroller(), "data-testid": dataTestId, ref: containerRef, children: childrenArray.map((child, index) => (jsxRuntime.jsx(react.Fragment, { children: child }, index))) }), isScrollable && !isAtBeginning ? (jsxRuntime.jsx(OverflowIndicator, { dataTestId: `${dataTestId}-left-indicator`, direction: "left", onClickScroll: handleScrollLeft })) : null, isScrollable && !isAtEnd ? (jsxRuntime.jsx(OverflowIndicator, { dataTestId: `${dataTestId}-right-indicator`, direction: "right", onClickScroll: handleScrollRight })) : null] }));
3094
+ return (jsxRuntime.jsxs(ZStack, { className: cvaHorizontalOverflowScrollerAndIndicatorsContainer({ className }), children: [jsxRuntime.jsx("div", { className: cvaHorizontalOverflowScroller(), "data-testid": dataTestId, ref: mergedRef, children: childrenArray.map((child, index) => (jsxRuntime.jsx(react.Fragment, { children: child }, index))) }), isScrollable && !isAtBeginning ? (jsxRuntime.jsx(OverflowIndicator, { dataTestId: `${dataTestId}-left-indicator`, direction: "left", onClickScroll: handleScrollLeft })) : null, isScrollable && !isAtEnd ? (jsxRuntime.jsx(OverflowIndicator, { dataTestId: `${dataTestId}-right-indicator`, direction: "right", onClickScroll: handleScrollRight })) : null] }));
3008
3095
  };
3009
3096
 
3010
3097
  const PADDING = 12;
@@ -3314,12 +3401,14 @@ const PopoverTrigger = function PopoverTrigger({ children, renderButton = false,
3314
3401
  const context = usePopoverContext();
3315
3402
  const ref = react$1.useMergeRefs([context.refs.setReference, propRef]);
3316
3403
  if (!renderButton && react.isValidElement(children)) {
3317
- return react.cloneElement(children, context.getReferenceProps({
3318
- ref,
3404
+ const referenceProps = context.getReferenceProps({
3319
3405
  ...props,
3320
3406
  ...children.props,
3321
3407
  "data-state": context.isOpen === true ? "open" : "closed",
3322
- }));
3408
+ });
3409
+ const cloneProps = { ...referenceProps };
3410
+ cloneProps.ref = ref;
3411
+ return react.cloneElement(children, cloneProps);
3323
3412
  }
3324
3413
  return (jsxRuntime.jsx(Button, { "data-state": context.isOpen === true ? "open" : "closed", ref: ref, type: "button", ...context.getReferenceProps(props), children: children }));
3325
3414
  };
@@ -4133,7 +4222,10 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
4133
4222
  const isAtTop = react.useMemo(() => virtualizer.scrollOffset === 0, [virtualizer.scrollOffset]);
4134
4223
  // totalSize must be out from from useMemo since otherwise we'll not be able to have the the totalSize as a dependency for the contentFillsContainer
4135
4224
  const totalSize = virtualizer.getTotalSize();
4136
- const contentFillsContainer = react.useMemo(() => (containerRef.current ? totalSize >= containerRef.current.clientHeight : false), [totalSize]);
4225
+ const contentFillsContainer = react.useMemo(() => {
4226
+ // eslint-disable-next-line react-hooks/refs
4227
+ return containerRef.current ? totalSize >= containerRef.current.clientHeight : false;
4228
+ }, [totalSize]);
4137
4229
  return {
4138
4230
  ...virtualizer,
4139
4231
  containerRef,
@@ -4799,12 +4891,20 @@ const Pagination = ({ previousPage, nextPage, canPreviousPage = false, canNextPa
4799
4891
  if (!loading && pageCount === undefined) {
4800
4892
  throw Error("Pagination should be provided with a pageCount");
4801
4893
  }
4894
+ const setCurrentPageRef = react.useRef(setCurrentPage);
4895
+ react.useEffect(() => {
4896
+ setCurrentPageRef.current = setCurrentPage;
4897
+ }, [setCurrentPage]);
4898
+ const setPageRef = react.useRef(setPage);
4899
+ react.useEffect(() => {
4900
+ setPageRef.current = setPage;
4901
+ }, [setPage]);
4802
4902
  react.useEffect(() => {
4803
4903
  if (pageIndex !== undefined && (isNaN(pageIndex) || pageIndex < 0)) {
4804
- setPage(pageIndex);
4805
- setCurrentPage(String(pageIndex + 1));
4904
+ setPageRef.current(pageIndex);
4905
+ setCurrentPageRef.current(String(pageIndex + 1));
4806
4906
  }
4807
- }, [pageIndex, setPage, setCurrentPage, pageCount]);
4907
+ }, [pageIndex, pageCount]);
4808
4908
  const handlePageChange = react.useCallback((action) => {
4809
4909
  const from = page;
4810
4910
  let to = from;
@@ -5304,17 +5404,25 @@ const cvaToggleItemContent = cssClassVarianceUtilities.cvaMerge([], {
5304
5404
  */
5305
5405
  const ToggleGroup = ({ list, selected, setSelected, onChange, disabled = false, size = "medium", isIconOnly = false, className, dataTestId, }) => {
5306
5406
  const [isMounted, setIsMounted] = react.useState(false);
5407
+ const [slidingLeft, setSlidingLeft] = react.useState(0);
5408
+ const [slidingWidth, setSlidingWidth] = react.useState(0);
5307
5409
  const buttonRefs = react.useRef([]);
5308
5410
  const selectedIndex = list.findIndex(item => item.id === selected);
5309
5411
  const validIndex = selectedIndex >= 0 ? selectedIndex : 0;
5310
- const containerPadding = 2; // p-0.5 = 2px
5311
- const gap = 4;
5312
- const slidingLeft = containerPadding +
5313
- buttonRefs.current.slice(0, validIndex).reduce((sum, ref) => sum + (ref?.offsetWidth ?? 0) + gap, 0);
5314
- const slidingWidth = buttonRefs.current[validIndex]?.offsetWidth ?? 0;
5315
- react.useEffect(() => {
5316
- setIsMounted(true);
5412
+ react.useLayoutEffect(() => {
5413
+ queueMicrotask(() => {
5414
+ setIsMounted(true);
5415
+ });
5317
5416
  }, []);
5417
+ react.useEffect(() => {
5418
+ const containerPadding = 2; // p-0.5 = 2px
5419
+ const gap = 4;
5420
+ const left = containerPadding +
5421
+ buttonRefs.current.slice(0, validIndex).reduce((sum, ref) => sum + (ref?.offsetWidth ?? 0) + gap, 0);
5422
+ const width = buttonRefs.current[validIndex]?.offsetWidth ?? 0;
5423
+ setSlidingLeft(left);
5424
+ setSlidingWidth(width);
5425
+ }, [validIndex]);
5318
5426
  return (jsxRuntime.jsx("div", { className: tailwindMerge.twMerge(cvaToggleGroup({ className }), cvaToggleGroupWithSlidingBackground({ isMounted })), "data-testid": dataTestId, style:
5319
5427
  // eslint-disable-next-line local-rules/no-typescript-assertion
5320
5428
  {
@@ -5613,7 +5721,7 @@ exports.useIsTextTruncated = useIsTextTruncated;
5613
5721
  exports.useList = useList;
5614
5722
  exports.useListItemHeight = useListItemHeight;
5615
5723
  exports.useMeasure = useMeasure;
5616
- exports.useMeasureElement = useMeasureElement;
5724
+ exports.useMergeRefs = useMergeRefs;
5617
5725
  exports.useModifierKey = useModifierKey;
5618
5726
  exports.useOverflowItems = useOverflowItems;
5619
5727
  exports.usePopoverContext = usePopoverContext;