From 6521f06c679bc33c76b1296d7aa934680929fe4a Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 25 Aug 2017 08:17:44 -0400 Subject: [PATCH] Blocks: Represent children as array of node objects --- blocks/api/index.js | 1 + blocks/api/nodes.js | 72 +++++++++++++++++++++++++++++++ blocks/api/source.js | 68 ++++++++++++++--------------- blocks/editable/tinymce.js | 5 ++- blocks/library/paragraph/index.js | 6 +-- blocks/library/pullquote/index.js | 4 +- blocks/library/quote/index.js | 6 +-- 7 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 blocks/api/nodes.js diff --git a/blocks/api/index.js b/blocks/api/index.js index b25e28735a5c8..043998c1ad419 100644 --- a/blocks/api/index.js +++ b/blocks/api/index.js @@ -5,6 +5,7 @@ import * as source from './source'; export { source }; export { createBlock, switchToBlockType } from './factory'; +export { toElement } from './nodes'; export { default as parse } from './parser'; export { default as pasteHandler } from './paste'; export { default as serialize, getBlockDefaultClassname } from './serializer'; diff --git a/blocks/api/nodes.js b/blocks/api/nodes.js new file mode 100644 index 0000000000000..f94f29bbfe1a1 --- /dev/null +++ b/blocks/api/nodes.js @@ -0,0 +1,72 @@ +/** + * External dependencies + */ +import { reduce, map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { createElement } from '@wordpress/element'; + +export function namedNodeMapToObject( namedNodeMap ) { + return reduce( namedNodeMap, ( result, entry ) => { + result[ entry.name ] = entry.value; + return result; + }, {} ); +} + +export function toText( node ) { + return { + type: 'text', + text: node.nodeValue, + }; +} + +export function toNode( node ) { + const { nodeName, attributes, childNodes } = node; + + return { + type: 'node', + name: nodeName.toLowerCase(), + attributes: namedNodeMapToObject( attributes ), + children: children( childNodes ), + }; +} + +export function children( childNodes ) { + return reduce( childNodes, ( result, childNode ) => { + switch ( childNode.nodeType ) { + case window.Node.ELEMENT_NODE: + result.push( toNode( childNode ) ); + break; + + case window.Node.TEXT_NODE: + result.push( toText( childNode ) ); + break; + } + + return result; + }, [] ); +} + +export function toElement( value ) { + if ( value === null || value === undefined ) { + return; + } + + if ( Array.isArray( value ) ) { + return map( toElement, value ); + } + + switch ( value.type ) { + case 'text': + return value.text; + + case 'element': + return createElement( + value.name, + value.attributes, + toElement( value.children ), + ); + } +} diff --git a/blocks/api/source.js b/blocks/api/source.js index eee04d56e604f..1e0df1bf70ab3 100644 --- a/blocks/api/source.js +++ b/blocks/api/source.js @@ -1,12 +1,6 @@ -/** - * WordPress dependencies - */ -import { createElement } from '@wordpress/element'; - /** * External dependencies */ -import { nodeListToReact, nodeToReact } from 'dom-react'; import { flow } from 'lodash'; import { attr as originalAttr, @@ -16,6 +10,26 @@ import { query as originalQuery, } from 'hpq'; +/** + * Internal dependencies + */ +import * as nodes from './nodes'; + +function applySelector( selector ) { + return ( element ) => { + if ( selector ) { + return element.querySelector( selector ); + } + + return element; + }; +} + +function applyKnownSourceFlag( source ) { + source._wpBlocksKnownSource = true; + return source; +} + /** * Given a source function creator, returns a new function which applies an * internal flag to the created source. @@ -24,10 +38,7 @@ import { * @return {Function} Modified source function creator */ function withKnownSourceFlag( fn ) { - return flow( fn, ( source ) => { - source._wpBlocksKnownSource = true; - return source; - } ); + return flow( fn, applyKnownSourceFlag ); } export const attr = withKnownSourceFlag( originalAttr ); @@ -35,29 +46,14 @@ export const prop = withKnownSourceFlag( originalProp ); export const html = withKnownSourceFlag( originalHtml ); export const text = withKnownSourceFlag( originalText ); export const query = withKnownSourceFlag( originalQuery ); -export const children = withKnownSourceFlag( ( selector ) => { - return ( domNode ) => { - let match = domNode; - - if ( selector ) { - match = domNode.querySelector( selector ); - } - - if ( match ) { - return nodeListToReact( match.childNodes || [], createElement ); - } - - return []; - }; -} ); -export const node = withKnownSourceFlag( ( selector ) => { - return ( domNode ) => { - let match = domNode; - - if ( selector ) { - match = domNode.querySelector( selector ); - } - - return nodeToReact( match, createElement ); - }; -} ); +export const children = ( selector ) => flow( + applySelector( selector ), + ( match ) => match.childNodes, + nodes.children, + applyKnownSourceFlag +); +export const node = ( selector ) => flow( + applySelector( selector ), + nodes.toNode, + applyKnownSourceFlag +); diff --git a/blocks/editable/tinymce.js b/blocks/editable/tinymce.js index fe1ba297bfed1..02e1a939893ce 100644 --- a/blocks/editable/tinymce.js +++ b/blocks/editable/tinymce.js @@ -8,7 +8,8 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Component, Children, createElement } from '@wordpress/element'; +import { Component, createElement } from '@wordpress/element'; +import { toElement } from '@wordpress/blocks'; export default class TinyMCE extends Component { componentDidMount() { @@ -85,7 +86,7 @@ export default class TinyMCE extends Component { // us to show and focus the content before it's truly ready to edit. let children; if ( defaultValue ) { - children = Children.toArray( defaultValue ); + children = toElement( defaultValue ); } return createElement( tagName, { diff --git a/blocks/library/paragraph/index.js b/blocks/library/paragraph/index.js index 1061d81e7f425..29970e5973fb0 100644 --- a/blocks/library/paragraph/index.js +++ b/blocks/library/paragraph/index.js @@ -8,7 +8,7 @@ import { concatChildren } from '@wordpress/element'; * Internal dependencies */ import './style.scss'; -import { registerBlockType, createBlock, source, setDefaultBlock } from '../../api'; +import { registerBlockType, createBlock, source, setDefaultBlock, toElement } from '../../api'; import AlignmentToolbar from '../../alignment-toolbar'; import BlockControls from '../../block-controls'; import Editable from '../../editable'; @@ -124,10 +124,10 @@ registerBlockType( 'core/paragraph', { const className = dropCap ? 'has-drop-cap' : null; if ( ! align ) { - return

{ content }

; + return

{ toElement( content ) }

; } - return

{ content }

; + return

{ toElement( content ) }

; }, } ); diff --git a/blocks/library/pullquote/index.js b/blocks/library/pullquote/index.js index a4223575338e2..46d7a6075eea9 100644 --- a/blocks/library/pullquote/index.js +++ b/blocks/library/pullquote/index.js @@ -8,7 +8,7 @@ import { __ } from '@wordpress/i18n'; */ import './editor.scss'; import './style.scss'; -import { registerBlockType, source } from '../../api'; +import { registerBlockType, source, toElement } from '../../api'; import Editable from '../../editable'; import BlockControls from '../../block-controls'; import BlockAlignmentToolbar from '../../block-alignment-toolbar'; @@ -95,7 +95,7 @@ registerBlockType( 'core/pullquote', { return (
- { value && value.map( ( paragraph, i ) =>

{ paragraph.props.children }

) } + { toElement( value ) } { citation && citation.length > 0 && (
{ citation }
) } diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index 71eb7a99f4484..c318cc7a993bf 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -13,7 +13,7 @@ import { Toolbar } from '@wordpress/components'; * Internal dependencies */ import './style.scss'; -import { registerBlockType, createBlock, source } from '../../api'; +import { registerBlockType, createBlock, source, toElement } from '../../api'; import AlignmentToolbar from '../../alignment-toolbar'; import BlockControls from '../../block-controls'; import Editable from '../../editable'; @@ -209,9 +209,7 @@ registerBlockType( 'core/quote', { className={ `blocks-quote-style-${ style }` } style={ { textAlign: align ? align : null } } > - { value.map( ( paragraph, i ) => ( -

{ paragraph.props.children }

- ) ) } + { toElement( value ) } { citation && citation.length > 0 && ( ) }