@prosekit/extensions 0.9.2 → 0.10.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.
@@ -0,0 +1,734 @@
1
+ import { defaultBlockAt, defineCommands, defineNodeSpec, definePlugin, findParentNode, getNodeType, insertNode, union } from "@prosekit/core";
2
+ import { TextSelection } from "@prosekit/pm/state";
3
+ import { CellSelection, TableMap, addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, cellAround, cellNear, columnResizing, deleteCellSelection, deleteColumn, deleteRow, deleteTable, inSameTable, mergeCells, splitCell, tableEditing, tableNodes } from "prosemirror-tables";
4
+
5
+ //#region src/table/table-utils/convert-array-of-rows-to-table-node.ts
6
+ /**
7
+ * Convert an array of rows to a table node.
8
+ *
9
+ * @internal
10
+ */
11
+ function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
12
+ const rowsPM = [];
13
+ const map = TableMap.get(tableNode);
14
+ for (let rowIndex = 0; rowIndex < map.height; rowIndex++) {
15
+ const row = tableNode.child(rowIndex);
16
+ const rowCells = [];
17
+ for (let colIndex = 0; colIndex < map.width; colIndex++) {
18
+ if (!arrayOfNodes[rowIndex][colIndex]) continue;
19
+ const cellPos = map.map[rowIndex * map.width + colIndex];
20
+ const cell = arrayOfNodes[rowIndex][colIndex];
21
+ const oldCell = tableNode.nodeAt(cellPos);
22
+ const newCell = oldCell.type.createChecked(Object.assign({}, cell.attrs), cell.content, cell.marks);
23
+ rowCells.push(newCell);
24
+ }
25
+ rowsPM.push(row.type.createChecked(row.attrs, rowCells, row.marks));
26
+ }
27
+ const newTable = tableNode.type.createChecked(tableNode.attrs, rowsPM, tableNode.marks);
28
+ return newTable;
29
+ }
30
+
31
+ //#endregion
32
+ //#region src/table/table-utils/convert-table-node-to-array-of-rows.ts
33
+ /**
34
+ * This function will transform the table node into a matrix of rows and columns
35
+ * respecting merged cells, for example this table:
36
+ *
37
+ * ```
38
+ * ┌──────┬──────┬─────────────┐
39
+ * │ A1 │ B1 │ C1 │
40
+ * ├──────┼──────┴──────┬──────┤
41
+ * │ A2 │ B2 │ │
42
+ * ├──────┼─────────────┤ D1 │
43
+ * │ A3 │ B3 │ C3 │ │
44
+ * └──────┴──────┴──────┴──────┘
45
+ * ```
46
+ *
47
+ * will be converted to the below:
48
+ *
49
+ * ```javascript
50
+ * [
51
+ * [A1, B1, C1, null],
52
+ * [A2, B2, null, D1],
53
+ * [A3, B3, C3, null],
54
+ * ]
55
+ * ```
56
+ * @internal
57
+ */
58
+ function convertTableNodeToArrayOfRows(tableNode) {
59
+ const map = TableMap.get(tableNode);
60
+ const rows = [];
61
+ const rowCount = map.height;
62
+ const colCount = map.width;
63
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
64
+ const row = [];
65
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
66
+ let cellIndex = rowIndex * colCount + colIndex;
67
+ let cellPos = map.map[cellIndex];
68
+ if (rowIndex > 0) {
69
+ const topCellIndex = cellIndex - colCount;
70
+ const topCellPos = map.map[topCellIndex];
71
+ if (cellPos === topCellPos) {
72
+ row.push(null);
73
+ continue;
74
+ }
75
+ }
76
+ if (colIndex > 0) {
77
+ const leftCellIndex = cellIndex - 1;
78
+ const leftCellPos = map.map[leftCellIndex];
79
+ if (cellPos === leftCellPos) {
80
+ row.push(null);
81
+ continue;
82
+ }
83
+ }
84
+ row.push(tableNode.nodeAt(cellPos));
85
+ }
86
+ rows.push(row);
87
+ }
88
+ return rows;
89
+ }
90
+
91
+ //#endregion
92
+ //#region src/table/table-utils/query.ts
93
+ /**
94
+ * Checks if the given object is a `CellSelection` instance.
95
+ *
96
+ * @public
97
+ */
98
+ function isCellSelection(value) {
99
+ return value instanceof CellSelection;
100
+ }
101
+ /**
102
+ * Find the closest table node.
103
+ *
104
+ * @internal
105
+ */
106
+ function findTable($pos) {
107
+ return findParentNode((node) => node.type.spec.tableRole === "table", $pos);
108
+ }
109
+ /**
110
+ * Try to find the anchor and head cell in the same table by using the given
111
+ * anchor and head as hit points, or fallback to the selection's anchor and
112
+ * head.
113
+ *
114
+ * @internal
115
+ */
116
+ function findCellRange(selection, anchorHit, headHit) {
117
+ if (anchorHit == null && headHit == null && isCellSelection(selection)) return [selection.$anchorCell, selection.$headCell];
118
+ const anchor = anchorHit ?? headHit ?? selection.anchor;
119
+ const head = headHit ?? anchorHit ?? selection.head;
120
+ const doc = selection.$head.doc;
121
+ const $anchorCell = findCellPos(doc, anchor);
122
+ const $headCell = findCellPos(doc, head);
123
+ if ($anchorCell && $headCell && inSameTable($anchorCell, $headCell)) return [$anchorCell, $headCell];
124
+ }
125
+ /**
126
+ * Try to find a resolved pos of a cell by using the given pos as a hit point.
127
+ *
128
+ * @internal
129
+ */
130
+ function findCellPos(doc, pos) {
131
+ const $pos = doc.resolve(pos);
132
+ return cellAround($pos) || cellNear($pos);
133
+ }
134
+
135
+ //#endregion
136
+ //#region src/table/table-utils/get-cells-in-column.ts
137
+ /**
138
+ * Returns an array of cells in a column(s), where `columnIndex` could be a column index or an array of column indexes.
139
+ *
140
+ * @internal
141
+ */
142
+ function getCellsInColumn(columnIndexes, selection) {
143
+ const table = findTable(selection.$from);
144
+ if (!table) return;
145
+ const map = TableMap.get(table.node);
146
+ const indexes = Array.isArray(columnIndexes) ? columnIndexes : [columnIndexes];
147
+ return indexes.filter((index) => index >= 0 && index <= map.width - 1).flatMap((index) => {
148
+ const cells = map.cellsInRect({
149
+ left: index,
150
+ right: index + 1,
151
+ top: 0,
152
+ bottom: map.height
153
+ });
154
+ return cells.map((nodePos) => {
155
+ const node = table.node.nodeAt(nodePos);
156
+ const pos = nodePos + table.start;
157
+ return {
158
+ pos,
159
+ start: pos + 1,
160
+ node,
161
+ depth: table.depth + 2
162
+ };
163
+ });
164
+ });
165
+ }
166
+
167
+ //#endregion
168
+ //#region src/table/table-utils/get-cells-in-row.ts
169
+ /**
170
+ * Returns an array of cells in a row(s), where `rowIndex` could be a row index or an array of row indexes.
171
+ *
172
+ * @internal
173
+ */
174
+ function getCellsInRow(rowIndex, selection) {
175
+ const table = findTable(selection.$from);
176
+ if (!table) return;
177
+ const map = TableMap.get(table.node);
178
+ const indexes = Array.isArray(rowIndex) ? rowIndex : [rowIndex];
179
+ return indexes.filter((index) => index >= 0 && index <= map.height - 1).flatMap((index) => {
180
+ const cells = map.cellsInRect({
181
+ left: 0,
182
+ right: map.width,
183
+ top: index,
184
+ bottom: index + 1
185
+ });
186
+ return cells.map((nodePos) => {
187
+ const node = table.node.nodeAt(nodePos);
188
+ const pos = nodePos + table.start;
189
+ return {
190
+ pos,
191
+ start: pos + 1,
192
+ node,
193
+ depth: table.depth + 2
194
+ };
195
+ });
196
+ });
197
+ }
198
+
199
+ //#endregion
200
+ //#region src/table/table-utils/get-selection-range-in-column.ts
201
+ /**
202
+ * Returns a range of rectangular selection spanning all merged cells around a
203
+ * column at index `columnIndex`.
204
+ *
205
+ * Original implementation from Atlassian (Apache License 2.0)
206
+ *
207
+ * https://bitbucket.org/atlassian/atlassian-frontend-mirror/src/5f91cb871e8248bc3bae5ddc30bb9fd9200fadbb/editor/editor-tables/src/utils/get-selection-range-in-column.ts#editor/editor-tables/src/utils/get-selection-range-in-column.ts
208
+ *
209
+ * @internal
210
+ */
211
+ function getSelectionRangeInColumn(tr, startColIndex, endColIndex = startColIndex) {
212
+ let startIndex = startColIndex;
213
+ let endIndex = endColIndex;
214
+ for (let i = startColIndex; i >= 0; i--) {
215
+ const cells = getCellsInColumn(i, tr.selection);
216
+ if (cells) cells.forEach((cell) => {
217
+ const maybeEndIndex = cell.node.attrs.colspan + i - 1;
218
+ if (maybeEndIndex >= startIndex) startIndex = i;
219
+ if (maybeEndIndex > endIndex) endIndex = maybeEndIndex;
220
+ });
221
+ }
222
+ for (let i = startColIndex; i <= endIndex; i++) {
223
+ const cells = getCellsInColumn(i, tr.selection);
224
+ if (cells) cells.forEach((cell) => {
225
+ const maybeEndIndex = cell.node.attrs.colspan + i - 1;
226
+ if (cell.node.attrs.colspan > 1 && maybeEndIndex > endIndex) endIndex = maybeEndIndex;
227
+ });
228
+ }
229
+ const indexes = [];
230
+ for (let i = startIndex; i <= endIndex; i++) {
231
+ const maybeCells = getCellsInColumn(i, tr.selection);
232
+ if (maybeCells && maybeCells.length > 0) indexes.push(i);
233
+ }
234
+ startIndex = indexes[0];
235
+ endIndex = indexes[indexes.length - 1];
236
+ const firstSelectedColumnCells = getCellsInColumn(startIndex, tr.selection);
237
+ const firstRowCells = getCellsInRow(0, tr.selection);
238
+ if (!firstSelectedColumnCells || !firstRowCells) return;
239
+ const $anchor = tr.doc.resolve(firstSelectedColumnCells[firstSelectedColumnCells.length - 1].pos);
240
+ let headCell;
241
+ for (let i = endIndex; i >= startIndex; i--) {
242
+ const columnCells = getCellsInColumn(i, tr.selection);
243
+ if (columnCells && columnCells.length > 0) {
244
+ for (let j = firstRowCells.length - 1; j >= 0; j--) if (firstRowCells[j].pos === columnCells[0].pos) {
245
+ headCell = columnCells[0];
246
+ break;
247
+ }
248
+ if (headCell) break;
249
+ }
250
+ }
251
+ if (!headCell) return;
252
+ const $head = tr.doc.resolve(headCell.pos);
253
+ return {
254
+ $anchor,
255
+ $head,
256
+ indexes
257
+ };
258
+ }
259
+
260
+ //#endregion
261
+ //#region src/table/table-utils/move-row-in-array-of-rows.ts
262
+ /**
263
+ * Move a row in an array of rows.
264
+ *
265
+ * @internal
266
+ */
267
+ function moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, directionOverride) {
268
+ const direction = indexesOrigin[0] > indexesTarget[0] ? -1 : 1;
269
+ const rowsExtracted = rows.splice(indexesOrigin[0], indexesOrigin.length);
270
+ const positionOffset = rowsExtracted.length % 2 === 0 ? 1 : 0;
271
+ let target;
272
+ if (directionOverride === -1 && direction === 1) target = indexesTarget[0] - 1;
273
+ else if (directionOverride === 1 && direction === -1) target = indexesTarget[indexesTarget.length - 1] - positionOffset + 1;
274
+ else target = direction === -1 ? indexesTarget[0] : indexesTarget[indexesTarget.length - 1] - positionOffset;
275
+ rows.splice(target, 0, ...rowsExtracted);
276
+ return rows;
277
+ }
278
+
279
+ //#endregion
280
+ //#region src/table/table-utils/transpose.ts
281
+ /**
282
+ * Transposes a 2D array by flipping columns to rows.
283
+ *
284
+ * Transposition is a familiar algebra concept where the matrix is flipped
285
+ * along its diagonal. For more details, see:
286
+ * https://en.wikipedia.org/wiki/Transpose
287
+ *
288
+ * @example
289
+ * ```javascript
290
+ * const arr = [
291
+ * ['a1', 'a2', 'a3'],
292
+ * ['b1', 'b2', 'b3'],
293
+ * ['c1', 'c2', 'c3'],
294
+ * ['d1', 'd2', 'd3'],
295
+ * ];
296
+ *
297
+ * const result = transpose(arr);
298
+ * result === [
299
+ * ['a1', 'b1', 'c1', 'd1'],
300
+ * ['a2', 'b2', 'c2', 'd2'],
301
+ * ['a3', 'b3', 'c3', 'd3'],
302
+ * ]
303
+ * ```
304
+ */
305
+ function transpose(array) {
306
+ return array[0].map((_, i) => {
307
+ return array.map((column) => column[i]);
308
+ });
309
+ }
310
+
311
+ //#endregion
312
+ //#region src/table/table-utils/move-column.ts
313
+ /**
314
+ * Move a column from index `origin` to index `target`.
315
+ *
316
+ * @internal
317
+ */
318
+ function moveColumn(moveColParams) {
319
+ const { tr, originIndex, targetIndex, select, pos } = moveColParams;
320
+ const $pos = tr.doc.resolve(pos);
321
+ const table = findTable($pos);
322
+ if (!table) return false;
323
+ const indexesOriginColumn = getSelectionRangeInColumn(tr, originIndex)?.indexes;
324
+ const indexesTargetColumn = getSelectionRangeInColumn(tr, targetIndex)?.indexes;
325
+ if (!indexesOriginColumn || !indexesTargetColumn) return false;
326
+ if (indexesOriginColumn.includes(targetIndex)) return false;
327
+ const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
328
+ tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
329
+ if (!select) return true;
330
+ const map = TableMap.get(newTable);
331
+ const start = table.start;
332
+ const index = targetIndex;
333
+ const lastCell = map.positionAt(map.height - 1, index, newTable);
334
+ const $lastCell = tr.doc.resolve(start + lastCell);
335
+ const firstCell = map.positionAt(0, index, newTable);
336
+ const $firstCell = tr.doc.resolve(start + firstCell);
337
+ tr.setSelection(CellSelection.colSelection($lastCell, $firstCell));
338
+ return true;
339
+ }
340
+ function moveTableColumn$1(table, indexesOrigin, indexesTarget, direction) {
341
+ let rows = transpose(convertTableNodeToArrayOfRows(table));
342
+ rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
343
+ rows = transpose(rows);
344
+ return convertArrayOfRowsToTableNode(table, rows);
345
+ }
346
+
347
+ //#endregion
348
+ //#region src/table/table-utils/get-selection-range-in-row.ts
349
+ /**
350
+ * Returns a range of rectangular selection spanning all merged cells around a
351
+ * row at index `rowIndex`.
352
+ *
353
+ * Original implementation from Atlassian (Apache License 2.0)
354
+ *
355
+ * https://bitbucket.org/atlassian/atlassian-frontend-mirror/src/5f91cb871e8248bc3bae5ddc30bb9fd9200fadbb/editor/editor-tables/src/utils/get-selection-range-in-row.ts#editor/editor-tables/src/utils/get-selection-range-in-row.ts
356
+ *
357
+ * @internal
358
+ */
359
+ function getSelectionRangeInRow(tr, startRowIndex, endRowIndex = startRowIndex) {
360
+ let startIndex = startRowIndex;
361
+ let endIndex = endRowIndex;
362
+ for (let i = startRowIndex; i >= 0; i--) {
363
+ const cells = getCellsInRow(i, tr.selection);
364
+ if (cells) cells.forEach((cell) => {
365
+ const maybeEndIndex = cell.node.attrs.rowspan + i - 1;
366
+ if (maybeEndIndex >= startIndex) startIndex = i;
367
+ if (maybeEndIndex > endIndex) endIndex = maybeEndIndex;
368
+ });
369
+ }
370
+ for (let i = startRowIndex; i <= endIndex; i++) {
371
+ const cells = getCellsInRow(i, tr.selection);
372
+ if (cells) cells.forEach((cell) => {
373
+ const maybeEndIndex = cell.node.attrs.rowspan + i - 1;
374
+ if (cell.node.attrs.rowspan > 1 && maybeEndIndex > endIndex) endIndex = maybeEndIndex;
375
+ });
376
+ }
377
+ const indexes = [];
378
+ for (let i = startIndex; i <= endIndex; i++) {
379
+ const maybeCells = getCellsInRow(i, tr.selection);
380
+ if (maybeCells && maybeCells.length > 0) indexes.push(i);
381
+ }
382
+ startIndex = indexes[0];
383
+ endIndex = indexes[indexes.length - 1];
384
+ const firstSelectedRowCells = getCellsInRow(startIndex, tr.selection);
385
+ const firstColumnCells = getCellsInColumn(0, tr.selection);
386
+ if (!firstSelectedRowCells || !firstColumnCells) return;
387
+ const $anchor = tr.doc.resolve(firstSelectedRowCells[firstSelectedRowCells.length - 1].pos);
388
+ let headCell;
389
+ for (let i = endIndex; i >= startIndex; i--) {
390
+ const rowCells = getCellsInRow(i, tr.selection);
391
+ if (rowCells && rowCells.length > 0) {
392
+ for (let j = firstColumnCells.length - 1; j >= 0; j--) if (firstColumnCells[j].pos === rowCells[0].pos) {
393
+ headCell = rowCells[0];
394
+ break;
395
+ }
396
+ if (headCell) break;
397
+ }
398
+ }
399
+ if (!headCell) return;
400
+ const $head = tr.doc.resolve(headCell.pos);
401
+ return {
402
+ $anchor,
403
+ $head,
404
+ indexes
405
+ };
406
+ }
407
+
408
+ //#endregion
409
+ //#region src/table/table-utils/move-row.ts
410
+ /**
411
+ * Move a row from index `origin` to index `target`.
412
+ *
413
+ * @internal
414
+ */
415
+ function moveRow(moveRowParams) {
416
+ const { tr, originIndex, targetIndex, select, pos } = moveRowParams;
417
+ const $pos = tr.doc.resolve(pos);
418
+ const table = findTable($pos);
419
+ if (!table) return false;
420
+ const indexesOriginRow = getSelectionRangeInRow(tr, originIndex)?.indexes;
421
+ const indexesTargetRow = getSelectionRangeInRow(tr, targetIndex)?.indexes;
422
+ if (!indexesOriginRow || !indexesTargetRow) return false;
423
+ if (indexesOriginRow.includes(targetIndex)) return false;
424
+ const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
425
+ tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
426
+ if (!select) return true;
427
+ const map = TableMap.get(newTable);
428
+ const start = table.start;
429
+ const index = targetIndex;
430
+ const lastCell = map.positionAt(index, map.width - 1, newTable);
431
+ const $lastCell = tr.doc.resolve(start + lastCell);
432
+ const firstCell = map.positionAt(index, 0, newTable);
433
+ const $firstCell = tr.doc.resolve(start + firstCell);
434
+ tr.setSelection(CellSelection.rowSelection($lastCell, $firstCell));
435
+ return true;
436
+ }
437
+ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
438
+ let rows = convertTableNodeToArrayOfRows(table);
439
+ rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
440
+ return convertArrayOfRowsToTableNode(table, rows);
441
+ }
442
+
443
+ //#endregion
444
+ //#region src/table/table-commands/move-table-column.ts
445
+ /**
446
+ * Move a table column from index `origin` to index `target`.
447
+ *
448
+ * @public
449
+ */
450
+ function moveTableColumn(options) {
451
+ return (state, dispatch) => {
452
+ const { from: originIndex, to: targetIndex, select = true, pos = state.selection.from } = options;
453
+ const tr = state.tr;
454
+ if (moveColumn({
455
+ tr,
456
+ originIndex,
457
+ targetIndex,
458
+ select,
459
+ pos
460
+ })) {
461
+ dispatch?.(tr);
462
+ return true;
463
+ }
464
+ return false;
465
+ };
466
+ }
467
+
468
+ //#endregion
469
+ //#region src/table/table-commands/move-table-row.ts
470
+ /**
471
+ * Move a table row from index `origin` to index `target`.
472
+ *
473
+ * @public
474
+ */
475
+ function moveTableRow(options) {
476
+ return (state, dispatch) => {
477
+ const { from: originIndex, to: targetIndex, select = true, pos = state.selection.from } = options;
478
+ const tr = state.tr;
479
+ if (moveRow({
480
+ tr,
481
+ originIndex,
482
+ targetIndex,
483
+ select,
484
+ pos
485
+ })) {
486
+ dispatch?.(tr);
487
+ return true;
488
+ }
489
+ return false;
490
+ };
491
+ }
492
+
493
+ //#endregion
494
+ //#region src/table/table-commands.ts
495
+ function createEmptyTable(schema, row, col, header) {
496
+ const tableType = getNodeType(schema, "table");
497
+ const tableRowType = getNodeType(schema, "tableRow");
498
+ const tableCellType = getNodeType(schema, "tableCell");
499
+ const tableHeaderCellType = getNodeType(schema, "tableHeaderCell");
500
+ if (header) {
501
+ const headerCell = tableHeaderCellType.createAndFill();
502
+ const headerCells = repeat(headerCell, col);
503
+ const headerRow = tableRowType.createAndFill(null, headerCells);
504
+ const bodyCell = tableCellType.createAndFill();
505
+ const bodyCells = repeat(bodyCell, col);
506
+ const bodyRow = tableRowType.createAndFill(null, bodyCells);
507
+ const bodyRows = repeat(bodyRow, row - 1);
508
+ return tableType.createAndFill(null, [headerRow, ...bodyRows]);
509
+ } else {
510
+ const bodyCell = tableCellType.createAndFill();
511
+ const bodyCells = repeat(bodyCell, col);
512
+ const bodyRow = tableRowType.createAndFill(null, bodyCells);
513
+ const bodyRows = repeat(bodyRow, row);
514
+ return tableType.createAndFill(null, bodyRows);
515
+ }
516
+ }
517
+ function repeat(node, length) {
518
+ return Array(length).fill(node);
519
+ }
520
+ /**
521
+ * Insert a table node with the given number of rows and columns, and optionally
522
+ * a header row.
523
+ *
524
+ * @param options
525
+ *
526
+ * @public
527
+ */
528
+ function insertTable(options) {
529
+ return (state, dispatch, view) => {
530
+ const { row, col, header = false } = options;
531
+ const table = createEmptyTable(state.schema, row, col, header);
532
+ return insertNode({ node: table })(state, dispatch, view);
533
+ };
534
+ }
535
+ /**
536
+ * When the selection is in a table node, create a default block after the table
537
+ * table, and move the cursor there.
538
+ *
539
+ * @public
540
+ */
541
+ const exitTable = (state, dispatch) => {
542
+ const { $head, $anchor } = state.selection;
543
+ if (!$head.sameParent($anchor)) return false;
544
+ let tableStart = -1;
545
+ let tableDepth = -1;
546
+ for (let depth = $head.depth; depth >= 0; depth--) {
547
+ const node$1 = $head.node(depth);
548
+ if (node$1.type.spec.tableRole === "table") {
549
+ tableStart = $head.before(depth);
550
+ tableDepth = depth;
551
+ }
552
+ }
553
+ if (tableStart < 0 || tableDepth <= 0) return false;
554
+ const above = $head.node(tableDepth - 1);
555
+ const after = $head.indexAfter(tableDepth - 1);
556
+ const type = defaultBlockAt(above.contentMatchAt(after));
557
+ const node = type?.createAndFill();
558
+ if (!type || !node || !above.canReplaceWith(after, after, type)) return false;
559
+ if (dispatch) {
560
+ const pos = $head.after(tableDepth);
561
+ const tr = state.tr.replaceWith(pos, pos, node);
562
+ tr.setSelection(TextSelection.near(tr.doc.resolve(pos), 1));
563
+ dispatch(tr.scrollIntoView());
564
+ }
565
+ return true;
566
+ };
567
+ /**
568
+ * @public
569
+ */
570
+ function selectTableColumn(options) {
571
+ return (state, dispatch) => {
572
+ const range = findCellRange(state.selection, options?.anchor, options?.head);
573
+ if (!range) return false;
574
+ if (dispatch) {
575
+ const [$anchorCell, $headCell] = range;
576
+ const selection = CellSelection.colSelection($anchorCell, $headCell);
577
+ dispatch(state.tr.setSelection(selection));
578
+ }
579
+ return true;
580
+ };
581
+ }
582
+ /**
583
+ * @public
584
+ */
585
+ function selectTableRow(options) {
586
+ return (state, dispatch) => {
587
+ const range = findCellRange(state.selection, options?.anchor, options?.head);
588
+ if (!range) return false;
589
+ if (dispatch) {
590
+ const [$anchorCell, $headCell] = range;
591
+ const selection = CellSelection.rowSelection($anchorCell, $headCell);
592
+ dispatch(state.tr.setSelection(selection));
593
+ }
594
+ return true;
595
+ };
596
+ }
597
+ /**
598
+ * @public
599
+ */
600
+ function selectTableCell(options) {
601
+ return (state, dispatch) => {
602
+ const $cellPos = findCellPos(state.doc, options?.pos ?? state.selection.anchor);
603
+ if (!$cellPos) return false;
604
+ if (dispatch) {
605
+ const selection = new CellSelection($cellPos);
606
+ dispatch(state.tr.setSelection(selection));
607
+ }
608
+ return true;
609
+ };
610
+ }
611
+ /**
612
+ * @public
613
+ */
614
+ function selectTable(options) {
615
+ return (state, dispatch) => {
616
+ const $pos = options?.pos ? state.doc.resolve(options.pos) : state.selection.$anchor;
617
+ const table = findTable($pos);
618
+ if (!table) return false;
619
+ const map = TableMap.get(table.node);
620
+ if (map.map.length === 0) return false;
621
+ if (dispatch) {
622
+ let tr = state.tr;
623
+ const firstCellPosInTable = map.map[0];
624
+ const lastCellPosInTable = map.map[map.map.length - 1];
625
+ const firstCellPos = table.pos + firstCellPosInTable + 1;
626
+ const lastCellPos = table.pos + lastCellPosInTable + 1;
627
+ const $firstCellPos = tr.doc.resolve(firstCellPos);
628
+ const $lastCellPos = tr.doc.resolve(lastCellPos);
629
+ const selection = new CellSelection($firstCellPos, $lastCellPos);
630
+ tr = tr.setSelection(selection);
631
+ dispatch?.(tr);
632
+ }
633
+ return true;
634
+ };
635
+ }
636
+ /**
637
+ * Adds commands for working with `table` nodes.
638
+ *
639
+ * @public
640
+ */
641
+ function defineTableCommands() {
642
+ return defineCommands({
643
+ insertTable,
644
+ exitTable: () => exitTable,
645
+ selectTable,
646
+ selectTableCell,
647
+ selectTableColumn,
648
+ selectTableRow,
649
+ addTableColumnBefore: () => addColumnBefore,
650
+ addTableColumnAfter: () => addColumnAfter,
651
+ addTableRowAbove: () => addRowBefore,
652
+ addTableRowBelow: () => addRowAfter,
653
+ deleteTable: () => deleteTable,
654
+ deleteTableColumn: () => deleteColumn,
655
+ deleteTableRow: () => deleteRow,
656
+ deleteCellSelection: () => deleteCellSelection,
657
+ mergeTableCells: () => mergeCells,
658
+ splitTableCell: () => splitCell,
659
+ moveTableRow,
660
+ moveTableColumn
661
+ });
662
+ }
663
+
664
+ //#endregion
665
+ //#region src/table/table-plugins.ts
666
+ /**
667
+ * @public
668
+ */
669
+ function defineTablePlugins() {
670
+ return definePlugin([tableEditing(), columnResizing()]);
671
+ }
672
+
673
+ //#endregion
674
+ //#region src/table/table-spec.ts
675
+ const cellContent = "block+";
676
+ const cellAttrs = {
677
+ colspan: { default: 1 },
678
+ rowspan: { default: 1 },
679
+ colwidth: { default: null }
680
+ };
681
+ const specs = tableNodes({
682
+ tableGroup: "block",
683
+ cellContent,
684
+ cellAttributes: {}
685
+ });
686
+ /**
687
+ * @internal
688
+ */
689
+ function defineTableSpec() {
690
+ return defineNodeSpec({
691
+ ...specs["table"],
692
+ content: "tableRow+",
693
+ name: "table"
694
+ });
695
+ }
696
+ /**
697
+ * @internal
698
+ */
699
+ function defineTableRowSpec() {
700
+ return defineNodeSpec({
701
+ ...specs["table_row"],
702
+ content: "(tableCell | tableHeaderCell)*",
703
+ name: "tableRow"
704
+ });
705
+ }
706
+ /**
707
+ * @internal
708
+ */
709
+ function defineTableCellSpec() {
710
+ return defineNodeSpec({
711
+ ...specs["table_cell"],
712
+ name: "tableCell",
713
+ attrs: cellAttrs
714
+ });
715
+ }
716
+ function defineTableHeaderCellSpec() {
717
+ return defineNodeSpec({
718
+ ...specs["table_header"],
719
+ name: "tableHeaderCell",
720
+ attrs: cellAttrs
721
+ });
722
+ }
723
+
724
+ //#endregion
725
+ //#region src/table/table.ts
726
+ /**
727
+ * @public
728
+ */
729
+ function defineTable() {
730
+ return union(defineTableSpec(), defineTableRowSpec(), defineTableCellSpec(), defineTableHeaderCellSpec(), defineTablePlugins(), defineTableCommands());
731
+ }
732
+
733
+ //#endregion
734
+ export { defineTable, defineTableCellSpec, defineTableCommands, defineTableHeaderCellSpec, defineTablePlugins, defineTableRowSpec, defineTableSpec, exitTable, findTable, insertTable, isCellSelection, moveTableColumn, moveTableRow, selectTable, selectTableCell, selectTableColumn, selectTableRow };