@platejs/ai 52.0.16 → 52.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-CyVv31EV.d.ts → index-Dyi5V2wm.d.ts} +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2003 -3
- package/dist/react/index.d.ts +34 -12
- package/dist/react/index.js +69 -16
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,1994 @@
|
|
|
1
1
|
import { a as removeAINodes, i as undoAI, n as BaseAIPlugin, o as removeAIMarks, r as withAIBatch, s as insertAINodes, t as getEditorPrompt } from "./getEditorPrompt-BnVrgUl3.js";
|
|
2
|
-
import { KEYS } from "platejs";
|
|
2
|
+
import { ElementApi, KEYS, NodeApi, PathApi, RangeApi, TextApi, bindFirst, combineTransformMatchOptions, createSlatePlugin, createTSlatePlugin, getEditorPlugin, getPluginTypes, isHotkey } from "platejs";
|
|
3
3
|
import { serializeMd } from "@platejs/markdown";
|
|
4
|
+
import cloneDeep from "lodash/cloneDeep.js";
|
|
4
5
|
|
|
6
|
+
//#region ../table/dist/constants-B6Sm9BNa.js
|
|
7
|
+
const getEmptyCellNode = (editor, { children, header, row } = {}) => {
|
|
8
|
+
header = header ?? (row ? row.children.every((c) => c.type === editor.getType(KEYS.th)) : false);
|
|
9
|
+
return {
|
|
10
|
+
children: children ?? [editor.api.create.block()],
|
|
11
|
+
type: header ? editor.getType(KEYS.th) : editor.getType(KEYS.td)
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const getEmptyRowNode = (editor, { colCount = 1, ...cellOptions } = {}) => {
|
|
15
|
+
const { api } = editor.getPlugin({ key: KEYS.table });
|
|
16
|
+
return {
|
|
17
|
+
children: Array.from({ length: colCount }).fill(colCount).map(() => api.create.tableCell(cellOptions)),
|
|
18
|
+
type: editor.getType(KEYS.tr)
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
const getEmptyTableNode = (editor, { colCount, header, rowCount = 0, ...cellOptions } = {}) => {
|
|
22
|
+
const { api } = editor.getPlugin({ key: KEYS.table });
|
|
23
|
+
return {
|
|
24
|
+
children: Array.from({ length: rowCount }).fill(rowCount).map((_, index) => api.create.tableRow({
|
|
25
|
+
colCount,
|
|
26
|
+
...cellOptions,
|
|
27
|
+
header: header && index === 0
|
|
28
|
+
})),
|
|
29
|
+
type: editor.getType(KEYS.table)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const getCellInNextTableRow = (editor, currentRowPath) => {
|
|
33
|
+
const nextRow = editor.api.node(PathApi.next(currentRowPath));
|
|
34
|
+
if (!nextRow) return;
|
|
35
|
+
const [nextRowNode, nextRowPath] = nextRow;
|
|
36
|
+
const nextCell = nextRowNode?.children?.[0];
|
|
37
|
+
const nextCellPath = nextRowPath.concat(0);
|
|
38
|
+
if (nextCell && nextCellPath) return editor.api.node(nextCellPath);
|
|
39
|
+
};
|
|
40
|
+
const getCellInPreviousTableRow = (editor, currentRowPath) => {
|
|
41
|
+
const prevPath = PathApi.previous(currentRowPath);
|
|
42
|
+
if (!prevPath) return;
|
|
43
|
+
const previousRow = editor.api.node(prevPath);
|
|
44
|
+
if (!previousRow) return;
|
|
45
|
+
const [previousRowNode, previousRowPath] = previousRow;
|
|
46
|
+
const previousCell = previousRowNode?.children?.[previousRowNode.children.length - 1];
|
|
47
|
+
const previousCellPath = previousRowPath.concat(previousRowNode.children.length - 1);
|
|
48
|
+
if (previousCell && previousCellPath) return editor.api.node(previousCellPath);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Returns the colspan attribute of the table cell element.
|
|
52
|
+
*
|
|
53
|
+
* @default 1 if undefined.
|
|
54
|
+
*/
|
|
55
|
+
const getColSpan = (cellElem) => cellElem.colSpan || Number(cellElem.attributes?.colspan) || 1;
|
|
56
|
+
function computeCellIndices(editor, { all, cellNode, tableNode }) {
|
|
57
|
+
const { api, getOptions, setOption } = getEditorPlugin(editor, { key: KEYS.table });
|
|
58
|
+
if (!tableNode) {
|
|
59
|
+
if (!cellNode) return;
|
|
60
|
+
tableNode = editor.api.above({
|
|
61
|
+
at: cellNode,
|
|
62
|
+
match: { type: editor.getType(KEYS.table) }
|
|
63
|
+
})?.[0];
|
|
64
|
+
if (!tableNode) return;
|
|
65
|
+
}
|
|
66
|
+
const { _cellIndices: prevIndices } = getOptions();
|
|
67
|
+
const cellIndices = { ...prevIndices };
|
|
68
|
+
let hasIndicesChanged = false;
|
|
69
|
+
const skipCells = [];
|
|
70
|
+
let targetIndices;
|
|
71
|
+
for (let rowIndex = 0; rowIndex < tableNode.children.length; rowIndex++) {
|
|
72
|
+
const row = tableNode.children[rowIndex];
|
|
73
|
+
let colIndex = 0;
|
|
74
|
+
for (const cellElement of row.children) {
|
|
75
|
+
while (skipCells[rowIndex]?.[colIndex]) colIndex++;
|
|
76
|
+
const currentIndices = {
|
|
77
|
+
col: colIndex,
|
|
78
|
+
row: rowIndex
|
|
79
|
+
};
|
|
80
|
+
const prevIndicesForCell = prevIndices[cellElement.id];
|
|
81
|
+
if (prevIndicesForCell?.col !== currentIndices.col || prevIndicesForCell?.row !== currentIndices.row) hasIndicesChanged = true;
|
|
82
|
+
cellIndices[cellElement.id] = currentIndices;
|
|
83
|
+
if (cellElement.id === cellNode?.id) {
|
|
84
|
+
targetIndices = currentIndices;
|
|
85
|
+
if (!all) break;
|
|
86
|
+
}
|
|
87
|
+
const colSpan = api.table.getColSpan(cellElement);
|
|
88
|
+
const rowSpan = api.table.getRowSpan(cellElement);
|
|
89
|
+
for (let r = 0; r < rowSpan; r++) {
|
|
90
|
+
skipCells[rowIndex + r] = skipCells[rowIndex + r] || [];
|
|
91
|
+
for (let c = 0; c < colSpan; c++) skipCells[rowIndex + r][colIndex + c] = true;
|
|
92
|
+
}
|
|
93
|
+
colIndex += colSpan;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (hasIndicesChanged) setOption("_cellIndices", cellIndices);
|
|
97
|
+
return targetIndices;
|
|
98
|
+
}
|
|
99
|
+
const getCellIndices = (editor, element) => {
|
|
100
|
+
const { getOption } = getEditorPlugin(editor, { key: KEYS.table });
|
|
101
|
+
let indices = getOption("cellIndices", element.id);
|
|
102
|
+
if (!indices) {
|
|
103
|
+
indices = computeCellIndices(editor, { cellNode: element });
|
|
104
|
+
if (!indices) editor.api.debug.warn("No cell indices found for element. Make sure all table cells have an id.", "TABLE_CELL_INDICES");
|
|
105
|
+
}
|
|
106
|
+
return indices ?? {
|
|
107
|
+
col: 0,
|
|
108
|
+
row: 0
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const getCellRowIndexByPath = (cellPath) => {
|
|
112
|
+
const index = cellPath.at(-2);
|
|
113
|
+
if (index === void 0) throw new Error(`can not get rowIndex of path ${cellPath}`);
|
|
114
|
+
return index;
|
|
115
|
+
};
|
|
116
|
+
/** Get td and th types */
|
|
117
|
+
const getCellTypes = (editor) => getPluginTypes(editor, [KEYS.td, KEYS.th]);
|
|
118
|
+
const getNextTableCell = (editor, _currentCell, currentPath, currentRow) => {
|
|
119
|
+
const cell = editor.api.node(PathApi.next(currentPath));
|
|
120
|
+
if (cell) return cell;
|
|
121
|
+
const [, currentRowPath] = currentRow;
|
|
122
|
+
return getCellInNextTableRow(editor, currentRowPath);
|
|
123
|
+
};
|
|
124
|
+
const getPreviousTableCell = (editor, _currentCell, currentPath, currentRow) => {
|
|
125
|
+
const prevPath = PathApi.previous(currentPath);
|
|
126
|
+
if (!prevPath) {
|
|
127
|
+
const [, currentRowPath] = currentRow;
|
|
128
|
+
return getCellInPreviousTableRow(editor, currentRowPath);
|
|
129
|
+
}
|
|
130
|
+
const cell = editor.api.node(prevPath);
|
|
131
|
+
if (cell) return cell;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Returns the rowspan attribute of the table cell element.
|
|
135
|
+
*
|
|
136
|
+
* @default 1 if undefined
|
|
137
|
+
*/
|
|
138
|
+
const getRowSpan = (cellElem) => cellElem.rowSpan || Number(cellElem.attributes?.rowspan) || 1;
|
|
139
|
+
const getTableAbove = (editor, options) => editor.api.block({
|
|
140
|
+
above: true,
|
|
141
|
+
match: { type: editor.getType(KEYS.table) },
|
|
142
|
+
...options
|
|
143
|
+
});
|
|
144
|
+
const getTableCellBorders = (editor, { cellIndices, defaultBorder = { size: 1 }, element }) => {
|
|
145
|
+
const cellPath = editor.api.findPath(element);
|
|
146
|
+
const [rowNode, rowPath] = editor.api.parent(cellPath) ?? [];
|
|
147
|
+
if (!rowNode || !rowPath) return {
|
|
148
|
+
bottom: defaultBorder,
|
|
149
|
+
right: defaultBorder
|
|
150
|
+
};
|
|
151
|
+
const [tableNode] = editor.api.parent(rowPath);
|
|
152
|
+
const { col } = cellIndices ?? getCellIndices(editor, element);
|
|
153
|
+
const isFirstCell = col === 0;
|
|
154
|
+
const isFirstRow = tableNode.children?.[0] === rowNode;
|
|
155
|
+
const getBorder = (dir) => {
|
|
156
|
+
const border = element.borders?.[dir];
|
|
157
|
+
return {
|
|
158
|
+
color: border?.color ?? defaultBorder.color,
|
|
159
|
+
size: border?.size ?? defaultBorder.size,
|
|
160
|
+
style: border?.style ?? defaultBorder.style
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
bottom: getBorder("bottom"),
|
|
165
|
+
left: isFirstCell ? getBorder("left") : void 0,
|
|
166
|
+
right: getBorder("right"),
|
|
167
|
+
top: isFirstRow ? getBorder("top") : void 0
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
/** Get the width of a cell with colSpan support. */
|
|
171
|
+
const getTableCellSize = (editor, { cellIndices, colSizes, element, rowSize }) => {
|
|
172
|
+
const { api } = getEditorPlugin(editor, { key: KEYS.table });
|
|
173
|
+
const path = editor.api.findPath(element);
|
|
174
|
+
if (!rowSize) {
|
|
175
|
+
const [rowElement] = editor.api.parent(path) ?? [];
|
|
176
|
+
if (!rowElement) return {
|
|
177
|
+
minHeight: 0,
|
|
178
|
+
width: 0
|
|
179
|
+
};
|
|
180
|
+
rowSize = rowElement.size;
|
|
181
|
+
}
|
|
182
|
+
if (!colSizes) {
|
|
183
|
+
const [, rowPath] = editor.api.parent(path);
|
|
184
|
+
const [tableNode] = editor.api.parent(rowPath);
|
|
185
|
+
colSizes = getTableOverriddenColSizes(tableNode);
|
|
186
|
+
}
|
|
187
|
+
const colSpan = api.table.getColSpan(element);
|
|
188
|
+
const { col } = cellIndices ?? getCellIndices(editor, element);
|
|
189
|
+
const width = (colSizes ?? []).slice(col, col + colSpan).reduce((total, w) => total + (w || 0), 0);
|
|
190
|
+
return {
|
|
191
|
+
minHeight: rowSize,
|
|
192
|
+
width
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const getTableColumnCount = (tableNode) => {
|
|
196
|
+
if (tableNode.children?.[0]?.children) return tableNode.children[0].children.map((element) => element.colSpan || element?.attributes?.colspan || 1).reduce((total, num) => Number(total) + Number(num));
|
|
197
|
+
return 0;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* If at (default = selection) is in table>tr>td|th, return table, row, and cell
|
|
201
|
+
* node entries.
|
|
202
|
+
*/
|
|
203
|
+
const getTableEntries = (editor, { at = editor.selection } = {}) => {
|
|
204
|
+
if (!at) return;
|
|
205
|
+
const cellEntry = editor.api.node({
|
|
206
|
+
at,
|
|
207
|
+
match: { type: getCellTypes(editor) }
|
|
208
|
+
});
|
|
209
|
+
if (!cellEntry) return;
|
|
210
|
+
const [, cellPath] = cellEntry;
|
|
211
|
+
const rowEntry = editor.api.above({
|
|
212
|
+
at: cellPath,
|
|
213
|
+
match: { type: editor.getType(KEYS.tr) }
|
|
214
|
+
});
|
|
215
|
+
if (!rowEntry) return;
|
|
216
|
+
const [, rowPath] = rowEntry;
|
|
217
|
+
const tableEntry = editor.api.above({
|
|
218
|
+
at: rowPath,
|
|
219
|
+
match: { type: editor.getType(KEYS.table) }
|
|
220
|
+
});
|
|
221
|
+
if (!tableEntry) return;
|
|
222
|
+
return {
|
|
223
|
+
cell: cellEntry,
|
|
224
|
+
row: rowEntry,
|
|
225
|
+
table: tableEntry
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
const getCellIndicesWithSpans = ({ col, row }, endCell) => ({
|
|
229
|
+
col: col + getColSpan(endCell) - 1,
|
|
230
|
+
row: row + getRowSpan(endCell) - 1
|
|
231
|
+
});
|
|
232
|
+
const findCellByIndexes = (editor, table, searchRowIndex, searchColIndex) => {
|
|
233
|
+
return table.children.flatMap((current) => current.children).find((cellNode) => {
|
|
234
|
+
const indices = getCellIndices(editor, cellNode);
|
|
235
|
+
const { col: _startColIndex, row: _startRowIndex } = indices;
|
|
236
|
+
const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(indices, cellNode);
|
|
237
|
+
if (searchColIndex >= _startColIndex && searchColIndex <= _endColIndex && searchRowIndex >= _startRowIndex && searchRowIndex <= _endRowIndex) return true;
|
|
238
|
+
return false;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Get sub table between 2 cell paths. Ensure that the selection is always a
|
|
243
|
+
* valid table grid.
|
|
244
|
+
*/
|
|
245
|
+
const getTableMergeGridByRange = (editor, { at, format }) => {
|
|
246
|
+
const { api, type } = getEditorPlugin(editor, BaseTablePlugin);
|
|
247
|
+
const startCellEntry = editor.api.node({
|
|
248
|
+
at: at.anchor.path,
|
|
249
|
+
match: { type: getCellTypes(editor) }
|
|
250
|
+
});
|
|
251
|
+
const endCellEntry = editor.api.node({
|
|
252
|
+
at: at.focus.path,
|
|
253
|
+
match: { type: getCellTypes(editor) }
|
|
254
|
+
});
|
|
255
|
+
const startCell = startCellEntry[0];
|
|
256
|
+
const endCell = endCellEntry[0];
|
|
257
|
+
const tablePath = at.anchor.path.slice(0, -2);
|
|
258
|
+
const realTable = editor.api.node({
|
|
259
|
+
at: tablePath,
|
|
260
|
+
match: { type }
|
|
261
|
+
})[0];
|
|
262
|
+
const { col: _startColIndex, row: _startRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, startCell), startCell);
|
|
263
|
+
const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, endCell), endCell);
|
|
264
|
+
let startRowIndex = Math.min(_startRowIndex, _endRowIndex);
|
|
265
|
+
let endRowIndex = Math.max(_startRowIndex, _endRowIndex);
|
|
266
|
+
let startColIndex = Math.min(_startColIndex, _endColIndex);
|
|
267
|
+
let endColIndex = Math.max(_startColIndex, _endColIndex);
|
|
268
|
+
const relativeRowIndex = endRowIndex - startRowIndex;
|
|
269
|
+
const relativeColIndex = endColIndex - startColIndex;
|
|
270
|
+
let table = api.create.table({
|
|
271
|
+
children: [],
|
|
272
|
+
colCount: relativeColIndex + 1,
|
|
273
|
+
rowCount: relativeRowIndex + 1
|
|
274
|
+
});
|
|
275
|
+
let cellEntries = [];
|
|
276
|
+
let cellsSet = /* @__PURE__ */ new WeakSet();
|
|
277
|
+
let rowIndex = startRowIndex;
|
|
278
|
+
let colIndex = startColIndex;
|
|
279
|
+
while (true) {
|
|
280
|
+
const cell = findCellByIndexes(editor, realTable, rowIndex, colIndex);
|
|
281
|
+
if (!cell) break;
|
|
282
|
+
const indicies = getCellIndices(editor, cell);
|
|
283
|
+
const { col: cellColWithSpan, row: cellRowWithSpan } = getCellIndicesWithSpans(indicies, cell);
|
|
284
|
+
const { col: cellCol, row: cellRow } = indicies;
|
|
285
|
+
if (cellRow < startRowIndex || cellRowWithSpan > endRowIndex || cellCol < startColIndex || cellColWithSpan > endColIndex) {
|
|
286
|
+
cellsSet = /* @__PURE__ */ new WeakSet();
|
|
287
|
+
cellEntries = [];
|
|
288
|
+
startRowIndex = Math.min(startRowIndex, cellRow);
|
|
289
|
+
endRowIndex = Math.max(endRowIndex, cellRowWithSpan);
|
|
290
|
+
startColIndex = Math.min(startColIndex, cellCol);
|
|
291
|
+
endColIndex = Math.max(endColIndex, cellColWithSpan);
|
|
292
|
+
rowIndex = startRowIndex;
|
|
293
|
+
colIndex = startColIndex;
|
|
294
|
+
const newRelativeRowIndex = endRowIndex - startRowIndex;
|
|
295
|
+
const newRelativeColIndex = endColIndex - startColIndex;
|
|
296
|
+
table = api.create.table({
|
|
297
|
+
children: [],
|
|
298
|
+
colCount: newRelativeColIndex + 1,
|
|
299
|
+
rowCount: newRelativeRowIndex + 1
|
|
300
|
+
});
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!cellsSet.has(cell)) {
|
|
304
|
+
cellsSet.add(cell);
|
|
305
|
+
const rows = table.children[rowIndex - startRowIndex].children;
|
|
306
|
+
rows[colIndex - startColIndex] = cell;
|
|
307
|
+
const cellPath = editor.api.findPath(cell);
|
|
308
|
+
cellEntries.push([cell, cellPath]);
|
|
309
|
+
}
|
|
310
|
+
if (colIndex + 1 <= endColIndex) colIndex += 1;
|
|
311
|
+
else if (rowIndex + 1 <= endRowIndex) {
|
|
312
|
+
colIndex = startColIndex;
|
|
313
|
+
rowIndex += 1;
|
|
314
|
+
} else break;
|
|
315
|
+
}
|
|
316
|
+
const formatType = format || "table";
|
|
317
|
+
if (formatType === "cell") return cellEntries;
|
|
318
|
+
table.children?.forEach((rowEl) => {
|
|
319
|
+
const rowElement = rowEl;
|
|
320
|
+
rowElement.children = rowElement.children?.filter((cellEl) => {
|
|
321
|
+
const cellElement = cellEl;
|
|
322
|
+
return api.table.getCellChildren(cellElement).length > 0;
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
if (formatType === "table") return [[table, tablePath]];
|
|
326
|
+
return {
|
|
327
|
+
cellEntries,
|
|
328
|
+
tableEntries: [[table, tablePath]]
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
/** Get sub table between 2 cell paths. */
|
|
332
|
+
const getTableGridByRange = (editor, { at, format = "table" }) => {
|
|
333
|
+
const { api } = editor.getPlugin({ key: KEYS.table });
|
|
334
|
+
const { disableMerge } = editor.getOptions(BaseTablePlugin);
|
|
335
|
+
if (!disableMerge) return getTableMergeGridByRange(editor, {
|
|
336
|
+
at,
|
|
337
|
+
format
|
|
338
|
+
});
|
|
339
|
+
const startCellPath = at.anchor.path;
|
|
340
|
+
const endCellPath = at.focus.path;
|
|
341
|
+
const _startRowIndex = startCellPath.at(-2);
|
|
342
|
+
const _endRowIndex = endCellPath.at(-2);
|
|
343
|
+
const _startColIndex = startCellPath.at(-1);
|
|
344
|
+
const _endColIndex = endCellPath.at(-1);
|
|
345
|
+
const startRowIndex = Math.min(_startRowIndex, _endRowIndex);
|
|
346
|
+
const endRowIndex = Math.max(_startRowIndex, _endRowIndex);
|
|
347
|
+
const startColIndex = Math.min(_startColIndex, _endColIndex);
|
|
348
|
+
const endColIndex = Math.max(_startColIndex, _endColIndex);
|
|
349
|
+
const tablePath = startCellPath.slice(0, -2);
|
|
350
|
+
const relativeRowIndex = endRowIndex - startRowIndex;
|
|
351
|
+
const relativeColIndex = endColIndex - startColIndex;
|
|
352
|
+
const table = api.create.table({
|
|
353
|
+
children: [],
|
|
354
|
+
colCount: relativeColIndex + 1,
|
|
355
|
+
rowCount: relativeRowIndex + 1
|
|
356
|
+
});
|
|
357
|
+
let rowIndex = startRowIndex;
|
|
358
|
+
let colIndex = startColIndex;
|
|
359
|
+
const cellEntries = [];
|
|
360
|
+
while (true) {
|
|
361
|
+
const cellPath = tablePath.concat([rowIndex, colIndex]);
|
|
362
|
+
const cell = NodeApi.get(editor, cellPath);
|
|
363
|
+
if (!cell) break;
|
|
364
|
+
const rows = table.children[rowIndex - startRowIndex].children;
|
|
365
|
+
rows[colIndex - startColIndex] = cell;
|
|
366
|
+
cellEntries.push([cell, cellPath]);
|
|
367
|
+
if (colIndex + 1 <= endColIndex) colIndex += 1;
|
|
368
|
+
else if (rowIndex + 1 <= endRowIndex) {
|
|
369
|
+
colIndex = startColIndex;
|
|
370
|
+
rowIndex += 1;
|
|
371
|
+
} else break;
|
|
372
|
+
}
|
|
373
|
+
if (format === "cell") return cellEntries;
|
|
374
|
+
return [[table, tablePath]];
|
|
375
|
+
};
|
|
376
|
+
/** Get sub table above anchor and focus. Format: tables or cells. */
|
|
377
|
+
const getTableGridAbove = (editor, { format = "table", ...options } = {}) => {
|
|
378
|
+
const { api } = editor.getPlugin({ key: KEYS.table });
|
|
379
|
+
const edges = editor.api.edgeBlocks({
|
|
380
|
+
match: { type: getCellTypes(editor) },
|
|
381
|
+
...options
|
|
382
|
+
});
|
|
383
|
+
if (edges) {
|
|
384
|
+
const [start, end] = edges;
|
|
385
|
+
if (!PathApi.equals(start[1], end[1])) return getTableGridByRange(editor, {
|
|
386
|
+
at: {
|
|
387
|
+
anchor: {
|
|
388
|
+
offset: 0,
|
|
389
|
+
path: start[1]
|
|
390
|
+
},
|
|
391
|
+
focus: {
|
|
392
|
+
offset: 0,
|
|
393
|
+
path: end[1]
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
format
|
|
397
|
+
});
|
|
398
|
+
if (format === "table") {
|
|
399
|
+
const table = api.create.table({ rowCount: 1 });
|
|
400
|
+
table.children[0].children = [start[0]];
|
|
401
|
+
return [[table, start[1].slice(0, -2)]];
|
|
402
|
+
}
|
|
403
|
+
return [start];
|
|
404
|
+
}
|
|
405
|
+
return [];
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* Returns node.colSizes if it exists, applying overrides, otherwise returns a
|
|
409
|
+
* 0-filled array.
|
|
410
|
+
*/
|
|
411
|
+
const getTableOverriddenColSizes = (tableNode, colSizeOverrides) => {
|
|
412
|
+
const colCount = getTableColumnCount(tableNode);
|
|
413
|
+
return (tableNode.colSizes ? [...tableNode.colSizes] : Array.from({ length: colCount }).fill(0)).map((size, index) => colSizeOverrides?.get?.(index) ?? size);
|
|
414
|
+
};
|
|
415
|
+
const deleteColumnWhenExpanded = (editor, tableEntry) => {
|
|
416
|
+
const [start, end] = RangeApi.edges(editor.selection);
|
|
417
|
+
const firstRow = NodeApi.child(tableEntry[0], 0);
|
|
418
|
+
const lastRow = NodeApi.child(tableEntry[0], tableEntry[0].children.length - 1);
|
|
419
|
+
const firstSelectionRow = editor.api.above({
|
|
420
|
+
at: start,
|
|
421
|
+
match: (n) => n.type === KEYS.tr
|
|
422
|
+
});
|
|
423
|
+
const lastSelectionRow = editor.api.above({
|
|
424
|
+
at: end,
|
|
425
|
+
match: (n) => n.type === KEYS.tr
|
|
426
|
+
});
|
|
427
|
+
if (!firstSelectionRow || !lastSelectionRow) return;
|
|
428
|
+
if (firstRow.id === firstSelectionRow[0].id && lastSelectionRow[0].id === lastRow.id) deleteSelection(editor);
|
|
429
|
+
};
|
|
430
|
+
const deleteSelection = (editor) => {
|
|
431
|
+
const cells = getTableGridAbove(editor, { format: "cell" });
|
|
432
|
+
const pathRefs = [];
|
|
433
|
+
cells.forEach(([_cell, cellPath]) => {
|
|
434
|
+
pathRefs.push(editor.api.pathRef(cellPath));
|
|
435
|
+
});
|
|
436
|
+
pathRefs.forEach((pathRef) => {
|
|
437
|
+
editor.tf.removeNodes({ at: pathRef.unref() });
|
|
438
|
+
});
|
|
439
|
+
};
|
|
440
|
+
const getCellPath = (editor, tableEntry, curRowIndex, curColIndex) => {
|
|
441
|
+
const [tableNode, tablePath] = tableEntry;
|
|
442
|
+
const foundColIndex = tableNode.children[curRowIndex].children.findIndex((c) => {
|
|
443
|
+
const { col: colIndex } = getCellIndices(editor, c);
|
|
444
|
+
return colIndex === curColIndex;
|
|
445
|
+
});
|
|
446
|
+
return tablePath.concat([curRowIndex, foundColIndex]);
|
|
447
|
+
};
|
|
448
|
+
const deleteTableMergeColumn = (editor) => {
|
|
449
|
+
const type = editor.getType(KEYS.table);
|
|
450
|
+
const tableEntry = editor.api.above({ match: { type } });
|
|
451
|
+
if (!tableEntry) return;
|
|
452
|
+
editor.tf.withoutNormalizing(() => {
|
|
453
|
+
const { api } = getEditorPlugin(editor, BaseTablePlugin);
|
|
454
|
+
if (editor.api.isExpanded()) return deleteColumnWhenExpanded(editor, tableEntry);
|
|
455
|
+
const table = tableEntry[0];
|
|
456
|
+
const selectedCellEntry = editor.api.above({ match: { type: getCellTypes(editor) } });
|
|
457
|
+
if (!selectedCellEntry) return;
|
|
458
|
+
const selectedCell = selectedCellEntry[0];
|
|
459
|
+
const { col: deletingColIndex } = getCellIndices(editor, selectedCell);
|
|
460
|
+
const colsDeleteNumber = api.table.getColSpan(selectedCell);
|
|
461
|
+
const endingColIndex = deletingColIndex + colsDeleteNumber - 1;
|
|
462
|
+
const rowNumber = table.children.length;
|
|
463
|
+
const affectedCellsSet = /* @__PURE__ */ new Set();
|
|
464
|
+
for (const rI of Array.from({ length: rowNumber }, (_, i) => i)) for (const cI of Array.from({ length: colsDeleteNumber }, (_, i) => i)) {
|
|
465
|
+
const found = findCellByIndexes(editor, table, rI, deletingColIndex + cI);
|
|
466
|
+
if (found) affectedCellsSet.add(found);
|
|
467
|
+
}
|
|
468
|
+
const affectedCells = Array.from(affectedCellsSet);
|
|
469
|
+
const { squizeColSpanCells } = affectedCells.reduce((acc, cur) => {
|
|
470
|
+
if (!cur) return acc;
|
|
471
|
+
const currentCell = cur;
|
|
472
|
+
const { col: curColIndex } = getCellIndices(editor, currentCell);
|
|
473
|
+
const curColSpan = api.table.getColSpan(currentCell);
|
|
474
|
+
if (curColIndex < deletingColIndex && curColSpan > 1) acc.squizeColSpanCells.push(currentCell);
|
|
475
|
+
else if (curColSpan > 1 && curColIndex + curColSpan - 1 > endingColIndex) acc.squizeColSpanCells.push(currentCell);
|
|
476
|
+
return acc;
|
|
477
|
+
}, { squizeColSpanCells: [] });
|
|
478
|
+
/** Change colSpans */
|
|
479
|
+
squizeColSpanCells.forEach((cur) => {
|
|
480
|
+
const curCell = cur;
|
|
481
|
+
const { col: curColIndex, row: curColRowIndex } = getCellIndices(editor, curCell);
|
|
482
|
+
const curColSpan = api.table.getColSpan(curCell);
|
|
483
|
+
const curCellPath = getCellPath(editor, tableEntry, curColRowIndex, curColIndex);
|
|
484
|
+
const colSpan = curColSpan - (Math.min(curColIndex + curColSpan - 1, endingColIndex) - deletingColIndex + 1);
|
|
485
|
+
const newCell = cloneDeep({
|
|
486
|
+
...curCell,
|
|
487
|
+
colSpan
|
|
488
|
+
});
|
|
489
|
+
if (newCell.attributes?.colspan) newCell.attributes.colspan = colSpan.toString();
|
|
490
|
+
editor.tf.setNodes(newCell, { at: curCellPath });
|
|
491
|
+
});
|
|
492
|
+
const trEntry = editor.api.above({ match: { type: editor.getType(KEYS.tr) } });
|
|
493
|
+
/** Remove cells */
|
|
494
|
+
if (selectedCell && trEntry && tableEntry && trEntry[0].children.length > 1) {
|
|
495
|
+
const [tableNode, tablePath] = tableEntry;
|
|
496
|
+
const paths = [];
|
|
497
|
+
affectedCells.forEach((cur) => {
|
|
498
|
+
const curCell = cur;
|
|
499
|
+
const { col: curColIndex, row: curRowIndex } = getCellIndices(editor, curCell);
|
|
500
|
+
if (!squizeColSpanCells.includes(curCell) && curColIndex >= deletingColIndex && curColIndex <= endingColIndex) {
|
|
501
|
+
const cellPath = getCellPath(editor, tableEntry, curRowIndex, curColIndex);
|
|
502
|
+
if (!paths[curRowIndex]) paths[curRowIndex] = [];
|
|
503
|
+
paths[curRowIndex].push(cellPath);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
paths.forEach((cellPaths) => {
|
|
507
|
+
const pathToDelete = cellPaths[0];
|
|
508
|
+
cellPaths.forEach(() => {
|
|
509
|
+
editor.tf.removeNodes({ at: pathToDelete });
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
const { colSizes } = tableNode;
|
|
513
|
+
if (colSizes) {
|
|
514
|
+
const newColSizes = [...colSizes];
|
|
515
|
+
newColSizes.splice(deletingColIndex, 1);
|
|
516
|
+
editor.tf.setNodes({ colSizes: newColSizes }, { at: tablePath });
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
};
|
|
521
|
+
const deleteRowWhenExpanded = (editor, [table, tablePath]) => {
|
|
522
|
+
const { api } = getEditorPlugin(editor, BaseTablePlugin);
|
|
523
|
+
const columnCount = getTableMergedColumnCount(table);
|
|
524
|
+
const cells = getTableGridAbove(editor, { format: "cell" });
|
|
525
|
+
const firsRowIndex = getCellRowIndexByPath(cells[0][1]);
|
|
526
|
+
if (firsRowIndex === null) return;
|
|
527
|
+
let acrossColumn = 0;
|
|
528
|
+
let lastRowIndex = -1;
|
|
529
|
+
let rowSpanCarry = 0;
|
|
530
|
+
let acrossRow = 0;
|
|
531
|
+
cells.forEach(([cell, cellPath]) => {
|
|
532
|
+
if (cellPath.at(-2) === firsRowIndex) acrossColumn += cell.colSpan ?? 1;
|
|
533
|
+
const currentRowIndex = getCellRowIndexByPath(cellPath);
|
|
534
|
+
if (lastRowIndex !== currentRowIndex) {
|
|
535
|
+
if (rowSpanCarry !== 0) {
|
|
536
|
+
rowSpanCarry--;
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const rowSpan = api.table.getRowSpan(cell);
|
|
540
|
+
rowSpanCarry = rowSpan && rowSpan > 1 ? rowSpan - 1 : 0;
|
|
541
|
+
acrossRow += rowSpan ?? 1;
|
|
542
|
+
}
|
|
543
|
+
lastRowIndex = currentRowIndex;
|
|
544
|
+
});
|
|
545
|
+
if (acrossColumn === columnCount) {
|
|
546
|
+
const pathRefs = [];
|
|
547
|
+
for (let i = firsRowIndex; i < firsRowIndex + acrossRow; i++) {
|
|
548
|
+
const removedPath = tablePath.concat(i);
|
|
549
|
+
pathRefs.push(editor.api.pathRef(removedPath));
|
|
550
|
+
}
|
|
551
|
+
pathRefs.forEach((item) => {
|
|
552
|
+
editor.tf.removeNodes({ at: item.unref() });
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const deleteTableMergeRow = (editor) => {
|
|
557
|
+
const { api, tf, type } = getEditorPlugin(editor, { key: KEYS.table });
|
|
558
|
+
if (editor.api.some({ match: { type } })) {
|
|
559
|
+
const currentTableItem = editor.api.above({ match: { type } });
|
|
560
|
+
if (!currentTableItem) return;
|
|
561
|
+
if (editor.api.isExpanded()) return deleteRowWhenExpanded(editor, currentTableItem);
|
|
562
|
+
const table = currentTableItem[0];
|
|
563
|
+
const selectedCellEntry = editor.api.above({ match: { type: getCellTypes(editor) } });
|
|
564
|
+
if (!selectedCellEntry) return;
|
|
565
|
+
const selectedCell = selectedCellEntry[0];
|
|
566
|
+
const { row: deletingRowIndex } = getCellIndices(editor, selectedCell);
|
|
567
|
+
const rowsDeleteNumber = api.table.getRowSpan(selectedCell);
|
|
568
|
+
const endingRowIndex = deletingRowIndex + rowsDeleteNumber - 1;
|
|
569
|
+
const colNumber = getTableColumnCount(table);
|
|
570
|
+
const affectedCellsSet = /* @__PURE__ */ new Set();
|
|
571
|
+
for (const cI of Array.from({ length: colNumber }, (_, i) => i)) for (const rI of Array.from({ length: rowsDeleteNumber }, (_, i) => i)) {
|
|
572
|
+
const found = findCellByIndexes(editor, table, deletingRowIndex + rI, cI);
|
|
573
|
+
affectedCellsSet.add(found);
|
|
574
|
+
}
|
|
575
|
+
const { moveToNextRowCells, squizeRowSpanCells } = Array.from(affectedCellsSet).reduce((acc, cur) => {
|
|
576
|
+
if (!cur) return acc;
|
|
577
|
+
const currentCell = cur;
|
|
578
|
+
const { row: curRowIndex } = getCellIndices(editor, currentCell);
|
|
579
|
+
const curRowSpan = api.table.getRowSpan(currentCell);
|
|
580
|
+
if (curRowIndex < deletingRowIndex && curRowSpan > 1) acc.squizeRowSpanCells.push(currentCell);
|
|
581
|
+
else if (curRowSpan > 1 && curRowIndex + curRowSpan - 1 > endingRowIndex) acc.moveToNextRowCells.push(currentCell);
|
|
582
|
+
return acc;
|
|
583
|
+
}, {
|
|
584
|
+
moveToNextRowCells: [],
|
|
585
|
+
squizeRowSpanCells: []
|
|
586
|
+
});
|
|
587
|
+
const nextRowIndex = deletingRowIndex + rowsDeleteNumber;
|
|
588
|
+
const nextRow = table.children[nextRowIndex];
|
|
589
|
+
if (nextRow === void 0 && deletingRowIndex === 0) {
|
|
590
|
+
tf.remove.table();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (nextRow) for (let index = 0; index < moveToNextRowCells.length; index++) {
|
|
594
|
+
const curRowCell = moveToNextRowCells[index];
|
|
595
|
+
const { col: curRowCellColIndex, row: curRowCellRowIndex } = getCellIndices(editor, curRowCell);
|
|
596
|
+
const curRowCellRowSpan = api.table.getRowSpan(curRowCell);
|
|
597
|
+
const startingCellIndex = nextRow.children.findIndex((curC) => {
|
|
598
|
+
const { col: curColIndex } = getCellIndices(editor, curC);
|
|
599
|
+
return curColIndex >= curRowCellColIndex;
|
|
600
|
+
});
|
|
601
|
+
if (startingCellIndex === -1) {
|
|
602
|
+
const startingCell$1 = nextRow.children.at(-1);
|
|
603
|
+
const startingCellPath$1 = editor.api.findPath(startingCell$1);
|
|
604
|
+
const tablePath$1 = startingCellPath$1.slice(0, -2);
|
|
605
|
+
const colPath$1 = startingCellPath$1.at(-1) + index + 1;
|
|
606
|
+
const nextRowStartCellPath$1 = [
|
|
607
|
+
...tablePath$1,
|
|
608
|
+
nextRowIndex,
|
|
609
|
+
colPath$1
|
|
610
|
+
];
|
|
611
|
+
const rowSpan$1 = curRowCellRowSpan - (endingRowIndex - curRowCellRowIndex + 1);
|
|
612
|
+
const newCell$1 = cloneDeep({
|
|
613
|
+
...curRowCell,
|
|
614
|
+
rowSpan: rowSpan$1
|
|
615
|
+
});
|
|
616
|
+
if (newCell$1.attributes?.rowspan) newCell$1.attributes.rowspan = rowSpan$1.toString();
|
|
617
|
+
editor.tf.insertNodes(newCell$1, { at: nextRowStartCellPath$1 });
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const startingCell = nextRow.children[startingCellIndex];
|
|
621
|
+
const { col: startingColIndex } = getCellIndices(editor, startingCell);
|
|
622
|
+
let incrementBy = index;
|
|
623
|
+
if (startingColIndex < curRowCellColIndex) incrementBy += 1;
|
|
624
|
+
const startingCellPath = editor.api.findPath(startingCell);
|
|
625
|
+
const tablePath = startingCellPath.slice(0, -2);
|
|
626
|
+
const colPath = startingCellPath.at(-1);
|
|
627
|
+
const nextRowStartCellPath = [
|
|
628
|
+
...tablePath,
|
|
629
|
+
nextRowIndex,
|
|
630
|
+
colPath + incrementBy
|
|
631
|
+
];
|
|
632
|
+
const rowSpan = curRowCellRowSpan - (endingRowIndex - curRowCellRowIndex + 1);
|
|
633
|
+
const newCell = cloneDeep({
|
|
634
|
+
...curRowCell,
|
|
635
|
+
rowSpan
|
|
636
|
+
});
|
|
637
|
+
if (newCell.attributes?.rowspan) newCell.attributes.rowspan = rowSpan.toString();
|
|
638
|
+
editor.tf.insertNodes(newCell, { at: nextRowStartCellPath });
|
|
639
|
+
}
|
|
640
|
+
squizeRowSpanCells.forEach((cur) => {
|
|
641
|
+
const curRowCell = cur;
|
|
642
|
+
const { row: curRowCellRowIndex } = getCellIndices(editor, curRowCell);
|
|
643
|
+
const curRowCellRowSpan = api.table.getRowSpan(curRowCell);
|
|
644
|
+
const curCellPath = editor.api.findPath(curRowCell);
|
|
645
|
+
const rowSpan = curRowCellRowSpan - (Math.min(curRowCellRowIndex + curRowCellRowSpan - 1, endingRowIndex) - deletingRowIndex + 1);
|
|
646
|
+
const newCell = cloneDeep({
|
|
647
|
+
...curRowCell,
|
|
648
|
+
rowSpan
|
|
649
|
+
});
|
|
650
|
+
if (newCell.attributes?.rowspan) newCell.attributes.rowspan = rowSpan.toString();
|
|
651
|
+
editor.tf.setNodes(newCell, { at: curCellPath });
|
|
652
|
+
});
|
|
653
|
+
const rowToDelete = table.children[deletingRowIndex];
|
|
654
|
+
const rowPath = editor.api.findPath(rowToDelete);
|
|
655
|
+
Array.from({ length: rowsDeleteNumber }).forEach(() => {
|
|
656
|
+
editor.tf.removeNodes({ at: rowPath });
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
const getTableMergedColumnCount = (tableNode) => tableNode.children?.[0]?.children?.reduce((prev, cur) => prev + (getColSpan(cur) ?? 1), 0);
|
|
661
|
+
const insertTableMergeColumn = (editor, { at, before, fromCell, header, select: shouldSelect } = {}) => {
|
|
662
|
+
const { api, getOptions, type } = getEditorPlugin(editor, BaseTablePlugin);
|
|
663
|
+
const { initialTableWidth, minColumnWidth } = getOptions();
|
|
664
|
+
if (at && !fromCell) {
|
|
665
|
+
if (NodeApi.get(editor, at)?.type === editor.getType(KEYS.table)) {
|
|
666
|
+
fromCell = NodeApi.lastChild(editor, at.concat([0]))[1];
|
|
667
|
+
at = void 0;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
const cellEntry = fromCell ? editor.api.node({
|
|
671
|
+
at: fromCell,
|
|
672
|
+
match: { type: getCellTypes(editor) }
|
|
673
|
+
}) : editor.api.block({ match: { type: getCellTypes(editor) } });
|
|
674
|
+
if (!cellEntry) return;
|
|
675
|
+
const [, cellPath] = cellEntry;
|
|
676
|
+
const cell = cellEntry[0];
|
|
677
|
+
const tableEntry = editor.api.block({
|
|
678
|
+
above: true,
|
|
679
|
+
at: cellPath,
|
|
680
|
+
match: { type }
|
|
681
|
+
});
|
|
682
|
+
if (!tableEntry) return;
|
|
683
|
+
const [tableNode, tablePath] = tableEntry;
|
|
684
|
+
const { col: cellColIndex } = getCellIndices(editor, cell);
|
|
685
|
+
const cellColSpan = api.table.getColSpan(cell);
|
|
686
|
+
let nextColIndex;
|
|
687
|
+
let checkingColIndex;
|
|
688
|
+
if (PathApi.isPath(at)) {
|
|
689
|
+
nextColIndex = cellColIndex;
|
|
690
|
+
checkingColIndex = cellColIndex - 1;
|
|
691
|
+
} else {
|
|
692
|
+
nextColIndex = before ? cellColIndex : cellColIndex + cellColSpan;
|
|
693
|
+
checkingColIndex = before ? cellColIndex : cellColIndex + cellColSpan - 1;
|
|
694
|
+
}
|
|
695
|
+
const rowNumber = tableNode.children.length;
|
|
696
|
+
const firstCol = nextColIndex <= 0;
|
|
697
|
+
let placementCorrection = before ? 0 : 1;
|
|
698
|
+
if (firstCol) {
|
|
699
|
+
checkingColIndex = 0;
|
|
700
|
+
placementCorrection = 0;
|
|
701
|
+
}
|
|
702
|
+
const affectedCellsSet = /* @__PURE__ */ new Set();
|
|
703
|
+
Array.from({ length: rowNumber }, (_, i) => i).forEach((rI) => {
|
|
704
|
+
const found = findCellByIndexes(editor, tableNode, rI, checkingColIndex);
|
|
705
|
+
if (found) affectedCellsSet.add(found);
|
|
706
|
+
});
|
|
707
|
+
Array.from(affectedCellsSet).forEach((curCell) => {
|
|
708
|
+
const { col: curColIndex, row: curRowIndex } = getCellIndices(editor, curCell);
|
|
709
|
+
const curRowSpan = api.table.getRowSpan(curCell);
|
|
710
|
+
const curColSpan = api.table.getColSpan(curCell);
|
|
711
|
+
const currentCellPath = getCellPath(editor, tableEntry, curRowIndex, curColIndex);
|
|
712
|
+
if (curColIndex + curColSpan - 1 >= nextColIndex && !firstCol && !before) {
|
|
713
|
+
const colSpan = curColSpan + 1;
|
|
714
|
+
const newCell = cloneDeep({
|
|
715
|
+
...curCell,
|
|
716
|
+
colSpan
|
|
717
|
+
});
|
|
718
|
+
if (newCell.attributes?.colspan) newCell.attributes.colspan = colSpan.toString();
|
|
719
|
+
editor.tf.setNodes(newCell, { at: currentCellPath });
|
|
720
|
+
} else {
|
|
721
|
+
const curRowPath = currentCellPath.slice(0, -1);
|
|
722
|
+
const curColPath = currentCellPath.at(-1);
|
|
723
|
+
const placementPath = [...curRowPath, before ? curColPath : curColPath + placementCorrection];
|
|
724
|
+
const rowElement = editor.api.parent(currentCellPath)[0];
|
|
725
|
+
const emptyCell = {
|
|
726
|
+
...api.create.tableCell({
|
|
727
|
+
header,
|
|
728
|
+
row: rowElement
|
|
729
|
+
}),
|
|
730
|
+
colSpan: 1,
|
|
731
|
+
rowSpan: curRowSpan
|
|
732
|
+
};
|
|
733
|
+
editor.tf.insertNodes(emptyCell, {
|
|
734
|
+
at: placementPath,
|
|
735
|
+
select: shouldSelect
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
editor.tf.withoutNormalizing(() => {
|
|
740
|
+
const { colSizes } = tableNode;
|
|
741
|
+
if (colSizes) {
|
|
742
|
+
let newColSizes = [
|
|
743
|
+
...colSizes.slice(0, nextColIndex),
|
|
744
|
+
0,
|
|
745
|
+
...colSizes.slice(nextColIndex)
|
|
746
|
+
];
|
|
747
|
+
if (initialTableWidth) {
|
|
748
|
+
newColSizes[nextColIndex] = colSizes[nextColIndex] ?? colSizes[nextColIndex - 1] ?? initialTableWidth / colSizes.length;
|
|
749
|
+
const oldTotal = colSizes.reduce((a, b) => a + b, 0);
|
|
750
|
+
const newTotal = newColSizes.reduce((a, b) => a + b, 0);
|
|
751
|
+
const maxTotal = Math.max(oldTotal, initialTableWidth);
|
|
752
|
+
if (newTotal > maxTotal) {
|
|
753
|
+
const factor = maxTotal / newTotal;
|
|
754
|
+
newColSizes = newColSizes.map((size) => Math.max(minColumnWidth ?? 0, Math.floor(size * factor)));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
editor.tf.setNodes({ colSizes: newColSizes }, { at: tablePath });
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
};
|
|
761
|
+
const insertTableMergeRow = (editor, { at, before, fromRow, header, select: shouldSelect } = {}) => {
|
|
762
|
+
const { api, type } = getEditorPlugin(editor, BaseTablePlugin);
|
|
763
|
+
if (at && !fromRow) {
|
|
764
|
+
if (NodeApi.get(editor, at)?.type === editor.getType(KEYS.table)) {
|
|
765
|
+
fromRow = NodeApi.lastChild(editor, at)[1];
|
|
766
|
+
at = void 0;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const trEntry = editor.api.block({
|
|
770
|
+
at: fromRow,
|
|
771
|
+
match: { type: editor.getType(KEYS.tr) }
|
|
772
|
+
});
|
|
773
|
+
if (!trEntry) return;
|
|
774
|
+
const [, trPath] = trEntry;
|
|
775
|
+
const tableEntry = editor.api.block({
|
|
776
|
+
above: true,
|
|
777
|
+
at: trPath,
|
|
778
|
+
match: { type }
|
|
779
|
+
});
|
|
780
|
+
if (!tableEntry) return;
|
|
781
|
+
const tableNode = tableEntry[0];
|
|
782
|
+
const cellEntry = editor.api.node({
|
|
783
|
+
at: fromRow,
|
|
784
|
+
match: { type: getCellTypes(editor) }
|
|
785
|
+
});
|
|
786
|
+
if (!cellEntry) return;
|
|
787
|
+
const [cellNode, cellPath] = cellEntry;
|
|
788
|
+
const cellElement = cellNode;
|
|
789
|
+
const cellRowSpan = api.table.getRowSpan(cellElement);
|
|
790
|
+
const { row: cellRowIndex } = getCellIndices(editor, cellElement);
|
|
791
|
+
const rowPath = cellPath.at(-2);
|
|
792
|
+
const tablePath = cellPath.slice(0, -2);
|
|
793
|
+
let nextRowIndex;
|
|
794
|
+
let checkingRowIndex;
|
|
795
|
+
let nextRowPath;
|
|
796
|
+
if (PathApi.isPath(at)) {
|
|
797
|
+
nextRowIndex = at.at(-1);
|
|
798
|
+
checkingRowIndex = cellRowIndex - 1;
|
|
799
|
+
nextRowPath = at;
|
|
800
|
+
} else {
|
|
801
|
+
nextRowIndex = before ? cellRowIndex : cellRowIndex + cellRowSpan;
|
|
802
|
+
checkingRowIndex = before ? cellRowIndex - 1 : cellRowIndex + cellRowSpan - 1;
|
|
803
|
+
nextRowPath = [...tablePath, before ? rowPath : rowPath + cellRowSpan];
|
|
804
|
+
}
|
|
805
|
+
const firstRow = nextRowIndex === 0;
|
|
806
|
+
if (firstRow) checkingRowIndex = 0;
|
|
807
|
+
const colCount = getTableColumnCount(tableNode);
|
|
808
|
+
const affectedCellsSet = /* @__PURE__ */ new Set();
|
|
809
|
+
Array.from({ length: colCount }, (_, i) => i).forEach((cI) => {
|
|
810
|
+
const found = findCellByIndexes(editor, tableNode, checkingRowIndex, cI);
|
|
811
|
+
if (found) affectedCellsSet.add(found);
|
|
812
|
+
});
|
|
813
|
+
const affectedCells = Array.from(affectedCellsSet);
|
|
814
|
+
const newRowChildren = [];
|
|
815
|
+
affectedCells.forEach((cur) => {
|
|
816
|
+
if (!cur) return;
|
|
817
|
+
const curCell = cur;
|
|
818
|
+
const { col: curColIndex, row: curRowIndex } = getCellIndices(editor, curCell);
|
|
819
|
+
const curRowSpan = api.table.getRowSpan(curCell);
|
|
820
|
+
const curColSpan = api.table.getColSpan(curCell);
|
|
821
|
+
const currentCellPath = getCellPath(editor, tableEntry, curRowIndex, curColIndex);
|
|
822
|
+
if (curRowIndex + curRowSpan - 1 >= nextRowIndex && !firstRow) {
|
|
823
|
+
const rowSpan = curRowSpan + 1;
|
|
824
|
+
const newCell = cloneDeep({
|
|
825
|
+
...curCell,
|
|
826
|
+
rowSpan
|
|
827
|
+
});
|
|
828
|
+
if (newCell.attributes?.rowspan) newCell.attributes.rowspan = rowSpan.toString();
|
|
829
|
+
editor.tf.setNodes(newCell, { at: currentCellPath });
|
|
830
|
+
} else {
|
|
831
|
+
const rowElement = editor.api.parent(currentCellPath)[0];
|
|
832
|
+
const emptyCell = api.create.tableCell({
|
|
833
|
+
header,
|
|
834
|
+
row: rowElement
|
|
835
|
+
});
|
|
836
|
+
newRowChildren.push({
|
|
837
|
+
...emptyCell,
|
|
838
|
+
colSpan: curColSpan,
|
|
839
|
+
rowSpan: 1
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
editor.tf.withoutNormalizing(() => {
|
|
844
|
+
editor.tf.insertNodes({
|
|
845
|
+
children: newRowChildren,
|
|
846
|
+
type: editor.getType(KEYS.tr)
|
|
847
|
+
}, {
|
|
848
|
+
at: nextRowPath,
|
|
849
|
+
select: false
|
|
850
|
+
});
|
|
851
|
+
if (shouldSelect) {
|
|
852
|
+
const cellEntry$1 = editor.api.node({
|
|
853
|
+
at: nextRowPath,
|
|
854
|
+
match: { type: getCellTypes(editor) }
|
|
855
|
+
});
|
|
856
|
+
if (cellEntry$1) {
|
|
857
|
+
const [, nextCellPath] = cellEntry$1;
|
|
858
|
+
editor.tf.select(nextCellPath);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
};
|
|
863
|
+
/** Merges multiple selected cells into one. */
|
|
864
|
+
const mergeTableCells = (editor) => {
|
|
865
|
+
const { api } = getEditorPlugin(editor, BaseTablePlugin);
|
|
866
|
+
const cellEntries = getTableGridAbove(editor, { format: "cell" });
|
|
867
|
+
editor.tf.withoutNormalizing(() => {
|
|
868
|
+
let colSpan = 0;
|
|
869
|
+
for (const entry of cellEntries) {
|
|
870
|
+
const [cell, path] = entry;
|
|
871
|
+
if (path.at(-2) === cellEntries[0][1].at(-2)) {
|
|
872
|
+
const cellColSpan = api.table.getColSpan(cell);
|
|
873
|
+
colSpan += cellColSpan;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
let rowSpan = 0;
|
|
877
|
+
const { col } = getCellIndices(editor, cellEntries[0][0]);
|
|
878
|
+
cellEntries.forEach((entry) => {
|
|
879
|
+
const cell = entry[0];
|
|
880
|
+
const { col: curCol } = getCellIndices(editor, cell);
|
|
881
|
+
if (col === curCol) rowSpan += api.table.getRowSpan(cell);
|
|
882
|
+
});
|
|
883
|
+
const mergingCellChildren = [];
|
|
884
|
+
for (const cellEntry of cellEntries) {
|
|
885
|
+
const [el] = cellEntry;
|
|
886
|
+
const cellChildren = api.table.getCellChildren(el);
|
|
887
|
+
if (cellChildren.length !== 1 || !editor.api.isEmpty(cellChildren[0])) mergingCellChildren.push(...cloneDeep(cellChildren));
|
|
888
|
+
}
|
|
889
|
+
const cols = {};
|
|
890
|
+
cellEntries.forEach(([_entry, path]) => {
|
|
891
|
+
const rowIndex = path.at(-2);
|
|
892
|
+
if (cols[rowIndex]) cols[rowIndex].push(path);
|
|
893
|
+
else cols[rowIndex] = [path];
|
|
894
|
+
});
|
|
895
|
+
Object.values(cols).forEach((paths) => {
|
|
896
|
+
paths?.forEach(() => {
|
|
897
|
+
editor.tf.removeNodes({ at: paths[0] });
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
const mergedCell = {
|
|
901
|
+
...api.create.tableCell({
|
|
902
|
+
children: mergingCellChildren,
|
|
903
|
+
header: cellEntries[0][0].type === editor.getType(KEYS.th)
|
|
904
|
+
}),
|
|
905
|
+
colSpan,
|
|
906
|
+
rowSpan
|
|
907
|
+
};
|
|
908
|
+
editor.tf.insertNodes(mergedCell, { at: cellEntries[0][1] });
|
|
909
|
+
});
|
|
910
|
+
editor.tf.select(editor.api.end(cellEntries[0][1]));
|
|
911
|
+
};
|
|
912
|
+
const splitTableCell = (editor) => {
|
|
913
|
+
const { api } = getEditorPlugin(editor, BaseTablePlugin);
|
|
914
|
+
const tableRowType = editor.getType(KEYS.tr);
|
|
915
|
+
const [[cellElem, path]] = getTableGridAbove(editor, { format: "cell" });
|
|
916
|
+
editor.tf.withoutNormalizing(() => {
|
|
917
|
+
const createEmptyCell = (children) => ({
|
|
918
|
+
...api.create.tableCell({
|
|
919
|
+
children,
|
|
920
|
+
header: cellElem.type === editor.getType(KEYS.th)
|
|
921
|
+
}),
|
|
922
|
+
colSpan: 1,
|
|
923
|
+
rowSpan: 1
|
|
924
|
+
});
|
|
925
|
+
const tablePath = path.slice(0, -2);
|
|
926
|
+
const [rowPath, colPath] = path.slice(-2);
|
|
927
|
+
const colSpan = api.table.getColSpan(cellElem);
|
|
928
|
+
const rowSpan = api.table.getRowSpan(cellElem);
|
|
929
|
+
const colPaths = [];
|
|
930
|
+
for (let i = 0; i < colSpan; i++) colPaths.push(colPath + i);
|
|
931
|
+
const { col } = getCellIndices(editor, cellElem);
|
|
932
|
+
editor.tf.removeNodes({ at: path });
|
|
933
|
+
const getClosestColPathForRow = (row, targetCol) => {
|
|
934
|
+
const rowEntry = editor.api.node({
|
|
935
|
+
at: [...tablePath, row],
|
|
936
|
+
match: { type: tableRowType }
|
|
937
|
+
});
|
|
938
|
+
if (!rowEntry) return 0;
|
|
939
|
+
const rowEl = rowEntry[0];
|
|
940
|
+
let closestColPath = [];
|
|
941
|
+
let smallestDiff = Number.POSITIVE_INFINITY;
|
|
942
|
+
let isDirectionLeft = false;
|
|
943
|
+
rowEl.children.forEach((cell) => {
|
|
944
|
+
const cellElement = cell;
|
|
945
|
+
const { col: cellCol } = getCellIndices(editor, cellElement);
|
|
946
|
+
const diff = Math.abs(cellCol - targetCol);
|
|
947
|
+
if (diff < smallestDiff) {
|
|
948
|
+
smallestDiff = diff;
|
|
949
|
+
closestColPath = editor.api.findPath(cellElement);
|
|
950
|
+
isDirectionLeft = cellCol < targetCol;
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
if (closestColPath.length > 0) {
|
|
954
|
+
const lastIndex = closestColPath.at(-1);
|
|
955
|
+
if (isDirectionLeft) return lastIndex + 1;
|
|
956
|
+
return lastIndex;
|
|
957
|
+
}
|
|
958
|
+
return 1;
|
|
959
|
+
};
|
|
960
|
+
for (let i = 0; i < rowSpan; i++) {
|
|
961
|
+
const currentRowPath = rowPath + i;
|
|
962
|
+
const pathForNextRows = getClosestColPathForRow(currentRowPath, col);
|
|
963
|
+
const newRowChildren = [];
|
|
964
|
+
const _rowPath = [...tablePath, currentRowPath];
|
|
965
|
+
const rowEntry = editor.api.node({
|
|
966
|
+
at: _rowPath,
|
|
967
|
+
match: { type: tableRowType }
|
|
968
|
+
});
|
|
969
|
+
for (let j = 0; j < colPaths.length; j++) {
|
|
970
|
+
const cellChildren = api.table.getCellChildren(cellElem);
|
|
971
|
+
const cellToInsert = i === 0 && j === 0 ? createEmptyCell(cellChildren) : createEmptyCell();
|
|
972
|
+
if (rowEntry) {
|
|
973
|
+
const currentColPath = i === 0 ? colPaths[j] : pathForNextRows;
|
|
974
|
+
const pathForNewCell = [
|
|
975
|
+
...tablePath,
|
|
976
|
+
currentRowPath,
|
|
977
|
+
currentColPath
|
|
978
|
+
];
|
|
979
|
+
editor.tf.insertNodes(cellToInsert, { at: pathForNewCell });
|
|
980
|
+
} else newRowChildren.push(cellToInsert);
|
|
981
|
+
}
|
|
982
|
+
if (!rowEntry) editor.tf.insertNodes({
|
|
983
|
+
children: newRowChildren,
|
|
984
|
+
type: editor.getType(KEYS.tr)
|
|
985
|
+
}, { at: _rowPath });
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
editor.tf.select(editor.api.end(path));
|
|
989
|
+
};
|
|
990
|
+
const normalizeInitialValueTable = ({ editor, type }) => {
|
|
991
|
+
const tables = editor.api.nodes({
|
|
992
|
+
at: [],
|
|
993
|
+
match: { type }
|
|
994
|
+
});
|
|
995
|
+
for (const [table] of tables) computeCellIndices(editor, { tableNode: table });
|
|
996
|
+
};
|
|
997
|
+
const deleteColumn = (editor) => {
|
|
998
|
+
const { getOptions, type } = getEditorPlugin(editor, { key: KEYS.table });
|
|
999
|
+
const { disableMerge } = getOptions();
|
|
1000
|
+
const tableEntry = editor.api.above({ match: { type } });
|
|
1001
|
+
if (!tableEntry) return;
|
|
1002
|
+
editor.tf.withoutNormalizing(() => {
|
|
1003
|
+
if (!disableMerge) {
|
|
1004
|
+
deleteTableMergeColumn(editor);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
if (editor.api.isExpanded()) return deleteColumnWhenExpanded(editor, tableEntry);
|
|
1008
|
+
const tdEntry = editor.api.above({ match: { type: getCellTypes(editor) } });
|
|
1009
|
+
const trEntry = editor.api.above({ match: { type: editor.getType(KEYS.tr) } });
|
|
1010
|
+
if (tdEntry && trEntry && tableEntry && trEntry[0].children.length > 1) {
|
|
1011
|
+
const [tableNode, tablePath] = tableEntry;
|
|
1012
|
+
const tdPath = tdEntry[1];
|
|
1013
|
+
const colIndex = tdPath.at(-1);
|
|
1014
|
+
const pathToDelete = tdPath.slice();
|
|
1015
|
+
const replacePathPos = pathToDelete.length - 2;
|
|
1016
|
+
tableNode.children.forEach((row, rowIdx) => {
|
|
1017
|
+
pathToDelete[replacePathPos] = rowIdx;
|
|
1018
|
+
if (row.children.length === 1 || colIndex > row.children.length - 1) return;
|
|
1019
|
+
editor.tf.removeNodes({ at: pathToDelete });
|
|
1020
|
+
});
|
|
1021
|
+
const { colSizes } = tableNode;
|
|
1022
|
+
if (colSizes) {
|
|
1023
|
+
const newColSizes = [...colSizes];
|
|
1024
|
+
newColSizes.splice(colIndex, 1);
|
|
1025
|
+
editor.tf.setNodes({ colSizes: newColSizes }, { at: tablePath });
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
};
|
|
1030
|
+
const deleteRow = (editor) => {
|
|
1031
|
+
const { getOptions, type } = getEditorPlugin(editor, { key: KEYS.table });
|
|
1032
|
+
const { disableMerge } = getOptions();
|
|
1033
|
+
if (!disableMerge) return deleteTableMergeRow(editor);
|
|
1034
|
+
if (editor.api.some({ match: { type } })) {
|
|
1035
|
+
const currentTableItem = editor.api.above({ match: { type } });
|
|
1036
|
+
if (!currentTableItem) return;
|
|
1037
|
+
if (editor.api.isExpanded()) return deleteRowWhenExpanded(editor, currentTableItem);
|
|
1038
|
+
const currentRowItem = editor.api.above({ match: { type: editor.getType(KEYS.tr) } });
|
|
1039
|
+
if (currentRowItem && currentTableItem && currentTableItem[0].children.length > 1) editor.tf.removeNodes({ at: currentRowItem[1] });
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
const deleteTable = (editor) => {
|
|
1043
|
+
if (editor.api.some({ match: { type: editor.getType(KEYS.table) } })) {
|
|
1044
|
+
const tableItem = editor.api.above({ match: { type: editor.getType(KEYS.table) } });
|
|
1045
|
+
if (tableItem) editor.tf.removeNodes({ at: tableItem[1] });
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
/**
|
|
1049
|
+
* Insert table. If selection in table and no 'at' specified, insert after
|
|
1050
|
+
* current table. Select start of new table.
|
|
1051
|
+
*/
|
|
1052
|
+
const insertTable = (editor, { colCount = 2, header, rowCount = 2 } = {}, { select: shouldSelect, ...options } = {}) => {
|
|
1053
|
+
const { api } = editor.getPlugin({ key: KEYS.table });
|
|
1054
|
+
const type = editor.getType(KEYS.table);
|
|
1055
|
+
editor.tf.withoutNormalizing(() => {
|
|
1056
|
+
const newTable = api.create.table({
|
|
1057
|
+
colCount,
|
|
1058
|
+
header,
|
|
1059
|
+
rowCount
|
|
1060
|
+
});
|
|
1061
|
+
if (!options.at) {
|
|
1062
|
+
const currentTableEntry = editor.api.block({ match: { type } });
|
|
1063
|
+
if (currentTableEntry) {
|
|
1064
|
+
const [, tablePath] = currentTableEntry;
|
|
1065
|
+
const insertPath = PathApi.next(tablePath);
|
|
1066
|
+
editor.tf.insertNodes(newTable, {
|
|
1067
|
+
at: insertPath,
|
|
1068
|
+
...options
|
|
1069
|
+
});
|
|
1070
|
+
if (editor.selection) editor.tf.select(editor.api.start(insertPath));
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
editor.tf.insertNodes(newTable, {
|
|
1075
|
+
nextBlock: !options.at,
|
|
1076
|
+
select: shouldSelect,
|
|
1077
|
+
...options
|
|
1078
|
+
});
|
|
1079
|
+
if (shouldSelect) {
|
|
1080
|
+
const tableEntry = editor.api.node({
|
|
1081
|
+
at: options.at,
|
|
1082
|
+
match: { type }
|
|
1083
|
+
});
|
|
1084
|
+
if (!tableEntry) return;
|
|
1085
|
+
editor.tf.select(editor.api.start(tableEntry[1]));
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
};
|
|
1089
|
+
const insertTableColumn = (editor, options = {}) => {
|
|
1090
|
+
const { api, getOptions, type } = getEditorPlugin(editor, BaseTablePlugin);
|
|
1091
|
+
const { disableMerge, initialTableWidth, minColumnWidth } = getOptions();
|
|
1092
|
+
if (!disableMerge) return insertTableMergeColumn(editor, options);
|
|
1093
|
+
const { before, header, select: shouldSelect } = options;
|
|
1094
|
+
let { at, fromCell } = options;
|
|
1095
|
+
if (at && !fromCell) {
|
|
1096
|
+
if (NodeApi.get(editor, at)?.type === editor.getType(KEYS.table)) {
|
|
1097
|
+
fromCell = NodeApi.lastChild(editor, at.concat([0]))[1];
|
|
1098
|
+
at = void 0;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
const cellEntry = editor.api.block({
|
|
1102
|
+
at: fromCell,
|
|
1103
|
+
match: { type: getCellTypes(editor) }
|
|
1104
|
+
});
|
|
1105
|
+
if (!cellEntry) return;
|
|
1106
|
+
const [, cellPath] = cellEntry;
|
|
1107
|
+
const tableEntry = editor.api.block({
|
|
1108
|
+
above: true,
|
|
1109
|
+
at: cellPath,
|
|
1110
|
+
match: { type }
|
|
1111
|
+
});
|
|
1112
|
+
if (!tableEntry) return;
|
|
1113
|
+
const [tableNode, tablePath] = tableEntry;
|
|
1114
|
+
let nextCellPath;
|
|
1115
|
+
let nextColIndex;
|
|
1116
|
+
if (PathApi.isPath(at)) {
|
|
1117
|
+
nextCellPath = at;
|
|
1118
|
+
nextColIndex = at.at(-1);
|
|
1119
|
+
} else {
|
|
1120
|
+
nextCellPath = before ? cellPath : PathApi.next(cellPath);
|
|
1121
|
+
nextColIndex = before ? cellPath.at(-1) : cellPath.at(-1) + 1;
|
|
1122
|
+
}
|
|
1123
|
+
const currentRowIndex = cellPath.at(-2);
|
|
1124
|
+
editor.tf.withoutNormalizing(() => {
|
|
1125
|
+
tableNode.children.forEach((row, rowIndex) => {
|
|
1126
|
+
const insertCellPath = [...nextCellPath];
|
|
1127
|
+
if (PathApi.isPath(at)) insertCellPath[at.length - 2] = rowIndex;
|
|
1128
|
+
else insertCellPath[cellPath.length - 2] = rowIndex;
|
|
1129
|
+
const isHeaderRow = header === void 0 ? row.children.every((c) => c.type === editor.getType(KEYS.th)) : header;
|
|
1130
|
+
editor.tf.insertNodes(api.create.tableCell({ header: isHeaderRow }), {
|
|
1131
|
+
at: insertCellPath,
|
|
1132
|
+
select: shouldSelect && rowIndex === currentRowIndex
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
const { colSizes } = tableNode;
|
|
1136
|
+
if (colSizes) {
|
|
1137
|
+
let newColSizes = [
|
|
1138
|
+
...colSizes.slice(0, nextColIndex),
|
|
1139
|
+
0,
|
|
1140
|
+
...colSizes.slice(nextColIndex)
|
|
1141
|
+
];
|
|
1142
|
+
if (initialTableWidth) {
|
|
1143
|
+
newColSizes[nextColIndex] = colSizes[nextColIndex] ?? colSizes[nextColIndex - 1] ?? initialTableWidth / colSizes.length;
|
|
1144
|
+
const oldTotal = colSizes.reduce((a, b) => a + b, 0);
|
|
1145
|
+
const newTotal = newColSizes.reduce((a, b) => a + b, 0);
|
|
1146
|
+
const maxTotal = Math.max(oldTotal, initialTableWidth);
|
|
1147
|
+
if (newTotal > maxTotal) {
|
|
1148
|
+
const factor = maxTotal / newTotal;
|
|
1149
|
+
newColSizes = newColSizes.map((size) => Math.max(minColumnWidth ?? 0, Math.floor(size * factor)));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
editor.tf.setNodes({ colSizes: newColSizes }, { at: tablePath });
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
};
|
|
1156
|
+
const insertTableRow = (editor, options = {}) => {
|
|
1157
|
+
const { api, getOptions, type } = getEditorPlugin(editor, BaseTablePlugin);
|
|
1158
|
+
const { disableMerge } = getOptions();
|
|
1159
|
+
if (!disableMerge) return insertTableMergeRow(editor, options);
|
|
1160
|
+
const { before, header, select: shouldSelect } = options;
|
|
1161
|
+
let { at, fromRow } = options;
|
|
1162
|
+
if (at && !fromRow) {
|
|
1163
|
+
if (NodeApi.get(editor, at)?.type === editor.getType(KEYS.table)) {
|
|
1164
|
+
fromRow = NodeApi.lastChild(editor, at)[1];
|
|
1165
|
+
at = void 0;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
const trEntry = editor.api.block({
|
|
1169
|
+
at: fromRow,
|
|
1170
|
+
match: { type: editor.getType(KEYS.tr) }
|
|
1171
|
+
});
|
|
1172
|
+
if (!trEntry) return;
|
|
1173
|
+
const [trNode, trPath] = trEntry;
|
|
1174
|
+
const tableEntry = editor.api.block({
|
|
1175
|
+
above: true,
|
|
1176
|
+
at: trPath,
|
|
1177
|
+
match: { type }
|
|
1178
|
+
});
|
|
1179
|
+
if (!tableEntry) return;
|
|
1180
|
+
const getEmptyRowNode$1 = () => ({
|
|
1181
|
+
children: trNode.children.map((_, i) => {
|
|
1182
|
+
const isHeaderColumn = !(tableEntry[0].children.length === 1) && tableEntry[0].children.every((n) => n.children[i].type === editor.getType(KEYS.th));
|
|
1183
|
+
return api.create.tableCell({ header: header ?? isHeaderColumn });
|
|
1184
|
+
}),
|
|
1185
|
+
type: editor.getType(KEYS.tr)
|
|
1186
|
+
});
|
|
1187
|
+
editor.tf.withoutNormalizing(() => {
|
|
1188
|
+
editor.tf.insertNodes(getEmptyRowNode$1(), { at: PathApi.isPath(at) ? at : before ? trPath : PathApi.next(trPath) });
|
|
1189
|
+
});
|
|
1190
|
+
if (shouldSelect) {
|
|
1191
|
+
const cellEntry = editor.api.block({ match: { type: getCellTypes(editor) } });
|
|
1192
|
+
if (!cellEntry) return;
|
|
1193
|
+
const [, nextCellPath] = cellEntry;
|
|
1194
|
+
if (PathApi.isPath(at)) nextCellPath[nextCellPath.length - 2] = at.at(-2);
|
|
1195
|
+
else nextCellPath[nextCellPath.length - 2] = before ? nextCellPath.at(-2) : nextCellPath.at(-2) + 1;
|
|
1196
|
+
editor.tf.select(nextCellPath);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
/** Move selection by cell unit. */
|
|
1200
|
+
const moveSelectionFromCell = (editor, { at, edge, fromOneCell, reverse } = {}) => {
|
|
1201
|
+
if (edge) {
|
|
1202
|
+
const cellEntries = getTableGridAbove(editor, {
|
|
1203
|
+
at,
|
|
1204
|
+
format: "cell"
|
|
1205
|
+
});
|
|
1206
|
+
const minCell = fromOneCell ? 0 : 1;
|
|
1207
|
+
if (cellEntries.length > minCell) {
|
|
1208
|
+
const [, firstCellPath] = cellEntries[0];
|
|
1209
|
+
const [, lastCellPath] = cellEntries.at(-1);
|
|
1210
|
+
const anchorPath = [...firstCellPath];
|
|
1211
|
+
const focusPath = [...lastCellPath];
|
|
1212
|
+
switch (edge) {
|
|
1213
|
+
case "bottom":
|
|
1214
|
+
focusPath[focusPath.length - 2] += 1;
|
|
1215
|
+
break;
|
|
1216
|
+
case "left":
|
|
1217
|
+
anchorPath[anchorPath.length - 1] -= 1;
|
|
1218
|
+
break;
|
|
1219
|
+
case "right":
|
|
1220
|
+
focusPath[focusPath.length - 1] += 1;
|
|
1221
|
+
break;
|
|
1222
|
+
case "top":
|
|
1223
|
+
anchorPath[anchorPath.length - 2] -= 1;
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
if (NodeApi.has(editor, anchorPath) && NodeApi.has(editor, focusPath)) editor.tf.select({
|
|
1227
|
+
anchor: editor.api.start(anchorPath),
|
|
1228
|
+
focus: editor.api.start(focusPath)
|
|
1229
|
+
});
|
|
1230
|
+
return true;
|
|
1231
|
+
}
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
const cellEntry = editor.api.block({
|
|
1235
|
+
at,
|
|
1236
|
+
match: { type: getCellTypes(editor) }
|
|
1237
|
+
});
|
|
1238
|
+
if (cellEntry) {
|
|
1239
|
+
const [, cellPath] = cellEntry;
|
|
1240
|
+
const nextCellPath = [...cellPath];
|
|
1241
|
+
const offset = reverse ? -1 : 1;
|
|
1242
|
+
nextCellPath[nextCellPath.length - 2] += offset;
|
|
1243
|
+
if (NodeApi.has(editor, nextCellPath)) editor.tf.select(editor.api.start(nextCellPath));
|
|
1244
|
+
else {
|
|
1245
|
+
const tablePath = cellPath.slice(0, -2);
|
|
1246
|
+
if (reverse) editor.tf.withoutNormalizing(() => {
|
|
1247
|
+
editor.tf.select(editor.api.start(tablePath));
|
|
1248
|
+
editor.tf.move({ reverse: true });
|
|
1249
|
+
});
|
|
1250
|
+
else editor.tf.withoutNormalizing(() => {
|
|
1251
|
+
editor.tf.select(editor.api.end(tablePath));
|
|
1252
|
+
editor.tf.move();
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
return true;
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
/**
|
|
1259
|
+
* Override the new selection if the previous selection and the new one are in
|
|
1260
|
+
* different cells.
|
|
1261
|
+
*/
|
|
1262
|
+
const overrideSelectionFromCell = (editor, newSelection) => {
|
|
1263
|
+
let hotkey;
|
|
1264
|
+
if (!editor.dom.currentKeyboardEvent || ![
|
|
1265
|
+
"up",
|
|
1266
|
+
"down",
|
|
1267
|
+
"shift+up",
|
|
1268
|
+
"shift+right",
|
|
1269
|
+
"shift+down",
|
|
1270
|
+
"shift+left"
|
|
1271
|
+
].some((key) => {
|
|
1272
|
+
const valid = isHotkey(key, editor.dom.currentKeyboardEvent);
|
|
1273
|
+
if (valid) hotkey = key;
|
|
1274
|
+
return valid;
|
|
1275
|
+
}) || !editor.selection?.focus || !newSelection?.focus || !editor.api.isAt({
|
|
1276
|
+
at: {
|
|
1277
|
+
anchor: editor.selection.focus,
|
|
1278
|
+
focus: newSelection.focus
|
|
1279
|
+
},
|
|
1280
|
+
blocks: true,
|
|
1281
|
+
match: { type: getCellTypes(editor) }
|
|
1282
|
+
})) return;
|
|
1283
|
+
if (!hotkey) return;
|
|
1284
|
+
const edge = KEY_SHIFT_EDGES[hotkey];
|
|
1285
|
+
if (edge && !editor.api.isAt({
|
|
1286
|
+
block: true,
|
|
1287
|
+
match: { type: getCellTypes(editor) }
|
|
1288
|
+
})) return;
|
|
1289
|
+
const prevSelection = editor.selection;
|
|
1290
|
+
const reverse = ["shift+up", "up"].includes(hotkey);
|
|
1291
|
+
setTimeout(() => {
|
|
1292
|
+
moveSelectionFromCell(editor, {
|
|
1293
|
+
at: prevSelection,
|
|
1294
|
+
edge,
|
|
1295
|
+
fromOneCell: true,
|
|
1296
|
+
reverse
|
|
1297
|
+
});
|
|
1298
|
+
}, 0);
|
|
1299
|
+
};
|
|
1300
|
+
/**
|
|
1301
|
+
* Selection table:
|
|
1302
|
+
*
|
|
1303
|
+
* - If anchor is in table, focus in a block before: set focus to start of table
|
|
1304
|
+
* - If anchor is in table, focus in a block after: set focus to end of table
|
|
1305
|
+
* - If focus is in table, anchor in a block before: set focus to end of table
|
|
1306
|
+
* - If focus is in table, anchor in a block after: set focus to the point before
|
|
1307
|
+
* start of table
|
|
1308
|
+
*/
|
|
1309
|
+
const withApplyTable = ({ api: _api, editor, getOptions, tf: { apply }, type: tableType }) => ({ transforms: { apply(op) {
|
|
1310
|
+
if (op.type === "set_selection" && op.newProperties) {
|
|
1311
|
+
const newSelection = {
|
|
1312
|
+
...editor.selection,
|
|
1313
|
+
...op.newProperties
|
|
1314
|
+
};
|
|
1315
|
+
if (RangeApi.isRange(newSelection) && editor.api.isAt({
|
|
1316
|
+
at: newSelection,
|
|
1317
|
+
blocks: true,
|
|
1318
|
+
match: (n) => n.type === tableType
|
|
1319
|
+
})) {
|
|
1320
|
+
const anchorEntry = editor.api.block({
|
|
1321
|
+
at: newSelection.anchor,
|
|
1322
|
+
match: (n) => n.type === tableType
|
|
1323
|
+
});
|
|
1324
|
+
if (anchorEntry) {
|
|
1325
|
+
const [, anchorPath] = anchorEntry;
|
|
1326
|
+
if (RangeApi.isBackward(newSelection)) op.newProperties.focus = editor.api.start(anchorPath);
|
|
1327
|
+
else if (editor.api.before(anchorPath)) op.newProperties.focus = editor.api.end(anchorPath);
|
|
1328
|
+
} else {
|
|
1329
|
+
const focusEntry = editor.api.block({
|
|
1330
|
+
at: newSelection.focus,
|
|
1331
|
+
match: (n) => n.type === tableType
|
|
1332
|
+
});
|
|
1333
|
+
if (focusEntry) {
|
|
1334
|
+
const [, focusPath] = focusEntry;
|
|
1335
|
+
if (RangeApi.isBackward(newSelection)) {
|
|
1336
|
+
const startPoint = editor.api.start(focusPath);
|
|
1337
|
+
const pointBefore = editor.api.before(startPoint);
|
|
1338
|
+
op.newProperties.focus = pointBefore ?? startPoint;
|
|
1339
|
+
} else op.newProperties.focus = editor.api.end(focusPath);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
overrideSelectionFromCell(editor, newSelection);
|
|
1344
|
+
}
|
|
1345
|
+
const opType = op.type === "remove_node" ? op.node.type : op.type === "move_node" ? editor.api.node(op.path)?.[0].type : void 0;
|
|
1346
|
+
const isTableOperation = (op.type === "remove_node" || op.type === "move_node") && opType && [
|
|
1347
|
+
editor.getType(KEYS.tr),
|
|
1348
|
+
tableType,
|
|
1349
|
+
...getCellTypes(editor)
|
|
1350
|
+
].includes(opType);
|
|
1351
|
+
if (isTableOperation && op.type === "remove_node") {
|
|
1352
|
+
const cells = [...editor.api.nodes({
|
|
1353
|
+
at: op.path,
|
|
1354
|
+
match: { type: getCellTypes(editor) }
|
|
1355
|
+
})];
|
|
1356
|
+
const cellIndices = getOptions()._cellIndices;
|
|
1357
|
+
cells.forEach(([cell]) => {
|
|
1358
|
+
delete cellIndices[cell.id];
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
apply(op);
|
|
1362
|
+
let table;
|
|
1363
|
+
if (isTableOperation && opType !== tableType) {
|
|
1364
|
+
table = editor.api.node({
|
|
1365
|
+
at: op.type === "move_node" ? op.newPath : op.path,
|
|
1366
|
+
match: { type: tableType }
|
|
1367
|
+
})?.[0];
|
|
1368
|
+
if (table) computeCellIndices(editor, { tableNode: table });
|
|
1369
|
+
}
|
|
1370
|
+
} } });
|
|
1371
|
+
/** Prevent cell deletion. */
|
|
1372
|
+
const withDeleteTable = ({ editor, tf: { deleteFragment }, type }) => ({ transforms: { deleteFragment(direction) {
|
|
1373
|
+
if (editor.api.isAt({
|
|
1374
|
+
block: true,
|
|
1375
|
+
match: (n) => n.type === type
|
|
1376
|
+
})) {
|
|
1377
|
+
const cellEntries = getTableGridAbove(editor, { format: "cell" });
|
|
1378
|
+
if (cellEntries.length > 1) {
|
|
1379
|
+
editor.tf.withoutNormalizing(() => {
|
|
1380
|
+
cellEntries.forEach(([, cellPath]) => {
|
|
1381
|
+
editor.tf.replaceNodes(editor.api.create.block(), {
|
|
1382
|
+
at: cellPath,
|
|
1383
|
+
children: true
|
|
1384
|
+
});
|
|
1385
|
+
});
|
|
1386
|
+
editor.tf.select({
|
|
1387
|
+
anchor: editor.api.start(cellEntries[0][1]),
|
|
1388
|
+
focus: editor.api.end(cellEntries.at(-1)[1])
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
deleteFragment(direction);
|
|
1395
|
+
} } });
|
|
1396
|
+
/** If selection is in a table, get subtable above. */
|
|
1397
|
+
const withGetFragmentTable = ({ api, api: { getFragment }, editor, type }) => ({ api: { getFragment() {
|
|
1398
|
+
const fragment = getFragment();
|
|
1399
|
+
const newFragment = [];
|
|
1400
|
+
fragment.forEach((node) => {
|
|
1401
|
+
if (node.type === type) {
|
|
1402
|
+
const rows = node.children;
|
|
1403
|
+
const rowCount = rows.length;
|
|
1404
|
+
if (!rowCount) return;
|
|
1405
|
+
const colCount = rows[0].children.length;
|
|
1406
|
+
if (rowCount <= 1 && colCount <= 1) {
|
|
1407
|
+
const cell = rows[0];
|
|
1408
|
+
const cellChildren = api.table.getCellChildren(cell);
|
|
1409
|
+
newFragment.push(...cellChildren[0].children);
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
const subTable = getTableGridAbove(editor);
|
|
1413
|
+
if (subTable.length > 0) {
|
|
1414
|
+
newFragment.push(subTable[0][0]);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
newFragment.push(node);
|
|
1419
|
+
});
|
|
1420
|
+
return newFragment;
|
|
1421
|
+
} } });
|
|
1422
|
+
/**
|
|
1423
|
+
* If inserting a table, If block above anchor is a table,
|
|
1424
|
+
*
|
|
1425
|
+
* - Replace each cell above by the inserted table until out of bounds.
|
|
1426
|
+
* - Select the inserted cells.
|
|
1427
|
+
*/
|
|
1428
|
+
const withInsertFragmentTable = ({ api, editor, getOptions, tf: { insert, insertFragment }, type }) => ({ transforms: { insertFragment(fragment) {
|
|
1429
|
+
const insertedTable = fragment.find((n) => n.type === type);
|
|
1430
|
+
if (!insertedTable) {
|
|
1431
|
+
if (getTableAbove(editor, { at: editor.selection?.anchor })) {
|
|
1432
|
+
const cellEntries = getTableGridAbove(editor, { format: "cell" });
|
|
1433
|
+
if (cellEntries.length > 1) {
|
|
1434
|
+
cellEntries.forEach((cellEntry) => {
|
|
1435
|
+
if (cellEntry) {
|
|
1436
|
+
const [, cellPath] = cellEntry;
|
|
1437
|
+
editor.tf.replaceNodes(cloneDeep(fragment), {
|
|
1438
|
+
at: cellPath,
|
|
1439
|
+
children: true
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
editor.tf.select({
|
|
1444
|
+
anchor: editor.api.start(cellEntries[0][1]),
|
|
1445
|
+
focus: editor.api.end(cellEntries.at(-1)[1])
|
|
1446
|
+
});
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
if (insertedTable) {
|
|
1452
|
+
if (getTableAbove(editor, { at: editor.selection?.anchor })) {
|
|
1453
|
+
const [cellEntry] = getTableGridAbove(editor, {
|
|
1454
|
+
at: editor.selection?.anchor,
|
|
1455
|
+
format: "cell"
|
|
1456
|
+
});
|
|
1457
|
+
if (cellEntry) {
|
|
1458
|
+
editor.tf.withoutNormalizing(() => {
|
|
1459
|
+
const [, startCellPath] = cellEntry;
|
|
1460
|
+
const cellPath = [...startCellPath];
|
|
1461
|
+
const startColIndex = cellPath.at(-1);
|
|
1462
|
+
let lastCellPath = null;
|
|
1463
|
+
let initRow = true;
|
|
1464
|
+
insertedTable.children.forEach((row) => {
|
|
1465
|
+
cellPath[cellPath.length - 1] = startColIndex;
|
|
1466
|
+
if (!initRow) {
|
|
1467
|
+
const fromRow = cellPath.slice(0, -1);
|
|
1468
|
+
cellPath[cellPath.length - 2] += 1;
|
|
1469
|
+
if (!NodeApi.has(editor, cellPath)) {
|
|
1470
|
+
if (getOptions().disableExpandOnInsert) return;
|
|
1471
|
+
insert.tableRow({ fromRow });
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
initRow = false;
|
|
1475
|
+
const insertedCells = row.children;
|
|
1476
|
+
let initCell = true;
|
|
1477
|
+
insertedCells.forEach((cell) => {
|
|
1478
|
+
if (!initCell) {
|
|
1479
|
+
const fromCell = [...cellPath];
|
|
1480
|
+
cellPath[cellPath.length - 1] += 1;
|
|
1481
|
+
if (!NodeApi.has(editor, cellPath)) {
|
|
1482
|
+
if (getOptions().disableExpandOnInsert) return;
|
|
1483
|
+
insert.tableColumn({ fromCell });
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
initCell = false;
|
|
1487
|
+
const cellChildren = api.table.getCellChildren(cell);
|
|
1488
|
+
editor.tf.replaceNodes(cloneDeep(cellChildren), {
|
|
1489
|
+
at: cellPath,
|
|
1490
|
+
children: true
|
|
1491
|
+
});
|
|
1492
|
+
lastCellPath = [...cellPath];
|
|
1493
|
+
});
|
|
1494
|
+
});
|
|
1495
|
+
if (lastCellPath) editor.tf.select({
|
|
1496
|
+
anchor: editor.api.start(startCellPath),
|
|
1497
|
+
focus: editor.api.end(lastCellPath)
|
|
1498
|
+
});
|
|
1499
|
+
});
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
} else if (fragment.length === 1 && fragment[0].type === KEYS.table) {
|
|
1503
|
+
editor.tf.insertNodes(fragment[0]);
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
insertFragment(fragment);
|
|
1508
|
+
} } });
|
|
1509
|
+
const withInsertTextTable = ({ editor, tf: { insertText } }) => ({ transforms: { insertText(text, options) {
|
|
1510
|
+
if (editor.api.isExpanded()) {
|
|
1511
|
+
if (getTableAbove(editor, { at: editor.selection?.anchor })) {
|
|
1512
|
+
if (getTableGridAbove(editor, { format: "cell" }).length > 1) editor.tf.collapse({ edge: "focus" });
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
insertText(text, options);
|
|
1516
|
+
} } });
|
|
1517
|
+
/**
|
|
1518
|
+
* Normalize table:
|
|
1519
|
+
*
|
|
1520
|
+
* - Wrap cell children in a paragraph if they are texts.
|
|
1521
|
+
*/
|
|
1522
|
+
const withNormalizeTable = ({ editor, getOption, getOptions, tf: { normalizeNode }, type }) => ({ transforms: { normalizeNode([n, path]) {
|
|
1523
|
+
const { enableUnsetSingleColSize, initialTableWidth } = getOptions();
|
|
1524
|
+
if (ElementApi.isElement(n)) {
|
|
1525
|
+
if (n.type === type) {
|
|
1526
|
+
const node = n;
|
|
1527
|
+
if (!node.children.some((child) => ElementApi.isElement(child) && child.type === editor.getType(KEYS.tr))) {
|
|
1528
|
+
editor.tf.removeNodes({ at: path });
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
if (node.colSizes && node.colSizes.length > 0 && enableUnsetSingleColSize && getTableColumnCount(node) < 2) {
|
|
1532
|
+
editor.tf.unsetNodes("colSizes", { at: path });
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
if (editor.api.block({
|
|
1536
|
+
above: true,
|
|
1537
|
+
at: path,
|
|
1538
|
+
match: { type }
|
|
1539
|
+
})) {
|
|
1540
|
+
editor.tf.unwrapNodes({ at: path });
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (initialTableWidth) {
|
|
1544
|
+
const tableNode = node;
|
|
1545
|
+
const colCount = tableNode.children[0]?.children?.length;
|
|
1546
|
+
if (colCount) {
|
|
1547
|
+
const colSizes = [];
|
|
1548
|
+
if (!tableNode.colSizes) for (let i = 0; i < colCount; i++) colSizes.push(initialTableWidth / colCount);
|
|
1549
|
+
else if (tableNode.colSizes.some((size) => !size)) tableNode.colSizes.forEach((colSize) => {
|
|
1550
|
+
colSizes.push(colSize || initialTableWidth / colCount);
|
|
1551
|
+
});
|
|
1552
|
+
if (colSizes.length > 0) {
|
|
1553
|
+
editor.tf.setNodes({ colSizes }, { at: path });
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
if (n.type === editor.getType(KEYS.tr)) {
|
|
1560
|
+
if (editor.api.parent(path)?.[0].type !== type) {
|
|
1561
|
+
editor.tf.unwrapNodes({ at: path });
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (getCellTypes(editor).includes(n.type)) {
|
|
1566
|
+
const node = n;
|
|
1567
|
+
const cellIndices = getOption("cellIndices", node.id);
|
|
1568
|
+
if (node.id && !cellIndices) computeCellIndices(editor, {
|
|
1569
|
+
all: true,
|
|
1570
|
+
cellNode: node
|
|
1571
|
+
});
|
|
1572
|
+
const { children } = node;
|
|
1573
|
+
if (editor.api.parent(path)?.[0].type !== editor.getType(KEYS.tr)) {
|
|
1574
|
+
editor.tf.unwrapNodes({ at: path });
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
if (TextApi.isText(children[0])) {
|
|
1578
|
+
editor.tf.wrapNodes(editor.api.create.block({}, path), {
|
|
1579
|
+
at: path,
|
|
1580
|
+
children: true
|
|
1581
|
+
});
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
normalizeNode([n, path]);
|
|
1587
|
+
} } });
|
|
1588
|
+
const withSetFragmentDataTable = ({ api, editor, plugin, tf: { setFragmentData } }) => ({ transforms: { setFragmentData(data, originEvent) {
|
|
1589
|
+
const tableEntry = getTableGridAbove(editor, { format: "table" })?.[0];
|
|
1590
|
+
const selectedCellEntries = getTableGridAbove(editor, { format: "cell" });
|
|
1591
|
+
const initialSelection = editor.selection;
|
|
1592
|
+
if (!tableEntry || !initialSelection) {
|
|
1593
|
+
setFragmentData(data, originEvent);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
const [tableNode, tablePath] = tableEntry;
|
|
1597
|
+
const tableRows = tableNode.children;
|
|
1598
|
+
tableNode.children = tableNode.children.filter((v) => v.children.length > 0);
|
|
1599
|
+
let textCsv = "";
|
|
1600
|
+
let textTsv = "";
|
|
1601
|
+
const divElement = document.createElement("div");
|
|
1602
|
+
const tableElement = document.createElement("table");
|
|
1603
|
+
/**
|
|
1604
|
+
* Cover single cell copy | cut operation. In this case, copy cell content
|
|
1605
|
+
* instead of table structure.
|
|
1606
|
+
*/
|
|
1607
|
+
if (tableEntry && initialSelection && selectedCellEntries.length === 1 && (originEvent === "copy" || originEvent === "cut")) {
|
|
1608
|
+
setFragmentData(data);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
editor.tf.withoutNormalizing(() => {
|
|
1612
|
+
tableRows.forEach((row) => {
|
|
1613
|
+
const rowCells = row.children;
|
|
1614
|
+
const cellStrings = [];
|
|
1615
|
+
const rowElement = row.type === editor.getType(KEYS.th) ? document.createElement("th") : document.createElement("tr");
|
|
1616
|
+
rowCells.forEach((cell) => {
|
|
1617
|
+
data.clearData();
|
|
1618
|
+
const cellPath = editor.api.findPath(cell);
|
|
1619
|
+
editor.tf.select({
|
|
1620
|
+
anchor: editor.api.start(cellPath),
|
|
1621
|
+
focus: editor.api.end(cellPath)
|
|
1622
|
+
});
|
|
1623
|
+
setFragmentData(data);
|
|
1624
|
+
cellStrings.push(data.getData("text/plain"));
|
|
1625
|
+
const cellElement = document.createElement("td");
|
|
1626
|
+
cellElement.colSpan = api.table.getColSpan(cell);
|
|
1627
|
+
cellElement.rowSpan = api.table.getRowSpan(cell);
|
|
1628
|
+
cellElement.innerHTML = data.getData("text/html");
|
|
1629
|
+
rowElement.append(cellElement);
|
|
1630
|
+
});
|
|
1631
|
+
tableElement.append(rowElement);
|
|
1632
|
+
textCsv += `${cellStrings.join(",")}\n`;
|
|
1633
|
+
textTsv += `${cellStrings.join(" ")}\n`;
|
|
1634
|
+
});
|
|
1635
|
+
const _tableEntry = editor.api.node({
|
|
1636
|
+
at: tablePath,
|
|
1637
|
+
match: { type: KEYS.table }
|
|
1638
|
+
});
|
|
1639
|
+
if (_tableEntry != null && _tableEntry.length > 0) {
|
|
1640
|
+
const realTable = _tableEntry[0];
|
|
1641
|
+
if (realTable.attributes != null) Object.entries(realTable.attributes).forEach(([key, value]) => {
|
|
1642
|
+
if (value != null && plugin.node.dangerouslyAllowAttributes?.includes(key)) tableElement.setAttribute(key, String(value));
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
editor.tf.select(initialSelection);
|
|
1646
|
+
divElement.append(tableElement);
|
|
1647
|
+
});
|
|
1648
|
+
data.setData("text/csv", textCsv);
|
|
1649
|
+
data.setData("text/tsv", textTsv);
|
|
1650
|
+
data.setData("text/plain", textTsv);
|
|
1651
|
+
data.setData("text/html", divElement.innerHTML);
|
|
1652
|
+
const selectedFragmentStr = JSON.stringify([tableNode]);
|
|
1653
|
+
const encodedFragment = window.btoa(encodeURIComponent(selectedFragmentStr));
|
|
1654
|
+
data.setData("application/x-slate-fragment", encodedFragment);
|
|
1655
|
+
} } });
|
|
1656
|
+
const withTableCellSelection = ({ api: { marks }, editor, tf: { addMark, removeMark, setNodes } }) => {
|
|
1657
|
+
return {
|
|
1658
|
+
api: { marks() {
|
|
1659
|
+
const apply = () => {
|
|
1660
|
+
const { selection } = editor;
|
|
1661
|
+
if (!selection || editor.api.isCollapsed()) return;
|
|
1662
|
+
const matchesCell = getTableGridAbove(editor, { format: "cell" });
|
|
1663
|
+
if (matchesCell.length <= 1) return;
|
|
1664
|
+
const markCounts = {};
|
|
1665
|
+
const totalMarks = {};
|
|
1666
|
+
let totalNodes = 0;
|
|
1667
|
+
matchesCell.forEach(([_cell, cellPath]) => {
|
|
1668
|
+
const textNodeEntry = editor.api.nodes({
|
|
1669
|
+
at: cellPath,
|
|
1670
|
+
match: (n) => TextApi.isText(n)
|
|
1671
|
+
});
|
|
1672
|
+
Array.from(textNodeEntry, (item) => item[0]).forEach((item) => {
|
|
1673
|
+
totalNodes++;
|
|
1674
|
+
const keys = Object.keys(item);
|
|
1675
|
+
if (keys.length === 1) return;
|
|
1676
|
+
keys.splice(keys.indexOf("text"), 1);
|
|
1677
|
+
keys.forEach((k) => {
|
|
1678
|
+
markCounts[k] = (markCounts[k] || 0) + 1;
|
|
1679
|
+
totalMarks[k] = item[k];
|
|
1680
|
+
});
|
|
1681
|
+
});
|
|
1682
|
+
});
|
|
1683
|
+
Object.keys(markCounts).forEach((mark) => {
|
|
1684
|
+
if (markCounts[mark] !== totalNodes) delete totalMarks[mark];
|
|
1685
|
+
});
|
|
1686
|
+
return totalMarks;
|
|
1687
|
+
};
|
|
1688
|
+
const result = apply();
|
|
1689
|
+
if (result) return result;
|
|
1690
|
+
return marks();
|
|
1691
|
+
} },
|
|
1692
|
+
transforms: {
|
|
1693
|
+
addMark(key, value) {
|
|
1694
|
+
const apply = () => {
|
|
1695
|
+
const { selection } = editor;
|
|
1696
|
+
if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
|
|
1697
|
+
const matchesCell = getTableGridAbove(editor, { format: "cell" });
|
|
1698
|
+
if (matchesCell.length <= 1) return;
|
|
1699
|
+
matchesCell.forEach(([_cell, cellPath]) => {
|
|
1700
|
+
editor.tf.setNodes({ [key]: value }, {
|
|
1701
|
+
at: cellPath,
|
|
1702
|
+
split: true,
|
|
1703
|
+
voids: true,
|
|
1704
|
+
match: (n) => TextApi.isText(n)
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
return true;
|
|
1708
|
+
};
|
|
1709
|
+
if (apply()) return;
|
|
1710
|
+
return addMark(key, value);
|
|
1711
|
+
},
|
|
1712
|
+
removeMark(key) {
|
|
1713
|
+
const apply = () => {
|
|
1714
|
+
const { selection } = editor;
|
|
1715
|
+
if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
|
|
1716
|
+
const matchesCell = getTableGridAbove(editor, { format: "cell" });
|
|
1717
|
+
if (matchesCell.length <= 1) return;
|
|
1718
|
+
matchesCell.forEach(([_cell, cellPath]) => {
|
|
1719
|
+
editor.tf.setNodes({ [key]: null }, {
|
|
1720
|
+
at: cellPath,
|
|
1721
|
+
split: true,
|
|
1722
|
+
voids: true,
|
|
1723
|
+
match: (n) => TextApi.isText(n)
|
|
1724
|
+
});
|
|
1725
|
+
});
|
|
1726
|
+
return true;
|
|
1727
|
+
};
|
|
1728
|
+
if (apply()) return;
|
|
1729
|
+
return removeMark(key);
|
|
1730
|
+
},
|
|
1731
|
+
setNodes(props, options) {
|
|
1732
|
+
const apply = () => {
|
|
1733
|
+
const { selection } = editor;
|
|
1734
|
+
if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
|
|
1735
|
+
if (options?.at) {
|
|
1736
|
+
const range = editor.api.range(options.at);
|
|
1737
|
+
if (range && !RangeApi.includes(selection, range)) return;
|
|
1738
|
+
}
|
|
1739
|
+
const matchesCell = getTableGridAbove(editor, { format: "cell" });
|
|
1740
|
+
if (matchesCell.length <= 1) return;
|
|
1741
|
+
setNodes(props, {
|
|
1742
|
+
...options,
|
|
1743
|
+
match: combineTransformMatchOptions(editor, (_, p) => matchesCell.some(([_$1, cellPath]) => PathApi.isCommon(cellPath, p)), options)
|
|
1744
|
+
});
|
|
1745
|
+
return true;
|
|
1746
|
+
};
|
|
1747
|
+
if (apply()) return;
|
|
1748
|
+
return setNodes(props, options);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
};
|
|
1753
|
+
const withTable = (ctx) => {
|
|
1754
|
+
const { editor, tf: { selectAll, tab }, type } = ctx;
|
|
1755
|
+
const cellSelection = withTableCellSelection(ctx);
|
|
1756
|
+
return {
|
|
1757
|
+
api: {
|
|
1758
|
+
...withGetFragmentTable(ctx).api,
|
|
1759
|
+
...cellSelection.api
|
|
1760
|
+
},
|
|
1761
|
+
transforms: {
|
|
1762
|
+
selectAll: () => {
|
|
1763
|
+
const apply = () => {
|
|
1764
|
+
const table = editor.api.above({ match: { type } });
|
|
1765
|
+
if (!table) return;
|
|
1766
|
+
const [, tablePath] = table;
|
|
1767
|
+
editor.tf.select(tablePath);
|
|
1768
|
+
return true;
|
|
1769
|
+
};
|
|
1770
|
+
if (apply()) return true;
|
|
1771
|
+
return selectAll();
|
|
1772
|
+
},
|
|
1773
|
+
tab: (options) => {
|
|
1774
|
+
const apply = () => {
|
|
1775
|
+
if (editor.selection && editor.api.isExpanded()) {
|
|
1776
|
+
if (Array.from(editor.api.nodes({
|
|
1777
|
+
at: editor.selection,
|
|
1778
|
+
match: { type: getCellTypes(editor) }
|
|
1779
|
+
})).length > 1) {
|
|
1780
|
+
editor.tf.collapse({ edge: "end" });
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const entries = getTableEntries(editor);
|
|
1785
|
+
if (!entries) return;
|
|
1786
|
+
const { cell, row } = entries;
|
|
1787
|
+
const [, cellPath] = cell;
|
|
1788
|
+
if (options.reverse) {
|
|
1789
|
+
const previousCell = getPreviousTableCell(editor, cell, cellPath, row);
|
|
1790
|
+
if (previousCell) {
|
|
1791
|
+
const [, previousCellPath] = previousCell;
|
|
1792
|
+
editor.tf.select(previousCellPath);
|
|
1793
|
+
}
|
|
1794
|
+
} else {
|
|
1795
|
+
const nextCell = getNextTableCell(editor, cell, cellPath, row);
|
|
1796
|
+
if (nextCell) {
|
|
1797
|
+
const [, nextCellPath] = nextCell;
|
|
1798
|
+
editor.tf.select(nextCellPath);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
return true;
|
|
1802
|
+
};
|
|
1803
|
+
if (apply()) return true;
|
|
1804
|
+
return tab(options);
|
|
1805
|
+
},
|
|
1806
|
+
...withNormalizeTable(ctx).transforms,
|
|
1807
|
+
...withDeleteTable(ctx).transforms,
|
|
1808
|
+
...withInsertFragmentTable(ctx).transforms,
|
|
1809
|
+
...withInsertTextTable(ctx).transforms,
|
|
1810
|
+
...withApplyTable(ctx).transforms,
|
|
1811
|
+
...withSetFragmentDataTable(ctx).transforms,
|
|
1812
|
+
...cellSelection.transforms
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
};
|
|
1816
|
+
const parse = ({ element, type }) => {
|
|
1817
|
+
const background = element.style.background || element.style.backgroundColor;
|
|
1818
|
+
if (background) return {
|
|
1819
|
+
background,
|
|
1820
|
+
type
|
|
1821
|
+
};
|
|
1822
|
+
return { type };
|
|
1823
|
+
};
|
|
1824
|
+
const BaseTableRowPlugin = createSlatePlugin({
|
|
1825
|
+
key: KEYS.tr,
|
|
1826
|
+
node: {
|
|
1827
|
+
isContainer: true,
|
|
1828
|
+
isElement: true,
|
|
1829
|
+
isStrictSiblings: true
|
|
1830
|
+
},
|
|
1831
|
+
parsers: { html: { deserializer: { rules: [{ validNodeName: "TR" }] } } }
|
|
1832
|
+
});
|
|
1833
|
+
const BaseTableCellPlugin = createSlatePlugin({
|
|
1834
|
+
key: KEYS.td,
|
|
1835
|
+
node: {
|
|
1836
|
+
dangerouslyAllowAttributes: ["colspan", "rowspan"],
|
|
1837
|
+
isContainer: true,
|
|
1838
|
+
isElement: true,
|
|
1839
|
+
isStrictSiblings: true,
|
|
1840
|
+
props: ({ element }) => ({
|
|
1841
|
+
colSpan: element?.attributes?.colspan,
|
|
1842
|
+
rowSpan: element?.attributes?.rowspan
|
|
1843
|
+
})
|
|
1844
|
+
},
|
|
1845
|
+
parsers: { html: { deserializer: {
|
|
1846
|
+
attributeNames: ["rowspan", "colspan"],
|
|
1847
|
+
parse,
|
|
1848
|
+
rules: [{ validNodeName: "TD" }]
|
|
1849
|
+
} } },
|
|
1850
|
+
rules: { merge: { removeEmpty: false } }
|
|
1851
|
+
});
|
|
1852
|
+
const BaseTableCellHeaderPlugin = createSlatePlugin({
|
|
1853
|
+
key: KEYS.th,
|
|
1854
|
+
node: {
|
|
1855
|
+
dangerouslyAllowAttributes: ["colspan", "rowspan"],
|
|
1856
|
+
isContainer: true,
|
|
1857
|
+
isElement: true,
|
|
1858
|
+
isStrictSiblings: true,
|
|
1859
|
+
props: ({ element }) => ({
|
|
1860
|
+
colSpan: element?.attributes?.colspan,
|
|
1861
|
+
rowSpan: element?.attributes?.rowspan
|
|
1862
|
+
})
|
|
1863
|
+
},
|
|
1864
|
+
parsers: { html: { deserializer: {
|
|
1865
|
+
attributeNames: ["rowspan", "colspan"],
|
|
1866
|
+
parse,
|
|
1867
|
+
rules: [{ validNodeName: "TH" }]
|
|
1868
|
+
} } },
|
|
1869
|
+
rules: { merge: { removeEmpty: false } }
|
|
1870
|
+
});
|
|
1871
|
+
/** Enables support for tables. */
|
|
1872
|
+
const BaseTablePlugin = createTSlatePlugin({
|
|
1873
|
+
key: KEYS.table,
|
|
1874
|
+
node: {
|
|
1875
|
+
isContainer: true,
|
|
1876
|
+
isElement: true
|
|
1877
|
+
},
|
|
1878
|
+
normalizeInitialValue: normalizeInitialValueTable,
|
|
1879
|
+
options: {
|
|
1880
|
+
_cellIndices: {},
|
|
1881
|
+
disableMerge: false,
|
|
1882
|
+
minColumnWidth: 48,
|
|
1883
|
+
selectedCells: null,
|
|
1884
|
+
selectedTables: null
|
|
1885
|
+
},
|
|
1886
|
+
parsers: { html: { deserializer: { rules: [{ validNodeName: "TABLE" }] } } },
|
|
1887
|
+
plugins: [
|
|
1888
|
+
BaseTableRowPlugin,
|
|
1889
|
+
BaseTableCellPlugin,
|
|
1890
|
+
BaseTableCellHeaderPlugin
|
|
1891
|
+
]
|
|
1892
|
+
}).extendSelectors(({ getOptions }) => ({ cellIndices: (id) => getOptions()._cellIndices[id] })).extendEditorApi(({ editor }) => ({
|
|
1893
|
+
create: {
|
|
1894
|
+
table: bindFirst(getEmptyTableNode, editor),
|
|
1895
|
+
tableCell: bindFirst(getEmptyCellNode, editor),
|
|
1896
|
+
tableRow: bindFirst(getEmptyRowNode, editor)
|
|
1897
|
+
},
|
|
1898
|
+
table: {
|
|
1899
|
+
getCellBorders: bindFirst(getTableCellBorders, editor),
|
|
1900
|
+
getCellSize: bindFirst(getTableCellSize, editor),
|
|
1901
|
+
getColSpan,
|
|
1902
|
+
getRowSpan,
|
|
1903
|
+
getCellChildren: (cell) => cell.children
|
|
1904
|
+
}
|
|
1905
|
+
})).extendEditorTransforms(({ editor }) => ({
|
|
1906
|
+
insert: {
|
|
1907
|
+
table: bindFirst(insertTable, editor),
|
|
1908
|
+
tableColumn: bindFirst(insertTableColumn, editor),
|
|
1909
|
+
tableRow: bindFirst(insertTableRow, editor)
|
|
1910
|
+
},
|
|
1911
|
+
remove: {
|
|
1912
|
+
table: bindFirst(deleteTable, editor),
|
|
1913
|
+
tableColumn: bindFirst(deleteColumn, editor),
|
|
1914
|
+
tableRow: bindFirst(deleteRow, editor)
|
|
1915
|
+
},
|
|
1916
|
+
table: {
|
|
1917
|
+
merge: bindFirst(mergeTableCells, editor),
|
|
1918
|
+
split: bindFirst(splitTableCell, editor)
|
|
1919
|
+
}
|
|
1920
|
+
})).overrideEditor(withTable);
|
|
1921
|
+
const KEY_SHIFT_EDGES = {
|
|
1922
|
+
"shift+down": "bottom",
|
|
1923
|
+
"shift+left": "left",
|
|
1924
|
+
"shift+right": "right",
|
|
1925
|
+
"shift+up": "top"
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
//#endregion
|
|
5
1929
|
//#region src/lib/utils/getMarkdown.ts
|
|
1930
|
+
/**
|
|
1931
|
+
* Serialize table cell content to markdown string. Multiple paragraphs are
|
|
1932
|
+
* joined with <br/>.
|
|
1933
|
+
*/
|
|
1934
|
+
const serializeCellContent = (editor, cell) => {
|
|
1935
|
+
const parts = [];
|
|
1936
|
+
for (const child of cell.children) {
|
|
1937
|
+
const md = serializeMd(editor, { value: [child] }).trim();
|
|
1938
|
+
if (md) parts.push(md);
|
|
1939
|
+
}
|
|
1940
|
+
return parts.join("<br/>");
|
|
1941
|
+
};
|
|
1942
|
+
/**
|
|
1943
|
+
* Serialize a table with selected cells replaced by <CellRef /> placeholders.
|
|
1944
|
+
* Returns the table markdown and a map of cell IDs to their cells.
|
|
1945
|
+
*/
|
|
1946
|
+
const serializeTableWithCellRefs = (editor, table, selectedCellIds) => {
|
|
1947
|
+
const rows = [];
|
|
1948
|
+
const selectedCells = [];
|
|
1949
|
+
let headerSeparator = "";
|
|
1950
|
+
for (let rowIdx = 0; rowIdx < table.children.length; rowIdx++) {
|
|
1951
|
+
const row = table.children[rowIdx];
|
|
1952
|
+
const cellTexts = [];
|
|
1953
|
+
for (const cell of row.children) {
|
|
1954
|
+
const cellId = cell.id;
|
|
1955
|
+
if (cellId && selectedCellIds.has(cellId)) {
|
|
1956
|
+
cellTexts.push(`<CellRef id="${cellId}" />`);
|
|
1957
|
+
selectedCells.push({
|
|
1958
|
+
cell,
|
|
1959
|
+
id: cellId
|
|
1960
|
+
});
|
|
1961
|
+
} else {
|
|
1962
|
+
const content = serializeCellContent(editor, cell);
|
|
1963
|
+
cellTexts.push(content);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
rows.push(`| ${cellTexts.join(" | ")} |`);
|
|
1967
|
+
if (rowIdx === 0) headerSeparator = `| ${cellTexts.map(() => "---").join(" | ")} |`;
|
|
1968
|
+
}
|
|
1969
|
+
if (rows.length > 0 && headerSeparator) rows.splice(1, 0, headerSeparator);
|
|
1970
|
+
return {
|
|
1971
|
+
selectedCells,
|
|
1972
|
+
tableMarkdown: `${rows.join("\n")}\n`
|
|
1973
|
+
};
|
|
1974
|
+
};
|
|
1975
|
+
/**
|
|
1976
|
+
* Serialize cell contents as separate Cell blocks. Each cell's content can
|
|
1977
|
+
* contain multiple blocks (paragraphs, lists, etc.) since it's outside the
|
|
1978
|
+
* table markdown structure.
|
|
1979
|
+
*/
|
|
1980
|
+
const serializeCellBlocks = (editor, cells) => {
|
|
1981
|
+
const blocks = [];
|
|
1982
|
+
for (const { cell, id } of cells) {
|
|
1983
|
+
const content = serializeMd(editor, { value: cell.children }).trim();
|
|
1984
|
+
blocks.push(`<Cell id="${id}">\n${content}\n</Cell>`);
|
|
1985
|
+
}
|
|
1986
|
+
return blocks.join("\n\n");
|
|
1987
|
+
};
|
|
6
1988
|
const getMarkdown = (editor, { type }) => {
|
|
7
1989
|
if (type === "editor" || type === "editorWithBlockId") return serializeMd(editor, { withBlockId: type === "editorWithBlockId" });
|
|
8
1990
|
if (type === "block" || type === "blockWithBlockId") return serializeMd(editor, {
|
|
9
|
-
value: editor.api.blocks({ mode: "
|
|
1991
|
+
value: editor.api.blocks({ mode: "lowest" }).map(([node]) => node),
|
|
10
1992
|
withBlockId: type === "blockWithBlockId"
|
|
11
1993
|
});
|
|
12
1994
|
if (type === "blockSelection" || type === "blockSelectionWithBlockId") {
|
|
@@ -23,6 +2005,23 @@ const getMarkdown = (editor, { type }) => {
|
|
|
23
2005
|
withBlockId: type === "blockSelectionWithBlockId"
|
|
24
2006
|
});
|
|
25
2007
|
}
|
|
2008
|
+
if (type === "tableCellWithId") {
|
|
2009
|
+
const cellEntries = getTableGridAbove(editor, { format: "cell" });
|
|
2010
|
+
if (cellEntries.length === 0) return "";
|
|
2011
|
+
const selectedCellIds = /* @__PURE__ */ new Set();
|
|
2012
|
+
for (const [cell] of cellEntries) {
|
|
2013
|
+
const cellId = cell.id;
|
|
2014
|
+
if (cellId) selectedCellIds.add(cellId);
|
|
2015
|
+
}
|
|
2016
|
+
const tableEntry = editor.api.block({
|
|
2017
|
+
at: editor.selection,
|
|
2018
|
+
match: { type: KEYS.table }
|
|
2019
|
+
});
|
|
2020
|
+
if (!tableEntry) return "";
|
|
2021
|
+
const table = tableEntry[0];
|
|
2022
|
+
const { selectedCells, tableMarkdown } = serializeTableWithCellRefs(editor, table, selectedCellIds);
|
|
2023
|
+
return `${tableMarkdown}\n${serializeCellBlocks(editor, selectedCells)}`;
|
|
2024
|
+
}
|
|
26
2025
|
return "";
|
|
27
2026
|
};
|
|
28
2027
|
|
|
@@ -36,7 +2035,8 @@ const replacePlaceholders = (editor, text, { prompt } = {}) => {
|
|
|
36
2035
|
"{blockWithBlockId}": "blockWithBlockId",
|
|
37
2036
|
"{block}": "block",
|
|
38
2037
|
"{editorWithBlockId}": "editorWithBlockId",
|
|
39
|
-
"{editor}": "editor"
|
|
2038
|
+
"{editor}": "editor",
|
|
2039
|
+
"{tableCellWithId}": "tableCellWithId"
|
|
40
2040
|
}).forEach(([placeholder, type]) => {
|
|
41
2041
|
if (result.includes(placeholder)) result = result.replace(placeholder, getMarkdown(editor, { type }));
|
|
42
2042
|
});
|