foundation.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  2. import { strings } from './constants';
  3. import { noop, get, cloneDeep } from 'lodash';
  4. const Boundary = strings.BOUNDARY_MAP;
  5. const OverflowDirection = strings.OVERFLOW_DIR;
  6. export interface OverflowListAdapter extends DefaultAdapter {
  7. updateStates: (state: any) => void;
  8. updateVisibleState: (visible: Map<string, boolean>) => void;
  9. notifyIntersect: (res: any) => void;
  10. getItemSizeMap: ()=>Map<string, number>
  11. }
  12. class OverflowListFoundation extends BaseFoundation<OverflowListAdapter> {
  13. constructor(adapter: OverflowListAdapter) {
  14. super({ ...adapter });
  15. }
  16. previousY = undefined;
  17. isScrollMode = (): boolean => {
  18. const { renderMode } = this.getProps();
  19. return renderMode === 'scroll';
  20. };
  21. getOverflowItem(): Array<Array<Record<string, any>>> {
  22. const { items } = this.getProps();
  23. const { visibleState, overflow } = this.getStates();
  24. if (!this.isScrollMode()) {
  25. return overflow;
  26. }
  27. const visibleStateArr = items.map(({ key }: { key: string }) => Boolean(visibleState.get(key)));
  28. const visibleStart = visibleStateArr.indexOf(true);
  29. const visibleEnd = visibleStateArr.lastIndexOf(true);
  30. const overflowList = [];
  31. overflowList[0] = visibleStart >= 0 ? items.slice(0, visibleStart) : [];
  32. overflowList[1] = visibleEnd >= 0 ? items.slice(visibleEnd + 1, items.length) : items;
  33. return overflowList;
  34. }
  35. handleIntersect(entries: Array<IntersectionObserverEntry>): void {
  36. const visibleState = cloneDeep(this.getState('visibleState'));
  37. const res = {};
  38. entries.forEach(entry => {
  39. const itemKey = get(entry, 'target.dataset.scrollkey');
  40. const visible = entry.isIntersecting;
  41. res[itemKey] = entry;
  42. visibleState.set(itemKey, visible);
  43. });
  44. let someItemVisible = false;
  45. for (const value of visibleState.values()) {
  46. if (value) {
  47. someItemVisible = true;
  48. break;
  49. }
  50. }
  51. // Any item is visible, indicating that the List is visible
  52. const wholeListVisible = someItemVisible;
  53. // If scrolling in the vertical direction makes the List invisible, no processing is required.
  54. // If this.previousY is undefined, it means that the List is mounted for the first time and will not be processed.
  55. const [entry1] = entries;
  56. const currentY = entry1.boundingClientRect.y;
  57. if (!wholeListVisible && this.previousY !== undefined && currentY !== this.previousY) {
  58. this.previousY = currentY;
  59. return;
  60. }
  61. this.previousY = currentY;
  62. this._adapter.updateVisibleState(visibleState);
  63. this._adapter.notifyIntersect(res);
  64. }
  65. getReversedItems = ()=>{
  66. const { items } = this.getProps();
  67. return cloneDeep(items).reverse();
  68. }
  69. handleCollapseOverflow(){
  70. const { minVisibleItems, collapseFrom } = this.getProps();
  71. const { overflowWidth, containerWidth, pivot: statePivot, overflowStatus } = this.getStates();
  72. const { items, onOverflow } = this.getProps();
  73. let itemWidths = overflowWidth, _pivot = 0;
  74. let overflowed = false;
  75. for (const size of this._adapter.getItemSizeMap().values()) {
  76. itemWidths += size;
  77. // 触发overflow
  78. if (itemWidths > containerWidth) {
  79. overflowed = true;
  80. break;
  81. }
  82. // 顺利遍历完整个列表,说明不存在overflow,直接渲染全部
  83. if (_pivot === items.length - 1) {
  84. this._adapter.updateStates({
  85. overflowStatus: "normal",
  86. pivot: items.length - 1,
  87. visible: items,
  88. overflow: []
  89. });
  90. break;
  91. }
  92. _pivot++;
  93. }
  94. if (overflowed) {
  95. const pivot = Math.max(minVisibleItems, _pivot);
  96. const isCollapseFromStart = collapseFrom === Boundary.START;
  97. const visible = isCollapseFromStart ? this.getReversedItems().slice(0, pivot).reverse() : items.slice(0, pivot);
  98. const overflow = isCollapseFromStart ? this.getReversedItems().slice(pivot).reverse() : items.slice(pivot);
  99. this._adapter.updateStates({
  100. overflowStatus: "overflowed",
  101. pivot: pivot,
  102. visible,
  103. overflow,
  104. });
  105. // trigger onOverflow
  106. if (statePivot !== pivot){
  107. onOverflow(overflow);
  108. }
  109. return;
  110. }
  111. }
  112. }
  113. export default OverflowListFoundation;