• 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/editor/link/LinkList.tsx
NEW
1
import type { ILinkCellValue } from '@teable/core';
×
NEW
2
import type { IGetRecordsRo } from '@teable/openapi';
×
3
import { uniqueId } from 'lodash';
×
4
import type { ForwardRefRenderFunction } from 'react';
×
5
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
×
6
import type { ICell, ICellItem, IGridRef, IRectangle } from '../../grid';
×
7
import {
×
8
  CombinedSelection,
×
9
  Grid,
×
10
  CellType,
×
11
  RegionType,
×
12
  DraggableType,
×
13
  SelectableType,
×
14
  SelectionRegionType,
×
15
  emptySelection,
×
16
  RowControlType,
×
17
} from '../../grid';
×
18
import {
×
19
  GridTooltip,
×
20
  useGridIcons,
×
21
  useGridTheme,
×
22
  useGridColumns,
×
23
  useGridAsyncRecords,
×
24
  useGridTooltipStore,
×
25
} from '../../grid-enhancements';
×
26
import { LinkListType } from './interface';
×
27

×
28
interface ILinkListProps {
×
29
  type: LinkListType;
×
30
  rowCount: number;
×
31
  readonly?: boolean;
×
32
  isMultiple?: boolean;
×
33
  recordQuery?: IGetRecordsRo;
×
34
  cellValue?: ILinkCellValue | ILinkCellValue[];
×
35
  onChange?: (value?: ILinkCellValue[]) => void;
×
36
  onExpand?: (recordId: string) => void;
×
37
}
×
38

×
39
export interface ILinkListRef {
×
40
  onReset: () => void;
×
41
  onForceUpdate: () => void;
×
42
  setSelection: (selection: CombinedSelection) => void;
×
43
  scrollToItem: (position: [columnIndex: number, rowIndex: number]) => void;
×
44
}
×
45

×
46
const LinkListBase: ForwardRefRenderFunction<ILinkListRef, ILinkListProps> = (
×
47
  props,
×
48
  forwardRef
×
49
) => {
×
50
  const { readonly, type, rowCount, cellValue, recordQuery, isMultiple, onChange, onExpand } =
×
51
    props;
×
52

×
53
  useImperativeHandle(forwardRef, () => ({
×
54
    onReset,
×
55
    onForceUpdate,
×
56
    setSelection: (selection: CombinedSelection) => {
×
57
      gridRef.current?.setSelection(selection);
×
58
    },
×
59
    scrollToItem: (position: [columnIndex: number, rowIndex: number]) => {
×
60
      gridRef.current?.scrollToItem(position);
×
61
    },
×
62
  }));
×
63

×
64
  const theme = useGridTheme();
×
65
  const customIcons = useGridIcons();
×
66
  const { openTooltip, closeTooltip } = useGridTooltipStore();
×
67
  const { columns, cellValue2GridDisplay } = useGridColumns(false);
×
68

×
69
  const gridRef = useRef<IGridRef>(null);
×
70
  const rowCountRef = useRef<number>(rowCount);
×
71
  rowCountRef.current = rowCount;
×
72
  const isSelectedType = type === LinkListType.Selected;
×
73
  const isExpandEnable = Boolean(onExpand);
×
74

×
75
  const { recordMap, onReset, onForceUpdate, onVisibleRegionChanged } = useGridAsyncRecords(
×
76
    undefined,
×
77
    recordQuery
×
78
  );
×
79

×
80
  const componentId = useMemo(() => uniqueId('link-editor-'), []);
×
81

×
82
  const rowControls = useMemo(() => {
×
83
    const controls = [];
×
84

×
85
    if (!readonly) {
×
86
      controls.push({
×
87
        type: RowControlType.Checkbox,
×
88
        icon: RowControlType.Checkbox,
×
89
      });
×
90
    }
×
91

×
92
    if (isExpandEnable) {
×
93
      controls.push({
×
94
        type: RowControlType.Expand,
×
95
        icon: RowControlType.Expand,
×
96
      });
×
97
    }
×
98
    return controls;
×
99
  }, [isExpandEnable, readonly]);
×
100

×
101
  useEffect(() => {
×
102
    if (!rowCount) return;
×
103
    gridRef.current?.setSelection(
×
104
      isSelectedType
×
105
        ? new CombinedSelection(SelectionRegionType.Rows, [[0, rowCount - 1]])
×
106
        : emptySelection
×
107
    );
×
108
  }, [rowCount, isSelectedType]);
×
109

×
110
  const onItemHovered = (type: RegionType, bounds: IRectangle, cellItem: ICellItem) => {
×
111
    const [columnIndex] = cellItem;
×
112
    const { description } = columns[columnIndex] ?? {};
×
113

×
114
    closeTooltip();
×
115

×
116
    if (type === RegionType.ColumnDescription && description) {
×
117
      openTooltip({
×
118
        id: componentId,
×
119
        text: description,
×
120
        position: bounds,
×
121
      });
×
122
    }
×
123
  };
×
124

×
125
  const getCellContent = useCallback<(cell: ICellItem) => ICell>(
×
126
    (cell) => {
×
127
      const [colIndex, rowIndex] = cell;
×
128
      const record = recordMap[rowIndex];
×
129
      if (record !== undefined) {
×
130
        const fieldId = columns[colIndex]?.id;
×
131
        if (!fieldId) return { type: CellType.Loading };
×
132
        return cellValue2GridDisplay(record, colIndex);
×
133
      }
×
134
      return { type: CellType.Loading };
×
135
    },
×
136
    [recordMap, columns, cellValue2GridDisplay]
×
137
  );
×
138

×
139
  // eslint-disable-next-line sonarjs/cognitive-complexity
×
140
  const onSelectionChanged = (selection: CombinedSelection) => {
×
141
    const { type } = selection;
×
142

×
143
    if (type === SelectionRegionType.None) {
×
144
      if (isSelectedType) {
×
145
        return onChange?.(undefined);
×
146
      }
×
147
      return cellValue
×
148
        ? onChange?.(Array.isArray(cellValue) ? cellValue : [cellValue])
×
149
        : onChange?.(cellValue);
×
150
    }
×
151

×
152
    if (type !== SelectionRegionType.Rows) return;
×
153

×
154
    let loadingInProgress = false;
×
155
    const rowIndexList = selection.flatten();
×
156

×
157
    if (isSelectedType && rowIndexList.length === rowCount) return;
×
158

×
159
    const newValues = rowIndexList
×
160
      .map((rowIndex) => {
×
161
        const record = recordMap[rowIndex];
×
162
        if (record == null) {
×
163
          loadingInProgress = true;
×
164
        }
×
165
        const id = record?.id;
×
166
        const title = record?.name ?? 'Untitled';
×
167
        return { id, title };
×
168
      })
×
169
      .filter((r) => r.id);
×
170

×
171
    if (loadingInProgress) return;
×
172

×
173
    if (isSelectedType) {
×
174
      return onChange?.(newValues);
×
175
    }
×
176

×
177
    const cv = cellValue == null ? null : Array.isArray(cellValue) ? cellValue : [cellValue];
×
178
    return onChange?.(isMultiple && cv ? [...cv, ...newValues] : newValues);
×
179
  };
×
180

×
181
  const onExpandInner = (rowIndex: number) => {
×
182
    const record = recordMap[rowIndex];
×
183
    if (record == null) return;
×
184
    onExpand?.(record.id);
×
185
  };
×
186

×
187
  return (
×
188
    <>
×
189
      <Grid
×
190
        ref={gridRef}
×
191
        style={{
×
192
          width: '100%',
×
193
          height: '100%',
×
194
        }}
×
195
        scrollBufferX={0}
×
196
        scrollBufferY={0}
×
197
        theme={theme}
×
198
        columns={columns}
×
199
        freezeColumnCount={0}
×
200
        rowCount={isSelectedType && !cellValue ? 0 : rowCount ?? 0}
×
201
        rowIndexVisible={false}
×
202
        customIcons={customIcons}
×
203
        rowControls={rowControls}
×
204
        draggable={DraggableType.None}
×
205
        selectable={readonly ? SelectableType.None : SelectableType.Row}
×
206
        isMultiSelectionEnable={isMultiple}
×
207
        onItemHovered={onItemHovered}
×
208
        getCellContent={getCellContent}
×
209
        onSelectionChanged={onSelectionChanged}
×
210
        onVisibleRegionChanged={onVisibleRegionChanged}
×
211
        onRowExpand={isExpandEnable ? onExpandInner : undefined}
×
212
      />
×
213
      <GridTooltip id={componentId} />
×
214
    </>
×
215
  );
×
216
};
×
217

×
218
export const LinkList = forwardRef(LinkListBase);
×
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