autumnnote 1.0.7 → 1.0.9
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/README.md +332 -638
- package/dist/autumnnote.css +40 -4
- package/dist/autumnnote.es.js +3375 -262
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +3374 -261
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +4 -0
- package/src/js/editing/Style.js +195 -44
- package/src/js/editing/Typing.js +2 -2
- package/src/js/i18n/de.js +320 -0
- package/src/js/i18n/en.js +328 -0
- package/src/js/i18n/es.js +320 -0
- package/src/js/i18n/fr.js +321 -0
- package/src/js/i18n/index.js +59 -0
- package/src/js/i18n/ja.js +321 -0
- package/src/js/i18n/ko.js +320 -0
- package/src/js/i18n/vi.js +321 -0
- package/src/js/i18n/zh.js +321 -0
- package/src/js/index.js +2 -1
- package/src/js/module/CodeTooltip.js +12 -9
- package/src/js/module/ContextMenu.js +13 -11
- package/src/js/module/Editor.js +14 -3
- package/src/js/module/EmojiDialog.js +19 -7
- package/src/js/module/FindReplace.js +14 -13
- package/src/js/module/IconDialog.js +25 -13
- package/src/js/module/ImageDialog.js +25 -20
- package/src/js/module/ImageTooltip.js +13 -12
- package/src/js/module/LinkDialog.js +10 -9
- package/src/js/module/LinkTooltip.js +6 -5
- package/src/js/module/Placeholder.js +6 -1
- package/src/js/module/ShortcutsDialog.js +5 -4
- package/src/js/module/Statusbar.js +6 -5
- package/src/js/module/TableTooltip.js +412 -102
- package/src/js/module/Toolbar.js +21 -15
- package/src/js/module/VideoDialog.js +10 -9
- package/src/js/module/VideoTooltip.js +12 -11
- package/src/js/settings.js +4 -0
- package/src/styles/autumnnote.scss +50 -5
- package/types/index.d.ts +58 -1
|
@@ -64,6 +64,43 @@ function getCellAfterVisualCol(row, visualIdx) {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Build a 2D grid map of the table, accounting for both rowspan and colspan.
|
|
69
|
+
*
|
|
70
|
+
* gridMap[r][c] = the DOM cell occupying visual grid position (r, c).
|
|
71
|
+
* cellPos = WeakMap: cell → { r, c, rs, cs } (top-left grid origin + span).
|
|
72
|
+
*
|
|
73
|
+
* Uses HTMLTableElement.rows which is scoped to the table itself and never
|
|
74
|
+
* includes rows from nested tables.
|
|
75
|
+
*
|
|
76
|
+
* @param {HTMLTableElement} table
|
|
77
|
+
* @returns {{ gridMap: Object, cellPos: WeakMap }}
|
|
78
|
+
*/
|
|
79
|
+
function buildGridMap(table) {
|
|
80
|
+
const rows = Array.from(table.rows);
|
|
81
|
+
const gridMap = {};
|
|
82
|
+
const cellPos = new WeakMap();
|
|
83
|
+
rows.forEach((row, r) => {
|
|
84
|
+
if (!gridMap[r]) gridMap[r] = {};
|
|
85
|
+
let c = 0;
|
|
86
|
+
for (const cell of row.cells) {
|
|
87
|
+
// Skip positions already occupied by a rowspan from a previous row
|
|
88
|
+
while (gridMap[r][c]) c++;
|
|
89
|
+
const rs = cell.rowSpan || 1;
|
|
90
|
+
const cs = cell.colSpan || 1;
|
|
91
|
+
cellPos.set(cell, { r, c, rs, cs });
|
|
92
|
+
for (let dr = 0; dr < rs; dr++) {
|
|
93
|
+
if (!gridMap[r + dr]) gridMap[r + dr] = {};
|
|
94
|
+
for (let dc = 0; dc < cs; dc++) {
|
|
95
|
+
gridMap[r + dr][c + dc] = cell;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
c += cs;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return { gridMap, cellPos };
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
const ICONS = {
|
|
68
105
|
rowAbove: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 3v7"/><path d="M9 7l3-4 3 4"/></svg>`,
|
|
69
106
|
rowBelow: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 12v7"/><path d="M9 17l3 4 3-4"/></svg>`,
|
|
@@ -71,11 +108,13 @@ const ICONS = {
|
|
|
71
108
|
colLeft: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><path d="M3 12h7"/><path d="M7 8l-4 4 4 4"/></svg>`,
|
|
72
109
|
colRight: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><path d="M12 12h9"/><path d="M17 8l4 4-4 4"/></svg>`,
|
|
73
110
|
deleteCol: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><line x1="15" y1="6" x2="21" y2="12"/><line x1="21" y1="6" x2="15" y2="12"/></svg>`,
|
|
74
|
-
mergeCells:
|
|
111
|
+
mergeCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="8" height="10" rx="1"/><rect x="14" y="7" width="8" height="10" rx="1"/><path d="M10 12h4"/><path d="M12 10l2 2-2 2"/></svg>`,
|
|
112
|
+
unmergeCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="1"/><line x1="12" y1="5" x2="12" y2="19" stroke-dasharray="2.5 2"/><line x1="2" y1="12" x2="22" y2="12" stroke-dasharray="2.5 2"/><path d="M9 9 L6 12 L9 15"/><path d="M15 9 L18 12 L15 15"/></svg>`,
|
|
75
113
|
colWidth: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="7" y1="4" x2="7" y2="20"/><line x1="17" y1="4" x2="17" y2="20"/><line x1="7" y1="12" x2="17" y2="12"/><path d="M10 9l-3 3 3 3"/><path d="M14 9l3 3-3 3"/></svg>`,
|
|
76
114
|
rowHeight: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="17" x2="20" y2="17"/><line x1="12" y1="7" x2="12" y2="17"/><path d="M9 10l3-3 3 3"/><path d="M9 14l3 3 3-3"/></svg>`,
|
|
77
115
|
tableBorder: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6" stroke-width="1"/><line x1="3" y1="13" x2="21" y2="13" stroke-width="2"/><line x1="3" y1="20" x2="21" y2="20" stroke-width="3"/></svg>`,
|
|
78
116
|
deleteTable: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg>`,
|
|
117
|
+
selectCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z" fill="currentColor" opacity="0.15"/><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z"/></svg>`,
|
|
79
118
|
};
|
|
80
119
|
|
|
81
120
|
export class TableTooltip {
|
|
@@ -92,6 +131,13 @@ export class TableTooltip {
|
|
|
92
131
|
this._sizeApply = null;
|
|
93
132
|
this._sizeTitleEl = null;
|
|
94
133
|
this._sizeInputEl = null;
|
|
134
|
+
// Cell selection
|
|
135
|
+
this._selectMode = false;
|
|
136
|
+
this._selectedCells = [];
|
|
137
|
+
this._selectStart = null;
|
|
138
|
+
this._selectDragging = false;
|
|
139
|
+
this._selectBtn = null;
|
|
140
|
+
this._editable = null;
|
|
95
141
|
}
|
|
96
142
|
|
|
97
143
|
initialize() {
|
|
@@ -102,6 +148,36 @@ export class TableTooltip {
|
|
|
102
148
|
document.body.appendChild(this._sizePopover);
|
|
103
149
|
|
|
104
150
|
const editable = this.context.layoutInfo.editable;
|
|
151
|
+
this._editable = editable;
|
|
152
|
+
|
|
153
|
+
// ── Cell selection drag ──────────────────────────────────────────────────
|
|
154
|
+
const onSelMousedown = (e) => {
|
|
155
|
+
if (!this._selectMode) return;
|
|
156
|
+
const cell = e.target.closest('td, th');
|
|
157
|
+
if (!cell || !editable.contains(cell)) return;
|
|
158
|
+
// Don't interfere with col/row resize (inline cursor is set by resize logic)
|
|
159
|
+
if (cell.style.cursor === 'col-resize' || cell.style.cursor === 'row-resize') return;
|
|
160
|
+
e.preventDefault(); // suppress text-cursor placement while selecting cells
|
|
161
|
+
this._activeTable = cell.closest('table');
|
|
162
|
+
this._selectStart = cell;
|
|
163
|
+
this._selectDragging = true;
|
|
164
|
+
this._setSelection([cell]);
|
|
165
|
+
};
|
|
166
|
+
const onSelMousemove = (e) => {
|
|
167
|
+
if (!this._selectMode || !this._selectDragging || !this._selectStart) return;
|
|
168
|
+
const cell = e.target.closest('td, th');
|
|
169
|
+
if (!cell || !editable.contains(cell)) return;
|
|
170
|
+
if (cell.closest('table') !== this._activeTable) return;
|
|
171
|
+
this._setSelection(this._getRectCells(this._selectStart, cell));
|
|
172
|
+
};
|
|
173
|
+
const onSelMouseup = () => { this._selectDragging = false; };
|
|
174
|
+
|
|
175
|
+
this._disposers.push(
|
|
176
|
+
on(editable, 'mousedown', onSelMousedown),
|
|
177
|
+
on(editable, 'mousemove', onSelMousemove),
|
|
178
|
+
on(document, 'mouseup', onSelMouseup),
|
|
179
|
+
);
|
|
180
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
105
181
|
|
|
106
182
|
this._disposers.push(
|
|
107
183
|
on(editable, 'mouseover', (e) => {
|
|
@@ -114,6 +190,7 @@ export class TableTooltip {
|
|
|
114
190
|
}
|
|
115
191
|
}, { passive: true }),
|
|
116
192
|
on(editable, 'mouseout', (e) => {
|
|
193
|
+
if (this._selectMode) return; // keep tooltip alive during cell selection
|
|
117
194
|
const to = e.relatedTarget;
|
|
118
195
|
if (!to || (
|
|
119
196
|
!editable.contains(to) &&
|
|
@@ -124,6 +201,7 @@ export class TableTooltip {
|
|
|
124
201
|
}
|
|
125
202
|
}, { passive: true }),
|
|
126
203
|
on(document, 'click', (e) => {
|
|
204
|
+
if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
|
|
127
205
|
if (this._activeTable &&
|
|
128
206
|
!this._activeTable.contains(e.target) &&
|
|
129
207
|
!this._el.contains(e.target) &&
|
|
@@ -210,11 +288,16 @@ export class TableTooltip {
|
|
|
210
288
|
if (_edge === 'col') {
|
|
211
289
|
_startW = _nearCell.offsetWidth;
|
|
212
290
|
_colIdx = getVisualColIndex(_nearCell);
|
|
213
|
-
// Cache column cells once so onDocMove never runs querySelectorAll per frame
|
|
291
|
+
// Cache column cells once so onDocMove never runs querySelectorAll per frame.
|
|
292
|
+
// F-1: skip merged cells (colSpan > 1) — setting width on a merged cell
|
|
293
|
+
// distributes it equally across all spanned columns instead of resizing
|
|
294
|
+
// only the target column. Rows that have an individual cell at this
|
|
295
|
+
// visual column index are resized independently as expected.
|
|
214
296
|
_colCells = _colIdx >= 0
|
|
215
297
|
? Array.from(_table.querySelectorAll('tr'))
|
|
216
298
|
.map(r => getCellAtVisualCol(r, _colIdx))
|
|
217
299
|
.filter(Boolean)
|
|
300
|
+
.filter(c => (c.colSpan || 1) === 1)
|
|
218
301
|
: [];
|
|
219
302
|
document.body.style.cursor = 'col-resize';
|
|
220
303
|
} else {
|
|
@@ -293,48 +376,56 @@ export class TableTooltip {
|
|
|
293
376
|
// ---------------------------------------------------------------------------
|
|
294
377
|
|
|
295
378
|
_buildTooltip() {
|
|
379
|
+
const L = this.context.locale.tooltips.table;
|
|
296
380
|
const el = createElement('div', {
|
|
297
381
|
class: 'an-link-tooltip an-table-tooltip',
|
|
298
382
|
role: 'toolbar',
|
|
299
|
-
'aria-label':
|
|
383
|
+
'aria-label': L.ariaLabel,
|
|
300
384
|
});
|
|
301
385
|
el.style.display = 'none';
|
|
302
386
|
|
|
303
387
|
// Label
|
|
304
388
|
this._label = createElement('span', { class: 'an-link-tooltip-url' });
|
|
305
|
-
this._label.textContent =
|
|
389
|
+
this._label.textContent = L.label;
|
|
306
390
|
el.appendChild(this._label);
|
|
307
391
|
|
|
308
392
|
el.appendChild(this._sep());
|
|
309
393
|
|
|
394
|
+
// Select-cells toggle
|
|
395
|
+
this._selectBtn = this._makeBtn(ICONS.selectCells, L.selectCells, () => this._toggleSelectMode());
|
|
396
|
+
el.appendChild(this._selectBtn);
|
|
397
|
+
|
|
398
|
+
el.appendChild(this._sep());
|
|
399
|
+
|
|
310
400
|
// Row operations
|
|
311
|
-
el.appendChild(this._makeBtn(ICONS.rowAbove,
|
|
312
|
-
el.appendChild(this._makeBtn(ICONS.rowBelow,
|
|
313
|
-
el.appendChild(this._makeBtn(ICONS.deleteRow,
|
|
401
|
+
el.appendChild(this._makeBtn(ICONS.rowAbove, L.addRowAbove, () => this._addRow('above')));
|
|
402
|
+
el.appendChild(this._makeBtn(ICONS.rowBelow, L.addRowBelow, () => this._addRow('below')));
|
|
403
|
+
el.appendChild(this._makeBtn(ICONS.deleteRow, L.deleteRow, () => this._deleteRow()));
|
|
314
404
|
|
|
315
405
|
el.appendChild(this._sep());
|
|
316
406
|
|
|
317
407
|
// Column operations
|
|
318
|
-
el.appendChild(this._makeBtn(ICONS.colLeft,
|
|
319
|
-
el.appendChild(this._makeBtn(ICONS.colRight,
|
|
320
|
-
el.appendChild(this._makeBtn(ICONS.deleteCol,
|
|
408
|
+
el.appendChild(this._makeBtn(ICONS.colLeft, L.addColumnLeft, () => this._addColumn('left')));
|
|
409
|
+
el.appendChild(this._makeBtn(ICONS.colRight, L.addColumnRight, () => this._addColumn('right')));
|
|
410
|
+
el.appendChild(this._makeBtn(ICONS.deleteCol, L.deleteColumn, () => this._deleteColumn()));
|
|
321
411
|
|
|
322
412
|
el.appendChild(this._sep());
|
|
323
413
|
|
|
324
414
|
// Merge cells
|
|
325
|
-
el.appendChild(this._makeBtn(ICONS.mergeCells,
|
|
415
|
+
el.appendChild(this._makeBtn(ICONS.mergeCells, L.mergeCells, () => this._mergeCells()));
|
|
416
|
+
el.appendChild(this._makeBtn(ICONS.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
|
|
326
417
|
|
|
327
418
|
el.appendChild(this._sep());
|
|
328
419
|
|
|
329
420
|
// Resize
|
|
330
|
-
el.appendChild(this._makeBtn(ICONS.colWidth,
|
|
331
|
-
el.appendChild(this._makeBtn(ICONS.rowHeight,
|
|
332
|
-
el.appendChild(this._makeBtn(ICONS.tableBorder,
|
|
421
|
+
el.appendChild(this._makeBtn(ICONS.colWidth, L.columnWidth, () => this._openSizePopover('col')));
|
|
422
|
+
el.appendChild(this._makeBtn(ICONS.rowHeight, L.rowHeight, () => this._openSizePopover('row')));
|
|
423
|
+
el.appendChild(this._makeBtn(ICONS.tableBorder,L.tableBorderWidth, () => this._openSizePopover('border')));
|
|
333
424
|
|
|
334
425
|
el.appendChild(this._sep());
|
|
335
426
|
|
|
336
427
|
// Delete table (danger)
|
|
337
|
-
el.appendChild(this._makeBtn(ICONS.deleteTable,
|
|
428
|
+
el.appendChild(this._makeBtn(ICONS.deleteTable, L.deleteTable, () => this._deleteTable(), true));
|
|
338
429
|
|
|
339
430
|
// Keep tooltip alive while hovering.
|
|
340
431
|
// Don't schedule hide on mouseleave when the size popover is open —
|
|
@@ -342,6 +433,7 @@ export class TableTooltip {
|
|
|
342
433
|
this._disposers.push(
|
|
343
434
|
on(el, 'mouseenter', () => this._clearTimers()),
|
|
344
435
|
on(el, 'mouseleave', () => {
|
|
436
|
+
if (this._selectMode) return; // keep tooltip alive during cell selection
|
|
345
437
|
if (this._sizePopover && this._sizePopover.style.display !== 'none') return;
|
|
346
438
|
this._scheduleHide();
|
|
347
439
|
}),
|
|
@@ -412,6 +504,13 @@ export class TableTooltip {
|
|
|
412
504
|
this._el.style.display = 'none';
|
|
413
505
|
this._activeTable = null;
|
|
414
506
|
this._activeCell = null;
|
|
507
|
+
// Reset select mode
|
|
508
|
+
if (this._selectMode) {
|
|
509
|
+
this._selectMode = false;
|
|
510
|
+
if (this._selectBtn) this._selectBtn.classList.remove('an-link-tooltip-btn--active');
|
|
511
|
+
if (this._editable) this._editable.classList.remove('an-table-select-mode');
|
|
512
|
+
}
|
|
513
|
+
this._clearSelection();
|
|
415
514
|
this._clearTimers();
|
|
416
515
|
this._hideSizePopover();
|
|
417
516
|
}
|
|
@@ -461,18 +560,129 @@ export class TableTooltip {
|
|
|
461
560
|
|| (this._activeTable && this._activeTable.querySelector('td, th'));
|
|
462
561
|
}
|
|
463
562
|
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
// Cell selection helpers
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
|
|
567
|
+
_toggleSelectMode() {
|
|
568
|
+
this._selectMode = !this._selectMode;
|
|
569
|
+
if (this._selectBtn) {
|
|
570
|
+
this._selectBtn.classList.toggle('an-link-tooltip-btn--active', this._selectMode);
|
|
571
|
+
}
|
|
572
|
+
if (this._editable) {
|
|
573
|
+
this._editable.classList.toggle('an-table-select-mode', this._selectMode);
|
|
574
|
+
}
|
|
575
|
+
if (!this._selectMode) {
|
|
576
|
+
this._clearSelection();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
_clearSelection() {
|
|
581
|
+
this._selectedCells.forEach((c) => c.classList.remove('an-cell-selected'));
|
|
582
|
+
this._selectedCells = [];
|
|
583
|
+
this._selectStart = null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
_setSelection(cells) {
|
|
587
|
+
// Remove highlight from cells no longer in selection
|
|
588
|
+
this._selectedCells.forEach((c) => {
|
|
589
|
+
if (!cells.includes(c)) c.classList.remove('an-cell-selected');
|
|
590
|
+
});
|
|
591
|
+
this._selectedCells = cells;
|
|
592
|
+
cells.forEach((c) => c.classList.add('an-cell-selected'));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Returns all cells in the rectangular area between startCell and endCell,
|
|
597
|
+
* correctly handling rowspan/colspan by using the grid map.
|
|
598
|
+
* The rect is expanded iteratively until it is stable — this ensures any
|
|
599
|
+
* merged cell that starts outside the initial rect but spans into it is
|
|
600
|
+
* fully included.
|
|
601
|
+
*/
|
|
602
|
+
_getRectCells(startCell, endCell) {
|
|
603
|
+
if (!startCell) return [];
|
|
604
|
+
if (!endCell || startCell === endCell) return [startCell];
|
|
605
|
+
const table = startCell.closest('table');
|
|
606
|
+
if (!table || !table.contains(endCell)) return [startCell];
|
|
607
|
+
|
|
608
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
609
|
+
const sp = cellPos.get(startCell);
|
|
610
|
+
const ep = cellPos.get(endCell);
|
|
611
|
+
if (!sp || !ep) return [startCell];
|
|
612
|
+
|
|
613
|
+
let minR = Math.min(sp.r, ep.r);
|
|
614
|
+
let maxR = Math.max(sp.r + sp.rs - 1, ep.r + ep.rs - 1);
|
|
615
|
+
let minC = Math.min(sp.c, ep.c);
|
|
616
|
+
let maxC = Math.max(sp.c + sp.cs - 1, ep.c + ep.cs - 1);
|
|
617
|
+
|
|
618
|
+
// Iteratively expand until stable — handles spans that exceed the current rect
|
|
619
|
+
let changed = true;
|
|
620
|
+
while (changed) {
|
|
621
|
+
changed = false;
|
|
622
|
+
for (let r = minR; r <= maxR; r++) {
|
|
623
|
+
const rowMap = gridMap[r];
|
|
624
|
+
if (!rowMap) continue;
|
|
625
|
+
for (let c = minC; c <= maxC; c++) {
|
|
626
|
+
const cell = rowMap[c];
|
|
627
|
+
if (!cell) continue;
|
|
628
|
+
const pos = cellPos.get(cell);
|
|
629
|
+
if (!pos) continue;
|
|
630
|
+
if (pos.r < minR) { minR = pos.r; changed = true; }
|
|
631
|
+
if (pos.r + pos.rs - 1 > maxR) { maxR = pos.r + pos.rs - 1; changed = true; }
|
|
632
|
+
if (pos.c < minC) { minC = pos.c; changed = true; }
|
|
633
|
+
if (pos.c + pos.cs - 1 > maxC) { maxC = pos.c + pos.cs - 1; changed = true; }
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Collect unique cells in document order
|
|
639
|
+
const seen = new Set();
|
|
640
|
+
const result = [];
|
|
641
|
+
for (let r = minR; r <= maxR; r++) {
|
|
642
|
+
const rowMap = gridMap[r];
|
|
643
|
+
if (!rowMap) continue;
|
|
644
|
+
for (let c = minC; c <= maxC; c++) {
|
|
645
|
+
const cell = rowMap[c];
|
|
646
|
+
if (cell && !seen.has(cell)) {
|
|
647
|
+
seen.add(cell);
|
|
648
|
+
result.push(cell);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return result.length > 0 ? result : [startCell];
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Returns the active cell set: user-selected cells when available,
|
|
657
|
+
* otherwise the single active/cursor cell.
|
|
658
|
+
* @returns {HTMLTableCellElement[]}
|
|
659
|
+
*/
|
|
660
|
+
_getSelectedCells() {
|
|
661
|
+
return this._selectedCells.length > 0
|
|
662
|
+
? this._selectedCells
|
|
663
|
+
: [this._getCell()].filter(Boolean);
|
|
664
|
+
}
|
|
665
|
+
|
|
464
666
|
// ---------------------------------------------------------------------------
|
|
465
667
|
// Table operations
|
|
466
668
|
// ---------------------------------------------------------------------------
|
|
467
669
|
|
|
468
670
|
_addRow(position) {
|
|
469
|
-
const
|
|
470
|
-
if (!
|
|
471
|
-
const
|
|
472
|
-
if (!
|
|
473
|
-
const
|
|
671
|
+
const cells = this._getSelectedCells();
|
|
672
|
+
if (!cells.length) return;
|
|
673
|
+
const table = cells[0].closest('table');
|
|
674
|
+
if (!table) return;
|
|
675
|
+
const allRows = Array.from(table.querySelectorAll('tr'));
|
|
676
|
+
const selectedRows = [...new Set(cells.map((c) => c.closest('tr')).filter(Boolean))];
|
|
677
|
+
// Reference row: topmost for 'above', bottommost for 'below'
|
|
678
|
+
const refRow = selectedRows.reduce((best, r) => {
|
|
679
|
+
const bi = allRows.indexOf(best);
|
|
680
|
+
const ri = allRows.indexOf(r);
|
|
681
|
+
return position === 'above' ? (ri < bi ? r : best) : (ri > bi ? r : best);
|
|
682
|
+
});
|
|
683
|
+
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
474
684
|
const newRow = document.createElement('tr');
|
|
475
|
-
const refCells = Array.from(
|
|
685
|
+
const refCells = Array.from(refRow.cells);
|
|
476
686
|
for (let i = 0; i < colCount; i++) {
|
|
477
687
|
const td = createElement('td', {}, ['\u00a0']);
|
|
478
688
|
const ref = refCells[i];
|
|
@@ -480,25 +690,24 @@ export class TableTooltip {
|
|
|
480
690
|
if (ref && ref.style.minWidth) td.style.minWidth = ref.style.minWidth;
|
|
481
691
|
newRow.appendChild(td);
|
|
482
692
|
}
|
|
483
|
-
if (position === 'above')
|
|
484
|
-
else
|
|
485
|
-
// Defer positioning until the browser has painted the new layout
|
|
693
|
+
if (position === 'above') refRow.parentElement?.insertBefore(newRow, refRow);
|
|
694
|
+
else refRow.insertAdjacentElement('afterend', newRow);
|
|
486
695
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
487
696
|
this.context.invoke('editor.afterCommand');
|
|
488
697
|
}
|
|
489
698
|
|
|
490
699
|
_addColumn(position) {
|
|
491
|
-
const
|
|
492
|
-
if (!
|
|
493
|
-
const table =
|
|
700
|
+
const cells = this._getSelectedCells();
|
|
701
|
+
if (!cells.length) return;
|
|
702
|
+
const table = cells[0].closest('table');
|
|
494
703
|
if (!table) return;
|
|
495
|
-
const
|
|
704
|
+
const colIndices = cells.map((c) => getVisualColIndex(c));
|
|
705
|
+
const targetColIdx = position === 'left' ? Math.min(...colIndices) : Math.max(...colIndices);
|
|
496
706
|
const rows = Array.from(table.querySelectorAll('tr'));
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const isHeaders = rows.map(r => r.closest('thead') !== null);
|
|
707
|
+
const refs = rows.map((r) => position === 'left'
|
|
708
|
+
? getCellAtVisualCol(r, targetColIdx)
|
|
709
|
+
: getCellAfterVisualCol(r, targetColIdx));
|
|
710
|
+
const isHeaders = rows.map((r) => r.closest('thead') !== null);
|
|
502
711
|
rows.forEach((r, i) => {
|
|
503
712
|
r.insertBefore(createElement(isHeaders[i] ? 'th' : 'td', {}, ['\u00a0']), refs[i]);
|
|
504
713
|
});
|
|
@@ -507,34 +716,45 @@ export class TableTooltip {
|
|
|
507
716
|
}
|
|
508
717
|
|
|
509
718
|
_deleteRow() {
|
|
510
|
-
const
|
|
511
|
-
if (!
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
if (!row || !table) return;
|
|
515
|
-
// Guard: do not delete the last body row (thead rows are not counted)
|
|
719
|
+
const cells = this._getSelectedCells();
|
|
720
|
+
if (!cells.length) return;
|
|
721
|
+
const table = cells[0].closest('table');
|
|
722
|
+
if (!table) return;
|
|
516
723
|
const tbody = table.querySelector('tbody');
|
|
517
|
-
const
|
|
518
|
-
|
|
724
|
+
const totalBodyRows = tbody
|
|
725
|
+
? tbody.querySelectorAll('tr').length
|
|
726
|
+
: table.querySelectorAll('tr').length;
|
|
727
|
+
const selectedRows = [...new Set(cells.map((c) => c.closest('tr')).filter(Boolean))];
|
|
728
|
+
const bodyRowsToDelete = selectedRows.filter((r) => r.closest('tbody'));
|
|
729
|
+
// Guard: keep at least one body row
|
|
730
|
+
if (bodyRowsToDelete.length >= totalBodyRows) return;
|
|
519
731
|
this._activeCell = null;
|
|
520
|
-
|
|
732
|
+
this._clearSelection();
|
|
733
|
+
selectedRows.forEach((r) => r.parentElement?.removeChild(r));
|
|
521
734
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
522
735
|
this.context.invoke('editor.afterCommand');
|
|
523
736
|
}
|
|
524
737
|
|
|
525
738
|
_deleteColumn() {
|
|
526
|
-
const
|
|
527
|
-
if (!
|
|
528
|
-
const table =
|
|
739
|
+
const cells = this._getSelectedCells();
|
|
740
|
+
if (!cells.length) return;
|
|
741
|
+
const table = cells[0].closest('table');
|
|
529
742
|
if (!table) return;
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
const
|
|
743
|
+
const tableRows = Array.from(table.querySelectorAll('tr'));
|
|
744
|
+
if (tableRows[0] && tableRows[0].cells.length <= 1) return; // guard: keep ≥1 col
|
|
745
|
+
const colIndices = [...new Set(cells.map((c) => getVisualColIndex(c)))];
|
|
746
|
+
if (colIndices.length >= (tableRows[0]?.cells.length ?? 1)) return;
|
|
747
|
+
// Collect all cell references upfront before any removal (avoids stale visual indices)
|
|
748
|
+
const cellsToDelete = [];
|
|
749
|
+
colIndices.forEach((colIdx) => {
|
|
750
|
+
tableRows.forEach((r) => {
|
|
751
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
752
|
+
if (c) cellsToDelete.push(c);
|
|
753
|
+
});
|
|
754
|
+
});
|
|
533
755
|
this._activeCell = null;
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const cells = rows.map(r => getCellAtVisualCol(r, visualColIdx));
|
|
537
|
-
cells.forEach((c, i) => { if (c) rows[i].removeChild(c); });
|
|
756
|
+
this._clearSelection();
|
|
757
|
+
cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
|
|
538
758
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
539
759
|
this.context.invoke('editor.afterCommand');
|
|
540
760
|
}
|
|
@@ -542,41 +762,57 @@ export class TableTooltip {
|
|
|
542
762
|
_mergeCells() {
|
|
543
763
|
const cell = this._getCell();
|
|
544
764
|
if (!cell) return;
|
|
545
|
-
const sel = window.getSelection();
|
|
546
|
-
if (!sel || sel.rangeCount === 0) return;
|
|
547
|
-
const range = sel.getRangeAt(0);
|
|
548
765
|
const table = cell.closest('table');
|
|
549
766
|
if (!table) return;
|
|
550
767
|
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (rows.length === 1) {
|
|
561
|
-
// Horizontal merge within a single row
|
|
562
|
-
const row = rows[0];
|
|
563
|
-
const rowSelected = Array.from(row.cells).filter((c) => selected.includes(c));
|
|
564
|
-
if (rowSelected.length < 2) return;
|
|
565
|
-
const first = rowSelected[0];
|
|
566
|
-
first.colSpan = rowSelected.reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
567
|
-
first.innerHTML = rowSelected.map((c) => c.innerHTML).join('');
|
|
568
|
-
rowSelected.slice(1).forEach((c) => row.removeChild(c));
|
|
569
|
-
} else {
|
|
570
|
-
// Vertical merge across rows — merge into first selected cell (rowspan)
|
|
571
|
-
const visualCols = [...new Set(selected.map((c) => getVisualColIndex(c)))];
|
|
572
|
-
if (visualCols.length !== 1) return; // only support single-column vertical merge
|
|
573
|
-
const first = selected[0];
|
|
574
|
-
first.rowSpan = selected.reduce((sum, c) => sum + (c.rowSpan || 1), 0);
|
|
575
|
-
first.innerHTML = selected.map((c) => c.innerHTML).join('');
|
|
576
|
-
selected.slice(1).forEach((c) => {
|
|
577
|
-
if (c.closest('tr')) c.closest('tr').removeChild(c);
|
|
768
|
+
// Prefer user panel-selected cells; fall back to text-selection range
|
|
769
|
+
let selected = this._getSelectedCells().filter((c) => table.contains(c));
|
|
770
|
+
if (selected.length < 2) {
|
|
771
|
+
const sel = window.getSelection();
|
|
772
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
773
|
+
const range = sel.getRangeAt(0);
|
|
774
|
+
const allCells = Array.from(table.querySelectorAll('td, th'));
|
|
775
|
+
selected = allCells.filter((c) => {
|
|
776
|
+
try { return range.intersectsNode(c); } catch { return false; }
|
|
578
777
|
});
|
|
778
|
+
if (selected.length < 2) return;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
782
|
+
|
|
783
|
+
// Bounding rect that encompasses every selected cell's full span
|
|
784
|
+
let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
|
|
785
|
+
selected.forEach((c) => {
|
|
786
|
+
const pos = cellPos.get(c);
|
|
787
|
+
if (!pos) return;
|
|
788
|
+
if (pos.r < minR) minR = pos.r;
|
|
789
|
+
if (pos.r + pos.rs - 1 > maxR) maxR = pos.r + pos.rs - 1;
|
|
790
|
+
if (pos.c < minC) minC = pos.c;
|
|
791
|
+
if (pos.c + pos.cs - 1 > maxC) maxC = pos.c + pos.cs - 1;
|
|
792
|
+
});
|
|
793
|
+
if (minR === Infinity) return;
|
|
794
|
+
|
|
795
|
+
// Collect every unique cell in the bounding rect
|
|
796
|
+
const seen = new Set();
|
|
797
|
+
const rectCells = [];
|
|
798
|
+
for (let r = minR; r <= maxR; r++) {
|
|
799
|
+
const rowMap = gridMap[r];
|
|
800
|
+
if (!rowMap) continue;
|
|
801
|
+
for (let c = minC; c <= maxC; c++) {
|
|
802
|
+
const tc = rowMap[c];
|
|
803
|
+
if (tc && !seen.has(tc)) { seen.add(tc); rectCells.push(tc); }
|
|
804
|
+
}
|
|
579
805
|
}
|
|
806
|
+
if (rectCells.length < 2) return;
|
|
807
|
+
|
|
808
|
+
const first = rectCells[0];
|
|
809
|
+
first.colSpan = maxC - minC + 1;
|
|
810
|
+
first.rowSpan = maxR - minR + 1;
|
|
811
|
+
first.style.verticalAlign = 'middle';
|
|
812
|
+
first.innerHTML = rectCells.map((c) => c.innerHTML).join('');
|
|
813
|
+
rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
|
|
814
|
+
|
|
815
|
+
this._clearSelection();
|
|
580
816
|
this.context.invoke('editor.afterCommand');
|
|
581
817
|
}
|
|
582
818
|
|
|
@@ -588,6 +824,73 @@ export class TableTooltip {
|
|
|
588
824
|
this.context.invoke('editor.afterCommand');
|
|
589
825
|
}
|
|
590
826
|
|
|
827
|
+
_unmergeCells() {
|
|
828
|
+
const cells = this._getSelectedCells();
|
|
829
|
+
if (!cells.length) return;
|
|
830
|
+
const table = cells[0].closest('table');
|
|
831
|
+
if (!table) return;
|
|
832
|
+
const mergedCells = cells.filter(
|
|
833
|
+
(c) => table.contains(c) && ((c.colSpan || 1) > 1 || (c.rowSpan || 1) > 1)
|
|
834
|
+
);
|
|
835
|
+
if (!mergedCells.length) return;
|
|
836
|
+
mergedCells.forEach((cell) => {
|
|
837
|
+
if (table.contains(cell)) this._unmergeOne(cell, table);
|
|
838
|
+
});
|
|
839
|
+
this._clearSelection();
|
|
840
|
+
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
841
|
+
this.context.invoke('editor.afterCommand');
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Split a single merged cell (colspan/rowspan > 1) back into individual cells.
|
|
846
|
+
* New cells are empty ( ); the original cell retains its content.
|
|
847
|
+
* @param {HTMLTableCellElement} cell
|
|
848
|
+
* @param {HTMLTableElement} table
|
|
849
|
+
*/
|
|
850
|
+
_unmergeOne(cell, table) {
|
|
851
|
+
const cs = cell.colSpan || 1;
|
|
852
|
+
const rs = cell.rowSpan || 1;
|
|
853
|
+
if (cs === 1 && rs === 1) return;
|
|
854
|
+
|
|
855
|
+
// Build grid map from current DOM state (before any mutation)
|
|
856
|
+
const { cellPos } = buildGridMap(table);
|
|
857
|
+
const pos = cellPos.get(cell);
|
|
858
|
+
if (!pos) return;
|
|
859
|
+
|
|
860
|
+
const { r, c } = pos;
|
|
861
|
+
const tableRows = Array.from(table.rows);
|
|
862
|
+
const tag = cell.tagName.toLowerCase(); // preserve td / th
|
|
863
|
+
|
|
864
|
+
// Reset the original cell
|
|
865
|
+
cell.rowSpan = 1;
|
|
866
|
+
cell.colSpan = 1;
|
|
867
|
+
cell.style.verticalAlign = '';
|
|
868
|
+
|
|
869
|
+
// Same row: insert (cs - 1) sibling cells after the original cell
|
|
870
|
+
if (cs > 1) {
|
|
871
|
+
const insertRef = cell.nextElementSibling;
|
|
872
|
+
for (let dc = 1; dc < cs; dc++) {
|
|
873
|
+
tableRows[r].insertBefore(createElement(tag, {}, ['\u00a0']), insertRef);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// Rows below: insert cs cells at the correct visual column position (for rowspan)
|
|
878
|
+
for (let dr = 1; dr < rs; dr++) {
|
|
879
|
+
const targetRow = tableRows[r + dr];
|
|
880
|
+
if (!targetRow) continue;
|
|
881
|
+
// Find the first cell in this row whose visual-col origin is beyond c
|
|
882
|
+
// (using cellPos built before mutations — valid for these untouched rows)
|
|
883
|
+
let ref = null;
|
|
884
|
+
for (const tc of targetRow.cells) {
|
|
885
|
+
const tp = cellPos.get(tc);
|
|
886
|
+
if (tp && tp.c > c) { ref = tc; break; }
|
|
887
|
+
}
|
|
888
|
+
for (let dc = 0; dc < cs; dc++) {
|
|
889
|
+
targetRow.insertBefore(createElement(tag, {}, ['\u00a0']), ref);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
591
894
|
// ---------------------------------------------------------------------------
|
|
592
895
|
// Size popover (column width / row height)
|
|
593
896
|
// ---------------------------------------------------------------------------
|
|
@@ -607,9 +910,9 @@ export class TableTooltip {
|
|
|
607
910
|
|
|
608
911
|
const actionsEl = createElement('div', { class: 'an-size-popover-actions' });
|
|
609
912
|
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
610
|
-
cancelBtn.textContent =
|
|
913
|
+
cancelBtn.textContent = this.context.locale.tooltips.table.cancelBtn;
|
|
611
914
|
const applyBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
612
|
-
applyBtn.textContent =
|
|
915
|
+
applyBtn.textContent = this.context.locale.tooltips.table.applyBtn;
|
|
613
916
|
actionsEl.appendChild(cancelBtn);
|
|
614
917
|
actionsEl.appendChild(applyBtn);
|
|
615
918
|
|
|
@@ -653,51 +956,58 @@ export class TableTooltip {
|
|
|
653
956
|
if (type === 'border') {
|
|
654
957
|
const table = cell.closest('table');
|
|
655
958
|
if (!table) return;
|
|
656
|
-
// Read current inline border-width, fall back to computed value, then default 1px
|
|
657
959
|
const firstCell = table.querySelector('td, th');
|
|
658
960
|
const currentPx = firstCell
|
|
659
961
|
? (parseInt(firstCell.style.borderWidth, 10) ||
|
|
660
962
|
parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1)
|
|
661
963
|
: 1;
|
|
662
|
-
this._sizeTitleEl.textContent =
|
|
964
|
+
this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
|
|
663
965
|
this._sizeInputEl.min = '0';
|
|
664
966
|
this._sizeInputEl.max = '10';
|
|
665
967
|
this._sizeInputEl.value = currentPx;
|
|
666
968
|
this._sizeApply = (val) => {
|
|
667
|
-
// Apply border-width to every cell; with border-collapse:collapse this
|
|
668
|
-
// controls all grid lines including the outer table border.
|
|
669
|
-
// Setting only borderWidth preserves the existing border-color from CSS.
|
|
670
969
|
const cells = Array.from(table.querySelectorAll('td, th'));
|
|
671
970
|
if (val === 0) {
|
|
672
|
-
cells.forEach(c => { c.style.borderWidth = '0'; c.style.borderStyle = 'none'; });
|
|
971
|
+
cells.forEach((c) => { c.style.borderWidth = '0'; c.style.borderStyle = 'none'; });
|
|
673
972
|
} else {
|
|
674
|
-
cells.forEach(c => { c.style.borderWidth = `${val}px`; c.style.borderStyle = 'solid'; });
|
|
973
|
+
cells.forEach((c) => { c.style.borderWidth = `${val}px`; c.style.borderStyle = 'solid'; });
|
|
675
974
|
}
|
|
676
975
|
this.context.invoke('editor.afterCommand');
|
|
677
976
|
};
|
|
678
977
|
} else {
|
|
679
978
|
const isCol = type === 'col';
|
|
680
|
-
|
|
979
|
+
const activeCells = this._getSelectedCells().filter((c) => {
|
|
980
|
+
const t = c.closest('table');
|
|
981
|
+
return t && t === cell.closest('table');
|
|
982
|
+
});
|
|
983
|
+
this._sizeTitleEl.textContent = isCol
|
|
984
|
+
? this.context.locale.tooltips.table.columnWidthPx
|
|
985
|
+
: this.context.locale.tooltips.table.rowHeightPx;
|
|
681
986
|
this._sizeInputEl.min = '1';
|
|
682
987
|
this._sizeInputEl.max = '2000';
|
|
683
988
|
this._sizeInputEl.value = isCol
|
|
684
989
|
? (cell.offsetWidth || 120)
|
|
685
990
|
: (cell.closest('tr') ? (cell.closest('tr').offsetHeight || 40) : 40);
|
|
686
991
|
this._sizeApply = (val) => {
|
|
992
|
+
const table = cell.closest('table');
|
|
993
|
+
if (!table) return;
|
|
687
994
|
if (isCol) {
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
995
|
+
// Apply to all selected columns (or just the current column)
|
|
996
|
+
const colIndices = [...new Set(activeCells.map((c) => getVisualColIndex(c)))];
|
|
997
|
+
const tableRows = Array.from(table.querySelectorAll('tr'));
|
|
998
|
+
colIndices.forEach((colIdx) => {
|
|
999
|
+
tableRows.forEach((r) => {
|
|
1000
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
1001
|
+
// F-1: skip merged cells — same reason as drag-resize _colCells.
|
|
1002
|
+
if (c && (c.colSpan || 1) === 1) { c.style.width = `${val}px`; c.style.minWidth = `${val}px`; }
|
|
1003
|
+
});
|
|
695
1004
|
});
|
|
696
1005
|
} else {
|
|
697
|
-
|
|
698
|
-
|
|
1006
|
+
// Apply to all selected rows (or just the current row)
|
|
1007
|
+
const selectedRows = [...new Set(activeCells.map((c) => c.closest('tr')).filter(Boolean))];
|
|
1008
|
+
selectedRows.forEach((row) => {
|
|
699
1009
|
for (const c of row.cells) { c.style.height = `${val}px`; c.style.minHeight = `${val}px`; }
|
|
700
|
-
}
|
|
1010
|
+
});
|
|
701
1011
|
}
|
|
702
1012
|
this.context.invoke('editor.afterCommand');
|
|
703
1013
|
};
|