@platejs/ai 52.1.0 → 52.3.8

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.js CHANGED
@@ -1,1931 +1,8 @@
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 { ElementApi, KEYS, NodeApi, PathApi, RangeApi, TextApi, bindFirst, combineTransformMatchOptions, createSlatePlugin, createTSlatePlugin, getEditorPlugin, getPluginTypes, isHotkey } from "platejs";
2
+ import { KEYS } from "platejs";
3
3
  import { serializeMd } from "@platejs/markdown";
4
- import cloneDeep from "lodash/cloneDeep.js";
4
+ import { getTableGridAbove } from "@platejs/table";
5
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
1929
6
  //#region src/lib/utils/getMarkdown.ts
1930
7
  /**
1931
8
  * Serialize table cell content to markdown string. Multiple paragraphs are
@@ -2028,7 +105,7 @@ const getMarkdown = (editor, { type }) => {
2028
105
  //#endregion
2029
106
  //#region src/lib/utils/replacePlaceholders.ts
2030
107
  const replacePlaceholders = (editor, text, { prompt } = {}) => {
2031
- let result = text.replace("{prompt}", prompt || "");
108
+ let result = text.split("{prompt}").join(prompt || "");
2032
109
  Object.entries({
2033
110
  "{blockSelectionWithBlockId}": "blockSelectionWithBlockId",
2034
111
  "{blockSelection}": "blockSelection",
@@ -2038,7 +115,7 @@ const replacePlaceholders = (editor, text, { prompt } = {}) => {
2038
115
  "{editor}": "editor",
2039
116
  "{tableCellWithId}": "tableCellWithId"
2040
117
  }).forEach(([placeholder, type]) => {
2041
- if (result.includes(placeholder)) result = result.replace(placeholder, getMarkdown(editor, { type }));
118
+ if (result.includes(placeholder)) result = result.split(placeholder).join(getMarkdown(editor, { type }));
2042
119
  });
2043
120
  return result;
2044
121
  };