@nerd-bible/wordgard 0.3.3

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/table.js ADDED
@@ -0,0 +1,1302 @@
1
+ import { Decoration, PointSet, Wordgard } from 'wordgard/editor';
2
+ import { GardState, Transaction, GardSelection, Correction } from 'wordgard/state';
3
+ import { Table, ColSpan, RowSpan, TableRow, Cell, BlockCell, HeaderCell, BlockHeaderCell } from 'wordgard/types';
4
+ import { Node, ValidationError, Token, ChangeSet } from 'wordgard/doc';
5
+ import { Command, moveByUnit, moveByWord, moveByLine, moveToLineSide, Menu } from 'wordgard/command';
6
+ import { tablePhrases } from 'wordgard/phrases';
7
+
8
+ class Rect {
9
+ startCol;
10
+ startRow;
11
+ endCol;
12
+ endRow;
13
+ constructor(startCol, startRow, endCol, endRow) {
14
+ this.startCol = startCol;
15
+ this.startRow = startRow;
16
+ this.endCol = endCol;
17
+ this.endRow = endRow;
18
+ }
19
+ }
20
+ class MapData {
21
+ table;
22
+ width;
23
+ height;
24
+ map;
25
+ problems;
26
+ cellEnds;
27
+ constructor(table, width, height, map, problems, cellEnds) {
28
+ this.table = table;
29
+ this.width = width;
30
+ this.height = height;
31
+ this.map = map;
32
+ this.problems = problems;
33
+ this.cellEnds = cellEnds;
34
+ }
35
+ }
36
+ let cache = /*@__PURE__*/(() => new WeakMap())();
37
+ class TableMap {
38
+ start;
39
+ data;
40
+ constructor(start, data) {
41
+ this.start = start;
42
+ this.data = data;
43
+ }
44
+ get width() { return this.data.width; }
45
+ get height() { return this.data.height; }
46
+ get table() { return this.data.table; }
47
+ get tablePos() { return this.start - 1; }
48
+ cellRect(pos) {
49
+ let localPos = pos - this.start, { map, width, height } = this.data;
50
+ for (let i = 0; i < map.length; i++)
51
+ if (map[i] == localPos) {
52
+ let startCol = i % width, startRow = (i / width) | 0;
53
+ let endCol = startCol + 1, endRow = startRow + 1;
54
+ for (let j = 1; endCol < width && map[i + j] == localPos; j++)
55
+ endCol++;
56
+ for (let j = 1; endRow < height && map[i + (width * j)] == localPos; j++)
57
+ endRow++;
58
+ return new Rect(startCol, startRow, endCol, endRow);
59
+ }
60
+ throw new RangeError(`No cell with offset ${pos} found`);
61
+ }
62
+ nearestCell(pos, bias) {
63
+ let localPos = pos - this.start, after = -1, before = -1;
64
+ let { map, cellEnds } = this.data;
65
+ for (let i = 0; i < map.length; i++) {
66
+ let cellPos = map[i];
67
+ if (cellPos > 0) {
68
+ if (cellPos >= localPos && (after < 0 || after > cellPos))
69
+ after = cellPos;
70
+ if (cellPos < localPos && before < cellPos)
71
+ before = cellPos;
72
+ }
73
+ }
74
+ if (before > -1) {
75
+ let beforeEnd = cellEnds.get(before);
76
+ if (beforeEnd > localPos || after < 0 || bias < 0)
77
+ return { from: before + this.start, to: beforeEnd + this.start };
78
+ }
79
+ return { from: after + this.start, to: cellEnds.get(after) + this.start };
80
+ }
81
+ cellEnd(pos) {
82
+ let end = this.data.cellEnds.get(pos - this.start);
83
+ if (end == null)
84
+ throw new Error(`No cell with offset ${pos} found`);
85
+ return end + this.start;
86
+ }
87
+ rectBetween(a, b) {
88
+ let { startCol: startColA, endCol: endColA, startRow: startRowA, endRow: endRowA } = this.cellRect(a);
89
+ let { startCol: startColB, endCol: endColB, startRow: startRowB, endRow: endRowB } = this.cellRect(b);
90
+ return new Rect(Math.min(startColA, startColB), Math.min(startRowA, startRowB), Math.max(endColA, endColB), Math.max(endRowA, endRowB));
91
+ }
92
+ cellsInRect(rect) {
93
+ let result = [], { map, width } = this.data;
94
+ for (let row = rect.startRow; row < rect.endRow; row++) {
95
+ for (let col = rect.startCol; col < rect.endCol; col++) {
96
+ let index = row * width + col, pos = map[index];
97
+ if (pos > 0 && result.indexOf(pos + this.start) < 0 &&
98
+ (col != rect.startCol || !col || map[index - 1] != pos) &&
99
+ (row != rect.startRow || !row || map[index - width] != pos))
100
+ result.push(pos + this.start);
101
+ }
102
+ }
103
+ return result;
104
+ }
105
+ cellAt(col, row) {
106
+ let { width, map } = this.data;
107
+ return map[col + row * width] + this.start || null;
108
+ }
109
+ rowPos(row) {
110
+ let { start } = this, { table } = this.data;
111
+ for (let r = 0; r < row; r++)
112
+ start += table.content[r].length;
113
+ return start;
114
+ }
115
+ cellInsertionPos(col, row) {
116
+ let { width, map } = this.data;
117
+ for (let scan = col;; scan++) {
118
+ if (scan == width)
119
+ return this.rowPos(row + 1) - 1;
120
+ let index = scan + row * width, pos = map[index];
121
+ if (pos && (!row || pos != map[index - width] && (!col || pos != map[index - 1])))
122
+ return pos + this.start;
123
+ }
124
+ }
125
+ getCell(pos) {
126
+ let found = this.data.table.plotAt(pos - this.start);
127
+ if (!found)
128
+ throw new Error("Invalid cell position");
129
+ return found;
130
+ }
131
+ cellsOverlapRectangle(rect) {
132
+ let { width, height, map } = this.data;
133
+ let indexTop = rect.startRow * width + rect.startCol, indexBefore = indexTop;
134
+ let indexBottom = (rect.endRow - 1) * width + rect.endCol, indexAfter = indexTop + (rect.endCol - rect.startCol - 1);
135
+ for (let i = rect.startRow; i < rect.endRow; i++) {
136
+ if (rect.startCol > 0 && sameCell(map[indexBefore], map[indexBefore - 1]) ||
137
+ rect.endCol < width && sameCell(map[indexAfter], map[indexAfter + 1]))
138
+ return true;
139
+ indexBefore += width;
140
+ indexAfter += width;
141
+ }
142
+ for (let i = rect.startCol; i < rect.endCol; i++) {
143
+ if (rect.startRow > 0 && sameCell(map[indexTop], map[indexTop - width]) ||
144
+ rect.endRow < height && sameCell(map[indexBottom], map[indexBottom + width]))
145
+ return true;
146
+ indexTop++;
147
+ indexBottom++;
148
+ }
149
+ return false;
150
+ }
151
+ static get(table, start) {
152
+ let data = cache.get(table);
153
+ if (!data)
154
+ cache.set(table, data = computeMap(table));
155
+ return new TableMap(start, data);
156
+ }
157
+ }
158
+ function sameCell(a, b) {
159
+ return a != 0 && a == b;
160
+ }
161
+ function computeMap(table) {
162
+ if (table.tag != Table)
163
+ throw new RangeError(`Not a table node: ${table.type.name}`);
164
+ let width = table.content[0].content.reduce((w, c) => w + (c.mark(ColSpan) ?? 1), 0);
165
+ let height = table.content.length;
166
+ let map = [], problems = null;
167
+ for (let i = 0, e = width * height; i < e; i++)
168
+ map[i] = 0;
169
+ let cellEnd = new Map();
170
+ for (let row = 0, pos = 0; row < height; row++) {
171
+ let rowNode = table.content[row], mapPos = row * width;
172
+ pos++;
173
+ for (let i = 0, col = 0;; i++) {
174
+ while (mapPos < map.length && map[mapPos] != 0)
175
+ mapPos++;
176
+ if (i == rowNode.content.length)
177
+ break;
178
+ let cellNode = rowNode.content[i];
179
+ cellEnd.set(pos, pos + cellNode.length);
180
+ let colSpan = cellNode.mark(ColSpan) ?? 1, rowSpan = cellNode.mark(RowSpan) ?? 1;
181
+ let exceed = col + colSpan - width;
182
+ if (exceed > 0) {
183
+ map = growMap(map, width, height, exceed);
184
+ width += exceed;
185
+ mapPos += row * exceed;
186
+ }
187
+ for (let h = 0; h < rowSpan; h++) {
188
+ if (h + row >= height) {
189
+ (problems || (problems = [])).push({ type: "overlong_rowspan", pos, n: rowSpan - h });
190
+ break;
191
+ }
192
+ let start = mapPos + (h * width), collided = 0;
193
+ for (let w = 0; w < colSpan; w++) {
194
+ if (map[start + w] == 0)
195
+ map[start + w] = pos;
196
+ else
197
+ collided++;
198
+ }
199
+ if (collided)
200
+ (problems || (problems = [])).push({ type: "collision", pos });
201
+ }
202
+ mapPos += colSpan;
203
+ col += colSpan;
204
+ pos += cellNode.length;
205
+ }
206
+ pos++;
207
+ }
208
+ for (let row = 0, i = width; row < height; row++, i += width) {
209
+ let missing = 0;
210
+ while (missing < width && map[i - missing - 1] == 0)
211
+ missing++;
212
+ if (missing)
213
+ (problems || (problems = [])).push({ type: "missing", row, n: missing });
214
+ }
215
+ return new MapData(table, width, height, map, problems, cellEnd);
216
+ }
217
+ function growMap(map, width, height, count) {
218
+ let newMap = [];
219
+ for (let row = 0, i = 0; row < height; row++) {
220
+ for (let col = 0; col < width; col++)
221
+ newMap.push(map[i++]);
222
+ for (let j = 0; j < count; j++)
223
+ newMap.push(0);
224
+ }
225
+ return newMap;
226
+ }
227
+
228
+ const cellSelectionDeco = /*@__PURE__*/GardState.Field.define({
229
+ create: getCellDeco,
230
+ update: (deco, tr) => {
231
+ return tr.docChanged || tr.selection ? getCellDeco(tr.state) : deco;
232
+ },
233
+ provide: f => Decoration.Point.source.of(s => s.field(f))
234
+ });
235
+ const selectedCell = /*@__PURE__*/Decoration.Point.attributes({ class: "wg-selected-cell" });
236
+ function getCellDeco(state) {
237
+ if (!(state.selection instanceof CellSelection))
238
+ return PointSet.empty;
239
+ return PointSet.create(state.selection.ranges.map(({ from }) => [from - 1, selectedCell]));
240
+ }
241
+ const tableSelectionFilter = /*@__PURE__*/(() => GardState.prec.low(Transaction.extender.of(tr => {
242
+ let normalized = CellSelection.normalize(tr.newSelection, tr.newDoc);
243
+ return normalized ? { selection: normalized } : null;
244
+ })))();
245
+ function resolveDir(dir, state) {
246
+ let block = state.sel.head.textblockParent;
247
+ return (dir == "right") == (block ? state.textblockLTR(block.node) : state.textLTR)
248
+ ? "forward" : "backward";
249
+ }
250
+ function cursorCommand(wg, { dir, extend }) {
251
+ let { state } = wg, { selection } = state;
252
+ if (!(selection instanceof CellSelection))
253
+ return false;
254
+ let newSel;
255
+ if (!extend) {
256
+ newSel = GardSelection.near(state, selection.replacementRange.from, 1);
257
+ }
258
+ else {
259
+ if (dir == "left" || dir == "right")
260
+ dir = resolveDir(dir, state);
261
+ newSel = selection.moveHead(state.doc, dir);
262
+ if (!newSel) {
263
+ let forward = dir == "forward" || dir == "down";
264
+ let table = state.sel.from.parent.parent;
265
+ let next = GardSelection.near(state, forward ? table.after : table.before, forward ? 1 : -1);
266
+ newSel = GardSelection.range(forward ? table.before : table.after, next.head, next.headSide);
267
+ }
268
+ }
269
+ wg.dispatch({
270
+ selection: newSel,
271
+ scrollIntoView: true,
272
+ userEvent: "select"
273
+ });
274
+ return true;
275
+ }
276
+ function moveToRowSide(wg, { dir, extend }) {
277
+ let { state } = wg, { selection } = state;
278
+ if (!(selection instanceof CellSelection))
279
+ return false;
280
+ if (dir == "left" || dir == "right")
281
+ dir = resolveDir(dir, state);
282
+ for (;;) {
283
+ let next = selection.moveHead(state.doc, dir);
284
+ if (!next)
285
+ break;
286
+ selection = next;
287
+ }
288
+ if (selection != state.selection)
289
+ wg.dispatch({
290
+ selection,
291
+ scrollIntoView: true,
292
+ userEvent: "select"
293
+ });
294
+ return true;
295
+ }
296
+ const cellSelectionTripleClick = /*@__PURE__*/Wordgard.mouseSelectionStyle.of((wg, event) => {
297
+ if (event.detail == 3) {
298
+ let pos = wg.state.doc.resolve(wg.posAtCoords({ x: event.clientX, y: event.clientY }).pos);
299
+ let cell = pos.matchingParent(n => wg.state.schema.matchNode(n.type, Node.Group.TableCell));
300
+ if (cell) {
301
+ let from = cell.before, to = cell.after;
302
+ return {
303
+ get(event) { return CellSelection.between(wg.state.doc, from, to) || GardSelection.near(wg.state, from, 1); },
304
+ update(update) { from = update.changes.mapPos(from, 1); to = Math.max(from, update.changes.mapPos(to, -1)); }
305
+ };
306
+ }
307
+ }
308
+ return null;
309
+ });
310
+ class CellSelection extends GardSelection {
311
+ anchorCell;
312
+ headCell;
313
+ _ranges;
314
+ anchorRange;
315
+ constructor(anchor, head,
316
+ anchorCell,
317
+ headCell,
318
+ _ranges,
319
+ anchorRange) {
320
+ super(anchor, head);
321
+ this.anchorCell = anchorCell;
322
+ this.headCell = headCell;
323
+ this._ranges = _ranges;
324
+ this.anchorRange = anchorRange;
325
+ }
326
+ get ranges() { return this._ranges; }
327
+ get replacementRange() { return this._ranges[this.anchorRange]; }
328
+ get domSelection() {
329
+ let { from, to } = this.replacementRange;
330
+ return { anchor: from, anchorSide: 1, head: to, headSide: -1 };
331
+ }
332
+ eq(other) {
333
+ return other instanceof CellSelection && other.anchor == this.anchor && other.head == this.head;
334
+ }
335
+ map(changes, cx, assoc = -1) {
336
+ let fromPos = changes.mapPos(this.from, 1), toPos = changes.mapPos(this.to, -1);
337
+ let from = cx.doc.resolve(fromPos), to = cx.doc.resolve(toPos);
338
+ let after = from.nodeAfter, before = to.nodeBefore;
339
+ if (after && after.type == Table.type)
340
+ fromPos += 2;
341
+ else if (after && after.type == TableRow.type)
342
+ fromPos++;
343
+ if (before && before.type == Table.type)
344
+ toPos -= 2;
345
+ else if (before && before.type == TableRow.type)
346
+ toPos--;
347
+ return (this.from == this.anchor ? CellSelection.between(cx.doc, fromPos, toPos) : CellSelection.between(cx.doc, toPos, fromPos))
348
+ || GardSelection.near(cx, changes.mapPos(this.head), assoc);
349
+ }
350
+ moveHead(doc, dir) {
351
+ let head = doc.resolve(this.head), inv = this.head < this.anchor;
352
+ let headPos = this.head - (inv ? 0 : head.nodeBefore.length);
353
+ let table = head.parent.parent, map = TableMap.get(table.node, table.start), rect = map.cellRect(headPos);
354
+ let anchorPos = inv ? map.nearestCell(this.anchor, -1).from : this.anchor;
355
+ let col = dir == "backward" ? rect.startCol - 1 : dir == "forward" ? rect.endCol : rect.startCol;
356
+ let row = dir == "up" ? rect.startRow - 1 : dir == "down" ? rect.endRow : rect.startRow;
357
+ if (col < 0 || col >= map.width || row < 0 || row >= map.height)
358
+ return null;
359
+ let newHead = map.cellAt(col, row);
360
+ if (newHead == null)
361
+ return null;
362
+ return newHead >= anchorPos ? CellSelection.between(doc, anchorPos, map.cellEnd(newHead))
363
+ : CellSelection.between(doc, newHead, map.cellEnd(anchorPos));
364
+ }
365
+ static between(doc, anchor, head) {
366
+ let from = doc.resolve(Math.min(anchor, head)), to = doc.resolve(Math.max(anchor, head));
367
+ let fromCell = from.nodeAfter, toCell = to.nodeBefore, table = from.parent?.parent;
368
+ if (anchor == head ||
369
+ !fromCell || !doc.schema.matchNode(fromCell.type, Node.Group.TableCell) ||
370
+ !toCell || !doc.schema.matchNode(toCell.type, Node.Group.TableCell) ||
371
+ !table || table.start > to.pos || table.end < to.pos)
372
+ return null;
373
+ let toPos = to.pos - toCell.length;
374
+ let map = TableMap.get(table.node, table.start);
375
+ let cells = map.cellsInRect(map.rectBetween(from.pos, toPos));
376
+ let anchorCell = anchor, headCell = head;
377
+ if (anchor > head)
378
+ anchorCell -= toCell.length;
379
+ else
380
+ headCell -= toCell.length;
381
+ return new CellSelection(anchor, head, anchorCell, headCell, cells.map(pos => ({ from: pos + 1, to: map.cellEnd(pos) - 1 })), cells.indexOf(head - (head < anchor ? 0 : toCell.length)));
382
+ }
383
+ static normalize(sel, doc) {
384
+ if (sel instanceof CellSelection)
385
+ return null;
386
+ let { from, to } = sel, modified = false;
387
+ for (let parent = doc.resolve(sel.from).parent, cell = null; parent; parent = parent.parent) {
388
+ if (doc.schema.matchNode(parent.node.type, Node.Group.TableCell))
389
+ cell = parent;
390
+ if (parent.node.type == Table.type) {
391
+ if (to > parent.end) {
392
+ from = parent.before;
393
+ modified = true;
394
+ }
395
+ else if (!cell || to > cell.end) {
396
+ let map = TableMap.get(parent.node, parent.start);
397
+ let start = map.nearestCell(from, 1), end = map.nearestCell(to, -1);
398
+ if (start.from > end.from)
399
+ end = start;
400
+ return sel.anchor < sel.head
401
+ ? CellSelection.between(doc, start.from, end.to)
402
+ : CellSelection.between(doc, end.to, start.from);
403
+ }
404
+ }
405
+ }
406
+ for (let parent = doc.resolve(sel.to).parent; parent; parent = parent.parent) {
407
+ if (parent.node.type == Table.type && from < parent.start) {
408
+ to = parent.after;
409
+ modified = true;
410
+ }
411
+ }
412
+ return !modified ? null : sel.anchor < sel.head ? GardSelection.range(from, to) : GardSelection.range(to, from);
413
+ }
414
+ static extension = /*@__PURE__*/(() => [
415
+ GardSelection.define("cell", CellSelection, sel => ({ anchor: sel.anchor, head: sel.head }), (doc, json) => {
416
+ if (!json || typeof json.anchor != "number" || typeof json.head != "number")
417
+ throw new ValidationError("Invalid JSON data for CellSelection");
418
+ let sel = CellSelection.between(doc, json.anchor, json.head);
419
+ if (!sel)
420
+ throw new ValidationError("Cell selection from JSON doesn't span actual cells");
421
+ return sel;
422
+ }),
423
+ cellSelectionDeco,
424
+ tableSelectionFilter,
425
+ Command.handler(moveByUnit, cursorCommand),
426
+ Command.handler(moveByWord, cursorCommand),
427
+ Command.handler(moveByLine, cursorCommand),
428
+ Command.handler(moveToLineSide, moveToRowSide),
429
+ cellSelectionTripleClick
430
+ ])();
431
+ }
432
+
433
+ const tableCorrection = /*@__PURE__*/Correction.onContent(Table, pos => {
434
+ let map = TableMap.get(pos.node, pos.start);
435
+ if (!map.data.problems)
436
+ return null;
437
+ let { schema } = pos.doc;
438
+ let mustAdd = [], changes = [];
439
+ for (let i = 0; i < map.height; i++)
440
+ mustAdd.push(0);
441
+ for (let i = 0; i < map.data.problems.length; i++) {
442
+ let prob = map.data.problems[i];
443
+ if (prob.type == "collision") {
444
+ let pos = prob.pos + map.start, rect = map.cellRect(pos), cell = map.getCell(pos);
445
+ let colSpan = ColSpan.isInSet(cell.marks), rowSpan = RowSpan.isInSet(cell.marks);
446
+ if (colSpan)
447
+ changes.push({ from: pos, remove: colSpan });
448
+ if (rowSpan)
449
+ changes.push({ from: pos, remove: rowSpan });
450
+ for (let row = rect.startRow, endRow = row + (rowSpan ? rowSpan.value : 1), first = true; row < endRow; row++) {
451
+ for (let col = rect.startCol, endCol = col + (colSpan ? colSpan.value : 1); col < endCol; col++) {
452
+ if (first) {
453
+ first = false;
454
+ }
455
+ else if (map.cellAt(col, row) == pos) {
456
+ let from = pos;
457
+ for (let scan = 0; from == pos; scan++)
458
+ from = map.cellInsertionPos(col + scan, row);
459
+ changes.push({ from, insert: [schema.createAndFill(cell.type.default)] });
460
+ }
461
+ }
462
+ }
463
+ }
464
+ else if (prob.type == "missing") {
465
+ mustAdd[prob.row] += prob.n;
466
+ }
467
+ else if (prob.type == "overlong_rowspan") {
468
+ let cell = map.getCell(prob.pos + map.start), cur = RowSpan.isInSet(cell.marks), newVal = cur.value - prob.n;
469
+ let from = pos.start + prob.pos;
470
+ changes.push(newVal == 1 ? { from, remove: cur } : { from, add: RowSpan.of(newVal) });
471
+ }
472
+ }
473
+ let first, last;
474
+ for (let i = 0; i < mustAdd.length; i++)
475
+ if (mustAdd[i]) {
476
+ if (first == null)
477
+ first = i;
478
+ last = i;
479
+ }
480
+ for (let i = 0, curPos = pos.start; i < map.height; i++) {
481
+ let row = pos.node.content[i], end = curPos + row.length;
482
+ let add = mustAdd[i];
483
+ if (add > 0) {
484
+ let cell = schema.defaultContentPlot(row.tag.type);
485
+ let nodes = [];
486
+ for (let j = 0; j < add; j++)
487
+ nodes.push(schema.createAndFill(cell));
488
+ let side = (i == 0 || first == i - 1) && last == i ? curPos + 1 : end - 1;
489
+ changes.push({ from: side, insert: nodes });
490
+ }
491
+ curPos = end;
492
+ }
493
+ return changes;
494
+ });
495
+
496
+ function tableContext(state, pos) {
497
+ let table, cells;
498
+ if (pos != null || !(state.selection instanceof CellSelection)) {
499
+ let ref = pos != null ? state.doc.resolve(pos) : state.sel.head;
500
+ let cellPos = ref.matchingParent(node => state.schema.matchNode(node.type, Node.Group.TableCell));
501
+ if (cellPos && cellPos.parent?.parent) {
502
+ table = cellPos.parent.parent;
503
+ cells = [cellPos.before];
504
+ }
505
+ }
506
+ else {
507
+ table = state.sel.anchor.parent.parent;
508
+ cells = state.selection.ranges.map(r => r.from - 1);
509
+ }
510
+ return cells ? { cells, map: TableMap.get(table.node, table.start) } : null;
511
+ }
512
+ function cellTag(schema) {
513
+ if (schema.has(Cell))
514
+ return Cell;
515
+ if (schema.has(BlockCell))
516
+ return BlockCell;
517
+ throw new Error(`No cell type in schema`);
518
+ }
519
+ function headerCellTag(schema) {
520
+ if (schema.has(HeaderCell))
521
+ return HeaderCell;
522
+ if (schema.has(BlockHeaderCell))
523
+ return BlockHeaderCell;
524
+ return null;
525
+ }
526
+ const toggleHeaderCell = ({ state }) => {
527
+ let cx = tableContext(state), header = headerCellTag(state.schema);
528
+ if (!cx || !header)
529
+ return false;
530
+ let cells = cx.cells.map(c => cx.map.getCell(c));
531
+ let changes = [];
532
+ if (cells.some(x => x.type != header.type)) {
533
+ for (let i = 0; i < cells.length; i++) {
534
+ let cell = cells[i], pos = cx.cells[i];
535
+ if (cell.type != header.type)
536
+ changes.push({ from: pos, to: pos + 1, insert: [state.schema.withMarksFrom(cell.tag, header)] });
537
+ }
538
+ }
539
+ else {
540
+ let tag = cellTag(state.schema);
541
+ for (let i = 0; i < cells.length; i++) {
542
+ let cell = cells[i], pos = cx.cells[i];
543
+ if (cell.type == header.type)
544
+ changes.push({ from: pos, to: pos + 1, insert: [state.schema.withMarksFrom(cell.tag, tag)] });
545
+ }
546
+ }
547
+ return { changes };
548
+ };
549
+ function selectedRect(state, cx) {
550
+ return state.selection instanceof CellSelection
551
+ ? cx.map.rectBetween(state.selection.anchorCell, state.selection.headCell)
552
+ : cx.map.cellRect(cx.cells[0]);
553
+ }
554
+ const addColumn = ({ state }, side) => {
555
+ let cx = tableContext(state);
556
+ if (!cx)
557
+ return false;
558
+ let { map } = cx, rect = selectedRect(state, cx);
559
+ let col = side == "before" ? rect.startCol : rect.endCol;
560
+ let changes = [], adjusted = new Set();
561
+ for (let row = 0, pos; row < map.height; row++) {
562
+ if (col > 0 && col < map.width && map.cellAt(col - 1, row) == (pos = map.cellAt(col, row)) && pos != null) {
563
+ if (!adjusted.has(pos)) {
564
+ let value = map.getCell(pos).mark(ColSpan) ?? 1;
565
+ changes.push({ from: pos, add: ColSpan.of(value + 1) });
566
+ adjusted.add(pos);
567
+ }
568
+ }
569
+ else {
570
+ changes.push({ from: map.cellInsertionPos(col, row), insert: [state.schema.createAndFill(cellTag(state.schema))] });
571
+ }
572
+ }
573
+ return {
574
+ changes,
575
+ userEvent: "insert.column"
576
+ };
577
+ };
578
+ const deleteColumn = ({ state }) => {
579
+ let cx = tableContext(state);
580
+ if (!cx)
581
+ return false;
582
+ let { map } = cx, rect = selectedRect(state, cx);
583
+ if (rect.startCol == 0 && rect.endCol == map.width) {
584
+ return {
585
+ changes: { from: map.tablePos, to: map.tablePos + map.table.length, fit: true },
586
+ selection: cx => GardSelection.near(cx, map.tablePos, -1),
587
+ userevent: "delete.table"
588
+ };
589
+ }
590
+ let changes = [], handled = new Set(), delPos = state.selection.from;
591
+ for (let row = 0; row < map.height; row++) {
592
+ for (let col = rect.startCol; col < rect.endCol;) {
593
+ let cell = map.cellAt(col, row);
594
+ if (cell == null)
595
+ continue;
596
+ let node = map.getCell(cell), span = node.mark(ColSpan) ?? 1;
597
+ if (col == rect.startCol && col > 0 && map.cellAt(col - 1, row) == cell) {
598
+ let cellRect = map.cellRect(cell);
599
+ if (!handled.has(cell)) {
600
+ let newSpan = rect.startCol - cellRect.startCol + (Math.max(0, cellRect.endCol - rect.endCol));
601
+ changes.push(newSpan == 1 ? { from: cell, remove: ColSpan.isInSet(node.marks) }
602
+ : { from: cell, add: ColSpan.of(newSpan) });
603
+ handled.add(cell);
604
+ }
605
+ col = cellRect.endCol;
606
+ }
607
+ else if (col + span > rect.endCol) {
608
+ if (!handled.has(cell)) {
609
+ let newSpan = col + span - rect.endCol;
610
+ changes.push(newSpan == 1 ? { from: cell, remove: ColSpan.isInSet(node.marks) }
611
+ : { from: cell, add: ColSpan.of(newSpan) });
612
+ handled.add(cell);
613
+ }
614
+ break;
615
+ }
616
+ else {
617
+ if (!handled.has(cell)) {
618
+ changes.push({ from: cell, to: cell + node.length });
619
+ delPos = Math.min(delPos, cell);
620
+ handled.add(cell);
621
+ }
622
+ col += span;
623
+ }
624
+ }
625
+ }
626
+ return {
627
+ changes,
628
+ selection: cx => GardSelection.near(cx, delPos, -1),
629
+ userEvent: "delete.column"
630
+ };
631
+ };
632
+ const addRow = ({ state }, side) => {
633
+ let cx = tableContext(state);
634
+ if (!cx)
635
+ return false;
636
+ let { map } = cx, rect = selectedRect(state, cx);
637
+ let row = side == "before" ? rect.startRow : rect.endRow;
638
+ let changes = [], adjusted = new Set();
639
+ let cellCount = map.width;
640
+ if (row > 0 && row < map.height) {
641
+ for (let col = 0; col < map.width; col++) {
642
+ let above = map.cellAt(col, row - 1), below = map.cellAt(col, row);
643
+ if (above != null && above == below) {
644
+ cellCount--;
645
+ if (!adjusted.has(above)) {
646
+ let value = map.getCell(above).mark(RowSpan);
647
+ changes.push({ from: above, add: RowSpan.of(value + 1) });
648
+ adjusted.add(above);
649
+ }
650
+ }
651
+ }
652
+ }
653
+ let cell = state.schema.createAndFill(cellTag(state.schema)), content = [];
654
+ for (let i = 0; i < cellCount; i++)
655
+ content.push(cell);
656
+ changes.push({ from: map.rowPos(row), insert: [TableRow.create(content)] });
657
+ return {
658
+ changes,
659
+ userEvent: "insert.row"
660
+ };
661
+ };
662
+ const deleteRow = ({ state }) => {
663
+ let cx = tableContext(state);
664
+ if (!cx)
665
+ return false;
666
+ let { map } = cx, rect = selectedRect(state, cx);
667
+ if (rect.startRow == 0 && rect.endRow == map.height) {
668
+ return {
669
+ changes: { from: map.tablePos, to: map.tablePos + map.table.length, fit: true },
670
+ selection: cx => GardSelection.near(cx, map.tablePos, -1),
671
+ userEvent: "delete.table"
672
+ };
673
+ }
674
+ let changes = [], handled = new Set();
675
+ for (let col = 0; col < map.width; col++) {
676
+ if (rect.startRow > 0) {
677
+ let above = map.cellAt(col, rect.startRow - 1);
678
+ if (above != null && !handled.has(above) && map.cellAt(col, rect.startRow) == above) {
679
+ let cellRect = map.cellRect(above);
680
+ let rowsAbove = rect.startRow - cellRect.startRow, rowsBelow = Math.max(0, cellRect.endRow - rect.endRow);
681
+ let rows = rowsAbove + rowsBelow;
682
+ changes.push(rows == 1 ? { from: above, remove: RowSpan.isInSet(map.getCell(above).marks) }
683
+ : { from: above, add: RowSpan.of(rows) });
684
+ handled.add(above);
685
+ }
686
+ }
687
+ if (rect.endRow < map.height) {
688
+ let below = map.cellAt(col, rect.endRow);
689
+ if (below != null && !handled.has(below) && map.cellAt(col, rect.endRow - 1) == below) {
690
+ let cell = map.getCell(below), cellRect = map.cellRect(below);
691
+ let rowSpan = cellRect.endRow - rect.endRow;
692
+ changes.push({ from: below, to: below + cell.length });
693
+ let copy = cell.withMarks(rowSpan == 1 ? RowSpan.removeFromSet(cell.marks) : RowSpan.of(rowSpan).addToSet(cell.marks));
694
+ changes.push({ from: map.cellInsertionPos(cellRect.startCol, rect.endRow), insert: [copy] });
695
+ handled.add(below);
696
+ }
697
+ }
698
+ }
699
+ let delPos = state.selection.from;
700
+ for (let row = rect.startRow; row < rect.endRow; row++) {
701
+ let rowPos = map.rowPos(row), rowNode = map.table.content[row];
702
+ delPos = Math.min(delPos, rowPos);
703
+ changes.push({ from: rowPos, to: rowPos + rowNode.length });
704
+ }
705
+ return {
706
+ changes,
707
+ selection: cx => GardSelection.near(cx, delPos, -1),
708
+ userEvent: "delete.row"
709
+ };
710
+ };
711
+ const mergeCells = ({ state }) => {
712
+ if (!(state.selection instanceof CellSelection) || state.selection.ranges.length == 1)
713
+ return false;
714
+ let cx = tableContext(state);
715
+ let { map } = cx, rect = selectedRect(state, cx);
716
+ if (map.cellsOverlapRectangle(rect))
717
+ return false;
718
+ let movedContent = [], changes = [];
719
+ for (let i = 1; i < cx.cells.length; i++) {
720
+ let pos = cx.cells[i], node = map.getCell(pos);
721
+ if (node.content.length && !(node.content[0].isPlot && !node.content[0].content.length))
722
+ movedContent = movedContent.concat(node.content);
723
+ changes.push({ from: pos, to: pos + node.length });
724
+ }
725
+ let pos = cx.cells[0];
726
+ let width = rect.endCol - rect.startCol, height = rect.endRow - rect.startRow;
727
+ if (width > 1)
728
+ changes.push({ from: pos, add: ColSpan.of(width) });
729
+ if (height > 1)
730
+ changes.push({ from: pos, add: RowSpan.of(height) });
731
+ if (movedContent.length)
732
+ changes.push({ from: map.cellEnd(pos) - 1, insert: movedContent });
733
+ return {
734
+ changes,
735
+ selection: cx => CellSelection.between(cx.doc, pos, pos + cx.doc.nodeAt(pos).length),
736
+ userEvent: "join.cell"
737
+ };
738
+ };
739
+ const splitCell = ({ state }) => {
740
+ let cx = tableContext(state);
741
+ if (!cx || cx.cells.length > 1)
742
+ return false;
743
+ let { map } = cx, pos = cx.cells[0], node = map.getCell(pos);
744
+ let colSpan = ColSpan.isInSet(node.marks), rowSpan = RowSpan.isInSet(node.marks);
745
+ if (!colSpan && !rowSpan)
746
+ return false;
747
+ let changes = [], rect = map.cellRect(pos), lastInsert = -1;
748
+ for (let row = rect.startRow, first = true; row < rect.endRow; row++) {
749
+ let insertPos = lastInsert = map.cellInsertionPos(rect.endCol, row);
750
+ let cell = state.schema.createAndFill(node.type.default);
751
+ for (let col = rect.startCol; col < rect.endCol; col++) {
752
+ if (first) {
753
+ first = false;
754
+ continue;
755
+ }
756
+ changes.push({ from: insertPos, insert: [cell] });
757
+ }
758
+ }
759
+ if (colSpan)
760
+ changes.push({ from: pos, remove: colSpan });
761
+ if (rowSpan)
762
+ changes.push({ from: pos, remove: rowSpan });
763
+ return {
764
+ changes,
765
+ selection: (cx, changes) => CellSelection.between(cx.doc, pos, changes.mapPos(lastInsert, 1)),
766
+ userEvent: "split.cell"
767
+ };
768
+ };
769
+
770
+ function fitSlice(schema, parent, slice, context) {
771
+ let wrap = schema.findWrapping(schema.docTag.type, parent.type);
772
+ if (!wrap)
773
+ return null;
774
+ let content = [schema.createAndFill(parent)];
775
+ for (let i = wrap.length - 1; i >= 0; i--)
776
+ content = [wrap[i].create(content)];
777
+ let doc = schema.doc(content);
778
+ let changes = ChangeSet.create(doc, { from: wrap.length + 1, to: doc.length - wrap.length - 1, insert: slice, fit: context });
779
+ doc = changes.apply(doc);
780
+ let node = doc.length > wrap.length && doc.plotAt(wrap.length);
781
+ return node && node.type == parent.type ? node : null;
782
+ }
783
+ function isTableContent(schema, type) {
784
+ return type == TableRow.type || schema.matchNode(type, Node.Group.TableCell);
785
+ }
786
+ function pastedCells(schema, slice, context) {
787
+ let table = null, tok;
788
+ if (slice.content.length == 1 && (tok = slice.content[0]).tokenType == Token.Type.Node &&
789
+ tok.type == Table.type) {
790
+ table = tok;
791
+ }
792
+ else if (context.length && isTableContent(schema, context[0].type) ||
793
+ slice.content.some(tok => tok.tokenType != Token.Type.Close && isTableContent(schema, tok.type))) {
794
+ table = fitSlice(schema, Table, slice, context);
795
+ }
796
+ return table && ensureRectangular(schema, table.content.map(row => row.content));
797
+ }
798
+ function ensureRectangular(schema, rows) {
799
+ let widths = [];
800
+ for (let i = 0; i < rows.length; i++) {
801
+ for (let cell of rows[i]) {
802
+ let rowSpan = cell.mark(RowSpan) ?? 1, colSpan = cell.mark(ColSpan) ?? 1;
803
+ for (let r = i; r < i + rowSpan; r++)
804
+ widths[r] = (widths[r] || 0) + colSpan;
805
+ }
806
+ }
807
+ let width = widths.reduce((a, b) => Math.max(a, b));
808
+ for (let r = 0; r < widths.length; r++) {
809
+ if (r >= rows.length)
810
+ rows[r] = [];
811
+ for (let i = widths[r]; i < width; i++)
812
+ rows[r] = rows[r].concat(schema.createAndFill(cellTag(schema)));
813
+ }
814
+ return { height: rows.length, width, rows };
815
+ }
816
+ function clipCells({ width, height, rows }, newWidth, newHeight) {
817
+ if (width != newWidth) {
818
+ let added = new Map();
819
+ rows = rows.map((row, i) => {
820
+ let cells = [], size = added.get(i) ?? 0, j = 0;
821
+ while (size < newWidth) {
822
+ let nextCell = row[(j++) % row.length];
823
+ let rowSpan = nextCell.mark(RowSpan) ?? 1, colSpan = nextCell.mark(ColSpan) ?? 1;
824
+ if (size + colSpan > newWidth) {
825
+ colSpan = newWidth - size;
826
+ nextCell = nextCell.withMarks(colSpan == 1 ? ColSpan.removeFromSet(nextCell.marks)
827
+ : ColSpan.of(colSpan).addToSet(nextCell.marks));
828
+ }
829
+ for (let k = 1; k < rowSpan; k++)
830
+ added.set(i + k, (added.get(i + k) ?? 0) + colSpan);
831
+ size += colSpan;
832
+ cells.push(nextCell);
833
+ }
834
+ return cells;
835
+ });
836
+ }
837
+ if (height != newHeight) {
838
+ let newRows = [];
839
+ for (let i = 0; i < newHeight; i++) {
840
+ let nextRow = rows[i % rows.length], space = newHeight - i;
841
+ newRows.push(nextRow.map(cell => {
842
+ let rowSpan = cell.mark(RowSpan) ?? 1;
843
+ return rowSpan <= space ? cell :
844
+ cell.withMarks(space == 1 ? RowSpan.removeFromSet(cell.marks) : RowSpan.of(space).addToSet(cell.marks));
845
+ }));
846
+ }
847
+ rows = newRows;
848
+ }
849
+ return { height: newHeight, width: newWidth, rows };
850
+ }
851
+ function growTableHorizontally(schema, map, width) {
852
+ let changes = [];
853
+ if (width > map.width) {
854
+ for (let row = 0; row < map.height; row++) {
855
+ let lastCell = map.table.content[row].lastChild;
856
+ let fillCell = schema.createAndFill((lastCell || cellTag(schema)).type.default), cells = [];
857
+ for (let i = map.width; i < width; i++)
858
+ cells.push(fillCell);
859
+ changes.push({ from: map.cellInsertionPos(map.width, row), insert: cells });
860
+ }
861
+ }
862
+ return changes;
863
+ }
864
+ function growTableVertically(schema, map, height) {
865
+ let changes = [];
866
+ if (height > map.height) {
867
+ let cells = [];
868
+ for (let i = 0; i < map.width; i++)
869
+ cells.push(schema.createAndFill(cellTag(schema)));
870
+ let row = TableRow.create(cells), rows = [];
871
+ for (let i = map.height; i < height; i++)
872
+ rows.push(row);
873
+ changes.push({ from: map.rowPos(map.height), insert: rows });
874
+ }
875
+ return changes;
876
+ }
877
+ function isolateVertically(schema, map, startCol, endCol, row) {
878
+ let changes = [];
879
+ if (row == 0 || row == map.height)
880
+ return changes;
881
+ for (let col = startCol; col < endCol;) {
882
+ let pos = map.cellAt(col, row);
883
+ if (pos != null && map.cellAt(col, row - 1) == pos) {
884
+ let node = map.getCell(pos), rect = map.cellRect(pos);
885
+ let topRows = row - rect.startRow, botRows = rect.endRow - row;
886
+ changes.push(topRows == 1 ? { from: pos, remove: RowSpan.isInSet(node.marks) } : { from: pos, add: RowSpan.of(topRows) });
887
+ let split = node.tag.withMarks(botRows == 1 ? RowSpan.removeFromSet(node.marks) : RowSpan.of(botRows).addToSet(node.marks));
888
+ changes.push({ from: map.cellInsertionPos(rect.startCol, row), insert: [schema.createAndFill(split)] });
889
+ col = rect.endCol;
890
+ }
891
+ else {
892
+ col++;
893
+ }
894
+ }
895
+ return changes;
896
+ }
897
+ function isolateHorizontally(schema, map, startRow, endRow, col) {
898
+ let changes = [];
899
+ if (col == 0 || col == map.width)
900
+ return changes;
901
+ for (let row = startRow; row < endRow;) {
902
+ let pos = map.cellAt(col, row);
903
+ if (pos != null && map.cellAt(col - 1, row) == pos) {
904
+ let node = map.getCell(pos), rect = map.cellRect(pos);
905
+ let topCols = col - rect.startCol, botCols = rect.endCol - col;
906
+ changes.push(topCols == 1 ? { from: pos, remove: ColSpan.isInSet(node.marks) } : { from: pos, add: ColSpan.of(topCols) });
907
+ let split = node.tag.withMarks(botCols == 1 ? ColSpan.removeFromSet(node.marks) : ColSpan.of(botCols).addToSet(node.marks));
908
+ changes.push({ from: map.cellInsertionPos(col, row), insert: [schema.createAndFill(split)] });
909
+ row = rect.endRow;
910
+ }
911
+ else {
912
+ row++;
913
+ }
914
+ }
915
+ return changes;
916
+ }
917
+ function insertCells(state, map, startCol, startRow, cells, event) {
918
+ let { schema } = state.doc;
919
+ let endCol = startCol + cells.width, endRow = startRow + cells.height;
920
+ let doc = state.doc, changeSet = null, changes = [];
921
+ function flush() {
922
+ if (changes.length) {
923
+ let newSet = ChangeSet.create(doc, changes);
924
+ doc = newSet.apply(doc);
925
+ changeSet = changeSet ? changeSet.compose(newSet) : newSet;
926
+ let table = doc.resolvePlot(map.tablePos);
927
+ map = TableMap.get(table.node, table.start);
928
+ changes = [];
929
+ }
930
+ }
931
+ if (map.width < endCol)
932
+ changes = growTableHorizontally(schema, map, endCol);
933
+ else if (endCol < map.width)
934
+ changes = isolateHorizontally(schema, map, startRow, endRow, endCol);
935
+ flush();
936
+ if (startCol)
937
+ changes = isolateHorizontally(schema, map, startRow, endRow, startCol);
938
+ flush();
939
+ if (map.height < endRow)
940
+ changes = growTableVertically(schema, map, endRow);
941
+ else if (endRow < map.height)
942
+ changes = isolateVertically(schema, map, startCol, endCol, endRow);
943
+ flush();
944
+ if (startRow)
945
+ changes = isolateVertically(schema, map, startCol, endCol, startRow);
946
+ flush();
947
+ for (let i = 0; i < cells.height; i++) {
948
+ let content = cells.rows[i], row = startRow + i;
949
+ changes.push({ from: map.cellInsertionPos(startCol, row), to: map.cellInsertionPos(endCol, row), insert: content });
950
+ }
951
+ flush();
952
+ let startCell = map.cellAt(startCol, startRow);
953
+ let endCell = map.cellAt(endCol - 1, endRow - 1);
954
+ let selection = startCell == null || endCell == null ? undefined
955
+ : CellSelection.between(doc, startCell, endCell + map.getCell(endCell).length);
956
+ return {
957
+ changes: changeSet,
958
+ selection,
959
+ userEvent: `${event}.paste`
960
+ };
961
+ }
962
+ function handleTablePaste(state, slice, context, drop) {
963
+ if (state.readOnly)
964
+ return false;
965
+ let { schema } = state.doc;
966
+ if (drop == null && state.selection instanceof CellSelection) {
967
+ let cells = pastedCells(schema, slice, context);
968
+ if (!cells) {
969
+ let single = fitSlice(schema, cellTag(schema), slice, context);
970
+ if (!single)
971
+ return false;
972
+ cells = { width: 1, height: 1, rows: [[single]] };
973
+ }
974
+ let table = state.sel.from.parent.parent, map = TableMap.get(table.node, table.start);
975
+ let rect = map.rectBetween(state.selection.anchorCell, state.selection.headCell);
976
+ return insertCells(state, map, rect.startCol, rect.startRow, clipCells(cells, rect.endCol - rect.startCol, rect.endRow - rect.startRow), "paste");
977
+ }
978
+ else {
979
+ let cx = tableContext(state, drop), cells;
980
+ if (!cx || !(cells = pastedCells(schema, slice, context)))
981
+ return false;
982
+ let rect = cx.map.cellRect(cx.cells[0]);
983
+ return insertCells(state, cx.map, rect.startCol, rect.startRow, cells, drop == null ? "drop" : "paste");
984
+ }
985
+ }
986
+ const tablePasteHandler = /*@__PURE__*/Wordgard.pasteHandler.of((wg, _event, slice, context) => {
987
+ let tr = handleTablePaste(wg.state, slice, context);
988
+ return tr && (wg.dispatch(tr), true);
989
+ });
990
+ const tableDropHandler = /*@__PURE__*/Wordgard.dropHandler.of((wg, _event, pos, move, slice, context) => {
991
+ if (wg.state.readOnly)
992
+ return false;
993
+ let tr = handleTablePaste(wg.state, slice, context, pos);
994
+ if (!tr)
995
+ return false;
996
+ if (move) {
997
+ let clear = [];
998
+ wg.state.doc.iterate(move.from, move.to, (node, pos) => {
999
+ if (wg.state.schema.matchNode(node.type, Node.Group.TableCell))
1000
+ clear.push({ from: pos + 1, to: pos + node.length - 1, fit: true });
1001
+ });
1002
+ let tr2 = wg.state.update(Transaction.merge(wg.state, tr, { changes: clear }));
1003
+ wg.dispatch(tr2);
1004
+ }
1005
+ else {
1006
+ wg.dispatch(tr);
1007
+ }
1008
+ return true;
1009
+ });
1010
+
1011
+ const SVG = "http://www.w3.org/2000/svg";
1012
+ class DimensionPicker {
1013
+ wg;
1014
+ finish;
1015
+ dom;
1016
+ svg;
1017
+ announce;
1018
+ width = 2;
1019
+ height = 2;
1020
+ gridWidth = 6;
1021
+ gridHeight = 4;
1022
+ ltr;
1023
+ constructor(wg, finish) {
1024
+ this.wg = wg;
1025
+ this.finish = finish;
1026
+ this.dom = document.createElement("div");
1027
+ this.dom.className = "wg-dimension-picker";
1028
+ this.announce = this.dom.appendChild(document.createElement("div"));
1029
+ this.announce.className = "wg-dimension-announce";
1030
+ this.announce.setAttribute("aria-live", "polite");
1031
+ this.svg = this.dom.appendChild(document.createElementNS(SVG, "svg"));
1032
+ this.svg.setAttribute("aria-hidden", "true");
1033
+ this.ltr = wg.state.textLTR;
1034
+ this.render();
1035
+ this.dom.addEventListener("mousemove", e => {
1036
+ let rect = this.svg.getBoundingClientRect();
1037
+ let xOff = this.ltr ? e.clientX - 4 - rect.left : rect.right - 4 - e.clientX;
1038
+ let yOff = e.clientY - 4 - rect.top;
1039
+ let x = Math.max(0, Math.floor(xOff / 19)), y = Math.max(0, Math.floor(yOff / 19));
1040
+ this.setSize(x + 1, y + 1);
1041
+ });
1042
+ this.dom.addEventListener("mousedown", e => {
1043
+ if (e.button == 0) {
1044
+ e.preventDefault();
1045
+ this.finish(this.width, this.height);
1046
+ }
1047
+ });
1048
+ this.dom.addEventListener("keydown", e => {
1049
+ if (e.key == (this.ltr ? "ArrowLeft" : "ArrowRight") && this.width > 1) {
1050
+ this.setSize(this.width - 1, this.height);
1051
+ }
1052
+ else if (e.key == (this.ltr ? "ArrowRight" : "ArrowLeft") && this.width < 15) {
1053
+ this.setSize(this.width + 1, this.height);
1054
+ }
1055
+ else if (e.key == "ArrowUp" && this.height > 1) {
1056
+ this.setSize(this.width, this.height - 1);
1057
+ }
1058
+ else if (e.key == "ArrowDown" && this.height < 15) {
1059
+ this.setSize(this.width, this.height + 1);
1060
+ }
1061
+ else if (e.key == " " || e.key == "Enter") {
1062
+ this.finish(this.width, this.height);
1063
+ }
1064
+ else {
1065
+ return;
1066
+ }
1067
+ e.preventDefault();
1068
+ });
1069
+ }
1070
+ render() {
1071
+ this.dom.setAttribute("aria-label", tablePhrases.get(this.wg.state, "dimensions_title", this.width, this.height));
1072
+ this.announce.textContent = tablePhrases.get(this.wg.state, "dimensions_live", this.width, this.height);
1073
+ this.svg.textContent = "";
1074
+ let width = this.gridWidth * 19 + 4;
1075
+ this.svg.setAttribute("width", String(width));
1076
+ this.svg.setAttribute("height", String(this.gridHeight * 19 + 4));
1077
+ for (let y = 0; y < this.gridHeight; y++)
1078
+ for (let x = 0; x < this.gridWidth; x++) {
1079
+ let rect = this.svg.appendChild(document.createElementNS(SVG, "rect"));
1080
+ rect.setAttribute("width", String(15));
1081
+ rect.setAttribute("height", String(15));
1082
+ rect.setAttribute("x", String(this.ltr ? x * 19 + 4 : width - (x + 1) * 19));
1083
+ rect.setAttribute("y", String(y * 19 + 4));
1084
+ rect.setAttribute("class", "wg-dimension-cell" + (x < this.width && y < this.height ? " wg-dimension-cell-active" : ""));
1085
+ }
1086
+ }
1087
+ setSize(width, height) {
1088
+ if (width == this.width && height == this.height)
1089
+ return;
1090
+ this.width = Math.min(15, width);
1091
+ this.height = Math.min(15, height);
1092
+ if (this.gridWidth <= this.width)
1093
+ this.gridWidth = Math.min(15, this.width + 1);
1094
+ if (this.gridHeight <= this.height)
1095
+ this.gridHeight = Math.min(15, this.height + 1);
1096
+ this.render();
1097
+ }
1098
+ }
1099
+ function insertTable(wg, width, height) {
1100
+ let { state } = wg, { schema } = state.doc, cell = cellTag(schema);
1101
+ if (!cell)
1102
+ return;
1103
+ let cellNode = schema.createAndFill(cell), cells = [];
1104
+ for (let i = 0; i < width; i++)
1105
+ cells.push(cellNode);
1106
+ let row = TableRow.create(cells), rows = [];
1107
+ for (let i = 0; i < height; i++)
1108
+ rows.push(row);
1109
+ let table = Table.create(rows), { from, to } = state.selection.replacementRange;
1110
+ let changes = ChangeSet.create(state.doc, { from, to, insert: [table], fit: true });
1111
+ let tablePos = changes.findInserted(tag => tag.type == Table.type);
1112
+ wg.dispatch({
1113
+ changes,
1114
+ selection: cx => GardSelection.near(cx, tablePos == null ? from : tablePos + 3, 1),
1115
+ userEvent: "insert.table"
1116
+ });
1117
+ }
1118
+ const dimensionPicker = /*@__PURE__*/Menu.CustomControl.define({
1119
+ render(wg, done) {
1120
+ return new DimensionPicker(wg, (width, height) => {
1121
+ done();
1122
+ insertTable(wg, width, height);
1123
+ wg.focus();
1124
+ });
1125
+ }
1126
+ });
1127
+ function tableMenu() {
1128
+ return [
1129
+ tableMenu.createTable,
1130
+ tableMenu.modifyTable,
1131
+ tableMenu.toggleHeader,
1132
+ tableMenu.addRowAbove, tableMenu.addRowBelow, tableMenu.deleteRow,
1133
+ tableMenu.addColumnBefore, tableMenu.addColumnAfter, tableMenu.deleteColumn,
1134
+ tableMenu.mergeCells, tableMenu.splitCell
1135
+ ];
1136
+ }
1137
+ const tableIcon = {
1138
+ icon: "M0 23a23 13 0 0 1 13-13h74a13 13 0 0 1 13 13v54a 13 13 0 0 1 -13 13h-74a13 13 0 0 1 -13 -13v-54M7 31v14h25v-14h-25M37 31v14h26v-14h-26M68 31v14h25v-14h-26M7 50v14h25v-14h-25M37 50v14h26v-14h-26M68 50v14h25v-14h-26M7 69v8a6 6 0 0 0 6 6h19v-14h-25M37 69v14h26v-14h-26M68 69v14h19a6 6 0 0 0 6 -6v-8h-26"
1139
+ };
1140
+ ;tableMenu = /*@__PURE__*/(function (tableMenu) {
1141
+ tableMenu.createTable = Menu.Submenu.define({
1142
+ select(state) {
1143
+ return state.schema.has(Table) && !state.sel.head.matchingParent(plot => plot.type == Table.type);
1144
+ },
1145
+ label: tableIcon,
1146
+ description: tablePhrases.ref("insert_table"),
1147
+ enable: s => !s.readOnly,
1148
+ parent: Menu.Group.insert,
1149
+ rank: 70,
1150
+ content: [dimensionPicker]
1151
+ });
1152
+ tableMenu.modifyTable = Menu.Submenu.define({
1153
+ select(state) {
1154
+ return !!state.sel.head.matchingParent(plot => plot.type == Table.type);
1155
+ },
1156
+ label: tableIcon,
1157
+ description: tablePhrases.ref("modify_table"),
1158
+ parent: Menu.Group.block,
1159
+ rank: 90
1160
+ });
1161
+ tableMenu.toggleHeader = Menu.Button.define({
1162
+ run: toggleHeaderCell,
1163
+ select: state => !!headerCellTag(state.schema),
1164
+ label: tablePhrases.ref("toggle_header"),
1165
+ enable: s => !s.readOnly,
1166
+ parent: tableMenu.modifyTable,
1167
+ rank: 10
1168
+ });
1169
+ tableMenu.addRowAbove = Menu.Button.define({
1170
+ run: wg => Command.dispatch(wg, addRow, "before"),
1171
+ label: tablePhrases.ref("add_row_above"),
1172
+ enable: s => !s.readOnly,
1173
+ parent: tableMenu.modifyTable,
1174
+ rank: 20,
1175
+ });
1176
+ tableMenu.addRowBelow = Menu.Button.define({
1177
+ run: wg => Command.dispatch(wg, addRow, "after"),
1178
+ label: tablePhrases.ref("add_row_below"),
1179
+ enable: s => !s.readOnly,
1180
+ parent: tableMenu.modifyTable,
1181
+ rank: 21,
1182
+ });
1183
+ tableMenu.deleteRow = Menu.Button.define({
1184
+ run: deleteRow,
1185
+ label: tablePhrases.ref("delete_row"),
1186
+ enable: s => !s.readOnly,
1187
+ parent: tableMenu.modifyTable,
1188
+ rank: 25
1189
+ });
1190
+ tableMenu.addColumnBefore = Menu.Button.define({
1191
+ run: wg => Command.dispatch(wg, addColumn, "before"),
1192
+ label: tablePhrases.ref("add_col_before"),
1193
+ enable: s => !s.readOnly,
1194
+ parent: tableMenu.modifyTable,
1195
+ rank: 30,
1196
+ });
1197
+ tableMenu.addColumnAfter = Menu.Button.define({
1198
+ run: wg => Command.dispatch(wg, addColumn, "after"),
1199
+ label: tablePhrases.ref("add_col_after"),
1200
+ enable: s => !s.readOnly,
1201
+ parent: tableMenu.modifyTable,
1202
+ rank: 31,
1203
+ });
1204
+ tableMenu.deleteColumn = Menu.Button.define({
1205
+ run: deleteColumn,
1206
+ label: tablePhrases.ref("delete_col"),
1207
+ enable: s => !s.readOnly,
1208
+ parent: tableMenu.modifyTable,
1209
+ rank: 35,
1210
+ });
1211
+ tableMenu.mergeCells = Menu.Button.define({
1212
+ run: mergeCells,
1213
+ select: state => {
1214
+ let { selection } = state;
1215
+ return selection instanceof CellSelection && selection.ranges.length > 1 &&
1216
+ state.schema.has(ColSpan) && state.schema.has(RowSpan);
1217
+ },
1218
+ label: tablePhrases.ref("merge_cells"),
1219
+ enable: s => !s.readOnly,
1220
+ parent: tableMenu.modifyTable,
1221
+ rank: 40,
1222
+ });
1223
+ tableMenu.splitCell = Menu.Button.define({
1224
+ run: splitCell,
1225
+ select: state => {
1226
+ let { selection } = state;
1227
+ if (!(selection instanceof CellSelection) || selection.ranges.length != 1)
1228
+ return false;
1229
+ let cell = state.sel.from.nodeAfter;
1230
+ return !!(cell && (cell.mark(ColSpan) || cell.mark(RowSpan)));
1231
+ },
1232
+ label: tablePhrases.ref("split_cell"),
1233
+ enable: s => !s.readOnly,
1234
+ parent: tableMenu.modifyTable,
1235
+ rank: 41,
1236
+ });
1237
+ ;return tableMenu})(tableMenu);
1238
+
1239
+ const tableTheme = /*@__PURE__*/Wordgard.styles({
1240
+ table: {
1241
+ borderCollapse: "collapse",
1242
+ tableLayout: "fixed",
1243
+ width: "100%",
1244
+ overflow: "hidden"
1245
+ },
1246
+ "td, th": {
1247
+ verticalAlign: "top",
1248
+ border: "1px solid var(--wg-border-color)",
1249
+ padding: "3px 6px",
1250
+ textAlign: "left"
1251
+ },
1252
+ ".wg-selected-cell": {
1253
+ background: "#ddf",
1254
+ "&::selection, & ::selection": { backgroundColor: "transparent" },
1255
+ "& :focus ::selection, & :focus::selection": { backgroundColor: "Highlight" }
1256
+ },
1257
+ ".wg-dimension-announce": {
1258
+ position: "absolute",
1259
+ width: "0px",
1260
+ overflow: "hidden"
1261
+ },
1262
+ ".wg-dimension-cell": {
1263
+ fill: "none",
1264
+ stroke: "#ccc",
1265
+ strokeWidth: "1.5px",
1266
+ rx: "2px"
1267
+ },
1268
+ ".wg-dimension-cell-active": {
1269
+ stroke: "var(--wg-highlight-color)"
1270
+ }
1271
+ });
1272
+ function tables(config = {}) {
1273
+ let result = [
1274
+ GardState.schemaElement.of(Table), GardState.schemaElement.of(TableRow),
1275
+ tableTheme,
1276
+ CellSelection,
1277
+ tableCorrection,
1278
+ tablePasteHandler,
1279
+ tableDropHandler,
1280
+ tableMenu()
1281
+ ];
1282
+ if (config.cellContent == "block") {
1283
+ result.push(GardState.schemaElement.of(BlockCell));
1284
+ if (config.headerCells != false)
1285
+ result.push(GardState.schemaElement.of(BlockHeaderCell));
1286
+ }
1287
+ else {
1288
+ result.push(GardState.schemaElement.of(Cell));
1289
+ if (config.headerCells != false)
1290
+ result.push(GardState.schemaElement.of(HeaderCell));
1291
+ }
1292
+ if (config.cellSpanning != false)
1293
+ result.push(GardState.schemaElement.of(RowSpan), GardState.schemaElement.of(ColSpan));
1294
+ return result;
1295
+ }
1296
+ ;tables = /*@__PURE__*/(function (tables) {
1297
+ tables.correction = tableCorrection;
1298
+ tables.pasteHandler = tablePasteHandler;
1299
+ tables.dropHandler = tableDropHandler;
1300
+ ;return tables})(tables);
1301
+
1302
+ export { CellSelection, addColumn, addRow, deleteColumn, deleteRow, handleTablePaste, mergeCells, splitCell, tableMenu, tables, toggleHeaderCell };