UNPKG

antd

Version:

An enterprise-class UI design language and React components implementation

88 lines 3.91 kB
import * as React from 'react'; import EllipsisOutlined from "@ant-design/icons/es/icons/EllipsisOutlined"; /** * Normalize a real item into the collapsed display list. * The original item key is preserved when provided, otherwise the original index keeps * React reconciliation stable while visible steps move around the collapsed list. */ function getDisplayStep(item, index) { return { item: { ...item, key: item.key ?? index }, originIndex: index }; } /** * Build the disabled ellipsis item between two visible steps. * Its status reflects the hidden range so the connector still communicates * finished or error progress, and its key is derived from the adjacent real steps. */ function getEllipsisStep(items, currentIndex, prevIndex, nextIndex, prefixCls) { const prevKey = items[prevIndex].key ?? prevIndex; const nextKey = items[nextIndex].key ?? nextIndex; const hasError = items.slice(prevIndex + 1, nextIndex).some(step => step.status === 'error'); const ellipsisStatus = hasError ? 'error' : nextIndex - 1 < currentIndex ? 'finish' : 'wait'; return { item: { key: `ellipsis-${prevKey}-${nextKey}`, title: '', icon: /*#__PURE__*/React.createElement(EllipsisOutlined), status: ellipsisStatus, disabled: true, className: `${prefixCls}-item-ellipsis` }, originIndex: -1 }; } /** * Pick the real step indexes that should remain visible when `maxCount` applies. * First, last, and current are always kept. Remaining slots are filled by priority: * current left, current right, first right, last left, then repeat at larger distances. * * `null` is inserted between non-contiguous indexes and later rendered as an ellipsis. */ function getCollapsedIndexes(total, currentIndex, maxCount) { const safeCurrent = Math.min(Math.max(currentIndex, 0), total - 1); const targetCount = Math.min(maxCount, total); const indexes = new Set([0, safeCurrent, total - 1]); for (let distance = 1; indexes.size < targetCount && distance < total; distance += 1) { const candidates = [safeCurrent - distance, safeCurrent + distance, distance, total - 1 - distance]; for (const index of candidates) { if (indexes.size >= targetCount) { break; } if (index >= 0 && index < total) { indexes.add(index); } } } return Array.from(indexes).sort((a, b) => a - b).flatMap((index, order, sortedIndexes) => order > 0 && index - sortedIndexes[order - 1] > 1 ? [null, index] : [index]); } /** * Convert the original items/current into the list consumed by rc-steps. * When steps are collapsed, rc-steps receives a compact item list and a remapped current, * while callers still interact with the original step indexes through `onChange`. */ export default function useDisplaySteps(mergedItems, current, initial, maxCount, prefixCls) { const canApplyMaxCount = maxCount !== undefined && maxCount >= 3 && mergedItems.length > maxCount; const mappedCurrent = current - initial; const displaySteps = React.useMemo(() => { if (!canApplyMaxCount) { return mergedItems.map((item, originIndex) => ({ item, originIndex })); } const collapsedIndexes = getCollapsedIndexes(mergedItems.length, mappedCurrent, maxCount); return collapsedIndexes.map((index, collapsedIndex) => index === null ? getEllipsisStep(mergedItems, mappedCurrent, collapsedIndexes[collapsedIndex - 1], collapsedIndexes[collapsedIndex + 1], prefixCls) : getDisplayStep(mergedItems[index], index)); }, [canApplyMaxCount, mappedCurrent, mergedItems, maxCount, prefixCls]); const displayCurrent = displaySteps.findIndex(step => step.originIndex === mappedCurrent); return { canApplyMaxCount, displaySteps, mappedDisplayCurrent: displayCurrent >= 0 ? displayCurrent : mappedCurrent, displayItems: displaySteps.map(step => step.item) }; }