diff --git a/tinymce-per-block/index.html b/tinymce-per-block/index.html index 8747e70c3beda2..d4e39f09590ab2 100644 --- a/tinymce-per-block/index.html +++ b/tinymce-per-block/index.html @@ -7,7 +7,7 @@
diff --git a/tinymce-per-block/src/blocks/heading-block/form.js b/tinymce-per-block/src/blocks/heading-block/form.js index 289c8ffeaab7e7..cf9ee9a5451b41 100644 --- a/tinymce-per-block/src/blocks/heading-block/form.js +++ b/tinymce-per-block/src/blocks/heading-block/form.js @@ -27,12 +27,11 @@ export default class HeadingBlockForm extends Component { }; setSize = ( size ) => () => { - this.props.setAttributes( { size } ); + this.props.change( { size } ); }; render() { const { block, isFocused } = this.props; - const className = block.attrs.size ? block.attrs.size : 'h2'; const sizes = [ { id: 'h1', icon: EditorHeading1Icon }, { id: 'h2', icon: EditorHeading2Icon }, @@ -49,7 +48,7 @@ export default class HeadingBlockForm extends Component { key={ id } onClick={ this.setSize( id ) } className={ classNames( 'block-list__block-control', { - 'is-selected': className === id + 'is-selected': block.size === id } ) } > @@ -62,7 +61,7 @@ export default class HeadingBlockForm extends Component { ) } -
+
{ + const nodeNames = [ 'h1', 'h2', 'h3' ]; + if ( + rawBlock.children.length !== 1 || + nodeNames.indexOf( rawBlock.children[ 0 ].name ) === -1 + ) { + return false; + } + + return { + blockType: 'heading', + content: serialize( rawBlock.children[ 0 ].children ), + size: rawBlock.children[ 0 ].name + }; + }, + serialize: ( block ) => { + const elementName = block.size ? block.size : 'h2'; + const rawHtml = `<${ elementName }>` + block.content + ``; + + return { + type: 'WP_Block', + blockType: 'heading', + attrs: { size: elementName }, + startText: '', + endText: '', + rawContent: '' + rawHtml + '', + children: parse( rawHtml ) + }; + } } ); diff --git a/tinymce-per-block/src/blocks/image-block/form.js b/tinymce-per-block/src/blocks/image-block/form.js index e2368b35ad30fc..a4b8ff7ccd9eaf 100644 --- a/tinymce-per-block/src/blocks/image-block/form.js +++ b/tinymce-per-block/src/blocks/image-block/form.js @@ -2,7 +2,6 @@ * External dependencies */ import { createElement, Component } from 'wp-elements'; -import { find } from 'lodash'; import { ImageNoAlignIcon, ImageAlignRightIcon, @@ -25,35 +24,21 @@ export default class ImageBlockForm extends Component { } setImageAlignment = ( id ) => () => { - this.props.setAttributes( { align: id } ); + this.props.change( { align: id } ); }; render() { - const { block, setAttributes, moveDown, moveUp, remove, appendBlock, isFocused } = this.props; - const { attrs, children } = block; - const image = find( children, ( { name } ) => 'img' === name ); - if ( ! image ) { - return null; - } - const caption = attrs.caption || ''; + const { block, change, moveDown, moveUp, remove, appendBlock, isFocused } = this.props; const removePrevious = () => { - if ( ! caption ) { + if ( ! block.caption ) { remove(); } }; const splitValue = ( left, right ) => { - setAttributes( { caption: left } ); + change( { caption: left } ); appendBlock( { - type: 'WP_Block', blockType: 'paragraph', - attrs: {}, - startText: '', - endText: '', - rawContent: '' + right + '', - children: [ { - type: 'Text', - value: right - } ] + content: right } ); }; const imageAlignments = [ @@ -62,7 +47,7 @@ export default class ImageBlockForm extends Component { { id: 'align-right', icon: ImageAlignRightIcon }, { id: 'align-full-width', icon: ImageFullWidthIcon }, ]; - const alignValue = attrs.align || 'no-align'; + const alignValue = block.align || 'no-align'; return (
@@ -85,7 +70,7 @@ export default class ImageBlockForm extends Component {
}
@@ -95,8 +80,8 @@ export default class ImageBlockForm extends Component { removePrevious={ removePrevious } moveDown={ moveDown } splitValue={ splitValue } - value={ caption } - onChange={ ( value ) => setAttributes( { caption: value } ) } + value={ block.caption } + onChange={ ( value ) => change( { caption: value } ) } placeholder="Enter a caption" />
diff --git a/tinymce-per-block/src/blocks/image-block/index.js b/tinymce-per-block/src/blocks/image-block/index.js index 56cd6ece653183..6cc56b6319c2fa 100644 --- a/tinymce-per-block/src/blocks/image-block/index.js +++ b/tinymce-per-block/src/blocks/image-block/index.js @@ -4,6 +4,8 @@ import { registerBlock } from 'wp-blocks'; import { FormatImageIcon } from 'dashicons'; +import { parse } from 'parsers/block'; + /** * Internal dependencies */ @@ -12,5 +14,33 @@ import form from './form'; registerBlock( 'image', { title: 'Image', icon: FormatImageIcon, - form + form, + parse: ( rawBlock ) => { + if ( + rawBlock.children.length !== 1 || + rawBlock.children[ 0 ].name !== 'img' + ) { + return false; + } + + return { + blockType: 'image', + src: rawBlock.children[ 0 ].attrs.src, + caption: rawBlock.children[ 0 ].attrs.caption || '', + align: rawBlock.children[ 0 ].attrs.align || 'no-align' + }; + }, + serialize: ( block ) => { + const rawHtml = ``; + + return { + type: 'WP_Block', + blockType: 'image', + attrs: { /* caption: block.caption , align: block.align */ }, + startText: '', + endText: '', + rawContent: '' + rawHtml + '', + children: parse( rawHtml ) + }; + } } ); diff --git a/tinymce-per-block/src/blocks/inline-text-block/form.js b/tinymce-per-block/src/blocks/inline-text-block/form.js index de40ea52b8179b..d3ff16384c7c0e 100644 --- a/tinymce-per-block/src/blocks/inline-text-block/form.js +++ b/tinymce-per-block/src/blocks/inline-text-block/form.js @@ -4,6 +4,7 @@ import { createElement, Component } from 'wp-elements'; import { EditableComponent } from 'wp-blocks'; +import { parse } from 'parsers/block'; import { serialize } from 'serializers/block'; export default class InlineTextBlockForm extends Component { @@ -21,9 +22,11 @@ export default class InlineTextBlockForm extends Component { return children; }; - const { block: { children }, remove, setChildren } = this.props; + const { block: { content }, remove, change } = this.props; remove( index ); - setTimeout( () => setChildren( getLeaves( children ).concat( getLeaves( block.children ) ) ) ); + setTimeout( () => change( + { content: serialize( getLeaves( parse( content ) ).concat( getLeaves( parse( block.content ) ) ) ) } + ) ); setTimeout( () => this.editable.updateContent() ); } @@ -36,17 +39,16 @@ export default class InlineTextBlockForm extends Component { }; render() { - const { block, setChildren, moveUp, moveDown, appendBlock, + const { block, change, moveUp, moveDown, appendBlock, mergeWithPrevious, remove, setToolbarState, focus, focusConfig } = this.props; - const { children } = block; const splitValue = ( left, right ) => { - setChildren( left ); + change( { content: left } ); setTimeout( () => this.editable.updateContent() ); if ( right ) { appendBlock( { ...block, - children: right + content: right } ); } else { appendBlock(); @@ -56,13 +58,13 @@ export default class InlineTextBlockForm extends Component { return ( setChildren( value ) } + onChange={ ( value ) => change( { content: value } ) } setToolbarState={ setToolbarState } focusConfig={ focusConfig } onFocusChange={ focus } diff --git a/tinymce-per-block/src/blocks/paragraph-block/form.js b/tinymce-per-block/src/blocks/paragraph-block/form.js index cf0128cefcc03c..3bf8ea5fc32562 100644 --- a/tinymce-per-block/src/blocks/paragraph-block/form.js +++ b/tinymce-per-block/src/blocks/paragraph-block/form.js @@ -22,13 +22,13 @@ export default class ParagraphBlockForm extends Component { this.toolbar && this.toolbar.setToolbarState( ...args ); }; - setAlignment = ( textAlign ) => { - this.props.setAttributes( { textAlign } ); + setAlignment = ( align ) => { + this.props.change( { align } ); }; render() { const { block, isFocused } = this.props; - const selectedTextAlign = block.attrs.textAlign || 'left'; + const selectedTextAlign = block.align || 'left'; const style = { textAlign: selectedTextAlign }; @@ -39,7 +39,7 @@ export default class ParagraphBlockForm extends Component { { isFocused &&
- +
diff --git a/tinymce-per-block/src/blocks/paragraph-block/index.js b/tinymce-per-block/src/blocks/paragraph-block/index.js index 1758804d2c37d5..b8015d151a4391 100644 --- a/tinymce-per-block/src/blocks/paragraph-block/index.js +++ b/tinymce-per-block/src/blocks/paragraph-block/index.js @@ -8,9 +8,39 @@ import { EditorParagraphIcon } from 'dashicons'; * Internal dependencies */ import form from './form'; +import { parse } from 'parsers/block'; +import { serialize } from 'serializers/block'; registerBlock( 'paragraph', { title: 'Paragraph', form: form, - icon: EditorParagraphIcon + icon: EditorParagraphIcon, + parse: ( rawBlock ) => { + if ( + rawBlock.children.length !== 1 || + rawBlock.children[ 0 ].name !== 'p' + ) { + return false; + } + + return { + blockType: 'paragraph', + align: rawBlock.attrs.align || 'no-align', + content: serialize( rawBlock.children ), + }; + }, + serialize: ( block ) => { + const children = parse( block.content ); + const rawHtml = serialize( children ); + + return { + type: 'WP_Block', + blockType: 'paragraph', + attrs: { /* align: block.align */ }, + startText: '', + endText: '', + rawContent: '' + rawHtml + '', + children + }; + } } ); diff --git a/tinymce-per-block/src/blocks/quote-block/form.js b/tinymce-per-block/src/blocks/quote-block/form.js index 07013cef42c3b2..bdacf8ea9b8b44 100644 --- a/tinymce-per-block/src/blocks/quote-block/form.js +++ b/tinymce-per-block/src/blocks/quote-block/form.js @@ -5,6 +5,7 @@ import { createElement, Component } from 'wp-elements'; import { EditableComponent, EnhancedInputComponent } from 'wp-blocks'; import { serialize } from 'serializers/block'; +import { parse } from 'parsers/block'; import EditableFormatToolbar from 'controls/editable-format-toolbar'; import BlockArrangement from 'controls/block-arrangement'; @@ -31,9 +32,11 @@ export default class QuoteBlockForm extends Component { return children; }; - const { block: { children }, remove, setChildren } = this.props; + const { block: { content }, remove, change } = this.props; remove( index ); - setTimeout( () => setChildren( getLeaves( children ).concat( getLeaves( block.children ) ) ) ); + setTimeout( () => change( + { content: serialize( getLeaves( parse( content ) ).concat( getLeaves( parse( block.content ) ) ) ) } + ) ); setTimeout( () => this.content.updateContent() ); } @@ -54,23 +57,13 @@ export default class QuoteBlockForm extends Component { }; render() { - const { block, setChildren, setAttributes, moveUp, - moveDown, remove, mergeWithPrevious, appendBlock, isFocused, focusConfig, focus } = this.props; - const { children } = block; - const cite = block.attrs.cite || ''; + const { block, change, moveUp, moveDown, remove, + mergeWithPrevious, appendBlock, isFocused, focusConfig, focus } = this.props; const splitValue = ( left, right ) => { - setAttributes( { cite: left } ); + change( { cite: left } ); appendBlock( { - type: 'WP_Block', blockType: 'paragraph', - attrs: {}, - startText: '', - endText: '', - rawContent: '' + right + '', - children: [ { - type: 'Text', - value: right - } ] + content: right } ); }; let focusInput = focusConfig && focusConfig.input; @@ -93,12 +86,12 @@ export default class QuoteBlockForm extends Component {
setChildren( value ) } + onChange={ ( value ) => change( { content: value } ) } setToolbarState={ this.setToolbarState } focusConfig={ focusInput === 'content' ? focusConfig : null } onFocusChange={ ( config ) => focus( Object.assign( { input: 'content' }, config ) ) } @@ -111,9 +104,9 @@ export default class QuoteBlockForm extends Component { moveUp={ this.moveToContent } removePrevious={ this.moveToContent } moveDown={ moveDown } - value={ cite } + value={ block.cite } splitValue={ splitValue } - onChange={ ( value ) => setAttributes( { cite: value } ) } + onChange={ ( value ) => change( { cite: value } ) } focusConfig={ focusInput === 'cite' ? focusConfig : null } onFocusChange={ ( config ) => focus( Object.assign( { input: 'cite' }, config ) ) } placeholder="Enter a cite" diff --git a/tinymce-per-block/src/blocks/quote-block/index.js b/tinymce-per-block/src/blocks/quote-block/index.js index 38353a5f9f4691..f2c84fe67b4819 100644 --- a/tinymce-per-block/src/blocks/quote-block/index.js +++ b/tinymce-per-block/src/blocks/quote-block/index.js @@ -9,11 +9,40 @@ import { /** * Internal dependencies */ +import { parse } from 'parsers/block'; +import { serialize } from 'serializers/block'; import form from './form'; registerBlock( 'quote', { title: 'Quote', form: form, icon: EditorQuoteIcon, - controls: [] + parse: ( rawBlock ) => { + if ( + rawBlock.children.length !== 1 || + rawBlock.children[ 0 ].name !== 'p' + ) { + return false; + } + + return { + blockType: 'quote', + cite: rawBlock.attrs.cite || '', + content: serialize( rawBlock.children ), + }; + }, + serialize: ( block ) => { + const children = parse( block.content ); + const rawHtml = serialize( children ); + + return { + type: 'WP_Block', + blockType: 'quote', + attrs: { cite: block.cite }, + startText: '', + endText: '', + rawContent: '' + rawHtml + '', + children + }; + } } ); diff --git a/tinymce-per-block/src/blocks/text-block/form.js b/tinymce-per-block/src/blocks/text-block/form.js index 5117574afc95af..cf50605aaee7b8 100644 --- a/tinymce-per-block/src/blocks/text-block/form.js +++ b/tinymce-per-block/src/blocks/text-block/form.js @@ -4,7 +4,6 @@ import { createElement, Component } from 'wp-elements'; import { EditableComponent } from 'wp-blocks'; -import { serialize } from 'serializers/block'; import AlignmentToolbar from 'controls/alignment-toolbar'; import EditableFormatToolbar from 'controls/editable-format-toolbar'; import BlockArrangement from 'controls/block-arrangement'; @@ -15,9 +14,10 @@ export default class TextBlockForm extends Component { if ( acceptedBlockTypes.indexOf( block.blockType ) === -1 ) { return; } - const { block: { children }, remove, setChildren } = this.props; + + const { block: { content }, remove, change } = this.props; remove( index ); - setTimeout( () => setChildren( children.concat( block.children ) ) ); + setTimeout( () => change( { content: content + block.content } ) ); setTimeout( () => this.editable.updateContent() ); } @@ -25,8 +25,8 @@ export default class TextBlockForm extends Component { this.editable = ref; } - setAlignment = ( textAlign ) => { - this.props.setAttributes( { textAlign } ); + setAlignment = ( align ) => { + this.props.change( { align } ); }; bindFormatToolbar = ( ref ) => { @@ -38,21 +38,20 @@ export default class TextBlockForm extends Component { }; render() { - const { block, isFocused, setChildren, moveUp, moveDown, appendBlock, + const { block, isFocused, change, moveUp, moveDown, appendBlock, mergeWithPrevious, remove, focusConfig, focus } = this.props; - const { children } = block; const splitValue = ( left, right ) => { - setChildren( left ); + change( { content: left } ); if ( right ) { appendBlock( { ...block, - children: right + content: right } ); } else { appendBlock(); } }; - const selectedTextAlign = block.attrs.textAlign || 'left'; + const selectedTextAlign = block.align || 'left'; const style = { textAlign: selectedTextAlign }; @@ -63,7 +62,7 @@ export default class TextBlockForm extends Component { { isFocused && (
- +
@@ -74,14 +73,14 @@ export default class TextBlockForm extends Component {
setChildren( value ) } + onChange={ ( value ) => change( { content: value } ) } focusConfig={ focusConfig } onFocusChange={ focus } /> diff --git a/tinymce-per-block/src/blocks/text-block/index.js b/tinymce-per-block/src/blocks/text-block/index.js index b714d2ccfd432d..d791a0de0941d7 100644 --- a/tinymce-per-block/src/blocks/text-block/index.js +++ b/tinymce-per-block/src/blocks/text-block/index.js @@ -7,10 +7,33 @@ import { EditorTableIcon } from 'dashicons'; /** * Internal dependencies */ +import { parse } from 'parsers/block'; +import { serialize } from 'serializers/block'; import form from './form'; registerBlock( 'text', { title: 'Text', form: form, icon: EditorTableIcon, + parse: ( rawBlock ) => { + return { + blockType: 'text', + align: rawBlock.attrs.align || 'no-align', + content: serialize( rawBlock.children ), + }; + }, + serialize: ( block ) => { + const children = parse( block.content ); + const rawHtml = serialize( children ); + + return { + type: 'WP_Block', + blockType: 'text', + attrs: { /* align: block.align */ }, + startText: '', + endText: '', + rawContent: '' + rawHtml + '', + children + }; + } } ); diff --git a/tinymce-per-block/src/external/wp-blocks/editable/index.js b/tinymce-per-block/src/external/wp-blocks/editable/index.js index 30cae4684ef382..b1003762b14366 100644 --- a/tinymce-per-block/src/external/wp-blocks/editable/index.js +++ b/tinymce-per-block/src/external/wp-blocks/editable/index.js @@ -5,8 +5,6 @@ import { createElement, Component } from 'wp-elements'; import tinymce from 'tinymce'; import { isEqual } from 'lodash'; -import { parse } from 'parsers/block'; - function initialize( node, inline, onSetup ) { if ( ! node ) { return; @@ -132,7 +130,7 @@ export default class EditableComponent extends Component { const hasAfter = !! childNodes.slice( splitIndex ) .reduce( ( memo, node ) => memo + node.textContent, '' ); this.editor.setContent( before ); - this.props.splitValue( parse( before ), hasAfter ? parse( after ) : '' ); + this.props.splitValue( before, hasAfter ? after : '' ); } ); } else if ( event.keyCode === 8 ) { if ( this.isStartOfEditor() ) { @@ -202,7 +200,7 @@ export default class EditableComponent extends Component { return; } - this.props.onChange( parse( content ) ); + this.props.onChange( content ); }; setRef = ( node ) => { diff --git a/tinymce-per-block/src/external/wp-blocks/input/index.js b/tinymce-per-block/src/external/wp-blocks/input/index.js index 450369567a61f7..a9de1f929c19e3 100644 --- a/tinymce-per-block/src/external/wp-blocks/input/index.js +++ b/tinymce-per-block/src/external/wp-blocks/input/index.js @@ -71,7 +71,9 @@ export default class EnhancedInput extends Component { render() { // Keeping splitValue to exclude it from props - const ignoredProps = [ 'value', 'splitValue', 'removePrevious', 'moveDown', 'moveUp', 'focusConfig', 'onFocusChange' ]; + const ignoredProps = [ + 'value', 'splitValue', 'removePrevious', 'moveDown', 'moveUp', 'focusConfig', 'onFocusChange' + ]; const { value } = this.props; const props = omit( this.props, ignoredProps ); diff --git a/tinymce-per-block/src/index.js b/tinymce-per-block/src/index.js index 287ba6453a02c6..b84fe2b689e1dd 100644 --- a/tinymce-per-block/src/index.js +++ b/tinymce-per-block/src/index.js @@ -30,7 +30,12 @@ class App extends Component { this.setState( { content: { block: nextContent, - html: serializers.block.serialize( nextContent ) + html: serializers.block.serialize( + nextContent.map( block => { + const blockDefinition = getBlock( block.blockType ); + return blockDefinition.serialize( block ); + } ) + ) } } ); return; @@ -38,7 +43,12 @@ class App extends Component { this.setState( { content: { block: parsers.block.parse( nextContent ) - .filter( block => !! getBlock( block.blockType ) ) + .filter( rawBlock => !! getBlock( rawBlock.blockType ) ) + .map( rawBlock => { + const blockDefinition = getBlock( rawBlock.blockType ); + const parsedBlock = blockDefinition.parse( rawBlock ); + return parsedBlock ? parsedBlock : getBlock( 'text' ).parse( rawBlock ); + } ) .map( block => Object.assign( { uid: uniqueId() }, block ) ), html: nextContent } diff --git a/tinymce-per-block/src/renderers/block/block-list/block.js b/tinymce-per-block/src/renderers/block/block-list/block.js index 4bc8c7f7e14756..50eea2da680498 100644 --- a/tinymce-per-block/src/renderers/block/block-list/block.js +++ b/tinymce-per-block/src/renderers/block/block-list/block.js @@ -4,7 +4,6 @@ import { createElement, Component } from 'wp-elements'; import classNames from 'classnames'; import { getBlock } from 'wp-blocks'; -import isEqualShallow from 'is-equal-shallow'; export default class BlockListBlock extends Component { setRef = ( blockNode ) => { @@ -37,25 +36,10 @@ export default class BlockListBlock extends Component { const { executeCommand, tabIndex, onFocus } = this.props; const state = { - setChildren( children ) { + change( changes ) { executeCommand( { type: 'change', - changes: { children } - } ); - }, - setAttributes( attributes ) { - if ( isEqualShallow( attributes, block.attrs ) ) { - return; - } - - executeCommand( { - type: 'change', - changes: { - attrs: { - ...block.attrs, - ...attributes - } - } + changes } ); }, appendBlock( newBlock ) { diff --git a/tinymce-per-block/src/renderers/block/block-list/index.js b/tinymce-per-block/src/renderers/block/block-list/index.js index dff9ab4c66a657..dd9aff38b65f84 100644 --- a/tinymce-per-block/src/renderers/block/block-list/index.js +++ b/tinymce-per-block/src/renderers/block/block-list/index.js @@ -60,20 +60,7 @@ class BlockList extends Component { append: ( { block: commandBlock } ) => { const createdBlock = commandBlock ? commandBlock - : { - type: 'WP_Block', - blockType: 'paragraph', - attrs: {}, - startText: '', - endText: '', - rawContent: '', - children: [ - { - type: 'Text', - value: ' ' - } - ] - }; + : { blockType: 'paragraph', content: ' ' }; this.onChange( [ ...content.slice( 0, index + 1 ), Object.assign( {}, createdBlock, { uid: uniqueId() } ),