@mce/table 0.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1227 @@
1
+ import { TextEditor, definePlugin, useEditor } from "mce";
2
+ import { normalizeTextContent, textContentToString } from "modern-idoc";
3
+ import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onBeforeMount, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, vShow, watch, withDirectives, withModifiers } from "vue";
4
+ //#region src/table.ts
5
+ var DEFAULT_COL_WIDTH = 100;
6
+ var DEFAULT_ROW_HEIGHT = 40;
7
+ /** White cell fill so the canvas renders a solid table (not a transparent grid). */
8
+ var CELL_BACKGROUND = "#ffffff";
9
+ /** 1px gridline drawn by the canvas on each cell. */
10
+ var CELL_BORDER_STYLE = {
11
+ borderColor: "#e5e7eb",
12
+ borderWidth: 1,
13
+ borderStyle: "solid",
14
+ boxSizing: "border-box"
15
+ };
16
+ function clone(value) {
17
+ return JSON.parse(JSON.stringify(value ?? null));
18
+ }
19
+ function defaultTextStyle(header = false) {
20
+ return {
21
+ fontSize: 13,
22
+ color: "#333333",
23
+ textAlign: "center",
24
+ verticalAlign: "middle",
25
+ fontWeight: header ? 700 : 400
26
+ };
27
+ }
28
+ /**
29
+ * Build a fresh cell child. The text element is given a `width`/`height` to fill
30
+ * its cell at render time (see {@link modelToTable}) so `verticalAlign` centers
31
+ * the text — the canvas has no flex/percent path that does this otherwise.
32
+ */
33
+ function createCellChild(text = "", refStyle) {
34
+ return {
35
+ style: refStyle ? clone(refStyle) : defaultTextStyle(),
36
+ text: { content: normalizeTextContent(text) },
37
+ meta: { inCanvasIs: "Element2D" }
38
+ };
39
+ }
40
+ function createCell(row, col, ref) {
41
+ return {
42
+ row,
43
+ col,
44
+ background: ref?.background ?? "#ffffff",
45
+ style: ref?.style ? clone(ref.style) : { ...CELL_BORDER_STYLE },
46
+ children: [createCellChild("", ref?.children?.[0]?.style)]
47
+ };
48
+ }
49
+ /** Deep-clone a live (normalized) table into an editable model. */
50
+ function cloneTableModel(table) {
51
+ return {
52
+ columns: (table.columns ?? []).map((c) => ({ width: c.width ?? DEFAULT_COL_WIDTH })),
53
+ rows: (table.rows ?? []).map((r) => ({ height: r.height ?? DEFAULT_ROW_HEIGHT })),
54
+ cells: clone(table.cells ?? [])
55
+ };
56
+ }
57
+ /**
58
+ * Serialise the model into the shape `Element2DTable.setProperties` accepts,
59
+ * sizing each cell's text child to its (possibly merged) cell rect so the
60
+ * canvas vertically/horizontally centers the text.
61
+ */
62
+ function modelToTable(model) {
63
+ const cells = clone(model.cells).map((cell) => {
64
+ const rect = getCellRect(model, cell);
65
+ const child = cell.children?.[0];
66
+ if (child) child.style = {
67
+ ...child.style,
68
+ width: rect.width,
69
+ height: rect.height
70
+ };
71
+ return cell;
72
+ });
73
+ return {
74
+ columns: model.columns.map((c) => ({ width: c.width })),
75
+ rows: model.rows.map((r) => ({ height: r.height })),
76
+ cells
77
+ };
78
+ }
79
+ function gridWidth(model) {
80
+ return model.columns.reduce((sum, c) => sum + (c.width || 0), 0);
81
+ }
82
+ function gridHeight(model) {
83
+ return model.rows.reduce((sum, r) => sum + (r.height || 0), 0);
84
+ }
85
+ function colLeft(model, col) {
86
+ let x = 0;
87
+ for (let i = 0; i < col && i < model.columns.length; i++) x += model.columns[i].width || 0;
88
+ return x;
89
+ }
90
+ function rowTop(model, row) {
91
+ let y = 0;
92
+ for (let i = 0; i < row && i < model.rows.length; i++) y += model.rows[i].height || 0;
93
+ return y;
94
+ }
95
+ function cellRowSpan(cell) {
96
+ return Math.max(1, cell.rowSpan ?? 1);
97
+ }
98
+ function cellColSpan(cell) {
99
+ return Math.max(1, cell.colSpan ?? 1);
100
+ }
101
+ /** Pixel rect (relative to the table's top-left) for an anchor cell. */
102
+ function getCellRect(model, cell) {
103
+ const left = colLeft(model, cell.col);
104
+ const top = rowTop(model, cell.row);
105
+ let width = 0;
106
+ for (let c = cell.col; c < cell.col + cellColSpan(cell) && c < model.columns.length; c++) width += model.columns[c].width || 0;
107
+ let height = 0;
108
+ for (let r = cell.row; r < cell.row + cellRowSpan(cell) && r < model.rows.length; r++) height += model.rows[r].height || 0;
109
+ return {
110
+ left,
111
+ top,
112
+ width,
113
+ height
114
+ };
115
+ }
116
+ function cellRange(cell) {
117
+ return {
118
+ minRow: cell.row,
119
+ minCol: cell.col,
120
+ maxRow: cell.row + cellRowSpan(cell) - 1,
121
+ maxCol: cell.col + cellColSpan(cell) - 1
122
+ };
123
+ }
124
+ /** Map every covered grid position `"row:col"` to its anchor cell. */
125
+ function buildCoverage(model) {
126
+ const map = /* @__PURE__ */ new Map();
127
+ for (const cell of model.cells) for (let r = cell.row; r < cell.row + cellRowSpan(cell); r++) for (let c = cell.col; c < cell.col + cellColSpan(cell); c++) map.set(`${r}:${c}`, cell);
128
+ return map;
129
+ }
130
+ function getCellAt(model, row, col) {
131
+ return buildCoverage(model).get(`${row}:${col}`);
132
+ }
133
+ function getCellText(cell) {
134
+ const content = cell?.children?.[0]?.text?.content;
135
+ return content ? textContentToString(content) : "";
136
+ }
137
+ function setCellText(cell, text) {
138
+ if (!cell.children?.length) {
139
+ cell.children = [createCellChild(text)];
140
+ return;
141
+ }
142
+ const child = cell.children[0];
143
+ if (!child.text) child.text = {};
144
+ child.text.content = normalizeTextContent(text);
145
+ }
146
+ /**
147
+ * Expand a raw selection so it never cuts through a merged cell — any cell
148
+ * whose span overlaps the range pulls the range out to cover it fully.
149
+ */
150
+ function normalizeRange(model, range) {
151
+ const result = { ...range };
152
+ let changed = true;
153
+ while (changed) {
154
+ changed = false;
155
+ for (const cell of model.cells) {
156
+ const cr = cellRange(cell);
157
+ if (!(cr.minRow <= result.maxRow && cr.maxRow >= result.minRow && cr.minCol <= result.maxCol && cr.maxCol >= result.minCol)) continue;
158
+ if (cr.minRow < result.minRow) {
159
+ result.minRow = cr.minRow;
160
+ changed = true;
161
+ }
162
+ if (cr.minCol < result.minCol) {
163
+ result.minCol = cr.minCol;
164
+ changed = true;
165
+ }
166
+ if (cr.maxRow > result.maxRow) {
167
+ result.maxRow = cr.maxRow;
168
+ changed = true;
169
+ }
170
+ if (cr.maxCol > result.maxCol) {
171
+ result.maxCol = cr.maxCol;
172
+ changed = true;
173
+ }
174
+ }
175
+ }
176
+ return result;
177
+ }
178
+ /** Fill any uncovered grid position with a fresh 1×1 cell; clamp out-of-range spans. */
179
+ function repairModel(model) {
180
+ const numRows = model.rows.length;
181
+ const numCols = model.columns.length;
182
+ model.cells = model.cells.filter((cell) => cell.row < numRows && cell.col < numCols && cell.row >= 0 && cell.col >= 0);
183
+ for (const cell of model.cells) {
184
+ cell.rowSpan = Math.min(cellRowSpan(cell), numRows - cell.row);
185
+ cell.colSpan = Math.min(cellColSpan(cell), numCols - cell.col);
186
+ if (cell.rowSpan <= 1) delete cell.rowSpan;
187
+ if (cell.colSpan <= 1) delete cell.colSpan;
188
+ }
189
+ const claimed = /* @__PURE__ */ new Map();
190
+ model.cells = model.cells.filter((cell) => {
191
+ if (claimed.has(`${cell.row}:${cell.col}`)) return false;
192
+ claimed.set(`${cell.row}:${cell.col}`, cell);
193
+ return true;
194
+ });
195
+ const coverage = buildCoverage(model);
196
+ for (let r = 0; r < numRows; r++) for (let c = 0; c < numCols; c++) if (!coverage.get(`${r}:${c}`)) {
197
+ const cell = createCell(r, c);
198
+ model.cells.push(cell);
199
+ coverage.set(`${r}:${c}`, cell);
200
+ }
201
+ model.cells.sort((a, b) => a.row - b.row || a.col - b.col);
202
+ }
203
+ function insertRow(model, index) {
204
+ const numCols = model.columns.length;
205
+ const refHeight = model.rows[Math.min(index, model.rows.length - 1)]?.height ?? DEFAULT_ROW_HEIGHT;
206
+ model.rows.splice(index, 0, { height: refHeight });
207
+ for (const cell of model.cells) {
208
+ const span = cellRowSpan(cell);
209
+ if (index <= cell.row) cell.row += 1;
210
+ else if (index < cell.row + span) cell.rowSpan = span + 1;
211
+ }
212
+ const coverage = buildCoverage(model);
213
+ for (let c = 0; c < numCols; c++) if (!coverage.get(`${index}:${c}`)) {
214
+ const above = coverage.get(`${index - 1}:${c}`) ?? coverage.get(`${index + 1}:${c}`);
215
+ model.cells.push(createCell(index, c, above));
216
+ }
217
+ repairModel(model);
218
+ }
219
+ function insertColumn(model, index) {
220
+ const numRows = model.rows.length;
221
+ const refWidth = model.columns[Math.min(index, model.columns.length - 1)]?.width ?? DEFAULT_COL_WIDTH;
222
+ model.columns.splice(index, 0, { width: refWidth });
223
+ for (const cell of model.cells) {
224
+ const span = cellColSpan(cell);
225
+ if (index <= cell.col) cell.col += 1;
226
+ else if (index < cell.col + span) cell.colSpan = span + 1;
227
+ }
228
+ const coverage = buildCoverage(model);
229
+ for (let r = 0; r < numRows; r++) if (!coverage.get(`${r}:${index}`)) {
230
+ const before = coverage.get(`${r}:${index - 1}`) ?? coverage.get(`${r}:${index + 1}`);
231
+ model.cells.push(createCell(r, index, before));
232
+ }
233
+ repairModel(model);
234
+ }
235
+ function removeRow(model, index) {
236
+ if (model.rows.length <= 1) return;
237
+ model.rows.splice(index, 1);
238
+ const next = [];
239
+ for (const cell of model.cells) {
240
+ const span = cellRowSpan(cell);
241
+ const last = cell.row + span - 1;
242
+ if (index < cell.row) {
243
+ cell.row -= 1;
244
+ next.push(cell);
245
+ } else if (index > last) next.push(cell);
246
+ else {
247
+ if (span <= 1) continue;
248
+ cell.rowSpan = span - 1;
249
+ next.push(cell);
250
+ }
251
+ }
252
+ model.cells = next;
253
+ repairModel(model);
254
+ }
255
+ function removeColumn(model, index) {
256
+ if (model.columns.length <= 1) return;
257
+ model.columns.splice(index, 1);
258
+ const next = [];
259
+ for (const cell of model.cells) {
260
+ const span = cellColSpan(cell);
261
+ const last = cell.col + span - 1;
262
+ if (index < cell.col) {
263
+ cell.col -= 1;
264
+ next.push(cell);
265
+ } else if (index > last) next.push(cell);
266
+ else {
267
+ if (span <= 1) continue;
268
+ cell.colSpan = span - 1;
269
+ next.push(cell);
270
+ }
271
+ }
272
+ model.cells = next;
273
+ repairModel(model);
274
+ }
275
+ /** Merge a (normalized) range into its top-left cell, dropping the rest. */
276
+ function mergeRange(model, range) {
277
+ const r = normalizeRange(model, range);
278
+ if (r.minRow === r.maxRow && r.minCol === r.maxCol) return void 0;
279
+ const anchor = getCellAt(model, r.minRow, r.minCol);
280
+ if (!anchor) return void 0;
281
+ anchor.row = r.minRow;
282
+ anchor.col = r.minCol;
283
+ anchor.rowSpan = r.maxRow - r.minRow + 1;
284
+ anchor.colSpan = r.maxCol - r.minCol + 1;
285
+ model.cells = model.cells.filter((cell) => {
286
+ if (cell === anchor) return true;
287
+ return cell.row < r.minRow || cell.row > r.maxRow || cell.col < r.minCol || cell.col > r.maxCol;
288
+ });
289
+ if (anchor.rowSpan <= 1) delete anchor.rowSpan;
290
+ if (anchor.colSpan <= 1) delete anchor.colSpan;
291
+ model.cells.sort((a, b) => a.row - b.row || a.col - b.col);
292
+ return anchor;
293
+ }
294
+ /** Reset a merged cell back to 1×1, re-filling the freed positions. */
295
+ function splitCell(model, cell) {
296
+ if (cellRowSpan(cell) <= 1 && cellColSpan(cell) <= 1) return;
297
+ delete cell.rowSpan;
298
+ delete cell.colSpan;
299
+ repairModel(model);
300
+ }
301
+ function isMergedCell(cell) {
302
+ return cellRowSpan(cell) > 1 || cellColSpan(cell) > 1;
303
+ }
304
+ //#endregion
305
+ //#region src/create.ts
306
+ /** A grid table backed by the native `table` element property; first row is a header. */
307
+ function createTableElement(rows = 3, cols = 3, options = {}) {
308
+ const { width = 360, height = 160 } = options;
309
+ const colWidth = width / cols;
310
+ const rowHeight = height / rows;
311
+ const cells = [];
312
+ for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) cells.push({
313
+ row: r,
314
+ col: c,
315
+ background: CELL_BACKGROUND,
316
+ style: { ...CELL_BORDER_STYLE },
317
+ children: [{
318
+ style: {
319
+ ...defaultTextStyle(r === 0),
320
+ width: colWidth,
321
+ height: rowHeight
322
+ },
323
+ text: { content: normalizeTextContent(r === 0 ? `列 ${c + 1}` : "") },
324
+ meta: { inCanvasIs: "Element2D" }
325
+ }]
326
+ });
327
+ return {
328
+ style: {
329
+ width,
330
+ height
331
+ },
332
+ table: {
333
+ columns: Array.from({ length: cols }, () => ({ width: colWidth })),
334
+ rows: Array.from({ length: rows }, () => ({ height: rowHeight })),
335
+ cells
336
+ },
337
+ meta: {
338
+ inPptIs: "Shape",
339
+ inCanvasIs: "Element2D",
340
+ inEditorIs: "Table"
341
+ }
342
+ };
343
+ }
344
+ //#endregion
345
+ //#region src/TableEditor.vue?vue&type=script&setup=true&lang.ts
346
+ var _hoisted_1 = { class: "m-table-editor__bar-name" };
347
+ var _hoisted_2 = { class: "m-table-editor__bar-content" };
348
+ var _hoisted_3 = ["onPointerdown"];
349
+ var _hoisted_4 = ["onPointerdown"];
350
+ var _hoisted_5 = ["onPointerdown"];
351
+ var _hoisted_6 = ["onPointerdown"];
352
+ var _hoisted_7 = [
353
+ "onPointerdown",
354
+ "onPointerenter",
355
+ "onDblclick",
356
+ "onContextmenu"
357
+ ];
358
+ var _hoisted_8 = ["disabled"];
359
+ var _hoisted_9 = ["disabled"];
360
+ var COL_H = 22;
361
+ var ROW_W = 28;
362
+ var BAR_H = 26;
363
+ //#endregion
364
+ //#region src/TableEditor.vue
365
+ var TableEditor_default = /* @__PURE__ */ defineComponent({
366
+ __name: "TableEditor",
367
+ setup(__props) {
368
+ const { elementSelection, state, getObb, camera, t } = useEditor();
369
+ function colLabel(index) {
370
+ let s = "";
371
+ let n = index;
372
+ do {
373
+ s = String.fromCharCode(65 + n % 26) + s;
374
+ n = Math.floor(n / 26) - 1;
375
+ } while (n >= 0);
376
+ return s;
377
+ }
378
+ const editingEl = ref();
379
+ const model = ref();
380
+ const anchor = ref({
381
+ row: 0,
382
+ col: 0
383
+ });
384
+ const focus = ref({
385
+ row: 0,
386
+ col: 0
387
+ });
388
+ const editingPos = ref(null);
389
+ const selecting = ref(false);
390
+ const menu = ref(null);
391
+ const textHost = ref();
392
+ let textEditorEl;
393
+ const editingChild = ref();
394
+ let clipboard = null;
395
+ const active = computed(() => state.value === "tableEditing" && !!model.value);
396
+ const mainStyleWithScale = computed(() => {
397
+ const { zoom, position } = camera.value;
398
+ return {
399
+ transformOrigin: "left top",
400
+ transform: `translate(${-position.x}px, ${-position.y}px) scale(${zoom.x}, ${zoom.y})`
401
+ };
402
+ });
403
+ const tableBoxStyle = computed(() => {
404
+ const el = editingEl.value;
405
+ if (!el) return {};
406
+ const obb = getObb(el);
407
+ return {
408
+ left: `${obb.left}px`,
409
+ top: `${obb.top}px`,
410
+ width: `${gridWidth(model.value)}px`,
411
+ height: `${gridHeight(model.value)}px`,
412
+ transform: obb.rotationDegrees ? `rotate(${obb.rotationDegrees}deg)` : void 0,
413
+ transformOrigin: "center center"
414
+ };
415
+ });
416
+ const renderCells = computed(() => {
417
+ const m = model.value;
418
+ if (!m) return [];
419
+ return m.cells.map((cell) => ({
420
+ cell,
421
+ rect: getCellRect(m, cell)
422
+ }));
423
+ });
424
+ const colBoundaries = computed(() => {
425
+ const m = model.value;
426
+ if (!m) return [];
427
+ return m.columns.map((_, i) => colLeft(m, i + 1));
428
+ });
429
+ const rowBoundaries = computed(() => {
430
+ const m = model.value;
431
+ if (!m) return [];
432
+ return m.rows.map((_, i) => rowTop(m, i + 1));
433
+ });
434
+ const selectionRange = computed(() => {
435
+ const m = model.value;
436
+ if (!m) return {
437
+ minRow: 0,
438
+ minCol: 0,
439
+ maxRow: 0,
440
+ maxCol: 0
441
+ };
442
+ return normalizeRange(m, {
443
+ minRow: Math.min(anchor.value.row, focus.value.row),
444
+ minCol: Math.min(anchor.value.col, focus.value.col),
445
+ maxRow: Math.max(anchor.value.row, focus.value.row),
446
+ maxCol: Math.max(anchor.value.col, focus.value.col)
447
+ });
448
+ });
449
+ const selectionRectStyle = computed(() => {
450
+ const m = model.value;
451
+ if (!m) return {};
452
+ const r = selectionRange.value;
453
+ const left = colLeft(m, r.minCol);
454
+ const top = rowTop(m, r.minRow);
455
+ let width = 0;
456
+ for (let c = r.minCol; c <= r.maxCol; c++) width += m.columns[c]?.width || 0;
457
+ let height = 0;
458
+ for (let row = r.minRow; row <= r.maxRow; row++) height += m.rows[row]?.height || 0;
459
+ return {
460
+ left: `${left}px`,
461
+ top: `${top}px`,
462
+ width: `${width}px`,
463
+ height: `${height}px`
464
+ };
465
+ });
466
+ const isSingleSelection = computed(() => {
467
+ const r = selectionRange.value;
468
+ return r.minRow === r.maxRow && r.minCol === r.maxCol;
469
+ });
470
+ const activeCellRef = computed(() => `${colLabel(focus.value.col)}${focus.value.row + 1}`);
471
+ const activeCellText = computed(() => getCellText(model.value ? getCellAt(model.value, focus.value.row, focus.value.col) : void 0));
472
+ function isColActive(ci) {
473
+ const r = selectionRange.value;
474
+ return ci >= r.minCol && ci <= r.maxCol;
475
+ }
476
+ function isRowActive(ri) {
477
+ const r = selectionRange.value;
478
+ return ri >= r.minRow && ri <= r.maxRow;
479
+ }
480
+ const selectedAnchorMerged = computed(() => {
481
+ const m = model.value;
482
+ if (!m) return false;
483
+ const cell = getCellAt(m, selectionRange.value.minRow, selectionRange.value.minCol);
484
+ return cell ? isMergedCell(cell) : false;
485
+ });
486
+ const editingChildStyle = computed(() => {
487
+ const child = editingChild.value;
488
+ if (!child) return { display: "none" };
489
+ const obb = getObb(child);
490
+ const textBox = child.text?.base?.boundingBox;
491
+ if (textBox) {
492
+ obb.left += textBox.left;
493
+ obb.top += textBox.top;
494
+ }
495
+ return obb.toCssStyle();
496
+ });
497
+ function flush() {
498
+ const el = editingEl.value;
499
+ const m = model.value;
500
+ if (!el || !m) return;
501
+ el.style.width = gridWidth(m);
502
+ el.style.height = gridHeight(m);
503
+ el.table.setProperties(modelToTable(m));
504
+ }
505
+ function enter(el) {
506
+ editingEl.value = el;
507
+ model.value = cloneTableModel(el.table);
508
+ anchor.value = {
509
+ row: 0,
510
+ col: 0
511
+ };
512
+ focus.value = {
513
+ row: 0,
514
+ col: 0
515
+ };
516
+ editingPos.value = null;
517
+ menu.value = null;
518
+ }
519
+ function exit() {
520
+ commitEdit();
521
+ flush();
522
+ editingEl.value = void 0;
523
+ model.value = void 0;
524
+ editingPos.value = null;
525
+ selecting.value = false;
526
+ menu.value = null;
527
+ }
528
+ watch(() => state.value, (now, prev) => {
529
+ if (now === "tableEditing" && prev !== "tableEditing") {
530
+ const el = elementSelection.value[0];
531
+ if (el?.table.isValid()) enter(el);
532
+ else state.value = void 0;
533
+ } else if (prev === "tableEditing" && now !== "tableEditing") exit();
534
+ });
535
+ function selectCell(pos, extend = false) {
536
+ closeMenu();
537
+ if (!extend) anchor.value = { ...pos };
538
+ focus.value = { ...pos };
539
+ }
540
+ function onCellPointerdown(e, cell) {
541
+ if (e.button === 2) return;
542
+ if (editingPos.value && editingPos.value.row === cell.row && editingPos.value.col === cell.col) return;
543
+ e.stopPropagation();
544
+ commitEdit();
545
+ const pos = {
546
+ row: cell.row,
547
+ col: cell.col
548
+ };
549
+ if (e.shiftKey) selectCell(pos, true);
550
+ else {
551
+ selectCell(pos);
552
+ selecting.value = true;
553
+ }
554
+ }
555
+ function onCellPointerenter(cell) {
556
+ if (selecting.value) focus.value = {
557
+ row: cell.row,
558
+ col: cell.col
559
+ };
560
+ }
561
+ function onCellDblclick(e, cell) {
562
+ e.stopPropagation();
563
+ startEdit({
564
+ row: cell.row,
565
+ col: cell.col
566
+ });
567
+ }
568
+ function onWindowPointerup() {
569
+ selecting.value = false;
570
+ }
571
+ function selectColumn(col) {
572
+ closeMenu();
573
+ commitEdit();
574
+ const m = model.value;
575
+ anchor.value = {
576
+ row: 0,
577
+ col
578
+ };
579
+ focus.value = {
580
+ row: m.rows.length - 1,
581
+ col
582
+ };
583
+ }
584
+ function selectRow(row) {
585
+ closeMenu();
586
+ commitEdit();
587
+ const m = model.value;
588
+ anchor.value = {
589
+ row,
590
+ col: 0
591
+ };
592
+ focus.value = {
593
+ row,
594
+ col: m.columns.length - 1
595
+ };
596
+ }
597
+ function selectAll() {
598
+ closeMenu();
599
+ commitEdit();
600
+ const m = model.value;
601
+ anchor.value = {
602
+ row: 0,
603
+ col: 0
604
+ };
605
+ focus.value = {
606
+ row: m.rows.length - 1,
607
+ col: m.columns.length - 1
608
+ };
609
+ }
610
+ function startEdit(pos) {
611
+ const m = model.value;
612
+ const el = editingEl.value;
613
+ const cell = getCellAt(m, pos.row, pos.col);
614
+ if (!cell || !el) return;
615
+ anchor.value = {
616
+ row: cell.row,
617
+ col: cell.col
618
+ };
619
+ focus.value = {
620
+ row: cell.row,
621
+ col: cell.col
622
+ };
623
+ const child = (el.table._cellNodes?.get(`${cell.row}:${cell.col}`))?.children?.[0];
624
+ if (!child) return;
625
+ const isEmpty = !getCellText(cell);
626
+ editingChild.value = child;
627
+ editingPos.value = {
628
+ row: cell.row,
629
+ col: cell.col
630
+ };
631
+ nextTick(() => {
632
+ const editor = textEditorEl;
633
+ if (!editor || editingChild.value !== child) return;
634
+ if (isEmpty) child.text.setContent(" ");
635
+ child.text.update();
636
+ editor.set(child.text.base);
637
+ editor.selectAll();
638
+ requestAnimationFrame(() => {
639
+ if (editingChild.value === child) editor.selectAll();
640
+ });
641
+ });
642
+ }
643
+ function commitEdit() {
644
+ const pos = editingPos.value;
645
+ const child = editingChild.value;
646
+ const m = model.value;
647
+ editingPos.value = null;
648
+ editingChild.value = void 0;
649
+ if (!pos || !child || !m) return;
650
+ const cell = getCellAt(m, pos.row, pos.col);
651
+ if (cell) {
652
+ const content = (child.text?.base)?.content;
653
+ if ((Array.isArray(content) ? content.flatMap((p) => (p.fragments ?? []).map((f) => f.content)).join("") : "").trim() === "") setCellText(cell, "");
654
+ else if (content) {
655
+ if (!cell.children?.length) cell.children = [{
656
+ text: {},
657
+ meta: { inCanvasIs: "Element2D" }
658
+ }];
659
+ if (!cell.children[0].text) cell.children[0].text = {};
660
+ cell.children[0].text.content = JSON.parse(JSON.stringify(content));
661
+ }
662
+ flush();
663
+ }
664
+ }
665
+ function cancelEdit() {
666
+ const child = editingChild.value;
667
+ const pos = editingPos.value;
668
+ const m = model.value;
669
+ editingPos.value = null;
670
+ editingChild.value = void 0;
671
+ if (child && pos && m) {
672
+ const content = getCellAt(m, pos.row, pos.col)?.children?.[0]?.text?.content;
673
+ child.text.base.content = content ? JSON.parse(JSON.stringify(content)) : [];
674
+ child.text.update();
675
+ editingEl.value?.requestRender?.();
676
+ }
677
+ }
678
+ function onTextUpdate() {
679
+ editingEl.value?.requestRender?.();
680
+ }
681
+ function withMutation(fn) {
682
+ commitEdit();
683
+ closeMenu();
684
+ fn();
685
+ flush();
686
+ }
687
+ function doInsertRow(offset) {
688
+ withMutation(() => insertRow(model.value, selectionRange.value.minRow + offset));
689
+ }
690
+ function doInsertColumn(offset) {
691
+ withMutation(() => insertColumn(model.value, selectionRange.value.minCol + offset));
692
+ }
693
+ function doRemoveRows() {
694
+ withMutation(() => {
695
+ const r = selectionRange.value;
696
+ for (let row = r.maxRow; row >= r.minRow; row--) removeRow(model.value, row);
697
+ clampSelection();
698
+ });
699
+ }
700
+ function doRemoveColumns() {
701
+ withMutation(() => {
702
+ const r = selectionRange.value;
703
+ for (let col = r.maxCol; col >= r.minCol; col--) removeColumn(model.value, col);
704
+ clampSelection();
705
+ });
706
+ }
707
+ function doMerge() {
708
+ withMutation(() => {
709
+ const cell = mergeRange(model.value, selectionRange.value);
710
+ if (cell) {
711
+ anchor.value = {
712
+ row: cell.row,
713
+ col: cell.col
714
+ };
715
+ focus.value = {
716
+ row: cell.row,
717
+ col: cell.col
718
+ };
719
+ }
720
+ });
721
+ }
722
+ function doSplit() {
723
+ withMutation(() => {
724
+ const cell = getCellAt(model.value, selectionRange.value.minRow, selectionRange.value.minCol);
725
+ if (cell) splitCell(model.value, cell);
726
+ });
727
+ }
728
+ function clampSelection() {
729
+ const m = model.value;
730
+ const maxRow = m.rows.length - 1;
731
+ const maxCol = m.columns.length - 1;
732
+ anchor.value = {
733
+ row: Math.min(anchor.value.row, maxRow),
734
+ col: Math.min(anchor.value.col, maxCol)
735
+ };
736
+ focus.value = {
737
+ row: Math.min(focus.value.row, maxRow),
738
+ col: Math.min(focus.value.col, maxCol)
739
+ };
740
+ }
741
+ function onColResizeDown(e, col) {
742
+ e.stopPropagation();
743
+ e.preventDefault();
744
+ const m = model.value;
745
+ const startX = e.clientX;
746
+ const startWidth = m.columns[col].width;
747
+ const target = e.currentTarget;
748
+ target.setPointerCapture(e.pointerId);
749
+ const move = (ev) => {
750
+ const delta = (ev.clientX - startX) / camera.value.zoom.x;
751
+ m.columns[col].width = Math.max(20, Math.round(startWidth + delta));
752
+ flush();
753
+ };
754
+ const up = (ev) => {
755
+ target.releasePointerCapture?.(ev.pointerId);
756
+ target.removeEventListener("pointermove", move);
757
+ target.removeEventListener("pointerup", up);
758
+ };
759
+ target.addEventListener("pointermove", move);
760
+ target.addEventListener("pointerup", up);
761
+ }
762
+ function onRowResizeDown(e, row) {
763
+ e.stopPropagation();
764
+ e.preventDefault();
765
+ const m = model.value;
766
+ const startY = e.clientY;
767
+ const startHeight = m.rows[row].height;
768
+ const target = e.currentTarget;
769
+ target.setPointerCapture(e.pointerId);
770
+ const move = (ev) => {
771
+ const delta = (ev.clientY - startY) / camera.value.zoom.y;
772
+ m.rows[row].height = Math.max(20, Math.round(startHeight + delta));
773
+ flush();
774
+ };
775
+ const up = (ev) => {
776
+ target.releasePointerCapture?.(ev.pointerId);
777
+ target.removeEventListener("pointermove", move);
778
+ target.removeEventListener("pointerup", up);
779
+ };
780
+ target.addEventListener("pointermove", move);
781
+ target.addEventListener("pointerup", up);
782
+ }
783
+ function onCellContextmenu(e, cell) {
784
+ e.preventDefault();
785
+ e.stopPropagation();
786
+ const r = selectionRange.value;
787
+ if (!(cell.row >= r.minRow && cell.row <= r.maxRow && cell.col >= r.minCol && cell.col <= r.maxCol)) selectCell({
788
+ row: cell.row,
789
+ col: cell.col
790
+ });
791
+ menu.value = {
792
+ x: e.clientX,
793
+ y: e.clientY
794
+ };
795
+ }
796
+ function closeMenu() {
797
+ menu.value = null;
798
+ }
799
+ function copySelection(cut = false) {
800
+ const m = model.value;
801
+ const r = selectionRange.value;
802
+ const texts = [];
803
+ for (let row = r.minRow; row <= r.maxRow; row++) {
804
+ const line = [];
805
+ for (let col = r.minCol; col <= r.maxCol; col++) {
806
+ const cell = getCellAt(m, row, col);
807
+ line.push(cell && cell.row === row && cell.col === col ? getCellText(cell) : "");
808
+ }
809
+ texts.push(line);
810
+ }
811
+ clipboard = {
812
+ rows: r.maxRow - r.minRow + 1,
813
+ cols: r.maxCol - r.minCol + 1,
814
+ texts
815
+ };
816
+ try {
817
+ navigator.clipboard?.writeText(texts.map((line) => line.join(" ")).join("\n"));
818
+ } catch {}
819
+ if (cut) clearSelection();
820
+ }
821
+ function pasteClipboard() {
822
+ if (!clipboard) return;
823
+ const m = model.value;
824
+ const r = selectionRange.value;
825
+ for (let i = 0; i < clipboard.rows; i++) for (let j = 0; j < clipboard.cols; j++) {
826
+ const cell = getCellAt(m, r.minRow + i, r.minCol + j);
827
+ if (cell && cell.row === r.minRow + i && cell.col === r.minCol + j) setCellText(cell, clipboard.texts[i][j]);
828
+ }
829
+ flush();
830
+ }
831
+ function clearSelection() {
832
+ const m = model.value;
833
+ const r = selectionRange.value;
834
+ for (const cell of m.cells) {
835
+ const cr = cellRange(cell);
836
+ if (cr.minRow >= r.minRow && cr.maxRow <= r.maxRow && cr.minCol >= r.minCol && cr.maxCol <= r.maxCol) setCellText(cell, "");
837
+ }
838
+ flush();
839
+ }
840
+ function moveFocus(dRow, dCol, extend = false) {
841
+ const m = model.value;
842
+ selectCell({
843
+ row: Math.max(0, Math.min(m.rows.length - 1, focus.value.row + dRow)),
844
+ col: Math.max(0, Math.min(m.columns.length - 1, focus.value.col + dCol))
845
+ }, extend);
846
+ }
847
+ function onKeydown(e) {
848
+ if (!active.value) return;
849
+ if (editingPos.value) {
850
+ if (e.key === "Enter" && !e.shiftKey) {
851
+ e.preventDefault();
852
+ e.stopPropagation();
853
+ commitEdit();
854
+ moveFocus(1, 0);
855
+ } else if (e.key === "Escape") {
856
+ e.preventDefault();
857
+ e.stopPropagation();
858
+ cancelEdit();
859
+ } else if (e.key === "Tab") {
860
+ e.preventDefault();
861
+ e.stopPropagation();
862
+ commitEdit();
863
+ moveFocus(0, e.shiftKey ? -1 : 1);
864
+ }
865
+ return;
866
+ }
867
+ if (e.ctrlKey || e.metaKey) {
868
+ switch (e.key.toLowerCase()) {
869
+ case "a":
870
+ e.preventDefault();
871
+ selectAll();
872
+ return;
873
+ case "c":
874
+ e.preventDefault();
875
+ copySelection();
876
+ return;
877
+ case "x":
878
+ e.preventDefault();
879
+ copySelection(true);
880
+ return;
881
+ case "v":
882
+ e.preventDefault();
883
+ pasteClipboard();
884
+ return;
885
+ }
886
+ return;
887
+ }
888
+ switch (e.key) {
889
+ case "Escape":
890
+ e.preventDefault();
891
+ state.value = void 0;
892
+ break;
893
+ case "Enter":
894
+ case "F2":
895
+ e.preventDefault();
896
+ startEdit(focus.value);
897
+ break;
898
+ case "Delete":
899
+ case "Backspace":
900
+ e.preventDefault();
901
+ clearSelection();
902
+ break;
903
+ case "Tab":
904
+ e.preventDefault();
905
+ moveFocus(0, e.shiftKey ? -1 : 1);
906
+ break;
907
+ case "ArrowUp":
908
+ e.preventDefault();
909
+ moveFocus(-1, 0, e.shiftKey);
910
+ break;
911
+ case "ArrowDown":
912
+ e.preventDefault();
913
+ moveFocus(1, 0, e.shiftKey);
914
+ break;
915
+ case "ArrowLeft":
916
+ e.preventDefault();
917
+ moveFocus(0, -1, e.shiftKey);
918
+ break;
919
+ case "ArrowRight":
920
+ e.preventDefault();
921
+ moveFocus(0, 1, e.shiftKey);
922
+ break;
923
+ }
924
+ }
925
+ onBeforeMount(() => {
926
+ TextEditor.register();
927
+ });
928
+ onMounted(() => {
929
+ const el = document.createElement("text-editor");
930
+ el.className = "m-table-editor__text-editor";
931
+ el.style.setProperty("--color", "var(--m-theme-primary)");
932
+ el.addEventListener("update", onTextUpdate);
933
+ el.addEventListener("submit", () => commitEdit());
934
+ textHost.value?.appendChild(el);
935
+ textEditorEl = el;
936
+ window.addEventListener("keydown", onKeydown, true);
937
+ window.addEventListener("pointerup", onWindowPointerup);
938
+ });
939
+ onBeforeUnmount(() => {
940
+ window.removeEventListener("keydown", onKeydown, true);
941
+ window.removeEventListener("pointerup", onWindowPointerup);
942
+ });
943
+ return (_ctx, _cache) => {
944
+ return withDirectives((openBlock(), createElementBlock("div", {
945
+ class: "m-table-editor",
946
+ style: normalizeStyle(mainStyleWithScale.value),
947
+ onContextmenu: _cache[13] || (_cache[13] = withModifiers(() => {}, ["prevent"]))
948
+ }, [
949
+ model.value ? (openBlock(), createElementBlock("div", {
950
+ key: 0,
951
+ class: "m-table-editor__box",
952
+ style: normalizeStyle(tableBoxStyle.value),
953
+ onPointerdown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
954
+ }, [
955
+ createElementVNode("div", {
956
+ class: "m-table-editor__bar",
957
+ style: normalizeStyle({
958
+ left: `-28px`,
959
+ top: `-48px`,
960
+ width: `${ROW_W + unref(gridWidth)(model.value)}px`,
961
+ height: `${BAR_H}px`
962
+ })
963
+ }, [createElementVNode("div", _hoisted_1, toDisplayString(activeCellRef.value), 1), createElementVNode("div", _hoisted_2, toDisplayString(activeCellText.value), 1)], 4),
964
+ createElementVNode("div", {
965
+ class: "m-table-editor__corner",
966
+ style: normalizeStyle({
967
+ left: `-28px`,
968
+ top: `-22px`,
969
+ width: `${ROW_W}px`,
970
+ height: `${COL_H}px`
971
+ }),
972
+ onPointerdown: withModifiers(selectAll, ["stop"])
973
+ }, null, 36),
974
+ (openBlock(true), createElementBlock(Fragment, null, renderList(model.value.columns, (col, ci) => {
975
+ return openBlock(), createElementBlock("div", {
976
+ key: `ch-${ci}`,
977
+ class: normalizeClass(["m-table-editor__col-header", { "m-table-editor__col-header--active": isColActive(ci) }]),
978
+ style: normalizeStyle({
979
+ left: `${unref(colLeft)(model.value, ci)}px`,
980
+ top: `-22px`,
981
+ width: `${col.width}px`,
982
+ height: `${COL_H}px`
983
+ }),
984
+ onPointerdown: withModifiers(($event) => selectColumn(ci), ["stop"])
985
+ }, toDisplayString(colLabel(ci)), 47, _hoisted_3);
986
+ }), 128)),
987
+ (openBlock(true), createElementBlock(Fragment, null, renderList(colBoundaries.value, (x, ci) => {
988
+ return openBlock(), createElementBlock("div", {
989
+ key: `cr-${ci}`,
990
+ class: "m-table-editor__col-resizer",
991
+ style: normalizeStyle({
992
+ left: `${x}px`,
993
+ top: `-22px`,
994
+ height: `${COL_H}px`
995
+ }),
996
+ onPointerdown: ($event) => onColResizeDown($event, ci)
997
+ }, null, 44, _hoisted_4);
998
+ }), 128)),
999
+ (openBlock(true), createElementBlock(Fragment, null, renderList(model.value.rows, (row, ri) => {
1000
+ return openBlock(), createElementBlock("div", {
1001
+ key: `rh-${ri}`,
1002
+ class: normalizeClass(["m-table-editor__row-header", { "m-table-editor__row-header--active": isRowActive(ri) }]),
1003
+ style: normalizeStyle({
1004
+ left: `-28px`,
1005
+ top: `${unref(rowTop)(model.value, ri)}px`,
1006
+ width: `${ROW_W}px`,
1007
+ height: `${row.height}px`
1008
+ }),
1009
+ onPointerdown: withModifiers(($event) => selectRow(ri), ["stop"])
1010
+ }, toDisplayString(ri + 1), 47, _hoisted_5);
1011
+ }), 128)),
1012
+ (openBlock(true), createElementBlock(Fragment, null, renderList(rowBoundaries.value, (y, ri) => {
1013
+ return openBlock(), createElementBlock("div", {
1014
+ key: `rr-${ri}`,
1015
+ class: "m-table-editor__row-resizer",
1016
+ style: normalizeStyle({
1017
+ left: `-28px`,
1018
+ top: `${y}px`,
1019
+ width: `${ROW_W}px`
1020
+ }),
1021
+ onPointerdown: ($event) => onRowResizeDown($event, ri)
1022
+ }, null, 44, _hoisted_6);
1023
+ }), 128)),
1024
+ (openBlock(true), createElementBlock(Fragment, null, renderList(renderCells.value, ({ cell, rect }) => {
1025
+ return openBlock(), createElementBlock("div", {
1026
+ key: `${cell.row}:${cell.col}`,
1027
+ class: "m-table-editor__cell",
1028
+ style: normalizeStyle({
1029
+ left: `${rect.left}px`,
1030
+ top: `${rect.top}px`,
1031
+ width: `${rect.width}px`,
1032
+ height: `${rect.height}px`
1033
+ }),
1034
+ onPointerdown: ($event) => onCellPointerdown($event, cell),
1035
+ onPointerenter: ($event) => onCellPointerenter(cell),
1036
+ onDblclick: ($event) => onCellDblclick($event, cell),
1037
+ onContextmenu: ($event) => onCellContextmenu($event, cell)
1038
+ }, null, 44, _hoisted_7);
1039
+ }), 128)),
1040
+ createElementVNode("div", {
1041
+ class: normalizeClass(["m-table-editor__selection", { "m-table-editor__selection--range": !isSingleSelection.value }]),
1042
+ style: normalizeStyle(selectionRectStyle.value)
1043
+ }, null, 6)
1044
+ ], 36)) : createCommentVNode("", true),
1045
+ withDirectives(createElementVNode("div", {
1046
+ ref_key: "textHost",
1047
+ ref: textHost,
1048
+ class: "m-table-editor__text",
1049
+ style: normalizeStyle(editingChildStyle.value),
1050
+ onPointerdown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"])),
1051
+ onDblclick: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"]))
1052
+ }, null, 36), [[vShow, editingChild.value]]),
1053
+ (openBlock(), createBlock(Teleport, { to: "body" }, [menu.value ? (openBlock(), createElementBlock("div", {
1054
+ key: 0,
1055
+ class: "m-table-editor__menu",
1056
+ style: normalizeStyle({
1057
+ left: `${menu.value.x}px`,
1058
+ top: `${menu.value.y}px`
1059
+ }),
1060
+ onPointerdown: _cache[11] || (_cache[11] = withModifiers(() => {}, ["stop"])),
1061
+ onContextmenu: _cache[12] || (_cache[12] = withModifiers(() => {}, ["prevent"]))
1062
+ }, [
1063
+ createElementVNode("button", {
1064
+ class: "m-table-editor__menu-item",
1065
+ onClick: _cache[3] || (_cache[3] = ($event) => doInsertRow(0))
1066
+ }, toDisplayString(unref(t)("table:insertRowAbove")), 1),
1067
+ createElementVNode("button", {
1068
+ class: "m-table-editor__menu-item",
1069
+ onClick: _cache[4] || (_cache[4] = ($event) => doInsertRow(1))
1070
+ }, toDisplayString(unref(t)("table:insertRowBelow")), 1),
1071
+ createElementVNode("button", {
1072
+ class: "m-table-editor__menu-item",
1073
+ onClick: _cache[5] || (_cache[5] = ($event) => doInsertColumn(0))
1074
+ }, toDisplayString(unref(t)("table:insertColLeft")), 1),
1075
+ createElementVNode("button", {
1076
+ class: "m-table-editor__menu-item",
1077
+ onClick: _cache[6] || (_cache[6] = ($event) => doInsertColumn(1))
1078
+ }, toDisplayString(unref(t)("table:insertColRight")), 1),
1079
+ _cache[14] || (_cache[14] = createElementVNode("div", { class: "m-table-editor__menu-sep" }, null, -1)),
1080
+ createElementVNode("button", {
1081
+ class: "m-table-editor__menu-item",
1082
+ onClick: _cache[7] || (_cache[7] = ($event) => doRemoveRows())
1083
+ }, toDisplayString(unref(t)("table:deleteRow")), 1),
1084
+ createElementVNode("button", {
1085
+ class: "m-table-editor__menu-item",
1086
+ onClick: _cache[8] || (_cache[8] = ($event) => doRemoveColumns())
1087
+ }, toDisplayString(unref(t)("table:deleteCol")), 1),
1088
+ _cache[15] || (_cache[15] = createElementVNode("div", { class: "m-table-editor__menu-sep" }, null, -1)),
1089
+ createElementVNode("button", {
1090
+ class: "m-table-editor__menu-item",
1091
+ disabled: isSingleSelection.value,
1092
+ onClick: _cache[9] || (_cache[9] = ($event) => doMerge())
1093
+ }, toDisplayString(unref(t)("table:mergeCells")), 9, _hoisted_8),
1094
+ createElementVNode("button", {
1095
+ class: "m-table-editor__menu-item",
1096
+ disabled: !selectedAnchorMerged.value,
1097
+ onClick: _cache[10] || (_cache[10] = ($event) => doSplit())
1098
+ }, toDisplayString(unref(t)("table:splitCell")), 9, _hoisted_9)
1099
+ ], 36)) : createCommentVNode("", true)]))
1100
+ ], 36)), [[vShow, active.value]]);
1101
+ };
1102
+ }
1103
+ });
1104
+ //#endregion
1105
+ //#region src/plugin.ts
1106
+ function plugin() {
1107
+ return definePlugin((editor) => {
1108
+ const { elementSelection, isElement, addElement, activateTool, registerSelectionRedirect, registerResizeOverride, registerEnterHandler, registerEditingState, registerToolbeltShapeItem, registerIcon } = editor;
1109
+ registerIcon("table", "M4,3H20A2,2 0 0,1 22,5V19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19V5A2,2 0 0,1 4,3M4,7V11H8V7H4M10,7V11H14V7H10M16,7V11H20V7H16M4,13V17H8V13H4M10,13V17H14V13H10M16,13V17H20V13H16Z");
1110
+ registerSelectionRedirect((node) => {
1111
+ return node.findAncestor((n) => isElement(n) && Boolean(n.table?.isValid?.())) ?? node;
1112
+ });
1113
+ registerResizeOverride((el, { scaleX, scaleY, newWidth, newHeight, options }) => {
1114
+ if (!el.table.isValid()) return false;
1115
+ const scaleFont = options.textFontSizeToFit ? scaleX : 1;
1116
+ const columns = el.table.columns.map((c) => ({
1117
+ ...c,
1118
+ width: (c.width || 0) * scaleX
1119
+ }));
1120
+ const rows = el.table.rows.map((r) => ({
1121
+ ...r,
1122
+ height: (r.height || 0) * scaleY
1123
+ }));
1124
+ const cells = JSON.parse(JSON.stringify(el.table.cells)).map((cell) => {
1125
+ const cs = cell.children?.[0]?.style;
1126
+ if (cs) {
1127
+ if (cs.width) cs.width *= scaleX;
1128
+ if (cs.height) cs.height *= scaleY;
1129
+ if (scaleFont !== 1 && cs.fontSize) cs.fontSize *= scaleFont;
1130
+ }
1131
+ return cell;
1132
+ });
1133
+ el.style.width = newWidth;
1134
+ el.style.height = newHeight;
1135
+ el.table.setProperties({
1136
+ columns,
1137
+ rows,
1138
+ cells
1139
+ });
1140
+ return true;
1141
+ });
1142
+ registerEnterHandler((el, ed) => {
1143
+ if (el.table.isValid()) {
1144
+ ed.state.value = "tableEditing";
1145
+ return true;
1146
+ }
1147
+ return false;
1148
+ });
1149
+ registerEditingState("tableEditing");
1150
+ registerToolbeltShapeItem("table");
1151
+ /** 改写某单元格并整体回写 table 以触发重渲。 */
1152
+ function patchCell(row, col, mutate, node) {
1153
+ const el = node ?? elementSelection.value[0];
1154
+ if (!el?.table) return;
1155
+ const data = el.table.toJSON?.() ?? el.table;
1156
+ const cell = data.cells?.find((c) => c.row === row && c.col === col);
1157
+ if (!cell) return;
1158
+ mutate(cell);
1159
+ el.table = data;
1160
+ el.requestDraw?.();
1161
+ }
1162
+ function setTableCellStyle(row, col, style, node) {
1163
+ patchCell(row, col, (cell) => cell.style = {
1164
+ ...cell.style ?? {},
1165
+ ...style
1166
+ }, node);
1167
+ }
1168
+ function setTableCellBackground(row, col, background, node) {
1169
+ patchCell(row, col, (cell) => cell.background = background, node);
1170
+ }
1171
+ return {
1172
+ name: "mce:table",
1173
+ commands: [{
1174
+ command: "setTableCellStyle",
1175
+ handle: setTableCellStyle
1176
+ }, {
1177
+ command: "setTableCellBackground",
1178
+ handle: setTableCellBackground
1179
+ }],
1180
+ tools: [{
1181
+ name: "table",
1182
+ handle: (start) => {
1183
+ addElement(createTableElement(), {
1184
+ position: start,
1185
+ active: true
1186
+ });
1187
+ return { end: () => activateTool(void 0) };
1188
+ }
1189
+ }],
1190
+ messages: {
1191
+ en: {
1192
+ "table": "Table",
1193
+ "tableEditing": "Editing table",
1194
+ "table:insertRowAbove": "Insert row above",
1195
+ "table:insertRowBelow": "Insert row below",
1196
+ "table:insertColLeft": "Insert column left",
1197
+ "table:insertColRight": "Insert column right",
1198
+ "table:deleteRow": "Delete rows",
1199
+ "table:deleteCol": "Delete columns",
1200
+ "table:mergeCells": "Merge cells",
1201
+ "table:splitCell": "Split cell"
1202
+ },
1203
+ zhHans: {
1204
+ "table": "表格",
1205
+ "tableEditing": "编辑表格...",
1206
+ "table:insertRowAbove": "上方插入行",
1207
+ "table:insertRowBelow": "下方插入行",
1208
+ "table:insertColLeft": "左侧插入列",
1209
+ "table:insertColRight": "右侧插入列",
1210
+ "table:deleteRow": "删除行",
1211
+ "table:deleteCol": "删除列",
1212
+ "table:mergeCells": "合并单元格",
1213
+ "table:splitCell": "拆分单元格"
1214
+ }
1215
+ },
1216
+ components: [{
1217
+ type: "overlay",
1218
+ component: TableEditor_default
1219
+ }]
1220
+ };
1221
+ });
1222
+ }
1223
+ //#endregion
1224
+ //#region src/index.ts
1225
+ var src_default = plugin;
1226
+ //#endregion
1227
+ export { CELL_BACKGROUND, CELL_BORDER_STYLE, buildCoverage, cellColSpan, cellRange, cellRowSpan, cloneTableModel, colLeft, createCellChild, createTableElement, src_default as default, defaultTextStyle, getCellAt, getCellRect, getCellText, gridHeight, gridWidth, insertColumn, insertRow, isMergedCell, mergeRange, modelToTable, normalizeRange, plugin, removeColumn, removeRow, rowTop, setCellText, splitCell };