From dfbca5f5967e04a30be28f96b53284d7acc2b138 Mon Sep 17 00:00:00 2001 From: iseulde Date: Wed, 7 Nov 2018 17:06:31 +0100 Subject: [PATCH 01/13] WIP --- .../editor/src/components/rich-text/index.js | 9 ++---- packages/format-library/src/index.js | 2 ++ .../format-library/src/invisible/index.js | 28 +++++++++++++++++++ packages/rich-text/src/create.js | 16 +++++++---- .../rich-text/src/register-format-type.js | 12 -------- packages/rich-text/src/to-dom.js | 16 ++++++++++- 6 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 packages/format-library/src/invisible/index.js diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index b048c442f594ed..e5c5105dc3adb1 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -447,16 +447,11 @@ export class RichText extends Component { * updated if differences are found. * * @param {Object} record The record to sync and apply. - * @param {boolean} _withoutApply If true, the record won't be applied to - * the live DOM. */ - onChange( record, _withoutApply ) { - if ( ! _withoutApply ) { - this.applyRecord( record ); - } - + onChange( record ) { const { start, end } = record; + this.applyRecord( record ); this.savedContent = this.valueToFormat( record ); this.props.onChange( this.savedContent ); this.setState( { start, end } ); diff --git a/packages/format-library/src/index.js b/packages/format-library/src/index.js index 387c2666f7e0e6..3767611277f613 100644 --- a/packages/format-library/src/index.js +++ b/packages/format-library/src/index.js @@ -7,6 +7,7 @@ import { image } from './image'; import { italic } from './italic'; import { link } from './link'; import { strikethrough } from './strikethrough'; +import { invisible } from './invisible'; /** * WordPress dependencies @@ -22,4 +23,5 @@ import { italic, link, strikethrough, + invisible, ].forEach( ( { name, ...settings } ) => registerFormatType( name, settings ) ); diff --git a/packages/format-library/src/invisible/index.js b/packages/format-library/src/invisible/index.js new file mode 100644 index 00000000000000..beeeaf20503791 --- /dev/null +++ b/packages/format-library/src/invisible/index.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { applyFormat } from '@wordpress/rich-text'; + +const name = 'core/invisible'; + +export const invisible = { + name, + title: 'invisible', + tagName: 'mark', + className: 'invisible', + prepareEditableTree( formats, text ) { + const search = 'Gutenberg'; + const index = text.indexOf( search ); + + if ( index === -1 ) { + return formats; + } + + const start = index; + const end = index + search.length; + + const newValue = applyFormat( { text, formats }, { type: name }, start, end ); + + return newValue.formats; + }, +}; diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index ec5a7eb1759382..7c25e6a0660281 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -53,6 +53,10 @@ function toFormat( { type, attributes } ) { formatType = select( 'core/rich-text' ).getFormatTypeForBareElement( type ); } + if ( formatType.prepareEditableTree ) { + return null; + } + if ( ! formatType ) { return attributes ? { type, attributes } : { type }; } @@ -359,11 +363,13 @@ function createFromElement( { } ), } ); - // Reuse the last format if it's equal. - if ( isFormatEqual( newFormat, lastFormat ) ) { - format = lastFormat; - } else { - format = newFormat; + if ( newFormat ) { + // Reuse the last format if it's equal. + if ( isFormatEqual( newFormat, lastFormat ) ) { + format = lastFormat; + } else { + format = newFormat; + } } } diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 1b475bf5d89d95..9e87fe277aff5b 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { isFunction } from 'lodash'; - /** * WordPress dependencies */ @@ -45,13 +40,6 @@ export function registerFormatType( name, settings ) { return; } - if ( ! settings || ! isFunction( settings.edit ) ) { - window.console.error( - 'The "edit" property must be specified and must be a valid function.' - ); - return; - } - if ( typeof settings.tagName !== 'string' || settings.tagName === '' diff --git a/packages/rich-text/src/to-dom.js b/packages/rich-text/src/to-dom.js index b5851817b5db7f..b62fce847e9c07 100644 --- a/packages/rich-text/src/to-dom.js +++ b/packages/rich-text/src/to-dom.js @@ -3,6 +3,7 @@ */ import { toTree } from './to-tree'; +import { getFormatTypes } from './get-format-types'; /** * Browser dependencies @@ -132,6 +133,16 @@ function padEmptyLines( { element, createLinePadding, multilineWrapperTags } ) { } } +function prepareFormats( value ) { + return getFormatTypes().reduce( ( accumlator, { prepareEditableTree } ) => { + if ( prepareEditableTree ) { + return prepareEditableTree( accumlator, value.text ); + } + + return accumlator; + }, value.formats ); +} + export function toDom( { value, multilineTag, @@ -142,7 +153,10 @@ export function toDom( { let endPath = []; const tree = toTree( { - value, + value: { + ...value, + formats: prepareFormats( value ), + }, multilineTag, multilineWrapperTags, createEmpty, From 6612db8552bf52adae37f096a4259e2c6ed2774a Mon Sep 17 00:00:00 2001 From: iseulde Date: Wed, 7 Nov 2018 18:43:21 +0100 Subject: [PATCH 02/13] Render editable tree as default value --- .../editor/src/components/rich-text/index.js | 16 +++++++++-- .../src/components/rich-text/tinymce.js | 28 +------------------ 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index e5c5105dc3adb1..f4ae71b38cff5b 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -804,6 +804,19 @@ export class RichText extends Component { return value; } + valueToEditableHTML( value ) { + return unstableToDom( { + value, + multilineTag: this.multilineTag, + multilineWrapperTags: this.multilineWrapperTags, + createLinePadding( doc ) { + const element = doc.createElement( 'br' ); + element.setAttribute( 'data-mce-bogus', '1' ); + return element; + }, + } ).body.innerHTML; + } + valueToFormat( { formats, text } ) { // Handle deprecated `children` and `node` sources. if ( this.usedDeprecatedChildrenSource ) { @@ -829,7 +842,6 @@ export class RichText extends Component { const { tagName: Tagname = 'div', style, - value, wrapperClassName, className, inlineToolbar = false, @@ -878,7 +890,7 @@ export class RichText extends Component { getSettings={ this.getSettings } onSetup={ this.onSetup } style={ style } - defaultValue={ value } + defaultValue={ this.valueToEditableHTML( record ) } isPlaceholderVisible={ isPlaceholderVisible } aria-label={ placeholder } aria-autocomplete="list" diff --git a/packages/editor/src/components/rich-text/tinymce.js b/packages/editor/src/components/rich-text/tinymce.js index e3dfb4145a67c0..11ebd13591e12b 100644 --- a/packages/editor/src/components/rich-text/tinymce.js +++ b/packages/editor/src/components/rich-text/tinymce.js @@ -10,8 +10,6 @@ import classnames from 'classnames'; */ import { Component, createElement } from '@wordpress/element'; import { BACKSPACE, DELETE, ENTER, LEFT, RIGHT } from '@wordpress/keycodes'; -import { toHTMLString } from '@wordpress/rich-text'; -import { children } from '@wordpress/blocks'; /** * Internal dependencies @@ -313,8 +311,6 @@ export default class TinyMCE extends Component { isPlaceholderVisible, onPaste, onInput, - multilineTag, - multilineWrapperTags, onKeyDown, onKeyUp, } = this.props; @@ -332,28 +328,6 @@ export default class TinyMCE extends Component { // If a default value is provided, render it into the DOM even before // TinyMCE finishes initializing. This avoids a short delay by allowing // us to show and focus the content before it's truly ready to edit. - let initialHTML = defaultValue; - - // Guard for blocks passing `null` in onSplit callbacks. May be removed - // if onSplit is revised to not pass a `null` value. - if ( defaultValue === null ) { - initialHTML = ''; - // Handle deprecated `children` and `node` sources. - } else if ( Array.isArray( defaultValue ) ) { - initialHTML = children.toHTML( defaultValue ); - } else if ( typeof defaultValue !== 'string' ) { - initialHTML = toHTMLString( { - value: defaultValue, - multilineTag, - multilineWrapperTags, - } ); - } - - if ( initialHTML === '' ) { - // Ensure the field is ready to receive focus by TinyMCE. - initialHTML = '
'; - } - return createElement( tagName, { ...ariaProps, className: classnames( className, 'editor-rich-text__tinymce' ), @@ -362,7 +336,7 @@ export default class TinyMCE extends Component { ref: this.bindEditorNode, style, suppressContentEditableWarning: true, - dangerouslySetInnerHTML: { __html: initialHTML }, + dangerouslySetInnerHTML: { __html: defaultValue }, onPaste, onInput, onFocus: this.onFocus, From 614ceb4bd9fb9637bdb1b2e1bffa5423f60d5f7b Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 12:26:46 +0100 Subject: [PATCH 03/13] withPrepareEditableTree HoC --- .../editor/src/components/rich-text/index.js | 12 ++++- .../format-library/src/invisible/index.js | 51 ++++++++++++++----- packages/rich-text/src/to-dom.js | 16 +++--- 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index f4ae71b38cff5b..2c89a62e2959ac 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -44,6 +44,7 @@ import { isCollapsed, } from '@wordpress/rich-text'; import { decodeEntities } from '@wordpress/html-entities'; +import { withFilters } from '@wordpress/components'; /** * Internal dependencies @@ -238,6 +239,7 @@ export class RichText extends Component { unwrapNode: ( node ) => !! node.getAttribute( 'data-mce-bogus' ), removeAttribute: ( attribute ) => attribute.indexOf( 'data-mce-' ) === 0, filterString: ( string ) => string.replace( TINYMCE_ZWSP, '' ), + prepareEditableTree: this.props.prepareEditableTree, } ); } @@ -252,6 +254,7 @@ export class RichText extends Component { element.setAttribute( 'data-mce-bogus', '1' ); return element; }, + prepareEditableTree: this.props.prepareEditableTree, } ); } @@ -734,7 +737,7 @@ export class RichText extends Component { } componentDidUpdate( prevProps ) { - const { tagName, value, isSelected } = this.props; + const { tagName, value, isSelected, propsToCheck = [] } = this.props; if ( tagName === prevProps.tagName && @@ -775,6 +778,11 @@ export class RichText extends Component { record.end = length; this.applyRecord( record ); } + + if ( propsToCheck.some( ( name ) => this.props[ name ] !== prevProps[ name ] ) ) { + const record = this.formatToValue( value ); + this.applyRecord( record ); + } } formatToValue( value ) { @@ -814,6 +822,7 @@ export class RichText extends Component { element.setAttribute( 'data-mce-bogus', '1' ); return element; }, + prepareEditableTree: this.props.prepareEditableTree, } ).body.innerHTML; } @@ -980,6 +989,7 @@ const RichTextContainer = compose( [ }; } ), withSafeTimeout, + withFilters( 'RichText' ), ] )( RichText ); RichTextContainer.Content = ( { value, tagName: Tag, multiline, ...props } ) => { diff --git a/packages/format-library/src/invisible/index.js b/packages/format-library/src/invisible/index.js index beeeaf20503791..cba897bd2616dc 100644 --- a/packages/format-library/src/invisible/index.js +++ b/packages/format-library/src/invisible/index.js @@ -2,6 +2,8 @@ * WordPress dependencies */ import { applyFormat } from '@wordpress/rich-text'; +import { addFilter } from '@wordpress/hooks'; +import { withSelect } from '@wordpress/data'; const name = 'core/invisible'; @@ -10,19 +12,44 @@ export const invisible = { title: 'invisible', tagName: 'mark', className: 'invisible', - prepareEditableTree( formats, text ) { - const search = 'Gutenberg'; - const index = text.indexOf( search ); + // Possible to remove? + prepareEditableTree: true, +}; - if ( index === -1 ) { - return formats; - } +function withPrepareEditableTree( OriginalComponent ) { + return withSelect( ( select ) => ( { + isEnabled: select( 'core/edit-post' ).getActiveGeneralSidebarName() === 'edit-post/block', + } ) )( ( props ) => ( + { + if ( ! props.isEnabled ) { + return formats; + } - const start = index; - const end = index + search.length; + const search = 'Gutenberg'; + const index = text.indexOf( search ); - const newValue = applyFormat( { text, formats }, { type: name }, start, end ); + if ( index === -1 ) { + return formats; + } - return newValue.formats; - }, -}; + const start = index; + const end = index + search.length; + + const newValue = applyFormat( { text, formats }, { type: name }, start, end ); + + return newValue.formats; + }, + ] } + /> + ) ); +} + +addFilter( 'RichText', 'my-plugin/with-rich-text-hoc', withPrepareEditableTree ); diff --git a/packages/rich-text/src/to-dom.js b/packages/rich-text/src/to-dom.js index b62fce847e9c07..b582c77083d9a0 100644 --- a/packages/rich-text/src/to-dom.js +++ b/packages/rich-text/src/to-dom.js @@ -3,7 +3,6 @@ */ import { toTree } from './to-tree'; -import { getFormatTypes } from './get-format-types'; /** * Browser dependencies @@ -133,13 +132,9 @@ function padEmptyLines( { element, createLinePadding, multilineWrapperTags } ) { } } -function prepareFormats( value ) { - return getFormatTypes().reduce( ( accumlator, { prepareEditableTree } ) => { - if ( prepareEditableTree ) { - return prepareEditableTree( accumlator, value.text ); - } - - return accumlator; +function prepareFormats( prepareEditableTree = [], value ) { + return prepareEditableTree.reduce( ( accumlator, fn ) => { + return fn( accumlator, value.text ); }, value.formats ); } @@ -148,6 +143,7 @@ export function toDom( { multilineTag, multilineWrapperTags, createLinePadding, + prepareEditableTree, } ) { let startPath = []; let endPath = []; @@ -155,7 +151,7 @@ export function toDom( { const tree = toTree( { value: { ...value, - formats: prepareFormats( value ), + formats: prepareFormats( prepareEditableTree, value ), }, multilineTag, multilineWrapperTags, @@ -202,6 +198,7 @@ export function apply( { multilineTag, multilineWrapperTags, createLinePadding, + prepareEditableTree, } ) { // Construct a new element tree in memory. const { body, selection } = toDom( { @@ -209,6 +206,7 @@ export function apply( { multilineTag, multilineWrapperTags, createLinePadding, + prepareEditableTree, } ); applyValue( body, current ); From 604c1e0a08df63fdc9a8db21384e2a76183546c1 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 13:17:20 +0100 Subject: [PATCH 04/13] Internalise filter --- .../format-library/src/invisible/index.js | 68 +++++++------------ packages/rich-text/src/create.js | 2 +- .../rich-text/src/register-format-type.js | 21 +++++- 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/packages/format-library/src/invisible/index.js b/packages/format-library/src/invisible/index.js index cba897bd2616dc..0f761a16b7b0f4 100644 --- a/packages/format-library/src/invisible/index.js +++ b/packages/format-library/src/invisible/index.js @@ -2,8 +2,6 @@ * WordPress dependencies */ import { applyFormat } from '@wordpress/rich-text'; -import { addFilter } from '@wordpress/hooks'; -import { withSelect } from '@wordpress/data'; const name = 'core/invisible'; @@ -12,44 +10,30 @@ export const invisible = { title: 'invisible', tagName: 'mark', className: 'invisible', - // Possible to remove? - prepareEditableTree: true, + getPropsForEditableTreePreparation( select ) { + return { + isEnabled: select( 'core/edit-post' ).getActiveGeneralSidebarName() === 'edit-post/block', + }; + }, + createPrepareEditableTree( props ) { + return ( formats, text ) => { + if ( ! props.isEnabled ) { + return formats; + } + + const search = 'Gutenberg'; + const index = text.indexOf( search ); + + if ( index === -1 ) { + return formats; + } + + const start = index; + const end = index + search.length; + + const newValue = applyFormat( { text, formats }, { type: name }, start, end ); + + return newValue.formats; + }; + }, }; - -function withPrepareEditableTree( OriginalComponent ) { - return withSelect( ( select ) => ( { - isEnabled: select( 'core/edit-post' ).getActiveGeneralSidebarName() === 'edit-post/block', - } ) )( ( props ) => ( - { - if ( ! props.isEnabled ) { - return formats; - } - - const search = 'Gutenberg'; - const index = text.indexOf( search ); - - if ( index === -1 ) { - return formats; - } - - const start = index; - const end = index + search.length; - - const newValue = applyFormat( { text, formats }, { type: name }, start, end ); - - return newValue.formats; - }, - ] } - /> - ) ); -} - -addFilter( 'RichText', 'my-plugin/with-rich-text-hoc', withPrepareEditableTree ); diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index 7c25e6a0660281..9cceaec30dda74 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -53,7 +53,7 @@ function toFormat( { type, attributes } ) { formatType = select( 'core/rich-text' ).getFormatTypeForBareElement( type ); } - if ( formatType.prepareEditableTree ) { + if ( formatType.createPrepareEditableTree ) { return null; } diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 9e87fe277aff5b..9f56be5b8c1b23 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -1,7 +1,8 @@ /** * WordPress dependencies */ -import { select, dispatch } from '@wordpress/data'; +import { select, dispatch, withSelect } from '@wordpress/data'; +import { addFilter } from '@wordpress/hooks'; /** * Registers a new format provided a unique name and an object defining its @@ -112,5 +113,23 @@ export function registerFormatType( name, settings ) { dispatch( 'core/rich-text' ).addFormatTypes( settings ); + if ( settings.createPrepareEditableTree && settings.getPropsForEditableTreePreparation ) { + addFilter( 'RichText', name, ( OriginalComponent ) => { + return withSelect( settings.getPropsForEditableTreePreparation )( ( props ) => ( + + ) ); + } ); + } + return settings; } From 880123bfe03038c44dcb284395ad27718659c472 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 13:40:59 +0100 Subject: [PATCH 05/13] Remove hook on deregister --- packages/editor/src/components/rich-text/index.js | 2 +- packages/format-library/src/invisible/index.js | 4 ++-- packages/rich-text/src/create.js | 2 +- packages/rich-text/src/register-format-type.js | 11 +++++++---- packages/rich-text/src/unregister-format-type.js | 8 ++++++++ 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 2c89a62e2959ac..cc0870005527db 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -989,7 +989,7 @@ const RichTextContainer = compose( [ }; } ), withSafeTimeout, - withFilters( 'RichText' ), + withFilters( 'experimentalRichText' ), ] )( RichText ); RichTextContainer.Content = ( { value, tagName: Tag, multiline, ...props } ) => { diff --git a/packages/format-library/src/invisible/index.js b/packages/format-library/src/invisible/index.js index 0f761a16b7b0f4..6998cb182ed76e 100644 --- a/packages/format-library/src/invisible/index.js +++ b/packages/format-library/src/invisible/index.js @@ -10,12 +10,12 @@ export const invisible = { title: 'invisible', tagName: 'mark', className: 'invisible', - getPropsForEditableTreePreparation( select ) { + __experimentalGetPropsForEditableTreePreparation( select ) { return { isEnabled: select( 'core/edit-post' ).getActiveGeneralSidebarName() === 'edit-post/block', }; }, - createPrepareEditableTree( props ) { + __experimentalCreatePrepareEditableTree( props ) { return ( formats, text ) => { if ( ! props.isEnabled ) { return formats; diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index 9cceaec30dda74..401dcc00c56f18 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -53,7 +53,7 @@ function toFormat( { type, attributes } ) { formatType = select( 'core/rich-text' ).getFormatTypeForBareElement( type ); } - if ( formatType.createPrepareEditableTree ) { + if ( formatType.__experimentalCreatePrepareEditableTree ) { return null; } diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 9f56be5b8c1b23..8f802a97fb6372 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -113,9 +113,12 @@ export function registerFormatType( name, settings ) { dispatch( 'core/rich-text' ).addFormatTypes( settings ); - if ( settings.createPrepareEditableTree && settings.getPropsForEditableTreePreparation ) { - addFilter( 'RichText', name, ( OriginalComponent ) => { - return withSelect( settings.getPropsForEditableTreePreparation )( ( props ) => ( + if ( + settings.__experimentalCreatePrepareEditableTree && + settings.__experimentalGetPropsForEditableTreePreparation + ) { + addFilter( 'experimentalRichText', name, ( OriginalComponent ) => { + return withSelect( settings.__experimentalGetPropsForEditableTreePreparation )( ( props ) => ( ) ); diff --git a/packages/rich-text/src/unregister-format-type.js b/packages/rich-text/src/unregister-format-type.js index a01b648622342e..cffaa3d025b94b 100644 --- a/packages/rich-text/src/unregister-format-type.js +++ b/packages/rich-text/src/unregister-format-type.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { select, dispatch } from '@wordpress/data'; +import { removeFilter } from '@wordpress/hooks'; /** * Unregisters a format. @@ -21,6 +22,13 @@ export function unregisterFormatType( name ) { return; } + if ( + oldFormat.__experimentalCreatePrepareEditableTree && + oldFormat.__experimentalGetPropsForEditableTreePreparation + ) { + removeFilter( 'experimentalRichText', name ); + } + dispatch( 'core/rich-text' ).removeFormatTypes( name ); return oldFormat; From 759c6a2dc81830541135a2d7745d77fad19f6803 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 14:08:26 +0100 Subject: [PATCH 06/13] prefix props passed --- packages/editor/src/components/rich-text/index.js | 15 +++++++++++++-- packages/rich-text/src/register-format-type.js | 10 ++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index cc0870005527db..4a26f2742dc84c 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -737,7 +737,7 @@ export class RichText extends Component { } componentDidUpdate( prevProps ) { - const { tagName, value, isSelected, propsToCheck = [] } = this.props; + const { tagName, value, isSelected } = this.props; if ( tagName === prevProps.tagName && @@ -779,7 +779,18 @@ export class RichText extends Component { this.applyRecord( record ); } - if ( propsToCheck.some( ( name ) => this.props[ name ] !== prevProps[ name ] ) ) { + // If any format props update, reapply value. + const shouldReapply = Object.keys( this.props ).some( ( name ) => { + if ( name.indexOf( 'format_' ) !== 0 ) { + return false; + } + + return Object.keys( this.props[ name ] ).some( ( subName ) => { + return this.props[ name ][ subName ] !== prevProps[ name ][ subName ]; + } ); + } ); + + if ( shouldReapply ) { const record = this.formatToValue( value ); this.applyRecord( record ); } diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 8f802a97fb6372..f534529b6fadc1 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -118,16 +118,14 @@ export function registerFormatType( name, settings ) { settings.__experimentalGetPropsForEditableTreePreparation ) { addFilter( 'experimentalRichText', name, ( OriginalComponent ) => { - return withSelect( settings.__experimentalGetPropsForEditableTreePreparation )( ( props ) => ( + return withSelect( ( sel ) => ( { + [ `format_${ name }` ]: settings.__experimentalGetPropsForEditableTreePreparation( sel ), + } ) )( ( props ) => ( ) ); From ca8e50e71060a17666c0976ae5fe872b9045b8b3 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 14:35:07 +0100 Subject: [PATCH 07/13] Remove example --- packages/format-library/src/index.js | 2 - .../format-library/src/invisible/index.js | 39 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 packages/format-library/src/invisible/index.js diff --git a/packages/format-library/src/index.js b/packages/format-library/src/index.js index 3767611277f613..387c2666f7e0e6 100644 --- a/packages/format-library/src/index.js +++ b/packages/format-library/src/index.js @@ -7,7 +7,6 @@ import { image } from './image'; import { italic } from './italic'; import { link } from './link'; import { strikethrough } from './strikethrough'; -import { invisible } from './invisible'; /** * WordPress dependencies @@ -23,5 +22,4 @@ import { italic, link, strikethrough, - invisible, ].forEach( ( { name, ...settings } ) => registerFormatType( name, settings ) ); diff --git a/packages/format-library/src/invisible/index.js b/packages/format-library/src/invisible/index.js deleted file mode 100644 index 6998cb182ed76e..00000000000000 --- a/packages/format-library/src/invisible/index.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * WordPress dependencies - */ -import { applyFormat } from '@wordpress/rich-text'; - -const name = 'core/invisible'; - -export const invisible = { - name, - title: 'invisible', - tagName: 'mark', - className: 'invisible', - __experimentalGetPropsForEditableTreePreparation( select ) { - return { - isEnabled: select( 'core/edit-post' ).getActiveGeneralSidebarName() === 'edit-post/block', - }; - }, - __experimentalCreatePrepareEditableTree( props ) { - return ( formats, text ) => { - if ( ! props.isEnabled ) { - return formats; - } - - const search = 'Gutenberg'; - const index = text.indexOf( search ); - - if ( index === -1 ) { - return formats; - } - - const start = index; - const end = index + search.length; - - const newValue = applyFormat( { text, formats }, { type: name }, start, end ); - - return newValue.formats; - }; - }, -}; From 61b0e954e34b77cf60587eb9059d2928609852b1 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 15:00:05 +0100 Subject: [PATCH 08/13] Fix typo --- packages/rich-text/src/create.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index 401dcc00c56f18..5ab6a34e6c903e 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -53,14 +53,14 @@ function toFormat( { type, attributes } ) { formatType = select( 'core/rich-text' ).getFormatTypeForBareElement( type ); } - if ( formatType.__experimentalCreatePrepareEditableTree ) { - return null; - } - if ( ! formatType ) { return attributes ? { type, attributes } : { type }; } + if ( formatType.__experimentalCreatePrepareEditableTree ) { + return null; + } + if ( ! attributes ) { return { type: formatType.name }; } From ca71e2f4454970b6fbf0a5416170457dcbcc851c Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 15:08:27 +0100 Subject: [PATCH 09/13] update snapshots --- .../src/list/test/__snapshots__/index.js.snap | 8 +++++--- .../src/pullquote/test/__snapshots__/index.js.snap | 8 +++++--- .../src/quote/test/__snapshots__/index.js.snap | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/list/test/__snapshots__/index.js.snap b/packages/block-library/src/list/test/__snapshots__/index.js.snap index 4eb99b89869723..80e123d44c6b4d 100644 --- a/packages/block-library/src/list/test/__snapshots__/index.js.snap +++ b/packages/block-library/src/list/test/__snapshots__/index.js.snap @@ -19,9 +19,11 @@ exports[`core/list block edit matches snapshot 1`] = ` data-is-placeholder-visible="true" role="textbox" > -
+
  • +
    +
    • -
      +

      +
      +

      -
      +

      +
      +

      Date: Thu, 8 Nov 2018 15:10:30 +0100 Subject: [PATCH 10/13] Remove test for edit function --- .../rich-text/src/test/register-format-type.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/rich-text/src/test/register-format-type.js b/packages/rich-text/src/test/register-format-type.js index cde3c900e8b41c..5fc78630be2bab 100644 --- a/packages/rich-text/src/test/register-format-type.js +++ b/packages/rich-text/src/test/register-format-type.js @@ -94,24 +94,6 @@ describe( 'registerFormatType', () => { expect( duplicateFormat ).toBeUndefined(); } ); - it( 'should error on undefined edit property', () => { - const format = registerFormatType( 'plugin/test', { - ...validSettings, - edit: undefined, - } ); - expect( console ).toHaveErroredWith( 'The "edit" property must be specified and must be a valid function.' ); - expect( format ).toBeUndefined(); - } ); - - it( 'should reject formats with an invalid edit function', () => { - const format = registerFormatType( validName, { - ...validSettings, - edit: 'not-a-function', - } ); - expect( console ).toHaveErroredWith( 'The "edit" property must be specified and must be a valid function.' ); - expect( format ).toBeUndefined(); - } ); - it( 'should reject formats without tag name', () => { const settings = { ...validSettings }; delete settings.tagName; From 447d63ad217fa875d718e2896b3aefc49adba15d Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 17:14:40 +0100 Subject: [PATCH 11/13] restore _withoutApply --- packages/editor/src/components/rich-text/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 4a26f2742dc84c..2408b32ca9273d 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -416,10 +416,7 @@ export class RichText extends Component { const record = this.createRecord(); const transformed = this.patterns.reduce( ( accumlator, transform ) => transform( accumlator ), record ); - // Don't apply changes if there's no transform. Content will be up to - // date. In the future we could always let it flow back in the live DOM - // if there are no performance issues. - this.onChange( transformed, record === transformed ); + this.onChange( transformed ); } /** @@ -450,11 +447,16 @@ export class RichText extends Component { * updated if differences are found. * * @param {Object} record The record to sync and apply. + * @param {boolean} _withoutApply If true, the record won't be applied to + * the live DOM. */ - onChange( record ) { + onChange( record, _withoutApply ) { + if ( ! _withoutApply ) { + this.applyRecord( record ); + } + const { start, end } = record; - this.applyRecord( record ); this.savedContent = this.valueToFormat( record ); this.props.onChange( this.savedContent ); this.setState( { start, end } ); From aa6f609b9dc0c0ed4b4747ded1084cf0ee26be58 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 8 Nov 2018 17:30:40 +0100 Subject: [PATCH 12/13] Update quote snapshot --- test/e2e/specs/blocks/__snapshots__/quote.test.js.snap | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap b/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap index 5257716d67d00c..6a46b767390efc 100644 --- a/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap +++ b/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap @@ -58,11 +58,7 @@ exports[`Quote can be converted to paragraphs and renders a paragraph for the ci " `; -exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = ` -" -

      -" -`; +exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = `""`; exports[`Quote can be converted to paragraphs and renders one paragraph block per

      within quote 1`] = ` " From 89539fdc319c83520094731c2ca1ef1fd2cff380 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 9 Nov 2018 08:45:51 +0100 Subject: [PATCH 13/13] Support passing a context variable to the custom format prepareEditTreeValue (#11630) --- packages/block-library/src/heading/edit.js | 1 + packages/block-library/src/list/index.js | 1 + packages/block-library/src/paragraph/edit.js | 1 + packages/block-library/src/quote/index.js | 9 +++++++-- packages/editor/src/components/rich-text/index.js | 6 +++++- packages/rich-text/src/register-format-type.js | 15 ++++++++++++--- 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/heading/edit.js b/packages/block-library/src/heading/edit.js index f0f02afb2579b1..711f0c100e4317 100644 --- a/packages/block-library/src/heading/edit.js +++ b/packages/block-library/src/heading/edit.js @@ -42,6 +42,7 @@ export default function HeadingEdit( {

      { ( ! RichText.isEmpty( citation ) || isSelected ) && ( setAttributes( { diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 2408b32ca9273d..a59fc64bf28b6c 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -959,12 +959,15 @@ const RichTextContainer = compose( [ withBlockEditContext( ( context, ownProps ) => { // When explicitly set as not selected, do nothing. if ( ownProps.isSelected === false ) { - return {}; + return { + clientId: context.clientId, + }; } // When explicitly set as selected, use the value stored in the context instead. if ( ownProps.isSelected === true ) { return { isSelected: context.isSelected, + clientId: context.clientId, }; } @@ -972,6 +975,7 @@ const RichTextContainer = compose( [ return { isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, setFocusedElement: context.setFocusedElement, + clientId: context.clientId, }; } ), withSelect( ( select ) => { diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index f534529b6fadc1..38af31b958e90c 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -118,14 +118,23 @@ export function registerFormatType( name, settings ) { settings.__experimentalGetPropsForEditableTreePreparation ) { addFilter( 'experimentalRichText', name, ( OriginalComponent ) => { - return withSelect( ( sel ) => ( { - [ `format_${ name }` ]: settings.__experimentalGetPropsForEditableTreePreparation( sel ), + return withSelect( ( sel, { clientId, identifier } ) => ( { + [ `format_${ name }` ]: settings.__experimentalGetPropsForEditableTreePreparation( + sel, + { + richTextIdentifier: identifier, + blockClientId: clientId, + } + ), } ) )( ( props ) => ( ) );