Skip to content

Commit

Permalink
Framework: Extract the block editor from the editor chrome to make it…
Browse files Browse the repository at this point in the history
… reusable
  • Loading branch information
youknowriad committed Jul 28, 2017
1 parent db05898 commit 9adeaf7
Show file tree
Hide file tree
Showing 109 changed files with 1,499 additions and 1,245 deletions.
40 changes: 11 additions & 29 deletions blocks/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,22 @@
import uuid from 'uuid/v4';
import { get, castArray, findIndex, isObjectLike, find } from 'lodash';

/**
* Internal dependencies
*/
import { getBlockType } from './registration';

/**
* Returns a block object given its type and attributes.
*
* @param {String} name Block name
* @param {String} blockType Block type
* @param {Object} attributes Block attributes
* @return {Object} Block object
*/
export function createBlock( name, attributes = {} ) {
// Get the type definition associated with a registered block.
const blockType = getBlockType( name );

export function createBlock( blockType, attributes = {} ) {
// Do we need this? What purpose does it have?
let defaultAttributes;
if ( blockType ) {
defaultAttributes = blockType.defaultAttributes;
}
const defaultAttributes = blockType.defaultAttributes;

// Blocks are stored with a unique ID, the assigned type name,
// and the block attributes.
return {
uid: uuid(),
name,
name: blockType.name,
isValid: true,
attributes: {
...defaultAttributes,
Expand All @@ -42,19 +31,18 @@ export function createBlock( name, attributes = {} ) {
/**
* Switch a block into one or more blocks of the new block type.
*
* @param {Object} block Block object
* @param {string} name Block name
* @return {Array} Block object
* @param {Object} block Block object
* @param {string} sourceType Source Block type
* @param {string} destinationType Destination Block type
* @return {Array} Block object
*/
export function switchToBlockType( block, name ) {
export function switchToBlockType( block, sourceType, destinationType ) {
// Find the right transformation by giving priority to the "to"
// transformation.
const destinationType = getBlockType( name );
const sourceType = getBlockType( block.name );
const transformationsFrom = get( destinationType, 'transforms.from', [] );
const transformationsTo = get( sourceType, 'transforms.to', [] );
const transformation =
find( transformationsTo, t => t.blocks.indexOf( name ) !== -1 ) ||
find( transformationsTo, t => t.blocks.indexOf( destinationType.name ) !== -1 ) ||
find( transformationsFrom, t => t.blocks.indexOf( block.name ) !== -1 );

// Stop if there is no valid transformation. (How did we get here?)
Expand All @@ -74,13 +62,7 @@ export function switchToBlockType( block, name ) {
// with an array instead.
transformationResults = castArray( transformationResults );

// Ensure that every block object returned by the transformation has a
// valid block type.
if ( transformationResults.some( ( result ) => ! getBlockType( result.name ) ) ) {
return null;
}

const firstSwitchedBlock = findIndex( transformationResults, ( result ) => result.name === name );
const firstSwitchedBlock = findIndex( transformationResults, ( result ) => result.name === destinationType.name );

// Ensure that at least one block object returned by the transformation has
// the expected "destination" block type.
Expand Down
42 changes: 19 additions & 23 deletions blocks/api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
* External dependencies
*/
import { parse as hpqParse } from 'hpq';
import { pickBy } from 'lodash';
import { pickBy, find } from 'lodash';

/**
* Internal dependencies
*/
import { parse as grammarParse } from './post.pegjs';
import { getBlockType, getUnknownTypeHandler } from './registration';
import { createBlock } from './factory';
import { getBeautifulContent, getSaveContent } from './serializer';

Expand Down Expand Up @@ -63,31 +62,23 @@ export function getBlockAttributes( blockType, rawContent, attributes ) {
/**
* Creates a block with fallback to the unknown type handler.
*
* @param {?String} name Block type name
* @param {String} rawContent Raw block content
* @param {?Object} attributes Attributes obtained from block delimiters
* @return {?Object} An initialized block object (if possible)
* @param {Object} blockType Block type name
* @param {Object} fallbackBlockType Fallback Block type name
* @param {String} rawContent Raw block content
* @param {?Object} attributes Attributes obtained from block delimiters
* @return {?Object} An initialized block object (if possible)
*/
export function createBlockWithFallback( name, rawContent, attributes ) {
// Use type from block content, otherwise find unknown handler.
name = name || getUnknownTypeHandler();

// Try finding type for known block name, else fall back again.
let blockType = getBlockType( name );
const fallbackBlock = getUnknownTypeHandler();
if ( ! blockType ) {
name = fallbackBlock;
blockType = getBlockType( name );
}
export function createBlockWithFallback( blockType, fallbackBlockType, rawContent, attributes ) {
const parsedBlockType = blockType || fallbackBlockType;

// Include in set only if type were determined.
// TODO do we ever expect there to not be an unknown type handler?
if ( blockType && ( rawContent || name !== fallbackBlock ) ) {
if ( parsedBlockType && ( rawContent || parsedBlockType !== fallbackBlockType ) ) {
// TODO allow blocks to opt-in to receiving a tree instead of a string.
// Gradually convert all blocks to this new format, then remove the
// string serialization.
const block = createBlock(
name,
blockType,
getBlockAttributes( blockType, rawContent, attributes )
);

Expand Down Expand Up @@ -138,13 +129,18 @@ export function isValidBlock( rawContent, blockType, attributes ) {
/**
* Parses the post content with a PegJS grammar and returns a list of blocks.
*
* @param {String} content The post content
* @return {Array} Block list
* @param {String} content The post content
* @param {Array} blockTypes Block Types
* @param {String} fallbackBlockName Fallback Block Name
* @return {Array} Block list
*/
export function parseWithGrammar( content ) {
export function parseWithGrammar( content, blockTypes, fallbackBlockName ) {
const getBlockType = ( name ) => find( blockTypes, ( bt ) => bt.name === name );
const fallbackBlockType = getBlockType( fallbackBlockName );
return grammarParse( content ).reduce( ( memo, blockNode ) => {
const { blockName, rawContent, attrs } = blockNode;
const block = createBlockWithFallback( blockName, rawContent.trim(), attrs );
const blockType = getBlockType( blockName );
const block = createBlockWithFallback( blockType, fallbackBlockType, rawContent.trim(), attrs );
if ( block ) {
memo.push( block );
}
Expand Down
10 changes: 6 additions & 4 deletions blocks/api/paste.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ export function normaliseToBlockLevelNodes( nodes ) {
}

export default function( nodes ) {
const blockTypes = getBlockTypes();
const unknownTypeBlockType = find( blockTypes, ( bt ) => bt.name === getUnknownTypeHandler() );
return normaliseToBlockLevelNodes( nodes ).map( ( node ) => {
const block = getBlockTypes().reduce( ( acc, blockType ) => {
const block = blockTypes.reduce( ( acc, blockType ) => {
if ( acc ) {
return acc;
}
Expand All @@ -83,17 +85,17 @@ export default function( nodes ) {
return acc;
}

const { name, defaultAttributes = [] } = blockType;
const { defaultAttributes = [] } = blockType;
const attributes = parseBlockAttributes( node.outerHTML, transform.attributes );

return createBlock( name, { ...defaultAttributes, ...attributes } );
return createBlock( blockType, { ...defaultAttributes, ...attributes } );
}, null );

if ( block ) {
return block;
}

return createBlock( getUnknownTypeHandler(), {
return createBlock( unknownTypeBlockType, {
content: node.outerHTML,
} );
} );
Expand Down
20 changes: 12 additions & 8 deletions blocks/api/serializer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { isEmpty, reduce, isObject } from 'lodash';
import { isEmpty, reduce, isObject, find } from 'lodash';
import { html as beautifyHtml } from 'js-beautify';
import classnames from 'classnames';

Expand All @@ -13,7 +13,6 @@ import { Component, createElement, renderToString, cloneElement, Children } from
/**
* Internal dependencies
*/
import { getBlockType } from './registration';
import { parseBlockAttributes } from './parser';

/**
Expand Down Expand Up @@ -125,9 +124,8 @@ export function getBeautifulContent( content ) {
} );
}

export function serializeBlock( block ) {
export function serializeBlock( block, blockType ) {
const blockName = block.name;
const blockType = getBlockType( blockName );

let saveContent;
if ( block.isValid ) {
Expand Down Expand Up @@ -162,9 +160,15 @@ export function serializeBlock( block ) {
/**
* Takes a block list and returns the serialized post content.
*
* @param {Array} blocks Block list
* @return {String} The post content
* @param {Array} blocks Block list
* @param {Array} blockTypes Block Types
* @return {String} The post content
*/
export default function serialize( blocks ) {
return blocks.map( serializeBlock ).join( '\n\n' );
export default function serialize( blocks, blockTypes ) {
const getBlockType = ( name ) => find( blockTypes, ( bt ) => bt.name === name );
return blocks
.map( ( block ) =>
serializeBlock( block, getBlockType( block.name ) )
)
.join( '\n\n' );
}
4 changes: 2 additions & 2 deletions blocks/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'element-closest';
* WordPress dependencies
*/
import { createElement, Component, renderToString } from 'element';
import { parse, pasteHandler } from '../api';
import { parse, pasteHandler, getBlockTypes, getUnknownTypeHandler } from '../api';
import { BACKSPACE, DELETE, ENTER } from 'utils/keycodes';

/**
Expand Down Expand Up @@ -145,7 +145,7 @@ export default class Editable extends Component {

// Internal paste, so parse.
if ( childNodes.some( isBlockDelimiter ) ) {
blocks = parse( event.node.innerHTML.replace( /<meta[^>]+>/, '' ) );
blocks = parse( getBlockTypes(), getUnknownTypeHandler(), event.node.innerHTML.replace( /<meta[^>]+>/, '' ) );
// External paste with block level content, so attempt to assign
// blocks.
} else if ( childNodes.some( isBlockPart ) ) {
Expand Down
7 changes: 4 additions & 3 deletions blocks/library/heading/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { isObject } from 'lodash';
import { isObject, find } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -113,8 +113,9 @@ registerBlockType( 'core/heading', {
};
},

edit( { attributes, setAttributes, focus, setFocus, mergeBlocks, insertBlocksAfter } ) {
edit( { attributes, setAttributes, focus, setFocus, mergeBlocks, insertBlocksAfter, editorConfig } ) {
const { align, content, nodeName, placeholder } = attributes;
const getBlockType = ( name ) => find( editorConfig.blockTypes, ( bt ) => bt.name === name );

return [
focus && (
Expand Down Expand Up @@ -170,7 +171,7 @@ registerBlockType( 'core/heading', {
setAttributes( { content: before } );
insertBlocksAfter( [
...blocks,
createBlock( 'core/text', { content: after } ),
createBlock( getBlockType( 'core/text' ), { content: after } ),
] );
} }
style={ { textAlign: align } }
Expand Down
10 changes: 8 additions & 2 deletions blocks/library/text/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import { find } from 'lodash';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -53,9 +58,10 @@ registerBlockType( 'core/text', {
};
},

edit( { attributes, setAttributes, insertBlocksAfter, focus, setFocus, mergeBlocks, onReplace } ) {
edit( { attributes, setAttributes, insertBlocksAfter, focus, setFocus, mergeBlocks, onReplace, editorConfig } ) {
const { align, content, dropCap, placeholder } = attributes;
const toggleDropCap = () => setAttributes( { dropCap: ! dropCap } );
const getBlockType = ( name ) => find( editorConfig.blockTypes, ( bt ) => bt.name === name );
return [
focus && (
<BlockControls key="controls">
Expand Down Expand Up @@ -95,7 +101,7 @@ registerBlockType( 'core/text', {
setAttributes( { content: before } );
insertBlocksAfter( [
...blocks,
createBlock( 'core/text', { content: after } ),
createBlock( getBlockType( 'core/text' ), { content: after } ),
] );
} }
onMerge={ mergeBlocks }
Expand Down
File renamed without changes.
Loading

0 comments on commit 9adeaf7

Please sign in to comment.