From 395ad1b1fbaaa4c4462c7b9dd3c2785a51cecd34 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 20 Apr 2017 19:01:12 +0200 Subject: [PATCH 1/7] Text formatting UI --- blocks/components/editable/index.js | 35 ++++++++++++++++++++++++++- blocks/library/text/index.js | 22 ++++++++++++++++- editor/modes/visual-editor/block.js | 34 ++++++++++++++++++++++++++ editor/modes/visual-editor/style.scss | 1 + languages/gutenberg.pot | 12 +++++++++ 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index e3e37a39e0391..5e14e3e769b10 100644 --- a/blocks/components/editable/index.js +++ b/blocks/components/editable/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { last } from 'lodash'; +import { compact, forEach, last, zipObject } from 'lodash'; import { Parser as HtmlToReactParser } from 'html-to-react'; /** @@ -11,6 +11,11 @@ import { Parser as HtmlToReactParser } from 'html-to-react'; import './style.scss'; const htmlToReactParser = new HtmlToReactParser(); +const formatMap = { + strong: 'bold', + em: 'italic', + del: 'strikethrough' +}; export default class Editable extends wp.element.Component { constructor() { @@ -21,6 +26,7 @@ export default class Editable extends wp.element.Component { this.onNewBlock = this.onNewBlock.bind( this ); this.bindNode = this.bindNode.bind( this ); this.onFocus = this.onFocus.bind( this ); + this.formats = {}; } componentDidMount() { @@ -50,6 +56,17 @@ export default class Editable extends wp.element.Component { editor.on( 'focusout', this.onChange ); editor.on( 'NewBlock', this.onNewBlock ); editor.on( 'focusin', this.onFocus ); + + if ( this.props.onFormatChange ) { + editor.on( 'nodechange', ( { parents } ) => { + const path = compact( parents.map( node => + formatMap[ node.nodeName.toLowerCase() ] + ) ); + + this.formats = zipObject( path, path.map( () => true ) ); + this.props.onFormatChange( this.formats ); + } ); + } } onInit() { @@ -183,6 +200,22 @@ export default class Editable extends wp.element.Component { } } + componentWillReceiveProps( nextProps ) { + forEach( nextProps.formats, ( state, format ) => { + const currentState = this.formats[ format ] || false; + + if ( state !== currentState ) { + if ( state ) { + this.editor.focus(); + this.editor.formatter.apply( format ); + } else { + this.editor.focus(); + this.editor.formatter.remove( format ); + } + } + } ); + } + render() { const { tagName: Tag = 'div', style, className } = this.props; const classes = classnames( 'blocks-editable', className ); diff --git a/blocks/library/text/index.js b/blocks/library/text/index.js index 75c60a6c9d79a..f3bfb4b8f8c58 100644 --- a/blocks/library/text/index.js +++ b/blocks/library/text/index.js @@ -44,7 +44,25 @@ registerBlock( 'core/text', { } ], - edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus } ) { + formatting: [ + { + icon: 'editor-bold', + title: wp.i18n.__( 'Bold' ), + format: 'bold' + }, + { + icon: 'editor-italic', + title: wp.i18n.__( 'Italic' ), + format: 'italic' + }, + { + icon: 'editor-strikethrough', + title: wp.i18n.__( 'Strikethrough' ), + format: 'strikethrough' + } + ], + + edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, onFormatChange, formats } ) { const { content =

, align } = attributes; return ( @@ -64,6 +82,8 @@ registerBlock( 'core/text', { content: after } ) ); } } + onFormatChange={ onFormatChange } + formats={ formats } /> ); }, diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 2f48ee0c20f7c..6bedb492eceb8 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -3,6 +3,7 @@ */ import { connect } from 'react-redux'; import classnames from 'classnames'; +import { isEqual } from 'lodash'; /** * Internal dependencies @@ -18,13 +19,36 @@ class VisualEditorBlock extends wp.element.Component { this.setAttributes = this.setAttributes.bind( this ); this.maybeDeselect = this.maybeDeselect.bind( this ); this.maybeHover = this.maybeHover.bind( this ); + this.onFormatChange = this.onFormatChange.bind( this ); + this.toggleFormat = this.toggleFormat.bind( this ); this.previousOffset = null; + this.state = { + formats: {} + }; } bindBlockNode( node ) { this.node = node; } + onFormatChange( formats ) { + if ( ! isEqual( formats, this.state.formats ) ) { + this.setState( ( prevState ) => { + return Object.assign( {}, prevState, { formats } ); + } ); + } + } + + toggleFormat( format ) { + this.setState( ( prevState ) => { + return Object.assign( {}, prevState, { + formats: Object.assign( {}, prevState.formats, { + [ format ]: ! prevState.formats[ format ] + } ) + } ); + } ); + } + componentWillReceiveProps( newProps ) { if ( this.props.order !== newProps.order && @@ -119,6 +143,14 @@ class VisualEditorBlock extends wp.element.Component { isActive: () => control.isActive( block.attributes ) } ) ) } /> ) } + { settings.formatting ? ( + ( { + ...control, + onClick: () => this.toggleFormat( control.format ), + isActive: () => !! this.state.formats[ control.format ] + } ) ) } /> + ) : null } } ); diff --git a/editor/modes/visual-editor/style.scss b/editor/modes/visual-editor/style.scss index 9d55b90e36b75..08ffe6cc45cdb 100644 --- a/editor/modes/visual-editor/style.scss +++ b/editor/modes/visual-editor/style.scss @@ -49,6 +49,7 @@ .editor-visual-editor__block-controls .editor-toolbar { display: inline-flex; + margin-right: 10px; } .editor-visual-editor__block-controls .editor-block-switcher { diff --git a/languages/gutenberg.pot b/languages/gutenberg.pot index 9976a7358a837..b516e90d86709 100644 --- a/languages/gutenberg.pot +++ b/languages/gutenberg.pot @@ -63,6 +63,18 @@ msgstr "" msgid "Text" msgstr "" +#: blocks/library/text/index.js:50 +msgid "Bold" +msgstr "" + +#: blocks/library/text/index.js:55 +msgid "Italic" +msgstr "" + +#: blocks/library/text/index.js:60 +msgid "Strikethrough" +msgstr "" + #: editor/components/inserter/index.js:37 msgid "Insert block" msgstr "" From c48a996145644ec86fea55fe820a3b3c1b88cf94 Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 24 Apr 2017 12:40:18 +0200 Subject: [PATCH 2/7] Move nodeChange callback to separate function --- blocks/components/editable/index.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index 5e14e3e769b10..fed41ec37fbec 100644 --- a/blocks/components/editable/index.js +++ b/blocks/components/editable/index.js @@ -26,6 +26,7 @@ export default class Editable extends wp.element.Component { this.onNewBlock = this.onNewBlock.bind( this ); this.bindNode = this.bindNode.bind( this ); this.onFocus = this.onFocus.bind( this ); + this.onNodeChange = this.onNodeChange.bind( this ); this.formats = {}; } @@ -58,14 +59,7 @@ export default class Editable extends wp.element.Component { editor.on( 'focusin', this.onFocus ); if ( this.props.onFormatChange ) { - editor.on( 'nodechange', ( { parents } ) => { - const path = compact( parents.map( node => - formatMap[ node.nodeName.toLowerCase() ] - ) ); - - this.formats = zipObject( path, path.map( () => true ) ); - this.props.onFormatChange( this.formats ); - } ); + editor.on( 'nodechange', this.onNodeChange ); } } @@ -136,6 +130,15 @@ export default class Editable extends wp.element.Component { } ); } + onNodeChange( { parents } ) { + const path = compact( parents.map( node => + formatMap[ node.nodeName.toLowerCase() ] + ) ); + + this.formats = zipObject( path, path.map( () => true ) ); + this.props.onFormatChange( this.formats ); + } + bindNode( ref ) { this.node = ref; } From 6546b7cffed53ced61d00d20f4327b5ead6fb1e5 Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 24 Apr 2017 12:44:38 +0200 Subject: [PATCH 3/7] Remove double --- blocks/components/editable/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index fed41ec37fbec..12d5b17f38817 100644 --- a/blocks/components/editable/index.js +++ b/blocks/components/editable/index.js @@ -208,11 +208,11 @@ export default class Editable extends wp.element.Component { const currentState = this.formats[ format ] || false; if ( state !== currentState ) { + this.editor.focus(); + if ( state ) { - this.editor.focus(); this.editor.formatter.apply( format ); } else { - this.editor.focus(); this.editor.formatter.remove( format ); } } From 4e00fec1e6b2a41b6d0df8ffba610ec37766fa9c Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 24 Apr 2017 12:53:07 +0200 Subject: [PATCH 4/7] Remove unnecessary logic in and --- editor/modes/visual-editor/block.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 6bedb492eceb8..3d01920ee58c1 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -32,20 +32,17 @@ class VisualEditorBlock extends wp.element.Component { } onFormatChange( formats ) { - if ( ! isEqual( formats, this.state.formats ) ) { - this.setState( ( prevState ) => { - return Object.assign( {}, prevState, { formats } ); - } ); - } + this.setState( { formats } ); } toggleFormat( format ) { - this.setState( ( prevState ) => { - return Object.assign( {}, prevState, { - formats: Object.assign( {}, prevState.formats, { - [ format ]: ! prevState.formats[ format ] - } ) - } ); + const { formats } = this.state; + + this.setState( { + formats: { + ...formats, + [ format ]: ! formats[ format ] + } } ); } From 76e3da696c388abc262709e7963e081baebe3d8c Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 24 Apr 2017 12:57:46 +0200 Subject: [PATCH 5/7] Fix linting error --- editor/modes/visual-editor/block.js | 1 - 1 file changed, 1 deletion(-) diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 3d01920ee58c1..5120f1acffac4 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -3,7 +3,6 @@ */ import { connect } from 'react-redux'; import classnames from 'classnames'; -import { isEqual } from 'lodash'; /** * Internal dependencies From 6b61f8cd12d377040750ce72eb1f15995fea5290 Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 24 Apr 2017 13:11:19 +0200 Subject: [PATCH 6/7] Move formatting control settings to --- blocks/library/text/index.js | 18 ------------------ editor/modes/visual-editor/block.js | 28 +++++++++++++++++++++++++--- languages/gutenberg.pot | 24 ++++++++++++------------ 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/blocks/library/text/index.js b/blocks/library/text/index.js index f3bfb4b8f8c58..5d1084fd146cb 100644 --- a/blocks/library/text/index.js +++ b/blocks/library/text/index.js @@ -44,24 +44,6 @@ registerBlock( 'core/text', { } ], - formatting: [ - { - icon: 'editor-bold', - title: wp.i18n.__( 'Bold' ), - format: 'bold' - }, - { - icon: 'editor-italic', - title: wp.i18n.__( 'Italic' ), - format: 'italic' - }, - { - icon: 'editor-strikethrough', - title: wp.i18n.__( 'Strikethrough' ), - format: 'strikethrough' - } - ], - edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, onFormatChange, formats } ) { const { content =

, align } = attributes; diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index 5120f1acffac4..6f7c705a697b1 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -11,6 +11,24 @@ import Toolbar from 'components/toolbar'; import BlockMover from 'components/block-mover'; import BlockSwitcher from 'components/block-switcher'; +const formattingControls = [ + { + icon: 'editor-bold', + title: wp.i18n.__( 'Bold' ), + format: 'bold' + }, + { + icon: 'editor-italic', + title: wp.i18n.__( 'Italic' ), + format: 'italic' + }, + { + icon: 'editor-strikethrough', + title: wp.i18n.__( 'Strikethrough' ), + format: 'strikethrough' + } +]; + class VisualEditorBlock extends wp.element.Component { constructor() { super( ...arguments ); @@ -31,6 +49,10 @@ class VisualEditorBlock extends wp.element.Component { } onFormatChange( formats ) { + if ( ! this.state.hasEditable ) { + this.setState( { hasEditable: true } ); + } + this.setState( { formats } ); } @@ -139,14 +161,14 @@ class VisualEditorBlock extends wp.element.Component { isActive: () => control.isActive( block.attributes ) } ) ) } /> ) } - { settings.formatting ? ( + { this.state.hasEditable && ( ( { + controls={ formattingControls.map( ( control ) => ( { ...control, onClick: () => this.toggleFormat( control.format ), isActive: () => !! this.state.formats[ control.format ] } ) ) } /> - ) : null } + ) } } Date: Mon, 24 Apr 2017 13:21:48 +0200 Subject: [PATCH 7/7] Optimise in . Props @aduth --- blocks/components/editable/index.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index 12d5b17f38817..bc524c5320525 100644 --- a/blocks/components/editable/index.js +++ b/blocks/components/editable/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { compact, forEach, last, zipObject } from 'lodash'; +import { forEach, last } from 'lodash'; import { Parser as HtmlToReactParser } from 'html-to-react'; /** @@ -131,11 +131,16 @@ export default class Editable extends wp.element.Component { } onNodeChange( { parents } ) { - const path = compact( parents.map( node => - formatMap[ node.nodeName.toLowerCase() ] - ) ); + this.formats = parents.reduce( ( result, node ) => { + const tag = node.nodeName.toLowerCase(); + + if ( formatMap.hasOwnProperty( tag ) ) { + result[ formatMap[ tag ] ] = true; + } + + return result; + }, {} ); - this.formats = zipObject( path, path.map( () => true ) ); this.props.onFormatChange( this.formats ); }