foundation.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. }
  11. class OverflowListFoundation extends BaseFoundation<OverflowListAdapter> {
  12. constructor(adapter: OverflowListAdapter) {
  13. super({ ...adapter });
  14. }
  15. previousY = undefined;
  16. isScrollMode = (): boolean => {
  17. const { renderMode } = this.getProps();
  18. return renderMode === 'scroll';
  19. };
  20. getOverflowItem(): Array<Array<Record<string, any>>> {
  21. const { items } = this.getProps();
  22. const { visibleState, overflow } = this.getStates();
  23. if (!this.isScrollMode()) {
  24. return overflow;
  25. }
  26. const visibleStateArr = items.map(({ key }: { key: string }) => Boolean(visibleState.get(key)));
  27. const visibleStart = visibleStateArr.indexOf(true);
  28. const visibleEnd = visibleStateArr.lastIndexOf(true);
  29. const overflowList = [];
  30. overflowList[0] = visibleStart >= 0 ? items.slice(0, visibleStart) : [];
  31. overflowList[1] = visibleEnd >= 0 ? items.slice(visibleEnd + 1, items.length) : items;
  32. return overflowList;
  33. }
  34. handleIntersect(entries: Array<IntersectionObserverEntry>): void {
  35. const visibleState = cloneDeep(this.getState('visibleState'));
  36. const res = {};
  37. entries.forEach(entry => {
  38. const itemKey = get(entry, 'target.dataset.scrollkey');
  39. const visible = entry.isIntersecting;
  40. res[itemKey] = entry;
  41. visibleState.set(itemKey, visible);
  42. });
  43. let someItemVisible = false;
  44. for (const value of visibleState.values()) {
  45. if (value) {
  46. someItemVisible = true;
  47. break;
  48. }
  49. }
  50. // Any item is visible, indicating that the List is visible
  51. const wholeListVisible = someItemVisible;
  52. // If scrolling in the vertical direction makes the List invisible, no processing is required.
  53. // If this.previousY is undefined, it means that the List is mounted for the first time and will not be processed.
  54. const [entry1] = entries;
  55. const currentY = entry1.boundingClientRect.y;
  56. if (!wholeListVisible && this.previousY !== undefined && currentY !== this.previousY) {
  57. this.previousY = currentY;
  58. return;
  59. }
  60. this.previousY = currentY;
  61. this._adapter.updateVisibleState(visibleState);
  62. this._adapter.notifyIntersect(res);
  63. }
  64. handlePartition(growing: number): void {
  65. const { direction, overflow, lastOverflowCount, visible } = this.getStates();
  66. const { minVisibleItems, collapseFrom, items } = this.getProps();
  67. let updateState = {};
  68. if (growing === OverflowDirection.NONE) {
  69. updateState = { direction: OverflowDirection.NONE };
  70. }
  71. if (growing === OverflowDirection.GROW) {
  72. const updatedOverflowCount = direction === OverflowDirection.NONE ? overflow.length : lastOverflowCount;
  73. updateState = {
  74. direction: OverflowDirection.GROW,
  75. lastOverflowCount: updatedOverflowCount,
  76. overflow: [],
  77. visible: items,
  78. };
  79. }
  80. if (growing === OverflowDirection.SHRINK && visible.length > minVisibleItems) {
  81. const collapseFromStart = collapseFrom === Boundary.START;
  82. const newVisible = visible.slice();
  83. const next = collapseFromStart ? newVisible.shift() : newVisible.pop();
  84. if (next !== undefined) {
  85. updateState = {
  86. // set SHRINK mode unless a GROW is already in progress.
  87. // GROW shows all items then shrinks until it settles, so we
  88. // preserve the fact that the original trigger was a GROW.
  89. direction: direction !== OverflowDirection.GROW ? OverflowDirection.SHRINK : direction,
  90. overflow: collapseFromStart ? [...overflow, next] : [next, ...overflow],
  91. visible: newVisible
  92. };
  93. }
  94. }
  95. this._adapter.updateStates(updateState);
  96. }
  97. }
  98. export default OverflowListFoundation;