UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

204 lines (203 loc) 6.83 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true, }); Object.defineProperty(exports, 'OverflowContainer', { enumerable: true, get: function () { return OverflowContainer; }, }); const _interop_require_default = require('@swc/helpers/_/_interop_require_default'); const _react = /*#__PURE__*/ _interop_require_default._(require('react')); const _useMergedRefs = require('../hooks/useMergedRefs.js'); const _Box = require('./Box.js'); const _useIsomorphicLayoutEffect = require('../hooks/useIsomorphicLayoutEffect.js'); const _useSafeContext = require('../hooks/useSafeContext.js'); const _dev = require('../functions/dev.js'); const _useResizeObserver = require('../hooks/useResizeObserver.js'); const OverflowContainerMain = _react.default.forwardRef( (props, forwardedRef) => { let { itemsCount, children, overflowOrientation, ...rest } = props; let [containerRef, visibleCount] = useOverflow( itemsCount, overflowOrientation, ); let overflowContainerContextValue = _react.default.useMemo( () => ({ visibleCount, itemsCount, }), [itemsCount, visibleCount], ); return _react.default.createElement( OverflowContainerContext.Provider, { value: overflowContainerContextValue, }, _react.default.createElement( _Box.Box, { ref: (0, _useMergedRefs.useMergedRefs)(forwardedRef, containerRef), ...rest, }, children, ), ); }, ); const OverflowContainerOverflowNode = (props) => { let { children } = props; let { visibleCount, itemsCount } = useOverflowContainerContext(); let isOverflowing = visibleCount < itemsCount; return isOverflowing ? children : null; }; const OverflowContainerComponent = _react.default.forwardRef( (props, forwardedRef) => { let { itemsCount, overflowOrientation = 'horizontal', ...rest } = props; let [size, setSize] = _react.default.useState(null); let [resizeRef] = (0, _useResizeObserver.useResizeObserver)(setSize); let ref = (0, _useMergedRefs.useMergedRefs)(resizeRef, forwardedRef); let key = `${itemsCount}${ 'vertical' === overflowOrientation ? size?.height : size?.width }`; return _react.default.createElement(OverflowContainerMain, { ...rest, key: key, ref: ref, itemsCount: itemsCount, overflowOrientation: overflowOrientation, }); }, ); const OverflowContainer = Object.assign(OverflowContainerComponent, { OverflowNode: OverflowContainerOverflowNode, useContext: useOverflowContainerContext, }); const OverflowContainerContext = _react.default.createContext(void 0); if ('development' === process.env.NODE_ENV) OverflowContainerContext.displayName = 'OverflowContainerContext'; const useOverflow = (itemsCount, orientation = 'horizontal') => { let [guessState, dispatch] = _react.default.useReducer( overflowGuessReducer, { itemsCount, }, overflowGuessReducerInitialState, ); let containerRef = _react.default.useRef(null); let isGuessing = _react.default.useRef(false); (0, _useIsomorphicLayoutEffect.useLayoutEffect)(() => { let { minGuess, maxGuess, isStabilized, visibleCount } = guessState; if (isStabilized) return; guessVisibleCount(); function guessVisibleCount() { if (isStabilized || isGuessing.current || _dev.isUnitTest) return; try { isGuessing.current = true; if (null == containerRef.current) return; let dimension = 'horizontal' === orientation ? 'Width' : 'Height'; let availableSize = containerRef.current[`offset${dimension}`]; let requiredSize = containerRef.current[`scroll${dimension}`]; let isOverflowing = availableSize < requiredSize; if ( 0 === itemsCount || (1 === visibleCount && isOverflowing) || (visibleCount === itemsCount && !isOverflowing) || (maxGuess - minGuess === 1 && visibleCount === minGuess) ) return void dispatch({ type: 'stabilize', }); if (maxGuess === visibleCount && !isOverflowing) return void dispatch({ type: 'shiftGuessRangeForward', }); isOverflowing ? dispatch({ type: 'decreaseMaxGuess', currentState: guessState, }) : dispatch({ type: 'increaseMinGuess', currentState: guessState, }); } finally { isGuessing.current = false; } } }, [guessState, itemsCount, orientation]); return [containerRef, guessState.visibleCount]; }; const STARTING_MAX_ITEMS_COUNT = 32; const overflowGuessReducerInitialState = ({ itemsCount }) => { let initialVisibleCount = Math.min(itemsCount, STARTING_MAX_ITEMS_COUNT); return _dev.isUnitTest ? { isStabilized: true, minGuess: null, maxGuess: null, itemsCount, visibleCount: itemsCount, } : { isStabilized: false, minGuess: 0, maxGuess: initialVisibleCount, itemsCount, visibleCount: initialVisibleCount, }; }; const overflowGuessReducer = (state, action) => { let getSafeVisibleCount = ({ visibleCount, itemsCount }) => Math.min(itemsCount, visibleCount); switch (action.type) { case 'decreaseMaxGuess': case 'increaseMinGuess': if (state.isStabilized) return state; let newMinGuess = state.minGuess; let newMaxGuess = state.maxGuess; if ('decreaseMaxGuess' === action.type) newMaxGuess = action.currentState.visibleCount; else newMinGuess = action.currentState.visibleCount; let newVisibleCount = Math.floor((newMinGuess + newMaxGuess) / 2); return { ...state, isStabilized: false, minGuess: newMinGuess, maxGuess: newMaxGuess, visibleCount: getSafeVisibleCount({ visibleCount: newVisibleCount, itemsCount: state.itemsCount, }), }; case 'shiftGuessRangeForward': if (state.isStabilized) return state; let doubleOfMaxGuess = 2 * state.maxGuess; return { ...state, isStabilized: false, minGuess: state.maxGuess, maxGuess: doubleOfMaxGuess, visibleCount: getSafeVisibleCount({ visibleCount: doubleOfMaxGuess, itemsCount: state.itemsCount, }), }; case 'stabilize': return { ...state, isStabilized: true, minGuess: null, maxGuess: null, }; default: return state; } }; function useOverflowContainerContext() { let overflowContainerContext = (0, _useSafeContext.useSafeContext)( OverflowContainerContext, ); return overflowContainerContext; }