/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
* @emails oncall+draft_js
*/
'use strict';
const React = require("react");
const UserAgent = require("fbjs/lib/UserAgent");
const invariant = require("fbjs/lib/invariant");
const isElement = require("./isElement"); // In IE, spans with
tags render as two newlines. By rendering a span
// with only a newline character, we can be sure to render a single line.
const useNewlineChar = UserAgent.isBrowser('IE <= 11');
/**
* Check whether the node should be considered a newline.
*/
function isNewline(node: Element): boolean {
return useNewlineChar ? node.textContent === '\n' : node.tagName === 'BR';
}
/**
* Placeholder elements for empty text content.
*
* What is this `data-text` attribute, anyway? It turns out that we need to
* put an attribute on the lowest-level text node in order to preserve correct
* spellcheck handling. If the is naked, Chrome and Safari may do
* bizarre things to do the DOM -- split text nodes, create extra spans, etc.
* If the has an attribute, this appears not to happen.
* See http://jsfiddle.net/9khdavod/ for the failure case, and
* http://jsfiddle.net/7pg143f7/ for the fixed case.
*/
const NEWLINE_A = ref => useNewlineChar ?
{'\n'}
:
;
const NEWLINE_B = ref => useNewlineChar ?
{'\n'}
:
;
type Props = {
children: string,
...
};
/**
* The lowest-level component in a `DraftEditor`, the text node component
* replaces the default React text node implementation. This allows us to
* perform custom handling of newline behavior and avoid re-rendering text
* nodes with DOM state that already matches the expectations of our immutable
* editor state.
*/
class DraftEditorTextNode extends React.Component {
_forceFlag: boolean;
_node: ?(HTMLSpanElement | HTMLBRElement);
constructor(props: Props) {
super(props); // By flipping this flag, we also keep flipping keys which forces
// React to remount this node every time it rerenders.
this._forceFlag = false;
}
shouldComponentUpdate(nextProps: Props): boolean {
const node = this._node;
const shouldBeNewline = nextProps.children === '';
invariant(isElement(node), 'node is not an Element');
const elementNode: Element = (node: any);
if (shouldBeNewline) {
return !isNewline(elementNode);
}
return elementNode.textContent !== nextProps.children;
}
componentDidMount(): void {
this._forceFlag = !this._forceFlag;
}
componentDidUpdate(): void {
this._forceFlag = !this._forceFlag;
}
render(): React.Node {
if (this.props.children === '') {
return this._forceFlag ? NEWLINE_A(ref => this._node = ref) : NEWLINE_B(ref => this._node = ref);
}
return this._node = ref}>
{this.props.children}
;
}
}
module.exports = DraftEditorTextNode;