autumnnote 1.16.0 → 2.0.0
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 +31 -2
- package/dist/autumnnote.cjs +21 -21
- package/dist/autumnnote.es.js +1173 -6068
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.min.js +21 -21
- package/dist/autumnnote.umd.js +21 -21
- package/dist/autumnnote.umd.js.map +1 -1
- package/dist/emoji-data-BDQX5kh-.js +2331 -0
- package/dist/emoji-data-BDQX5kh-.js.map +1 -0
- package/package.json +5 -1
- package/src/js/Context.js +68 -8
- package/src/js/i18n/all.js +27 -0
- package/src/js/i18n/index.js +45 -19
- package/src/js/index.js +12 -2
- package/src/js/index.umd.js +5 -0
- package/src/js/module/BaseMediaTooltip.js +142 -0
- package/src/js/module/BaseResizer.js +312 -0
- package/src/js/module/EmojiDialog.js +41 -494
- package/src/js/module/ImageResizer.js +23 -241
- package/src/js/module/ImageTooltip.js +16 -111
- package/src/js/module/VideoResizer.js +38 -239
- package/src/js/module/VideoTooltip.js +27 -114
- package/src/js/module/emoji-data.js +496 -0
- package/types/i18n/all.d.ts +14 -0
- package/types/i18n/de.d.ts +4 -0
- package/types/i18n/en.d.ts +4 -0
- package/types/i18n/es.d.ts +4 -0
- package/types/i18n/fr.d.ts +4 -0
- package/types/i18n/ja.d.ts +4 -0
- package/types/i18n/ko.d.ts +4 -0
- package/types/i18n/vi.d.ts +4 -0
- package/types/i18n/zh.d.ts +4 -0
- package/types/index.d.ts +30 -4
|
@@ -1,84 +1,34 @@
|
|
|
1
1
|
// ImageResizer.js - Interactive resize handles for selected images in the editor
|
|
2
|
-
import {
|
|
2
|
+
import { BaseResizer } from './BaseResizer.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Pos length 2 → corner handle → maintain aspect ratio.
|
|
9
|
-
* Pos length 1 → edge handle → single-axis resize.
|
|
10
|
-
*/
|
|
11
|
-
const HANDLE_DEFS = [
|
|
12
|
-
{ pos: 'nw', cursor: 'nw-resize' },
|
|
13
|
-
{ pos: 'n', cursor: 'n-resize' },
|
|
14
|
-
{ pos: 'ne', cursor: 'ne-resize' },
|
|
15
|
-
{ pos: 'e', cursor: 'e-resize' },
|
|
16
|
-
{ pos: 'se', cursor: 'se-resize' },
|
|
17
|
-
{ pos: 's', cursor: 's-resize' },
|
|
18
|
-
{ pos: 'sw', cursor: 'sw-resize' },
|
|
19
|
-
{ pos: 'w', cursor: 'w-resize' },
|
|
20
|
-
];
|
|
4
|
+
export class ImageResizer extends BaseResizer {
|
|
5
|
+
get _overlayClass() { return 'an-image-resizer'; }
|
|
6
|
+
get _selectedClass() { return 'an-image-selected'; }
|
|
21
7
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this._activeImg = null;
|
|
28
|
-
this._overlay = null;
|
|
29
|
-
this._disposers = [];
|
|
30
|
-
this._positionRaf = null;
|
|
8
|
+
/** @param {EventTarget|null} el @returns {HTMLImageElement|null} */
|
|
9
|
+
_findTarget(el) {
|
|
10
|
+
return /** @type {HTMLImageElement|null} */ (
|
|
11
|
+
el instanceof Element ? el.closest('img') : null
|
|
12
|
+
);
|
|
31
13
|
}
|
|
32
14
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const editable = this.context.layoutInfo.editable;
|
|
40
|
-
|
|
41
|
-
// Debounce globalThis resize — _updateOverlayPosition already has rAF gating but
|
|
42
|
-
// every call cancels + re-schedules it; a debounce reduces that churn.
|
|
43
|
-
let _resizeDebounce = null;
|
|
44
|
-
const onWindowResize = () => {
|
|
45
|
-
clearTimeout(_resizeDebounce);
|
|
46
|
-
_resizeDebounce = setTimeout(() => this._updateOverlayPosition(), 100);
|
|
15
|
+
/** @param {HTMLImageElement} img */
|
|
16
|
+
_getStartSize(img) {
|
|
17
|
+
return {
|
|
18
|
+
w: img.offsetWidth || img.naturalWidth || 100,
|
|
19
|
+
h: img.offsetHeight || img.naturalHeight || 100,
|
|
47
20
|
};
|
|
21
|
+
}
|
|
48
22
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
on(editable, 'contextmenu', (e) => {
|
|
53
|
-
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
54
|
-
const img = /** @type {Element} */ (e.target)?.closest('img');
|
|
55
|
-
if (img) this._select(img);
|
|
56
|
-
}),
|
|
57
|
-
on(document, 'click', (e) => this._onDocClick(e)),
|
|
58
|
-
on(globalThis, 'scroll', () => this._updateOverlayPosition(), { passive: true }),
|
|
59
|
-
on(globalThis, 'resize', onWindowResize, { passive: true }),
|
|
60
|
-
on(editable, 'scroll', () => this._updateOverlayPosition(), { passive: true }),
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return this;
|
|
23
|
+
_getMinSize() {
|
|
24
|
+
const min = this.context.options?.minImageSize ?? 20;
|
|
25
|
+
return { minW: min, minH: min };
|
|
64
26
|
}
|
|
65
27
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this._dragDisposers.forEach((d) => d());
|
|
71
|
-
this._dragDisposers = null;
|
|
72
|
-
}
|
|
73
|
-
if (this._positionRaf) {
|
|
74
|
-
cancelAnimationFrame(this._positionRaf);
|
|
75
|
-
this._positionRaf = null;
|
|
76
|
-
}
|
|
77
|
-
this._deselect();
|
|
78
|
-
if (this._overlay?.parentNode) {
|
|
79
|
-
this._overlay.remove();
|
|
80
|
-
}
|
|
81
|
-
this._overlay = null;
|
|
28
|
+
/** @param {HTMLImageElement} img @param {number} w @param {number} h */
|
|
29
|
+
_applySize(img, w, h) {
|
|
30
|
+
img.style.width = `${w}px`;
|
|
31
|
+
img.style.height = `${h}px`;
|
|
82
32
|
}
|
|
83
33
|
|
|
84
34
|
// ---------------------------------------------------------------------------
|
|
@@ -87,174 +37,6 @@ export class ImageResizer {
|
|
|
87
37
|
|
|
88
38
|
/** @returns {HTMLImageElement|null} */
|
|
89
39
|
getActiveImage() {
|
|
90
|
-
return this.
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Re-sync overlay position (call after external size changes). */
|
|
94
|
-
updateOverlay() {
|
|
95
|
-
this._updateOverlayPosition();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Programmatically clear the current image selection. */
|
|
99
|
-
deselect() {
|
|
100
|
-
this._deselect();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Internal
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
_buildOverlay() {
|
|
108
|
-
const overlay = document.createElement('div');
|
|
109
|
-
overlay.className = 'an-image-resizer';
|
|
110
|
-
overlay.style.display = 'none';
|
|
111
|
-
|
|
112
|
-
HANDLE_DEFS.forEach(({ pos }) => {
|
|
113
|
-
const h = document.createElement('div');
|
|
114
|
-
h.className = `an-resize-handle an-resize-${pos}`;
|
|
115
|
-
h.dataset.handle = pos;
|
|
116
|
-
// Attach handle listeners here so they're torn down in destroy() via _disposers
|
|
117
|
-
this._disposers.push(
|
|
118
|
-
on(h, 'mousedown', (e) => {
|
|
119
|
-
e.preventDefault();
|
|
120
|
-
e.stopPropagation();
|
|
121
|
-
this._startResize(e, pos);
|
|
122
|
-
}),
|
|
123
|
-
);
|
|
124
|
-
overlay.appendChild(h);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
return overlay;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
_onEditorClick(e) {
|
|
131
|
-
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
132
|
-
const img = e.target.closest('img');
|
|
133
|
-
if (img) {
|
|
134
|
-
// Prevent browser from deselecting the current text selection / opening native resize
|
|
135
|
-
e.preventDefault();
|
|
136
|
-
this._select(img);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
_onDocClick(e) {
|
|
141
|
-
if (!this._activeImg) return;
|
|
142
|
-
if (e.target === this._activeImg) return;
|
|
143
|
-
if (this._overlay?.contains(e.target)) return;
|
|
144
|
-
// Don't deselect while interacting with the context menu
|
|
145
|
-
if (e.target.closest('.an-contextmenu')) return;
|
|
146
|
-
this._deselect();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
_select(img) {
|
|
150
|
-
if (this._activeImg && this._activeImg !== img) {
|
|
151
|
-
this._activeImg.classList.remove('an-image-selected');
|
|
152
|
-
}
|
|
153
|
-
this._activeImg = img;
|
|
154
|
-
this._lastOverlayPos = null; // invalidate position cache on new selection
|
|
155
|
-
img.classList.add('an-image-selected');
|
|
156
|
-
this._updateOverlayPosition();
|
|
157
|
-
this._overlay.style.display = 'block';
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
_deselect() {
|
|
161
|
-
if (this._activeImg) {
|
|
162
|
-
this._activeImg.classList.remove('an-image-selected');
|
|
163
|
-
this._activeImg = null;
|
|
164
|
-
}
|
|
165
|
-
if (this._overlay) this._overlay.style.display = 'none';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
_updateOverlayPosition() {
|
|
169
|
-
if (this._positionRaf) cancelAnimationFrame(this._positionRaf);
|
|
170
|
-
this._positionRaf = requestAnimationFrame(() => {
|
|
171
|
-
this._positionRaf = null;
|
|
172
|
-
this._updateOverlayPositionNow();
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
_updateOverlayPositionNow() {
|
|
177
|
-
if (!this._activeImg || !this._overlay) return;
|
|
178
|
-
const offsetParent = this._overlay.offsetParent || this._container;
|
|
179
|
-
const containerRect = offsetParent.getBoundingClientRect();
|
|
180
|
-
const rect = this._activeImg.getBoundingClientRect();
|
|
181
|
-
|
|
182
|
-
// Skip DOM writes when position hasn't changed (common during non-scroll rAF ticks)
|
|
183
|
-
const p = this._lastOverlayPos;
|
|
184
|
-
if (p?.l === rect.left && p.t === rect.top && p.w === rect.width && p.h === rect.height) return;
|
|
185
|
-
this._lastOverlayPos = { l: rect.left, t: rect.top, w: rect.width, h: rect.height };
|
|
186
|
-
|
|
187
|
-
const left = rect.left - containerRect.left + offsetParent.scrollLeft;
|
|
188
|
-
const top = rect.top - containerRect.top + offsetParent.scrollTop;
|
|
189
|
-
this._overlay.style.left = `${left}px`;
|
|
190
|
-
this._overlay.style.top = `${top}px`;
|
|
191
|
-
this._overlay.style.width = `${rect.width}px`;
|
|
192
|
-
this._overlay.style.height = `${rect.height}px`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
_startResize(e, pos) {
|
|
196
|
-
const img = this._activeImg;
|
|
197
|
-
if (!img) return;
|
|
198
|
-
|
|
199
|
-
const startX = e.clientX;
|
|
200
|
-
const startY = e.clientY;
|
|
201
|
-
const startW = img.offsetWidth || img.naturalWidth || 100;
|
|
202
|
-
const startH = img.offsetHeight || img.naturalHeight || 100;
|
|
203
|
-
const aspectRatio = startW / (startH || 1);
|
|
204
|
-
const isCorner = pos.length === 2; // 'nw','ne','se','sw'
|
|
205
|
-
|
|
206
|
-
const editable = this.context.layoutInfo.editable;
|
|
207
|
-
const minSz = this.context.options?.minImageSize ?? 20;
|
|
208
|
-
let _raf = null; // rAF handle — ensures at most one write per paint frame
|
|
209
|
-
|
|
210
|
-
const onMove = (me) => {
|
|
211
|
-
if (_raf !== null) return; // frame already pending, discard this event
|
|
212
|
-
const clientX = me.clientX;
|
|
213
|
-
const clientY = me.clientY;
|
|
214
|
-
_raf = requestAnimationFrame(() => {
|
|
215
|
-
_raf = null;
|
|
216
|
-
const dx = clientX - startX;
|
|
217
|
-
const dy = clientY - startY;
|
|
218
|
-
const maxW = editable.clientWidth || Infinity;
|
|
219
|
-
let newW = startW;
|
|
220
|
-
let newH = startH;
|
|
221
|
-
|
|
222
|
-
if (pos.includes('e')) newW = Math.max(minSz, startW + dx);
|
|
223
|
-
if (pos.includes('w')) newW = Math.max(minSz, startW - dx);
|
|
224
|
-
if (pos.includes('s')) newH = Math.max(minSz, startH + dy);
|
|
225
|
-
if (pos.includes('n')) newH = Math.max(minSz, startH - dy);
|
|
226
|
-
|
|
227
|
-
newW = Math.min(newW, maxW);
|
|
228
|
-
|
|
229
|
-
if (isCorner) {
|
|
230
|
-
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
231
|
-
newH = Math.max(minSz, Math.round(newW / aspectRatio));
|
|
232
|
-
} else {
|
|
233
|
-
newW = Math.min(Math.max(minSz, Math.round(newH * aspectRatio)), maxW);
|
|
234
|
-
newH = Math.max(minSz, Math.round(newW / aspectRatio));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
img.style.width = `${newW}px`;
|
|
239
|
-
img.style.height = `${newH}px`;
|
|
240
|
-
this._updateOverlayPosition();
|
|
241
|
-
});
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const onUp = () => {
|
|
245
|
-
if (_raf !== null) { cancelAnimationFrame(_raf); _raf = null; }
|
|
246
|
-
document.removeEventListener('mousemove', onMove);
|
|
247
|
-
document.removeEventListener('mouseup', onUp);
|
|
248
|
-
this._dragDisposers = null;
|
|
249
|
-
this.context.invoke('editor.afterCommand');
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
document.addEventListener('mousemove', onMove);
|
|
253
|
-
document.addEventListener('mouseup', onUp);
|
|
254
|
-
// Track these so destroy() can clean them up if called during an active drag
|
|
255
|
-
this._dragDisposers = [
|
|
256
|
-
() => document.removeEventListener('mousemove', onMove),
|
|
257
|
-
() => document.removeEventListener('mouseup', onUp),
|
|
258
|
-
];
|
|
40
|
+
return /** @type {HTMLImageElement|null} */ (this._active);
|
|
259
41
|
}
|
|
260
42
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Displays a horizontal action bar below (or above) the selected image,
|
|
3
3
|
// similar in appearance and interaction to LinkTooltip.
|
|
4
4
|
import { createElement, on } from '../core/dom.js';
|
|
5
|
+
import { BaseMediaTooltip } from './BaseMediaTooltip.js';
|
|
5
6
|
|
|
6
7
|
const ICONS = {
|
|
7
8
|
floatLeft: `<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="4" width="8" height="8" rx="1"/><line x1="12" y1="6" x2="22" y2="6"/><line x1="12" y1="9" x2="22" y2="9"/><line x1="12" y1="12" x2="22" y2="12"/><line x1="2" y1="16" x2="22" y2="16"/><line x1="2" y1="20" x2="18" y2="20"/></svg>`,
|
|
@@ -16,20 +17,7 @@ const ICONS = {
|
|
|
16
17
|
crop: `<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"><polyline points="6 2 6 18 22 18"/><polyline points="2 6 18 6 18 22"/></svg>`,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
const HIDE_DELAY = 180;
|
|
21
|
-
|
|
22
|
-
export class ImageTooltip {
|
|
23
|
-
/** @param {import('../Context.js').Context} context */
|
|
24
|
-
constructor(context) {
|
|
25
|
-
this.context = context;
|
|
26
|
-
this._el = null;
|
|
27
|
-
this._activeImg = null;
|
|
28
|
-
this._showTimer = null;
|
|
29
|
-
this._hideTimer = null;
|
|
30
|
-
this._disposers = [];
|
|
31
|
-
}
|
|
32
|
-
|
|
20
|
+
export class ImageTooltip extends BaseMediaTooltip {
|
|
33
21
|
initialize() {
|
|
34
22
|
this._el = this._buildTooltip();
|
|
35
23
|
document.body.appendChild(this._el);
|
|
@@ -54,7 +42,7 @@ export class ImageTooltip {
|
|
|
54
42
|
// Hide when image is deselected by clicking elsewhere
|
|
55
43
|
on(document, 'click', (e) => {
|
|
56
44
|
const et = /** @type {Node} */ (e.target);
|
|
57
|
-
if (this.
|
|
45
|
+
if (this._active && !this._active.contains(et) && !this._el.contains(et)) {
|
|
58
46
|
this._hide();
|
|
59
47
|
}
|
|
60
48
|
}),
|
|
@@ -140,97 +128,14 @@ export class ImageTooltip {
|
|
|
140
128
|
return el;
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
/**
|
|
144
|
-
* @param {string} icon
|
|
145
|
-
* @param {string} title
|
|
146
|
-
* @param {Function} handler
|
|
147
|
-
* @param {boolean} [isDanger]
|
|
148
|
-
*/
|
|
149
|
-
_makeBtn(icon, title, handler, isDanger = false) {
|
|
150
|
-
const btn = createElement('button', {
|
|
151
|
-
type: 'button',
|
|
152
|
-
class: isDanger ? 'an-link-tooltip-btn an-link-tooltip-btn--danger' : 'an-link-tooltip-btn',
|
|
153
|
-
title,
|
|
154
|
-
});
|
|
155
|
-
btn.innerHTML = icon;
|
|
156
|
-
this._disposers.push(on(btn, 'click', (e) => {
|
|
157
|
-
e.preventDefault();
|
|
158
|
-
e.stopPropagation();
|
|
159
|
-
handler();
|
|
160
|
-
}));
|
|
161
|
-
return btn;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ---------------------------------------------------------------------------
|
|
165
|
-
// Show / Hide
|
|
166
|
-
// ---------------------------------------------------------------------------
|
|
167
|
-
|
|
168
|
-
_scheduleShow(img) {
|
|
169
|
-
if (this._activeImg === img && this._el.style.display !== 'none') return;
|
|
170
|
-
clearTimeout(this._hideTimer);
|
|
171
|
-
this._hideTimer = null;
|
|
172
|
-
clearTimeout(this._showTimer);
|
|
173
|
-
this._showTimer = setTimeout(() => {
|
|
174
|
-
this._activeImg = img;
|
|
175
|
-
this._show(img);
|
|
176
|
-
}, SHOW_DELAY);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
_scheduleHide() {
|
|
180
|
-
clearTimeout(this._showTimer);
|
|
181
|
-
this._showTimer = null;
|
|
182
|
-
// Always reset the hide timer so rapid mouseout→mouseover sequences
|
|
183
|
-
// don't leave a stale timer that hides the tooltip prematurely.
|
|
184
|
-
clearTimeout(this._hideTimer);
|
|
185
|
-
this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
_show(_img) {
|
|
189
|
-
this._el.style.display = 'flex';
|
|
190
|
-
// Defer positioning: offsetWidth on a newly-visible element forces layout
|
|
191
|
-
requestAnimationFrame(() => {
|
|
192
|
-
if (this._activeImg) this._positionNear(this._activeImg);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
_hide() {
|
|
197
|
-
this._el.style.display = 'none';
|
|
198
|
-
this._activeImg = null;
|
|
199
|
-
this._clearTimers();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
_clearTimers() {
|
|
203
|
-
clearTimeout(this._showTimer);
|
|
204
|
-
clearTimeout(this._hideTimer);
|
|
205
|
-
this._showTimer = null;
|
|
206
|
-
this._hideTimer = null;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
_positionNear(img) {
|
|
210
|
-
const rect = img.getBoundingClientRect();
|
|
211
|
-
const tipW = this._el.offsetWidth || 220;
|
|
212
|
-
const tipH = this._el.offsetHeight || 32;
|
|
213
|
-
const margin = 6;
|
|
214
|
-
|
|
215
|
-
let top = rect.bottom + margin;
|
|
216
|
-
let left = rect.left + (rect.width - tipW) / 2;
|
|
217
|
-
|
|
218
|
-
if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
|
|
219
|
-
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
220
|
-
if (left < margin) left = margin;
|
|
221
|
-
|
|
222
|
-
this._el.style.top = `${top}px`;
|
|
223
|
-
this._el.style.left = `${left}px`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
131
|
// ---------------------------------------------------------------------------
|
|
227
132
|
// Actions
|
|
228
133
|
// ---------------------------------------------------------------------------
|
|
229
134
|
|
|
230
135
|
_setFloat(value) {
|
|
231
|
-
const img = this.
|
|
136
|
+
const img = this._active;
|
|
232
137
|
if (!img) return;
|
|
233
|
-
const target = img.closest('figure.an-figure') || img;
|
|
138
|
+
const target = /** @type {HTMLElement} */ (img.closest('figure.an-figure') || img);
|
|
234
139
|
target.style.float = value;
|
|
235
140
|
target.style.display = '';
|
|
236
141
|
// Clear explicit width set for centering so the figure is free to float
|
|
@@ -239,13 +144,13 @@ export class ImageTooltip {
|
|
|
239
144
|
target.style.marginRight = value === 'left' ? '12px' : '';
|
|
240
145
|
this.context.invoke('editor.afterCommand');
|
|
241
146
|
this.context.invoke('imageResizer.updateOverlay');
|
|
242
|
-
requestAnimationFrame(() => { if (this.
|
|
147
|
+
requestAnimationFrame(() => { if (this._active) this._positionNear(this._active); });
|
|
243
148
|
}
|
|
244
149
|
|
|
245
150
|
_setCenter() {
|
|
246
|
-
const img = this.
|
|
151
|
+
const img = this._active;
|
|
247
152
|
if (!img) return;
|
|
248
|
-
const target = img.closest('figure.an-figure') || img;
|
|
153
|
+
const target = /** @type {HTMLElement} */ (img.closest('figure.an-figure') || img);
|
|
249
154
|
target.style.float = '';
|
|
250
155
|
target.style.display = 'block';
|
|
251
156
|
// C1/C3: use fit-content so the block figure shrinks to the image width;
|
|
@@ -255,17 +160,17 @@ export class ImageTooltip {
|
|
|
255
160
|
target.style.marginRight = 'auto';
|
|
256
161
|
this.context.invoke('editor.afterCommand');
|
|
257
162
|
this.context.invoke('imageResizer.updateOverlay');
|
|
258
|
-
requestAnimationFrame(() => { if (this.
|
|
163
|
+
requestAnimationFrame(() => { if (this._active) this._positionNear(this._active); });
|
|
259
164
|
}
|
|
260
165
|
|
|
261
166
|
_resetSize() {
|
|
262
|
-
const img = this.
|
|
167
|
+
const img = this._active;
|
|
263
168
|
if (!img) return;
|
|
264
169
|
img.style.width = '';
|
|
265
170
|
img.style.height = '';
|
|
266
171
|
this.context.invoke('editor.afterCommand');
|
|
267
172
|
this.context.invoke('imageResizer.updateOverlay');
|
|
268
|
-
requestAnimationFrame(() => { if (this.
|
|
173
|
+
requestAnimationFrame(() => { if (this._active) this._positionNear(this._active); });
|
|
269
174
|
}
|
|
270
175
|
|
|
271
176
|
/**
|
|
@@ -275,7 +180,7 @@ export class ImageTooltip {
|
|
|
275
180
|
* @param {number} delta
|
|
276
181
|
*/
|
|
277
182
|
_rotate(delta) {
|
|
278
|
-
const img = this.
|
|
183
|
+
const img = this._active;
|
|
279
184
|
if (!img) return;
|
|
280
185
|
// Parse current rotation angle from inline transform
|
|
281
186
|
const current = img.style.transform || '';
|
|
@@ -291,11 +196,11 @@ export class ImageTooltip {
|
|
|
291
196
|
}
|
|
292
197
|
this.context.invoke('editor.afterCommand');
|
|
293
198
|
this.context.invoke('imageResizer.updateOverlay');
|
|
294
|
-
requestAnimationFrame(() => { if (this.
|
|
199
|
+
requestAnimationFrame(() => { if (this._active) this._positionNear(this._active); });
|
|
295
200
|
}
|
|
296
201
|
|
|
297
202
|
_delete() {
|
|
298
|
-
const img = this.
|
|
203
|
+
const img = this._active;
|
|
299
204
|
if (!img) return;
|
|
300
205
|
this._hide();
|
|
301
206
|
this.context.invoke('imageResizer.deselect');
|
|
@@ -309,7 +214,7 @@ export class ImageTooltip {
|
|
|
309
214
|
}
|
|
310
215
|
|
|
311
216
|
_crop() {
|
|
312
|
-
const img = this.
|
|
217
|
+
const img = this._active;
|
|
313
218
|
if (!img) return;
|
|
314
219
|
// Hide the tooltip while the crop overlay is active
|
|
315
220
|
this._hide();
|
|
@@ -318,7 +223,7 @@ export class ImageTooltip {
|
|
|
318
223
|
}
|
|
319
224
|
|
|
320
225
|
_toggleCaption() {
|
|
321
|
-
const img = this.
|
|
226
|
+
const img = this._active;
|
|
322
227
|
if (!img) return;
|
|
323
228
|
|
|
324
229
|
// If already inside a figure, move focus to the figcaption
|