suneditor 3.2.3 → 3.2.4
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/dist/suneditor.min.js +1 -1
- package/package.json +1 -1
- package/src/core/event/actions/index.js +5 -2
- package/src/core/event/effects/keydown.registry.js +6 -4
- package/src/core/event/eventOrchestrator.js +7 -3
- package/src/core/event/handlers/handler_ww_input.js +1 -1
- package/src/core/event/handlers/handler_ww_key.js +7 -6
- package/src/core/event/ports.js +4 -6
- package/src/core/event/rules/keydown.rule.backspace.js +1 -0
- package/src/core/event/rules/keydown.rule.enter.js +9 -9
- package/src/core/logic/shell/ui.js +4 -3
- package/types/core/event/actions/index.d.ts +1 -1
- package/types/core/event/effects/keydown.registry.d.ts +3 -2
- package/types/core/event/eventOrchestrator.d.ts +7 -3
- package/types/core/event/ports.d.ts +4 -4
package/package.json
CHANGED
|
@@ -210,12 +210,15 @@ export const A = {
|
|
|
210
210
|
*/
|
|
211
211
|
tabFormatIndent: (range, formatEl, shift) => ({ t: 'tab.format.indent', p: { range, formatEl, shift } }),
|
|
212
212
|
|
|
213
|
-
// ===
|
|
213
|
+
// === caret ===
|
|
214
214
|
/**
|
|
215
|
+
* @description Scroll the editor view to the caret. Used after Enter, Backspace, and any edit that can move the caret out of view.
|
|
215
216
|
* @param {Range} range Range object
|
|
216
217
|
* @returns {Action}
|
|
217
218
|
*/
|
|
218
|
-
|
|
219
|
+
caretScrollTo: (range) => ({ t: 'caret.scrollTo', p: { range } }),
|
|
220
|
+
|
|
221
|
+
// === enter ===
|
|
219
222
|
/**
|
|
220
223
|
* @param {Element} formatEl
|
|
221
224
|
* @returns {Action}
|
|
@@ -402,13 +402,15 @@ export default {
|
|
|
402
402
|
ports.selection.setRange(r.sc, r.so, r.ec, r.eo);
|
|
403
403
|
},
|
|
404
404
|
|
|
405
|
-
/** [
|
|
405
|
+
/** [caret] */
|
|
406
406
|
|
|
407
|
-
/** @action
|
|
408
|
-
'
|
|
409
|
-
ports.
|
|
407
|
+
/** @action caretScrollTo */
|
|
408
|
+
'caret.scrollTo': ({ ports }, { range }) => {
|
|
409
|
+
ports.caretScrollTo(range);
|
|
410
410
|
},
|
|
411
411
|
|
|
412
|
+
/** [enter] */
|
|
413
|
+
|
|
412
414
|
/** @action enterLineAddDefault */
|
|
413
415
|
'enter.line.addDefault': ({ ports }, { formatEl }) => {
|
|
414
416
|
const newFormat = ports.format.addLineAfter(formatEl);
|
|
@@ -214,11 +214,15 @@ class EventOrchestrator extends KernelInjector {
|
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
216
|
* @internal
|
|
217
|
-
* @description Normalize the
|
|
218
|
-
*
|
|
217
|
+
* @description Normalize the edit range before the reducer reads it, for every key we custom-handle
|
|
218
|
+
* (Enter, Backspace, Delete). Resets an element-level container (a `line` such as `P`) to a text node so
|
|
219
|
+
* the rules' text-offset arithmetic (`isEdgePoint`, `endOffset === textContent.length`, ...) is valid; and
|
|
220
|
+
* when the caret sits inside a zero-width text node adjacent to a `<br>`, moves it onto the `<br>`.
|
|
221
|
+
* Without this, Backspace/Delete misfire whenever the browser reports the container as the line itself
|
|
222
|
+
* (child-index offset) instead of a `<br>`/ZWS text node.
|
|
219
223
|
* @returns {?(HTMLElement|Text)} The updated selection node, or `null` when the caret is not on a line (no normalization ran).
|
|
220
224
|
*/
|
|
221
|
-
|
|
225
|
+
_normalizeEditRange() {
|
|
222
226
|
if (!this.$.format.isLine(this.$.selection.getRange()?.startContainer)) return null;
|
|
223
227
|
|
|
224
228
|
this.$.selection.resetRangeToTextNode();
|
|
@@ -50,13 +50,14 @@ export async function OnKeyDown_wysiwyg(fc, e) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* default key action — normalize the
|
|
54
|
-
*
|
|
55
|
-
* on keydown re-traps the iOS/mobile IME marked-text
|
|
56
|
-
*
|
|
53
|
+
* default key action — normalize the edit range before the reducer reads it, for every custom-handled key.
|
|
54
|
+
* - Enter: skipped when handled from `beforeinput` (useEnterFromBeforeInput),
|
|
55
|
+
* - because mutating the DOM here on keydown re-traps the iOS/mobile IME marked-text.
|
|
56
|
+
* - Backspace/Delete: always normalize on keydown (they have no `beforeinput` route).
|
|
57
57
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const enterOnKeydown = keyCodeMap.isEnter(keyCode) && !useEnterFromBeforeInput(this.$.store, e);
|
|
59
|
+
if (enterOnKeydown || keyCodeMap.isRemoveKey(keyCode)) {
|
|
60
|
+
const normalized = this._normalizeEditRange();
|
|
60
61
|
if (normalized) selectionNode = normalized;
|
|
61
62
|
}
|
|
62
63
|
|
package/src/core/event/ports.js
CHANGED
|
@@ -173,15 +173,13 @@ export function makePorts(inst, { _styleNodes }) {
|
|
|
173
173
|
formatAttrsTempCache: (attrs) => (inst._formatAttrsTemp = attrs),
|
|
174
174
|
setOnShortcutKey: (v) => (inst._onShortcutKey = v),
|
|
175
175
|
|
|
176
|
-
// === enter event specific ===
|
|
177
176
|
/**
|
|
178
|
-
* @description Scrolls the editor view to the caret position after
|
|
179
|
-
* @param {Range} range Pre-
|
|
177
|
+
* @description Scrolls the editor view to the caret position after an edit (Enter, Backspace, ...).
|
|
178
|
+
* @param {Range} range Pre-edit snapshot range (fallback only).
|
|
180
179
|
*/
|
|
181
|
-
|
|
180
|
+
caretScrollTo(range) {
|
|
182
181
|
ui._iframeAutoHeight(frameContext);
|
|
183
182
|
|
|
184
|
-
// Scroll to the *live* post-Enter caret, not the pre-Enter `range` snapshot.
|
|
185
183
|
selection.scrollTo(selection.getRange() || range, {
|
|
186
184
|
behavior: 'auto',
|
|
187
185
|
block: 'nearest',
|
|
@@ -231,5 +229,5 @@ export function makePorts(inst, { _styleNodes }) {
|
|
|
231
229
|
* @property {(v: boolean) => void} setOnShortcutKey
|
|
232
230
|
*
|
|
233
231
|
* @property {(e: Event) => void} enterPrevent
|
|
234
|
-
* @property {(range: Range) => void}
|
|
232
|
+
* @property {(range: Range) => void} caretScrollTo
|
|
235
233
|
*/
|
|
@@ -79,7 +79,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
79
79
|
if (shift) {
|
|
80
80
|
ports.enterPrevent(e);
|
|
81
81
|
actions.push(A.enterShiftBr(range));
|
|
82
|
-
actions.push(A.
|
|
82
|
+
actions.push(A.caretScrollTo(range));
|
|
83
83
|
return true;
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -107,7 +107,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
107
107
|
if (formatEndEdge && (/^H[1-6]$/i.test(formatEl.nodeName) || /^HR$/i.test(formatEl.nodeName))) {
|
|
108
108
|
ports.enterPrevent(e);
|
|
109
109
|
actions.push(A.enterLineAddDefault(formatEl));
|
|
110
|
-
actions.push(A.
|
|
110
|
+
actions.push(A.caretScrollTo(range));
|
|
111
111
|
return true;
|
|
112
112
|
} else if (rangeEl && formatEl && !dom.check.isTableCell(rangeEl) && !/^FIGCAPTION$/i.test(rangeEl.nodeName)) {
|
|
113
113
|
// add default List line
|
|
@@ -118,7 +118,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
118
118
|
) {
|
|
119
119
|
ports.enterPrevent(e);
|
|
120
120
|
actions.push(A.enterListAddItem(formatEl, selectionNode));
|
|
121
|
-
actions.push(A.
|
|
121
|
+
actions.push(A.caretScrollTo(range));
|
|
122
122
|
return true;
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -146,7 +146,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
146
146
|
} else {
|
|
147
147
|
actions.push(A.enterBrLineInsert(range));
|
|
148
148
|
}
|
|
149
|
-
actions.push(A.
|
|
149
|
+
actions.push(A.caretScrollTo(range));
|
|
150
150
|
return true;
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -157,7 +157,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
157
157
|
} else {
|
|
158
158
|
actions.push(A.enterFormatInsertBrNode(wSelection));
|
|
159
159
|
}
|
|
160
|
-
actions.push(A.
|
|
160
|
+
actions.push(A.caretScrollTo(range));
|
|
161
161
|
return true;
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -167,7 +167,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
167
167
|
actions.push(
|
|
168
168
|
A.enterFormatBreakAtEdge(formatEl, selectionNode, formatStartEdge, formatEndEdge, bidiSwapped),
|
|
169
169
|
);
|
|
170
|
-
actions.push(A.
|
|
170
|
+
actions.push(A.caretScrollTo(range));
|
|
171
171
|
return true;
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -194,13 +194,13 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
194
194
|
actions.push(A.enterFormatBreakAtCursor(formatEl, range));
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
actions.push(A.
|
|
197
|
+
actions.push(A.caretScrollTo(range));
|
|
198
198
|
return true;
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
if (selectRange) {
|
|
203
|
-
actions.push(A.
|
|
203
|
+
actions.push(A.caretScrollTo(range));
|
|
204
204
|
return true;
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -211,7 +211,7 @@ export function reduceEnterDown(actions, ports, ctx) {
|
|
|
211
211
|
) {
|
|
212
212
|
ports.enterPrevent(e);
|
|
213
213
|
actions.push(A.enterFigcaptionExitInList(formatEl));
|
|
214
|
-
actions.push(A.
|
|
214
|
+
actions.push(A.caretScrollTo(range));
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
return true;
|
|
@@ -885,14 +885,15 @@ class UIManager {
|
|
|
885
885
|
if (!text) return false;
|
|
886
886
|
|
|
887
887
|
const wysiwyg = fc.get('wysiwyg');
|
|
888
|
-
const prev = wysiwyg.querySelector('.se-placeholder-line');
|
|
889
888
|
|
|
890
889
|
const line = this.#store.get('hasFocus') ? this.#$.format.getLine(this.#$.selection.selectionNode) : null;
|
|
891
890
|
const inTableCell = !!line && !!dom.query.getParentElement(line, dom.check.isTableCell);
|
|
892
891
|
const target = dom.check.isEmptyLine(line) && !dom.check.isListCell(line) && !inTableCell ? line : null;
|
|
893
892
|
|
|
894
|
-
|
|
895
|
-
|
|
893
|
+
const prevMarkers = wysiwyg.querySelectorAll('.se-placeholder-line');
|
|
894
|
+
for (let i = 0; i < prevMarkers.length; i++) {
|
|
895
|
+
const prev = prevMarkers[i];
|
|
896
|
+
if (prev === target) continue;
|
|
896
897
|
prev.classList.remove('se-placeholder-line');
|
|
897
898
|
prev.removeAttribute('data-se-placeholder-line');
|
|
898
899
|
if (!prev.getAttribute('class')) prev.removeAttribute('class');
|
|
@@ -45,7 +45,7 @@ export namespace A {
|
|
|
45
45
|
function deleteBrLineRowMerge(rowEndBr: Node): Action;
|
|
46
46
|
function deleteSoftBreakMerge(br: Node): Action;
|
|
47
47
|
function tabFormatIndent(range: Range, formatEl: Element, shift: boolean): Action;
|
|
48
|
-
function
|
|
48
|
+
function caretScrollTo(range: Range): Action;
|
|
49
49
|
function enterLineAddDefault(formatEl: Element): Action;
|
|
50
50
|
function enterListAddItem(formatEl: Element, selectionNode: Node): Action;
|
|
51
51
|
function enterFormatExitEmpty(formatEl: Element, rangeEl: Element): Action;
|
|
@@ -44,9 +44,10 @@ declare const _default: {
|
|
|
44
44
|
/** [tab] */
|
|
45
45
|
/** @action tabFormatIndent */
|
|
46
46
|
'tab.format.indent': ({ ports, ctx }: EffectContext_keydown, { range, formatEl, shift }: any) => boolean;
|
|
47
|
+
/** [caret] */
|
|
48
|
+
/** @action caretScrollTo */
|
|
49
|
+
'caret.scrollTo': ({ ports }: EffectContext_keydown, { range }: any) => void;
|
|
47
50
|
/** [enter] */
|
|
48
|
-
/** @action enterScrollTo */
|
|
49
|
-
'enter.scrollTo': ({ ports }: EffectContext_keydown, { range }: any) => void;
|
|
50
51
|
/** @action enterLineAddDefault */
|
|
51
52
|
'enter.line.addDefault': ({ ports }: EffectContext_keydown, { formatEl }: any) => void;
|
|
52
53
|
/** @action enterListAddItem */
|
|
@@ -93,11 +93,15 @@ declare class EventOrchestrator extends KernelInjector {
|
|
|
93
93
|
_setDefaultLine(formatName: string | null): void;
|
|
94
94
|
/**
|
|
95
95
|
* @internal
|
|
96
|
-
* @description Normalize the
|
|
97
|
-
*
|
|
96
|
+
* @description Normalize the edit range before the reducer reads it, for every key we custom-handle
|
|
97
|
+
* (Enter, Backspace, Delete). Resets an element-level container (a `line` such as `P`) to a text node so
|
|
98
|
+
* the rules' text-offset arithmetic (`isEdgePoint`, `endOffset === textContent.length`, ...) is valid; and
|
|
99
|
+
* when the caret sits inside a zero-width text node adjacent to a `<br>`, moves it onto the `<br>`.
|
|
100
|
+
* Without this, Backspace/Delete misfire whenever the browser reports the container as the line itself
|
|
101
|
+
* (child-index offset) instead of a `<br>`/ZWS text node.
|
|
98
102
|
* @returns {?(HTMLElement|Text)} The updated selection node, or `null` when the caret is not on a line (no normalization ran).
|
|
99
103
|
*/
|
|
100
|
-
|
|
104
|
+
_normalizeEditRange(): (HTMLElement | Text) | null;
|
|
101
105
|
/**
|
|
102
106
|
* @internal
|
|
103
107
|
* @description Handles data transfer actions for `paste` and `drop` events.
|
|
@@ -168,10 +168,10 @@ export function makePorts(
|
|
|
168
168
|
formatAttrsTempCache: (attrs: any) => any;
|
|
169
169
|
setOnShortcutKey: (v: any) => any;
|
|
170
170
|
/**
|
|
171
|
-
* @description Scrolls the editor view to the caret position after
|
|
172
|
-
* @param {Range} range Pre-
|
|
171
|
+
* @description Scrolls the editor view to the caret position after an edit (Enter, Backspace, ...).
|
|
172
|
+
* @param {Range} range Pre-edit snapshot range (fallback only).
|
|
173
173
|
*/
|
|
174
|
-
|
|
174
|
+
caretScrollTo(range: Range): void;
|
|
175
175
|
/**
|
|
176
176
|
* @description Prevents the default behavior of the `Enter` key.
|
|
177
177
|
* On the `beforeinput` Enter path the IME has already committed, so a plain `preventDefault` suffices.
|
|
@@ -261,5 +261,5 @@ export type EventReducerPorts = {
|
|
|
261
261
|
formatAttrsTempCache: (attrs: { [x: string]: any }) => void;
|
|
262
262
|
setOnShortcutKey: (v: boolean) => void;
|
|
263
263
|
enterPrevent: (e: Event) => void;
|
|
264
|
-
|
|
264
|
+
caretScrollTo: (range: Range) => void;
|
|
265
265
|
};
|