Skip to content

Commit 15e0cd4

Browse files
authored
fix(YfmTable): fix runtime error in table cell nodeView (#274)
1 parent 28c6049 commit 15e0cd4

File tree

1 file changed

+54
-49
lines changed

1 file changed

+54
-49
lines changed

src/extensions/yfm/YfmTable/plugins/YfmTableControls/yfmTableCellView.tsx

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,18 @@ import {
99
TrashBin,
1010
Xmark,
1111
} from '@gravity-ui/icons';
12-
import {Button, DropdownMenu, Icon} from '@gravity-ui/uikit';
13-
import {NodeWithPos, findChildren, findParentNodeClosestToPos} from 'prosemirror-utils';
14-
import {EditorView, NodeView, NodeViewConstructor} from 'prosemirror-view';
15-
import {createPortal} from 'react-dom';
12+
import {Button, DropdownMenu, Icon, Portal} from '@gravity-ui/uikit';
13+
import type {Node} from 'prosemirror-model';
14+
import {NodeWithPos, findParentNodeClosestToPos} from 'prosemirror-utils';
15+
import type {EditorView, NodeView, NodeViewConstructor} from 'prosemirror-view';
1616

1717
import {cn} from '../../../../../classname';
1818
import {bindActions} from '../../../../../core';
1919
import {i18n} from '../../../../../i18n/yfm-table';
2020
import {ErrorLoggerBoundary} from '../../../../../react-utils/ErrorBoundary';
21-
import {
22-
getTableDimensions,
23-
isTableCellNode,
24-
isTableNode,
25-
isTableRowNode,
26-
} from '../../../../../table-utils';
21+
import {getTableDimensions, isTableNode} from '../../../../../table-utils';
22+
import {getChildByNode} from '../../../../../utils/node-children';
23+
import {getChildrenOfNode} from '../../../../../utils/nodes';
2724
import {getReactRendererFromState} from '../../../../behavior/ReactRenderer';
2825

2926
import {controlActions} from './actions';
@@ -204,60 +201,53 @@ const Controls: React.FC<Props> = function Controls({
204201
};
205202

206203
export const yfmTableCellView: NodeViewConstructor = (node, view, getPos): NodeView => {
207-
const dom = document.createElement('td');
208-
const contentDOM = document.createElement('div');
209-
const control = document.createElement('span');
210-
control.setAttribute('style', 'width: 0; height: 0; float: left;');
211-
212-
dom.setAttribute('style', 'position: relative; overflow: visible;');
213-
214204
const getParentTable = () =>
215205
findParentNodeClosestToPos(view.state.doc.resolve(getPos()!), isTableNode);
216-
const parentTable = getParentTable();
217206

218-
// @ts-expect-error
219-
if (!parentTable) return {};
220-
221-
const rowNodes = findChildren(parentTable?.node, isTableRowNode);
207+
const parentTable = getParentTable();
222208

223-
const relativeCelPos = getPos()! - parentTable?.pos;
209+
const cellCoords = parentTable ? findCellCoords(parentTable.node, node) : null;
224210

225-
const rowNumber = rowNodes.findIndex(
226-
(_v, i) =>
227-
relativeCelPos > rowNodes[i].pos &&
228-
relativeCelPos < (rowNodes[i + 1]?.pos || Number.MAX_SAFE_INTEGER),
229-
);
211+
// @ts-expect-error
212+
if (!cellCoords) return {};
230213

231-
const parentRow = rowNodes[rowNumber];
214+
const {cellIndex, rowIndex} = cellCoords;
215+
const isFirstRow = rowIndex === 0;
216+
const isFirstColumn = cellIndex === 0;
232217

233-
const columnNumber = findChildren(parentRow.node, isTableCellNode).findIndex((c) => {
234-
return c.pos === relativeCelPos - parentRow.pos - 2;
235-
});
218+
if (!isFirstRow && !isFirstColumn) {
219+
// in this case, we don't need custom nodeView.
220+
// @ts-expect-error
221+
return {};
222+
}
236223

237-
const isFirstInRow = rowNodes.findIndex((r) => r.pos === relativeCelPos - 2) >= 0;
224+
const dom = document.createElement('td');
225+
const contentDOM = document.createElement('div');
226+
const control = document.createElement('span');
227+
control.setAttribute('style', 'width: 0; height: 0; float: left;');
228+
dom.setAttribute('style', 'position: relative; overflow: visible;');
238229

239230
dom.appendChild(contentDOM);
240231
dom.appendChild(control);
241232

242-
const renderItem = getReactRendererFromState(view.state).createItem('yfm-table-cell-view', () =>
243-
createPortal(
244-
<ErrorLoggerBoundary>
245-
<Controls
246-
columnNumber={columnNumber}
247-
rowNumber={rowNumber}
248-
isFirstInRow={isFirstInRow}
249-
isInFirstRow={rowNumber === 0}
250-
getParentTable={getParentTable}
251-
view={view}
252-
/>
253-
</ErrorLoggerBoundary>,
254-
control,
233+
const renderItem = getReactRendererFromState(view.state).createItem(
234+
'yfm-table-cell-view',
235+
() => (
236+
<Portal container={control}>
237+
<ErrorLoggerBoundary>
238+
<Controls
239+
columnNumber={cellIndex}
240+
rowNumber={rowIndex}
241+
isFirstInRow={isFirstColumn}
242+
isInFirstRow={isFirstRow}
243+
getParentTable={getParentTable}
244+
view={view}
245+
/>
246+
</ErrorLoggerBoundary>
247+
</Portal>
255248
),
256249
);
257250

258-
contentDOM.setAttribute('data-row-number', String(rowNumber));
259-
contentDOM.setAttribute('data-col-number', String(columnNumber));
260-
261251
return {
262252
dom,
263253
contentDOM,
@@ -275,3 +265,18 @@ export const yfmTableCellView: NodeViewConstructor = (node, view, getPos): NodeV
275265
},
276266
};
277267
};
268+
269+
function findCellCoords(table: Node, cell: Node) {
270+
if (!table.lastChild) return null;
271+
const rows = getChildrenOfNode(table.lastChild); // children of tBody
272+
for (const row of rows) {
273+
const node = getChildByNode(row.node, cell);
274+
if (node) {
275+
return {
276+
rowIndex: row.index,
277+
cellIndex: node.index,
278+
};
279+
}
280+
}
281+
return null;
282+
}

0 commit comments

Comments
 (0)