Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
[draft] Fix typing into text nodes containing Tab
Browse files Browse the repository at this point in the history
Summary:
lolwaffle chrome is a mystery. if you have like

```
<span>ABC\tDEF</span>
```

(where \t is a tab), typing *anywhere* in that span will break the span into two pieces leaving you with like

```
<span>AB</span>x<span>C\tDEF</span>
```

Test Plan: paste "a\tbc" into text field. type into it. no crashes.
  • Loading branch information
sophiebits committed Jul 12, 2017
1 parent 1c6a49b commit 5863399
Showing 1 changed file with 65 additions and 41 deletions.
106 changes: 65 additions & 41 deletions src/component/handlers/edit/editOnBeforeInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function editOnBeforeInput(editor: DraftEditor, e: SyntheticInputEvent): void {
// reduces re-renders and preserves spellcheck highlighting. If the selection
// is not collapsed, we will re-render.
var selection = editorState.getSelection();
var anchorKey = selection.getAnchorKey();

if (!selection.isCollapsed()) {
e.preventDefault();
Expand All @@ -125,9 +126,6 @@ function editOnBeforeInput(editor: DraftEditor, e: SyntheticInputEvent): void {
return;
}

var mayAllowNative = !isSelectionAtLeafStart(
editor._latestCommittedEditorState,
);
var newEditorState = replaceText(
editorState,
chars,
Expand All @@ -138,49 +136,75 @@ function editOnBeforeInput(editor: DraftEditor, e: SyntheticInputEvent): void {
),
);

if (!mayAllowNative) {
e.preventDefault();
editor.update(newEditorState);
return;
// Bunch of different cases follow where we need to prevent native insertion.
let mustPreventNative = false;
if (!mustPreventNative) {
// Browsers tend to insert text in weird places in the DOM when typing at
// the start of a leaf, so we'll handle it ourselves.
mustPreventNative = isSelectionAtLeafStart(
editor._latestCommittedEditorState,
);
}

var anchorKey = selection.getAnchorKey();
var anchorTree = editorState.getBlockTree(anchorKey);

// Check the old and new "fingerprints" of the current block to determine
// whether this insertion requires any addition or removal of text nodes,
// in which case we would prevent the native character insertion.
var originalFingerprint = BlockTree.getFingerprint(anchorTree);
var newFingerprint = BlockTree.getFingerprint(
newEditorState.getBlockTree(anchorKey),
);

if (
mustPreventDefaultForCharacter(chars) ||
originalFingerprint !== newFingerprint ||
(
if (!mustPreventNative) {
// Chrome will also split up a node into two pieces if it contains a Tab
// char, for no explicable reason. Seemingly caused by this commit:
// https://chromium.googlesource.com/chromium/src/+/013ac5eaf3%5E%21/
const nativeSelection = global.getSelection();
// Selection is necessarily collapsed at this point due to earlier check.
if (
nativeSelection.anchorNode !== null &&
nativeSelection.anchorNode.nodeType === Node.TEXT_NODE
) {
// See isTabHTMLSpanElement in chromium EditingUtilities.cpp.
const parentNode = nativeSelection.anchorNode.parentNode;
mustPreventNative =
parentNode.nodeName === 'SPAN' &&
parentNode.firstChild.nodeType === Node.TEXT_NODE &&
parentNode.firstChild.nodeValue.indexOf('\t') !== -1;
}
}
if (!mustPreventNative) {
// Check the old and new "fingerprints" of the current block to determine
// whether this insertion requires any addition or removal of text nodes,
// in which case we would prevent the native character insertion.
var originalFingerprint = BlockTree.getFingerprint(
editorState.getBlockTree(anchorKey)
);
var newFingerprint = BlockTree.getFingerprint(
newEditorState.getBlockTree(anchorKey),
);
mustPreventNative = originalFingerprint !== newFingerprint;
}
if (!mustPreventNative) {
mustPreventNative = mustPreventDefaultForCharacter(chars);
}
if (!mustPreventNative) {
mustPreventNative =
nullthrows(newEditorState.getDirectionMap()).get(anchorKey) !==
nullthrows(editorState.getDirectionMap()).get(anchorKey)
)
) {
nullthrows(editorState.getDirectionMap()).get(anchorKey);
}

if (mustPreventNative) {
e.preventDefault();
editor.update(newEditorState);
} else {
newEditorState = EditorState.set(newEditorState, {
nativelyRenderedContent: newEditorState.getCurrentContent(),
});
// The native event is allowed to occur. To allow user onChange handlers to
// change the inserted text, we wait until the text is actually inserted
// before we actually update our state. That way when we rerender, the text
// we see in the DOM will already have been inserted properly.
editor._pendingStateFromBeforeInput = newEditorState;
setImmediate(() => {
if (editor._pendingStateFromBeforeInput !== undefined) {
editor.update(editor._pendingStateFromBeforeInput);
editor._pendingStateFromBeforeInput = undefined;
}
});
return;
}

// We made it all the way! Let the browser do its thing and insert the char.
newEditorState = EditorState.set(newEditorState, {
nativelyRenderedContent: newEditorState.getCurrentContent(),
});
// The native event is allowed to occur. To allow user onChange handlers to
// change the inserted text, we wait until the text is actually inserted
// before we actually update our state. That way when we rerender, the text
// we see in the DOM will already have been inserted properly.
editor._pendingStateFromBeforeInput = newEditorState;
setImmediate(() => {
if (editor._pendingStateFromBeforeInput !== undefined) {
editor.update(editor._pendingStateFromBeforeInput);
editor._pendingStateFromBeforeInput = undefined;
}
});
}

module.exports = editOnBeforeInput;

0 comments on commit 5863399

Please sign in to comment.