• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 8389034572

22 Mar 2024 10:38AM CUT coverage: 26.087% (-2.1%) from 28.208%
8389034572

Pull #487

github

web-flow
Merge 3045b1f94 into a06c6afb1
Pull Request #487: refactor: move zod schema to openapi

2100 of 3363 branches covered (62.44%)

282 of 757 new or added lines in 74 files covered. (37.25%)

224 existing lines in 8 files now uncovered.

25574 of 98035 relevant lines covered (26.09%)

5.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/sdk/src/components/grid-enhancements/hooks/use-grid-async-records.ts
NEW
1
import type { IRecord } from '@teable/core';
×
NEW
2
import type { IGetRecordsRo } from '@teable/openapi';
×
3
import { inRange, debounce } from 'lodash';
×
4
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
×
5
import type { IGridProps, IRectangle } from '../..';
×
6
import { useTableId } from '../../../hooks';
×
7
import { useRecords } from '../../../hooks/use-records';
×
8
import { useViewId } from '../../../hooks/use-view-id';
×
9
import { Record } from '../../../model';
×
10

×
11
// eslint-disable-next-line
×
12
export const LOAD_PAGE_SIZE = 300;
×
13
const defaultVisiblePages = { x: 0, y: 0, width: 0, height: 0 };
×
14

×
15
type IRes = {
×
16
  recordMap: IRecordIndexMap;
×
17
  onReset: () => void;
×
18
  onForceUpdate: () => void;
×
19
  onRowOrdered: (rowIndexCollection: number[], newRowIndex: number) => void;
×
20
  onVisibleRegionChanged: NonNullable<IGridProps['onVisibleRegionChanged']>;
×
21
};
×
22

×
23
export type IRecordIndexMap = { [i: number | string]: Record };
×
24

×
25
export const useGridAsyncRecords = (
×
26
  initRecords?: IRecord[],
×
27
  initQuery?: IGetRecordsRo,
×
28
  outerQuery?: Pick<IGetRecordsRo, 'filter' | 'orderBy' | 'groupBy'>
×
29
): IRes => {
×
30
  const [query, setQuery] = useState<IGetRecordsRo>({
×
31
    skip: 0,
×
32
    take: LOAD_PAGE_SIZE,
×
33
    ...initQuery,
×
34
  });
×
35
  const recordsQuery = useMemo(() => ({ ...query, ...outerQuery }), [query, outerQuery]);
×
36
  const tableId = useTableId();
×
37
  const viewId = useViewId();
×
38
  const queryRef = useRef(query);
×
39
  queryRef.current = query;
×
40
  const records = useRecords(recordsQuery, initRecords);
×
41
  const [loadedRecordMap, setLoadedRecordMap] = useState<IRecordIndexMap>(() =>
×
42
    records.reduce((acc, record, i) => {
×
43
      acc[i] = record;
×
44
      return acc;
×
45
    }, {} as IRecordIndexMap)
×
46
  );
×
47
  const [visiblePages, setVisiblePages] = useState<IRectangle>(defaultVisiblePages);
×
48
  const visiblePagesRef = useRef(visiblePages);
×
49
  visiblePagesRef.current = visiblePages;
×
50

×
51
  const onForceUpdate = useCallback(() => {
×
52
    const startIndex = queryRef.current.skip ?? 0;
×
53
    const take = queryRef.current.take ?? LOAD_PAGE_SIZE;
×
54
    setLoadedRecordMap((preLoadedRecords) => {
×
55
      const cacheLen = take * 2;
×
56
      const [cacheStartIndex, cacheEndIndex] = [
×
57
        Math.max(startIndex - cacheLen / 2, 0),
×
58
        startIndex + records.length + cacheLen / 2,
×
59
      ];
×
60
      const newRecordsState: IRecordIndexMap = {};
×
61
      for (let i = cacheStartIndex; i < cacheEndIndex; i++) {
×
62
        if (startIndex <= i && i < startIndex + records.length) {
×
63
          newRecordsState[i] = records[i - startIndex];
×
64
          continue;
×
65
        }
×
66
        newRecordsState[i] = preLoadedRecords[i];
×
67
      }
×
68
      return newRecordsState;
×
69
    });
×
70
  }, [records]);
×
71

×
72
  useEffect(() => onForceUpdate(), [onForceUpdate]);
×
73

×
74
  useEffect(() => {
×
75
    const { y, height } = visiblePages;
×
76
    setQuery((cv) => {
×
77
      if (cv.skip === undefined) {
×
78
        return cv;
×
79
      }
×
80

×
81
      const take = initQuery?.take ?? cv.take ?? LOAD_PAGE_SIZE;
×
82

×
83
      const pageOffsetSize = take / 3;
×
84
      const pageGap = take / 3;
×
85

×
86
      const visibleStartIndex = cv.skip <= y ? cv.skip - pageOffsetSize : cv.skip + pageOffsetSize;
×
87
      const visibleEndIndex = visibleStartIndex + take;
×
88
      const viewInRange =
×
89
        inRange(y, visibleStartIndex, visibleEndIndex) &&
×
90
        inRange(y + height, visibleStartIndex, visibleEndIndex);
×
91
      if (!viewInRange) {
×
92
        const skip = Math.floor(y / pageGap) * pageGap - pageGap;
×
93
        return {
×
94
          take: cv.take,
×
95
          ...initQuery,
×
96
          skip: Math.max(0, skip),
×
97
        };
×
98
      }
×
99
      return {
×
100
        take: cv.take,
×
101
        ...initQuery,
×
102
        skip: cv.skip,
×
103
      };
×
104
    });
×
105
  }, [visiblePages, initQuery]);
×
106

×
107
  const updateVisiblePages = useMemo(() => {
×
108
    return debounce(setVisiblePages, 30, { maxWait: 500 });
×
109
  }, []);
×
110

×
111
  const onVisibleRegionChanged: NonNullable<IGridProps['onVisibleRegionChanged']> = useCallback(
×
112
    (r) => {
×
113
      const { y, height } = visiblePagesRef.current;
×
114
      if (r.y === y && r.height === height) return;
×
115
      updateVisiblePages(r);
×
116
    },
×
117
    [updateVisiblePages]
×
118
  );
×
119

×
120
  const onReset = useCallback(() => {
×
121
    setLoadedRecordMap({});
×
122
    setVisiblePages(defaultVisiblePages);
×
123
  }, []);
×
124

×
125
  const onRowOrdered = useCallback(
×
126
    (rowIndexCollection: number[], newRowIndex: number) => {
×
127
      const operationRecordIds: string[] = [];
×
128

×
129
      for (const rowIndex of rowIndexCollection) {
×
130
        const record = loadedRecordMap[rowIndex];
×
131
        if (!record) {
×
132
          throw new Error('Can not find record by index: ' + rowIndex);
×
133
        }
×
134
        operationRecordIds.push(record.id);
×
135
      }
×
136

×
137
      if (!viewId) {
×
138
        throw new Error('Can not find view id');
×
139
      }
×
140

×
141
      if (newRowIndex === 0) {
×
142
        Record.updateRecordOrders(tableId as string, viewId, {
×
143
          anchorId: loadedRecordMap[0].id,
×
144
          position: 'before',
×
145
          recordIds: operationRecordIds,
×
146
        });
×
147
        return;
×
148
      }
×
149
      const record = loadedRecordMap[newRowIndex - 1];
×
150
      if (!record) {
×
151
        throw new Error("Can't find target record by index: " + newRowIndex);
×
152
      }
×
153
      Record.updateRecordOrders(tableId as string, viewId, {
×
154
        anchorId: record.id,
×
155
        position: 'after',
×
156
        recordIds: operationRecordIds,
×
157
      });
×
158
    },
×
159
    [viewId, loadedRecordMap, tableId]
×
160
  );
×
161

×
162
  return {
×
163
    recordMap: loadedRecordMap,
×
164
    onVisibleRegionChanged,
×
165
    onRowOrdered,
×
166
    onForceUpdate,
×
167
    onReset,
×
168
  };
×
169
};
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc