diff --git a/editor/assets/stylesheets/main.scss b/editor/assets/stylesheets/main.scss
index 63a59ee44950e4..c5c5023a54f7aa 100644
--- a/editor/assets/stylesheets/main.scss
+++ b/editor/assets/stylesheets/main.scss
@@ -1,21 +1,28 @@
+@import './variables';
@import '../../inserter/style';
+@import '../../editor/style';
body.toplevel_page_gutenberg {
background: #fff;
+
+ #update-nag, .update-nag {
+ position: absolute;
+ right: 0;
+ top: 50px;
+ }
+
+ #wpcontent {
+ padding-left: 0;
+ }
}
.gutenberg {
- margin: -20px 0 0 -20px;
- padding: 60px;
-
* {
box-sizing: border-box;
}
}
.gutenberg__editor {
- max-width: 700px;
- margin: 0 auto;
position: relative;
img {
max-width: 100%;
diff --git a/editor/assets/stylesheets/variables.scss b/editor/assets/stylesheets/variables.scss
new file mode 100644
index 00000000000000..730512d97cd07d
--- /dev/null
+++ b/editor/assets/stylesheets/variables.scss
@@ -0,0 +1,2 @@
+$gray: #575D65;
+$light-gray: #e0e5e9;
diff --git a/editor/editor/editor.js b/editor/editor/editor.js
deleted file mode 100644
index 6d2e6f315d9bd6..00000000000000
--- a/editor/editor/editor.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Internal dependencies
- */
-import InserterButton from '../inserter/button';
-
-const Editor = ( { state: { blocks, inserter }, toggleInserter } ) => {
- return (
-
-
- { blocks.map( ( block, index ) =>
-
- { wp.blocks.getBlockSettings( block.blockType ).edit( block.attributes ) }
-
- ) }
-
-
-
- );
-};
-
-export default Editor;
diff --git a/editor/editor/index.js b/editor/editor/index.js
index fc720e1c6fb817..cb7e9731229959 100644
--- a/editor/editor/index.js
+++ b/editor/editor/index.js
@@ -1,35 +1,18 @@
/**
* Internal dependencies
*/
-import EditorComponent from './editor';
+import EditorLayout from './layout';
export default class Editor {
constructor( id, settings ) {
- this.toggleInserter = this.toggleInserter.bind( this );
this.id = id;
this.settings = settings;
- this.state = {
- inserter: { opened: false },
- blocks: wp.blocks.parse( settings.content )
- };
- console.log( this.state.blocks ); // eslint-disable-line no-console
this.render();
}
- setState( newState ) {
- this.state = Object.assign( {}, this.state, newState );
- this.render();
- }
-
- toggleInserter() {
- this.setState( {
- inserter: { opened: ! this.state.inserter.opened }
- } );
- }
-
render() {
wp.element.render(
- ,
+ ,
document.getElementById( this.id )
);
}
diff --git a/editor/editor/layout.js b/editor/editor/layout.js
new file mode 100644
index 00000000000000..064972747b7633
--- /dev/null
+++ b/editor/editor/layout.js
@@ -0,0 +1,50 @@
+/**
+ * Internal dependencies
+ */
+import ModeSwitcher from './mode-switcher';
+import EditorText from './mode/text';
+import EditorVisual from './mode/visual';
+
+class Layout extends wp.element.Component {
+ constructor( props ) {
+ super( ...arguments );
+ this.switchMode = this.switchMode.bind( this );
+ this.state = {
+ mode: 'visual',
+ html: props.initialContent,
+ blocks: wp.blocks.parse( this.props.initialContent )
+ };
+ }
+
+ switchMode( newMode ) {
+ // TODO: we need a serializer from blocks here
+ const html = this.state.html;
+ const blocks = this.mode === 'visual' ? this.state.blocks : wp.blocks.parse( this.state.html );
+ this.setState( {
+ mode: newMode,
+ html,
+ blocks
+ } );
+ }
+
+ createChangeHandler( type ) {
+ return ( value ) => this.setState( { [ type ]: value } );
+ }
+
+ render() {
+ const { mode, html, blocks } = this.state;
+ return (
+
+
+
+
+
+ { mode === 'text' && }
+ { mode === 'visual' && }
+
+
+ );
+ }
+}
+
+export default Layout;
diff --git a/editor/editor/mode-switcher.js b/editor/editor/mode-switcher.js
new file mode 100644
index 00000000000000..2743d8f7acad1f
--- /dev/null
+++ b/editor/editor/mode-switcher.js
@@ -0,0 +1,53 @@
+class ModeSwitcher extends wp.element.Component {
+ constructor() {
+ super( ...arguments );
+ this.toggle = this.toggle.bind( this );
+ this.state = {
+ opened: false
+ };
+ }
+
+ toggle() {
+ this.setState( {
+ opened: ! this.state.opened
+ } );
+ }
+
+ render() {
+ const { opened } = this.state;
+ const modes = [
+ { value: 'visual', label: 'Visual' },
+ { value: 'text', label: 'Text' },
+ ];
+ const switchMode = ( mode ) => () => {
+ this.setState( { opened: false } );
+ this.props.onSwitch( mode );
+ };
+ const currentMode = modes.find( ( { value } ) => value === this.props.mode );
+
+ return (
+
+
+ { opened &&
+
+
+ { modes.map( ( mode ) =>
+
+ ) }
+
+ }
+
+ );
+ }
+}
+
+export default ModeSwitcher;
diff --git a/editor/editor/mode/text.js b/editor/editor/mode/text.js
new file mode 100644
index 00000000000000..708d4c6a0080ea
--- /dev/null
+++ b/editor/editor/mode/text.js
@@ -0,0 +1,13 @@
+function Text( { html, onChange } ) {
+ const changeValue = ( event ) => {
+ onChange( event.target.value );
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default Text;
diff --git a/editor/editor/mode/visual.js b/editor/editor/mode/visual.js
new file mode 100644
index 00000000000000..945783aecddae1
--- /dev/null
+++ b/editor/editor/mode/visual.js
@@ -0,0 +1,34 @@
+/**
+ * Internal dependencies
+ */
+import InserterButton from '../../inserter/button';
+
+function Blocks( { blocks, onChange } ) {
+ const onChangeBlock = ( index ) => ( changes ) => {
+ const newBlock = {
+ ...blocks[ index ],
+ changes
+ };
+
+ onChange( [
+ ...blocks.slice( 0, index ),
+ newBlock,
+ ...blocks.slice( index + 1 )
+ ] );
+ };
+
+ return (
+
+
+ { blocks.map( ( block, index ) =>
+
+ { wp.blocks.getBlockSettings( block.blockType ).edit( block.attributes, onChangeBlock( index ) ) }
+
+ ) }
+
+
+
+ );
+}
+
+export default Blocks;
diff --git a/editor/editor/style.scss b/editor/editor/style.scss
new file mode 100644
index 00000000000000..91ed2e4351ad61
--- /dev/null
+++ b/editor/editor/style.scss
@@ -0,0 +1,85 @@
+.editor__header {
+ padding: 10px;
+ border-bottom: 1px solid $light-gray;
+}
+
+.editor-mode-switcher {
+ position: relative;
+}
+
+.editor-mode-switcher__toggle {
+ color: $gray;
+ padding: 8px;
+ align-items: center;
+ cursor: pointer;
+ border: none;
+ background: none;
+ border-right: 1px solid $light-gray;
+ vertical-align: middle;
+ line-height: 20px;
+ outline: none;
+}
+
+.editor-mode-switcher__content {
+ position: absolute;
+ top: 40px;
+ border: 1px solid $light-gray;
+ color: $gray;
+ min-width: 100px;
+
+ button {
+ padding: 8px;
+ display: block;
+ border: none;
+ background: white;
+ outline: none;
+ cursor: pointer;
+ width: 100%;
+ }
+}
+
+.editor-mode-switcher__arrow {
+ border: 10px dashed $light-gray;
+ height: 0;
+ line-height: 0;
+ position: absolute;
+ width: 0;
+ z-index: 1;
+ top: -10px;
+ left: 50%;
+ margin-left: -10px;
+ border-bottom-style: solid;
+ border-top: none;
+ border-left-color: transparent;
+ border-right-color: transparent;
+
+ &:before {
+ top: 2px;
+ border: 10px solid white;
+ content: " ";
+ position: absolute;
+ left: 50%;
+ margin-left: -10px;
+ border-bottom-style: solid;
+ border-top: none;
+ border-left-color: transparent;
+ border-right-color: transparent;
+ }
+}
+
+.editor-mode-text {
+ padding: 60px;
+ width: 700px;
+ margin: 0 auto;
+
+ textarea {
+ width: 100%;
+ min-height: 300px;
+ }
+}
+
+.editor-mode-visual {
+ padding: 60px;
+ width: 700px;
+ margin: 0 auto;
+}
diff --git a/editor/inserter/button.js b/editor/inserter/button.js
index ff55376a659cb5..5051fa9de8198e 100644
--- a/editor/inserter/button.js
+++ b/editor/inserter/button.js
@@ -3,23 +3,38 @@
*/
import Inserter from './';
-const InserterButton = ( { opened, onClick } ) => {
- const toggle = ( event ) => {
- event.preventDefault();
- onClick();
- };
- return (
-
-
- { opened && }
-
- );
-};
+class InserterButton extends wp.element.Component {
+ constructor() {
+ super( ...arguments );
+ this.toggle = this.toggle.bind( this );
+ this.state = {
+ opened: false
+ };
+ }
+
+ toggle() {
+ this.setState( {
+ opened: ! this.state.opened
+ } );
+ }
+
+ render() {
+ const { opened } = this.state;
+
+ return (
+
+
+ { opened && }
+
+ );
+ }
+}
export default InserterButton;
diff --git a/editor/inserter/index.js b/editor/inserter/index.js
index 11113cc592a31b..53f9a088d2ab28 100644
--- a/editor/inserter/index.js
+++ b/editor/inserter/index.js
@@ -1,4 +1,4 @@
-const Inserter = () => {
+function Inserter() {
const blocks = wp.blocks.getBlocks();
return (
@@ -17,6 +17,6 @@ const Inserter = () => {
);
-};
+}
export default Inserter;
diff --git a/editor/inserter/style.scss b/editor/inserter/style.scss
index 46ac47435b1dea..b0e48ee7c6f564 100644
--- a/editor/inserter/style.scss
+++ b/editor/inserter/style.scss
@@ -9,11 +9,11 @@
.inserter__button-toggle {
display: inline-block;
- color: #87919d;
+ color: $gray;
background: none;
cursor: pointer;
border-radius: 50%;
- border: 2px solid #87919d;
+ border: 2px solid $gray;
width: 26px;
height: 26px;
padding: 0;
@@ -35,7 +35,7 @@
.inserter {
width: 280px;
box-shadow: 0px 3px 20px rgba( 18, 24, 30, .1 ), 0px 1px 3px rgba( 18, 24, 30, .1 );
- border: 1px solid #e0e5e9;
+ border: 1px solid $light-gray;
position: absolute;
left: -127px;
bottom: 42px;
@@ -43,7 +43,7 @@
}
.inserter__arrow {
- border: 10px dashed #e0e5e9;
+ border: 10px dashed $light-gray;
height: 0;
line-height: 0;
position: absolute;
@@ -85,7 +85,7 @@
width: 100%;
border: none;
margin: 0;
- border-top: 1px solid #e0e5e9;
+ border-top: 1px solid $light-gray;
padding: 8px 16px;
font-size: 13px;
diff --git a/webpack.config.js b/webpack.config.js
index 3f35ff8bcd8363..f34bec3ad3b6ab 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -75,11 +75,11 @@ switch ( process.env.NODE_ENV ) {
case 'test':
config.target = 'node';
config.module.rules = [
- ...config.module.rules,
...[ 'element', 'blocks', 'editor' ].map( ( entry ) => ( {
test: require.resolve( './' + entry + '/index.js' ),
use: 'expose-loader?wp.' + entry
- } ) )
+ } ) ),
+ ...config.module.rules
];
config.entry = [
'./element/index.js',