foundation.ts 4.7 KB

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