/** * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule DraftEditorLeaf.react * @format * */ 'use strict'; var _assign = require('object-assign'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var DraftEditorTextNode = require('./DraftEditorTextNode.react'); var React = require('react'); var ReactDOM = require('react-dom'); var invariant = require('fbjs/lib/invariant'); var setDraftEditorSelection = require('./setDraftEditorSelection'); /** * All leaf nodes in the editor are spans with single text nodes. Leaf * elements are styled based on the merging of an optional custom style map * and a default style map. * * `DraftEditorLeaf` also provides a wrapper for calling into the imperative * DOM Selection API. In this way, top-level components can declaratively * maintain the selection state. */ var DraftEditorLeaf = function (_React$Component) { _inherits(DraftEditorLeaf, _React$Component); function DraftEditorLeaf() { _classCallCheck(this, DraftEditorLeaf); return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); } DraftEditorLeaf.prototype._setSelection = function _setSelection() { var selection = this.props.selection; // If selection state is irrelevant to the parent block, no-op. if (selection == null || !selection.getHasFocus()) { return; } var _props = this.props, block = _props.block, start = _props.start, text = _props.text; var blockKey = block.getKey(); var end = start + text.length; if (!selection.hasEdgeWithin(blockKey, start, end)) { return; } // Determine the appropriate target node for selection. If the child // is not a text node, it is a
spacer. In this case, use the // itself as the selection target. var node = ReactDOM.findDOMNode(this); !node ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing node') : invariant(false) : void 0; var child = node.firstChild; !child ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing child') : invariant(false) : void 0; var targetNode = void 0; if (child.nodeType === Node.TEXT_NODE) { targetNode = child; } else if (child.tagName === 'BR') { targetNode = node; } else { targetNode = child.firstChild; !targetNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing targetNode') : invariant(false) : void 0; } setDraftEditorSelection(selection, targetNode, blockKey, start, end); }; /** * By making individual leaf instances aware of their context within * the text of the editor, we can set our selection range more * easily than we could in the non-React world. * * Note that this depends on our maintaining tight control over the * DOM structure of the DraftEditor component. If leaves had multiple * text nodes, this would be harder. */ DraftEditorLeaf.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) { var leafNode = ReactDOM.findDOMNode(this.leaf); !leafNode ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Missing leafNode') : invariant(false) : void 0; return leafNode.textContent !== nextProps.text || nextProps.styleSet !== this.props.styleSet || nextProps.forceSelection; }; DraftEditorLeaf.prototype.componentDidUpdate = function componentDidUpdate() { this._setSelection(); }; DraftEditorLeaf.prototype.componentDidMount = function componentDidMount() { this._setSelection(); }; DraftEditorLeaf.prototype.render = function render() { var _this2 = this; var block = this.props.block; var text = this.props.text; // If the leaf is at the end of its block and ends in a soft newline, append // an extra line feed character. Browsers collapse trailing newline // characters, which leaves the cursor in the wrong place after a // shift+enter. The extra character repairs this. if (text.endsWith('\n') && this.props.isLast) { text += '\n'; } var _props2 = this.props, customStyleMap = _props2.customStyleMap, customStyleFn = _props2.customStyleFn, offsetKey = _props2.offsetKey, styleSet = _props2.styleSet; var styleObj = styleSet.reduce(function (map, styleName) { var mergedStyles = {}; var style = customStyleMap[styleName]; if (style !== undefined && map.textDecoration !== style.textDecoration) { // .trim() is necessary for IE9/10/11 and Edge mergedStyles.textDecoration = [map.textDecoration, style.textDecoration].join(' ').trim(); } return _assign(map, style, mergedStyles); }, {}); if (customStyleFn) { var newStyles = customStyleFn(styleSet, block); styleObj = _assign(styleObj, newStyles); } return React.createElement( 'span', { 'data-offset-key': offsetKey, ref: function ref(_ref) { return _this2.leaf = _ref; }, style: styleObj }, React.createElement( DraftEditorTextNode, null, text ) ); }; return DraftEditorLeaf; }(React.Component); module.exports = DraftEditorLeaf;