suneditor 3.3.2 → 3.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suneditor",
3
- "version": "3.3.2",
3
+ "version": "3.3.3",
4
4
  "description": "Vanilla JavaScript based WYSIWYG web editor",
5
5
  "author": "Yi JiHong",
6
6
  "license": "MIT",
@@ -1107,6 +1107,7 @@ class EventOrchestrator extends KernelInjector {
1107
1107
  }
1108
1108
 
1109
1109
  this.#scrollContainer();
1110
+ this.#ui.blockHandle?.syncScroll();
1110
1111
 
1111
1112
  // document type page
1112
1113
  if (this.$.frameContext.has('documentType_use_page')) {
@@ -1159,6 +1160,7 @@ class EventOrchestrator extends KernelInjector {
1159
1160
  #OnScroll_Abs() {
1160
1161
  this.#menu.dropdownOff();
1161
1162
  this.#scrollContainer();
1163
+ this.#ui.blockHandle?.syncScroll();
1162
1164
  }
1163
1165
 
1164
1166
  /**
@@ -22,6 +22,8 @@ const _styleNodes = Object.preventExtensions({ value: [] });
22
22
  * @param {KeyboardEvent} e - Event object
23
23
  */
24
24
  export async function OnKeyDown_wysiwyg(fc, e) {
25
+ this.$.ui.blockHandle?.hideOnKeyDown();
26
+
25
27
  if ((this.isComposing = keyCodeMap.isComposing(e))) return true;
26
28
  if (this.$.ui.selectMenuOn || !e.isTrusted) return;
27
29
 
@@ -54,6 +54,10 @@ class BlockHandle {
54
54
  #pendingMouseY = undefined;
55
55
  /** @type {number|null} */
56
56
  #hideTimer = null;
57
+ /** @type {boolean} */
58
+ #keyboardLock = false;
59
+ /** @type {number|undefined} */
60
+ #lastMouseY = undefined;
57
61
  /** @type {HTMLElement[]} */
58
62
  #hoverLines = [];
59
63
 
@@ -123,8 +127,6 @@ class BlockHandle {
123
127
  em.addEvent(wrapper, 'dragover', this.#onDragOver.bind(this));
124
128
  em.addEvent(wrapper, 'drop', this.#onDrop.bind(this));
125
129
  }
126
-
127
- em.addEvent(this.#$.frameContext.get('eventWysiwyg'), 'keydown', this.#onWrapperKeyDown.bind(this), true);
128
130
  }
129
131
 
130
132
  /**
@@ -161,13 +163,24 @@ class BlockHandle {
161
163
  this.#cancelHide();
162
164
  // Lock handle while action menu is open or dragging
163
165
  if (this.#actionMenu?.isOpen || this.#isDragging) return;
166
+
167
+ if (this.#keyboardLock) {
168
+ if (mouseY === this.#lastMouseY) return;
169
+ this.#keyboardLock = false;
170
+ }
171
+
172
+ const framePending = this.#pendingTarget !== null;
164
173
  this.#pendingTarget = eventTarget;
174
+ this.#lastMouseY = mouseY;
165
175
  this.#pendingMouseY = mouseY;
166
- if (this.#rafId) return;
176
+ if (framePending) return;
177
+
167
178
  this.#rafId = _w.requestAnimationFrame(() => {
168
179
  this.#rafId = null;
169
- if (this.#pendingTarget) {
170
- this.#doPosition(this.#pendingTarget, this.#pendingMouseY);
180
+ const target = this.#pendingTarget;
181
+ this.#pendingTarget = null;
182
+ if (target) {
183
+ this.#doPosition(target, this.#pendingMouseY);
171
184
  }
172
185
  });
173
186
  }
@@ -321,6 +334,7 @@ class BlockHandle {
321
334
  if (!this.#$) return;
322
335
  if (this.#isDragging) return;
323
336
 
337
+ this.#keyboardLock = false;
324
338
  this.#cancelHide();
325
339
 
326
340
  // Don't reposition while action menu is open
@@ -494,6 +508,24 @@ class BlockHandle {
494
508
 
495
509
  const totalOffset = indent + centeringExtra;
496
510
 
511
+ const toolbarEl = this.#$.context?.get('toolbar_main');
512
+ if (toolbarEl) {
513
+ const tRect = toolbarEl.getBoundingClientRect();
514
+ const handleWidth = this.#handle.offsetWidth || 0;
515
+ const hTop = firstLineCenterVP - handleHeight / 2;
516
+ const hLeft = isRtl ? areaRect.right - totalOffset - handleWidth : areaRect.left + totalOffset;
517
+ if (
518
+ tRect.height > 0 &&
519
+ hTop < tRect.bottom - 1 &&
520
+ hTop + handleHeight > tRect.top + 1 &&
521
+ hLeft < tRect.right &&
522
+ hLeft + handleWidth > tRect.left
523
+ ) {
524
+ this.#hideHandle();
525
+ return;
526
+ }
527
+ }
528
+
497
529
  this.#handle.style.top = top + 'px';
498
530
  if (isRtl) {
499
531
  this.#handle.style.left = '';
@@ -628,10 +660,13 @@ class BlockHandle {
628
660
  }
629
661
 
630
662
  /**
663
+ * @internal
631
664
  * @description Editor keyboard activity (typing, Enter, etc.)
632
665
  */
633
- #onWrapperKeyDown() {
634
- this.#clearHoverLines();
666
+ hideOnKeyDown() {
667
+ if (this.#actionMenu?.isOpen) return;
668
+ this.#keyboardLock = true;
669
+ this.hideNow();
635
670
  }
636
671
 
637
672
  /**
@@ -136,6 +136,8 @@ class Component {
136
136
 
137
137
  if (insertBehavior === undefined) insertBehavior = this.#options.get('componentInsertBehavior');
138
138
 
139
+ if (this.isSelected) this.__deselect();
140
+
139
141
  const r = this.#$.html.remove();
140
142
  const isInline = this.isInline(element);
141
143
  this.#$.selection.getRangeAndAddLine(this.#$.selection.getRange(), r.container);
@@ -195,7 +197,7 @@ class Component {
195
197
 
196
198
  const targetElement = /** @type {HTMLElement} */ (oNode || element);
197
199
 
198
- if (scrollTo) this.#$.selection.scrollTo(targetElement, { behavior: 'auto' });
200
+ if (scrollTo) this.#$.selection.scrollTo(targetElement, { behavior: 'auto', noFocus: true });
199
201
  if (insertBehavior !== null) this.applyInsertBehavior(element, oNode, insertBehavior);
200
202
 
201
203
  return targetElement;
@@ -1,7 +1,36 @@
1
- import { _w } from '../../../helper/env';
1
+ import { _w, _d } from '../../../helper/env';
2
2
  import { getNodeFromPath, getNodePath } from '../../../helper/dom/domQuery';
3
3
  import { numbers } from '../../../helper';
4
4
 
5
+ const TRANSIENT_UI_CLASSES = [
6
+ 'se-component-selected',
7
+ 'se-figure-over-selected',
8
+ 'se-block-hover',
9
+ 'se-pre-code-focus',
10
+ ];
11
+ const TRANSIENT_UI_CLASS_RE = new RegExp(TRANSIENT_UI_CLASSES.join('|'));
12
+ const TRANSIENT_UI_CLASS_SELECTOR = '.' + TRANSIENT_UI_CLASSES.join(', .');
13
+
14
+ /**
15
+ * @description Strips transient UI-state classes (selection/hover markers) from a snapshot's HTML.
16
+ * - only while a marker is actually present in the serialized content.
17
+ * @param {string} html Serialized wysiwyg content
18
+ * @returns {string} The cleaned content
19
+ */
20
+ function cleanSnapshot(html) {
21
+ if (!TRANSIENT_UI_CLASS_RE.test(html)) return html;
22
+
23
+ const t = _d.createElement('template');
24
+ t.innerHTML = html;
25
+
26
+ const marked = t.content.querySelectorAll(TRANSIENT_UI_CLASS_SELECTOR);
27
+ for (let i = 0; i < marked.length; i++) {
28
+ marked[i].classList.remove(...TRANSIENT_UI_CLASSES);
29
+ }
30
+
31
+ return t.innerHTML;
32
+ }
33
+
5
34
  /**
6
35
  * @description History stack closure
7
36
  * @param {SunEditor.Kernel} kernel
@@ -206,7 +235,7 @@ export default function History(kernel) {
206
235
  $.pluginManager.checkFileInfo(false);
207
236
 
208
237
  const fc = frameRoots.get(rootKey);
209
- const current = fc.get('wysiwyg').innerHTML;
238
+ const current = cleanSnapshot(fc.get('wysiwyg').innerHTML);
210
239
  const root = rootStack[rootKey];
211
240
  if (!current || (root.value[root.index] && current === root.value[root.index].content)) return;
212
241
  if (stack.length > stackIndex + 1) refreshRoots(root);
@@ -319,7 +348,7 @@ export default function History(kernel) {
319
348
  */
320
349
  overwrite(rootKey) {
321
350
  setStack(
322
- frameRoots.get(rootKey || store.get('rootKey')).get('wysiwyg').innerHTML,
351
+ cleanSnapshot(frameRoots.get(rootKey || store.get('rootKey')).get('wysiwyg').innerHTML),
323
352
  null,
324
353
  store.get('rootKey'),
325
354
  0,
@@ -249,6 +249,7 @@ class Modal {
249
249
  }
250
250
 
251
251
  this.#targetElement = null;
252
+ this.isUpdate = false;
252
253
  }
253
254
 
254
255
  /**
@@ -1,5 +1,5 @@
1
1
  import { PluginCommand, PluginDropdown } from '../../interfaces';
2
- import { converter, dom } from '../../helper';
2
+ import { converter, dom, env } from '../../helper';
3
3
  import { Controller } from '../../modules/contract';
4
4
  import { SelectMenu } from '../../modules/ui';
5
5
 
@@ -133,6 +133,14 @@ class CodeBlock extends PluginCommand {
133
133
  this.#hoverSelectMenu.on(this.#hoverButton, this.#onHoverSelect.bind(this));
134
134
  this.#buildHoverMenu('');
135
135
 
136
+ this.$.eventManager.addEvent(containerEl, 'mousedown', (e) => {
137
+ if (env.isMobile || this.#hoverSelectMenu.form?.contains(dom.query.getEventTarget(e))) {
138
+ this.$.store.set('_preventBlur', true);
139
+ } else {
140
+ e.preventDefault();
141
+ }
142
+ });
143
+
136
144
  // selectMenu
137
145
  this.$.eventManager.addEvent(this.#hoverButton, 'click', (e) => {
138
146
  e.preventDefault();
@@ -47,5 +47,10 @@ declare class BlockHandle {
47
47
  * @description Cleanup — remove listeners, destroy menus, null references.
48
48
  */
49
49
  destroy(): void;
50
+ /**
51
+ * @internal
52
+ * @description Editor keyboard activity (typing, Enter, etc.)
53
+ */
54
+ hideOnKeyDown(): void;
50
55
  #private;
51
56
  }