diff --git a/blocks/library/index.js b/blocks/library/index.js
index 93a71f0cc3accb..a8bf7438692355 100644
--- a/blocks/library/index.js
+++ b/blocks/library/index.js
@@ -9,6 +9,7 @@ import './separator';
import './button';
import './pullquote';
import './table';
+import './table2';
import './preformatted';
import './code';
import './html';
diff --git a/blocks/library/table2/index.js b/blocks/library/table2/index.js
new file mode 100644
index 00000000000000..7ac3a8cdb3a5f4
--- /dev/null
+++ b/blocks/library/table2/index.js
@@ -0,0 +1,71 @@
+/**
+ * Internal dependencies
+ */
+import './style.scss';
+import { registerBlockType, query as hpq } from '../../api';
+import TableBlock from './table-block';
+import BlockControls from '../../block-controls';
+import BlockAlignmentToolbar from '../../block-alignment-toolbar';
+
+const { children } = hpq;
+
+registerBlockType( 'core/table2', {
+ title: wp.i18n.__( 'TinyMCE Table' ),
+ icon: 'editor-table',
+ category: 'formatting',
+
+ attributes: {
+ content: children( 'table' ),
+ },
+
+ defaultAttributes: {
+ content: [
+
+
|
|
+
|
|
+ ,
+ ],
+ },
+
+ getEditWrapperProps( attributes ) {
+ const { align } = attributes;
+ if ( 'left' === align || 'right' === align || 'wide' === align ) {
+ return { 'data-align': align };
+ }
+ },
+
+ edit( { attributes, setAttributes, focus, setFocus, className } ) {
+ const { content } = attributes;
+ const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } );
+ return [
+ focus && (
+
+
+
+ ),
+ {
+ setAttributes( { content: nextContent } );
+ } }
+ content={ content }
+ focus={ focus }
+ onFocus={ setFocus }
+ className={ className }
+ />,
+ ];
+ },
+
+ save( { attributes } ) {
+ const { content } = attributes;
+ return (
+
+ );
+ },
+} );
diff --git a/blocks/library/table2/style.scss b/blocks/library/table2/style.scss
new file mode 100644
index 00000000000000..70ad43999e2331
--- /dev/null
+++ b/blocks/library/table2/style.scss
@@ -0,0 +1,38 @@
+.editor-visual-editor__block[data-type="core/table2"] {
+
+ .editor-visual-editor__block-controls > div {
+ display: flex;
+ }
+
+ &[data-align="left"],
+ &[data-align="right"] {
+ min-width: 33%;
+ max-width: 50%;
+ }
+
+ &[data-align="left"] {
+ float: left;
+ margin-right: $block-padding;
+ }
+
+ &[data-align="right"] {
+ float: right;
+ margin-left: $block-padding;
+ }
+}
+
+.wp-block-table-2 {
+ table {
+ border-collapse: collapse;
+ width: 100%;
+ }
+
+ td, th {
+ padding: 0.5em;
+ border: 1px solid currentColor;
+ }
+
+ td[data-mce-selected="1"], th[data-mce-selected="1"] {
+ background-color: $light-gray-500;
+ }
+}
diff --git a/blocks/library/table2/table-block.js b/blocks/library/table2/table-block.js
new file mode 100644
index 00000000000000..567aac26b79dfa
--- /dev/null
+++ b/blocks/library/table2/table-block.js
@@ -0,0 +1,90 @@
+import Editable from '../../editable';
+import BlockControls from '../../block-controls';
+import { Toolbar, DropdownMenu } from 'components';
+
+function execCommand( command ) {
+ return ( editor ) => {
+ if ( editor ) {
+ editor.execCommand( command );
+ }
+ };
+}
+
+const TABLE_CONTROLS = [
+ {
+ icon: 'table-row-before',
+ title: wp.i18n.__( 'Insert Row Before' ),
+ onClick: execCommand( 'mceTableInsertRowBefore' ),
+ },
+ {
+ icon: 'table-row-after',
+ title: wp.i18n.__( 'Insert Row After' ),
+ onClick: execCommand( 'mceTableInsertRowAfter' ),
+ },
+ {
+ icon: 'table-row-delete',
+ title: wp.i18n.__( 'Delete Row' ),
+ onClick: execCommand( 'mceTableDeleteRow' ),
+ },
+ {
+ icon: 'table-col-before',
+ title: wp.i18n.__( 'Insert Column Before' ),
+ onClick: execCommand( 'mceTableInsertColBefore' ),
+ },
+ {
+ icon: 'table-col-after',
+ title: wp.i18n.__( 'Insert Column After' ),
+ onClick: execCommand( 'mceTableInsertColAfter' ),
+ },
+ {
+ icon: 'table-col-delete',
+ title: wp.i18n.__( 'Delete Column' ),
+ onClick: execCommand( 'mceTableDeleteCol' ),
+ },
+];
+
+export default class TableBlock extends wp.element.Component {
+ constructor() {
+ super();
+ this.state = {
+ editor: null,
+ };
+ }
+
+ render() {
+ const { content, focus, onFocus, onChange, className } = this.props;
+
+ return [
+ ( {
+ ...settings,
+ plugins: ( settings.plugins || [] ).concat( 'table' ),
+ } ) }
+ onSetup={ ( editor ) => this.setState( { editor } ) }
+ onChange={ onChange }
+ value={ content }
+ focus={ focus }
+ onFocus={ onFocus }
+ />,
+ focus && (
+
+
+
+ ( {
+ ...control,
+ onClick: () => control.onClick( this.state.editor ),
+ } ) ) }
+ />
+
+
+
+ ),
+ ];
+ }
+}
diff --git a/blocks/test/fixtures/core__table2.html b/blocks/test/fixtures/core__table2.html
new file mode 100644
index 00000000000000..6b5205d3a0b45a
--- /dev/null
+++ b/blocks/test/fixtures/core__table2.html
@@ -0,0 +1,49 @@
+
+
+
+
+ | Version |
+ Musician |
+ Date |
+
+
+
+
+ | .70 |
+ No musician chosen. |
+ May 27, 2003 |
+
+
+ | 1.0 |
+ Miles Davis |
+ January 3, 2004 |
+
+
+ | Lots of versions skipped, see the full list |
+ … |
+ … |
+
+
+ | 4.4 |
+ Clifford Brown |
+ December 8, 2015 |
+
+
+ | 4.5 |
+ Coleman Hawkins |
+ April 12, 2016 |
+
+
+ | 4.6 |
+ Pepper Adams |
+ August 16, 2016 |
+
+
+ | 4.7 |
+ Sarah Vaughan |
+ December 6, 2016 |
+
+
+
+
+
diff --git a/blocks/test/fixtures/core__table2.json b/blocks/test/fixtures/core__table2.json
new file mode 100644
index 00000000000000..fb877aac30beb1
--- /dev/null
+++ b/blocks/test/fixtures/core__table2.json
@@ -0,0 +1,246 @@
+[
+ {
+ "uid": "_uid_0",
+ "name": "core/table2",
+ "attributes": {
+ "content": [
+ "\n ",
+ {
+ "type": "thead",
+ "children": [
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "th",
+ "children": "Version"
+ },
+ "\n ",
+ {
+ "type": "th",
+ "children": "Musician"
+ },
+ "\n ",
+ {
+ "type": "th",
+ "children": "Date"
+ },
+ "\n "
+ ]
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tbody",
+ "children": [
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2003/05/wordpress-now-available/"
+ },
+ "children": ".70"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "No musician chosen."
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "May 27, 2003"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2004/01/wordpress-10/"
+ },
+ "children": "1.0"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "Miles Davis"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "January 3, 2004"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": [
+ "Lots of versions skipped, see ",
+ {
+ "type": "a",
+ "attributes": {
+ "href": "https://codex.wordpress.org/WordPress_Versions"
+ },
+ "children": "the full list"
+ }
+ ]
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "…"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "…"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2015/12/clifford/"
+ },
+ "children": "4.4"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "Clifford Brown"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "December 8, 2015"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2016/04/coleman/"
+ },
+ "children": "4.5"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "Coleman Hawkins"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "April 12, 2016"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2016/08/pepper/"
+ },
+ "children": "4.6"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "Pepper Adams"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "August 16, 2016"
+ },
+ "\n "
+ ]
+ },
+ "\n ",
+ {
+ "type": "tr",
+ "children": [
+ "\n ",
+ {
+ "type": "td",
+ "children": {
+ "type": "a",
+ "attributes": {
+ "href": "https://wordpress.org/news/2016/12/vaughan/"
+ },
+ "children": "4.7"
+ }
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "Sarah Vaughan"
+ },
+ "\n ",
+ {
+ "type": "td",
+ "children": "December 6, 2016"
+ },
+ "\n "
+ ]
+ },
+ "\n "
+ ]
+ },
+ "\n"
+ ]
+ }
+ }
+]
diff --git a/blocks/test/fixtures/core__table2.parsed.json b/blocks/test/fixtures/core__table2.parsed.json
new file mode 100644
index 00000000000000..3a71111906ab81
--- /dev/null
+++ b/blocks/test/fixtures/core__table2.parsed.json
@@ -0,0 +1,11 @@
+[
+ {
+ "blockName": "core/table2",
+ "attrs": null,
+ "rawContent": "\n\n \n \n | Version | \n Musician | \n Date | \n
\n \n \n \n | .70 | \n No musician chosen. | \n May 27, 2003 | \n
\n \n | 1.0 | \n Miles Davis | \n January 3, 2004 | \n
\n \n | Lots of versions skipped, see the full list | \n … | \n … | \n
\n \n | 4.4 | \n Clifford Brown | \n December 8, 2015 | \n
\n \n | 4.5 | \n Coleman Hawkins | \n April 12, 2016 | \n
\n \n | 4.6 | \n Pepper Adams | \n August 16, 2016 | \n
\n \n | 4.7 | \n Sarah Vaughan | \n December 6, 2016 | \n
\n \n
\n"
+ },
+ {
+ "attrs": {},
+ "rawContent": "\n\n"
+ }
+]
diff --git a/blocks/test/fixtures/core__table2.serialized.html b/blocks/test/fixtures/core__table2.serialized.html
new file mode 100644
index 00000000000000..6696490689288e
--- /dev/null
+++ b/blocks/test/fixtures/core__table2.serialized.html
@@ -0,0 +1,48 @@
+
+
+
+
+ | Version |
+ Musician |
+ Date |
+
+
+
+
+ | .70 |
+ No musician chosen. |
+ May 27, 2003 |
+
+
+ | 1.0 |
+ Miles Davis |
+ January 3, 2004 |
+
+
+ | Lots of versions skipped, see the full list |
+ … |
+ … |
+
+
+ | 4.4 |
+ Clifford Brown |
+ December 8, 2015 |
+
+
+ | 4.5 |
+ Coleman Hawkins |
+ April 12, 2016 |
+
+
+ | 4.6 |
+ Pepper Adams |
+ August 16, 2016 |
+
+
+ | 4.7 |
+ Sarah Vaughan |
+ December 6, 2016 |
+
+
+
+
\ No newline at end of file
diff --git a/components/dashicon/index.js b/components/dashicon/index.js
index 0e7328fbbfbfa5..bfc8ec1ebbb71c 100644
--- a/components/dashicon/index.js
+++ b/components/dashicon/index.js
@@ -696,6 +696,24 @@ export default class Dashicon extends wp.element.Component {
case 'store':
path = 'M1 10c.41.29.96.43 1.5.43.55 0 1.09-.14 1.5-.43.62-.46 1-1.17 1-2 0 .83.37 1.54 1 2 .41.29.96.43 1.5.43.55 0 1.09-.14 1.5-.43.62-.46 1-1.17 1-2 0 .83.37 1.54 1 2 .41.29.96.43 1.51.43.54 0 1.08-.14 1.49-.43.62-.46 1-1.17 1-2 0 .83.37 1.54 1 2 .41.29.96.43 1.5.43.55 0 1.09-.14 1.5-.43.63-.46 1-1.17 1-2V7l-3-7H4L0 7v1c0 .83.37 1.54 1 2zm2 8.99h5v-5h4v5h5v-7c-.37-.05-.72-.22-1-.43-.63-.45-1-.73-1-1.56 0 .83-.38 1.11-1 1.56-.41.3-.95.43-1.49.44-.55 0-1.1-.14-1.51-.44-.63-.45-1-.73-1-1.56 0 .83-.38 1.11-1 1.56-.41.3-.95.43-1.5.44-.54 0-1.09-.14-1.5-.44-.63-.45-1-.73-1-1.57 0 .84-.38 1.12-1 1.57-.29.21-.63.38-1 .44v6.99z';
break;
+ case 'table-col-delete':
+ path = 'M6.4,9.98l1.28,-1.28v-0.256l-1.28,-1.28v2.8160000000000003zM12.8,8.448l1.28,-1.28v2.752l-1.28,-1.28v-0.192zM20.48,17.92v-17.92h-20.48v17.92h20.48zM19.2,15.36h-5.12v-1.024l-0.256,0.256l-1.024,-1.024v1.7919999999999998h-5.12v-1.7919999999999998l-1.024,1.024l-0.256,-0.256v1.024h-5.12v-14.08h5.12v2.3680000000000003l0.7040000000000001,-0.7040000000000001l0.5760000000000001,0.5760000000000001v-2.3040000000000003h5.12v2.3040000000000003l0.96,-0.96l0.32,0.32v-1.6640000000000001h5.12v14.144000000000002zM13.44,13.248l-3.136,-3.136l-3.264,3.264l-1.536,-1.536l3.264,-3.264l-3.136,-3.136l1.536,-1.536l3.136,3.136l3.2,-3.2l1.536,1.536l-3.2,3.2l3.136,3.136l-1.536,1.536z';
+ break;
+ case 'table-col-after':
+ path = 'M14.08,12.864v-3.648h3.648v-1.7919999999999998h-3.648v-3.648h-1.7280000000000002v3.648h-3.7119999999999997v1.7919999999999998h3.7119999999999997v3.648zM0,17.92v-17.92h20.48v17.92h-20.48zM6.4,1.28h-5.12v3.84h5.12v-3.84zM6.4,6.4h-5.12v3.84h5.12v-3.84zM6.4,11.52h-5.12v3.84h5.12v-3.84zM19.2,1.28h-11.52v14.08h11.52v-14.08z';
+ break;
+ case 'table-col-before':
+ path = 'M6.4,3.7760000000000002v3.648h-3.648v1.7919999999999998h3.648v3.648h1.7280000000000002v-3.648h3.7119999999999997v-1.7919999999999998h-3.7119999999999997v-3.648zM0,17.92v-17.92h20.48v17.92h-20.48zM12.8,1.28h-11.52v14.08h11.52v-14.08zM19.2,1.28h-5.12v3.84h5.12v-3.84zM19.2,6.4h-5.12v3.84h5.12v-3.84zM19.2,11.52h-5.12v3.84h5.12v-3.84z';
+ break;
+ case 'table-row-delete':
+ path = 'M17.728,11.456l-3.136,-3.136l3.2,-3.2l-1.536,-1.536l-3.2,3.2l-3.136,-3.136l-1.536,1.472l3.2,3.2l-3.264,3.264l1.536,1.536l3.264,-3.264l3.136,3.136l1.472,-1.536zM0,17.92v-17.92h20.48v17.92h-20.48zM19.2,11.52h-0.44799999999999995l-1.28,-1.28h1.7280000000000002v-3.84h-1.7919999999999998l1.28,-1.28h0.512v-3.84h-17.92v3.84h6.207999999999999l1.28,1.28h-7.4879999999999995v3.84h7.4239999999999995l-1.28,1.28h-6.144v3.84h17.92v-3.84z';
+ break;
+ case 'table-row-after':
+ path = 'M13.824000000000002,10.176h-2.88v-2.88h-1.4080000000000001v2.88h-2.88v1.344h2.88v2.88h1.4080000000000001v-2.88h2.88zM0,17.92v-17.92h20.48v17.92h-20.48zM6.4,1.28h-5.12v3.84h5.12v-3.84zM12.8,1.28h-5.12v3.84h5.12v-3.84zM19.2,1.28h-5.12v3.84h5.12v-3.84zM19.2,6.336h-17.92v9.024h17.92v-9.024z';
+ break;
+ case 'table-row-before':
+ path = 'M6.656000000000001,6.4639999999999995h2.88v2.88h1.4080000000000001v-2.88h2.88v-1.344h-2.88v-2.88h-1.4080000000000001v2.88h-2.88zM0,17.92v-17.92h20.48v17.92h-20.48zM7.68,15.36h5.12v-3.84h-5.12v3.84zM1.28,15.36h5.12v-3.84h-5.12v3.84zM19.2,1.28h-17.92v9.024h17.92v-9.024zM19.2,11.52h-5.12v3.84h5.12v-3.84z';
+ break;
case 'tablet':
path = 'M4 2h12c.55 0 1 .45 1 1v14c0 .55-.45 1-1 1H4c-.55 0-1-.45-1-1V3c0-.55.45-1 1-1zm11 14V4H5v12h10zM6 5h6l-6 5V5z';
break;
diff --git a/components/dropdown-menu/index.js b/components/dropdown-menu/index.js
new file mode 100644
index 00000000000000..5d017fa8e172f1
--- /dev/null
+++ b/components/dropdown-menu/index.js
@@ -0,0 +1,222 @@
+/**
+ * External dependencies
+ */
+import classNames from 'classnames';
+import clickOutside from 'react-click-outside';
+import { findIndex } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import IconButton from 'components/icon-button';
+import Dashicon from 'components/dashicon';
+import { findDOMNode } from 'element';
+import { TAB, ESCAPE, LEFT, UP, RIGHT, DOWN } from 'utils/keycodes';
+
+/**
+ * Internal dependencies
+ */
+import './style.scss';
+
+class DropdownMenu extends wp.element.Component {
+ constructor() {
+ super( ...arguments );
+ this.bindMenuRef = this.bindMenuRef.bind( this );
+ this.closeMenu = this.closeMenu.bind( this );
+ this.toggleMenu = this.toggleMenu.bind( this );
+ this.findActiveIndex = this.findActiveIndex.bind( this );
+ this.focusIndex = this.focusIndex.bind( this );
+ this.focusPrevious = this.focusPrevious.bind( this );
+ this.focusNext = this.focusNext.bind( this );
+ this.handleKeyDown = this.handleKeyDown.bind( this );
+ this.menuRef = null;
+ this.state = {
+ open: false,
+ };
+ }
+
+ bindMenuRef( node ) {
+ this.menuRef = node;
+ }
+
+ handleClickOutside() {
+ if ( ! this.state.open ) {
+ return;
+ }
+
+ this.closeMenu();
+ }
+
+ closeMenu() {
+ this.setState( {
+ open: false,
+ } );
+ }
+
+ toggleMenu() {
+ this.setState( {
+ open: ! this.state.open,
+ } );
+ }
+
+ findActiveIndex() {
+ if ( this.menuRef ) {
+ const menu = findDOMNode( this.menuRef );
+ const menuItem = document.activeElement;
+ if ( menuItem.parentNode === menu ) {
+ return findIndex( menu.children, ( child ) => child === menuItem );
+ }
+ return -1;
+ }
+ }
+
+ focusIndex( index ) {
+ if ( this.menuRef ) {
+ const menu = findDOMNode( this.menuRef );
+ if ( index < 0 ) {
+ menu.previousElementSibling.focus();
+ } else {
+ menu.children[ index ].focus();
+ }
+ }
+ }
+
+ focusPrevious() {
+ const i = this.findActiveIndex();
+ const prevI = i <= -1 ? -1 : i - 1;
+ this.focusIndex( prevI );
+ }
+
+ focusNext() {
+ const i = this.findActiveIndex();
+ const maxI = this.props.controls.length - 1;
+ const nextI = i >= maxI ? maxI : i + 1;
+ this.focusIndex( nextI );
+ }
+
+ handleKeyDown( keydown ) {
+ if ( this.state.open ) {
+ switch ( keydown.keyCode ) {
+ case ESCAPE:
+ keydown.preventDefault();
+ keydown.stopPropagation();
+ this.closeMenu();
+ const node = findDOMNode( this );
+ const toggle = node.querySelector( '.components-dropdown-menu__toggle' );
+ toggle.focus();
+ if ( this.props.onSelect ) {
+ this.props.onSelect( null );
+ }
+ break;
+
+ case TAB:
+ keydown.preventDefault();
+ if ( keydown.shiftKey ) {
+ this.focusPrevious();
+ } else {
+ this.focusNext();
+ }
+ break;
+
+ case LEFT:
+ case UP:
+ keydown.preventDefault();
+ this.focusPrevious();
+ break;
+
+ case RIGHT:
+ case DOWN:
+ keydown.preventDefault();
+ this.focusNext();
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ switch ( keydown.keyCode ) {
+ case DOWN:
+ keydown.preventDefault();
+ this.toggleMenu();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ componentDidMount() {
+ const node = findDOMNode( this );
+ node.addEventListener( 'keydown', this.handleKeyDown, false );
+ }
+
+ componentWillUnmount() {
+ const node = findDOMNode( this );
+ node.removeEventListener( 'keydown', this.handleKeyDown, false );
+ }
+
+ render() {
+ const {
+ icon = 'menu',
+ label,
+ menuLabel,
+ controls,
+ onSelect,
+ } = this.props;
+
+ if ( ! controls || ! controls.length ) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ { this.state.open &&
+
+ { controls.map( ( control, index ) => (
+ {
+ event.stopPropagation();
+ this.closeMenu();
+ if ( control.onClick ) {
+ control.onClick();
+ }
+ if ( onSelect ) {
+ onSelect( index );
+ }
+ } }
+ className="components-dropdown-menu__menu-item"
+ icon={ control.icon }
+ role="menuitem"
+ >
+ { control.title }
+
+ ) ) }
+
+ }
+
+ );
+ }
+}
+
+export default clickOutside( DropdownMenu );
diff --git a/components/dropdown-menu/style.scss b/components/dropdown-menu/style.scss
new file mode 100644
index 00000000000000..398c2003706959
--- /dev/null
+++ b/components/dropdown-menu/style.scss
@@ -0,0 +1,81 @@
+.components-dropdown-menu {
+ padding: 3px;
+ position: relative;
+ display: flex;
+
+ .components-dropdown-menu__toggle {
+ width: auto;
+ margin: 0px;
+ padding: 4px;
+ border: 1px solid transparent;
+ border-radius: 0;
+ display: flex;
+ flex-direction: row;
+
+ &.is-active,
+ &.is-active:hover {
+ background-color: $dark-gray-500;
+ color: $white;
+ }
+
+ &:focus:before {
+ top: -3px;
+ right: -3px;
+ bottom: -3px;
+ left: -3px;
+ }
+
+ &:hover,
+ &:focus,
+ &:not(:disabled):hover {
+ box-shadow: none;
+ outline: none;
+ color: $dark-gray-500;
+ border-color: $dark-gray-500;
+ }
+
+ &.is-active > svg,
+ &.is-active:hover > svg {
+ background-color: $dark-gray-500;
+ color: $white;
+ }
+ }
+}
+
+.components-dropdown-menu__menu {
+ position: absolute;
+ top: $block-controls-height - 1px;
+ left: -1px;
+ box-shadow: $shadow-popover;
+ border: 1px solid $light-gray-500;
+ background: $white;
+ padding: 3px 3px 0 3px;
+ font-family: $default-font;
+ font-size: $default-font-size;
+ line-height: $default-line-height;
+
+ .components-dropdown-menu__menu-item {
+ width: 100%;
+ margin-bottom: 3px;
+ padding: 6px;
+ background: none;
+ border: 1px solid transparent;
+ border-radius: 0;
+ outline: none;
+ color: $dark-gray-500;
+ cursor: pointer;
+
+ &:hover,
+ &:focus,
+ &:not(:disabled):hover {
+ box-shadow: none;
+ color: $dark-gray-500;
+ border-color: $dark-gray-500;
+ }
+
+ .dashicon {
+ margin-right: 5px;
+ }
+ }
+}
+
diff --git a/components/index.js b/components/index.js
index 8d5a608fa52d42..6e7e951a400c24 100644
--- a/components/index.js
+++ b/components/index.js
@@ -15,6 +15,7 @@ export { default as ResponsiveWrapper } from './responsive-wrapper';
export { default as SandBox } from './sandbox';
export { default as Spinner } from './spinner';
export { default as Toolbar } from './toolbar';
+export { default as DropdownMenu } from './dropdown-menu';
export { default as Popover } from './popover';
// Higher-Order Components
diff --git a/lib/client-assets.php b/lib/client-assets.php
index d13492e66f0058..c0dd42335918f9 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -103,7 +103,7 @@ function gutenberg_register_scripts_and_styles() {
wp_register_script(
'wp-blocks',
gutenberg_url( 'blocks/build/index.js' ),
- array( 'wp-element', 'wp-components', 'wp-utils', 'tinymce-nightly', 'tinymce-nightly-lists', 'tinymce-nightly-paste' ),
+ array( 'wp-element', 'wp-components', 'wp-utils', 'tinymce-nightly', 'tinymce-nightly-lists', 'tinymce-nightly-paste', 'tinymce-nightly-table' ),
filemtime( gutenberg_dir_path() . 'blocks/build/index.js' )
);
@@ -177,6 +177,11 @@ function gutenberg_register_vendor_scripts() {
'https://fiddle.azurewebsites.net/tinymce/nightly/plugins/paste/plugin' . $suffix . '.js',
array( 'tinymce-nightly' )
);
+ gutenberg_register_vendor_script(
+ 'tinymce-nightly-table',
+ 'https://fiddle.azurewebsites.net/tinymce/nightly/plugins/table/plugin' . $suffix . '.js',
+ array( 'tinymce-nightly' )
+ );
}
/**