• 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/Editor.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, Dialog, DialogContent, DialogTrigger, useToast } from '@teable/ui-lib';
×
6
import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
×
7
import { AnchorProvider } from '../../../context';
×
8
import { useTranslation } from '../../../context/app/i18n';
×
9
import { ExpandRecorder } from '../../expand-record';
×
10
import type { ILinkEditorMainRef } from './EditorMain';
×
11
import { LinkEditorMain } from './EditorMain';
×
12
import { LinkListType } from './interface';
×
13
import { LinkCard } from './LinkCard';
×
14
import type { ILinkListRef } from './LinkList';
×
15
import { LinkList } from './LinkList';
×
16

×
17
interface ILinkEditorProps {
×
18
  options: ILinkFieldOptions;
×
19
  fieldId: string;
×
20
  recordId?: string;
×
21
  readonly?: boolean;
×
22
  className?: string;
×
23
  cellValue?: ILinkCellValue | ILinkCellValue[];
×
24
  displayType?: LinkDisplayType;
×
25
  onChange?: (value?: ILinkCellValue | ILinkCellValue[]) => void;
×
26
}
×
27

×
28
export enum LinkDisplayType {
×
29
  Grid = 'grid',
×
30
  List = 'list',
×
31
}
×
32

×
33
export const LinkEditor = (props: ILinkEditorProps) => {
×
34
  const {
×
35
    fieldId,
×
36
    recordId,
×
37
    cellValue,
×
38
    options,
×
39
    onChange,
×
40
    readonly,
×
41
    className,
×
42
    displayType = LinkDisplayType.Grid,
×
43
  } = props;
×
44
  const { toast } = useToast();
×
45
  const listRef = useRef<ILinkListRef>(null);
×
46
  const linkEditorMainRef = useRef<ILinkEditorMainRef>(null);
×
47
  const [isEditing, setEditing] = useState<boolean>(false);
×
48
  const [values, setValues] = useState<ILinkCellValue[]>();
×
49
  const [expandRecordId, setExpandRecordId] = useState<string>();
×
50
  const { t } = useTranslation();
×
51

×
52
  const { foreignTableId, relationship } = options;
×
53
  const isMultiple = isMultiValueLink(relationship);
×
54
  const cvArray = Array.isArray(cellValue) || !cellValue ? cellValue : [cellValue];
×
55
  const recordIds = cvArray?.map((cv) => cv.id);
×
56
  const selectedRowCount = recordIds?.length ?? 0;
×
57

×
58
  const recordQuery = useMemo((): IGetRecordsRo => {
×
59
    return {
×
60
      filterLinkCellSelected: recordId ? [fieldId, recordId] : fieldId,
×
61
    };
×
62
  }, [fieldId, recordId]);
×
63

×
64
  useEffect(() => {
×
65
    if (cellValue == null) return setValues(cellValue);
×
66
    setValues(Array.isArray(cellValue) ? cellValue : [cellValue]);
×
67
  }, [cellValue]);
×
68

×
69
  const updateExpandRecordId = (recordId?: string) => {
×
70
    if (recordId) {
×
71
      const existed = document.getElementById(`${foreignTableId}-${recordId}`);
×
72
      if (existed) {
×
73
        toast({ description: 'This record is already open.' });
×
74
        return;
×
75
      }
×
76
    }
×
77
    setExpandRecordId(recordId);
×
78
  };
×
79

×
80
  const onRecordExpand = (recordId: string) => {
×
81
    updateExpandRecordId(recordId);
×
82
  };
×
83

×
84
  const onRecordDelete = (recordId: string) => {
×
85
    onChange?.(
×
86
      isMultiple ? (cellValue as ILinkCellValue[])?.filter((cv) => cv.id !== recordId) : undefined
×
87
    );
×
88
  };
×
89

×
90
  const onRecordListChange = useCallback((value?: ILinkCellValue[]) => {
×
91
    setValues(value);
×
92
  }, []);
×
93

×
94
  const onOpenChange = (open: boolean) => {
×
95
    if (open) return setEditing?.(true);
×
96
    return linkEditorMainRef.current?.onReset();
×
97
  };
×
98

×
99
  const onExpandRecord = (recordId: string) => {
×
100
    setExpandRecordId(recordId);
×
101
  };
×
102

×
103
  const onConfirm = () => {
×
104
    if (values == null) return onChange?.(undefined);
×
105
    onChange?.(isMultiple ? values : values[0]);
×
106
  };
×
107

×
108
  return (
×
109
    <div className="space-y-3">
×
110
      {Boolean(selectedRowCount) &&
×
111
        (displayType === LinkDisplayType.Grid ? (
×
112
          <div className="relative h-40 w-full overflow-hidden rounded-md border">
×
113
            <AnchorProvider tableId={foreignTableId}>
×
114
              <LinkList
×
115
                ref={listRef}
×
116
                type={LinkListType.Selected}
×
117
                rowCount={selectedRowCount}
×
118
                readonly={readonly}
×
119
                cellValue={cellValue}
×
120
                isMultiple={isMultiple}
×
121
                recordQuery={recordQuery}
×
122
                onChange={onRecordListChange}
×
123
                onExpand={onRecordExpand}
×
124
              />
×
125
            </AnchorProvider>
×
126
          </div>
×
127
        ) : (
×
128
          cvArray?.map(({ id, title }) => (
×
129
            <LinkCard
×
130
              key={id}
×
131
              title={title}
×
132
              readonly={readonly}
×
133
              onClick={() => onRecordExpand(id)}
×
134
              onDelete={() => onRecordDelete(id)}
×
135
            />
×
136
          ))
×
137
        ))}
×
138
      {!readonly && (
×
139
        <>
×
140
          <div className="flex justify-between">
×
141
            <Dialog open={isEditing} onOpenChange={onOpenChange}>
×
142
              <DialogTrigger asChild>
×
143
                <Button variant="outline" size={'sm'} className={className}>
×
144
                  <Plus />
×
145
                  {t('editor.link.selectRecord')}
×
146
                </Button>
×
147
              </DialogTrigger>
×
148
              <DialogContent className="flex h-[520px] max-w-4xl flex-col">
×
149
                <LinkEditorMain
×
150
                  {...props}
×
151
                  ref={linkEditorMainRef}
×
152
                  isEditing={isEditing}
×
153
                  setEditing={setEditing}
×
154
                  onExpandRecord={onExpandRecord}
×
155
                />
×
156
              </DialogContent>
×
157
            </Dialog>
×
158
            {Boolean(selectedRowCount) && displayType === LinkDisplayType.Grid && (
×
159
              <Button size={'sm'} onClick={onConfirm}>
×
160
                {t('common.confirm')}
×
161
              </Button>
×
162
            )}
×
163
          </div>
×
164
          <ExpandRecorder
×
165
            tableId={foreignTableId}
×
166
            recordId={expandRecordId}
×
167
            recordIds={recordIds}
×
168
            onUpdateRecordIdCallback={updateExpandRecordId}
×
169
            onClose={() => updateExpandRecordId(undefined)}
×
170
          />
×
171
        </>
×
172
      )}
×
173
    </div>
×
174
  );
×
175
};
×
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