diff --git a/blocks/components/editable/index.js b/blocks/components/editable/index.js
index bc524c53205250..072615f44564fc 100644
--- a/blocks/components/editable/index.js
+++ b/blocks/components/editable/index.js
@@ -2,14 +2,19 @@
* External dependencies
*/
import classnames from 'classnames';
-import { forEach, last } from 'lodash';
+import { last, isEqual } from 'lodash';
import { Parser as HtmlToReactParser } from 'html-to-react';
+import { Fill } from 'react-slot-fill';
/**
* Internal dependencies
*/
import './style.scss';
+ // TODO: We mustn't import by relative path traversing from blocks to editor
+ // as we're doing here; instead, we should consider a common components path.
+import Toolbar from '../../../editor/components/toolbar';
+
const htmlToReactParser = new HtmlToReactParser();
const formatMap = {
strong: 'bold',
@@ -17,17 +22,39 @@ const formatMap = {
del: 'strikethrough'
};
+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'
+ }
+];
+
export default class Editable extends wp.element.Component {
constructor() {
super( ...arguments );
+
this.onInit = this.onInit.bind( this );
this.onSetup = this.onSetup.bind( this );
this.onChange = this.onChange.bind( this );
this.onNewBlock = this.onNewBlock.bind( this );
- this.bindNode = this.bindNode.bind( this );
+ this.bindEditorNode = this.bindEditorNode.bind( this );
this.onFocus = this.onFocus.bind( this );
this.onNodeChange = this.onNodeChange.bind( this );
- this.formats = {};
+
+ this.state = {
+ formats: {}
+ };
}
componentDidMount() {
@@ -36,7 +63,7 @@ export default class Editable extends wp.element.Component {
initialize() {
const config = {
- target: this.node,
+ target: this.editorNode,
theme: false,
inline: true,
toolbar: false,
@@ -57,10 +84,7 @@ 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 );
- }
+ editor.on( 'nodechange', this.onNodeChange );
}
onInit() {
@@ -131,7 +155,7 @@ export default class Editable extends wp.element.Component {
}
onNodeChange( { parents } ) {
- this.formats = parents.reduce( ( result, node ) => {
+ const formats = parents.reduce( ( result, node ) => {
const tag = node.nodeName.toLowerCase();
if ( formatMap.hasOwnProperty( tag ) ) {
@@ -141,11 +165,13 @@ export default class Editable extends wp.element.Component {
return result;
}, {} );
- this.props.onFormatChange( this.formats );
+ if ( ! isEqual( this.state.formats, formats ) ) {
+ this.setState( { formats } );
+ }
}
- bindNode( ref ) {
- this.node = ref;
+ bindEditorNode( ref ) {
+ this.editorNode = ref;
}
updateContent() {
@@ -208,31 +234,46 @@ export default class Editable extends wp.element.Component {
}
}
- componentWillReceiveProps( nextProps ) {
- forEach( nextProps.formats, ( state, format ) => {
- const currentState = this.formats[ format ] || false;
+ isFormatActive( format ) {
+ return !! this.state.formats[ format ];
+ }
- if ( state !== currentState ) {
- this.editor.focus();
+ toggleFormat( format ) {
+ this.editor.focus();
- if ( state ) {
- this.editor.formatter.apply( format );
- } else {
- this.editor.formatter.remove( format );
- }
- }
- } );
+ if ( this.isFormatActive( format ) ) {
+ this.editor.formatter.remove( format );
+ } else {
+ this.editor.formatter.apply( format );
+ }
}
render() {
- const { tagName: Tag = 'div', style, className } = this.props;
+ const { tagName: Tag = 'div', style, focus, className } = this.props;
const classes = classnames( 'blocks-editable', className );
- return (
+ let element = (
+ className={ classes }
+ key="editor" />
);
+
+ if ( focus ) {
+ element = [
+
+ ( {
+ ...control,
+ onClick: () => this.toggleFormat( control.format ),
+ isActive: this.isFormatActive( control.format )
+ } ) ) } />
+ ,
+ element
+ ];
+ }
+
+ return element;
}
}
diff --git a/blocks/library/text/index.js b/blocks/library/text/index.js
index 5d1084fd146cbd..75c60a6c9d79a1 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, onFormatChange, formats } ) {
+ edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus } ) {
const { content =
, align } = attributes;
return (
@@ -64,8 +64,6 @@ registerBlock( 'core/text', {
content: after
} ) );
} }
- onFormatChange={ onFormatChange }
- formats={ formats }
/>
);
},
diff --git a/editor/index.js b/editor/index.js
index 5c9b8ab0d4c4b8..4a003fdf0de398 100644
--- a/editor/index.js
+++ b/editor/index.js
@@ -1,7 +1,8 @@
/**
* External dependencies
*/
-import { Provider } from 'react-redux';
+import { Provider as ReduxProvider } from 'react-redux';
+import { Provider as SlotFillProvider } from 'react-slot-fill';
/**
* Internal dependencies
@@ -24,9 +25,11 @@ export function createEditorInstance( id, post ) {
} );
wp.element.render(
-
-
- ,
+
+
+
+
+ ,
document.getElementById( id )
);
}
diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js
index 441287f052a0f2..f524930ee38fd1 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 { Slot } from 'react-slot-fill';
/**
* Internal dependencies
@@ -11,24 +12,6 @@ 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 );
@@ -36,37 +19,13 @@ 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 &&
@@ -169,14 +128,7 @@ 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/languages/gutenberg.pot b/languages/gutenberg.pot
index c55d14c19e9142..69d5764cf232ae 100644
--- a/languages/gutenberg.pot
+++ b/languages/gutenberg.pot
@@ -3,6 +3,18 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"X-Generator: babel-plugin-wp-i18n\n"
+#: blocks/components/editable/index.js:28
+msgid "Bold"
+msgstr ""
+
+#: blocks/components/editable/index.js:33
+msgid "Italic"
+msgstr ""
+
+#: blocks/components/editable/index.js:38
+msgid "Strikethrough"
+msgstr ""
+
#: blocks/library/embed/index.js:10
msgid "Embed"
msgstr ""
@@ -102,18 +114,6 @@ 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"
diff --git a/package.json b/package.json
index a90facfc9c60c0..48df1ab96db118 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"react-autosize-textarea": "^0.4.2",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
+ "react-slot-fill": "^1.0.0-alpha.11",
"redux": "^3.6.0",
"uuid": "^3.0.1"
}
diff --git a/webpack.config.js b/webpack.config.js
index 2907fb75b43b83..352778f3eedfca 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -25,6 +25,13 @@ const config = {
'react-dom': 'ReactDOM',
'react-dom/server': 'ReactDOMServer'
},
+ resolve: {
+ alias: {
+ // There are currently resolution errors on RSF's "mitt" dependency
+ // when imported as native ES module
+ 'react-slot-fill': 'react-slot-fill/lib/rsf.js'
+ }
+ },
module: {
rules: [
{