autumnnote 1.5.0 → 1.6.1
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 +10 -8
- package/dist/autumnnote.css +324 -4
- package/dist/autumnnote.es.js +943 -526
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +935 -519
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +21 -3
- package/src/js/Context.js +23 -16
- package/src/js/core/detectLang.js +98 -0
- package/src/js/core/dom.js +67 -11
- package/src/js/core/env.js +1 -1
- package/src/js/core/func.js +1 -1
- package/src/js/core/lists.js +1 -1
- package/src/js/core/markdown.js +32 -31
- package/src/js/core/range.js +8 -8
- package/src/js/editing/History.js +10 -10
- package/src/js/editing/Style.js +44 -44
- package/src/js/editing/Table.js +5 -7
- package/src/js/editing/Typing.js +19 -19
- package/src/js/i18n/en.js +2 -0
- package/src/js/i18n/vi.js +2 -0
- package/src/js/index.js +3 -2
- package/src/js/module/AutoSaveRestore.js +2 -4
- package/src/js/module/BubbleToolbar.js +51 -34
- package/src/js/module/Buttons.js +20 -18
- package/src/js/module/Clipboard.js +16 -17
- package/src/js/module/CodeTooltip.js +48 -24
- package/src/js/module/Codeview.js +5 -7
- package/src/js/module/ContextMenu.js +37 -37
- package/src/js/module/Editor.js +77 -26
- package/src/js/module/EmojiDialog.js +24 -18
- package/src/js/module/FindReplace.js +93 -66
- package/src/js/module/Fullscreen.js +1 -1
- package/src/js/module/IconDialog.js +39 -35
- package/src/js/module/ImageCropOverlay.js +13 -13
- package/src/js/module/ImageDialog.js +19 -13
- package/src/js/module/ImageResizer.js +5 -5
- package/src/js/module/ImageTooltip.js +20 -16
- package/src/js/module/LinkDialog.js +20 -14
- package/src/js/module/LinkTooltip.js +15 -12
- package/src/js/module/MarkdownShortcuts.js +11 -14
- package/src/js/module/Mention.js +11 -13
- package/src/js/module/Placeholder.js +1 -1
- package/src/js/module/ShortcutsDialog.js +2 -4
- package/src/js/module/Statusbar.js +5 -7
- package/src/js/module/TableTooltip.js +196 -36
- package/src/js/module/Toolbar.js +35 -34
- package/src/js/module/VideoDialog.js +16 -10
- package/src/js/module/VideoResizer.js +7 -9
- package/src/js/module/VideoTooltip.js +15 -10
- package/src/js/renderer.js +5 -3
- package/src/js/settings.js +53 -36
- package/src/styles/autumnnote.scss +332 -7
|
@@ -109,9 +109,7 @@ export class Statusbar {
|
|
|
109
109
|
this._dragDisposers.forEach((d) => d());
|
|
110
110
|
this._dragDisposers = null;
|
|
111
111
|
}
|
|
112
|
-
|
|
113
|
-
this.el.parentNode.removeChild(this.el);
|
|
114
|
-
}
|
|
112
|
+
this.el?.remove();
|
|
115
113
|
this.el = null;
|
|
116
114
|
}
|
|
117
115
|
|
|
@@ -136,7 +134,7 @@ export class Statusbar {
|
|
|
136
134
|
const MIN_EDITABLE = 40;
|
|
137
135
|
const fixedH = Array.from(containerEl.children)
|
|
138
136
|
.filter(child => !child.classList.contains('an-editable'))
|
|
139
|
-
.reduce((sum, child) => sum + child.offsetHeight, 0);
|
|
137
|
+
.reduce((sum, child) => sum + /** @type {HTMLElement} */ (child).offsetHeight, 0);
|
|
140
138
|
const trueMin = Math.max(this.options.minHeight || 100, fixedH + MIN_EDITABLE);
|
|
141
139
|
containerEl.style.height = `${Math.max(trueMin, startH + delta)}px`;
|
|
142
140
|
};
|
|
@@ -207,7 +205,7 @@ export class Statusbar {
|
|
|
207
205
|
_bindContentEvents() {
|
|
208
206
|
const editable = this.context.layoutInfo.editable;
|
|
209
207
|
const updateDebounced = debounce(() => this.update(), 200);
|
|
210
|
-
const d = on(editable, 'input', updateDebounced);
|
|
208
|
+
const d = on(editable, 'input', /** @type {EventListener} */ (updateDebounced));
|
|
211
209
|
this._disposers.push(d);
|
|
212
210
|
}
|
|
213
211
|
|
|
@@ -217,7 +215,7 @@ export class Statusbar {
|
|
|
217
215
|
// textContent is faster than innerText (no layout flush, no CSS visibility check)
|
|
218
216
|
const text = editable.textContent || '';
|
|
219
217
|
const words = _countWords(text);
|
|
220
|
-
const chars = text.
|
|
218
|
+
const chars = text.replaceAll('\n', '').length;
|
|
221
219
|
const maxWords = this.options.maxWords || 0;
|
|
222
220
|
const maxChars = this.options.maxChars || 0;
|
|
223
221
|
|
|
@@ -249,6 +247,6 @@ export class Statusbar {
|
|
|
249
247
|
*/
|
|
250
248
|
getCharCount() {
|
|
251
249
|
const editable = this.context.layoutInfo.editable;
|
|
252
|
-
return (
|
|
250
|
+
return (editable.innerText || '').replaceAll('\n', '').length;
|
|
253
251
|
}
|
|
254
252
|
}
|
|
@@ -58,7 +58,7 @@ function getCellAfterVisualCol(row, visualIdx) {
|
|
|
58
58
|
if (vIdx > visualIdx) {
|
|
59
59
|
// next cell after the one that starts at / spans visualIdx
|
|
60
60
|
const next = c.nextElementSibling;
|
|
61
|
-
return (next && next.tagName === 'TD' || next && next.tagName === 'TH') ? next : null;
|
|
61
|
+
return (next && next.tagName === 'TD' || next && next.tagName === 'TH') ? /** @type {HTMLTableCellElement} */ (next) : null;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
return null;
|
|
@@ -115,8 +115,15 @@ const ICONS = {
|
|
|
115
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>`,
|
|
116
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
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>`,
|
|
118
|
+
cellShade: `<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"><path d="M19 11L8.93 3.36a1 1 0 0 0-1.29.08L3.22 7.8a1 1 0 0 0-.07 1.29L11 20"/><path d="m5 14 5-5"/><path d="M22 22a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2c0-1.5 2.5-5 3.5-5s3.5 3.5 3.5 5z"/></svg>`,
|
|
118
119
|
};
|
|
119
120
|
|
|
121
|
+
const SHADE_PRESETS = [
|
|
122
|
+
'#000000', '#434343', '#666666', '#999999', '#b7b7b7', '#cccccc', '#efefef', '#ffffff',
|
|
123
|
+
'#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#4a86e8', '#9900ff', '#ff00ff',
|
|
124
|
+
'#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#c9daf8', '#d9d2e9', '#ead1dc',
|
|
125
|
+
];
|
|
126
|
+
|
|
120
127
|
export class TableTooltip {
|
|
121
128
|
/** @param {import('../Context.js').Context} context */
|
|
122
129
|
constructor(context) {
|
|
@@ -131,6 +138,10 @@ export class TableTooltip {
|
|
|
131
138
|
this._sizeApply = null;
|
|
132
139
|
this._sizeTitleEl = null;
|
|
133
140
|
this._sizeInputEl = null;
|
|
141
|
+
// Cell shade popover
|
|
142
|
+
this._shadePopover = null;
|
|
143
|
+
this._shadeTitleEl = null;
|
|
144
|
+
this._shadeColorStrip = null;
|
|
134
145
|
// Cell selection
|
|
135
146
|
this._selectMode = false;
|
|
136
147
|
this._selectedCells = [];
|
|
@@ -147,6 +158,9 @@ export class TableTooltip {
|
|
|
147
158
|
this._sizePopover = this._buildSizePopover();
|
|
148
159
|
document.body.appendChild(this._sizePopover);
|
|
149
160
|
|
|
161
|
+
this._shadePopover = this._buildCellShadePopover();
|
|
162
|
+
document.body.appendChild(this._shadePopover);
|
|
163
|
+
|
|
150
164
|
const editable = this.context.layoutInfo.editable;
|
|
151
165
|
this._editable = editable;
|
|
152
166
|
|
|
@@ -182,33 +196,42 @@ export class TableTooltip {
|
|
|
182
196
|
this._disposers.push(
|
|
183
197
|
on(editable, 'mouseover', (e) => {
|
|
184
198
|
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
185
|
-
const table = e.target
|
|
199
|
+
const table = /** @type {Element} */ (e.target)?.closest('table');
|
|
186
200
|
if (table && editable.contains(table)) {
|
|
187
|
-
const cell = e.target
|
|
188
|
-
if (cell)
|
|
201
|
+
const cell = /** @type {Element} */ (e.target)?.closest('td, th');
|
|
202
|
+
if (cell) {
|
|
203
|
+
this._activeCell = cell;
|
|
204
|
+
this._syncShadeStrip();
|
|
205
|
+
}
|
|
189
206
|
this._scheduleShow(table);
|
|
190
207
|
}
|
|
191
208
|
}, { passive: true }),
|
|
192
209
|
on(editable, 'mouseout', (e) => {
|
|
193
210
|
if (this._selectMode) return; // keep tooltip alive during cell selection
|
|
194
|
-
const to = e.relatedTarget;
|
|
211
|
+
const to = /** @type {Node|null} */ (/** @type {MouseEvent} */ (e).relatedTarget);
|
|
195
212
|
if (!to || (
|
|
196
213
|
!editable.contains(to) &&
|
|
197
214
|
!this._el.contains(to) &&
|
|
198
|
-
!
|
|
215
|
+
!this._sizePopover?.contains(to)
|
|
199
216
|
)) {
|
|
200
217
|
this._scheduleHide();
|
|
201
218
|
}
|
|
202
219
|
}, { passive: true }),
|
|
203
220
|
on(document, 'click', (e) => {
|
|
204
|
-
|
|
221
|
+
const et = /** @type {Node} */ (e.target);
|
|
222
|
+
if (this._selectMode && this._activeTable?.contains(et)) return;
|
|
205
223
|
if (this._activeTable &&
|
|
206
|
-
!this._activeTable.contains(
|
|
207
|
-
!this._el.contains(
|
|
208
|
-
!
|
|
224
|
+
!this._activeTable.contains(et) &&
|
|
225
|
+
!this._el.contains(et) &&
|
|
226
|
+
!this._sizePopover?.contains(et)) {
|
|
209
227
|
this._hide();
|
|
210
228
|
}
|
|
211
229
|
}),
|
|
230
|
+
// Sync shade strip whenever selection moves to a different cell
|
|
231
|
+
on(document, 'selectionchange', () => this._syncShadeStrip()),
|
|
232
|
+
// Hide when the page scrolls or resizes — the tooltip position becomes stale
|
|
233
|
+
on(globalThis, 'scroll', () => this._hide(), { passive: true }),
|
|
234
|
+
on(globalThis, 'resize', () => this._hide(), { passive: true }),
|
|
212
235
|
);
|
|
213
236
|
|
|
214
237
|
this._initResize();
|
|
@@ -363,12 +386,16 @@ export class TableTooltip {
|
|
|
363
386
|
this._clearTimers();
|
|
364
387
|
this._disposers.forEach((d) => d());
|
|
365
388
|
this._disposers = [];
|
|
366
|
-
if (this._el && this._el.parentNode) this._el.
|
|
389
|
+
if (this._el && this._el.parentNode) this._el.remove();
|
|
367
390
|
this._el = null;
|
|
368
391
|
if (this._sizePopover && this._sizePopover.parentNode) {
|
|
369
|
-
this._sizePopover.
|
|
392
|
+
this._sizePopover.remove();
|
|
370
393
|
}
|
|
371
394
|
this._sizePopover = null;
|
|
395
|
+
if (this._shadePopover && this._shadePopover.parentNode) {
|
|
396
|
+
this._shadePopover.remove();
|
|
397
|
+
}
|
|
398
|
+
this._shadePopover = null;
|
|
372
399
|
}
|
|
373
400
|
|
|
374
401
|
// ---------------------------------------------------------------------------
|
|
@@ -417,6 +444,27 @@ export class TableTooltip {
|
|
|
417
444
|
|
|
418
445
|
el.appendChild(this._sep());
|
|
419
446
|
|
|
447
|
+
// Cell background shading — uses color-strip variant like foreColor/hiliteColor buttons
|
|
448
|
+
const shadeBtn = createElement('button', {
|
|
449
|
+
type: 'button',
|
|
450
|
+
class: 'an-link-tooltip-btn an-link-tooltip-btn--shade',
|
|
451
|
+
title: L.cellBackground,
|
|
452
|
+
});
|
|
453
|
+
const shadeSvgWrap = createElement('span', { class: 'an-bubble-btn-svg' });
|
|
454
|
+
shadeSvgWrap.innerHTML = ICONS.cellShade;
|
|
455
|
+
const shadeStrip = createElement('span', { class: 'an-link-tooltip-color-strip' });
|
|
456
|
+
shadeBtn.appendChild(shadeSvgWrap);
|
|
457
|
+
shadeBtn.appendChild(shadeStrip);
|
|
458
|
+
this._shadeColorStrip = shadeStrip;
|
|
459
|
+
this._disposers.push(on(shadeBtn, 'click', (e) => {
|
|
460
|
+
e.preventDefault();
|
|
461
|
+
e.stopPropagation();
|
|
462
|
+
this._openCellShadePopover();
|
|
463
|
+
}));
|
|
464
|
+
el.appendChild(shadeBtn);
|
|
465
|
+
|
|
466
|
+
el.appendChild(this._sep());
|
|
467
|
+
|
|
420
468
|
// Resize
|
|
421
469
|
el.appendChild(this._makeBtn(ICONS.colWidth, L.columnWidth, () => this._openSizePopover('col')));
|
|
422
470
|
el.appendChild(this._makeBtn(ICONS.rowHeight, L.rowHeight, () => this._openSizePopover('row')));
|
|
@@ -434,7 +482,8 @@ export class TableTooltip {
|
|
|
434
482
|
on(el, 'mouseenter', () => this._clearTimers()),
|
|
435
483
|
on(el, 'mouseleave', () => {
|
|
436
484
|
if (this._selectMode) return; // keep tooltip alive during cell selection
|
|
437
|
-
if (this._sizePopover
|
|
485
|
+
if (this._sizePopover && this._sizePopover.style.display !== 'none') return;
|
|
486
|
+
if (this._shadePopover && this._shadePopover.style.display !== 'none') return;
|
|
438
487
|
this._scheduleHide();
|
|
439
488
|
}),
|
|
440
489
|
);
|
|
@@ -494,12 +543,19 @@ export class TableTooltip {
|
|
|
494
543
|
_show() {
|
|
495
544
|
if (!this._activeTable) return;
|
|
496
545
|
this._el.style.display = 'flex';
|
|
546
|
+
this._syncShadeStrip();
|
|
497
547
|
// Defer: offsetWidth on a newly-visible element forces layout synchronously
|
|
498
548
|
requestAnimationFrame(() => {
|
|
499
549
|
if (this._activeTable) this._positionNear(this._activeTable);
|
|
500
550
|
});
|
|
501
551
|
}
|
|
502
552
|
|
|
553
|
+
_syncShadeStrip() {
|
|
554
|
+
if (!this._shadeColorStrip || !this._el || this._el.style.display === 'none') return;
|
|
555
|
+
const cell = this._getCell();
|
|
556
|
+
this._shadeColorStrip.style.background = cell?.style.backgroundColor || 'transparent';
|
|
557
|
+
}
|
|
558
|
+
|
|
503
559
|
_hide() {
|
|
504
560
|
this._el.style.display = 'none';
|
|
505
561
|
this._activeTable = null;
|
|
@@ -534,7 +590,7 @@ export class TableTooltip {
|
|
|
534
590
|
let top = rect.top - tipH - margin;
|
|
535
591
|
|
|
536
592
|
if (top < margin) top = rect.bottom + margin;
|
|
537
|
-
if (left + tipW >
|
|
593
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
538
594
|
if (left < margin) left = margin;
|
|
539
595
|
|
|
540
596
|
this._el.style.left = `${left}px`;
|
|
@@ -547,17 +603,17 @@ export class TableTooltip {
|
|
|
547
603
|
|
|
548
604
|
_getCell() {
|
|
549
605
|
// Prefer the cell under the current text cursor (most intuitive for operations)
|
|
550
|
-
const sel =
|
|
606
|
+
const sel = globalThis.getSelection();
|
|
551
607
|
if (sel && sel.rangeCount) {
|
|
552
608
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
553
609
|
if (container.nodeType === 3) container = container.parentElement;
|
|
554
|
-
const cellFromSel =
|
|
555
|
-
if (cellFromSel && this._activeTable
|
|
610
|
+
const cellFromSel = /** @type {Element} */ (container)?.closest('td, th');
|
|
611
|
+
if (cellFromSel && this._activeTable?.contains(cellFromSel)) {
|
|
556
612
|
return cellFromSel;
|
|
557
613
|
}
|
|
558
614
|
}
|
|
559
615
|
return this._activeCell
|
|
560
|
-
||
|
|
616
|
+
|| this._activeTable?.querySelector('td, th');
|
|
561
617
|
}
|
|
562
618
|
|
|
563
619
|
// ---------------------------------------------------------------------------
|
|
@@ -679,7 +735,7 @@ export class TableTooltip {
|
|
|
679
735
|
const bi = allRows.indexOf(best);
|
|
680
736
|
const ri = allRows.indexOf(r);
|
|
681
737
|
return position === 'above' ? (ri < bi ? r : best) : (ri > bi ? r : best);
|
|
682
|
-
});
|
|
738
|
+
}, selectedRows[0]);
|
|
683
739
|
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
684
740
|
const newRow = document.createElement('tr');
|
|
685
741
|
const refCells = Array.from(refRow.cells);
|
|
@@ -730,7 +786,7 @@ export class TableTooltip {
|
|
|
730
786
|
if (bodyRowsToDelete.length >= totalBodyRows) return;
|
|
731
787
|
this._activeCell = null;
|
|
732
788
|
this._clearSelection();
|
|
733
|
-
selectedRows.forEach((r) => r.
|
|
789
|
+
selectedRows.forEach((r) => r.remove());
|
|
734
790
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
735
791
|
this.context.invoke('editor.afterCommand');
|
|
736
792
|
}
|
|
@@ -754,7 +810,7 @@ export class TableTooltip {
|
|
|
754
810
|
});
|
|
755
811
|
this._activeCell = null;
|
|
756
812
|
this._clearSelection();
|
|
757
|
-
cellsToDelete.forEach((c) => c.
|
|
813
|
+
cellsToDelete.forEach((c) => c.remove());
|
|
758
814
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
759
815
|
this.context.invoke('editor.afterCommand');
|
|
760
816
|
}
|
|
@@ -768,7 +824,7 @@ export class TableTooltip {
|
|
|
768
824
|
// Prefer user panel-selected cells; fall back to text-selection range
|
|
769
825
|
let selected = this._getSelectedCells().filter((c) => table.contains(c));
|
|
770
826
|
if (selected.length < 2) {
|
|
771
|
-
const sel =
|
|
827
|
+
const sel = globalThis.getSelection();
|
|
772
828
|
if (!sel || sel.rangeCount === 0) return;
|
|
773
829
|
const range = sel.getRangeAt(0);
|
|
774
830
|
const allCells = Array.from(table.querySelectorAll('td, th'));
|
|
@@ -810,7 +866,7 @@ export class TableTooltip {
|
|
|
810
866
|
first.rowSpan = maxR - minR + 1;
|
|
811
867
|
first.style.verticalAlign = 'middle';
|
|
812
868
|
first.innerHTML = rectCells.map((c) => c.innerHTML).join('');
|
|
813
|
-
rectCells.slice(1).forEach((c) => c.
|
|
869
|
+
rectCells.slice(1).forEach((c) => c.remove());
|
|
814
870
|
|
|
815
871
|
this._clearSelection();
|
|
816
872
|
this.context.invoke('editor.afterCommand');
|
|
@@ -820,7 +876,7 @@ export class TableTooltip {
|
|
|
820
876
|
const table = this._activeTable;
|
|
821
877
|
if (!table) return;
|
|
822
878
|
this._hide();
|
|
823
|
-
if (table.parentNode) table.
|
|
879
|
+
if (table.parentNode) table.remove();
|
|
824
880
|
this.context.invoke('editor.afterCommand');
|
|
825
881
|
}
|
|
826
882
|
|
|
@@ -901,9 +957,9 @@ export class TableTooltip {
|
|
|
901
957
|
|
|
902
958
|
const titleEl = createElement('div', { class: 'an-size-popover-title' });
|
|
903
959
|
const body = createElement('div', { class: 'an-size-popover-body' });
|
|
904
|
-
const inputEl = createElement('input', {
|
|
960
|
+
const inputEl = /** @type {HTMLInputElement} */ (createElement('input', {
|
|
905
961
|
type: 'number', class: 'an-size-input', min: '1', max: '2000', step: '1',
|
|
906
|
-
});
|
|
962
|
+
}));
|
|
907
963
|
const unitEl = createElement('span', { class: 'an-size-unit' }, ['px']);
|
|
908
964
|
body.appendChild(inputEl);
|
|
909
965
|
body.appendChild(unitEl);
|
|
@@ -925,20 +981,22 @@ export class TableTooltip {
|
|
|
925
981
|
this._sizeApply = null;
|
|
926
982
|
|
|
927
983
|
const d1 = on(applyBtn, 'click', () => {
|
|
928
|
-
const val = parseInt(this._sizeInputEl.value, 10);
|
|
984
|
+
const val = Number.parseInt(this._sizeInputEl.value, 10);
|
|
929
985
|
if (val > 0 && typeof this._sizeApply === 'function') this._sizeApply(val);
|
|
930
986
|
this._hideSizePopover();
|
|
931
987
|
});
|
|
932
988
|
const d2 = on(cancelBtn, 'click', () => this._hideSizePopover());
|
|
933
989
|
const d3 = on(inputEl, 'keydown', (e) => {
|
|
934
|
-
|
|
935
|
-
if (
|
|
990
|
+
const ke = /** @type {KeyboardEvent} */ (e);
|
|
991
|
+
if (ke.key === 'Enter') { e.preventDefault(); applyBtn.click(); }
|
|
992
|
+
if (ke.key === 'Escape') this._hideSizePopover();
|
|
936
993
|
});
|
|
937
994
|
const d4 = on(document, 'click', (e) => {
|
|
995
|
+
const et = /** @type {Node} */ (e.target);
|
|
938
996
|
if (this._sizePopover &&
|
|
939
997
|
this._sizePopover.style.display !== 'none' &&
|
|
940
|
-
!this._sizePopover.contains(
|
|
941
|
-
!this._el.contains(
|
|
998
|
+
!this._sizePopover.contains(et) &&
|
|
999
|
+
!this._el.contains(et)) {
|
|
942
1000
|
this._hideSizePopover();
|
|
943
1001
|
}
|
|
944
1002
|
});
|
|
@@ -958,13 +1016,13 @@ export class TableTooltip {
|
|
|
958
1016
|
if (!table) return;
|
|
959
1017
|
const firstCell = table.querySelector('td, th');
|
|
960
1018
|
const currentPx = firstCell
|
|
961
|
-
? (parseInt(firstCell.style.borderWidth, 10) ||
|
|
962
|
-
parseInt(
|
|
1019
|
+
? (Number.parseInt(firstCell.style.borderWidth, 10) ||
|
|
1020
|
+
Number.parseInt(globalThis.getComputedStyle(firstCell).borderWidth, 10) || 1)
|
|
963
1021
|
: 1;
|
|
964
1022
|
this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
|
|
965
1023
|
this._sizeInputEl.min = '0';
|
|
966
1024
|
this._sizeInputEl.max = '10';
|
|
967
|
-
this._sizeInputEl.value = currentPx;
|
|
1025
|
+
this._sizeInputEl.value = String(currentPx);
|
|
968
1026
|
this._sizeApply = (val) => {
|
|
969
1027
|
const cells = Array.from(table.querySelectorAll('td, th'));
|
|
970
1028
|
if (val === 0) {
|
|
@@ -1021,8 +1079,8 @@ export class TableTooltip {
|
|
|
1021
1079
|
const ph = this._sizePopover.offsetHeight || 110;
|
|
1022
1080
|
let left = tipRect.left;
|
|
1023
1081
|
let top = tipRect.bottom + 6;
|
|
1024
|
-
if (left + pw >
|
|
1025
|
-
if (top + ph >
|
|
1082
|
+
if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
|
|
1083
|
+
if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
|
|
1026
1084
|
this._sizePopover.style.left = `${left}px`;
|
|
1027
1085
|
this._sizePopover.style.top = `${top}px`;
|
|
1028
1086
|
if (this._sizeInputEl) { this._sizeInputEl.focus(); this._sizeInputEl.select(); }
|
|
@@ -1033,4 +1091,106 @@ export class TableTooltip {
|
|
|
1033
1091
|
if (this._sizePopover) this._sizePopover.style.display = 'none';
|
|
1034
1092
|
this._sizeApply = null;
|
|
1035
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
// ---------------------------------------------------------------------------
|
|
1096
|
+
// Cell background shade popover
|
|
1097
|
+
// ---------------------------------------------------------------------------
|
|
1098
|
+
|
|
1099
|
+
_buildCellShadePopover() {
|
|
1100
|
+
const pop = createElement('div', { class: 'an-cell-shade-popover' });
|
|
1101
|
+
pop.style.display = 'none';
|
|
1102
|
+
|
|
1103
|
+
const title = createElement('div', { class: 'an-size-popover-title' });
|
|
1104
|
+
pop.appendChild(title);
|
|
1105
|
+
this._shadeTitleEl = title;
|
|
1106
|
+
|
|
1107
|
+
// 24-color palette — reuse existing CSS classes
|
|
1108
|
+
const palette = createElement('div', { class: 'an-context-color-palette' });
|
|
1109
|
+
SHADE_PRESETS.forEach((color) => {
|
|
1110
|
+
const sw = createElement('div', { class: 'an-context-color-swatch', title: color });
|
|
1111
|
+
sw.style.background = color;
|
|
1112
|
+
this._disposers.push(on(sw, 'click', (e) => {
|
|
1113
|
+
e.stopPropagation();
|
|
1114
|
+
this._applyCellShade(color);
|
|
1115
|
+
}));
|
|
1116
|
+
palette.appendChild(sw);
|
|
1117
|
+
});
|
|
1118
|
+
pop.appendChild(palette);
|
|
1119
|
+
|
|
1120
|
+
// "No shading" row — clears background color
|
|
1121
|
+
const noShadeRow = createElement('div', { class: 'an-context-color-custom' });
|
|
1122
|
+
const noShadeBtn = createElement('button', { type: 'button', class: 'an-shade-no-color' });
|
|
1123
|
+
this._disposers.push(on(noShadeBtn, 'click', () => this._applyCellShade('')));
|
|
1124
|
+
noShadeRow.appendChild(noShadeBtn);
|
|
1125
|
+
pop.appendChild(noShadeRow);
|
|
1126
|
+
this._shadeNoBtn = noShadeBtn;
|
|
1127
|
+
|
|
1128
|
+
// Custom color input
|
|
1129
|
+
const customRow = createElement('div', { class: 'an-context-color-custom' });
|
|
1130
|
+
const colorInput = /** @type {HTMLInputElement} */ (createElement('input', { type: 'color', class: 'an-shade-color-input', value: '#ffffff' }));
|
|
1131
|
+
const customLabel = createElement('span');
|
|
1132
|
+
customLabel.textContent = 'Custom…';
|
|
1133
|
+
this._disposers.push(on(colorInput, 'change', () => this._applyCellShade(colorInput.value)));
|
|
1134
|
+
customRow.appendChild(colorInput);
|
|
1135
|
+
customRow.appendChild(customLabel);
|
|
1136
|
+
pop.appendChild(customRow);
|
|
1137
|
+
|
|
1138
|
+
// Prevent mousedown from collapsing editor selection; keep tooltip alive while hovering
|
|
1139
|
+
this._disposers.push(
|
|
1140
|
+
on(pop, 'mousedown', (e) => e.preventDefault()),
|
|
1141
|
+
on(pop, 'mouseenter', () => this._clearTimers()),
|
|
1142
|
+
on(pop, 'mouseleave', () => this._scheduleHide()),
|
|
1143
|
+
);
|
|
1144
|
+
|
|
1145
|
+
// Close on outside click
|
|
1146
|
+
this._disposers.push(on(document, 'click', (e) => {
|
|
1147
|
+
const et = /** @type {Node} */ (e.target);
|
|
1148
|
+
if (this._shadePopover &&
|
|
1149
|
+
this._shadePopover.style.display !== 'none' &&
|
|
1150
|
+
!this._shadePopover.contains(et) &&
|
|
1151
|
+
!this._el?.contains(et)) {
|
|
1152
|
+
this._hideCellShadePopover();
|
|
1153
|
+
}
|
|
1154
|
+
}));
|
|
1155
|
+
|
|
1156
|
+
return pop;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
_openCellShadePopover() {
|
|
1160
|
+
if (!this._shadePopover) return;
|
|
1161
|
+
const L = this.context.locale.tooltips.table;
|
|
1162
|
+
if (this._shadeTitleEl) this._shadeTitleEl.textContent = L.cellBackground;
|
|
1163
|
+
if (this._shadeNoBtn) this._shadeNoBtn.textContent = L.noShading;
|
|
1164
|
+
|
|
1165
|
+
this._shadePopover.style.display = 'block';
|
|
1166
|
+
requestAnimationFrame(() => {
|
|
1167
|
+
if (!this._shadePopover || !this._el) return;
|
|
1168
|
+
const pw = this._shadePopover.offsetWidth || 170;
|
|
1169
|
+
const ph = this._shadePopover.offsetHeight || 120;
|
|
1170
|
+
const tipRect = this._el.getBoundingClientRect();
|
|
1171
|
+
let left = tipRect.left;
|
|
1172
|
+
let top = tipRect.bottom + 6;
|
|
1173
|
+
if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
|
|
1174
|
+
if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
|
|
1175
|
+
this._shadePopover.style.left = `${Math.max(8, left)}px`;
|
|
1176
|
+
this._shadePopover.style.top = `${Math.max(8, top)}px`;
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
_hideCellShadePopover() {
|
|
1181
|
+
if (this._shadePopover) this._shadePopover.style.display = 'none';
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
_applyCellShade(color) {
|
|
1185
|
+
const cells = this._selectMode ? this._selectedCells : [this._getCell()];
|
|
1186
|
+
cells.forEach((cell) => {
|
|
1187
|
+
if (cell) cell.style.backgroundColor = color;
|
|
1188
|
+
});
|
|
1189
|
+
// Update the color strip on the shade button to reflect the applied color
|
|
1190
|
+
if (this._shadeColorStrip) {
|
|
1191
|
+
this._shadeColorStrip.style.background = color || 'transparent';
|
|
1192
|
+
}
|
|
1193
|
+
this._hideCellShadePopover();
|
|
1194
|
+
this.context.invoke('editor.afterCommand');
|
|
1195
|
+
}
|
|
1036
1196
|
}
|