autumnnote 1.0.7 → 1.0.8
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 +3 -3
- package/dist/autumnnote.css +40 -4
- package/dist/autumnnote.es.js +513 -107
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +513 -107
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/editing/Style.js +195 -44
- package/src/js/editing/Typing.js +2 -2
- package/src/js/module/Editor.js +14 -3
- package/src/js/module/EmojiDialog.js +11 -0
- package/src/js/module/IconDialog.js +11 -0
- package/src/js/module/ImageDialog.js +9 -5
- package/src/js/module/Placeholder.js +6 -1
- package/src/js/module/TableTooltip.js +393 -86
- package/src/styles/autumnnote.scss +50 -5
package/dist/autumnnote.umd.js
CHANGED
|
@@ -293,8 +293,24 @@
|
|
|
293
293
|
}
|
|
294
294
|
/**
|
|
295
295
|
* Strikethrough / removes strikethrough.
|
|
296
|
+
* Falls back to manual DOM manipulation inside nested formats where
|
|
297
|
+
* execCommand's state detection is unreliable (mirrors underline() logic).
|
|
296
298
|
*/
|
|
297
|
-
|
|
299
|
+
function strikethrough() {
|
|
300
|
+
const sel = window.getSelection();
|
|
301
|
+
if (!sel || !sel.rangeCount) return;
|
|
302
|
+
let sc = sel.getRangeAt(0).startContainer;
|
|
303
|
+
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
304
|
+
const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
|
|
305
|
+
const nativeState = document.queryCommandState("strikeThrough");
|
|
306
|
+
if (sEl && !nativeState) {
|
|
307
|
+
const parent = sEl.parentNode;
|
|
308
|
+
while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
|
|
309
|
+
parent.removeChild(sEl);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
execCommand("strikeThrough");
|
|
313
|
+
}
|
|
298
314
|
/**
|
|
299
315
|
* Superscript toggle.
|
|
300
316
|
*/
|
|
@@ -327,6 +343,22 @@
|
|
|
327
343
|
function fontSize(size, editable = document) {
|
|
328
344
|
const sel = window.getSelection();
|
|
329
345
|
const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
|
|
346
|
+
if (wasCollapsed && sel && sel.rangeCount > 0) {
|
|
347
|
+
try {
|
|
348
|
+
const range = sel.getRangeAt(0);
|
|
349
|
+
const span = document.createElement("span");
|
|
350
|
+
span.style.fontSize = size;
|
|
351
|
+
const zwsNode = document.createTextNode("");
|
|
352
|
+
span.appendChild(zwsNode);
|
|
353
|
+
range.insertNode(span);
|
|
354
|
+
const nr = document.createRange();
|
|
355
|
+
nr.setStart(zwsNode, zwsNode.textContent.length);
|
|
356
|
+
nr.collapse(true);
|
|
357
|
+
sel.removeAllRanges();
|
|
358
|
+
sel.addRange(nr);
|
|
359
|
+
} catch (_) {}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
330
362
|
execCommand("fontSize", "7");
|
|
331
363
|
const scope = editable instanceof HTMLElement ? editable : document;
|
|
332
364
|
const newSpans = [];
|
|
@@ -338,27 +370,17 @@
|
|
|
338
370
|
el.parentNode.removeChild(el);
|
|
339
371
|
newSpans.push(span);
|
|
340
372
|
});
|
|
341
|
-
if (sel && newSpans.length > 0) {
|
|
373
|
+
if (!wasCollapsed && sel && newSpans.length > 0) {
|
|
342
374
|
const first = newSpans[0];
|
|
343
375
|
const last = newSpans[newSpans.length - 1];
|
|
344
376
|
try {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
sel.addRange(nr);
|
|
353
|
-
} else {
|
|
354
|
-
const nr = document.createRange();
|
|
355
|
-
const startNode = first.firstChild || first;
|
|
356
|
-
const endNode = last.lastChild || last;
|
|
357
|
-
nr.setStart(startNode, 0);
|
|
358
|
-
nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
|
|
359
|
-
sel.removeAllRanges();
|
|
360
|
-
sel.addRange(nr);
|
|
361
|
-
}
|
|
377
|
+
const nr = document.createRange();
|
|
378
|
+
const startNode = first.firstChild || first;
|
|
379
|
+
const endNode = last.lastChild || last;
|
|
380
|
+
nr.setStart(startNode, 0);
|
|
381
|
+
nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
|
|
382
|
+
sel.removeAllRanges();
|
|
383
|
+
sel.addRange(nr);
|
|
362
384
|
} catch (_) {}
|
|
363
385
|
}
|
|
364
386
|
}
|
|
@@ -389,8 +411,57 @@
|
|
|
389
411
|
var indent = () => execCommand("indent");
|
|
390
412
|
/**
|
|
391
413
|
* Outdents the list or block.
|
|
414
|
+
* G.5: When cursor is inside a checklist item, "outdent" means converting
|
|
415
|
+
* that item back to a regular <p> element rather than calling execCommand
|
|
416
|
+
* (which would destroy the ul > li checklist structure).
|
|
392
417
|
*/
|
|
393
|
-
|
|
418
|
+
function outdent() {
|
|
419
|
+
const sel = window.getSelection();
|
|
420
|
+
if (sel && sel.rangeCount) {
|
|
421
|
+
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
422
|
+
if (container.nodeType === 3) container = container.parentElement;
|
|
423
|
+
const checkLi = container && container.closest && container.closest(".an-checklist li");
|
|
424
|
+
if (checkLi) {
|
|
425
|
+
_checklistItemToP(checkLi);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
execCommand("outdent");
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* G.5 helper: splits a checklist at checkLi, converts it to a <p>,
|
|
433
|
+
* and keeps items before/after as separate checklists.
|
|
434
|
+
* @param {HTMLElement} checkLi
|
|
435
|
+
*/
|
|
436
|
+
function _checklistItemToP(checkLi) {
|
|
437
|
+
const checkUl = checkLi.closest(".an-checklist");
|
|
438
|
+
if (!checkUl) return;
|
|
439
|
+
const allLis = Array.from(checkUl.children);
|
|
440
|
+
const liIndex = allLis.indexOf(checkLi);
|
|
441
|
+
const afterLis = allLis.slice(liIndex + 1);
|
|
442
|
+
const p = document.createElement("p");
|
|
443
|
+
p.textContent = Array.from(checkLi.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/\u200B/g, "").trim() || "\xA0";
|
|
444
|
+
if (afterLis.length > 0) {
|
|
445
|
+
const newUl = document.createElement("ul");
|
|
446
|
+
newUl.className = "an-checklist";
|
|
447
|
+
afterLis.forEach((li) => newUl.appendChild(li));
|
|
448
|
+
checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
|
|
449
|
+
}
|
|
450
|
+
checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
|
|
451
|
+
checkUl.removeChild(checkLi);
|
|
452
|
+
if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
|
|
453
|
+
try {
|
|
454
|
+
const nr = document.createRange();
|
|
455
|
+
const firstChild = p.firstChild;
|
|
456
|
+
nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
|
|
457
|
+
nr.collapse(true);
|
|
458
|
+
const s = window.getSelection();
|
|
459
|
+
if (s) {
|
|
460
|
+
s.removeAllRanges();
|
|
461
|
+
s.addRange(nr);
|
|
462
|
+
}
|
|
463
|
+
} catch {}
|
|
464
|
+
}
|
|
394
465
|
/**
|
|
395
466
|
* Inserts an unordered list or converts selection.
|
|
396
467
|
*/
|
|
@@ -594,10 +665,60 @@
|
|
|
594
665
|
sel.addRange(nr);
|
|
595
666
|
return;
|
|
596
667
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
668
|
+
if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
|
|
669
|
+
const BLOCK_TAGS_MULTI = new Set([
|
|
670
|
+
"P",
|
|
671
|
+
"DIV",
|
|
672
|
+
"H1",
|
|
673
|
+
"H2",
|
|
674
|
+
"H3",
|
|
675
|
+
"H4",
|
|
676
|
+
"H5",
|
|
677
|
+
"H6",
|
|
678
|
+
"BLOCKQUOTE",
|
|
679
|
+
"PRE",
|
|
680
|
+
"LI"
|
|
681
|
+
]);
|
|
682
|
+
const blocks = [];
|
|
683
|
+
const seenBlocks = /* @__PURE__ */ new Set();
|
|
684
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
685
|
+
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
686
|
+
let node;
|
|
687
|
+
while (node = iter.nextNode()) {
|
|
688
|
+
if (!range.intersectsNode(node)) continue;
|
|
689
|
+
let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
690
|
+
while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
|
|
691
|
+
if (block && !seenBlocks.has(block)) {
|
|
692
|
+
seenBlocks.add(block);
|
|
693
|
+
blocks.push(block);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (blocks.length === 0) return;
|
|
697
|
+
const newUl = document.createElement("ul");
|
|
698
|
+
newUl.className = "an-checklist";
|
|
699
|
+
let lastTextNode = null;
|
|
700
|
+
blocks.forEach((block) => {
|
|
701
|
+
const li = document.createElement("li");
|
|
702
|
+
const cb = document.createElement("input");
|
|
703
|
+
cb.type = "checkbox";
|
|
704
|
+
cb.setAttribute("contenteditable", "false");
|
|
705
|
+
li.appendChild(cb);
|
|
706
|
+
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
707
|
+
const tn = document.createTextNode(blockText || "");
|
|
708
|
+
li.appendChild(tn);
|
|
709
|
+
newUl.appendChild(li);
|
|
710
|
+
lastTextNode = tn;
|
|
711
|
+
});
|
|
712
|
+
const firstBlock = blocks[0];
|
|
713
|
+
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
714
|
+
blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
|
|
715
|
+
if (lastTextNode) {
|
|
716
|
+
const nr = document.createRange();
|
|
717
|
+
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
718
|
+
nr.collapse(true);
|
|
719
|
+
sel.removeAllRanges();
|
|
720
|
+
sel.addRange(nr);
|
|
721
|
+
}
|
|
601
722
|
}
|
|
602
723
|
/**
|
|
603
724
|
* Returns true when the cursor is inside a checklist item.
|
|
@@ -1676,7 +1797,7 @@
|
|
|
1676
1797
|
const para = closestPara(range.sc, editable);
|
|
1677
1798
|
if (para && isLi(para)) {
|
|
1678
1799
|
event.preventDefault();
|
|
1679
|
-
if (event.shiftKey)
|
|
1800
|
+
if (event.shiftKey) outdent();
|
|
1680
1801
|
else execCommand("indent");
|
|
1681
1802
|
return true;
|
|
1682
1803
|
}
|
|
@@ -2097,7 +2218,12 @@
|
|
|
2097
2218
|
};
|
|
2098
2219
|
const isReadOnly = () => this.context.layoutInfo.container.classList.contains("an-disabled");
|
|
2099
2220
|
this._disposers.push(on(editable, "keydown", onKeydown), on(editable, "beforeinput", onBeforeInput), on(editable, "input", onInput), on(document, "selectionchange", onSelChange), on(editable, "click", onCheckboxClick), on(editable, "mouseup", fixChecklistCursor), on(editable, "keyup", fixChecklistCursor), on(editable, "dragstart", (e) => {
|
|
2100
|
-
if (isReadOnly())
|
|
2221
|
+
if (isReadOnly()) {
|
|
2222
|
+
e.preventDefault();
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
const target = e.target;
|
|
2226
|
+
if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
|
|
2101
2227
|
}), on(editable, "drop", (e) => {
|
|
2102
2228
|
if (isReadOnly()) e.preventDefault();
|
|
2103
2229
|
}));
|
|
@@ -2254,7 +2380,7 @@
|
|
|
2254
2380
|
* @param {string} html - HTML string (will be sanitised)
|
|
2255
2381
|
*/
|
|
2256
2382
|
setHTML(html) {
|
|
2257
|
-
this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html);
|
|
2383
|
+
this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html, { allowIframes: true });
|
|
2258
2384
|
if (this._history) this._history.reset();
|
|
2259
2385
|
this.afterCommand();
|
|
2260
2386
|
}
|
|
@@ -3629,7 +3755,7 @@
|
|
|
3629
3755
|
_update() {
|
|
3630
3756
|
const editable = this.context.layoutInfo.editable;
|
|
3631
3757
|
const isFocused = document.activeElement === editable;
|
|
3632
|
-
const isEmpty = !editable.textContent.trim() && !editable.querySelector("img, table, hr, .an-video-wrapper");
|
|
3758
|
+
const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
|
|
3633
3759
|
editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
|
|
3634
3760
|
}
|
|
3635
3761
|
};
|
|
@@ -4064,7 +4190,7 @@
|
|
|
4064
4190
|
const fileInput = createElement("input", {
|
|
4065
4191
|
type: "file",
|
|
4066
4192
|
class: "an-input",
|
|
4067
|
-
accept: "image
|
|
4193
|
+
accept: "image/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif"
|
|
4068
4194
|
});
|
|
4069
4195
|
this._fileInput = fileInput;
|
|
4070
4196
|
const fileHint = createElement("p", { class: "an-dialog-hint" });
|
|
@@ -4111,14 +4237,15 @@
|
|
|
4111
4237
|
_onFileChange() {
|
|
4112
4238
|
const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
|
|
4113
4239
|
if (!file || !file.type.startsWith("image/")) return;
|
|
4114
|
-
if ([
|
|
4115
|
-
"image/
|
|
4116
|
-
"image/
|
|
4117
|
-
"image/
|
|
4118
|
-
"image/
|
|
4119
|
-
"image/
|
|
4120
|
-
|
|
4121
|
-
|
|
4240
|
+
if (!new Set([
|
|
4241
|
+
"image/jpeg",
|
|
4242
|
+
"image/png",
|
|
4243
|
+
"image/gif",
|
|
4244
|
+
"image/webp",
|
|
4245
|
+
"image/svg+xml",
|
|
4246
|
+
"image/avif"
|
|
4247
|
+
]).has(file.type)) {
|
|
4248
|
+
const message = `Format "${file.type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`;
|
|
4122
4249
|
if (this._fileHint) this._fileHint.textContent = message;
|
|
4123
4250
|
this.context.triggerEvent("imageError", {
|
|
4124
4251
|
file,
|
|
@@ -5572,6 +5699,47 @@
|
|
|
5572
5699
|
}
|
|
5573
5700
|
return null;
|
|
5574
5701
|
}
|
|
5702
|
+
/**
|
|
5703
|
+
* Build a 2D grid map of the table, accounting for both rowspan and colspan.
|
|
5704
|
+
*
|
|
5705
|
+
* gridMap[r][c] = the DOM cell occupying visual grid position (r, c).
|
|
5706
|
+
* cellPos = WeakMap: cell → { r, c, rs, cs } (top-left grid origin + span).
|
|
5707
|
+
*
|
|
5708
|
+
* Uses HTMLTableElement.rows which is scoped to the table itself and never
|
|
5709
|
+
* includes rows from nested tables.
|
|
5710
|
+
*
|
|
5711
|
+
* @param {HTMLTableElement} table
|
|
5712
|
+
* @returns {{ gridMap: Object, cellPos: WeakMap }}
|
|
5713
|
+
*/
|
|
5714
|
+
function buildGridMap(table) {
|
|
5715
|
+
const rows = Array.from(table.rows);
|
|
5716
|
+
const gridMap = {};
|
|
5717
|
+
const cellPos = /* @__PURE__ */ new WeakMap();
|
|
5718
|
+
rows.forEach((row, r) => {
|
|
5719
|
+
if (!gridMap[r]) gridMap[r] = {};
|
|
5720
|
+
let c = 0;
|
|
5721
|
+
for (const cell of row.cells) {
|
|
5722
|
+
while (gridMap[r][c]) c++;
|
|
5723
|
+
const rs = cell.rowSpan || 1;
|
|
5724
|
+
const cs = cell.colSpan || 1;
|
|
5725
|
+
cellPos.set(cell, {
|
|
5726
|
+
r,
|
|
5727
|
+
c,
|
|
5728
|
+
rs,
|
|
5729
|
+
cs
|
|
5730
|
+
});
|
|
5731
|
+
for (let dr = 0; dr < rs; dr++) {
|
|
5732
|
+
if (!gridMap[r + dr]) gridMap[r + dr] = {};
|
|
5733
|
+
for (let dc = 0; dc < cs; dc++) gridMap[r + dr][c + dc] = cell;
|
|
5734
|
+
}
|
|
5735
|
+
c += cs;
|
|
5736
|
+
}
|
|
5737
|
+
});
|
|
5738
|
+
return {
|
|
5739
|
+
gridMap,
|
|
5740
|
+
cellPos
|
|
5741
|
+
};
|
|
5742
|
+
}
|
|
5575
5743
|
var ICONS$2 = {
|
|
5576
5744
|
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>`,
|
|
5577
5745
|
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>`,
|
|
@@ -5580,10 +5748,12 @@
|
|
|
5580
5748
|
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>`,
|
|
5581
5749
|
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>`,
|
|
5582
5750
|
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>`,
|
|
5751
|
+
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>`,
|
|
5583
5752
|
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>`,
|
|
5584
5753
|
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>`,
|
|
5585
5754
|
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>`,
|
|
5586
|
-
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
|
|
5755
|
+
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>`,
|
|
5756
|
+
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>`
|
|
5587
5757
|
};
|
|
5588
5758
|
var TableTooltip = class {
|
|
5589
5759
|
/** @param {import('../Context.js').Context} context */
|
|
@@ -5599,6 +5769,12 @@
|
|
|
5599
5769
|
this._sizeApply = null;
|
|
5600
5770
|
this._sizeTitleEl = null;
|
|
5601
5771
|
this._sizeInputEl = null;
|
|
5772
|
+
this._selectMode = false;
|
|
5773
|
+
this._selectedCells = [];
|
|
5774
|
+
this._selectStart = null;
|
|
5775
|
+
this._selectDragging = false;
|
|
5776
|
+
this._selectBtn = null;
|
|
5777
|
+
this._editable = null;
|
|
5602
5778
|
}
|
|
5603
5779
|
initialize() {
|
|
5604
5780
|
this._el = this._buildTooltip();
|
|
@@ -5606,6 +5782,29 @@
|
|
|
5606
5782
|
this._sizePopover = this._buildSizePopover();
|
|
5607
5783
|
document.body.appendChild(this._sizePopover);
|
|
5608
5784
|
const editable = this.context.layoutInfo.editable;
|
|
5785
|
+
this._editable = editable;
|
|
5786
|
+
const onSelMousedown = (e) => {
|
|
5787
|
+
if (!this._selectMode) return;
|
|
5788
|
+
const cell = e.target.closest("td, th");
|
|
5789
|
+
if (!cell || !editable.contains(cell)) return;
|
|
5790
|
+
if (cell.style.cursor === "col-resize" || cell.style.cursor === "row-resize") return;
|
|
5791
|
+
e.preventDefault();
|
|
5792
|
+
this._activeTable = cell.closest("table");
|
|
5793
|
+
this._selectStart = cell;
|
|
5794
|
+
this._selectDragging = true;
|
|
5795
|
+
this._setSelection([cell]);
|
|
5796
|
+
};
|
|
5797
|
+
const onSelMousemove = (e) => {
|
|
5798
|
+
if (!this._selectMode || !this._selectDragging || !this._selectStart) return;
|
|
5799
|
+
const cell = e.target.closest("td, th");
|
|
5800
|
+
if (!cell || !editable.contains(cell)) return;
|
|
5801
|
+
if (cell.closest("table") !== this._activeTable) return;
|
|
5802
|
+
this._setSelection(this._getRectCells(this._selectStart, cell));
|
|
5803
|
+
};
|
|
5804
|
+
const onSelMouseup = () => {
|
|
5805
|
+
this._selectDragging = false;
|
|
5806
|
+
};
|
|
5807
|
+
this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
|
|
5609
5808
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
5610
5809
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
5611
5810
|
const table = e.target.closest("table");
|
|
@@ -5615,9 +5814,11 @@
|
|
|
5615
5814
|
this._scheduleShow(table);
|
|
5616
5815
|
}
|
|
5617
5816
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
5817
|
+
if (this._selectMode) return;
|
|
5618
5818
|
const to = e.relatedTarget;
|
|
5619
5819
|
if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
|
|
5620
5820
|
}, { passive: true }), on(document, "click", (e) => {
|
|
5821
|
+
if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
|
|
5621
5822
|
if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
|
|
5622
5823
|
}));
|
|
5623
5824
|
this._initResize();
|
|
@@ -5690,7 +5891,7 @@
|
|
|
5690
5891
|
if (_edge === "col") {
|
|
5691
5892
|
_startW = _nearCell.offsetWidth;
|
|
5692
5893
|
_colIdx = getVisualColIndex(_nearCell);
|
|
5693
|
-
_colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean) : [];
|
|
5894
|
+
_colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean).filter((c) => (c.colSpan || 1) === 1) : [];
|
|
5694
5895
|
document.body.style.cursor = "col-resize";
|
|
5695
5896
|
} else {
|
|
5696
5897
|
_row = _nearCell.closest("tr");
|
|
@@ -5761,6 +5962,9 @@
|
|
|
5761
5962
|
this._label.textContent = "Table";
|
|
5762
5963
|
el.appendChild(this._label);
|
|
5763
5964
|
el.appendChild(this._sep());
|
|
5965
|
+
this._selectBtn = this._makeBtn(ICONS$2.selectCells, "Select Cells", () => this._toggleSelectMode());
|
|
5966
|
+
el.appendChild(this._selectBtn);
|
|
5967
|
+
el.appendChild(this._sep());
|
|
5764
5968
|
el.appendChild(this._makeBtn(ICONS$2.rowAbove, "Add Row Above", () => this._addRow("above")));
|
|
5765
5969
|
el.appendChild(this._makeBtn(ICONS$2.rowBelow, "Add Row Below", () => this._addRow("below")));
|
|
5766
5970
|
el.appendChild(this._makeBtn(ICONS$2.deleteRow, "Delete Row", () => this._deleteRow()));
|
|
@@ -5770,6 +5974,7 @@
|
|
|
5770
5974
|
el.appendChild(this._makeBtn(ICONS$2.deleteCol, "Delete Column", () => this._deleteColumn()));
|
|
5771
5975
|
el.appendChild(this._sep());
|
|
5772
5976
|
el.appendChild(this._makeBtn(ICONS$2.mergeCells, "Merge Cells", () => this._mergeCells()));
|
|
5977
|
+
el.appendChild(this._makeBtn(ICONS$2.unmergeCells, "Unmerge Cells", () => this._unmergeCells()));
|
|
5773
5978
|
el.appendChild(this._sep());
|
|
5774
5979
|
el.appendChild(this._makeBtn(ICONS$2.colWidth, "Column Width", () => this._openSizePopover("col")));
|
|
5775
5980
|
el.appendChild(this._makeBtn(ICONS$2.rowHeight, "Row Height", () => this._openSizePopover("row")));
|
|
@@ -5777,6 +5982,7 @@
|
|
|
5777
5982
|
el.appendChild(this._sep());
|
|
5778
5983
|
el.appendChild(this._makeBtn(ICONS$2.deleteTable, "Delete Table", () => this._deleteTable(), true));
|
|
5779
5984
|
this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
|
|
5985
|
+
if (this._selectMode) return;
|
|
5780
5986
|
if (this._sizePopover && this._sizePopover.style.display !== "none") return;
|
|
5781
5987
|
this._scheduleHide();
|
|
5782
5988
|
}));
|
|
@@ -5832,6 +6038,12 @@
|
|
|
5832
6038
|
this._el.style.display = "none";
|
|
5833
6039
|
this._activeTable = null;
|
|
5834
6040
|
this._activeCell = null;
|
|
6041
|
+
if (this._selectMode) {
|
|
6042
|
+
this._selectMode = false;
|
|
6043
|
+
if (this._selectBtn) this._selectBtn.classList.remove("an-link-tooltip-btn--active");
|
|
6044
|
+
if (this._editable) this._editable.classList.remove("an-table-select-mode");
|
|
6045
|
+
}
|
|
6046
|
+
this._clearSelection();
|
|
5835
6047
|
this._clearTimers();
|
|
5836
6048
|
this._hideSizePopover();
|
|
5837
6049
|
}
|
|
@@ -5865,14 +6077,111 @@
|
|
|
5865
6077
|
}
|
|
5866
6078
|
return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
|
|
5867
6079
|
}
|
|
6080
|
+
_toggleSelectMode() {
|
|
6081
|
+
this._selectMode = !this._selectMode;
|
|
6082
|
+
if (this._selectBtn) this._selectBtn.classList.toggle("an-link-tooltip-btn--active", this._selectMode);
|
|
6083
|
+
if (this._editable) this._editable.classList.toggle("an-table-select-mode", this._selectMode);
|
|
6084
|
+
if (!this._selectMode) this._clearSelection();
|
|
6085
|
+
}
|
|
6086
|
+
_clearSelection() {
|
|
6087
|
+
this._selectedCells.forEach((c) => c.classList.remove("an-cell-selected"));
|
|
6088
|
+
this._selectedCells = [];
|
|
6089
|
+
this._selectStart = null;
|
|
6090
|
+
}
|
|
6091
|
+
_setSelection(cells) {
|
|
6092
|
+
this._selectedCells.forEach((c) => {
|
|
6093
|
+
if (!cells.includes(c)) c.classList.remove("an-cell-selected");
|
|
6094
|
+
});
|
|
6095
|
+
this._selectedCells = cells;
|
|
6096
|
+
cells.forEach((c) => c.classList.add("an-cell-selected"));
|
|
6097
|
+
}
|
|
6098
|
+
/**
|
|
6099
|
+
* Returns all cells in the rectangular area between startCell and endCell,
|
|
6100
|
+
* correctly handling rowspan/colspan by using the grid map.
|
|
6101
|
+
* The rect is expanded iteratively until it is stable — this ensures any
|
|
6102
|
+
* merged cell that starts outside the initial rect but spans into it is
|
|
6103
|
+
* fully included.
|
|
6104
|
+
*/
|
|
6105
|
+
_getRectCells(startCell, endCell) {
|
|
6106
|
+
if (!startCell) return [];
|
|
6107
|
+
if (!endCell || startCell === endCell) return [startCell];
|
|
6108
|
+
const table = startCell.closest("table");
|
|
6109
|
+
if (!table || !table.contains(endCell)) return [startCell];
|
|
6110
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
6111
|
+
const sp = cellPos.get(startCell);
|
|
6112
|
+
const ep = cellPos.get(endCell);
|
|
6113
|
+
if (!sp || !ep) return [startCell];
|
|
6114
|
+
let minR = Math.min(sp.r, ep.r);
|
|
6115
|
+
let maxR = Math.max(sp.r + sp.rs - 1, ep.r + ep.rs - 1);
|
|
6116
|
+
let minC = Math.min(sp.c, ep.c);
|
|
6117
|
+
let maxC = Math.max(sp.c + sp.cs - 1, ep.c + ep.cs - 1);
|
|
6118
|
+
let changed = true;
|
|
6119
|
+
while (changed) {
|
|
6120
|
+
changed = false;
|
|
6121
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6122
|
+
const rowMap = gridMap[r];
|
|
6123
|
+
if (!rowMap) continue;
|
|
6124
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6125
|
+
const cell = rowMap[c];
|
|
6126
|
+
if (!cell) continue;
|
|
6127
|
+
const pos = cellPos.get(cell);
|
|
6128
|
+
if (!pos) continue;
|
|
6129
|
+
if (pos.r < minR) {
|
|
6130
|
+
minR = pos.r;
|
|
6131
|
+
changed = true;
|
|
6132
|
+
}
|
|
6133
|
+
if (pos.r + pos.rs - 1 > maxR) {
|
|
6134
|
+
maxR = pos.r + pos.rs - 1;
|
|
6135
|
+
changed = true;
|
|
6136
|
+
}
|
|
6137
|
+
if (pos.c < minC) {
|
|
6138
|
+
minC = pos.c;
|
|
6139
|
+
changed = true;
|
|
6140
|
+
}
|
|
6141
|
+
if (pos.c + pos.cs - 1 > maxC) {
|
|
6142
|
+
maxC = pos.c + pos.cs - 1;
|
|
6143
|
+
changed = true;
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6149
|
+
const result = [];
|
|
6150
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6151
|
+
const rowMap = gridMap[r];
|
|
6152
|
+
if (!rowMap) continue;
|
|
6153
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6154
|
+
const cell = rowMap[c];
|
|
6155
|
+
if (cell && !seen.has(cell)) {
|
|
6156
|
+
seen.add(cell);
|
|
6157
|
+
result.push(cell);
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
return result.length > 0 ? result : [startCell];
|
|
6162
|
+
}
|
|
6163
|
+
/**
|
|
6164
|
+
* Returns the active cell set: user-selected cells when available,
|
|
6165
|
+
* otherwise the single active/cursor cell.
|
|
6166
|
+
* @returns {HTMLTableCellElement[]}
|
|
6167
|
+
*/
|
|
6168
|
+
_getSelectedCells() {
|
|
6169
|
+
return this._selectedCells.length > 0 ? this._selectedCells : [this._getCell()].filter(Boolean);
|
|
6170
|
+
}
|
|
5868
6171
|
_addRow(position) {
|
|
5869
|
-
const
|
|
5870
|
-
if (!
|
|
5871
|
-
const
|
|
5872
|
-
if (!
|
|
5873
|
-
const
|
|
6172
|
+
const cells = this._getSelectedCells();
|
|
6173
|
+
if (!cells.length) return;
|
|
6174
|
+
const table = cells[0].closest("table");
|
|
6175
|
+
if (!table) return;
|
|
6176
|
+
const allRows = Array.from(table.querySelectorAll("tr"));
|
|
6177
|
+
const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
|
|
6178
|
+
const bi = allRows.indexOf(best);
|
|
6179
|
+
const ri = allRows.indexOf(r);
|
|
6180
|
+
return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
|
|
6181
|
+
});
|
|
6182
|
+
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
5874
6183
|
const newRow = document.createElement("tr");
|
|
5875
|
-
const refCells = Array.from(
|
|
6184
|
+
const refCells = Array.from(refRow.cells);
|
|
5876
6185
|
for (let i = 0; i < colCount; i++) {
|
|
5877
6186
|
const td = createElement("td", {}, ["\xA0"]);
|
|
5878
6187
|
const ref = refCells[i];
|
|
@@ -5880,19 +6189,20 @@
|
|
|
5880
6189
|
if (ref && ref.style.minWidth) td.style.minWidth = ref.style.minWidth;
|
|
5881
6190
|
newRow.appendChild(td);
|
|
5882
6191
|
}
|
|
5883
|
-
if (position === "above")
|
|
5884
|
-
else
|
|
6192
|
+
if (position === "above") refRow.parentElement?.insertBefore(newRow, refRow);
|
|
6193
|
+
else refRow.insertAdjacentElement("afterend", newRow);
|
|
5885
6194
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
5886
6195
|
this.context.invoke("editor.afterCommand");
|
|
5887
6196
|
}
|
|
5888
6197
|
_addColumn(position) {
|
|
5889
|
-
const
|
|
5890
|
-
if (!
|
|
5891
|
-
const table =
|
|
6198
|
+
const cells = this._getSelectedCells();
|
|
6199
|
+
if (!cells.length) return;
|
|
6200
|
+
const table = cells[0].closest("table");
|
|
5892
6201
|
if (!table) return;
|
|
5893
|
-
const
|
|
6202
|
+
const colIndices = cells.map((c) => getVisualColIndex(c));
|
|
6203
|
+
const targetColIdx = position === "left" ? Math.min(...colIndices) : Math.max(...colIndices);
|
|
5894
6204
|
const rows = Array.from(table.querySelectorAll("tr"));
|
|
5895
|
-
const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r,
|
|
6205
|
+
const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, targetColIdx) : getCellAfterVisualCol(r, targetColIdx));
|
|
5896
6206
|
const isHeaders = rows.map((r) => r.closest("thead") !== null);
|
|
5897
6207
|
rows.forEach((r, i) => {
|
|
5898
6208
|
r.insertBefore(createElement(isHeaders[i] ? "th" : "td", {}, ["\xA0"]), refs[i]);
|
|
@@ -5901,68 +6211,93 @@
|
|
|
5901
6211
|
this.context.invoke("editor.afterCommand");
|
|
5902
6212
|
}
|
|
5903
6213
|
_deleteRow() {
|
|
5904
|
-
const
|
|
5905
|
-
if (!
|
|
5906
|
-
const
|
|
5907
|
-
|
|
5908
|
-
if (!row || !table) return;
|
|
6214
|
+
const cells = this._getSelectedCells();
|
|
6215
|
+
if (!cells.length) return;
|
|
6216
|
+
const table = cells[0].closest("table");
|
|
6217
|
+
if (!table) return;
|
|
5909
6218
|
const tbody = table.querySelector("tbody");
|
|
5910
|
-
|
|
6219
|
+
const totalBodyRows = tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length;
|
|
6220
|
+
const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
|
|
6221
|
+
if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
|
|
5911
6222
|
this._activeCell = null;
|
|
5912
|
-
|
|
6223
|
+
this._clearSelection();
|
|
6224
|
+
selectedRows.forEach((r) => r.parentElement?.removeChild(r));
|
|
5913
6225
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
5914
6226
|
this.context.invoke("editor.afterCommand");
|
|
5915
6227
|
}
|
|
5916
6228
|
_deleteColumn() {
|
|
5917
|
-
const
|
|
5918
|
-
if (!
|
|
5919
|
-
const table =
|
|
6229
|
+
const cells = this._getSelectedCells();
|
|
6230
|
+
if (!cells.length) return;
|
|
6231
|
+
const table = cells[0].closest("table");
|
|
5920
6232
|
if (!table) return;
|
|
5921
|
-
const
|
|
5922
|
-
if (
|
|
5923
|
-
const
|
|
5924
|
-
|
|
5925
|
-
const
|
|
5926
|
-
|
|
5927
|
-
|
|
6233
|
+
const tableRows = Array.from(table.querySelectorAll("tr"));
|
|
6234
|
+
if (tableRows[0] && tableRows[0].cells.length <= 1) return;
|
|
6235
|
+
const colIndices = [...new Set(cells.map((c) => getVisualColIndex(c)))];
|
|
6236
|
+
if (colIndices.length >= (tableRows[0]?.cells.length ?? 1)) return;
|
|
6237
|
+
const cellsToDelete = [];
|
|
6238
|
+
colIndices.forEach((colIdx) => {
|
|
6239
|
+
tableRows.forEach((r) => {
|
|
6240
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
6241
|
+
if (c) cellsToDelete.push(c);
|
|
6242
|
+
});
|
|
5928
6243
|
});
|
|
6244
|
+
this._activeCell = null;
|
|
6245
|
+
this._clearSelection();
|
|
6246
|
+
cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
|
|
5929
6247
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
5930
6248
|
this.context.invoke("editor.afterCommand");
|
|
5931
6249
|
}
|
|
5932
6250
|
_mergeCells() {
|
|
5933
6251
|
const cell = this._getCell();
|
|
5934
6252
|
if (!cell) return;
|
|
5935
|
-
const sel = window.getSelection();
|
|
5936
|
-
if (!sel || sel.rangeCount === 0) return;
|
|
5937
|
-
const range = sel.getRangeAt(0);
|
|
5938
6253
|
const table = cell.closest("table");
|
|
5939
6254
|
if (!table) return;
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
const rowSelected = Array.from(row.cells).filter((c) => selected.includes(c));
|
|
5952
|
-
if (rowSelected.length < 2) return;
|
|
5953
|
-
const first = rowSelected[0];
|
|
5954
|
-
first.colSpan = rowSelected.reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
5955
|
-
first.innerHTML = rowSelected.map((c) => c.innerHTML).join("");
|
|
5956
|
-
rowSelected.slice(1).forEach((c) => row.removeChild(c));
|
|
5957
|
-
} else {
|
|
5958
|
-
if ([...new Set(selected.map((c) => getVisualColIndex(c)))].length !== 1) return;
|
|
5959
|
-
const first = selected[0];
|
|
5960
|
-
first.rowSpan = selected.reduce((sum, c) => sum + (c.rowSpan || 1), 0);
|
|
5961
|
-
first.innerHTML = selected.map((c) => c.innerHTML).join("");
|
|
5962
|
-
selected.slice(1).forEach((c) => {
|
|
5963
|
-
if (c.closest("tr")) c.closest("tr").removeChild(c);
|
|
6255
|
+
let selected = this._getSelectedCells().filter((c) => table.contains(c));
|
|
6256
|
+
if (selected.length < 2) {
|
|
6257
|
+
const sel = window.getSelection();
|
|
6258
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
6259
|
+
const range = sel.getRangeAt(0);
|
|
6260
|
+
selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
|
|
6261
|
+
try {
|
|
6262
|
+
return range.intersectsNode(c);
|
|
6263
|
+
} catch {
|
|
6264
|
+
return false;
|
|
6265
|
+
}
|
|
5964
6266
|
});
|
|
6267
|
+
if (selected.length < 2) return;
|
|
6268
|
+
}
|
|
6269
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
6270
|
+
let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
|
|
6271
|
+
selected.forEach((c) => {
|
|
6272
|
+
const pos = cellPos.get(c);
|
|
6273
|
+
if (!pos) return;
|
|
6274
|
+
if (pos.r < minR) minR = pos.r;
|
|
6275
|
+
if (pos.r + pos.rs - 1 > maxR) maxR = pos.r + pos.rs - 1;
|
|
6276
|
+
if (pos.c < minC) minC = pos.c;
|
|
6277
|
+
if (pos.c + pos.cs - 1 > maxC) maxC = pos.c + pos.cs - 1;
|
|
6278
|
+
});
|
|
6279
|
+
if (minR === Infinity) return;
|
|
6280
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6281
|
+
const rectCells = [];
|
|
6282
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6283
|
+
const rowMap = gridMap[r];
|
|
6284
|
+
if (!rowMap) continue;
|
|
6285
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6286
|
+
const tc = rowMap[c];
|
|
6287
|
+
if (tc && !seen.has(tc)) {
|
|
6288
|
+
seen.add(tc);
|
|
6289
|
+
rectCells.push(tc);
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
5965
6292
|
}
|
|
6293
|
+
if (rectCells.length < 2) return;
|
|
6294
|
+
const first = rectCells[0];
|
|
6295
|
+
first.colSpan = maxC - minC + 1;
|
|
6296
|
+
first.rowSpan = maxR - minR + 1;
|
|
6297
|
+
first.style.verticalAlign = "middle";
|
|
6298
|
+
first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
|
|
6299
|
+
rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
|
|
6300
|
+
this._clearSelection();
|
|
5966
6301
|
this.context.invoke("editor.afterCommand");
|
|
5967
6302
|
}
|
|
5968
6303
|
_deleteTable() {
|
|
@@ -5972,6 +6307,57 @@
|
|
|
5972
6307
|
if (table.parentNode) table.parentNode.removeChild(table);
|
|
5973
6308
|
this.context.invoke("editor.afterCommand");
|
|
5974
6309
|
}
|
|
6310
|
+
_unmergeCells() {
|
|
6311
|
+
const cells = this._getSelectedCells();
|
|
6312
|
+
if (!cells.length) return;
|
|
6313
|
+
const table = cells[0].closest("table");
|
|
6314
|
+
if (!table) return;
|
|
6315
|
+
const mergedCells = cells.filter((c) => table.contains(c) && ((c.colSpan || 1) > 1 || (c.rowSpan || 1) > 1));
|
|
6316
|
+
if (!mergedCells.length) return;
|
|
6317
|
+
mergedCells.forEach((cell) => {
|
|
6318
|
+
if (table.contains(cell)) this._unmergeOne(cell, table);
|
|
6319
|
+
});
|
|
6320
|
+
this._clearSelection();
|
|
6321
|
+
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
6322
|
+
this.context.invoke("editor.afterCommand");
|
|
6323
|
+
}
|
|
6324
|
+
/**
|
|
6325
|
+
* Split a single merged cell (colspan/rowspan > 1) back into individual cells.
|
|
6326
|
+
* New cells are empty ( ); the original cell retains its content.
|
|
6327
|
+
* @param {HTMLTableCellElement} cell
|
|
6328
|
+
* @param {HTMLTableElement} table
|
|
6329
|
+
*/
|
|
6330
|
+
_unmergeOne(cell, table) {
|
|
6331
|
+
const cs = cell.colSpan || 1;
|
|
6332
|
+
const rs = cell.rowSpan || 1;
|
|
6333
|
+
if (cs === 1 && rs === 1) return;
|
|
6334
|
+
const { cellPos } = buildGridMap(table);
|
|
6335
|
+
const pos = cellPos.get(cell);
|
|
6336
|
+
if (!pos) return;
|
|
6337
|
+
const { r, c } = pos;
|
|
6338
|
+
const tableRows = Array.from(table.rows);
|
|
6339
|
+
const tag = cell.tagName.toLowerCase();
|
|
6340
|
+
cell.rowSpan = 1;
|
|
6341
|
+
cell.colSpan = 1;
|
|
6342
|
+
cell.style.verticalAlign = "";
|
|
6343
|
+
if (cs > 1) {
|
|
6344
|
+
const insertRef = cell.nextElementSibling;
|
|
6345
|
+
for (let dc = 1; dc < cs; dc++) tableRows[r].insertBefore(createElement(tag, {}, ["\xA0"]), insertRef);
|
|
6346
|
+
}
|
|
6347
|
+
for (let dr = 1; dr < rs; dr++) {
|
|
6348
|
+
const targetRow = tableRows[r + dr];
|
|
6349
|
+
if (!targetRow) continue;
|
|
6350
|
+
let ref = null;
|
|
6351
|
+
for (const tc of targetRow.cells) {
|
|
6352
|
+
const tp = cellPos.get(tc);
|
|
6353
|
+
if (tp && tp.c > c) {
|
|
6354
|
+
ref = tc;
|
|
6355
|
+
break;
|
|
6356
|
+
}
|
|
6357
|
+
}
|
|
6358
|
+
for (let dc = 0; dc < cs; dc++) targetRow.insertBefore(createElement(tag, {}, ["\xA0"]), ref);
|
|
6359
|
+
}
|
|
6360
|
+
}
|
|
5975
6361
|
_buildSizePopover() {
|
|
5976
6362
|
const popover = createElement("div", { class: "an-size-popover" });
|
|
5977
6363
|
popover.style.display = "none";
|
|
@@ -6053,27 +6439,35 @@
|
|
|
6053
6439
|
};
|
|
6054
6440
|
} else {
|
|
6055
6441
|
const isCol = type === "col";
|
|
6442
|
+
const activeCells = this._getSelectedCells().filter((c) => {
|
|
6443
|
+
const t = c.closest("table");
|
|
6444
|
+
return t && t === cell.closest("table");
|
|
6445
|
+
});
|
|
6056
6446
|
this._sizeTitleEl.textContent = isCol ? "Column Width (px)" : "Row Height (px)";
|
|
6057
6447
|
this._sizeInputEl.min = "1";
|
|
6058
6448
|
this._sizeInputEl.max = "2000";
|
|
6059
6449
|
this._sizeInputEl.value = isCol ? cell.offsetWidth || 120 : cell.closest("tr") ? cell.closest("tr").offsetHeight || 40 : 40;
|
|
6060
6450
|
this._sizeApply = (val) => {
|
|
6451
|
+
const table = cell.closest("table");
|
|
6452
|
+
if (!table) return;
|
|
6061
6453
|
if (isCol) {
|
|
6062
|
-
const
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
c
|
|
6067
|
-
c.
|
|
6068
|
-
|
|
6454
|
+
const colIndices = [...new Set(activeCells.map((c) => getVisualColIndex(c)))];
|
|
6455
|
+
const tableRows = Array.from(table.querySelectorAll("tr"));
|
|
6456
|
+
colIndices.forEach((colIdx) => {
|
|
6457
|
+
tableRows.forEach((r) => {
|
|
6458
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
6459
|
+
if (c && (c.colSpan || 1) === 1) {
|
|
6460
|
+
c.style.width = `${val}px`;
|
|
6461
|
+
c.style.minWidth = `${val}px`;
|
|
6462
|
+
}
|
|
6463
|
+
});
|
|
6069
6464
|
});
|
|
6070
|
-
} else {
|
|
6071
|
-
const
|
|
6072
|
-
if (row) for (const c of row.cells) {
|
|
6465
|
+
} else [...new Set(activeCells.map((c) => c.closest("tr")).filter(Boolean))].forEach((row) => {
|
|
6466
|
+
for (const c of row.cells) {
|
|
6073
6467
|
c.style.height = `${val}px`;
|
|
6074
6468
|
c.style.minHeight = `${val}px`;
|
|
6075
6469
|
}
|
|
6076
|
-
}
|
|
6470
|
+
});
|
|
6077
6471
|
this.context.invoke("editor.afterCommand");
|
|
6078
6472
|
};
|
|
6079
6473
|
}
|
|
@@ -8928,7 +9322,13 @@
|
|
|
8928
9322
|
range.selectNodeContents(editable);
|
|
8929
9323
|
range.collapse(false);
|
|
8930
9324
|
}
|
|
9325
|
+
const _sc = range.startContainer;
|
|
9326
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
|
|
8931
9327
|
range.deleteContents();
|
|
9328
|
+
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
9329
|
+
range.setStart(_tdAnchor, 0);
|
|
9330
|
+
range.collapse(true);
|
|
9331
|
+
}
|
|
8932
9332
|
const textNode = document.createTextNode(char);
|
|
8933
9333
|
range.insertNode(textNode);
|
|
8934
9334
|
range.setStartAfter(textNode);
|
|
@@ -9497,7 +9897,13 @@
|
|
|
9497
9897
|
range.selectNodeContents(editable);
|
|
9498
9898
|
range.collapse(false);
|
|
9499
9899
|
}
|
|
9900
|
+
const _sc = range.startContainer;
|
|
9901
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
|
|
9500
9902
|
range.deleteContents();
|
|
9903
|
+
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
9904
|
+
range.setStart(_tdAnchor, 0);
|
|
9905
|
+
range.collapse(true);
|
|
9906
|
+
}
|
|
9501
9907
|
range.insertNode(iconEl);
|
|
9502
9908
|
let caretTextNode = iconEl.nextSibling;
|
|
9503
9909
|
if (!caretTextNode || caretTextNode.nodeType !== Node.TEXT_NODE) {
|