Skip to content

Commit 44ea0ba

Browse files
author
Jonathan Bachman
committed
fix(android keyboard): compositionEvents - based on facebookarchive#1774
1 parent 126ce99 commit 44ea0ba

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

src/component/base/DraftEditor.react.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
143143
this._onCharacterData = this._buildHandler('onCharacterData');
144144
this._onCompositionEnd = this._buildHandler('onCompositionEnd');
145145
this._onCompositionStart = this._buildHandler('onCompositionStart');
146+
this._onCompositionUpdate = this._buildHandler('onCompositionUpdate');
146147
this._onCopy = this._buildHandler('onCopy');
147148
this._onCut = this._buildHandler('onCut');
148149
this._onDragEnd = this._buildHandler('onDragEnd');
@@ -287,6 +288,7 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
287288
onBlur={this._onBlur}
288289
onCompositionEnd={this._onCompositionEnd}
289290
onCompositionStart={this._onCompositionStart}
291+
onCompositionUpdate={this._onCompositionUpdate}
290292
onCopy={this._onCopy}
291293
onCut={this._onCut}
292294
onDragEnd={this._onDragEnd}

src/component/handlers/composition/DraftEditorCompositionHandler.js

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const Keys = require('Keys');
2323
const getEntityKeyForSelection = require('getEntityKeyForSelection');
2424
const isEventHandled = require('isEventHandled');
2525
const isSelectionAtLeafStart = require('isSelectionAtLeafStart');
26+
const onInput = require('editOnInput');
2627

2728
/**
2829
* Millisecond delay to allow `compositionstart` to fire again upon
@@ -44,11 +45,13 @@ const RESOLVE_DELAY = 20;
4445
*/
4546
let resolved = false;
4647
let stillComposing = false;
47-
let textInputData = '';
48+
let beforeInputData = null;
49+
let compositionUpdateData = null;
50+
let compositionEndData = null;
4851

4952
var DraftEditorCompositionHandler = {
5053
onBeforeInput: function(editor: DraftEditor, e: SyntheticInputEvent<>): void {
51-
textInputData = (textInputData || '') + e.data;
54+
beforeInputData = (beforeInputData || '') + e.data;
5255
},
5356

5457
/**
@@ -59,6 +62,17 @@ var DraftEditorCompositionHandler = {
5962
stillComposing = true;
6063
},
6164

65+
/**
66+
* A `compositionupdate` event has fired. Update the current composition
67+
* session.
68+
*/
69+
onCompositionUpdate: function(
70+
editor: DraftEditor,
71+
e: SyntheticInputEvent<>,
72+
): void {
73+
compositionUpdateData = e.data;
74+
},
75+
6276
/**
6377
* Attempt to end the current composition session.
6478
*
@@ -73,9 +87,13 @@ var DraftEditorCompositionHandler = {
7387
* twice could break the DOM, we only use the first event. Example: Arabic
7488
* Google Input Tools on Windows 8.1 fires `compositionend` three times.
7589
*/
76-
onCompositionEnd: function(editor: DraftEditor): void {
90+
onCompositionEnd: function(
91+
editor: DraftEditor,
92+
e: SyntheticInputEvent<>,
93+
): void {
7794
resolved = false;
7895
stillComposing = false;
96+
compositionEndData = compositionEndData || e.data;
7997
setTimeout(() => {
8098
if (!resolved) {
8199
DraftEditorCompositionHandler.resolveComposition(editor);
@@ -115,6 +133,34 @@ var DraftEditorCompositionHandler = {
115133
}
116134
},
117135

136+
/**
137+
* Normalizes platform inconsistencies with input event data.
138+
*
139+
* When beforeInputData is present, it is only preferred if its length
140+
* is greater than that of the last compositionUpdate event data. This is
141+
* meant to resolve IME incosistencies where compositionUpdate may contain
142+
* only the last character or the entire composition depending on language
143+
* (e.g. Korean vs. Japanese).
144+
*
145+
* When beforeInputData is not present, compositionUpdate data is preferred.
146+
* This resolves issues with some platforms where beforeInput is never fired
147+
* (e.g. Android with certain keyboard and browser combinations).
148+
*
149+
* Lastly, if neither beforeInput nor compositionUpdate events are fired, use
150+
* the data in the compositionEnd event
151+
*/
152+
normalizeCompositionInput: function(): ?string {
153+
const beforeInputDataLength = beforeInputData ? beforeInputData.length : 0;
154+
const compositionUpdateDataLength = compositionUpdateData
155+
? compositionUpdateData.length
156+
: 0;
157+
const updateData =
158+
beforeInputDataLength > compositionUpdateDataLength
159+
? beforeInputData
160+
: compositionUpdateData;
161+
return updateData || compositionEndData;
162+
},
163+
118164
/**
119165
* Attempt to insert composed characters into the document.
120166
*
@@ -136,13 +182,13 @@ var DraftEditorCompositionHandler = {
136182
}
137183

138184
resolved = true;
139-
const composedChars = textInputData;
140-
textInputData = '';
141-
142-
const editorState = EditorState.set(editor._latestEditorState, {
143-
inCompositionMode: false,
144-
});
185+
let composedChars;
186+
composedChars = this.normalizeCompositionInput();
187+
beforeInputData = null;
188+
compositionUpdateData = null;
189+
compositionEndData = null;
145190

191+
const editorState = editor._latestEditorState;
146192
const currentStyle = editorState.getCurrentInlineStyle();
147193
const entityKey = getEntityKeyForSelection(
148194
editorState.getCurrentContent(),
@@ -186,6 +232,15 @@ var DraftEditorCompositionHandler = {
186232
return;
187233
}
188234

235+
onInput(editor);
236+
editor.restoreEditorDOM();
237+
editor.update(
238+
EditorState.set(editorState, {
239+
nativelyRenderedContent: null,
240+
inCompositionMode: false,
241+
}),
242+
);
243+
189244
if (mustReset) {
190245
editor.update(
191246
EditorState.set(editorState, {

0 commit comments

Comments
 (0)