123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- 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 || 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 restContextProps = {
- order: displayRest ? mergedDisplayCount : Number.MAX_SAFE_INTEGER,
- className: "".concat(itemPrefixCls, "-rest"),
- registerSize: registerOverflowSize,
- display: displayRest
- };
- var mergedRenderRest = renderRest || defaultRenderRest;
- var restNode = renderRawRest ? /*#__PURE__*/React.createElement(OverflowContext.Provider, {
- value: _objectSpread(_objectSpread({}, itemSharedProps), restContextProps)
- }, renderRawRest(omittedItems)) : /*#__PURE__*/React.createElement(Item, _extends({}, itemSharedProps, restContextProps), typeof mergedRenderRest === 'function' ? mergedRenderRest(omittedItems) : mergedRenderRest);
- 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));
- return isResponsive ? /*#__PURE__*/React.createElement(ResizeObserver, {
- onResize: onOverflowResize,
- disabled: !shouldResponsive
- }, overflowNode) : 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;
|