import _extends from "@babel/runtime/helpers/esm/extends"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; var _excluded = ["prefixCls", "data", "renderItem", "renderRawItem", "itemKey", "itemWidth", "ssr", "style", "className", "maxCount", "renderRest", "renderRawRest", "suffix", "component", "itemComponent", "onVisibleChange"]; import * as React from 'react'; import { useState, useMemo, useCallback } from 'react'; import classNames from 'classnames'; import ResizeObserver from 'rc-resize-observer'; import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect"; import Item from './Item'; import useEffectState, { useBatcher } from './hooks/useEffectState'; import RawItem from './RawItem'; import { OverflowContext } from './context'; var RESPONSIVE = 'responsive'; var INVALIDATE = 'invalidate'; export { OverflowContext } from './context'; function defaultRenderRest(omittedItems) { return "+ ".concat(omittedItems.length, " ..."); } function Overflow(props, ref) { var _props$prefixCls = props.prefixCls, prefixCls = _props$prefixCls === void 0 ? 'rc-overflow' : _props$prefixCls, _props$data = props.data, data = _props$data === void 0 ? [] : _props$data, renderItem = props.renderItem, renderRawItem = props.renderRawItem, itemKey = props.itemKey, _props$itemWidth = props.itemWidth, itemWidth = _props$itemWidth === void 0 ? 10 : _props$itemWidth, ssr = props.ssr, style = props.style, className = props.className, maxCount = props.maxCount, renderRest = props.renderRest, renderRawRest = props.renderRawRest, suffix = props.suffix, _props$component = props.component, Component = _props$component === void 0 ? 'div' : _props$component, itemComponent = props.itemComponent, onVisibleChange = props.onVisibleChange, restProps = _objectWithoutProperties(props, _excluded); var fullySSR = ssr === 'full'; var notifyEffectUpdate = useBatcher(); var _useEffectState = useEffectState(notifyEffectUpdate, null), _useEffectState2 = _slicedToArray(_useEffectState, 2), containerWidth = _useEffectState2[0], setContainerWidth = _useEffectState2[1]; var mergedContainerWidth = containerWidth || 0; var _useEffectState3 = useEffectState(notifyEffectUpdate, new Map()), _useEffectState4 = _slicedToArray(_useEffectState3, 2), itemWidths = _useEffectState4[0], setItemWidths = _useEffectState4[1]; var _useEffectState5 = useEffectState(notifyEffectUpdate, 0), _useEffectState6 = _slicedToArray(_useEffectState5, 2), prevRestWidth = _useEffectState6[0], setPrevRestWidth = _useEffectState6[1]; var _useEffectState7 = useEffectState(notifyEffectUpdate, 0), _useEffectState8 = _slicedToArray(_useEffectState7, 2), restWidth = _useEffectState8[0], setRestWidth = _useEffectState8[1]; var _useEffectState9 = useEffectState(notifyEffectUpdate, 0), _useEffectState10 = _slicedToArray(_useEffectState9, 2), suffixWidth = _useEffectState10[0], setSuffixWidth = _useEffectState10[1]; var _useState = useState(null), _useState2 = _slicedToArray(_useState, 2), suffixFixedStart = _useState2[0], setSuffixFixedStart = _useState2[1]; var _useState3 = useState(null), _useState4 = _slicedToArray(_useState3, 2), displayCount = _useState4[0], setDisplayCount = _useState4[1]; var mergedDisplayCount = React.useMemo(function () { if (displayCount === null && fullySSR) { return Number.MAX_SAFE_INTEGER; } return displayCount || 0; }, [displayCount, containerWidth]); var _useState5 = useState(false), _useState6 = _slicedToArray(_useState5, 2), restReady = _useState6[0], setRestReady = _useState6[1]; var itemPrefixCls = "".concat(prefixCls, "-item"); // Always use the max width to avoid blink var mergedRestWidth = Math.max(prevRestWidth, restWidth); // ================================= Data ================================= var isResponsive = maxCount === RESPONSIVE; var shouldResponsive = data.length && isResponsive; var invalidate = maxCount === INVALIDATE; /** * When is `responsive`, we will always render rest node to get the real width of it for calculation */ var showRest = shouldResponsive || typeof maxCount === 'number' && data.length > maxCount; var mergedData = useMemo(function () { var items = data; if (shouldResponsive) { if (containerWidth === null && fullySSR) { items = data; } else { items = data.slice(0, Math.min(data.length, mergedContainerWidth / itemWidth)); } } else if (typeof maxCount === 'number') { items = data.slice(0, maxCount); } return items; }, [data, itemWidth, containerWidth, maxCount, shouldResponsive]); var omittedItems = useMemo(function () { if (shouldResponsive) { return data.slice(mergedDisplayCount + 1); } return data.slice(mergedData.length); }, [data, mergedData, shouldResponsive, mergedDisplayCount]); // ================================= Item ================================= var getKey = useCallback(function (item, index) { var _ref; if (typeof itemKey === 'function') { return itemKey(item); } return (_ref = itemKey && (item === null || item === void 0 ? void 0 : item[itemKey])) !== null && _ref !== void 0 ? _ref : index; }, [itemKey]); var mergedRenderItem = useCallback(renderItem || function (item) { return item; }, [renderItem]); function updateDisplayCount(count, suffixFixedStartVal, notReady) { // React 18 will sync render even when the value is same in some case. // We take `mergedData` as deps which may cause dead loop if it's dynamic generate. // ref: https://github.com/ant-design/ant-design/issues/36559 if (displayCount === count && (suffixFixedStartVal === undefined || suffixFixedStartVal === suffixFixedStart)) { return; } setDisplayCount(count); if (!notReady) { setRestReady(count < data.length - 1); onVisibleChange === null || onVisibleChange === void 0 ? void 0 : onVisibleChange(count); } if (suffixFixedStartVal !== undefined) { setSuffixFixedStart(suffixFixedStartVal); } } // ================================= Size ================================= function onOverflowResize(_, element) { setContainerWidth(element.clientWidth); } function registerSize(key, width) { setItemWidths(function (origin) { var clone = new Map(origin); if (width === null) { clone.delete(key); } else { clone.set(key, width); } return clone; }); } function registerOverflowSize(_, width) { setRestWidth(width); setPrevRestWidth(restWidth); } function registerSuffixSize(_, width) { setSuffixWidth(width); } // ================================ Effect ================================ function getItemWidth(index) { return itemWidths.get(getKey(mergedData[index], index)); } useLayoutEffect(function () { if (mergedContainerWidth && typeof mergedRestWidth === 'number' && mergedData) { var totalWidth = suffixWidth; var len = mergedData.length; var lastIndex = len - 1; // When data count change to 0, reset this since not loop will reach if (!len) { updateDisplayCount(0, null); return; } for (var i = 0; i < len; i += 1) { var currentItemWidth = getItemWidth(i); // Fully will always render if (fullySSR) { currentItemWidth = currentItemWidth || 0; } // Break since data not ready if (currentItemWidth === undefined) { updateDisplayCount(i - 1, undefined, true); break; } // Find best match totalWidth += currentItemWidth; if ( // Only one means `totalWidth` is the final width lastIndex === 0 && totalWidth <= mergedContainerWidth || // Last two width will be the final width i === lastIndex - 1 && totalWidth + getItemWidth(lastIndex) <= mergedContainerWidth) { // Additional check if match the end updateDisplayCount(lastIndex, null); break; } else if (totalWidth + mergedRestWidth > mergedContainerWidth) { // Can not hold all the content to show rest updateDisplayCount(i - 1, totalWidth - currentItemWidth - suffixWidth + restWidth); break; } } if (suffix && getItemWidth(0) + suffixWidth > mergedContainerWidth) { setSuffixFixedStart(null); } } }, [mergedContainerWidth, itemWidths, restWidth, suffixWidth, getKey, mergedData]); // ================================ Render ================================ var displayRest = restReady && !!omittedItems.length; var suffixStyle = {}; if (suffixFixedStart !== null && shouldResponsive) { suffixStyle = { position: 'absolute', left: suffixFixedStart, top: 0 }; } var itemSharedProps = { prefixCls: itemPrefixCls, responsive: shouldResponsive, component: itemComponent, invalidate: invalidate }; // >>>>> Choice render fun by `renderRawItem` var internalRenderItemNode = renderRawItem ? function (item, index) { var key = getKey(item, index); return /*#__PURE__*/React.createElement(OverflowContext.Provider, { key: key, value: _objectSpread(_objectSpread({}, itemSharedProps), {}, { order: index, item: item, itemKey: key, registerSize: registerSize, display: index <= mergedDisplayCount }) }, renderRawItem(item, index)); } : function (item, index) { var key = getKey(item, index); return /*#__PURE__*/React.createElement(Item, _extends({}, itemSharedProps, { order: index, key: key, item: item, renderItem: mergedRenderItem, itemKey: key, registerSize: registerSize, display: index <= mergedDisplayCount })); }; // >>>>> Rest node var restNode; var restContextProps = { order: displayRest ? mergedDisplayCount : Number.MAX_SAFE_INTEGER, className: "".concat(itemPrefixCls, "-rest"), registerSize: registerOverflowSize, display: displayRest }; if (!renderRawRest) { var mergedRenderRest = renderRest || defaultRenderRest; restNode = /*#__PURE__*/React.createElement(Item, _extends({}, itemSharedProps, restContextProps), typeof mergedRenderRest === 'function' ? mergedRenderRest(omittedItems) : mergedRenderRest); } else if (renderRawRest) { restNode = /*#__PURE__*/React.createElement(OverflowContext.Provider, { value: _objectSpread(_objectSpread({}, itemSharedProps), restContextProps) }, renderRawRest(omittedItems)); } var overflowNode = /*#__PURE__*/React.createElement(Component, _extends({ className: classNames(!invalidate && prefixCls, className), style: style, ref: ref }, restProps), mergedData.map(internalRenderItemNode), showRest ? restNode : null, suffix && /*#__PURE__*/React.createElement(Item, _extends({}, itemSharedProps, { responsive: isResponsive, responsiveDisabled: !shouldResponsive, order: mergedDisplayCount, className: "".concat(itemPrefixCls, "-suffix"), registerSize: registerSuffixSize, display: true, style: suffixStyle }), suffix)); if (isResponsive) { overflowNode = /*#__PURE__*/React.createElement(ResizeObserver, { onResize: onOverflowResize, disabled: !shouldResponsive }, overflowNode); } return overflowNode; } var ForwardOverflow = /*#__PURE__*/React.forwardRef(Overflow); ForwardOverflow.displayName = 'Overflow'; ForwardOverflow.Item = RawItem; ForwardOverflow.RESPONSIVE = RESPONSIVE; ForwardOverflow.INVALIDATE = INVALIDATE; // Convert to generic type export default ForwardOverflow;