@lofcz/platejs-table 52.0.11 → 52.3.6

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.
@@ -1,4 +1,4 @@
1
- import { ElementApi, KEYS, NodeApi, PathApi, PointApi, RangeApi, TextApi, bindFirst, combineTransformMatchOptions, createSlatePlugin, createTSlatePlugin, getEditorPlugin, getPluginTypes, isHotkey } from "platejs";
1
+ import { ElementApi, KEYS, NodeApi, PathApi, PointApi, RangeApi, TextApi, bindFirst, combineTransformMatchOptions, createSlatePlugin, createTSlatePlugin, getEditorPlugin, getPluginTypes } from "platejs";
2
2
  import cloneDeep from "lodash/cloneDeep.js";
3
3
 
4
4
  //#region src/lib/api/getEmptyCellNode.ts
@@ -34,30 +34,6 @@ const getEmptyTableNode = (editor, { colCount, header, rowCount = 0, ...cellOpti
34
34
  };
35
35
  };
36
36
 
37
- //#endregion
38
- //#region src/lib/queries/getCellInNextTableRow.ts
39
- const getCellInNextTableRow = (editor, currentRowPath) => {
40
- const nextRow = editor.api.node(PathApi.next(currentRowPath));
41
- if (!nextRow) return;
42
- const [nextRowNode, nextRowPath] = nextRow;
43
- const nextCell = nextRowNode?.children?.[0];
44
- const nextCellPath = nextRowPath.concat(0);
45
- if (nextCell && nextCellPath) return editor.api.node(nextCellPath);
46
- };
47
-
48
- //#endregion
49
- //#region src/lib/queries/getCellInPreviousTableRow.ts
50
- const getCellInPreviousTableRow = (editor, currentRowPath) => {
51
- const prevPath = PathApi.previous(currentRowPath);
52
- if (!prevPath) return;
53
- const previousRow = editor.api.node(prevPath);
54
- if (!previousRow) return;
55
- const [previousRowNode, previousRowPath] = previousRow;
56
- const previousCell = previousRowNode?.children?.[previousRowNode.children.length - 1];
57
- const previousCellPath = previousRowPath.concat(previousRowNode.children.length - 1);
58
- if (previousCell && previousCellPath) return editor.api.node(previousCellPath);
59
- };
60
-
61
37
  //#endregion
62
38
  //#region src/lib/queries/getColSpan.ts
63
39
  /**
@@ -67,6 +43,22 @@ const getCellInPreviousTableRow = (editor, currentRowPath) => {
67
43
  */
68
44
  const getColSpan = (cellElem) => cellElem.colSpan || Number(cellElem.attributes?.colspan) || 1;
69
45
 
46
+ //#endregion
47
+ //#region src/lib/queries/getRowSpan.ts
48
+ /**
49
+ * Returns the rowspan attribute of the table cell element.
50
+ *
51
+ * @default 1 if undefined
52
+ */
53
+ const getRowSpan = (cellElem) => cellElem.rowSpan || Number(cellElem.attributes?.rowspan) || 1;
54
+
55
+ //#endregion
56
+ //#region src/lib/merge/getCellIndicesWithSpans.ts
57
+ const getCellIndicesWithSpans = ({ col, row }, endCell) => ({
58
+ col: col + getColSpan(endCell) - 1,
59
+ row: row + getRowSpan(endCell) - 1
60
+ });
61
+
70
62
  //#endregion
71
63
  //#region src/lib/utils/computeCellIndices.ts
72
64
  function computeCellIndices(editor, { all, cellNode, tableNode }) {
@@ -142,17 +134,100 @@ const getCellRowIndexByPath = (cellPath) => {
142
134
  const getCellTypes = (editor) => getPluginTypes(editor, [KEYS.td, KEYS.th]);
143
135
 
144
136
  //#endregion
145
- //#region src/lib/queries/getLeftTableCell.ts
146
- const getLeftTableCell = (editor, { at: cellPath } = {}) => {
147
- if (!cellPath) {
148
- cellPath = editor.api.node({ match: { type: getCellTypes(editor) } })?.[1];
149
- if (!cellPath) return;
150
- }
151
- if (!cellPath.at(-1)) return;
152
- const prevCellPath = PathApi.previous(cellPath);
153
- return editor.api.node(prevCellPath);
137
+ //#region src/lib/queries/getTableEntries.ts
138
+ /**
139
+ * If at (default = selection) is in table>tr>td|th, return table, row, and cell
140
+ * node entries.
141
+ */
142
+ const getTableEntries = (editor, { at = editor.selection } = {}) => {
143
+ if (!at) return;
144
+ const cellEntry = editor.api.node({
145
+ at,
146
+ match: { type: getCellTypes(editor) }
147
+ });
148
+ if (!cellEntry) return;
149
+ const [, cellPath] = cellEntry;
150
+ const rowEntry = editor.api.above({
151
+ at: cellPath,
152
+ match: { type: editor.getType(KEYS.tr) }
153
+ });
154
+ if (!rowEntry) return;
155
+ const [, rowPath] = rowEntry;
156
+ const tableEntry = editor.api.above({
157
+ at: rowPath,
158
+ match: { type: editor.getType(KEYS.table) }
159
+ });
160
+ if (!tableEntry) return;
161
+ return {
162
+ cell: cellEntry,
163
+ row: rowEntry,
164
+ table: tableEntry
165
+ };
154
166
  };
155
167
 
168
+ //#endregion
169
+ //#region src/lib/queries/getAdjacentTableCell.ts
170
+ const adjacentTableCellLookup = /* @__PURE__ */ new WeakMap();
171
+ const getLookupKey = (row, col) => `${row}:${col}`;
172
+ const createTableCellLookup = (editor, tableEntry) => {
173
+ const [table, tablePath] = tableEntry;
174
+ const cachedLookup = adjacentTableCellLookup.get(table);
175
+ if (cachedLookup) return cachedLookup;
176
+ const nextLookup = /* @__PURE__ */ new Map();
177
+ table.children.forEach((rowNode, rowIndex) => {
178
+ rowNode.children.forEach((cellNode, cellIndex) => {
179
+ const cellEntry = [cellNode, tablePath.concat([rowIndex, cellIndex])];
180
+ const indices = getCellIndices(editor, cellEntry[0]);
181
+ const { col: endCol, row: endRow } = getCellIndicesWithSpans(indices, cellEntry[0]);
182
+ for (let row = indices.row; row <= endRow; row++) for (let col = indices.col; col <= endCol; col++) nextLookup.set(getLookupKey(row, col), cellEntry);
183
+ });
184
+ });
185
+ adjacentTableCellLookup.set(table, nextLookup);
186
+ return nextLookup;
187
+ };
188
+ const getAdjacentTableCell = (editor, { at, deltaCol = 0, deltaRow = 0 } = {}) => {
189
+ const entries = getTableEntries(editor, { at });
190
+ if (!entries) return;
191
+ const [cell] = entries.cell;
192
+ const tableEntry = entries.table;
193
+ const { col, row } = getCellIndices(editor, cell);
194
+ const nextCol = col + deltaCol;
195
+ const nextRow = row + deltaRow;
196
+ if (nextCol < 0 || nextRow < 0) return;
197
+ return createTableCellLookup(editor, tableEntry).get(getLookupKey(nextRow, nextCol));
198
+ };
199
+
200
+ //#endregion
201
+ //#region src/lib/queries/getCellInNextTableRow.ts
202
+ const getCellInNextTableRow = (editor, currentRowPath) => {
203
+ const nextRow = editor.api.node(PathApi.next(currentRowPath));
204
+ if (!nextRow) return;
205
+ const [nextRowNode, nextRowPath] = nextRow;
206
+ const nextCell = nextRowNode?.children?.[0];
207
+ const nextCellPath = nextRowPath.concat(0);
208
+ if (nextCell && nextCellPath) return editor.api.node(nextCellPath);
209
+ };
210
+
211
+ //#endregion
212
+ //#region src/lib/queries/getCellInPreviousTableRow.ts
213
+ const getCellInPreviousTableRow = (editor, currentRowPath) => {
214
+ const prevPath = PathApi.previous(currentRowPath);
215
+ if (!prevPath) return;
216
+ const previousRow = editor.api.node(prevPath);
217
+ if (!previousRow) return;
218
+ const [previousRowNode, previousRowPath] = previousRow;
219
+ const previousCell = previousRowNode?.children?.[previousRowNode.children.length - 1];
220
+ const previousCellPath = previousRowPath.concat(previousRowNode.children.length - 1);
221
+ if (previousCell && previousCellPath) return editor.api.node(previousCellPath);
222
+ };
223
+
224
+ //#endregion
225
+ //#region src/lib/queries/getLeftTableCell.ts
226
+ const getLeftTableCell = (editor, { at: cellPath } = {}) => getAdjacentTableCell(editor, {
227
+ at: cellPath,
228
+ deltaCol: -1
229
+ });
230
+
156
231
  //#endregion
157
232
  //#region src/lib/queries/getNextTableCell.ts
158
233
  const getNextTableCell = (editor, _currentCell, currentPath, currentRow) => {
@@ -163,25 +238,277 @@ const getNextTableCell = (editor, _currentCell, currentPath, currentRow) => {
163
238
  };
164
239
 
165
240
  //#endregion
166
- //#region src/lib/queries/getPreviousTableCell.ts
167
- const getPreviousTableCell = (editor, _currentCell, currentPath, currentRow) => {
168
- const prevPath = PathApi.previous(currentPath);
169
- if (!prevPath) {
170
- const [, currentRowPath] = currentRow;
171
- return getCellInPreviousTableRow(editor, currentRowPath);
241
+ //#region src/lib/queries/getPreviousTableCell.ts
242
+ const getPreviousTableCell = (editor, _currentCell, currentPath, currentRow) => {
243
+ const prevPath = PathApi.previous(currentPath);
244
+ if (!prevPath) {
245
+ const [, currentRowPath] = currentRow;
246
+ return getCellInPreviousTableRow(editor, currentRowPath);
247
+ }
248
+ const cell = editor.api.node(prevPath);
249
+ if (cell) return cell;
250
+ };
251
+
252
+ //#endregion
253
+ //#region src/lib/merge/findCellByIndexes.ts
254
+ const findCellByIndexes = (editor, table, searchRowIndex, searchColIndex) => {
255
+ return table.children.flatMap((current) => current.children).find((cellNode) => {
256
+ const indices = getCellIndices(editor, cellNode);
257
+ const { col: _startColIndex, row: _startRowIndex } = indices;
258
+ const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(indices, cellNode);
259
+ if (searchColIndex >= _startColIndex && searchColIndex <= _endColIndex && searchRowIndex >= _startRowIndex && searchRowIndex <= _endRowIndex) return true;
260
+ return false;
261
+ });
262
+ };
263
+
264
+ //#endregion
265
+ //#region src/lib/merge/getTableGridByRange.ts
266
+ /**
267
+ * Get sub table between 2 cell paths. Ensure that the selection is always a
268
+ * valid table grid.
269
+ */
270
+ const getTableMergeGridByRange = (editor, { at, format }) => {
271
+ const { api, type } = getEditorPlugin(editor, BaseTablePlugin);
272
+ const startCellEntry = editor.api.node({
273
+ at: at.anchor.path,
274
+ match: { type: getCellTypes(editor) }
275
+ });
276
+ const endCellEntry = editor.api.node({
277
+ at: at.focus.path,
278
+ match: { type: getCellTypes(editor) }
279
+ });
280
+ const startCell = startCellEntry[0];
281
+ const endCell = endCellEntry[0];
282
+ const tablePath = startCellEntry[1].slice(0, -2);
283
+ const realTable = editor.api.node({
284
+ at: tablePath,
285
+ match: { type }
286
+ })[0];
287
+ const { col: _startColIndex, row: _startRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, startCell), startCell);
288
+ const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, endCell), endCell);
289
+ let startRowIndex = Math.min(_startRowIndex, _endRowIndex);
290
+ let endRowIndex = Math.max(_startRowIndex, _endRowIndex);
291
+ let startColIndex = Math.min(_startColIndex, _endColIndex);
292
+ let endColIndex = Math.max(_startColIndex, _endColIndex);
293
+ const relativeRowIndex = endRowIndex - startRowIndex;
294
+ const relativeColIndex = endColIndex - startColIndex;
295
+ let table = api.create.table({
296
+ children: [],
297
+ colCount: relativeColIndex + 1,
298
+ rowCount: relativeRowIndex + 1
299
+ });
300
+ let cellEntries = [];
301
+ let cellsSet = /* @__PURE__ */ new WeakSet();
302
+ let rowIndex = startRowIndex;
303
+ let colIndex = startColIndex;
304
+ while (true) {
305
+ const cell = findCellByIndexes(editor, realTable, rowIndex, colIndex);
306
+ if (!cell) break;
307
+ const indicies = getCellIndices(editor, cell);
308
+ const { col: cellColWithSpan, row: cellRowWithSpan } = getCellIndicesWithSpans(indicies, cell);
309
+ const { col: cellCol, row: cellRow } = indicies;
310
+ if (cellRow < startRowIndex || cellRowWithSpan > endRowIndex || cellCol < startColIndex || cellColWithSpan > endColIndex) {
311
+ cellsSet = /* @__PURE__ */ new WeakSet();
312
+ cellEntries = [];
313
+ startRowIndex = Math.min(startRowIndex, cellRow);
314
+ endRowIndex = Math.max(endRowIndex, cellRowWithSpan);
315
+ startColIndex = Math.min(startColIndex, cellCol);
316
+ endColIndex = Math.max(endColIndex, cellColWithSpan);
317
+ rowIndex = startRowIndex;
318
+ colIndex = startColIndex;
319
+ const newRelativeRowIndex = endRowIndex - startRowIndex;
320
+ const newRelativeColIndex = endColIndex - startColIndex;
321
+ table = api.create.table({
322
+ children: [],
323
+ colCount: newRelativeColIndex + 1,
324
+ rowCount: newRelativeRowIndex + 1
325
+ });
326
+ continue;
327
+ }
328
+ if (!cellsSet.has(cell)) {
329
+ cellsSet.add(cell);
330
+ const rows = table.children[rowIndex - startRowIndex].children;
331
+ rows[colIndex - startColIndex] = cell;
332
+ const cellPath = editor.api.findPath(cell);
333
+ cellEntries.push([cell, cellPath]);
334
+ }
335
+ if (colIndex + 1 <= endColIndex) colIndex += 1;
336
+ else if (rowIndex + 1 <= endRowIndex) {
337
+ colIndex = startColIndex;
338
+ rowIndex += 1;
339
+ } else break;
340
+ }
341
+ const formatType = format || "table";
342
+ if (formatType === "cell") return cellEntries;
343
+ table.children?.forEach((rowEl) => {
344
+ const rowElement = rowEl;
345
+ rowElement.children = rowElement.children?.filter((cellEl) => {
346
+ const cellElement = cellEl;
347
+ return api.table.getCellChildren(cellElement).length > 0;
348
+ });
349
+ });
350
+ if (formatType === "table") return [[table, tablePath]];
351
+ return {
352
+ cellEntries,
353
+ tableEntries: [[table, tablePath]]
354
+ };
355
+ };
356
+
357
+ //#endregion
358
+ //#region src/lib/queries/getTableGridByRange.ts
359
+ /** Get sub table between 2 cell paths. */
360
+ const getTableGridByRange = (editor, { at, format = "table" }) => {
361
+ const { api } = editor.getPlugin({ key: KEYS.table });
362
+ const { disableMerge } = editor.getOptions(BaseTablePlugin);
363
+ if (!disableMerge) return getTableMergeGridByRange(editor, {
364
+ at,
365
+ format
366
+ });
367
+ const startCellPath = at.anchor.path;
368
+ const endCellPath = at.focus.path;
369
+ const _startRowIndex = startCellPath.at(-2);
370
+ const _endRowIndex = endCellPath.at(-2);
371
+ const _startColIndex = startCellPath.at(-1);
372
+ const _endColIndex = endCellPath.at(-1);
373
+ const startRowIndex = Math.min(_startRowIndex, _endRowIndex);
374
+ const endRowIndex = Math.max(_startRowIndex, _endRowIndex);
375
+ const startColIndex = Math.min(_startColIndex, _endColIndex);
376
+ const endColIndex = Math.max(_startColIndex, _endColIndex);
377
+ const tablePath = startCellPath.slice(0, -2);
378
+ const relativeRowIndex = endRowIndex - startRowIndex;
379
+ const relativeColIndex = endColIndex - startColIndex;
380
+ const table = api.create.table({
381
+ children: [],
382
+ colCount: relativeColIndex + 1,
383
+ rowCount: relativeRowIndex + 1
384
+ });
385
+ let rowIndex = startRowIndex;
386
+ let colIndex = startColIndex;
387
+ const cellEntries = [];
388
+ while (true) {
389
+ const cellPath = tablePath.concat([rowIndex, colIndex]);
390
+ const cell = NodeApi.get(editor, cellPath);
391
+ if (!cell) break;
392
+ const rows = table.children[rowIndex - startRowIndex].children;
393
+ rows[colIndex - startColIndex] = cell;
394
+ cellEntries.push([cell, cellPath]);
395
+ if (colIndex + 1 <= endColIndex) colIndex += 1;
396
+ else if (rowIndex + 1 <= endRowIndex) {
397
+ colIndex = startColIndex;
398
+ rowIndex += 1;
399
+ } else break;
400
+ }
401
+ if (format === "cell") return cellEntries;
402
+ return [[table, tablePath]];
403
+ };
404
+
405
+ //#endregion
406
+ //#region src/lib/queries/getTableGridAbove.ts
407
+ /** Get sub table above anchor and focus. Format: tables or cells. */
408
+ const getTableGridAbove = (editor, { format = "table", ...options } = {}) => {
409
+ const { api } = editor.getPlugin({ key: KEYS.table });
410
+ const edges = editor.api.edgeBlocks({
411
+ match: { type: getCellTypes(editor) },
412
+ ...options
413
+ });
414
+ if (edges) {
415
+ const [start, end] = edges;
416
+ if (!PathApi.equals(start[1], end[1])) return getTableGridByRange(editor, {
417
+ at: {
418
+ anchor: {
419
+ offset: 0,
420
+ path: start[1]
421
+ },
422
+ focus: {
423
+ offset: 0,
424
+ path: end[1]
425
+ }
426
+ },
427
+ format
428
+ });
429
+ if (format === "table") {
430
+ const table = api.create.table({ rowCount: 1 });
431
+ table.children[0].children = [start[0]];
432
+ return [[table, start[1].slice(0, -2)]];
433
+ }
434
+ return [start];
435
+ }
436
+ return [];
437
+ };
438
+
439
+ //#endregion
440
+ //#region src/lib/queries/getSelectedCells.ts
441
+ const selectionQueryCache = /* @__PURE__ */ new WeakMap();
442
+ const getSelectionQueryCache = (editor) => {
443
+ const { selection } = editor;
444
+ const { children } = editor;
445
+ const cachedValue = selectionQueryCache.get(editor);
446
+ if (cachedValue && cachedValue.children === children && cachedValue.selection === selection) return cachedValue;
447
+ const nextValue = {
448
+ children,
449
+ selection
450
+ };
451
+ selectionQueryCache.set(editor, nextValue);
452
+ return nextValue;
453
+ };
454
+ const getSelectedCellEntries = (editor) => {
455
+ const cache = getSelectionQueryCache(editor);
456
+ if ("cellEntries" in cache) return cache.cellEntries ?? [];
457
+ const cellEntries = getTableGridAbove(editor, { format: "cell" });
458
+ const nextValue = cellEntries.length > 1 ? cellEntries : [];
459
+ cache.cellEntries = nextValue;
460
+ return nextValue;
461
+ };
462
+ const getSelectedCells = (editor) => {
463
+ const cache = getSelectionQueryCache(editor);
464
+ if ("selectedCells" in cache) return cache.selectedCells ?? null;
465
+ const cellEntries = getSelectedCellEntries(editor);
466
+ if (cellEntries.length === 0) {
467
+ cache.selectedCells = null;
468
+ return null;
172
469
  }
173
- const cell = editor.api.node(prevPath);
174
- if (cell) return cell;
470
+ const nextValue = cellEntries.map(([cell]) => cell);
471
+ cache.selectedCells = nextValue;
472
+ return nextValue;
175
473
  };
176
-
177
- //#endregion
178
- //#region src/lib/queries/getRowSpan.ts
179
- /**
180
- * Returns the rowspan attribute of the table cell element.
181
- *
182
- * @default 1 if undefined
183
- */
184
- const getRowSpan = (cellElem) => cellElem.rowSpan || Number(cellElem.attributes?.rowspan) || 1;
474
+ const getSelectedCellIds = (editor) => {
475
+ const cache = getSelectionQueryCache(editor);
476
+ if ("selectedCellIds" in cache) return cache.selectedCellIds ?? null;
477
+ const selectedCellIds = getSelectedCellEntries(editor).map(([cell]) => cell.id).filter((id) => !!id);
478
+ const nextValue = selectedCellIds.length > 0 ? selectedCellIds : null;
479
+ cache.selectedCellIds = nextValue;
480
+ return nextValue;
481
+ };
482
+ const getSelectedTableIds = (editor) => {
483
+ const cache = getSelectionQueryCache(editor);
484
+ if ("selectedTableIds" in cache) return cache.selectedTableIds ?? null;
485
+ const selectedTables = getSelectedTables(editor);
486
+ if (!selectedTables) {
487
+ cache.selectedTableIds = null;
488
+ return null;
489
+ }
490
+ const selectedTableIds = selectedTables.map((table) => table.id).filter((id) => !!id);
491
+ const nextValue = selectedTableIds.length > 0 ? selectedTableIds : null;
492
+ cache.selectedTableIds = nextValue;
493
+ return nextValue;
494
+ };
495
+ const getSelectedCell = (editor, id) => {
496
+ if (!id) return null;
497
+ return getSelectedCellEntries(editor).find(([cell]) => cell.id === id)?.[0] ?? null;
498
+ };
499
+ const getSelectedTables = (editor) => {
500
+ const cache = getSelectionQueryCache(editor);
501
+ if ("selectedTables" in cache) return cache.selectedTables ?? null;
502
+ if (getSelectedCellEntries(editor).length === 0) {
503
+ cache.selectedTables = null;
504
+ return null;
505
+ }
506
+ const nextValue = getTableGridAbove(editor, { format: "table" }).map(([table]) => table);
507
+ cache.selectedTables = nextValue;
508
+ return nextValue;
509
+ };
510
+ const isCellSelected = (editor, id) => !!getSelectedCell(editor, id);
511
+ const isSelectingCell = (editor) => getSelectedCellEntries(editor).length > 0;
185
512
 
186
513
  //#endregion
187
514
  //#region src/lib/queries/getSelectedCellsBoundingBox.ts
@@ -211,21 +538,10 @@ function getSelectedCellsBoundingBox(editor, cells) {
211
538
 
212
539
  //#endregion
213
540
  //#region src/lib/queries/getTopTableCell.ts
214
- const getTopTableCell = (editor, { at: cellPath } = {}) => {
215
- if (!cellPath) {
216
- cellPath = editor.api.node({ match: { type: getCellTypes(editor) } })?.[1];
217
- if (!cellPath) return;
218
- }
219
- const cellIndex = cellPath.at(-1);
220
- const rowIndex = cellPath.at(-2);
221
- if (rowIndex === 0) return;
222
- const cellAbovePath = [
223
- ...PathApi.parent(PathApi.parent(cellPath)),
224
- rowIndex - 1,
225
- cellIndex
226
- ];
227
- return editor.api.node(cellAbovePath);
228
- };
541
+ const getTopTableCell = (editor, { at: cellPath } = {}) => getAdjacentTableCell(editor, {
542
+ at: cellPath,
543
+ deltaRow: -1
544
+ });
229
545
 
230
546
  //#endregion
231
547
  //#region src/lib/queries/getSelectedCellsBorders.ts
@@ -419,317 +735,104 @@ function isSelectedCellBorder(editor, cells, side) {
419
735
  if (!cellAboveEntry) return true;
420
736
  const [cellAboveNode] = cellAboveEntry;
421
737
  return (cellAboveNode.borders?.bottom?.size ?? 1) >= 1;
422
- }
423
- if (side === "bottom" && rr === maxRow) return (cell.borders?.bottom?.size ?? 1) >= 1;
424
- if (side === "left" && cc === minCol) {
425
- if (col === 0) return (cell.borders?.left?.size ?? 1) >= 1;
426
- const prevCellEntry = getLeftTableCell(editor, { at: cellPath });
427
- if (!prevCellEntry) return true;
428
- const [prevCellNode] = prevCellEntry;
429
- return (prevCellNode.borders?.right?.size ?? 1) >= 1;
430
- }
431
- if (side === "right" && cc === maxCol) return (cell.borders?.right?.size ?? 1) >= 1;
432
- }
433
- return true;
434
- });
435
- }
436
-
437
- //#endregion
438
- //#region src/lib/queries/getTableAbove.ts
439
- const getTableAbove = (editor, options) => editor.api.block({
440
- above: true,
441
- match: { type: editor.getType(KEYS.table) },
442
- ...options
443
- });
444
-
445
- //#endregion
446
- //#region src/lib/queries/getTableCellBorders.ts
447
- const getTableCellBorders = (editor, { cellIndices, defaultBorder = { size: 1 }, element }) => {
448
- const cellPath = editor.api.findPath(element);
449
- const [rowNode, rowPath] = editor.api.parent(cellPath) ?? [];
450
- if (!rowNode || !rowPath) return {
451
- bottom: defaultBorder,
452
- right: defaultBorder
453
- };
454
- const [tableNode] = editor.api.parent(rowPath);
455
- const { col } = cellIndices ?? getCellIndices(editor, element);
456
- const isFirstCell = col === 0;
457
- const isFirstRow = tableNode.children?.[0] === rowNode;
458
- const getBorder = (dir) => {
459
- const border = element.borders?.[dir];
460
- return {
461
- color: border?.color ?? defaultBorder.color,
462
- size: border?.size ?? defaultBorder.size,
463
- style: border?.style ?? defaultBorder.style
464
- };
465
- };
466
- return {
467
- bottom: getBorder("bottom"),
468
- left: isFirstCell ? getBorder("left") : void 0,
469
- right: getBorder("right"),
470
- top: isFirstRow ? getBorder("top") : void 0
471
- };
472
- };
473
-
474
- //#endregion
475
- //#region src/lib/queries/getTableCellSize.ts
476
- /** Get the width of a cell with colSpan support. */
477
- const getTableCellSize = (editor, { cellIndices, colSizes, element, rowSize }) => {
478
- const { api } = getEditorPlugin(editor, { key: KEYS.table });
479
- const path = editor.api.findPath(element);
480
- if (!rowSize) {
481
- const [rowElement] = editor.api.parent(path) ?? [];
482
- if (!rowElement) return {
483
- minHeight: 0,
484
- width: 0
485
- };
486
- rowSize = rowElement.size;
487
- }
488
- if (!colSizes) {
489
- const [, rowPath] = editor.api.parent(path);
490
- const [tableNode] = editor.api.parent(rowPath);
491
- colSizes = getTableOverriddenColSizes(tableNode);
492
- }
493
- const colSpan = api.table.getColSpan(element);
494
- const { col } = cellIndices ?? getCellIndices(editor, element);
495
- const width = (colSizes ?? []).slice(col, col + colSpan).reduce((total, w) => total + (w || 0), 0);
496
- return {
497
- minHeight: rowSize,
498
- width
499
- };
500
- };
501
-
502
- //#endregion
503
- //#region src/lib/queries/getTableColumnCount.ts
504
- const getTableColumnCount = (tableNode) => {
505
- 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));
506
- return 0;
507
- };
508
-
509
- //#endregion
510
- //#region src/lib/queries/getTableEntries.ts
511
- /**
512
- * If at (default = selection) is in table>tr>td|th, return table, row, and cell
513
- * node entries.
514
- */
515
- const getTableEntries = (editor, { at = editor.selection } = {}) => {
516
- if (!at) return;
517
- const cellEntry = editor.api.node({
518
- at,
519
- match: { type: getCellTypes(editor) }
520
- });
521
- if (!cellEntry) return;
522
- const [, cellPath] = cellEntry;
523
- const rowEntry = editor.api.above({
524
- at: cellPath,
525
- match: { type: editor.getType(KEYS.tr) }
526
- });
527
- if (!rowEntry) return;
528
- const [, rowPath] = rowEntry;
529
- const tableEntry = editor.api.above({
530
- at: rowPath,
531
- match: { type: editor.getType(KEYS.table) }
532
- });
533
- if (!tableEntry) return;
534
- return {
535
- cell: cellEntry,
536
- row: rowEntry,
537
- table: tableEntry
538
- };
539
- };
540
-
541
- //#endregion
542
- //#region src/lib/merge/getCellIndicesWithSpans.ts
543
- const getCellIndicesWithSpans = ({ col, row }, endCell) => ({
544
- col: col + getColSpan(endCell) - 1,
545
- row: row + getRowSpan(endCell) - 1
546
- });
547
-
548
- //#endregion
549
- //#region src/lib/merge/findCellByIndexes.ts
550
- const findCellByIndexes = (editor, table, searchRowIndex, searchColIndex) => {
551
- return table.children.flatMap((current) => current.children).find((cellNode) => {
552
- const indices = getCellIndices(editor, cellNode);
553
- const { col: _startColIndex, row: _startRowIndex } = indices;
554
- const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(indices, cellNode);
555
- if (searchColIndex >= _startColIndex && searchColIndex <= _endColIndex && searchRowIndex >= _startRowIndex && searchRowIndex <= _endRowIndex) return true;
556
- return false;
557
- });
558
- };
559
-
560
- //#endregion
561
- //#region src/lib/merge/getTableGridByRange.ts
562
- /**
563
- * Get sub table between 2 cell paths. Ensure that the selection is always a
564
- * valid table grid.
565
- */
566
- const getTableMergeGridByRange = (editor, { at, format }) => {
567
- const { api, type } = getEditorPlugin(editor, BaseTablePlugin);
568
- const startCellEntry = editor.api.node({
569
- at: at.anchor.path,
570
- match: { type: getCellTypes(editor) }
571
- });
572
- const endCellEntry = editor.api.node({
573
- at: at.focus.path,
574
- match: { type: getCellTypes(editor) }
575
- });
576
- const startCell = startCellEntry[0];
577
- const endCell = endCellEntry[0];
578
- const tablePath = at.anchor.path.slice(0, -2);
579
- const realTable = editor.api.node({
580
- at: tablePath,
581
- match: { type }
582
- })[0];
583
- const { col: _startColIndex, row: _startRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, startCell), startCell);
584
- const { col: _endColIndex, row: _endRowIndex } = getCellIndicesWithSpans(getCellIndices(editor, endCell), endCell);
585
- let startRowIndex = Math.min(_startRowIndex, _endRowIndex);
586
- let endRowIndex = Math.max(_startRowIndex, _endRowIndex);
587
- let startColIndex = Math.min(_startColIndex, _endColIndex);
588
- let endColIndex = Math.max(_startColIndex, _endColIndex);
589
- const relativeRowIndex = endRowIndex - startRowIndex;
590
- const relativeColIndex = endColIndex - startColIndex;
591
- let table = api.create.table({
592
- children: [],
593
- colCount: relativeColIndex + 1,
594
- rowCount: relativeRowIndex + 1
595
- });
596
- let cellEntries = [];
597
- let cellsSet = /* @__PURE__ */ new WeakSet();
598
- let rowIndex = startRowIndex;
599
- let colIndex = startColIndex;
600
- while (true) {
601
- const cell = findCellByIndexes(editor, realTable, rowIndex, colIndex);
602
- if (!cell) break;
603
- const indicies = getCellIndices(editor, cell);
604
- const { col: cellColWithSpan, row: cellRowWithSpan } = getCellIndicesWithSpans(indicies, cell);
605
- const { col: cellCol, row: cellRow } = indicies;
606
- if (cellRow < startRowIndex || cellRowWithSpan > endRowIndex || cellCol < startColIndex || cellColWithSpan > endColIndex) {
607
- cellsSet = /* @__PURE__ */ new WeakSet();
608
- cellEntries = [];
609
- startRowIndex = Math.min(startRowIndex, cellRow);
610
- endRowIndex = Math.max(endRowIndex, cellRowWithSpan);
611
- startColIndex = Math.min(startColIndex, cellCol);
612
- endColIndex = Math.max(endColIndex, cellColWithSpan);
613
- rowIndex = startRowIndex;
614
- colIndex = startColIndex;
615
- const newRelativeRowIndex = endRowIndex - startRowIndex;
616
- const newRelativeColIndex = endColIndex - startColIndex;
617
- table = api.create.table({
618
- children: [],
619
- colCount: newRelativeColIndex + 1,
620
- rowCount: newRelativeRowIndex + 1
621
- });
622
- continue;
623
- }
624
- if (!cellsSet.has(cell)) {
625
- cellsSet.add(cell);
626
- const rows = table.children[rowIndex - startRowIndex].children;
627
- rows[colIndex - startColIndex] = cell;
628
- const cellPath = editor.api.findPath(cell);
629
- cellEntries.push([cell, cellPath]);
630
- }
631
- if (colIndex + 1 <= endColIndex) colIndex += 1;
632
- else if (rowIndex + 1 <= endRowIndex) {
633
- colIndex = startColIndex;
634
- rowIndex += 1;
635
- } else break;
636
- }
637
- const formatType = format || "table";
638
- if (formatType === "cell") return cellEntries;
639
- table.children?.forEach((rowEl) => {
640
- const rowElement = rowEl;
641
- rowElement.children = rowElement.children?.filter((cellEl) => {
642
- const cellElement = cellEl;
643
- return api.table.getCellChildren(cellElement).length > 0;
644
- });
738
+ }
739
+ if (side === "bottom" && rr === maxRow) return (cell.borders?.bottom?.size ?? 1) >= 1;
740
+ if (side === "left" && cc === minCol) {
741
+ if (col === 0) return (cell.borders?.left?.size ?? 1) >= 1;
742
+ const prevCellEntry = getLeftTableCell(editor, { at: cellPath });
743
+ if (!prevCellEntry) return true;
744
+ const [prevCellNode] = prevCellEntry;
745
+ return (prevCellNode.borders?.right?.size ?? 1) >= 1;
746
+ }
747
+ if (side === "right" && cc === maxCol) return (cell.borders?.right?.size ?? 1) >= 1;
748
+ }
749
+ return true;
645
750
  });
646
- if (formatType === "table") return [[table, tablePath]];
751
+ }
752
+
753
+ //#endregion
754
+ //#region src/lib/queries/getTableAbove.ts
755
+ const getTableAbove = (editor, options) => editor.api.block({
756
+ above: true,
757
+ match: { type: editor.getType(KEYS.table) },
758
+ ...options
759
+ });
760
+
761
+ //#endregion
762
+ //#region src/lib/queries/getTableCellBorders.ts
763
+ const getTableCellBorders = (editor, { cellIndices, defaultBorder = { size: 1 }, element }) => {
764
+ const cellPath = editor.api.findPath(element);
765
+ const [rowNode, rowPath] = editor.api.parent(cellPath) ?? [];
766
+ if (!rowNode || !rowPath) return {
767
+ bottom: defaultBorder,
768
+ right: defaultBorder
769
+ };
770
+ const [tableNode] = editor.api.parent(rowPath) ?? [];
771
+ const tableType = editor.getType(KEYS.table);
772
+ if (!tableNode || tableNode.type !== tableType) return {
773
+ bottom: defaultBorder,
774
+ right: defaultBorder
775
+ };
776
+ const { col } = cellIndices ?? getCellIndices(editor, element);
777
+ const isFirstCell = col === 0;
778
+ const isFirstRow = tableNode.children?.[0] === rowNode;
779
+ const getBorder = (dir) => {
780
+ const border = element.borders?.[dir];
781
+ return {
782
+ color: border?.color ?? defaultBorder.color,
783
+ size: border?.size ?? defaultBorder.size,
784
+ style: border?.style ?? defaultBorder.style
785
+ };
786
+ };
647
787
  return {
648
- cellEntries,
649
- tableEntries: [[table, tablePath]]
788
+ bottom: getBorder("bottom"),
789
+ left: isFirstCell ? getBorder("left") : void 0,
790
+ right: getBorder("right"),
791
+ top: isFirstRow ? getBorder("top") : void 0
650
792
  };
651
793
  };
652
794
 
653
795
  //#endregion
654
- //#region src/lib/queries/getTableGridByRange.ts
655
- /** Get sub table between 2 cell paths. */
656
- const getTableGridByRange = (editor, { at, format = "table" }) => {
657
- const { api } = editor.getPlugin({ key: KEYS.table });
658
- const { disableMerge } = editor.getOptions(BaseTablePlugin);
659
- if (!disableMerge) return getTableMergeGridByRange(editor, {
660
- at,
661
- format
662
- });
663
- const startCellPath = at.anchor.path;
664
- const endCellPath = at.focus.path;
665
- const _startRowIndex = startCellPath.at(-2);
666
- const _endRowIndex = endCellPath.at(-2);
667
- const _startColIndex = startCellPath.at(-1);
668
- const _endColIndex = endCellPath.at(-1);
669
- const startRowIndex = Math.min(_startRowIndex, _endRowIndex);
670
- const endRowIndex = Math.max(_startRowIndex, _endRowIndex);
671
- const startColIndex = Math.min(_startColIndex, _endColIndex);
672
- const endColIndex = Math.max(_startColIndex, _endColIndex);
673
- const tablePath = startCellPath.slice(0, -2);
674
- const relativeRowIndex = endRowIndex - startRowIndex;
675
- const relativeColIndex = endColIndex - startColIndex;
676
- const table = api.create.table({
677
- children: [],
678
- colCount: relativeColIndex + 1,
679
- rowCount: relativeRowIndex + 1
680
- });
681
- let rowIndex = startRowIndex;
682
- let colIndex = startColIndex;
683
- const cellEntries = [];
684
- while (true) {
685
- const cellPath = tablePath.concat([rowIndex, colIndex]);
686
- const cell = NodeApi.get(editor, cellPath);
687
- if (!cell) break;
688
- const rows = table.children[rowIndex - startRowIndex].children;
689
- rows[colIndex - startColIndex] = cell;
690
- cellEntries.push([cell, cellPath]);
691
- if (colIndex + 1 <= endColIndex) colIndex += 1;
692
- else if (rowIndex + 1 <= endRowIndex) {
693
- colIndex = startColIndex;
694
- rowIndex += 1;
695
- } else break;
796
+ //#region src/lib/queries/getTableCellSize.ts
797
+ /** Get the width of a cell with colSpan support. */
798
+ const getTableCellSize = (editor, { cellIndices, colSizes, element, rowSize }) => {
799
+ const { api } = getEditorPlugin(editor, { key: KEYS.table });
800
+ const path = editor.api.findPath(element);
801
+ if (!rowSize) {
802
+ const [rowElement] = editor.api.parent(path) ?? [];
803
+ if (!rowElement || rowElement.type !== editor.getType(KEYS.tr)) return {
804
+ minHeight: 0,
805
+ width: 0
806
+ };
807
+ rowSize = rowElement.size ?? 0;
696
808
  }
697
- if (format === "cell") return cellEntries;
698
- return [[table, tablePath]];
809
+ if (!colSizes) {
810
+ const [, rowPath] = editor.api.parent(path) ?? [];
811
+ if (!rowPath) return {
812
+ minHeight: rowSize,
813
+ width: 0
814
+ };
815
+ const [tableNode] = editor.api.parent(rowPath) ?? [];
816
+ if (!tableNode) return {
817
+ minHeight: rowSize,
818
+ width: 0
819
+ };
820
+ colSizes = getTableOverriddenColSizes(tableNode);
821
+ }
822
+ const colSpan = api.table.getColSpan(element);
823
+ const { col } = cellIndices ?? getCellIndices(editor, element);
824
+ const width = (colSizes ?? []).slice(col, col + colSpan).reduce((total, w) => total + (w || 0), 0);
825
+ return {
826
+ minHeight: rowSize,
827
+ width
828
+ };
699
829
  };
700
830
 
701
831
  //#endregion
702
- //#region src/lib/queries/getTableGridAbove.ts
703
- /** Get sub table above anchor and focus. Format: tables or cells. */
704
- const getTableGridAbove = (editor, { format = "table", ...options } = {}) => {
705
- const { api } = editor.getPlugin({ key: KEYS.table });
706
- const edges = editor.api.edgeBlocks({
707
- match: { type: getCellTypes(editor) },
708
- ...options
709
- });
710
- if (edges) {
711
- const [start, end] = edges;
712
- if (!PathApi.equals(start[1], end[1])) return getTableGridByRange(editor, {
713
- at: {
714
- anchor: {
715
- offset: 0,
716
- path: start[1]
717
- },
718
- focus: {
719
- offset: 0,
720
- path: end[1]
721
- }
722
- },
723
- format
724
- });
725
- if (format === "table") {
726
- const table = api.create.table({ rowCount: 1 });
727
- table.children[0].children = [start[0]];
728
- return [[table, start[1].slice(0, -2)]];
729
- }
730
- return [start];
731
- }
732
- return [];
832
+ //#region src/lib/queries/getTableColumnCount.ts
833
+ const getTableColumnCount = (tableNode) => {
834
+ 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));
835
+ return 0;
733
836
  };
734
837
 
735
838
  //#endregion
@@ -1215,27 +1318,6 @@ const insertTableMergeRow = (editor, { at, before, fromRow, header, select: shou
1215
1318
  });
1216
1319
  };
1217
1320
 
1218
- //#endregion
1219
- //#region src/lib/merge/isTableRectangular.ts
1220
- const allEqual = (arr) => arr.every((val) => val === arr[0]);
1221
- /**
1222
- * Checks if the given table is rectangular, meaning all rows have the same
1223
- * effective number of cells, considering colspan and rowspan.
1224
- */
1225
- const isTableRectangular = (table) => {
1226
- const arr = [];
1227
- table?.children?.forEach((row, rI) => {
1228
- row.children?.forEach((cell) => {
1229
- const cellElem = cell;
1230
- Array.from({ length: getRowSpan(cellElem) || 1 }).forEach((_, i) => {
1231
- if (!arr[rI + i]) arr[rI + i] = 0;
1232
- arr[rI + i] += getColSpan(cellElem);
1233
- });
1234
- });
1235
- });
1236
- return allEqual(arr);
1237
- };
1238
-
1239
1321
  //#endregion
1240
1322
  //#region src/lib/merge/mergeTableCells.ts
1241
1323
  /** Merges multiple selected cells into one. */
@@ -1661,51 +1743,6 @@ const moveSelectionFromCell = (editor, { at, edge, fromOneCell, reverse } = {})
1661
1743
  }
1662
1744
  };
1663
1745
 
1664
- //#endregion
1665
- //#region src/lib/transforms/overrideSelectionFromCell.ts
1666
- /**
1667
- * Override the new selection if the previous selection and the new one are in
1668
- * different cells.
1669
- */
1670
- const overrideSelectionFromCell = (editor, newSelection) => {
1671
- let hotkey;
1672
- if (!editor.dom.currentKeyboardEvent || ![
1673
- "up",
1674
- "down",
1675
- "shift+up",
1676
- "shift+right",
1677
- "shift+down",
1678
- "shift+left"
1679
- ].some((key) => {
1680
- const valid = isHotkey(key, editor.dom.currentKeyboardEvent);
1681
- if (valid) hotkey = key;
1682
- return valid;
1683
- }) || !editor.selection?.focus || !newSelection?.focus || !editor.api.isAt({
1684
- at: {
1685
- anchor: editor.selection.focus,
1686
- focus: newSelection.focus
1687
- },
1688
- blocks: true,
1689
- match: { type: getCellTypes(editor) }
1690
- })) return;
1691
- if (!hotkey) return;
1692
- const edge = KEY_SHIFT_EDGES[hotkey];
1693
- if (edge && !editor.api.isAt({
1694
- block: true,
1695
- match: { type: getCellTypes(editor) }
1696
- })) return;
1697
- const prevSelection = editor.selection;
1698
- const reverse = ["shift+up", "up"].includes(hotkey);
1699
- setTimeout(() => {
1700
- moveSelectionFromCell(editor, {
1701
- at: prevSelection,
1702
- edge,
1703
- fromOneCell: true,
1704
- reverse
1705
- });
1706
- }, 0);
1707
- };
1708
-
1709
1746
  //#endregion
1710
1747
  //#region src/lib/transforms/setBorderSize.ts
1711
1748
  const setBorderSize = (editor, size, { at, border = "all" } = {}) => {
@@ -1844,6 +1881,54 @@ const setTableRowSize = (editor, { height, rowIndex }, options = {}) => {
1844
1881
  editor.tf.setNodes({ size: height }, { at: tableRowPath });
1845
1882
  };
1846
1883
 
1884
+ //#endregion
1885
+ //#region src/lib/transforms/shouldMoveSelectionFromCell.ts
1886
+ const VISUAL_LINE_TOLERANCE = 1;
1887
+ const getRangeClientRects = (domRange) => Array.from(domRange?.getClientRects?.() ?? []).filter((rect) => rect.height > 0);
1888
+ const getTableMoveSelectionContext = (editor, point = editor.selection?.anchor) => {
1889
+ if (!point || !editor.api.isAt({
1890
+ block: true,
1891
+ match: { type: getCellTypes(editor) }
1892
+ })) return;
1893
+ const cellEntry = editor.api.block({
1894
+ at: point,
1895
+ match: { type: getCellTypes(editor) }
1896
+ });
1897
+ const blockEntry = editor.api.block({ at: point });
1898
+ if (!cellEntry || !blockEntry) return;
1899
+ const [, cellPath] = cellEntry;
1900
+ const [, blockPath] = blockEntry;
1901
+ return {
1902
+ blockPath,
1903
+ cellPath,
1904
+ point
1905
+ };
1906
+ };
1907
+ const hasAdjacentBlockInCell = (editor, { blockPath, cellPath, reverse }) => {
1908
+ const adjacentBlock = reverse ? editor.api.previous({
1909
+ at: blockPath,
1910
+ block: true
1911
+ }) : editor.api.next({
1912
+ at: blockPath,
1913
+ block: true
1914
+ });
1915
+ return !!adjacentBlock && PathApi.isAncestor(cellPath, adjacentBlock[1]);
1916
+ };
1917
+ const shouldMoveSelectionFromCell = (editor, { blockPath, point, reverse }) => {
1918
+ const blockRange = editor.api.range(blockPath);
1919
+ const isAtBlockEdge = reverse ? editor.api.isStart(point, blockPath) : editor.api.isEnd(point, blockPath);
1920
+ if (!blockRange) return isAtBlockEdge;
1921
+ const caretRects = getRangeClientRects(editor.api.toDOMRange({
1922
+ anchor: point,
1923
+ focus: point
1924
+ }));
1925
+ const blockRects = getRangeClientRects(editor.api.toDOMRange(blockRange));
1926
+ if (caretRects.length === 0 || blockRects.length === 0) return isAtBlockEdge;
1927
+ const caretRect = caretRects.at(-1);
1928
+ const boundary = reverse ? Math.min(...blockRects.map((rect) => rect.top)) : Math.max(...blockRects.map((rect) => rect.bottom));
1929
+ return reverse ? caretRect.top <= boundary + VISUAL_LINE_TOLERANCE : caretRect.bottom >= boundary - VISUAL_LINE_TOLERANCE;
1930
+ };
1931
+
1847
1932
  //#endregion
1848
1933
  //#region src/lib/withApplyTable.ts
1849
1934
  /**
@@ -1889,7 +1974,6 @@ const withApplyTable = ({ api: _api, editor, getOptions, tf: { apply }, type: ta
1889
1974
  }
1890
1975
  }
1891
1976
  }
1892
- overrideSelectionFromCell(editor, newSelection);
1893
1977
  }
1894
1978
  const opType = op.type === "remove_node" ? op.node.type : op.type === "move_node" ? editor.api.node(op.path)?.[0].type : void 0;
1895
1979
  const isTableOperation = (op.type === "remove_node" || op.type === "move_node") && opType && [
@@ -2250,108 +2334,116 @@ const withSetFragmentDataTable = ({ api, editor, plugin, tf: { setFragmentData }
2250
2334
 
2251
2335
  //#endregion
2252
2336
  //#region src/lib/withTableCellSelection.tsx
2253
- const withTableCellSelection = ({ api: { marks }, editor, tf: { addMark, removeMark, setNodes } }) => {
2254
- return {
2255
- api: { marks() {
2337
+ const isTargetingSelectedCell = (editor, target, cellPaths) => {
2338
+ if (PathApi.isPath(target)) return cellPaths.some((cellPath) => PathApi.isCommon(cellPath, target));
2339
+ const range = editor.api.range(target);
2340
+ if (!range) return false;
2341
+ return cellPaths.some((cellPath) => {
2342
+ const cellRange = editor.api.range(cellPath);
2343
+ if (!cellRange) return false;
2344
+ return RangeApi.includes(cellRange, range.anchor) || RangeApi.includes(cellRange, range.focus) || RangeApi.includes(range, cellRange);
2345
+ });
2346
+ };
2347
+ const withTableCellSelection = ({ api: { marks }, editor, tf: { addMark, removeMark, setNodes } }) => ({
2348
+ api: { marks() {
2349
+ const apply = () => {
2350
+ const { selection } = editor;
2351
+ if (!selection || editor.api.isCollapsed()) return;
2352
+ const matchesCell = getTableGridAbove(editor, { format: "cell" });
2353
+ if (matchesCell.length <= 1) return;
2354
+ const markCounts = {};
2355
+ const totalMarks = {};
2356
+ let totalNodes = 0;
2357
+ matchesCell.forEach(([_cell, cellPath]) => {
2358
+ const textNodeEntry = editor.api.nodes({
2359
+ at: cellPath,
2360
+ match: (n) => TextApi.isText(n)
2361
+ });
2362
+ Array.from(textNodeEntry, (item) => item[0]).forEach((item) => {
2363
+ totalNodes++;
2364
+ const keys = Object.keys(item);
2365
+ if (keys.length === 1) return;
2366
+ keys.splice(keys.indexOf("text"), 1);
2367
+ keys.forEach((k) => {
2368
+ markCounts[k] = (markCounts[k] || 0) + 1;
2369
+ totalMarks[k] = item[k];
2370
+ });
2371
+ });
2372
+ });
2373
+ Object.keys(markCounts).forEach((mark) => {
2374
+ if (markCounts[mark] !== totalNodes) delete totalMarks[mark];
2375
+ });
2376
+ return totalMarks;
2377
+ };
2378
+ const result = apply();
2379
+ if (result) return result;
2380
+ return marks();
2381
+ } },
2382
+ transforms: {
2383
+ addMark(key, value) {
2256
2384
  const apply = () => {
2257
2385
  const { selection } = editor;
2258
- if (!selection || editor.api.isCollapsed()) return;
2386
+ if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2259
2387
  const matchesCell = getTableGridAbove(editor, { format: "cell" });
2260
2388
  if (matchesCell.length <= 1) return;
2261
- const markCounts = {};
2262
- const totalMarks = {};
2263
- let totalNodes = 0;
2264
2389
  matchesCell.forEach(([_cell, cellPath]) => {
2265
- const textNodeEntry = editor.api.nodes({
2390
+ editor.tf.setNodes({ [key]: value }, {
2266
2391
  at: cellPath,
2392
+ split: true,
2393
+ voids: true,
2267
2394
  match: (n) => TextApi.isText(n)
2268
2395
  });
2269
- Array.from(textNodeEntry, (item) => item[0]).forEach((item) => {
2270
- totalNodes++;
2271
- const keys = Object.keys(item);
2272
- if (keys.length === 1) return;
2273
- keys.splice(keys.indexOf("text"), 1);
2274
- keys.forEach((k) => {
2275
- markCounts[k] = (markCounts[k] || 0) + 1;
2276
- totalMarks[k] = item[k];
2277
- });
2396
+ });
2397
+ return true;
2398
+ };
2399
+ if (apply()) return;
2400
+ return addMark(key, value);
2401
+ },
2402
+ removeMark(key) {
2403
+ const apply = () => {
2404
+ const { selection } = editor;
2405
+ if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2406
+ const matchesCell = getTableGridAbove(editor, { format: "cell" });
2407
+ if (matchesCell.length <= 1) return;
2408
+ matchesCell.forEach(([_cell, cellPath]) => {
2409
+ editor.tf.setNodes({ [key]: null }, {
2410
+ at: cellPath,
2411
+ split: true,
2412
+ voids: true,
2413
+ match: (n) => TextApi.isText(n)
2278
2414
  });
2279
2415
  });
2280
- Object.keys(markCounts).forEach((mark) => {
2281
- if (markCounts[mark] !== totalNodes) delete totalMarks[mark];
2416
+ return true;
2417
+ };
2418
+ if (apply()) return;
2419
+ return removeMark(key);
2420
+ },
2421
+ setNodes(props, options) {
2422
+ const apply = () => {
2423
+ const { selection } = editor;
2424
+ if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2425
+ const matchesCell = getTableGridAbove(editor, { format: "cell" });
2426
+ if (matchesCell.length <= 1) return;
2427
+ if (options?.at) {
2428
+ const cellPaths = matchesCell.map(([, cellPath]) => cellPath);
2429
+ if (!isTargetingSelectedCell(editor, options.at, cellPaths)) return;
2430
+ }
2431
+ setNodes(props, {
2432
+ ...options,
2433
+ match: combineTransformMatchOptions(editor, (_, p) => matchesCell.some(([_$1, cellPath]) => PathApi.isCommon(cellPath, p)), options)
2282
2434
  });
2283
- return totalMarks;
2435
+ return true;
2284
2436
  };
2285
- const result = apply();
2286
- if (result) return result;
2287
- return marks();
2288
- } },
2289
- transforms: {
2290
- addMark(key, value) {
2291
- const apply = () => {
2292
- const { selection } = editor;
2293
- if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2294
- const matchesCell = getTableGridAbove(editor, { format: "cell" });
2295
- if (matchesCell.length <= 1) return;
2296
- matchesCell.forEach(([_cell, cellPath]) => {
2297
- editor.tf.setNodes({ [key]: value }, {
2298
- at: cellPath,
2299
- split: true,
2300
- voids: true,
2301
- match: (n) => TextApi.isText(n)
2302
- });
2303
- });
2304
- return true;
2305
- };
2306
- if (apply()) return;
2307
- return addMark(key, value);
2308
- },
2309
- removeMark(key) {
2310
- const apply = () => {
2311
- const { selection } = editor;
2312
- if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2313
- const matchesCell = getTableGridAbove(editor, { format: "cell" });
2314
- if (matchesCell.length <= 1) return;
2315
- matchesCell.forEach(([_cell, cellPath]) => {
2316
- editor.tf.setNodes({ [key]: null }, {
2317
- at: cellPath,
2318
- split: true,
2319
- voids: true,
2320
- match: (n) => TextApi.isText(n)
2321
- });
2322
- });
2323
- return true;
2324
- };
2325
- if (apply()) return;
2326
- return removeMark(key);
2327
- },
2328
- setNodes(props, options) {
2329
- const apply = () => {
2330
- const { selection } = editor;
2331
- if (!selection || editor.api.isCollapsed() || editor.meta.isNormalizing) return;
2332
- if (options?.at) {
2333
- const range = editor.api.range(options.at);
2334
- if (range && !RangeApi.includes(selection, range)) return;
2335
- }
2336
- const matchesCell = getTableGridAbove(editor, { format: "cell" });
2337
- if (matchesCell.length <= 1) return;
2338
- setNodes(props, {
2339
- ...options,
2340
- match: combineTransformMatchOptions(editor, (_, p) => matchesCell.some(([_$1, cellPath]) => PathApi.isCommon(cellPath, p)), options)
2341
- });
2342
- return true;
2343
- };
2344
- if (apply()) return;
2345
- return setNodes(props, options);
2346
- }
2437
+ if (apply()) return;
2438
+ return setNodes(props, options);
2347
2439
  }
2348
- };
2349
- };
2440
+ }
2441
+ });
2350
2442
 
2351
2443
  //#endregion
2352
2444
  //#region src/lib/withTable.ts
2353
2445
  const withTable = (ctx) => {
2354
- const { editor, tf: { selectAll, tab }, type } = ctx;
2446
+ const { editor, tf: { moveLine, selectAll, tab }, type } = ctx;
2355
2447
  const cellSelection = withTableCellSelection(ctx);
2356
2448
  return {
2357
2449
  api: {
@@ -2359,6 +2451,27 @@ const withTable = (ctx) => {
2359
2451
  ...cellSelection.api
2360
2452
  },
2361
2453
  transforms: {
2454
+ moveLine: (options) => {
2455
+ const apply = () => {
2456
+ if (!editor.api.isCollapsed()) return;
2457
+ const context = getTableMoveSelectionContext(editor);
2458
+ if (!context) return;
2459
+ const { blockPath, cellPath, point } = context;
2460
+ if (hasAdjacentBlockInCell(editor, {
2461
+ blockPath,
2462
+ cellPath,
2463
+ reverse: options.reverse
2464
+ })) return;
2465
+ if (!shouldMoveSelectionFromCell(editor, {
2466
+ blockPath,
2467
+ point,
2468
+ reverse: options.reverse
2469
+ })) return;
2470
+ return moveSelectionFromCell(editor, { reverse: options.reverse });
2471
+ };
2472
+ if (apply()) return true;
2473
+ return moveLine(options);
2474
+ },
2362
2475
  selectAll: () => {
2363
2476
  const apply = () => {
2364
2477
  const table = editor.api.above({ match: { type } });
@@ -2481,6 +2594,9 @@ const BaseTablePlugin = createTSlatePlugin({
2481
2594
  normalizeInitialValue: normalizeInitialValueTable,
2482
2595
  options: {
2483
2596
  _cellIndices: {},
2597
+ _selectedCellIds: void 0,
2598
+ _selectedTableIds: void 0,
2599
+ _selectionVersion: 0,
2484
2600
  disableMerge: false,
2485
2601
  minColumnWidth: 48,
2486
2602
  selectedCells: null,
@@ -2492,7 +2608,41 @@ const BaseTablePlugin = createTSlatePlugin({
2492
2608
  BaseTableCellPlugin,
2493
2609
  BaseTableCellHeaderPlugin
2494
2610
  ]
2495
- }).extendSelectors(({ getOptions }) => ({ cellIndices: (id) => getOptions()._cellIndices[id] })).extendEditorApi(({ editor }) => ({
2611
+ }).extendSelectors(({ editor, getOptions }) => ({
2612
+ cellIndices: (id) => getOptions()._cellIndices[id],
2613
+ isCellSelected: (id) => {
2614
+ const selectedCellIds = getOptions()._selectedCellIds;
2615
+ if (selectedCellIds !== void 0) return !!id && (selectedCellIds?.includes(id) ?? false);
2616
+ return isCellSelected(editor, id);
2617
+ },
2618
+ isSelectingCell: () => {
2619
+ const selectedCellIds = getOptions()._selectedCellIds;
2620
+ if (selectedCellIds !== void 0) return !!selectedCellIds;
2621
+ return isSelectingCell(editor);
2622
+ },
2623
+ selectedCell: (id) => {
2624
+ getOptions()._selectionVersion;
2625
+ return getSelectedCell(editor, id);
2626
+ },
2627
+ selectedCellIds: () => {
2628
+ const selectedCellIds = getOptions()._selectedCellIds;
2629
+ if (selectedCellIds !== void 0) return selectedCellIds;
2630
+ return getSelectedCellIds(editor);
2631
+ },
2632
+ selectedCells: () => {
2633
+ getOptions()._selectionVersion;
2634
+ return getSelectedCells(editor);
2635
+ },
2636
+ selectedTableIds: () => {
2637
+ const selectedTableIds = getOptions()._selectedTableIds;
2638
+ if (selectedTableIds !== void 0) return selectedTableIds;
2639
+ return getSelectedTableIds(editor);
2640
+ },
2641
+ selectedTables: () => {
2642
+ getOptions()._selectionVersion;
2643
+ return getSelectedTables(editor);
2644
+ }
2645
+ })).extendEditorApi(({ editor }) => ({
2496
2646
  create: {
2497
2647
  table: bindFirst(getEmptyTableNode, editor),
2498
2648
  tableCell: bindFirst(getEmptyCellNode, editor),
@@ -2501,9 +2651,16 @@ const BaseTablePlugin = createTSlatePlugin({
2501
2651
  table: {
2502
2652
  getCellBorders: bindFirst(getTableCellBorders, editor),
2503
2653
  getCellSize: bindFirst(getTableCellSize, editor),
2654
+ getSelectedCell: bindFirst(getSelectedCell, editor),
2655
+ getSelectedCellIds: bindFirst(getSelectedCellIds, editor),
2656
+ getSelectedCells: bindFirst(getSelectedCells, editor),
2657
+ getSelectedTableIds: bindFirst(getSelectedTableIds, editor),
2658
+ getSelectedTables: bindFirst(getSelectedTables, editor),
2504
2659
  getColSpan,
2505
2660
  getRowSpan,
2506
- getCellChildren: (cell) => cell.children
2661
+ getCellChildren: (cell) => cell.children,
2662
+ isCellSelected: bindFirst(isCellSelected, editor),
2663
+ isSelectingCell: bindFirst(isSelectingCell, editor)
2507
2664
  }
2508
2665
  })).extendEditorTransforms(({ editor }) => ({
2509
2666
  insert: {
@@ -2532,5 +2689,4 @@ const KEY_SHIFT_EDGES = {
2532
2689
  };
2533
2690
 
2534
2691
  //#endregion
2535
- export { isSelectedCellBordersNone as $, mergeTableCells as A, getTableOverriddenColSizes as B, insertTableColumn as C, deleteColumn as D, deleteRow as E, deleteTableMergeRow as F, getCellIndicesWithSpans as G, getTableGridByRange as H, deleteRowWhenExpanded as I, getTableCellSize as J, getTableEntries as K, deleteTableMergeColumn as L, insertTableMergeRow as M, insertTableMergeColumn as N, normalizeInitialValueTable as O, getTableMergedColumnCount as P, isSelectedCellBorder as Q, getCellPath as R, insertTableRow as S, deleteTable as T, getTableMergeGridByRange as U, getTableGridAbove as V, findCellByIndexes as W, getTableAbove as X, getTableCellBorders as Y, getSelectedCellsBorders as Z, setTableMarginLeft as _, BaseTableRowPlugin as a, getNextTableCell as at, overrideSelectionFromCell as b, withSetFragmentDataTable as c, getCellRowIndexByPath as ct, withInsertFragmentTable as d, getColSpan as dt, isSelectedCellBordersOuter as et, withGetFragmentTable as f, getCellInPreviousTableRow as ft, setTableRowSize as g, getEmptyCellNode as gt, withApplyTable as h, getEmptyRowNode as ht, BaseTablePlugin as i, getPreviousTableCell as it, isTableRectangular as j, splitTableCell as k, withNormalizeTable as l, getCellIndices as lt, withDeleteTable as m, getEmptyTableNode as mt, BaseTableCellHeaderPlugin as n, getSelectedCellsBoundingBox as nt, withTable as o, getLeftTableCell as ot, preventDeleteTableCell as p, getCellInNextTableRow as pt, getTableColumnCount as q, BaseTableCellPlugin as r, getRowSpan as rt, withTableCellSelection as s, getCellTypes as st, KEY_SHIFT_EDGES as t, getTopTableCell as tt, withInsertTextTable as u, computeCellIndices as ut, setTableColSize as v, insertTable as w, moveSelectionFromCell as x, setBorderSize as y, deleteColumnWhenExpanded as z };
2536
- //# sourceMappingURL=constants-B6Sm9BNa.js.map
2692
+ export { getSelectedCellEntries as $, normalizeInitialValueTable as A, deleteColumnWhenExpanded as B, moveSelectionFromCell as C, getColSpan as Ct, deleteTable as D, insertTable as E, getEmptyCellNode as Et, getTableMergedColumnCount as F, getTableAbove as G, getTableColumnCount as H, deleteTableMergeRow as I, isSelectedCellBordersNone as J, getSelectedCellsBorders as K, deleteRowWhenExpanded as L, mergeTableCells as M, insertTableMergeRow as N, deleteRow as O, insertTableMergeColumn as P, getSelectedCell as Q, deleteTableMergeColumn as R, setBorderSize as S, getRowSpan as St, insertTableColumn as T, getEmptyRowNode as Tt, getTableCellSize as U, getTableOverriddenColSizes as V, getTableCellBorders as W, getTopTableCell as X, isSelectedCellBordersOuter as Y, getSelectedCellsBoundingBox as Z, hasAdjacentBlockInCell as _, getCellTypes as _t, BaseTableRowPlugin as a, isSelectingCell as at, setTableMarginLeft as b, computeCellIndices as bt, withSetFragmentDataTable as c, getTableMergeGridByRange as ct, withInsertFragmentTable as d, getNextTableCell as dt, getSelectedCellIds as et, withGetFragmentTable as f, getLeftTableCell as ft, getTableMoveSelectionContext as g, getTableEntries as gt, withApplyTable as h, getAdjacentTableCell as ht, BaseTablePlugin as i, isCellSelected as it, splitTableCell as j, deleteColumn as k, withNormalizeTable as l, findCellByIndexes as lt, withDeleteTable as m, getCellInNextTableRow as mt, BaseTableCellHeaderPlugin as n, getSelectedTableIds as nt, withTable as o, getTableGridAbove as ot, preventDeleteTableCell as p, getCellInPreviousTableRow as pt, isSelectedCellBorder as q, BaseTableCellPlugin as r, getSelectedTables as rt, withTableCellSelection as s, getTableGridByRange as st, KEY_SHIFT_EDGES as t, getSelectedCells as tt, withInsertTextTable as u, getPreviousTableCell as ut, shouldMoveSelectionFromCell as v, getCellRowIndexByPath as vt, insertTableRow as w, getEmptyTableNode as wt, setTableColSize as x, getCellIndicesWithSpans as xt, setTableRowSize as y, getCellIndices as yt, getCellPath as z };