suneditor 3.1.4 → 3.2.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/dist/suneditor-contents.min.css +1 -1
- package/dist/suneditor.min.css +28 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +2 -1
- package/src/assets/design/color.css +12 -0
- package/src/assets/design/size.css +9 -2
- package/src/assets/icons/defaultIcons.js +96 -47
- package/src/assets/suneditor-contents.css +3 -2
- package/src/assets/suneditor.css +481 -31
- package/src/core/config/contextProvider.js +2 -1
- package/src/core/config/eventManager.js +41 -2
- package/src/core/config/optionProvider.js +23 -2
- package/src/core/editor.js +32 -14
- package/src/core/event/actions/index.js +76 -9
- package/src/core/event/effects/common.registry.js +2 -1
- package/src/core/event/effects/keydown.registry.js +169 -33
- package/src/core/event/effects/ruleHelpers.js +20 -6
- package/src/core/event/eventOrchestrator.js +162 -30
- package/src/core/event/handlers/handler_toolbar.js +4 -2
- package/src/core/event/handlers/handler_ww_dragDrop.js +10 -0
- package/src/core/event/handlers/handler_ww_input.js +84 -2
- package/src/core/event/handlers/handler_ww_key.js +82 -21
- package/src/core/event/handlers/handler_ww_mouse.js +45 -8
- package/src/core/event/ports.js +27 -13
- package/src/core/event/reducers/keydown.reducer.js +33 -5
- package/src/core/event/rules/keydown.rule.arrow.js +12 -2
- package/src/core/event/rules/keydown.rule.backspace.js +110 -15
- package/src/core/event/rules/keydown.rule.delete.js +62 -7
- package/src/core/event/rules/keydown.rule.enter.js +62 -31
- package/src/core/event/support/defaultLineManager.js +9 -2
- package/src/core/event/support/selectionState.js +13 -3
- package/src/core/kernel/coreKernel.js +1 -0
- package/src/core/logic/dom/char.js +13 -3
- package/src/core/logic/dom/format.js +171 -39
- package/src/core/logic/dom/html.js +350 -72
- package/src/core/logic/dom/inline.js +229 -34
- package/src/core/logic/dom/listFormat.js +111 -20
- package/src/core/logic/dom/nodeTransform.js +36 -11
- package/src/core/logic/dom/offset.js +116 -31
- package/src/core/logic/dom/selection.js +147 -29
- package/src/core/logic/panel/blockHandle.js +785 -0
- package/src/core/logic/panel/blockResolver.js +278 -0
- package/src/core/logic/panel/finder.js +16 -6
- package/src/core/logic/panel/menu.js +119 -18
- package/src/core/logic/panel/toolbar.js +75 -19
- package/src/core/logic/panel/viewer.js +76 -20
- package/src/core/logic/shell/_commandExecutor.js +44 -9
- package/src/core/logic/shell/commandDispatcher.js +46 -4
- package/src/core/logic/shell/component.js +102 -25
- package/src/core/logic/shell/focusManager.js +13 -3
- package/src/core/logic/shell/history.js +21 -6
- package/src/core/logic/shell/pluginManager.js +21 -13
- package/src/core/logic/shell/shortcuts.js +9 -1
- package/src/core/logic/shell/ui.js +139 -37
- package/src/core/schema/frameContext.js +34 -3
- package/src/core/schema/options.js +46 -4
- package/src/core/section/constructor.js +537 -106
- package/src/core/section/documentType.js +35 -6
- package/src/helper/converter.js +24 -7
- package/src/helper/dom/domCheck.js +25 -5
- package/src/helper/dom/domQuery.js +2 -1
- package/src/helper/dom/domUtils.js +17 -4
- package/src/helper/env.js +11 -2
- package/src/helper/keyCodeMap.js +6 -2
- package/src/helper/markdown.js +24 -5
- package/src/helper/msOffice.js +9 -2
- package/src/interfaces/plugins.js +1 -1
- package/src/langs/ckb.js +0 -1
- package/src/langs/cs.js +0 -1
- package/src/langs/da.js +0 -1
- package/src/langs/de.js +0 -1
- package/src/langs/en.js +0 -1
- package/src/langs/es.js +0 -1
- package/src/langs/fa.js +0 -1
- package/src/langs/fr.js +0 -1
- package/src/langs/he.js +0 -1
- package/src/langs/hu.js +0 -1
- package/src/langs/index.js +27 -1
- package/src/langs/it.js +0 -1
- package/src/langs/ja.js +0 -1
- package/src/langs/km.js +0 -1
- package/src/langs/ko.js +0 -1
- package/src/langs/lv.js +0 -1
- package/src/langs/nl.js +0 -1
- package/src/langs/pl.js +0 -1
- package/src/langs/pt_br.js +0 -1
- package/src/langs/ro.js +0 -1
- package/src/langs/ru.js +0 -1
- package/src/langs/se.js +0 -1
- package/src/langs/tr.js +0 -1
- package/src/langs/uk.js +0 -1
- package/src/langs/ur.js +0 -1
- package/src/langs/zh_cn.js +0 -1
- package/src/modules/contract/Browser.js +58 -14
- package/src/modules/contract/ColorPicker.js +12 -4
- package/src/modules/contract/Controller.js +37 -7
- package/src/modules/contract/Figure.js +156 -36
- package/src/modules/contract/HueSlider.js +27 -4
- package/src/modules/contract/Modal.js +50 -10
- package/src/modules/manager/FileManager.js +50 -8
- package/src/modules/ui/CommandMenu.js +597 -0
- package/src/modules/ui/ModalAnchorEditor.js +33 -6
- package/src/modules/ui/SelectMenu.js +481 -36
- package/src/modules/ui/index.js +1 -0
- package/src/plugins/browser/audioGallery.js +2 -1
- package/src/plugins/browser/fileBrowser.js +9 -2
- package/src/plugins/browser/fileGallery.js +2 -1
- package/src/plugins/browser/imageGallery.js +18 -3
- package/src/plugins/browser/videoGallery.js +13 -4
- package/src/plugins/command/blockquote.js +6 -1
- package/src/plugins/command/codeBlock.js +43 -5
- package/src/plugins/command/exportPDF.js +20 -4
- package/src/plugins/command/fileUpload.js +46 -11
- package/src/plugins/command/list_bulleted.js +6 -1
- package/src/plugins/command/list_numbered.js +6 -1
- package/src/plugins/dropdown/align.js +21 -33
- package/src/plugins/dropdown/backgroundColor.js +10 -2
- package/src/plugins/dropdown/blockStyle.js +17 -22
- package/src/plugins/dropdown/font.js +34 -34
- package/src/plugins/dropdown/hr.js +15 -43
- package/src/plugins/dropdown/layout.js +10 -27
- package/src/plugins/dropdown/lineHeight.js +10 -33
- package/src/plugins/dropdown/list.js +19 -25
- package/src/plugins/dropdown/paragraphStyle.js +16 -24
- package/src/plugins/dropdown/table/index.js +72 -17
- package/src/plugins/dropdown/table/render/table.html.js +3 -1
- package/src/plugins/dropdown/table/services/table.cell.js +33 -7
- package/src/plugins/dropdown/table/services/table.clipboard.js +5 -1
- package/src/plugins/dropdown/table/services/table.grid.js +40 -9
- package/src/plugins/dropdown/table/services/table.resize.js +55 -11
- package/src/plugins/dropdown/table/services/table.selection.js +27 -7
- package/src/plugins/dropdown/table/services/table.style.js +120 -20
- package/src/plugins/dropdown/template.js +13 -29
- package/src/plugins/dropdown/textStyle.js +22 -35
- package/src/plugins/field/autocomplete.js +8 -4
- package/src/plugins/field/slashCommand.js +297 -0
- package/src/plugins/index.js +3 -0
- package/src/plugins/input/fontSize.js +37 -8
- package/src/plugins/input/pageNavigator.js +7 -1
- package/src/plugins/modal/audio.js +71 -17
- package/src/plugins/modal/drawing.js +41 -11
- package/src/plugins/modal/embed.js +92 -22
- package/src/plugins/modal/image/index.js +114 -28
- package/src/plugins/modal/image/services/image.size.js +9 -2
- package/src/plugins/modal/image/services/image.upload.js +38 -4
- package/src/plugins/modal/link.js +9 -2
- package/src/plugins/modal/math.js +10 -2
- package/src/plugins/modal/video/index.js +112 -26
- package/src/plugins/modal/video/render/video.html.js +4 -1
- package/src/plugins/modal/video/services/video.size.js +15 -4
- package/src/plugins/modal/video/services/video.upload.js +10 -2
- package/src/plugins/popup/anchor.js +14 -2
- package/src/suneditor.js +8 -2
- package/src/themes/cobalt.css +12 -0
- package/src/themes/cream.css +12 -0
- package/src/themes/dark.css +12 -0
- package/src/themes/midnight.css +12 -0
- package/src/typedef.js +32 -21
- package/types/assets/icons/defaultIcons.d.ts +1 -0
- package/types/core/config/contextProvider.d.ts +7 -1
- package/types/core/config/eventManager.d.ts +37 -4
- package/types/core/config/optionProvider.d.ts +5 -1
- package/types/core/event/actions/index.d.ts +44 -6
- package/types/core/event/effects/keydown.registry.d.ts +37 -7
- package/types/core/event/effects/ruleHelpers.d.ts +6 -1
- package/types/core/event/eventOrchestrator.d.ts +13 -3
- package/types/core/event/handlers/handler_ww_clipboard.d.ts +15 -3
- package/types/core/event/handlers/handler_ww_dragDrop.d.ts +14 -2
- package/types/core/event/handlers/handler_ww_input.d.ts +10 -2
- package/types/core/event/handlers/handler_ww_key.d.ts +10 -2
- package/types/core/event/handlers/handler_ww_mouse.d.ts +25 -5
- package/types/core/event/ports.d.ts +11 -4
- package/types/core/event/reducers/keydown.reducer.d.ts +12 -3
- package/types/core/event/rules/keydown.rule.backspace.d.ts +6 -1
- package/types/core/kernel/store.d.ts +4 -1
- package/types/core/logic/dom/format.d.ts +32 -14
- package/types/core/logic/dom/html.d.ts +11 -3
- package/types/core/logic/dom/nodeTransform.d.ts +6 -6
- package/types/core/logic/panel/blockHandle.d.ts +64 -0
- package/types/core/logic/panel/blockResolver.d.ts +50 -0
- package/types/core/logic/panel/menu.d.ts +72 -3
- package/types/core/logic/shell/commandDispatcher.d.ts +8 -0
- package/types/core/logic/shell/component.d.ts +5 -3
- package/types/core/logic/shell/shortcuts.d.ts +10 -1
- package/types/core/logic/shell/ui.d.ts +7 -1
- package/types/core/schema/context.d.ts +7 -1
- package/types/core/schema/frameContext.d.ts +36 -4
- package/types/core/schema/options.d.ts +83 -2
- package/types/core/section/constructor.d.ts +38 -1
- package/types/events.d.ts +208 -32
- package/types/helper/converter.d.ts +4 -2
- package/types/helper/dom/domCheck.d.ts +12 -2
- package/types/helper/dom/domQuery.d.ts +35 -7
- package/types/helper/dom/domUtils.d.ts +4 -1
- package/types/interfaces/plugins.d.ts +2 -2
- package/types/langs/_Lang.d.ts +0 -1
- package/types/modules/contract/ColorPicker.d.ts +2 -2
- package/types/modules/contract/Controller.d.ts +7 -0
- package/types/modules/contract/Figure.d.ts +31 -4
- package/types/modules/ui/CommandMenu.d.ts +262 -0
- package/types/modules/ui/SelectMenu.d.ts +56 -3
- package/types/modules/ui/index.d.ts +1 -0
- package/types/plugins/browser/audioGallery.d.ts +1 -1
- package/types/plugins/browser/fileBrowser.d.ts +1 -1
- package/types/plugins/browser/fileGallery.d.ts +1 -1
- package/types/plugins/browser/imageGallery.d.ts +1 -1
- package/types/plugins/browser/videoGallery.d.ts +1 -1
- package/types/plugins/dropdown/align.d.ts +1 -1
- package/types/plugins/dropdown/blockStyle.d.ts +3 -1
- package/types/plugins/dropdown/table/index.d.ts +4 -1
- package/types/plugins/dropdown/table/services/table.clipboard.d.ts +5 -1
- package/types/plugins/dropdown/table/services/table.grid.d.ts +10 -2
- package/types/plugins/field/autocomplete.d.ts +0 -1
- package/types/plugins/field/slashCommand.d.ts +165 -0
- package/types/plugins/index.d.ts +3 -0
- package/types/plugins/modal/link.d.ts +4 -1
- package/types/typedef.d.ts +48 -23
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { dom, env, unicode, keyCodeMap } from '../../../helper';
|
|
2
2
|
import { actionExecutor } from '../executor';
|
|
3
3
|
import { makePorts } from '../ports';
|
|
4
|
-
import { reduceKeydown } from '../reducers/keydown.reducer';
|
|
4
|
+
import { reduceKeydown, ENTER_FROM_BEFOREINPUT } from '../reducers/keydown.reducer';
|
|
5
5
|
|
|
6
6
|
const { _w } = env;
|
|
7
7
|
const FRONT_ZEROWIDTH = new RegExp(unicode.zeroWidthSpace + '+', '');
|
|
@@ -47,25 +47,37 @@ export async function OnKeyDown_wysiwyg(fc, e) {
|
|
|
47
47
|
if (!this.$.store.mode.isSubBalloonAlways) this._hideToolbar_sub();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
/**
|
|
51
|
+
* default key action — normalize the Enter range before the reducer reads it.
|
|
52
|
+
* Skipped when Enter is handled from `beforeinput` (ENTER_FROM_BEFOREINPUT): mutating the DOM here
|
|
53
|
+
* on keydown re-traps the iOS/mobile IME marked-text — `dispatchEnter` runs this same normalization
|
|
54
|
+
* later, after the IME has committed.
|
|
55
|
+
*/
|
|
56
|
+
if (!ENTER_FROM_BEFOREINPUT && keyCodeMap.isEnter(keyCode)) {
|
|
57
|
+
const normalized = this._normalizeEnterRange();
|
|
58
|
+
if (normalized) selectionNode = normalized;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
const range = this.$.selection.getRange();
|
|
60
62
|
const formatEl = /** @type {HTMLElement} */ (this.$.format.getLine(selectionNode, null) || selectionNode);
|
|
61
63
|
|
|
62
64
|
/** Shortcuts */
|
|
63
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
ctrl &&
|
|
67
|
+
!keyCodeMap.isNonTextKey(keyCode) &&
|
|
68
|
+
this.$.shortcuts.command(e, ctrl, shift, keyCode, '', false, null, null)
|
|
69
|
+
) {
|
|
64
70
|
this._onShortcutKey = true;
|
|
65
71
|
e.preventDefault();
|
|
66
72
|
e.stopPropagation();
|
|
67
73
|
return false;
|
|
68
|
-
} else if (
|
|
74
|
+
} else if (
|
|
75
|
+
!ctrl &&
|
|
76
|
+
!keyCodeMap.isNonTextKey(keyCode) &&
|
|
77
|
+
this.$.format.isLine(formatEl) &&
|
|
78
|
+
range.collapsed &&
|
|
79
|
+
dom.check.isEdgePoint(range.startContainer, 0, 'front')
|
|
80
|
+
) {
|
|
69
81
|
const keyword = /** @type {Text} */ (range.startContainer).substringData?.(0, range.startOffset);
|
|
70
82
|
if (keyword && this.$.shortcuts.command(e, false, shift, keyCode, keyword, true, formatEl, range)) {
|
|
71
83
|
this._onShortcutKey = true;
|
|
@@ -77,16 +89,36 @@ export async function OnKeyDown_wysiwyg(fc, e) {
|
|
|
77
89
|
this._onShortcutKey = false;
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
// plugin event
|
|
81
|
-
if ((await this._callPluginEventAsync('onKeyDown', { frameContext: fc, event: e, range, line: formatEl })) === false) return;
|
|
82
|
-
|
|
83
92
|
// reducer / actions
|
|
84
93
|
/** @type {import('../reducers/keydown.reducer').KeydownReducerCtx} */
|
|
85
|
-
const ctx = {
|
|
94
|
+
const ctx = {
|
|
95
|
+
e,
|
|
96
|
+
fc,
|
|
97
|
+
store: this.$.store,
|
|
98
|
+
options: this.$.options,
|
|
99
|
+
frameOptions: this.$.frameOptions,
|
|
100
|
+
range,
|
|
101
|
+
selectionNode,
|
|
102
|
+
formatEl,
|
|
103
|
+
keyCode,
|
|
104
|
+
ctrl,
|
|
105
|
+
alt,
|
|
106
|
+
shift,
|
|
107
|
+
};
|
|
86
108
|
const ports = makePorts(this, { _styleNodes });
|
|
87
109
|
|
|
88
110
|
// action execute
|
|
89
111
|
const actions = await reduceKeydown(ports, ctx);
|
|
112
|
+
|
|
113
|
+
if ((await this.$.eventManager.triggerEvent('onKeyDown', { frameContext: fc, event: e })) === false) return;
|
|
114
|
+
|
|
115
|
+
// plugin event
|
|
116
|
+
if (
|
|
117
|
+
(await this._callPluginEventAsync('onKeyDown', { frameContext: fc, event: e, range, line: formatEl })) === false
|
|
118
|
+
)
|
|
119
|
+
return;
|
|
120
|
+
|
|
121
|
+
// action execute
|
|
90
122
|
await actionExecutor(actions, { ports, ctx });
|
|
91
123
|
}
|
|
92
124
|
|
|
@@ -110,7 +142,11 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
110
142
|
const range = this.$.selection.getRange();
|
|
111
143
|
let selectionNode = this.$.selection.getNode();
|
|
112
144
|
|
|
113
|
-
if (
|
|
145
|
+
if (
|
|
146
|
+
(this.$.store.mode.isBalloon || this.$.store.mode.isSubBalloon) &&
|
|
147
|
+
(((this.$.store.mode.isBalloonAlways || this.$.store.mode.isSubBalloonAlways) && !keyCodeMap.isEsc(keyCode)) ||
|
|
148
|
+
!range.collapsed)
|
|
149
|
+
) {
|
|
114
150
|
if (this.$.store.mode.isBalloonAlways || this.$.store.mode.isSubBalloonAlways) {
|
|
115
151
|
if (!keyCodeMap.isEsc(keyCode)) this._showToolbarBalloonDelay();
|
|
116
152
|
} else {
|
|
@@ -121,14 +157,25 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
121
157
|
}
|
|
122
158
|
|
|
123
159
|
/** when format tag deleted */
|
|
124
|
-
if (
|
|
160
|
+
if (
|
|
161
|
+
keyCodeMap.isBackspace(keyCode) &&
|
|
162
|
+
dom.check.isWysiwygFrame(selectionNode) &&
|
|
163
|
+
selectionNode.textContent === '' &&
|
|
164
|
+
selectionNode.children.length === 0
|
|
165
|
+
) {
|
|
125
166
|
e.preventDefault();
|
|
126
167
|
e.stopPropagation();
|
|
127
168
|
|
|
128
169
|
selectionNode.innerHTML = '';
|
|
129
170
|
|
|
130
171
|
const currentNodeName = this.$.store.get('currentNodes')[0];
|
|
131
|
-
const oFormatTag = dom.utils.createElement(
|
|
172
|
+
const oFormatTag = dom.utils.createElement(
|
|
173
|
+
this.$.format.isLine(currentNodeName) && !dom.check.isListCell(currentNodeName)
|
|
174
|
+
? currentNodeName
|
|
175
|
+
: this.$.options.get('defaultLine'),
|
|
176
|
+
null,
|
|
177
|
+
'<br>',
|
|
178
|
+
);
|
|
132
179
|
selectionNode.appendChild(oFormatTag);
|
|
133
180
|
this.$.selection.setRange(oFormatTag, 0, oFormatTag, 0);
|
|
134
181
|
this.applyTagEffect();
|
|
@@ -172,7 +219,12 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
172
219
|
}
|
|
173
220
|
|
|
174
221
|
const textKey = !keyState.ctrl && !keyState.alt && !keyCodeMap.isNonTextKey(keyCode);
|
|
175
|
-
if (
|
|
222
|
+
if (
|
|
223
|
+
textKey &&
|
|
224
|
+
selectionNode.nodeType === 3 &&
|
|
225
|
+
unicode.zeroWidthRegExp.test(selectionNode.textContent) &&
|
|
226
|
+
!(e.isComposing !== undefined ? e.isComposing : this.isComposing)
|
|
227
|
+
) {
|
|
176
228
|
let so = range.startOffset,
|
|
177
229
|
eo = range.endOffset;
|
|
178
230
|
const frontZeroWidthCnt = (selectionNode.textContent.substring(0, eo).match(FRONT_ZEROWIDTH) || '').length;
|
|
@@ -182,7 +234,12 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
182
234
|
this.$.selection.setRange(selectionNode, so < 0 ? 0 : so, selectionNode, eo < 0 ? 0 : eo);
|
|
183
235
|
}
|
|
184
236
|
|
|
185
|
-
if (
|
|
237
|
+
if (
|
|
238
|
+
keyCodeMap.isRemoveKey(keyCode) &&
|
|
239
|
+
dom.check.isZeroWidth(formatEl?.textContent) &&
|
|
240
|
+
!formatEl.previousElementSibling &&
|
|
241
|
+
!dom.check.isListCell(formatEl)
|
|
242
|
+
) {
|
|
186
243
|
const rsMode = this.$.options.get('retainStyleMode');
|
|
187
244
|
if (rsMode !== 'none' && _styleNodes.value?.length > 0) {
|
|
188
245
|
if (rsMode === 'repeat') {
|
|
@@ -209,7 +266,10 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
209
266
|
if (fc.has('documentType_use_header')) {
|
|
210
267
|
if (keyCodeMap.isDocumentTypeObserverKey(keyCode)) {
|
|
211
268
|
fc.get('documentType').reHeader();
|
|
212
|
-
const el = dom.query.getParentElement(
|
|
269
|
+
const el = dom.query.getParentElement(
|
|
270
|
+
this.$.selection.selectionNode,
|
|
271
|
+
this.$.format.isLine.bind(this.$.format),
|
|
272
|
+
);
|
|
213
273
|
fc.get('documentType').on(el);
|
|
214
274
|
} else {
|
|
215
275
|
const el = dom.query.getParentElement(selectionNode, (current) => current.nodeType === 1);
|
|
@@ -220,7 +280,8 @@ export async function OnKeyUp_wysiwyg(fc, e) {
|
|
|
220
280
|
// user event
|
|
221
281
|
if ((await this.$.eventManager.triggerEvent('onKeyUp', { frameContext: fc, event: e })) === false) return;
|
|
222
282
|
// plugin event
|
|
223
|
-
if ((await this._callPluginEventAsync('onKeyUp', { frameContext: fc, event: e, range, line: formatEl })) === false)
|
|
283
|
+
if ((await this._callPluginEventAsync('onKeyUp', { frameContext: fc, event: e, range, line: formatEl })) === false)
|
|
284
|
+
return;
|
|
224
285
|
|
|
225
286
|
if (keyCodeMap.isHistoryRelevantKey(keyCode)) {
|
|
226
287
|
this.$.history.push(true);
|
|
@@ -3,6 +3,12 @@ import { _DragHandle } from '../../../modules/ui';
|
|
|
3
3
|
|
|
4
4
|
const { _w } = env;
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Per-frame rAF state for wysiwyg mousemove coalescing.
|
|
8
|
+
* @type {WeakMap<*, {rafId: ?number, event: ?MouseEvent}>}
|
|
9
|
+
*/
|
|
10
|
+
const __wwMousemoveRaf = new WeakMap();
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* @typedef {import('../eventOrchestrator').default} EventManagerThis_handler_ww_mouse
|
|
8
14
|
*/
|
|
@@ -102,7 +108,12 @@ export async function OnClick_wysiwyg(fc, e) {
|
|
|
102
108
|
if (e.detail === 3) {
|
|
103
109
|
const range = this.$.selection.getRange();
|
|
104
110
|
if (this.$.format.isLine(range.endContainer) && range.endOffset === 0) {
|
|
105
|
-
this.$.selection.setRange(
|
|
111
|
+
this.$.selection.setRange(
|
|
112
|
+
range.startContainer,
|
|
113
|
+
range.startOffset,
|
|
114
|
+
range.startContainer,
|
|
115
|
+
range.startContainer.textContent.length,
|
|
116
|
+
);
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
119
|
|
|
@@ -122,7 +133,8 @@ export async function OnClick_wysiwyg(fc, e) {
|
|
|
122
133
|
!dom.check.isWysiwygFrame(selectionNode) &&
|
|
123
134
|
!this.$.component.is(selectionNode) &&
|
|
124
135
|
(!dom.check.isTableElements(selectionNode) || dom.check.isTableCell(selectionNode)) &&
|
|
125
|
-
this._setDefaultLine(this.$.format.isBlock(rangeEl) ? 'DIV' : this.$.options.get('defaultLine')) !==
|
|
136
|
+
this._setDefaultLine(this.$.format.isBlock(rangeEl) ? 'DIV' : this.$.options.get('defaultLine')) !==
|
|
137
|
+
null
|
|
126
138
|
) {
|
|
127
139
|
e.preventDefault();
|
|
128
140
|
this.$.focusManager.focus();
|
|
@@ -134,7 +146,8 @@ export async function OnClick_wysiwyg(fc, e) {
|
|
|
134
146
|
this.$.commandDispatcher._copyFormat();
|
|
135
147
|
|
|
136
148
|
// Defer balloon toggle — selection range is finalized after mouseup event returns
|
|
137
|
-
if (this.$.store.mode.isBalloon || this.$.store.mode.isSubBalloon)
|
|
149
|
+
if (this.$.store.mode.isBalloon || this.$.store.mode.isSubBalloon)
|
|
150
|
+
_w.setTimeout(this._toggleToolbarBalloon.bind(this), 0);
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
/**
|
|
@@ -144,14 +157,35 @@ export async function OnClick_wysiwyg(fc, e) {
|
|
|
144
157
|
*/
|
|
145
158
|
export function OnMouseMove_wysiwyg(fc, e) {
|
|
146
159
|
if (fc.get('isReadOnly') || fc.get('isDisabled')) return false;
|
|
147
|
-
const eventTarget = dom.query.getEventTarget(e);
|
|
148
160
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
let s = __wwMousemoveRaf.get(fc);
|
|
162
|
+
|
|
163
|
+
if (!s) {
|
|
164
|
+
s = { rafId: null, event: null };
|
|
165
|
+
__wwMousemoveRaf.set(fc, s);
|
|
152
166
|
}
|
|
153
167
|
|
|
154
|
-
|
|
168
|
+
s.event = e;
|
|
169
|
+
if (s.rafId !== null) return;
|
|
170
|
+
|
|
171
|
+
s.rafId = _w.requestAnimationFrame(() => {
|
|
172
|
+
const ev = s.event;
|
|
173
|
+
s.rafId = null;
|
|
174
|
+
s.event = null;
|
|
175
|
+
if (!ev) return;
|
|
176
|
+
|
|
177
|
+
const eventTarget = dom.query.getEventTarget(ev);
|
|
178
|
+
|
|
179
|
+
// over component — skip while select menu (block handle menu) is open
|
|
180
|
+
if (_DragHandle.get('__overInfo') !== false && !this.$.ui.selectMenuOn) {
|
|
181
|
+
this.$.component.hoverSelect(eventTarget);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// block handle
|
|
185
|
+
this.$.ui.blockHandle?.positionForTarget(eventTarget, ev.clientY);
|
|
186
|
+
|
|
187
|
+
this._callPluginEvent('onMouseMove', { frameContext: fc, event: ev });
|
|
188
|
+
});
|
|
155
189
|
}
|
|
156
190
|
|
|
157
191
|
/**
|
|
@@ -160,6 +194,9 @@ export function OnMouseMove_wysiwyg(fc, e) {
|
|
|
160
194
|
* @param {MouseEvent} e - Event object
|
|
161
195
|
*/
|
|
162
196
|
export async function OnMouseLeave_wysiwyg(fc, e) {
|
|
197
|
+
// block handle hide
|
|
198
|
+
this.$.ui.blockHandle?.hide(e);
|
|
199
|
+
|
|
163
200
|
// user event
|
|
164
201
|
if ((await this.$.eventManager.triggerEvent('onMouseLeave', { frameContext: fc, event: e })) === false) return;
|
|
165
202
|
// plugin event
|
package/src/core/event/ports.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { isMobile } from '../../helper/env';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @typedef {import('./eventOrchestrator').default} EventManagerInstanceType
|
|
5
3
|
*/
|
|
@@ -37,6 +35,7 @@ import { isMobile } from '../../helper/env';
|
|
|
37
35
|
* @property {(...args: Parameters<Format['isEdgeLine']>) => ReturnType<Format['isEdgeLine']>} isEdgeLine
|
|
38
36
|
* @property {(...args: Parameters<Format['removeBlock']>) => ReturnType<Format['removeBlock']>} removeBlock
|
|
39
37
|
* @property {(...args: Parameters<Format['addLine']>) => ReturnType<Format['addLine']>} addLine
|
|
38
|
+
* @property {(...args: Parameters<Format['addLineAfter']>) => ReturnType<Format['addLineAfter']>} addLineAfter
|
|
40
39
|
*/
|
|
41
40
|
|
|
42
41
|
/**
|
|
@@ -82,7 +81,20 @@ import { isMobile } from '../../helper/env';
|
|
|
82
81
|
* @param {*} param1._styleNodes - Style nodes reference object
|
|
83
82
|
*/
|
|
84
83
|
export function makePorts(inst, { _styleNodes }) {
|
|
85
|
-
const {
|
|
84
|
+
const {
|
|
85
|
+
frameContext,
|
|
86
|
+
ui,
|
|
87
|
+
focusManager,
|
|
88
|
+
selection,
|
|
89
|
+
format,
|
|
90
|
+
listFormat,
|
|
91
|
+
component,
|
|
92
|
+
html,
|
|
93
|
+
nodeTransform,
|
|
94
|
+
history,
|
|
95
|
+
char,
|
|
96
|
+
menu,
|
|
97
|
+
} = inst.$;
|
|
86
98
|
|
|
87
99
|
return {
|
|
88
100
|
// focusManager
|
|
@@ -112,6 +124,7 @@ export function makePorts(inst, { _styleNodes }) {
|
|
|
112
124
|
isEdgeLine: (node, offset, dir) => format.isEdgeLine(node, offset, dir),
|
|
113
125
|
removeBlock: (n, p) => format.removeBlock(n, p),
|
|
114
126
|
addLine: (el, nextOrTag) => format.addLine(el, nextOrTag),
|
|
127
|
+
addLineAfter: (el) => format.addLineAfter(el),
|
|
115
128
|
},
|
|
116
129
|
|
|
117
130
|
listFormat: {
|
|
@@ -159,25 +172,26 @@ export function makePorts(inst, { _styleNodes }) {
|
|
|
159
172
|
// === enter event specific ===
|
|
160
173
|
/**
|
|
161
174
|
* @description Scrolls the editor view to the caret position after pressing `Enter`.
|
|
162
|
-
* @param {Range} range
|
|
175
|
+
* @param {Range} range Pre-Enter snapshot range (fallback only).
|
|
163
176
|
*/
|
|
164
177
|
enterScrollTo(range) {
|
|
165
178
|
ui._iframeAutoHeight(frameContext);
|
|
166
179
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
// Scroll to the *live* post-Enter caret, not the pre-Enter `range` snapshot.
|
|
181
|
+
selection.scrollTo(selection.getRange() || range, {
|
|
182
|
+
behavior: 'auto',
|
|
183
|
+
block: 'nearest',
|
|
184
|
+
inline: 'nearest',
|
|
185
|
+
});
|
|
170
186
|
},
|
|
171
187
|
/**
|
|
172
|
-
* @description Prevents the default behavior of the `Enter` key
|
|
173
|
-
*
|
|
188
|
+
* @description Prevents the default behavior of the `Enter` key.
|
|
189
|
+
* Enter now runs from `beforeinput` (post-IME-commit), so the former mobile focus-shuffle
|
|
190
|
+
* (temp-focus → refocus, to force-end a virtual-keyboard IME session) is unnecessary.
|
|
191
|
+
* @param {Event} e The keyboard/input event
|
|
174
192
|
*/
|
|
175
193
|
enterPrevent(e) {
|
|
176
194
|
e.preventDefault();
|
|
177
|
-
if (!isMobile) return;
|
|
178
|
-
|
|
179
|
-
inst.__focusTemp.focus({ preventScroll: true });
|
|
180
|
-
frameContext.get('wysiwyg').focus({ preventScroll: true });
|
|
181
195
|
},
|
|
182
196
|
};
|
|
183
197
|
}
|
|
@@ -9,13 +9,23 @@ import { A } from '../actions';
|
|
|
9
9
|
|
|
10
10
|
const { isOSX_IOS } = env;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @description Enter is processed from the `beforeinput` event (post-IME-commit) instead of `keydown`,
|
|
14
|
+
* to avoid trapping iOS/mobile IME marked-text when `keydown` mutates the DOM (see `handler_ww_input.js`).
|
|
15
|
+
* Flip to `false` to instantly restore the legacy synchronous `keydown` Enter path — no other file needs
|
|
16
|
+
* touching for rollback (the `keydown` Enter gate, the `handler_ww_key` normalization guard, and the
|
|
17
|
+
* `beforeinput` dispatch all key off this single flag).
|
|
18
|
+
* @type {boolean}
|
|
19
|
+
*/
|
|
20
|
+
export const ENTER_FROM_BEFOREINPUT = true;
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* @typedef {import('../ports').EventReducerPorts} EventPorts
|
|
14
24
|
*/
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
27
|
* @typedef {Object} KeydownReducerCtx - Keydown Reducer Context object
|
|
18
|
-
* @property {KeyboardEvent} ctx.e - The keyboard event
|
|
28
|
+
* @property {KeyboardEvent|InputEvent} ctx.e - The keyboard event (or the `beforeinput` InputEvent when Enter is dispatched from `beforeinput`)
|
|
19
29
|
* @property {SunEditor.FrameContext} ctx.fc - Frame context object
|
|
20
30
|
* @property {SunEditor.Store} ctx.store - Editor store object
|
|
21
31
|
* @property {SunEditor.Options} ctx.options - Options object
|
|
@@ -62,8 +72,13 @@ export async function reduceKeydown(ports, ctx) {
|
|
|
62
72
|
break;
|
|
63
73
|
}
|
|
64
74
|
case 'Enter' /** enter key */: {
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
// Enter is handled in `beforeinput` (IME stability) — see ENTER_FROM_BEFOREINPUT.
|
|
76
|
+
// Gate only (not an early-return) so the post-switch `documentTypeRefreshHeader`
|
|
77
|
+
// still runs for a selectRange + Enter on keydown.
|
|
78
|
+
if (!ENTER_FROM_BEFOREINPUT) {
|
|
79
|
+
if (reduceEnterDown(actions, ports, ctx) === false) {
|
|
80
|
+
return actions;
|
|
81
|
+
}
|
|
67
82
|
}
|
|
68
83
|
break;
|
|
69
84
|
}
|
|
@@ -79,13 +94,26 @@ export async function reduceKeydown(ports, ctx) {
|
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
const selectRange = !range.collapsed || range.startContainer !== range.endContainer;
|
|
82
|
-
if (
|
|
97
|
+
if (
|
|
98
|
+
!ctrl &&
|
|
99
|
+
!alt &&
|
|
100
|
+
!selectRange &&
|
|
101
|
+
!keyCodeMap.isNonTextKey(keyCode) &&
|
|
102
|
+
dom.check.isBreak(range.commonAncestorContainer)
|
|
103
|
+
) {
|
|
83
104
|
actions.push(A.keydownInputInsertZWS());
|
|
84
105
|
return actions;
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
// document type
|
|
88
|
-
if (
|
|
109
|
+
if (
|
|
110
|
+
fc.has('documentType_use_header') &&
|
|
111
|
+
selectRange &&
|
|
112
|
+
!ctrl &&
|
|
113
|
+
!alt &&
|
|
114
|
+
!shift &&
|
|
115
|
+
!keyCodeMap.isDirectionKey(keyCode)
|
|
116
|
+
) {
|
|
89
117
|
actions.push(A.documentTypeRefreshHeader());
|
|
90
118
|
return actions;
|
|
91
119
|
}
|
|
@@ -23,7 +23,16 @@ export function reduceArrowDown(actions, ports, ctx) {
|
|
|
23
23
|
const rtl = ctx.options.get('_rtl');
|
|
24
24
|
|
|
25
25
|
// In RTL, ArrowLeft is forward (toward end on the left), ArrowRight is backward (toward start on the right)
|
|
26
|
-
const hDir =
|
|
26
|
+
const hDir =
|
|
27
|
+
keyCode === 'ArrowLeft'
|
|
28
|
+
? rtl
|
|
29
|
+
? 'forward'
|
|
30
|
+
: 'back'
|
|
31
|
+
: keyCode === 'ArrowRight'
|
|
32
|
+
? rtl
|
|
33
|
+
? 'back'
|
|
34
|
+
: 'forward'
|
|
35
|
+
: null;
|
|
27
36
|
|
|
28
37
|
// next component
|
|
29
38
|
let cmponentInfo = null;
|
|
@@ -46,7 +55,8 @@ export function reduceArrowDown(actions, ports, ctx) {
|
|
|
46
55
|
}
|
|
47
56
|
} else if (hDir === 'back') {
|
|
48
57
|
if (dom.check.isEdgePoint(selectionNode, range.startOffset, 'front')) {
|
|
49
|
-
const prevEl =
|
|
58
|
+
const prevEl =
|
|
59
|
+
selectionNode.previousElementSibling || dom.query.getPreviousDeepestNode(selectionNode);
|
|
50
60
|
if (prevEl) {
|
|
51
61
|
if (component.is(prevEl)) cmponentInfo = component.get(prevEl);
|
|
52
62
|
} else if (component.is(formatEl.previousElementSibling)) {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { dom } from '../../../helper';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cleanRemovedTags,
|
|
4
|
+
hardDelete,
|
|
5
|
+
isUneditableNode,
|
|
6
|
+
setDefaultLine,
|
|
7
|
+
isRtlBidiMismatch,
|
|
8
|
+
} from '../effects/ruleHelpers';
|
|
3
9
|
import { A } from '../actions';
|
|
4
10
|
|
|
5
11
|
/**
|
|
@@ -23,7 +29,11 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
23
29
|
|
|
24
30
|
const selectRange = !range.collapsed || range.startContainer !== range.endContainer;
|
|
25
31
|
// RTL bidi guard: if offset 0 is actually at the visual end due to LTR text in RTL line, skip front-edge handling
|
|
26
|
-
const bidiNotFront =
|
|
32
|
+
const bidiNotFront =
|
|
33
|
+
options.get('_rtl') &&
|
|
34
|
+
!selectRange &&
|
|
35
|
+
range.startOffset === 0 &&
|
|
36
|
+
isRtlBidiMismatch(range, formatEl, 'front', fc.get('_wd'));
|
|
27
37
|
|
|
28
38
|
actions.push(A.componentDeselect());
|
|
29
39
|
actions.push(A.cacheStyleNode());
|
|
@@ -33,7 +43,13 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
33
43
|
return true;
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
!format.isNormalLine(formatEl) &&
|
|
48
|
+
!format.isBrLine(formatEl) &&
|
|
49
|
+
!fc.get('wysiwyg').firstElementChild &&
|
|
50
|
+
!component.is(selectionNode) &&
|
|
51
|
+
setDefaultLine(ports, options.get('defaultLine')) !== null
|
|
52
|
+
) {
|
|
37
53
|
actions.push(A.preventStop());
|
|
38
54
|
return false;
|
|
39
55
|
}
|
|
@@ -62,7 +78,9 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
62
78
|
!selectionNode.previousSibling &&
|
|
63
79
|
!dom.check.isListCell(formatEl) &&
|
|
64
80
|
format.isLine(formatEl) &&
|
|
65
|
-
(!format.isBrLine(formatEl) ||
|
|
81
|
+
(!format.isBrLine(formatEl) ||
|
|
82
|
+
format.isClosureBrLine(formatEl) ||
|
|
83
|
+
dom.check.isWysiwygFrame(formatEl.parentNode))
|
|
66
84
|
) {
|
|
67
85
|
// closure range
|
|
68
86
|
if (format.isClosureBlock(formatEl.parentNode)) {
|
|
@@ -79,7 +97,11 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
79
97
|
}
|
|
80
98
|
|
|
81
99
|
// maintain default format
|
|
82
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
dom.check.isWysiwygFrame(formatEl.parentNode) &&
|
|
102
|
+
formatEl.childNodes.length <= 1 &&
|
|
103
|
+
(!formatEl.firstChild || dom.check.isZeroWidth(formatEl.textContent))
|
|
104
|
+
) {
|
|
83
105
|
actions.push(A.preventStop());
|
|
84
106
|
actions.push(A.backspaceFormatMaintain(formatEl));
|
|
85
107
|
}
|
|
@@ -90,7 +112,14 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
90
112
|
|
|
91
113
|
// clean remove tag
|
|
92
114
|
const startCon = range.startContainer;
|
|
93
|
-
if (
|
|
115
|
+
if (
|
|
116
|
+
formatEl &&
|
|
117
|
+
!bidiNotFront &&
|
|
118
|
+
!formatEl.previousElementSibling &&
|
|
119
|
+
range.startOffset === 0 &&
|
|
120
|
+
startCon.nodeType === 3 &&
|
|
121
|
+
dom.check.isZeroWidth(startCon)
|
|
122
|
+
) {
|
|
94
123
|
if (cleanRemovedTags(ports, startCon, formatEl) === true) return true;
|
|
95
124
|
}
|
|
96
125
|
|
|
@@ -136,8 +165,12 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
136
165
|
dom.check.isListCell(formatEl) &&
|
|
137
166
|
dom.check.isList(rangeEl) &&
|
|
138
167
|
(dom.check.isListCell(rangeEl.parentElement) || formatEl.previousElementSibling) &&
|
|
139
|
-
(selectionNode === formatEl ||
|
|
140
|
-
|
|
168
|
+
(selectionNode === formatEl ||
|
|
169
|
+
(selectionNode.nodeType === 3 &&
|
|
170
|
+
(!selectionNode.previousSibling || dom.check.isList(selectionNode.previousSibling)))) &&
|
|
171
|
+
(format.getLine(range.startContainer, null) !== format.getLine(range.endContainer, null)
|
|
172
|
+
? rangeEl.contains(range.startContainer)
|
|
173
|
+
: range.startOffset === 0 && range.collapsed && !bidiNotFront)
|
|
141
174
|
) {
|
|
142
175
|
if (range.startContainer !== range.endContainer) {
|
|
143
176
|
actions.push(A.prevent());
|
|
@@ -149,10 +182,16 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
149
182
|
actions.push(A.prevent());
|
|
150
183
|
|
|
151
184
|
let prevLast = prev;
|
|
152
|
-
if (
|
|
185
|
+
if (
|
|
186
|
+
!prev.contains(formatEl) &&
|
|
187
|
+
dom.check.isListCell(prevLast) &&
|
|
188
|
+
dom.check.isList(prevLast.lastElementChild)
|
|
189
|
+
) {
|
|
153
190
|
prevLast = /** @type {HTMLLIElement} */ (prevLast.lastElementChild.lastElementChild);
|
|
154
191
|
while (dom.check.isListCell(prevLast) && dom.check.isList(prevLast.lastElementChild)) {
|
|
155
|
-
prevLast = /** @type {HTMLLIElement} */ (
|
|
192
|
+
prevLast = /** @type {HTMLLIElement} */ (
|
|
193
|
+
prevLast.lastElementChild && prevLast.lastElementChild.lastElementChild
|
|
194
|
+
);
|
|
156
195
|
}
|
|
157
196
|
prev = prevLast;
|
|
158
197
|
}
|
|
@@ -171,7 +210,10 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
171
210
|
let comm = commonCon;
|
|
172
211
|
while (comm && comm !== rangeEl && !dom.check.isWysiwygFrame(comm)) {
|
|
173
212
|
if (comm.previousSibling) {
|
|
174
|
-
if (
|
|
213
|
+
if (
|
|
214
|
+
comm.previousSibling.nodeType === 1 ||
|
|
215
|
+
!dom.check.isZeroWidth(comm.previousSibling.textContent.trim())
|
|
216
|
+
) {
|
|
175
217
|
detach = false;
|
|
176
218
|
break;
|
|
177
219
|
}
|
|
@@ -181,7 +223,15 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
181
223
|
|
|
182
224
|
if (detach && rangeEl.parentNode) {
|
|
183
225
|
actions.push(A.prevent());
|
|
184
|
-
actions.push(
|
|
226
|
+
actions.push(
|
|
227
|
+
A.formatRemoveBlock(
|
|
228
|
+
rangeEl,
|
|
229
|
+
dom.check.isListCell(formatEl) ? [formatEl] : null,
|
|
230
|
+
null,
|
|
231
|
+
false,
|
|
232
|
+
false,
|
|
233
|
+
),
|
|
234
|
+
);
|
|
185
235
|
actions.push(A.historyPush(true));
|
|
186
236
|
return true;
|
|
187
237
|
}
|
|
@@ -199,13 +249,25 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
199
249
|
}
|
|
200
250
|
|
|
201
251
|
// component
|
|
202
|
-
if (
|
|
252
|
+
if (
|
|
253
|
+
!selectRange &&
|
|
254
|
+
formatEl &&
|
|
255
|
+
(range.startOffset === 0 || (selectionNode === formatEl ? formatEl.childNodes[range.startOffset] : false))
|
|
256
|
+
) {
|
|
203
257
|
const isList = dom.check.isListCell(formatEl);
|
|
204
258
|
const sel = selectionNode === formatEl ? formatEl.childNodes[range.startOffset] : selectionNode;
|
|
205
259
|
const prev = (isList ? sel : formatEl).previousSibling;
|
|
206
260
|
// select file component
|
|
207
|
-
const ignoreZWS =
|
|
208
|
-
|
|
261
|
+
const ignoreZWS =
|
|
262
|
+
isList ||
|
|
263
|
+
((commonCon.nodeType === 3 || dom.check.isBreak(commonCon)) &&
|
|
264
|
+
!commonCon.previousSibling &&
|
|
265
|
+
range.startOffset === 0);
|
|
266
|
+
if (
|
|
267
|
+
sel &&
|
|
268
|
+
(isList || !sel.previousSibling) &&
|
|
269
|
+
((commonCon && component.is(commonCon.previousSibling)) || (ignoreZWS && component.is(prev)))
|
|
270
|
+
) {
|
|
209
271
|
const fileComponentInfo = component.get(prev);
|
|
210
272
|
if (fileComponentInfo) {
|
|
211
273
|
actions.push(A.preventStop());
|
|
@@ -225,5 +287,38 @@ export function reduceBackspaceDown(actions, ports, ctx) {
|
|
|
225
287
|
}
|
|
226
288
|
}
|
|
227
289
|
|
|
290
|
+
// empty row inside a brLine (PRE): Enter leaves the caret on a bare <br> that begins an empty row
|
|
291
|
+
if (
|
|
292
|
+
!selectRange &&
|
|
293
|
+
format.isBrLine(formatEl) &&
|
|
294
|
+
dom.check.isBreak(range.startContainer) &&
|
|
295
|
+
range.startOffset === 0
|
|
296
|
+
) {
|
|
297
|
+
let rowStartBr = range.startContainer.previousSibling;
|
|
298
|
+
if (rowStartBr?.nodeType === 3 && dom.check.isZeroWidth(rowStartBr)) rowStartBr = rowStartBr.previousSibling;
|
|
299
|
+
if (dom.check.isBreak(rowStartBr)) {
|
|
300
|
+
actions.push(A.preventStop());
|
|
301
|
+
actions.push(A.backspaceBrLineRowMerge(range.startContainer, rowStartBr));
|
|
302
|
+
actions.push(A.historyPush(true));
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// empty line: merge into the previous line
|
|
308
|
+
const emptyLinePrev = formatEl?.previousElementSibling;
|
|
309
|
+
if (
|
|
310
|
+
!selectRange &&
|
|
311
|
+
formatEl &&
|
|
312
|
+
format.isNormalLine(formatEl) &&
|
|
313
|
+
!dom.check.isListCell(formatEl) &&
|
|
314
|
+
dom.check.isEmptyLine(formatEl) &&
|
|
315
|
+
(format.isNormalLine(emptyLinePrev) || format.isBrLine(emptyLinePrev))
|
|
316
|
+
) {
|
|
317
|
+
actions.push(A.preventStop());
|
|
318
|
+
actions.push(A.backspaceEmptyLineMergePrev(formatEl, emptyLinePrev));
|
|
319
|
+
actions.push(A.historyPush(true));
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
228
323
|
return true;
|
|
229
324
|
}
|