@ckeditor/ckeditor5-typing 45.0.0 → 45.1.0-alpha.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/dist/index.js +143 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/delete.js +19 -0
- package/src/input.d.ts +1 -1
- package/src/input.js +116 -108
- package/src/inserttextobserver.d.ts +9 -0
- package/src/inserttextobserver.js +7 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-typing",
|
|
3
|
-
"version": "45.0.0",
|
|
3
|
+
"version": "45.1.0-alpha.0",
|
|
4
4
|
"description": "Typing feature for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "src/index.js",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@ckeditor/ckeditor5-core": "45.0.0",
|
|
17
|
-
"@ckeditor/ckeditor5-engine": "45.0.0",
|
|
18
|
-
"@ckeditor/ckeditor5-utils": "45.0.0",
|
|
16
|
+
"@ckeditor/ckeditor5-core": "45.1.0-alpha.0",
|
|
17
|
+
"@ckeditor/ckeditor5-engine": "45.1.0-alpha.0",
|
|
18
|
+
"@ckeditor/ckeditor5-utils": "45.1.0-alpha.0",
|
|
19
19
|
"es-toolkit": "1.32.0"
|
|
20
20
|
},
|
|
21
21
|
"author": "CKSource (http://cksource.com/)",
|
package/src/delete.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module typing/delete
|
|
7
|
+
*/
|
|
8
|
+
import { BubblingEventInfo, DomEventData } from '@ckeditor/ckeditor5-engine';
|
|
5
9
|
import { Plugin } from '@ckeditor/ckeditor5-core';
|
|
6
10
|
import { keyCodes } from '@ckeditor/ckeditor5-utils';
|
|
7
11
|
import DeleteCommand from './deletecommand.js';
|
|
@@ -73,7 +77,22 @@ export default class Delete extends Plugin {
|
|
|
73
77
|
const ancestorLimit = editor.model.schema.getLimitElement(modelDocument.selection);
|
|
74
78
|
const limitStartPosition = editor.model.createPositionAt(ancestorLimit, 0);
|
|
75
79
|
if (limitStartPosition.isTouching(modelDocument.selection.getFirstPosition())) {
|
|
80
|
+
// Stop the beforeinput event as it could be invalid.
|
|
76
81
|
data.preventDefault();
|
|
82
|
+
// Create a fake delete event so all features can act on it and the target range is proper.
|
|
83
|
+
const modelRange = editor.model.schema.getNearestSelectionRange(limitStartPosition, 'forward');
|
|
84
|
+
if (!modelRange) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const viewSelection = view.createSelection(editor.editing.mapper.toViewRange(modelRange));
|
|
88
|
+
const targetRange = viewSelection.getFirstRange();
|
|
89
|
+
const eventInfo = new BubblingEventInfo(document, 'delete', targetRange);
|
|
90
|
+
const deleteData = {
|
|
91
|
+
unit: 'selection',
|
|
92
|
+
direction: 'backward',
|
|
93
|
+
selectionToRemove: viewSelection
|
|
94
|
+
};
|
|
95
|
+
viewDocument.fire(eventInfo, new DomEventData(view, data.domEvent, deleteData));
|
|
77
96
|
}
|
|
78
97
|
});
|
|
79
98
|
if (this.editor.plugins.has('UndoEditing')) {
|
package/src/input.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export default class Input extends Plugin {
|
|
|
13
13
|
/**
|
|
14
14
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
15
15
|
*/
|
|
16
|
-
private
|
|
16
|
+
private _typingQueue;
|
|
17
17
|
/**
|
|
18
18
|
* @inheritDoc
|
|
19
19
|
*/
|
package/src/input.js
CHANGED
|
@@ -19,7 +19,7 @@ export default class Input extends Plugin {
|
|
|
19
19
|
/**
|
|
20
20
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
_typingQueue;
|
|
23
23
|
/**
|
|
24
24
|
* @inheritDoc
|
|
25
25
|
*/
|
|
@@ -41,25 +41,34 @@ export default class Input extends Plugin {
|
|
|
41
41
|
const view = editor.editing.view;
|
|
42
42
|
const mapper = editor.editing.mapper;
|
|
43
43
|
const modelSelection = model.document.selection;
|
|
44
|
-
this.
|
|
44
|
+
this._typingQueue = new TypingQueue(editor);
|
|
45
45
|
view.addObserver(InsertTextObserver);
|
|
46
46
|
// TODO The above default configuration value should be defined using editor.config.define() once it's fixed.
|
|
47
47
|
const insertTextCommand = new InsertTextCommand(editor, editor.config.get('typing.undoStep') || 20);
|
|
48
48
|
// Register `insertText` command and add `input` command as an alias for backward compatibility.
|
|
49
49
|
editor.commands.add('insertText', insertTextCommand);
|
|
50
50
|
editor.commands.add('input', insertTextCommand);
|
|
51
|
+
this.listenTo(view.document, 'beforeinput', () => {
|
|
52
|
+
// Flush queue on the next beforeinput event because it could happen
|
|
53
|
+
// that the mutation observer does not notice the DOM change in time.
|
|
54
|
+
this._typingQueue.flush('next beforeinput');
|
|
55
|
+
}, { priority: 'high' });
|
|
51
56
|
this.listenTo(view.document, 'insertText', (evt, data) => {
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
if (
|
|
57
|
+
const { text, selection: viewSelection } = data;
|
|
58
|
+
// In case of a synthetic event, make sure that selection is not fake.
|
|
59
|
+
if (view.document.selection.isFake && viewSelection && view.document.selection.isSimilar(viewSelection)) {
|
|
55
60
|
data.preventDefault();
|
|
56
61
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
if (!insertTextCommand.isEnabled) {
|
|
63
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
64
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
65
|
+
// @if CK_DEBUG_TYPING // '%cInsertText command is disabled - prevent DOM change.',
|
|
66
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
67
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
68
|
+
// @if CK_DEBUG_TYPING // }
|
|
69
|
+
data.preventDefault();
|
|
70
|
+
return;
|
|
61
71
|
}
|
|
62
|
-
const { text, selection: viewSelection } = data;
|
|
63
72
|
let modelRanges;
|
|
64
73
|
// If view selection was specified, translate it to model selection.
|
|
65
74
|
if (viewSelection) {
|
|
@@ -100,41 +109,31 @@ export default class Input extends Plugin {
|
|
|
100
109
|
return;
|
|
101
110
|
}
|
|
102
111
|
}
|
|
112
|
+
// Note: the TypingQueue stores live-ranges internally as RTC could change the model while waiting for mutations.
|
|
103
113
|
const commandData = {
|
|
104
114
|
text: insertText,
|
|
105
115
|
selection: model.createSelection(modelRanges)
|
|
106
116
|
};
|
|
107
|
-
// This is a
|
|
117
|
+
// This is a beforeinput event, so we need to wait until the browser updates the DOM,
|
|
108
118
|
// and we could apply changes to the model and verify if the DOM is valid.
|
|
109
119
|
// The browser applies changes to the DOM not immediately on beforeinput event.
|
|
110
120
|
// We just wait for mutation observer to notice changes or as a fallback a timeout.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
128
|
-
// @if CK_DEBUG_TYPING // `%cExecute insertText:%c "${ commandData.text }"%c ` +
|
|
129
|
-
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getFirstPosition().path }]-` +
|
|
130
|
-
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getLastPosition().path }]`,
|
|
131
|
-
// @if CK_DEBUG_TYPING // 'font-weight: bold',
|
|
132
|
-
// @if CK_DEBUG_TYPING // 'color: blue',
|
|
133
|
-
// @if CK_DEBUG_TYPING // ''
|
|
134
|
-
// @if CK_DEBUG_TYPING // ) );
|
|
135
|
-
// @if CK_DEBUG_TYPING // }
|
|
136
|
-
editor.execute('insertText', commandData);
|
|
137
|
-
view.scrollToTheSelection();
|
|
121
|
+
//
|
|
122
|
+
// Previously we were cancelling the non-composition events, but it caused issues especially in Safari.
|
|
123
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
124
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
125
|
+
// @if CK_DEBUG_TYPING // `%cQueue insertText:%c "${ commandData.text }"%c ` +
|
|
126
|
+
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getFirstPosition().path }]-` +
|
|
127
|
+
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getLastPosition().path }]` +
|
|
128
|
+
// @if CK_DEBUG_TYPING // ` queue size: ${ this._typingQueue.length + 1 }`,
|
|
129
|
+
// @if CK_DEBUG_TYPING // 'font-weight: bold',
|
|
130
|
+
// @if CK_DEBUG_TYPING // 'color: blue',
|
|
131
|
+
// @if CK_DEBUG_TYPING // ''
|
|
132
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
133
|
+
// @if CK_DEBUG_TYPING // }
|
|
134
|
+
this._typingQueue.push(commandData, Boolean(data.isComposing));
|
|
135
|
+
if (data.domEvent.defaultPrevented) {
|
|
136
|
+
this._typingQueue.flush('beforeinput default prevented');
|
|
138
137
|
}
|
|
139
138
|
});
|
|
140
139
|
// Delete selected content on composition start.
|
|
@@ -177,102 +176,91 @@ export default class Input extends Plugin {
|
|
|
177
176
|
// @if CK_DEBUG_TYPING // ) );
|
|
178
177
|
// @if CK_DEBUG_TYPING // }
|
|
179
178
|
deleteSelectionContent(model, insertTextCommand);
|
|
180
|
-
});
|
|
179
|
+
}, { priority: 'high' });
|
|
181
180
|
}
|
|
182
|
-
// Apply
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
this.
|
|
187
|
-
if (!view.document.isComposing) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
// Check if mutations are relevant for queued changes.
|
|
181
|
+
// Apply changes to the model as they are applied to the DOM by the browser.
|
|
182
|
+
// On beforeinput event, the DOM is not yet modified. We wait for detected mutations to apply model changes.
|
|
183
|
+
this.listenTo(view.document, 'mutations', (evt, { mutations }) => {
|
|
184
|
+
// Check if mutations are relevant for queued changes.
|
|
185
|
+
if (this._typingQueue.hasAffectedElements()) {
|
|
191
186
|
for (const { node } of mutations) {
|
|
192
187
|
const viewElement = findMappedViewAncestor(node, mapper);
|
|
193
188
|
const modelElement = mapper.toModelElement(viewElement);
|
|
194
|
-
if (this.
|
|
195
|
-
this.
|
|
189
|
+
if (this._typingQueue.isElementAffected(modelElement)) {
|
|
190
|
+
this._typingQueue.flush('mutations');
|
|
196
191
|
return;
|
|
197
192
|
}
|
|
198
193
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
194
|
+
}
|
|
195
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
196
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
197
|
+
// @if CK_DEBUG_TYPING // '%cMutations not related to the composition.',
|
|
198
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
199
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
200
|
+
// @if CK_DEBUG_TYPING // }
|
|
201
|
+
});
|
|
202
|
+
// Make sure that all changes are applied to the model before the end of composition.
|
|
203
|
+
this.listenTo(view.document, 'compositionend', () => {
|
|
204
|
+
this._typingQueue.flush('before composition end');
|
|
205
|
+
}, { priority: 'high' });
|
|
206
|
+
// Trigger mutations check after the composition completes to fix all DOM changes that got ignored during composition.
|
|
207
|
+
// On Android, the Renderer is not disabled while composing. While updating DOM nodes, we ignore some changes
|
|
208
|
+
// that are not that important (like NBSP vs. plain space character) and could break the composition flow.
|
|
209
|
+
// After composition is completed, we trigger additional `mutations` event for elements affected by the composition
|
|
210
|
+
// so the Renderer can adjust the DOM to the expected structure without breaking the composition.
|
|
211
|
+
this.listenTo(view.document, 'compositionend', () => {
|
|
212
|
+
// There could be new item queued on the composition end, so flush it.
|
|
213
|
+
this._typingQueue.flush('after composition end');
|
|
214
|
+
const mutations = [];
|
|
215
|
+
if (this._typingQueue.hasAffectedElements()) {
|
|
216
|
+
for (const element of this._typingQueue.flushAffectedElements()) {
|
|
218
217
|
const viewElement = mapper.toViewElement(element);
|
|
219
218
|
if (!viewElement) {
|
|
220
219
|
continue;
|
|
221
220
|
}
|
|
222
221
|
mutations.push({ type: 'children', node: viewElement });
|
|
223
222
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// @if CK_DEBUG_TYPING // }
|
|
231
|
-
view.document.fire('mutations', { mutations });
|
|
232
|
-
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
233
|
-
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
234
|
-
// @if CK_DEBUG_TYPING // }
|
|
235
|
-
}
|
|
236
|
-
}, { priority: 'lowest' });
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
// After composition end we need to verify if there are no left-overs.
|
|
240
|
-
// Listening at the lowest priority so after the `InsertTextObserver` added above (all composed text
|
|
223
|
+
}
|
|
224
|
+
// Fire composition mutations, if any.
|
|
225
|
+
//
|
|
226
|
+
// For non-Android:
|
|
227
|
+
// After the composition end, we need to verify if there are no left-overs.
|
|
228
|
+
// Listening at the lowest priority, so after the `InsertTextObserver` added above (all composed text
|
|
241
229
|
// should already be applied to the model, view, and DOM).
|
|
242
|
-
// On non-Android the `Renderer` is blocked while user is composing but the `MutationObserver` still collects
|
|
230
|
+
// On non-Android the `Renderer` is blocked while the user is composing, but the `MutationObserver` still collects
|
|
243
231
|
// mutated nodes and fires `mutations` events.
|
|
244
232
|
// Those events are recorded by the `Renderer` but not applied to the DOM while composing.
|
|
245
233
|
// We need to trigger those checks (and fixes) once again but this time without specifying the exact mutations
|
|
246
234
|
// since they are already recorded by the `Renderer`.
|
|
247
|
-
// It in
|
|
235
|
+
// It in most cases just clears the internal record of mutated text nodes
|
|
248
236
|
// since all changes should already be applied to the DOM.
|
|
249
|
-
// This is especially needed when user cancels composition, so we can clear nodes marked to sync.
|
|
250
|
-
|
|
237
|
+
// This is especially needed when a user cancels composition, so we can clear nodes marked to sync.
|
|
238
|
+
if (mutations.length || !env.isAndroid) {
|
|
251
239
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
252
240
|
// @if CK_DEBUG_TYPING // console.group( ..._buildLogMessage( this, 'Input',
|
|
253
|
-
// @if CK_DEBUG_TYPING // '%
|
|
241
|
+
// @if CK_DEBUG_TYPING // '%cFire post-composition mutation fixes.',
|
|
254
242
|
// @if CK_DEBUG_TYPING // 'font-weight: bold'
|
|
255
243
|
// @if CK_DEBUG_TYPING // ) );
|
|
256
244
|
// @if CK_DEBUG_TYPING // }
|
|
257
|
-
view.document.fire('mutations', { mutations
|
|
245
|
+
view.document.fire('mutations', { mutations });
|
|
258
246
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
259
247
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
260
248
|
// @if CK_DEBUG_TYPING // }
|
|
261
|
-
}
|
|
262
|
-
}
|
|
249
|
+
}
|
|
250
|
+
}, { priority: 'lowest' });
|
|
263
251
|
}
|
|
264
252
|
/**
|
|
265
253
|
* @inheritDoc
|
|
266
254
|
*/
|
|
267
255
|
destroy() {
|
|
268
256
|
super.destroy();
|
|
269
|
-
this.
|
|
257
|
+
this._typingQueue.destroy();
|
|
270
258
|
}
|
|
271
259
|
}
|
|
272
260
|
/**
|
|
273
261
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
274
262
|
*/
|
|
275
|
-
class
|
|
263
|
+
class TypingQueue {
|
|
276
264
|
/**
|
|
277
265
|
* The editor instance.
|
|
278
266
|
*/
|
|
@@ -286,9 +274,13 @@ class CompositionQueue {
|
|
|
286
274
|
*/
|
|
287
275
|
_queue = [];
|
|
288
276
|
/**
|
|
289
|
-
*
|
|
277
|
+
* Whether there is any composition enqueued or plain typing only.
|
|
278
|
+
*/
|
|
279
|
+
_isComposing = false;
|
|
280
|
+
/**
|
|
281
|
+
* A set of model elements. The typing happened in those elements. It's used for mutations check.
|
|
290
282
|
*/
|
|
291
|
-
|
|
283
|
+
_affectedElements = new Set();
|
|
292
284
|
/**
|
|
293
285
|
* @inheritDoc
|
|
294
286
|
*/
|
|
@@ -300,7 +292,7 @@ class CompositionQueue {
|
|
|
300
292
|
*/
|
|
301
293
|
destroy() {
|
|
302
294
|
this.flushDebounced.cancel();
|
|
303
|
-
this.
|
|
295
|
+
this._affectedElements.clear();
|
|
304
296
|
while (this._queue.length) {
|
|
305
297
|
this.shift();
|
|
306
298
|
}
|
|
@@ -314,7 +306,7 @@ class CompositionQueue {
|
|
|
314
306
|
/**
|
|
315
307
|
* Push next insertText command data to the queue.
|
|
316
308
|
*/
|
|
317
|
-
push(commandData) {
|
|
309
|
+
push(commandData, isComposing) {
|
|
318
310
|
const commandLiveData = {
|
|
319
311
|
text: commandData.text
|
|
320
312
|
};
|
|
@@ -323,10 +315,11 @@ class CompositionQueue {
|
|
|
323
315
|
for (const range of commandData.selection.getRanges()) {
|
|
324
316
|
commandLiveData.selectionRanges.push(LiveRange.fromRange(range));
|
|
325
317
|
// Keep reference to the model element for later mutation checks.
|
|
326
|
-
this.
|
|
318
|
+
this._affectedElements.add(range.start.parent);
|
|
327
319
|
}
|
|
328
320
|
}
|
|
329
321
|
this._queue.push(commandLiveData);
|
|
322
|
+
this._isComposing ||= isComposing;
|
|
330
323
|
this.flushDebounced();
|
|
331
324
|
}
|
|
332
325
|
/**
|
|
@@ -385,6 +378,15 @@ class CompositionQueue {
|
|
|
385
378
|
editor.execute('insertText', commandData);
|
|
386
379
|
}
|
|
387
380
|
buffer.unlock();
|
|
381
|
+
if (!this._isComposing) {
|
|
382
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
383
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
384
|
+
// @if CK_DEBUG_TYPING // 'Clear affected elements set'
|
|
385
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
386
|
+
// @if CK_DEBUG_TYPING // }
|
|
387
|
+
this._affectedElements.clear();
|
|
388
|
+
}
|
|
389
|
+
this._isComposing = false;
|
|
388
390
|
});
|
|
389
391
|
view.scrollToTheSelection();
|
|
390
392
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
@@ -392,17 +394,23 @@ class CompositionQueue {
|
|
|
392
394
|
// @if CK_DEBUG_TYPING // }
|
|
393
395
|
}
|
|
394
396
|
/**
|
|
395
|
-
* Returns `true` if the given model element is related to recent
|
|
397
|
+
* Returns `true` if the given model element is related to recent typing.
|
|
398
|
+
*/
|
|
399
|
+
isElementAffected(element) {
|
|
400
|
+
return this._affectedElements.has(element);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Returns `true` if there are any affected elements in the queue.
|
|
396
404
|
*/
|
|
397
|
-
|
|
398
|
-
return this.
|
|
405
|
+
hasAffectedElements() {
|
|
406
|
+
return this._affectedElements.size > 0;
|
|
399
407
|
}
|
|
400
408
|
/**
|
|
401
|
-
* Returns an array of
|
|
409
|
+
* Returns an array of typing-related elements and clears the internal list.
|
|
402
410
|
*/
|
|
403
|
-
|
|
404
|
-
const result = Array.from(this.
|
|
405
|
-
this.
|
|
411
|
+
flushAffectedElements() {
|
|
412
|
+
const result = Array.from(this._affectedElements);
|
|
413
|
+
this._affectedElements.clear();
|
|
406
414
|
return result;
|
|
407
415
|
}
|
|
408
416
|
}
|
|
@@ -52,4 +52,13 @@ export interface InsertTextEventData extends DomEventData {
|
|
|
52
52
|
* If not specified, the insertion should occur at the current view selection.
|
|
53
53
|
*/
|
|
54
54
|
selection?: ViewSelection | ViewDocumentSelection;
|
|
55
|
+
/**
|
|
56
|
+
* A flag indicating that event was fired during composition.
|
|
57
|
+
*
|
|
58
|
+
* Corresponds to the
|
|
59
|
+
* {@link module:engine/view/document~Document#event:compositionstart},
|
|
60
|
+
* {@link module:engine/view/document~Document#event:compositionupdate},
|
|
61
|
+
* and {@link module:engine/view/document~Document#event:compositionend } trio.
|
|
62
|
+
*/
|
|
63
|
+
isComposing?: boolean;
|
|
55
64
|
}
|
|
@@ -48,7 +48,7 @@ export default class InsertTextObserver extends Observer {
|
|
|
48
48
|
if (!this.isEnabled) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
const { data: text, targetRanges, inputType, domEvent } = data;
|
|
51
|
+
const { data: text, targetRanges, inputType, domEvent, isComposing } = data;
|
|
52
52
|
if (!typingInputTypes.includes(inputType)) {
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
@@ -58,7 +58,8 @@ export default class InsertTextObserver extends Observer {
|
|
|
58
58
|
const eventInfo = new EventInfo(viewDocument, 'insertText');
|
|
59
59
|
viewDocument.fire(eventInfo, new DomEventData(view, domEvent, {
|
|
60
60
|
text,
|
|
61
|
-
selection: view.createSelection(targetRanges)
|
|
61
|
+
selection: view.createSelection(targetRanges),
|
|
62
|
+
isComposing
|
|
62
63
|
}));
|
|
63
64
|
// Stop the beforeinput event if `delete` event was stopped.
|
|
64
65
|
// https://github.com/ckeditor/ckeditor5/issues/753
|
|
@@ -92,11 +93,12 @@ export default class InsertTextObserver extends Observer {
|
|
|
92
93
|
// 1. The SelectionObserver is blocked and the view is not updated with the composition changes.
|
|
93
94
|
// 2. The last moment before it's locked is the `compositionstart` event.
|
|
94
95
|
// 3. The `SelectionObserver` is listening for `compositionstart` event and immediately converts
|
|
95
|
-
// the selection.
|
|
96
|
+
// the selection. Handle this at the low priority so after the rendering is blocked.
|
|
96
97
|
viewDocument.fire('insertText', new DomEventData(view, domEvent, {
|
|
97
|
-
text: data
|
|
98
|
+
text: data,
|
|
99
|
+
isComposing: true
|
|
98
100
|
}));
|
|
99
|
-
}, { priority: '
|
|
101
|
+
}, { priority: 'low' });
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
/**
|