/** * 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 * * This file is a fork of DraftEditorContents.react.js for tree nodes * * This is unstable and not part of the public API and should not be used by * production systems. This file may be update/removed without notice. */ 'use strict'; import type { BlockNodeRecord } from "./BlockNodeRecord"; import type { DraftBlockRenderMap } from "./DraftBlockRenderMap"; import type { DraftInlineStyle } from "./DraftInlineStyle"; import type EditorState from "./EditorState"; import type { BidiDirection } from "fbjs/lib/UnicodeBidiDirection"; const DraftEditorBlockNode = require("./DraftEditorBlockNode.react"); const DraftOffsetKey = require("./DraftOffsetKey"); const React = require("react"); const nullthrows = require("fbjs/lib/nullthrows"); type Props = { blockRenderMap: DraftBlockRenderMap, blockRendererFn: (block: BlockNodeRecord) => ?Object, blockStyleFn?: (block: BlockNodeRecord) => string, customStyleFn?: (style: DraftInlineStyle, block: BlockNodeRecord) => ?Object, customStyleMap?: Object, editorKey?: string, editorState: EditorState, textDirectionality?: BidiDirection, ... }; /** * `DraftEditorContents` is the container component for all block components * rendered for a `DraftEditor`. It is optimized to aggressively avoid * re-rendering blocks whenever possible. * * This component is separate from `DraftEditor` because certain props * (for instance, ARIA props) must be allowed to update without affecting * the contents of the editor. */ class DraftEditorContentsExperimental extends React.Component { shouldComponentUpdate(nextProps: Props): boolean { const prevEditorState = this.props.editorState; const nextEditorState = nextProps.editorState; const prevDirectionMap = prevEditorState.getDirectionMap(); const nextDirectionMap = nextEditorState.getDirectionMap(); // Text direction has changed for one or more blocks. We must re-render. if (prevDirectionMap !== nextDirectionMap) { return true; } const didHaveFocus = prevEditorState.getSelection().getHasFocus(); const nowHasFocus = nextEditorState.getSelection().getHasFocus(); if (didHaveFocus !== nowHasFocus) { return true; } const nextNativeContent = nextEditorState.getNativelyRenderedContent(); const wasComposing = prevEditorState.isInCompositionMode(); const nowComposing = nextEditorState.isInCompositionMode(); // If the state is unchanged or we're currently rendering a natively // rendered state, there's nothing new to be done. if (prevEditorState === nextEditorState || nextNativeContent !== null && nextEditorState.getCurrentContent() === nextNativeContent || wasComposing && nowComposing) { return false; } const prevContent = prevEditorState.getCurrentContent(); const nextContent = nextEditorState.getCurrentContent(); const prevDecorator = prevEditorState.getDecorator(); const nextDecorator = nextEditorState.getDecorator(); return wasComposing !== nowComposing || prevContent !== nextContent || prevDecorator !== nextDecorator || nextEditorState.mustForceSelection(); } render(): React.Node { const { blockRenderMap, blockRendererFn, blockStyleFn, customStyleMap, customStyleFn, editorState, editorKey, textDirectionality } = this.props; const content = editorState.getCurrentContent(); const selection = editorState.getSelection(); const forceSelection = editorState.mustForceSelection(); const decorator = editorState.getDecorator(); const directionMap = nullthrows(editorState.getDirectionMap()); const blocksAsArray = content.getBlocksAsArray(); const rootBlock = blocksAsArray[0]; const processedBlocks = []; let nodeBlock = rootBlock; while (nodeBlock) { const blockKey = nodeBlock.getKey(); const blockProps = { blockRenderMap, blockRendererFn, blockStyleFn, contentState: content, customStyleFn, customStyleMap, decorator, editorKey, editorState, forceSelection, selection, block: nodeBlock, direction: textDirectionality ? textDirectionality : directionMap.get(blockKey), tree: editorState.getBlockTree(blockKey) }; const configForType = blockRenderMap.get(nodeBlock.getType()) || blockRenderMap.get('unstyled'); const wrapperTemplate = configForType.wrapper; processedBlocks.push({ /* $FlowFixMe[incompatible-type] (>=0.112.0 site=www,mobile) This * comment suppresses an error found when Flow v0.112 was deployed. To * see the error delete this comment and run Flow. */ block: , wrapperTemplate, key: blockKey, offsetKey: DraftOffsetKey.encode(blockKey, 0, 0) }); const nextBlockKey = nodeBlock.getNextSiblingKey(); nodeBlock = nextBlockKey ? content.getBlockForKey(nextBlockKey) : null; } // Group contiguous runs of blocks that have the same wrapperTemplate const outputBlocks = []; for (let ii = 0; ii < processedBlocks.length;) { const info: any = processedBlocks[ii]; if (info.wrapperTemplate) { const blocks = []; do { blocks.push(processedBlocks[ii].block); ii++; } while (ii < processedBlocks.length && processedBlocks[ii].wrapperTemplate === info.wrapperTemplate); const wrapperElement = React.cloneElement(info.wrapperTemplate, { key: info.key + '-wrap', 'data-offset-key': info.offsetKey }, blocks); outputBlocks.push(wrapperElement); } else { outputBlocks.push(info.block); ii++; } } return
{outputBlocks}
; } } module.exports = DraftEditorContentsExperimental;