172 lines
5.9 KiB
Plaintext
172 lines
5.9 KiB
Plaintext
/**
|
|
* 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 DraftEditorContentsExperimental.react
|
|
* @format
|
|
* @flow
|
|
*
|
|
* 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 { BidiDirection } from 'fbjs/lib/UnicodeBidiDirection';
|
|
|
|
const DraftEditorBlockNode = require('./DraftEditorBlockNode.react');
|
|
const DraftOffsetKey = require('./DraftOffsetKey');
|
|
const EditorState = require('./EditorState');
|
|
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<Props> {
|
|
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({
|
|
block: <DraftEditorBlockNode key={blockKey} {...blockProps} />,
|
|
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 <div data-contents="true">{outputBlocks}</div>;
|
|
}
|
|
}
|
|
|
|
module.exports = DraftEditorContentsExperimental; |