diff --git a/shared/index.css b/shared/index.css index 2d93b543545d5..ec3992d37e81d 100644 --- a/shared/index.css +++ b/shared/index.css @@ -148,6 +148,8 @@ body { #editor figcaption { margin-top: 0.5em; + font-size: 0.9em; + font-style: italic; } #editor figure img, @@ -180,6 +182,22 @@ body { content: '— ' } +#editor table { + border-collapse: collapse; + table-layout: fixed; + width: 100%; +} + +#editor table { + width: 100%; +} + +#editor td, +#editor th { + padding: 0.5em; + border: 1px solid currentColor; +} + #editor:after { content: "."; visibility: hidden; diff --git a/shared/tinymce/toolbar.js b/shared/tinymce/toolbar.js index 096fd64e26f0f..eeb1369a314d3 100644 --- a/shared/tinymce/toolbar.js +++ b/shared/tinymce/toolbar.js @@ -55,6 +55,47 @@ } } ) ); + tinymce.ui.Factory.add( 'svglistbox', tinymce.ui.ListBox.extend( { + renderHtml: function() { + var id = this._id; + var prefix = this.classPrefix; + var icon = this.state.get( 'icon' ); + var text = this.state.get( 'text' ); + var html = ''; + + if ( icon && icon.indexOf( 'gridicons-' ) === 0 ) { + html += ( + '' + + '' + + '' + ); + } else if ( icon ) { + html += ''; + } + + if ( text ) { + this.classes.add( 'btn-has-text' ); + html += '' + this.encode( text ) + ''; + } + + html += ( + '' + + '' + + '' + ); + + this.aria( 'role', 'button' ); + + return ( + '
' + + '' + + '
' + ); + } + } ) ); + tinymce.PluginManager.add( 'toolbar', function( editor ) { var each = tinymce.each; var DOM = tinymce.DOM; @@ -76,9 +117,9 @@ var itemName; function onClick( callback ) { - return function() { + return function( event ) { editor.undoManager.transact( function() { - callback( window.wp.blocks.getSelectedBlock(), editor ); + callback( window.wp.blocks.getSelectedBlock(), editor, event ); } ); } } diff --git a/tinymce-single/blocks.js b/tinymce-single/blocks.js index 49205351e56ab..7fe08c09006dc 100644 --- a/tinymce-single/blocks.js +++ b/tinymce-single/blocks.js @@ -23,6 +23,18 @@ } ); } }, + getType: function( name ) { + var settings = []; + var key; + + for ( key in _blocks ) { + if ( _blocks[ key ].type === name ) { + settings.push( _blocks[ key ] ); + } + } + + return settings; + }, registerControl: function( name, settings ) { _controls[ name ] = settings; }, diff --git a/tinymce-single/blocks/core/table/register.js b/tinymce-single/blocks/core/table/register.js new file mode 100644 index 0000000000000..933d8654707de --- /dev/null +++ b/tinymce-single/blocks/core/table/register.js @@ -0,0 +1,84 @@ +( function( wp ) { + + function insertEmpty() { + return ( + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '




' + + '
' + ); + } + + wp.blocks.registerBlock( { + name: 'table', + nameSpace: 'core', + displayName: 'Table', + type: 'media', + icon: 'gridicons-grid', + editable: [ 'table', 'figcaption' ], + insert: insertEmpty, + controls: [ + 'block-align-left', + 'block-align-center', + 'block-align-right', + 'block-align-full', + { + classes: 'gridicons-rotate', + icon: 'gridicons-indent-right', + onClick: function( block, editor ) { + editor.execCommand( 'mceTableInsertRowBefore' ); + } + }, + { + classes: 'gridicons-rotate', + icon: 'gridicons-indent-left', + onClick: function( block, editor ) { + editor.execCommand( 'mceTableInsertRowAfter' ); + } + }, + { + icon: 'gridicons-indent-right', + onClick: function( block, editor ) { + editor.execCommand( 'mceTableInsertColBefore' ); + } + }, + { + icon: 'gridicons-indent-left', + onClick: function( block, editor ) { + editor.execCommand( 'mceTableInsertColAfter' ); + } + }, + { + icon: 'gridicons-caption', + onClick: function( block ) { + var figcaption = block.querySelector( 'figcaption' ); + + if ( figcaption ) { + block.removeChild( figcaption ); + } else { + block.insertAdjacentHTML( 'beforeend', + '

' ); + } + + window.wp.blocks.selectBlock( block ); + }, + isActive: function( block ) { + return !! block.querySelector( 'figcaption' ); + } + }, + { + icon: 'gridicons-cog', + onClick: function() {} + } + ] + } ); + +} )( window.wp ); diff --git a/tinymce-single/blocks/elements/blockquote/register.js b/tinymce-single/blocks/elements/blockquote/register.js index 0e15d6d792f99..6c60101ea50f6 100644 --- a/tinymce-single/blocks/elements/blockquote/register.js +++ b/tinymce-single/blocks/elements/blockquote/register.js @@ -1,50 +1,61 @@ -window.wp.blocks.registerBlock( { - name: 'blockquote', - displayName: 'Quote', - elements: [ 'blockquote' ], - type: 'text', - icon: 'gridicons-quote', - restrictToInline: [ 'footer' ], - controls: [ - { - classes: 'remove-formatting', - icon: 'gridicons-quote', - onClick: function( block, editor ) { - var footer = block.querySelector( 'footer' ); - var firstChild = block.firstChild; - - if ( footer ) { - block.removeChild( footer ); - } +( function( wp ) { + function insertEmpty() { + return '


'; + } - while ( block.firstChild ) { - block.parentNode.insertBefore( block.firstChild, block ); - } + function fromBaseState( block, editor ) { + editor.formatter.apply( 'blockquote', block ); + } - block.parentNode.removeChild( block ); + function toBaseState( block ) { + var footer = block.querySelector( 'footer' ); + var firstChild = block.firstChild; - window.wp.blocks.selectBlock( firstChild ); - } - }, - { - icon: 'gridicons-caption', - onClick: function( block ) { - var footer = block.querySelector( 'footer' ); - - if ( footer ) { - block.removeChild( footer ); - } else { - block.insertAdjacentHTML( 'beforeend', - '' ); - } - }, - isActive: function( block ) { - return !! block.querySelector( 'footer' ); - } + if ( footer ) { + block.removeChild( footer ); } - ], - insert: function() { - return '


'; + + while ( block.firstChild ) { + block.parentNode.insertBefore( block.firstChild, block ); + } + + block.parentNode.removeChild( block ); + + window.wp.blocks.selectBlock( firstChild ); } -} ); + window.wp.blocks.registerBlock( { + name: 'blockquote', + displayName: 'Quote', + elements: [ 'blockquote' ], + type: 'text', + icon: 'gridicons-quote', + restrictToInline: [ 'footer' ], + controls: [ + { + classes: 'remove-formatting', + icon: 'gridicons-quote', + onClick: toBaseState + }, + { + icon: 'gridicons-caption', + onClick: function( block ) { + var footer = block.querySelector( 'footer' ); + + if ( footer ) { + block.removeChild( footer ); + } else { + block.insertAdjacentHTML( 'beforeend', + '' ); + } + }, + isActive: function( block ) { + return !! block.querySelector( 'footer' ); + } + } + ], + insert: insertEmpty, + fromBaseState: fromBaseState, + toBaseState: toBaseState + } ); +} )( window.wp ); diff --git a/tinymce-single/blocks/elements/headings/register.js b/tinymce-single/blocks/elements/headings/register.js index eb626c09d3af1..0b30920a4bc8b 100644 --- a/tinymce-single/blocks/elements/headings/register.js +++ b/tinymce-single/blocks/elements/headings/register.js @@ -26,6 +26,14 @@ return controls; } + function toBaseState( block, editor ) { + editor.formatter.apply( 'p', block ); + } + + function fromBaseState( block, editor ) { + editor.formatter.apply( 'h1', block ); + } + wp.blocks.registerBlock( { name: 'heading', displayName: 'Heading', @@ -33,6 +41,8 @@ type: 'text', icon: 'gridicons-heading', controls: getControls(), + toBaseState: toBaseState, + fromBaseState: fromBaseState, insert: function() { // Maybe detect best heading based on document outline. return '


'; diff --git a/tinymce-single/blocks/elements/paragraph/register.js b/tinymce-single/blocks/elements/paragraph/register.js index 46d96a1393529..498cb04648fee 100644 --- a/tinymce-single/blocks/elements/paragraph/register.js +++ b/tinymce-single/blocks/elements/paragraph/register.js @@ -3,36 +3,16 @@ window.wp.blocks.registerBlock( { displayName: 'Paragraph', elements: [ 'p' ], type: 'text', + section: 'text', icon: 'gridicons-posts', controls: [ - { - icon: 'gridicons-heading', - onClick: function( block, editor ) { - editor.formatter.apply( 'h1' ); - } - }, - { - icon: 'gridicons-quote', - onClick: function( block, editor ) { - editor.formatter.apply( 'blockquote' ); - } - }, - { - icon: 'gridicons-list-unordered', - onClick: function( block ) { - wp.blocks.getBlockSettings( 'elements:list' ).fromBaseState( block ); - } - }, - { - icon: 'gridicons-code', - onClick: function( block, editor ) { - editor.formatter.apply( 'pre' ); - } - }, + 'text-switcher', 'text-align-left', 'text-align-center', 'text-align-right' ], + toBaseState: function() {}, + fromBaseState: function() {}, insert: function() { return '


'; } diff --git a/tinymce-single/blocks/elements/preformatted/register.js b/tinymce-single/blocks/elements/preformatted/register.js index 449daf03fdb07..9623f227d92c1 100644 --- a/tinymce-single/blocks/elements/preformatted/register.js +++ b/tinymce-single/blocks/elements/preformatted/register.js @@ -1,22 +1,39 @@ -window.wp.blocks.registerBlock( { - name: 'preformatted', - displayName: 'Preformatted', - elements: [ 'pre' ], - type: 'text', - icon: 'gridicons-code', - controls: [ - { - icon: 'gridicons-cog' - }, - { - classes: 'remove-formatting', - icon: 'gridicons-code', - onClick: function( block, editor ) { - editor.formatter.remove( 'pre' ); - } - } - ], - insert: function() { +( function( wp ) { + + function insertEmpty() { return '

'; } -} ); + + function fromBaseState( block, editor ) { + editor.formatter.apply( 'pre', block ); + } + + function toBaseState( block, editor ) { + editor.formatter.remove( 'pre', block ); + } + + window.wp.blocks.registerBlock( { + name: 'preformatted', + displayName: 'Preformatted', + elements: [ 'pre' ], + type: 'text', + icon: 'gridicons-code', + controls: [ + { + icon: 'gridicons-cog', + onClick: function() {} + }, + { + classes: 'remove-formatting', + icon: 'gridicons-code', + onClick: function( block, editor ) { + editor.formatter.remove( 'pre', block ); + } + } + ], + insert: insertEmpty, + fromBaseState: fromBaseState, + toBaseState: toBaseState + } ); + +} )( window.wp ); diff --git a/tinymce-single/blocks/my-awesome-plugin/youtube/register.js b/tinymce-single/blocks/my-awesome-plugin/youtube/register.js index 5dc04a52b05fb..792647b0efab9 100644 --- a/tinymce-single/blocks/my-awesome-plugin/youtube/register.js +++ b/tinymce-single/blocks/my-awesome-plugin/youtube/register.js @@ -42,7 +42,26 @@ 'block-align-right', 'block-align-full', { - icon: 'gridicons-cog' + icon: 'gridicons-caption', + onClick: function( block ) { + var figcaption = block.querySelector( 'figcaption' ); + + if ( figcaption ) { + block.removeChild( figcaption ); + } else { + block.insertAdjacentHTML( 'beforeend', + '

' ); + } + + window.wp.blocks.selectBlock( block ); + }, + isActive: function( block ) { + return !! block.querySelector( 'figcaption' ); + } + }, + { + icon: 'gridicons-cog', + onClick: function() {} } ], insert: insertEmpty, diff --git a/tinymce-single/index.html b/tinymce-single/index.html index 40e39d9cfc1e7..ab9520d62b144 100644 --- a/tinymce-single/index.html +++ b/tinymce-single/index.html @@ -72,11 +72,12 @@

NASA discovers system of seven Earth-sized planets

- + + + - diff --git a/tinymce-single/tinymce/block.css b/tinymce-single/tinymce/block.css index 23e3d61ca09a6..96817f2eb1b98 100644 --- a/tinymce-single/tinymce/block.css +++ b/tinymce-single/tinymce/block.css @@ -146,6 +146,10 @@ svg.gridicon { transform-origin: top left; } +.mce-gridicons-rotate svg { + transform: rotate( 90deg ); +} + div.mce-inline-toolbar-grp { background-color: #fff; border: 1px solid #e1e6ea; @@ -337,3 +341,19 @@ div.mce-inline-toolbar-grp.block-toolbar > div.mce-stack-layout { display: block; margin: 0 auto 20px; } + +.mce-floatpanel { + position: absolute; + background-color: #fff; + border: 1px solid #e1e6ea; + box-shadow: 0px 3px 20px rgba( 18, 24, 30, .1 ), 0px 1px 3px rgba( 18, 24, 30, .1 ); + margin-top: 2px; +} + +.mce-listbox .mce-txt { + display: none; +} + +.mce-menu-item { + padding: 4px; +} diff --git a/tinymce-single/tinymce/block.js b/tinymce-single/tinymce/block.js index c1837e4aab4b0..9bc36351f0932 100644 --- a/tinymce-single/tinymce/block.js +++ b/tinymce-single/tinymce/block.js @@ -46,6 +46,31 @@ editor.addButton( name, settings ); } ); + var textBlocks = wp.blocks.getType( 'text' ); + + editor.addButton( 'text-switcher', { + type: 'svglistbox', + icon: 'gridicons-posts', + values: textBlocks.map( function( settings ) { + return { + text: settings.displayName, + value: settings._id + } + } ), + onClick: function( event ) { + if ( event.control && event.control.settings.value ) { + var block = wp.blocks.getSelectedBlock(); + var currentSettings = wp.blocks.getBlockSettingsByElement( block ); + var nextSettings = wp.blocks.getBlockSettings( event.control.settings.value ); + + editor.undoManager.transact( function() { + currentSettings.toBaseState( block, editor ); + nextSettings.fromBaseState( block, editor ); + } ); + } + } + } ); + editor.on( 'pastePreProcess', function( event ) { var block = getSelectedBlock(); var settings = wp.blocks.getBlockSettingsByElement( block ); @@ -126,19 +151,22 @@ editor.on( 'keydown', function( event ) { if ( event.keyCode === tinymce.util.VK.ENTER ) { - var selectedBlock = wp.blocks.getSelectedBlock(); - var blockSettings = wp.blocks.getBlockSettingsByElement( selectedBlock ); + var block = wp.blocks.getSelectedBlock(); + var settings = wp.blocks.getBlockSettingsByElement( block ); - if ( editor.$( selectedBlock ).attr( 'contenteditable' ) === 'false' ) { + if ( editor.$( block ).attr( 'contenteditable' ) === 'false' ) { event.preventDefault(); } - if ( blockSettings && blockSettings.restrictToInline ) { - blockSettings.restrictToInline.forEach( function( selector ) { + if ( settings ) { + var restrict = ( settings.restrictToInline || [] ).concat( settings.editable || [] ); + + restrict.forEach( function( selector ) { var node = editor.selection.getNode(); if ( editor.$( node ).is( selector ) || editor.$( node ).parents( selector ).length ) { event.preventDefault(); + editor.execCommand( 'InsertLineBreak' ); } } ); } diff --git a/tinymce-single/tinymce/config.js b/tinymce-single/tinymce/config.js index e265bda75342c..3f12f58bfc8c1 100644 --- a/tinymce-single/tinymce/config.js +++ b/tinymce-single/tinymce/config.js @@ -10,6 +10,7 @@ window.tinymce.init( { 'clean-paste', 'lists', 'paste', + 'table', 'toolbar', 'wplink', 'wptextpattern'