import BaseFoundation, { DefaultAdapter } from '../base/foundation'; import { strings } from './constants'; import { noop, get, cloneDeep } from 'lodash'; const Boundary = strings.BOUNDARY_MAP; const OverflowDirection = strings.OVERFLOW_DIR; export interface OverflowListAdapter extends DefaultAdapter { updateStates: (state: any) => void; updateVisibleState: (visible: Map) => void; notifyIntersect: (res: any) => void; getItemSizeMap: ()=>Map } class OverflowListFoundation extends BaseFoundation { constructor(adapter: OverflowListAdapter) { super({ ...adapter }); } previousY = undefined; isScrollMode = (): boolean => { const { renderMode } = this.getProps(); return renderMode === 'scroll'; }; getOverflowItem(): Array>> { const { items } = this.getProps(); const { visibleState, overflow } = this.getStates(); if (!this.isScrollMode()) { return overflow; } const visibleStateArr = items.map(({ key }: { key: string }) => Boolean(visibleState.get(key))); const visibleStart = visibleStateArr.indexOf(true); const visibleEnd = visibleStateArr.lastIndexOf(true); const overflowList = []; overflowList[0] = visibleStart >= 0 ? items.slice(0, visibleStart) : []; overflowList[1] = visibleEnd >= 0 ? items.slice(visibleEnd + 1, items.length) : items; return overflowList; } handleIntersect(entries: Array): void { const visibleState = cloneDeep(this.getState('visibleState')); const res = {}; entries.forEach(entry => { const itemKey = get(entry, 'target.dataset.scrollkey'); const visible = entry.isIntersecting; res[itemKey] = entry; visibleState.set(itemKey, visible); }); let someItemVisible = false; for (const value of visibleState.values()) { if (value) { someItemVisible = true; break; } } // Any item is visible, indicating that the List is visible const wholeListVisible = someItemVisible; // If scrolling in the vertical direction makes the List invisible, no processing is required. // If this.previousY is undefined, it means that the List is mounted for the first time and will not be processed. const [entry1] = entries; const currentY = entry1.boundingClientRect.y; if (!wholeListVisible && this.previousY !== undefined && currentY !== this.previousY) { this.previousY = currentY; return; } this.previousY = currentY; this._adapter.updateVisibleState(visibleState); this._adapter.notifyIntersect(res); } getReversedItems = ()=>{ const { items } = this.getProps(); return cloneDeep(items).reverse(); } handleCollapseOverflow(){ const { minVisibleItems, collapseFrom } = this.getProps(); const { overflowWidth, containerWidth, pivot: statePivot, overflowStatus } = this.getStates(); const { items, onOverflow } = this.getProps(); let itemWidths = overflowWidth, _pivot = 0; let overflowed = false; for (const size of this._adapter.getItemSizeMap().values()) { itemWidths += size; // 触发overflow if (itemWidths > containerWidth) { overflowed = true; break; } // 顺利遍历完整个列表,说明不存在overflow,直接渲染全部 if (_pivot === items.length - 1) { this._adapter.updateStates({ overflowStatus: "normal", pivot: items.length - 1, visible: items, overflow: [] }); break; } _pivot++; } if (overflowed) { const pivot = Math.max(minVisibleItems, _pivot); const isCollapseFromStart = collapseFrom === Boundary.START; const visible = isCollapseFromStart ? this.getReversedItems().slice(0, pivot).reverse() : items.slice(0, pivot); const overflow = isCollapseFromStart ? this.getReversedItems().slice(pivot).reverse() : items.slice(pivot); this._adapter.updateStates({ overflowStatus: "overflowed", pivot: pivot, visible, overflow, }); // trigger onOverflow if (statePivot !== pivot){ onOverflow(overflow); } return; } } } export default OverflowListFoundation;