• 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/EditorMain.tsx
NEW
1
import type { ILinkCellValue, ILinkFieldOptions } from '@teable/core';
×
2
import { isMultiValueLink } from '@teable/core';
×
3
import { Plus } from '@teable/icons';
×
NEW
4
import type { IGetRecordsRo } from '@teable/openapi';
×
5
import { Button, Input, Tabs, TabsList, TabsTrigger } from '@teable/ui-lib';
×
6
import {
×
7
  forwardRef,
×
8
  useCallback,
×
9
  useEffect,
×
10
  useImperativeHandle,
×
11
  useMemo,
×
12
  useRef,
×
13
  useState,
×
14
} from 'react';
×
15
import type { ForwardRefRenderFunction } from 'react';
×
16
import { AnchorProvider } from '../../../context';
×
17
import { useTranslation } from '../../../context/app/i18n';
×
18
import { useBase, useTable } from '../../../hooks';
×
19
import { Table } from '../../../model';
×
20
import { LinkListType } from './interface';
×
21
import type { ILinkListRef } from './LinkList';
×
22
import { LinkList } from './LinkList';
×
23

×
24
export interface ILinkEditorMainProps {
×
25
  fieldId: string;
×
26
  recordId?: string;
×
27
  options: ILinkFieldOptions;
×
28
  cellValue?: ILinkCellValue | ILinkCellValue[];
×
29
  isEditing?: boolean;
×
30
  setEditing?: (isEditing: boolean) => void;
×
31
  onChange?: (value?: ILinkCellValue | ILinkCellValue[]) => void;
×
32
  onExpandRecord?: (recordId: string) => void;
×
33
}
×
34

×
35
export interface ILinkEditorMainRef {
×
36
  onReset: () => void;
×
37
}
×
38

×
39
const LinkEditorInnerBase: ForwardRefRenderFunction<ILinkEditorMainRef, ILinkEditorMainProps> = (
×
40
  props,
×
41
  forwardRef
×
42
) => {
×
43
  const { recordId, fieldId, options, cellValue, isEditing, setEditing, onChange, onExpandRecord } =
×
44
    props;
×
45

×
46
  useImperativeHandle(forwardRef, () => ({
×
47
    onReset,
×
48
  }));
×
49

×
50
  const base = useBase();
×
51
  const table = useTable();
×
52
  const { t } = useTranslation();
×
53

×
54
  const listRef = useRef<ILinkListRef>(null);
×
55
  const [rowCount, setRowCount] = useState<number>(0);
×
56
  const [values, setValues] = useState<ILinkCellValue[]>();
×
57
  const [listType, setListType] = useState<LinkListType>(LinkListType.Unselected);
×
58

×
59
  const baseId = base.id;
×
60
  const tableId = table?.id;
×
61
  const isMultiple = isMultiValueLink(options.relationship);
×
62

×
63
  const recordQuery = useMemo((): IGetRecordsRo => {
×
64
    if (listType === LinkListType.Selected) {
×
65
      return {
×
66
        filterLinkCellSelected: recordId ? [fieldId, recordId] : fieldId,
×
67
      };
×
68
    }
×
69
    return {
×
70
      filterLinkCellCandidate: recordId ? [fieldId, recordId] : fieldId,
×
71
    };
×
72
  }, [fieldId, recordId, listType]);
×
73

×
74
  useEffect(() => {
×
75
    if (!isEditing) return;
×
76
    listRef.current?.onForceUpdate();
×
77
    if (cellValue == null) return setValues(cellValue);
×
78
    setValues(Array.isArray(cellValue) ? cellValue : [cellValue]);
×
79
  }, [cellValue, isEditing]);
×
80

×
81
  useEffect(() => {
×
82
    if (baseId == null || tableId == null) return;
×
83

×
84
    Table.getRowCount(tableId, recordQuery).then((res) => {
×
85
      setRowCount(res.data.rowCount);
×
86
    });
×
87
  }, [tableId, baseId, recordQuery]);
×
88

×
89
  const onViewShown = (type: LinkListType) => {
×
90
    if (type === listType) return;
×
91
    listRef.current?.onReset();
×
92
    setListType(type);
×
93
  };
×
94

×
95
  const onAppendRecord = async () => {
×
96
    if (baseId == null || table == null || tableId == null) return;
×
97

×
98
    const res = await table.createRecord({});
×
99
    const record = res.data.records[0];
×
100

×
101
    if (record != null) {
×
102
      onExpandRecord?.(record.id);
×
103
    }
×
104

×
105
    Table.getRowCount(tableId, recordQuery).then((res) => {
×
106
      const rowCount = res.data.rowCount;
×
107
      setRowCount(() => rowCount);
×
108
      listRef.current?.scrollToItem([0, rowCount - 1]);
×
109
    });
×
110
  };
×
111

×
112
  const onReset = () => {
×
113
    setValues(undefined);
×
114
    setEditing?.(false);
×
115
    setListType(LinkListType.Unselected);
×
116
    listRef.current?.onReset();
×
117
  };
×
118

×
119
  const onListChange = useCallback((value?: ILinkCellValue[]) => {
×
120
    setValues(value);
×
121
  }, []);
×
122

×
123
  const onConfirm = () => {
×
124
    onReset();
×
125
    if (values == null) return onChange?.(undefined);
×
126
    onChange?.(isMultiple ? values : values[0]);
×
127
  };
×
128

×
129
  return (
×
130
    <>
×
131
      <div className="text-lg">{t('editor.link.placeholder')}</div>
×
132
      <div className="flex justify-between">
×
133
        <Input className="flex-1" placeholder={t('editor.link.searchPlaceholder')} disabled />
×
134
        <div className="ml-4">
×
135
          <Tabs defaultValue="unselected" orientation="horizontal" className="flex gap-4">
×
136
            <TabsList className="">
×
137
              <TabsTrigger
×
138
                className="px-4"
×
139
                value="unselected"
×
140
                onClick={() => onViewShown(LinkListType.Unselected)}
×
141
              >
×
142
                {t('editor.link.unselected')}
×
143
              </TabsTrigger>
×
144
              <TabsTrigger
×
145
                className="px-4"
×
146
                value="selected"
×
147
                onClick={() => onViewShown(LinkListType.Selected)}
×
148
              >
×
149
                {t('editor.link.selected')}
×
150
              </TabsTrigger>
×
151
            </TabsList>
×
152
          </Tabs>
×
153
        </div>
×
154
      </div>
×
155
      <div className="relative w-full flex-1 overflow-hidden rounded-md border">
×
156
        <LinkList
×
157
          ref={listRef}
×
158
          type={listType}
×
159
          rowCount={rowCount}
×
160
          cellValue={cellValue}
×
161
          isMultiple={isMultiple}
×
162
          recordQuery={recordQuery}
×
163
          onChange={onListChange}
×
164
        />
×
165
      </div>
×
166
      <div className="flex justify-between">
×
167
        <Button variant="ghost" onClick={onAppendRecord}>
×
168
          <Plus className="size-4" />
×
169
          {t('editor.link.create')}
×
170
        </Button>
×
171
        <div>
×
172
          <Button variant="outline" onClick={onReset}>
×
173
            {t('common.cancel')}
×
174
          </Button>
×
175
          <Button className="ml-4" onClick={onConfirm}>
×
176
            {t('common.confirm')}
×
177
          </Button>
×
178
        </div>
×
179
      </div>
×
180
    </>
×
181
  );
×
182
};
×
183

×
184
const LinkEditorInner = forwardRef(LinkEditorInnerBase);
×
185

×
186
const LinkEditorMainBase: ForwardRefRenderFunction<ILinkEditorMainRef, ILinkEditorMainProps> = (
×
187
  props,
×
188
  forwardRef
×
189
) => {
×
190
  const { options } = props;
×
191
  const tableId = options.foreignTableId;
×
192

×
193
  return (
×
194
    <AnchorProvider tableId={tableId}>
×
195
      <LinkEditorInner ref={forwardRef} {...props} />
×
196
    </AnchorProvider>
×
197
  );
×
198
};
×
199

×
200
export const LinkEditorMain = forwardRef(LinkEditorMainBase);
×
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