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

teableio / teable / 8389034572

22 Mar 2024 10:38AM UTC 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
/apps/nextjs-app/src/features/app/blocks/share/view/component/grid/GridViewBase.tsx
1
import { useMutation } from '@tanstack/react-query';
×
NEW
2
import type { IGridViewOptions, IFilter } from '@teable/core';
×
3
import { RowHeightLevel, mergeFilter } from '@teable/core';
×
NEW
4
import type { IGetRecordsRo, IRangesRo } from '@teable/openapi';
×
5
import { shareViewCopy } from '@teable/openapi';
×
6
import type {
×
7
  CombinedSelection,
×
8
  ICell,
×
9
  ICellItem,
×
10
  IGridRef,
×
11
  IGroupPoint,
×
12
  IRectangle,
×
13
} from '@teable/sdk/components';
×
14
import {
×
15
  DraggableType,
×
16
  Grid,
×
17
  useGridAsyncRecords,
×
18
  useGridColumnResize,
×
19
  useGridColumnStatistics,
×
20
  useGridColumns,
×
21
  useGridIcons,
×
22
  useGridTheme,
×
23
  RowControlType,
×
24
  CellType,
×
25
  useGridGroupCollection,
×
26
  useGridCollapsedGroup,
×
27
  RowCounter,
×
28
  useGridColumnOrder,
×
29
  generateLocalId,
×
30
} from '@teable/sdk/components';
×
31
import {
×
32
  useGroupPoint,
×
33
  useIsHydrated,
×
34
  useIsTouchDevice,
×
35
  useRowCount,
×
36
  useSSRRecord,
×
37
  useSSRRecords,
×
38
  useTableId,
×
39
  useView,
×
40
} from '@teable/sdk/hooks';
×
41
import { Skeleton, useToast } from '@teable/ui-lib/shadcn';
×
42
import { useRouter } from 'next/router';
×
43
import { useCallback, useEffect, useMemo, useRef } from 'react';
×
44
import { useClickAway } from 'react-use';
×
45
import { StatisticMenu } from '@/features/app/blocks/view/grid/components';
×
46
import { ExpandRecordContainer } from '@/features/app/components/ExpandRecordContainer';
×
47
import type { IExpandRecordContainerRef } from '@/features/app/components/ExpandRecordContainer/types';
×
48
import { GIRD_ROW_HEIGHT_DEFINITIONS } from '../../../../view/grid/const';
×
49
import { useCopy } from '../../../../view/grid/hooks';
×
50
import { useGridViewStore } from '../../../../view/grid/store/gridView';
×
51

×
52
export const GridViewBase = () => {
×
53
  const view = useView();
×
54
  const tableId = useTableId();
×
55
  const router = useRouter();
×
56
  const isHydrated = useIsHydrated();
×
57
  const groupPoints = useGroupPoint();
×
58
  const prepare = isHydrated && view;
×
59
  const gridRef = useRef<IGridRef>(null);
×
60
  const container = useRef<HTMLDivElement>(null);
×
61
  const expandRecordRef = useRef<IExpandRecordContainerRef>(null);
×
62
  const { toast } = useToast();
×
63
  const theme = useGridTheme();
×
64
  const rowCount = useRowCount();
×
65
  const ssrRecords = useSSRRecords();
×
66
  const ssrRecord = useSSRRecord();
×
67
  const isTouchDevice = useIsTouchDevice();
×
68
  const { setSelection, openStatisticMenu } = useGridViewStore();
×
69
  const { columns: originalColumns, cellValue2GridDisplay } = useGridColumns();
×
70
  const { columns, onColumnResize } = useGridColumnResize(originalColumns);
×
71
  const { columnStatistics } = useGridColumnStatistics(columns);
×
72
  const { onColumnOrdered } = useGridColumnOrder();
×
73

×
74
  const customIcons = useGridIcons();
×
75
  const { mutateAsync: copy } = useMutation({
×
76
    mutationFn: (copyRo: IRangesRo) => shareViewCopy(router.query.shareId as string, copyRo),
×
77
  });
×
78
  const copyMethod = useCopy({ copyReq: copy });
×
79
  const { filter, sort, group } = view ?? {};
×
80
  const realRowCount = rowCount ?? ssrRecords?.length ?? 0;
×
81

×
82
  const groupCollection = useGridGroupCollection();
×
83

×
84
  const { viewGroupQuery, collapsedGroupIds, onCollapsedGroupChanged } = useGridCollapsedGroup(
×
85
    generateLocalId(tableId, view?.id),
×
86
    groupPoints
×
87
  );
×
88

×
89
  const viewQuery = useMemo(() => {
×
90
    const mergedFilter = mergeFilter(filter, viewGroupQuery?.filter);
×
91
    return {
×
92
      filter: mergedFilter as IFilter,
×
93
      orderBy: sort?.sortObjs as IGetRecordsRo['orderBy'],
×
94
      groupBy: group as IGetRecordsRo['groupBy'],
×
95
    };
×
96
  }, [filter, sort?.sortObjs, group, viewGroupQuery]);
×
97

×
98
  const { onVisibleRegionChanged, recordMap } = useGridAsyncRecords(
×
99
    ssrRecords,
×
100
    undefined,
×
101
    viewQuery
×
102
  );
×
103

×
104
  useClickAway(container, () => {
×
105
    gridRef.current?.resetState();
×
106
  });
×
107

×
108
  useEffect(() => {
×
109
    const recordIds = Object.keys(recordMap)
×
110
      .sort((a, b) => Number(a) - Number(b))
×
111
      .map((key) => recordMap[key]?.id)
×
112
      .filter(Boolean);
×
113
    expandRecordRef.current?.updateRecordIds?.(recordIds);
×
114
  }, [expandRecordRef, recordMap]);
×
115

×
116
  const onRowExpandInner = (rowIndex: number) => {
×
117
    const recordId = recordMap[rowIndex]?.id;
×
118
    if (!recordId) {
×
119
      return;
×
120
    }
×
121
    router.push(
×
122
      {
×
123
        pathname: router.pathname,
×
124
        query: { ...router.query, recordId },
×
125
      },
×
126
      undefined,
×
127
      {
×
128
        shallow: true,
×
129
      }
×
130
    );
×
131
  };
×
132

×
133
  const rowHeightLevel = useMemo(() => {
×
134
    if (view == null) return RowHeightLevel.Short;
×
135
    return (view.options as IGridViewOptions)?.rowHeight || RowHeightLevel.Short;
×
136
  }, [view]);
×
137

×
138
  const onSelectionChanged = useCallback(
×
139
    (selection: CombinedSelection) => {
×
140
      setSelection(selection);
×
141
    },
×
142
    [setSelection]
×
143
  );
×
144

×
145
  const rowControls = useMemo(
×
146
    () => [
×
147
      {
×
148
        type: RowControlType.Checkbox,
×
149
        icon: RowControlType.Checkbox,
×
150
      },
×
151
      {
×
152
        type: RowControlType.Expand,
×
153
        icon: RowControlType.Expand,
×
154
      },
×
155
    ],
×
156
    []
×
157
  );
×
158

×
159
  const getCellContent = useCallback<(cell: ICellItem) => ICell>(
×
160
    (cell) => {
×
161
      const [colIndex, rowIndex] = cell;
×
162
      const record = recordMap[rowIndex];
×
163
      if (record !== undefined) {
×
164
        const fieldId = columns[colIndex]?.id;
×
165
        if (!fieldId) return { type: CellType.Loading };
×
166
        return cellValue2GridDisplay(record, colIndex);
×
167
      }
×
168
      return { type: CellType.Loading };
×
169
    },
×
170
    [recordMap, columns, cellValue2GridDisplay]
×
171
  );
×
172

×
173
  const onCopy = useCallback(
×
174
    async (selection: CombinedSelection) => {
×
175
      const allowCopy = view?.shareMeta?.allowCopy;
×
176
      if (!allowCopy) {
×
177
        toast({ title: "Sorry, the table's owner has disabled copying" });
×
178
        return;
×
179
      }
×
180
      const toaster = toast({
×
181
        title: 'Copying...',
×
182
      });
×
183
      await copyMethod(selection);
×
184
      toaster.update({ id: toaster.id, title: 'Copied success!' });
×
185
    },
×
186
    [copyMethod, view?.shareMeta?.allowCopy, toast]
×
187
  );
×
188

×
189
  const onColumnStatisticClick = useCallback(
×
190
    (colIndex: number, bounds: IRectangle) => {
×
191
      const { x, y, width, height } = bounds;
×
192
      const fieldId = columns[colIndex].id;
×
193
      openStatisticMenu({ fieldId, position: { x, y, width, height } });
×
194
    },
×
195
    [columns, openStatisticMenu]
×
196
  );
×
197

×
198
  return (
×
199
    <div ref={container} className="relative size-full overflow-hidden">
×
200
      {prepare ? (
×
201
        <>
×
202
          <Grid
×
203
            ref={gridRef}
×
204
            theme={theme}
×
205
            draggable={DraggableType.Column}
×
206
            isTouchDevice={isTouchDevice}
×
207
            rowCount={realRowCount}
×
208
            rowHeight={GIRD_ROW_HEIGHT_DEFINITIONS[rowHeightLevel]}
×
209
            columnStatistics={columnStatistics}
×
210
            freezeColumnCount={isTouchDevice ? 0 : 1}
×
211
            columns={columns}
×
212
            customIcons={customIcons}
×
213
            rowControls={rowControls}
×
214
            style={{
×
215
              width: '100%',
×
216
              height: '100%',
×
217
            }}
×
218
            collapsedGroupIds={collapsedGroupIds}
×
219
            groupCollection={groupCollection}
×
220
            groupPoints={groupPoints as unknown as IGroupPoint[]}
×
221
            getCellContent={getCellContent}
×
222
            onVisibleRegionChanged={onVisibleRegionChanged}
×
223
            onSelectionChanged={onSelectionChanged}
×
224
            onCopy={onCopy}
×
225
            onRowExpand={onRowExpandInner}
×
226
            onColumnResize={onColumnResize}
×
227
            onColumnOrdered={onColumnOrdered}
×
228
            onColumnStatisticClick={onColumnStatisticClick}
×
229
            onCollapsedGroupChanged={onCollapsedGroupChanged}
×
230
          />
×
231
          <RowCounter rowCount={realRowCount} className="absolute bottom-3 left-0" />
×
232
        </>
×
233
      ) : (
×
234
        <div className="flex w-full items-center space-x-4">
×
235
          <div className="w-full space-y-3 px-2">
×
236
            <Skeleton className="h-6 w-full" />
×
237
            <Skeleton className="h-6 w-full" />
×
238
            <Skeleton className="h-6 w-full" />
×
239
          </div>
×
240
        </div>
×
241
      )}
×
242
      <StatisticMenu />
×
243
      <ExpandRecordContainer ref={expandRecordRef} recordServerData={ssrRecord} />
×
244
    </div>
×
245
  );
×
246
};
×
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