src/model/immutable/BlockTree.js (108 lines of code) (raw):

/** * 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'; import type {BlockNodeRecord} from 'BlockNodeRecord'; import type CharacterMetadata from 'CharacterMetadata'; import type ContentState from 'ContentState'; import type {DraftDecoratorType} from 'DraftDecoratorType'; const findRangesImmutable = require('findRangesImmutable'); const getOwnObjectValues = require('getOwnObjectValues'); const Immutable = require('immutable'); const {List, Repeat, Record} = Immutable; const returnTrue = function () { return true; }; const defaultLeafRange: { start: ?number, end: ?number, ... } = { start: null, end: null, }; const LeafRange = (Record(defaultLeafRange): any); export type DecoratorRangeRawType = { start: ?number, end: ?number, decoratorKey: ?string, // $FlowFixMe[value-as-type] leaves: ?Array<LeafRange>, ... }; type DecoratorRangeType = { start: ?number, end: ?number, decoratorKey: ?string, // $FlowFixMe[value-as-type] leaves: ?List<LeafRange>, ... }; const defaultDecoratorRange: DecoratorRangeType = { start: null, end: null, decoratorKey: null, leaves: null, }; const DecoratorRange = (Record(defaultDecoratorRange): any); const BlockTree = { /** * Generate a block tree for a given ContentBlock/decorator pair. */ generate( contentState: ContentState, block: BlockNodeRecord, decorator: ?DraftDecoratorType, // $FlowFixMe[value-as-type] ): List<DecoratorRange> { const textLength = block.getLength(); if (!textLength) { return List.of( new DecoratorRange({ start: 0, end: 0, decoratorKey: null, leaves: List.of(new LeafRange({start: 0, end: 0})), }), ); } const leafSets = []; const decorations = decorator ? decorator.getDecorations(block, contentState) : List(Repeat(null, textLength)); const chars = block.getCharacterList(); findRangesImmutable(decorations, areEqual, returnTrue, (start, end) => { leafSets.push( new DecoratorRange({ start, end, decoratorKey: decorations.get(start), leaves: generateLeaves(chars.slice(start, end).toList(), start), }), ); }); return List(leafSets); }, // $FlowFixMe[value-as-type] fromJS({leaves, ...other}: DecoratorRangeRawType): DecoratorRange { return new DecoratorRange({ ...other, leaves: leaves != null ? List( Array.isArray(leaves) ? leaves : getOwnObjectValues(leaves), ).map(leaf => LeafRange(leaf)) : null, }); }, }; /** * Generate LeafRange records for a given character list. */ function generateLeaves( characters: List<CharacterMetadata>, offset: number, // $FlowFixMe[value-as-type] ): List<LeafRange> { const leaves = []; const inlineStyles = characters.map(c => c.getStyle()).toList(); findRangesImmutable(inlineStyles, areEqual, returnTrue, (start, end) => { leaves.push( new LeafRange({ start: start + offset, end: end + offset, }), ); }); return List(leaves); } function areEqual(a: any, b: any): boolean { return a === b; } module.exports = BlockTree;