diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js index e3e37a39e0391..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 { last } from 'lodash'; +import { forEach, last } 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,8 @@ 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 = {}; } componentDidMount() { @@ -50,6 +57,10 @@ 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', this.onNodeChange ); + } } onInit() { @@ -119,6 +130,20 @@ export default class Editable extends wp.element.Component { } ); } + onNodeChange( { parents } ) { + this.formats = parents.reduce( ( result, node ) => { + const tag = node.nodeName.toLowerCase(); + + if ( formatMap.hasOwnProperty( tag ) ) { + result[ formatMap[ tag ] ] = true; + } + + return result; + }, {} ); + + this.props.onFormatChange( this.formats ); + } + bindNode( ref ) { this.node = ref; } @@ -183,6 +208,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 ) { + this.editor.focus(); + + if ( state ) { + this.editor.formatter.apply( format ); + } else { + 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..5d1084fd146cb 100644 --- a/blocks/library/text/index.js +++ b/blocks/library/text/index.js @@ -44,7 +44,7 @@ registerBlock( 'core/text', { } ], - edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus } ) { + edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, onFormatChange, formats } ) { const { content =

, align } = attributes; return ( @@ -64,6 +64,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..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 ); @@ -18,13 +36,37 @@ 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 ( ! this.state.hasEditable ) { + this.setState( { hasEditable: true } ); + } + + this.setState( { formats } ); + } + + toggleFormat( format ) { + const { formats } = this.state; + + this.setState( { + formats: { + ...formats, + [ format ]: ! formats[ format ] + } + } ); + } + componentWillReceiveProps( newProps ) { if ( this.props.order !== newProps.order && @@ -119,6 +161,14 @@ class VisualEditorBlock extends wp.element.Component { isActive: () => control.isActive( block.attributes ) } ) ) } /> ) } + { this.state.hasEditable && ( + ( { + ...control, + onClick: () => this.toggleFormat( control.format ), + isActive: () => !! this.state.formats[ control.format ] + } ) ) } /> + ) } } ); 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..30725235af442 100644 --- a/languages/gutenberg.pot +++ b/languages/gutenberg.pot @@ -95,6 +95,18 @@ msgstr "" msgid "Publish" msgstr "" +#: editor/modes/visual-editor/block.js:17 +msgid "Bold" +msgstr "" + +#: editor/modes/visual-editor/block.js:22 +msgid "Italic" +msgstr "" + +#: editor/modes/visual-editor/block.js:27 +msgid "Strikethrough" +msgstr "" + #: editor/header/mode-switcher/index.js:24 msgctxt "Name for the Text editor tab (formerly HTML)" msgid "Text"