@itwin/itwinui-react
Version:
A react component library for iTwinUI
204 lines (203 loc) • 6.83 kB
JavaScript
;
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;
}