diff --git a/edit-post/assets/stylesheets/_mixins.scss b/edit-post/assets/stylesheets/_mixins.scss index ee17b9b3c45e3b..c352646eea0075 100644 --- a/edit-post/assets/stylesheets/_mixins.scss +++ b/edit-post/assets/stylesheets/_mixins.scss @@ -304,3 +304,16 @@ text-align: center; font-size: $default-font-size; } + +@mixin dropdown-arrow() { + content: ""; + pointer-events: none; + display: block; + position: absolute; + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 5px solid currentColor; + right: 6px; +} diff --git a/packages/components/src/dropdown-menu/index.js b/packages/components/src/dropdown-menu/index.js index 0ec348097c7db2..9434eab2098d7f 100644 --- a/packages/components/src/dropdown-menu/index.js +++ b/packages/components/src/dropdown-menu/index.js @@ -2,6 +2,7 @@ * External dependencies */ import classnames from 'classnames'; +import { flatMap } from 'lodash'; /** * WordPress dependencies @@ -12,7 +13,6 @@ import { DOWN } from '@wordpress/keycodes'; * Internal dependencies */ import IconButton from '../icon-button'; -import Dashicon from '../dashicon'; import Dropdown from '../dropdown'; import { NavigableMenu } from '../navigable-container'; @@ -21,14 +21,21 @@ function DropdownMenu( { label, menuLabel, controls, + className, } ) { if ( ! controls || ! controls.length ) { return null; } + // Normalize controls to nested array of objects (sets of controls) + let controlSets = controls; + if ( ! Array.isArray( controlSets[ 0 ] ) ) { + controlSets = [ controlSets ]; + } + return ( { const openOnArrowDown = ( event ) => { @@ -40,11 +47,7 @@ function DropdownMenu( { }; return ( - + ); } } @@ -64,26 +67,31 @@ function DropdownMenu( { role="menu" aria-label={ menuLabel } > - { controls.map( ( control, index ) => ( - { - if ( control.isDisabled ) { - return; - } - event.stopPropagation(); - onClose(); - if ( control.onClick ) { - control.onClick(); - } - } } - className="components-dropdown-menu__menu-item" - icon={ control.icon } - role="menuitem" - disabled={ control.isDisabled } - > - { control.title } - + { flatMap( controlSets, ( controlSet, indexOfSet ) => ( + controlSet.map( ( control, indexOfControl ) => ( + { + event.stopPropagation(); + onClose(); + if ( control.onClick ) { + control.onClick(); + } + } } + className={ classnames( + 'components-dropdown-menu__menu-item', + { + 'has-separator': indexOfSet > 0 && indexOfControl === 0, + 'is-active': control.isActive, + }, + ) } + icon={ control.icon } + role="menuitem" + disabled={ control.isDisabled } + > + { control.title } + + ) ) ) ) } ); diff --git a/packages/components/src/dropdown-menu/style.scss b/packages/components/src/dropdown-menu/style.scss index fa53a1040e4a08..a63eab3c69924b 100644 --- a/packages/components/src/dropdown-menu/style.scss +++ b/packages/components/src/dropdown-menu/style.scss @@ -7,12 +7,12 @@ margin: 0; padding: 4px; border: $border-width solid transparent; - border-radius: 0; display: flex; flex-direction: row; &.is-active, &.is-active:hover { + box-shadow: none; background-color: $dark-gray-500; color: $white; } @@ -26,17 +26,19 @@ &:hover, &:focus, - &:not(:disabled):hover { - box-shadow: none; - outline: none; - color: $dark-gray-500; - border-color: $dark-gray-500; + &:not(:disabled):not([aria-disabled="true"]):not(.is-default):hover { + @include formatting-button-style__hover(); } - &.is-active > svg, - &.is-active:hover > svg { - background-color: $dark-gray-500; - color: $white; + .components-dropdown-menu__indicator { + display: inline-block; + margin-left: 10px; + + // Add a dropdown arrow indicator. + &::after { + @include dropdown-arrow(); + top: $icon-button-size-small / 2 + 1px; + } } } } @@ -45,9 +47,8 @@ } .components-dropdown-menu__menu { - // note that left is set by react in a style attribute width: 100%; - padding: 3px 3px 0; + padding: 9px; font-family: $default-font; font-size: $default-font-size; line-height: $default-line-height; @@ -55,13 +56,46 @@ .components-dropdown-menu__menu-item { width: 100%; padding: 6px; - border-radius: 0; outline: none; cursor: pointer; + margin-bottom: $grid-size-small; + + &.has-separator { + margin-top: 6px; + position: relative; + overflow: visible; + } + + &.has-separator::before { + display: block; + content: ""; + box-sizing: content-box; + background-color: $light-gray-500; + position: absolute; + top: -3px; + left: 0; + right: 0; + height: 1px; + } + + // Plain menu styles. + &:focus:not(:disabled):not([aria-disabled="true"]):not(.is-default) { + @include menu-style__focus(); + } - .dashicon { - margin-right: 8px; + // Formatting buttons + > svg { + border-radius: $radius-round-rectangle; + + // This assumes 20x20px dashicons. + padding: 2px; + width: $icon-button-size-small; + height: $icon-button-size-small; + margin: -1px $grid-size -1px 0; + } + + &:not(:disabled):not([aria-disabled="true"]):not(.is-default).is-active > svg { + @include formatting-button-style__active(); } } } - diff --git a/packages/components/src/toolbar/index.js b/packages/components/src/toolbar/index.js index 91f1baffbd2119..548c5fb4affffc 100644 --- a/packages/components/src/toolbar/index.js +++ b/packages/components/src/toolbar/index.js @@ -8,6 +8,7 @@ import { flatMap } from 'lodash'; * Internal dependencies */ import IconButton from '../icon-button'; +import DropdownMenu from '../dropdown-menu'; import ToolbarContainer from './toolbar-container'; import ToolbarButtonContainer from './toolbar-button-container'; @@ -41,7 +42,7 @@ import ToolbarButtonContainer from './toolbar-button-container'; * * @return {ReactElement} The rendered toolbar. */ -function Toolbar( { controls = [], children, className } ) { +function Toolbar( { controls = [], children, className, isCollapsed, icon, label } ) { if ( ( ! controls || ! controls.length ) && ! children @@ -55,6 +56,17 @@ function Toolbar( { controls = [], children, className } ) { controlSets = [ controlSets ]; } + if ( isCollapsed ) { + return ( + + ); + } + return ( { flatMap( controlSets, ( controlSet, indexOfSet ) => ( diff --git a/packages/editor/src/components/alignment-toolbar/index.js b/packages/editor/src/components/alignment-toolbar/index.js index 73fac92bdd7686..5aa2d61b0c0830 100644 --- a/packages/editor/src/components/alignment-toolbar/index.js +++ b/packages/editor/src/components/alignment-toolbar/index.js @@ -1,8 +1,21 @@ +/** + * External dependencies + */ +import { find } from 'lodash'; + /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { Toolbar } from '@wordpress/components'; +import { withViewportMatch } from '@wordpress/viewport'; +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { withBlockEditContext } from '../block-edit/context'; const ALIGNMENT_CONTROLS = [ { @@ -22,13 +35,18 @@ const ALIGNMENT_CONTROLS = [ }, ]; -export default function AlignmentToolbar( { value, onChange } ) { +export function AlignmentToolbar( { isCollapsed, value, onChange } ) { function applyOrUnset( align ) { return () => onChange( value === align ? undefined : align ); } + const activeAlignment = find( ALIGNMENT_CONTROLS, ( control ) => control.align === value ); + return ( { const { align } = control; const isActive = ( value === align ); @@ -42,3 +60,21 @@ export default function AlignmentToolbar( { value, onChange } ) { /> ); } + +export default compose( + withBlockEditContext( ( { clientId } ) => { + return { + clientId, + }; + } ), + withViewportMatch( { isLargeViewport: 'medium' } ), + withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => { + const { getBlockRootClientId, getEditorSettings } = select( 'core/editor' ); + return { + isCollapsed: isCollapsed || ! isLargeViewport || ( + ! getEditorSettings().hasFixedToolbar && + getBlockRootClientId( clientId ) + ), + }; + } ), +)( AlignmentToolbar ); diff --git a/packages/editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap b/packages/editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap index 76d307f173edeb..2308f4cda66ae1 100644 --- a/packages/editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/alignment-toolbar/test/__snapshots__/index.js.snap @@ -27,5 +27,7 @@ exports[`AlignmentToolbar should match snapshot 1`] = ` }, ] } + icon="editor-alignleft" + label="Change Text Alignment" /> `; diff --git a/packages/editor/src/components/alignment-toolbar/test/index.js b/packages/editor/src/components/alignment-toolbar/test/index.js index 4c0df34079de99..803a5a56e6a828 100644 --- a/packages/editor/src/components/alignment-toolbar/test/index.js +++ b/packages/editor/src/components/alignment-toolbar/test/index.js @@ -6,7 +6,7 @@ import { shallow } from 'enzyme'; /** * Internal dependencies */ -import AlignmentToolbar from '../'; +import { AlignmentToolbar } from '../'; describe( 'AlignmentToolbar', () => { const alignment = 'left'; diff --git a/packages/editor/src/components/block-alignment-toolbar/index.js b/packages/editor/src/components/block-alignment-toolbar/index.js index 6477611dcd7b85..acfe1cb4ed3442 100644 --- a/packages/editor/src/components/block-alignment-toolbar/index.js +++ b/packages/editor/src/components/block-alignment-toolbar/index.js @@ -3,7 +3,14 @@ */ import { __ } from '@wordpress/i18n'; import { Toolbar } from '@wordpress/components'; +import { withViewportMatch } from '@wordpress/viewport'; import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { withBlockEditContext } from '../block-edit/context'; const BLOCK_ALIGNMENTS_CONTROLS = { left: { @@ -31,7 +38,7 @@ const BLOCK_ALIGNMENTS_CONTROLS = { const DEFAULT_CONTROLS = [ 'left', 'center', 'right', 'wide', 'full' ]; const WIDE_CONTROLS = [ 'wide', 'full' ]; -export function BlockAlignmentToolbar( { value, onChange, controls = DEFAULT_CONTROLS, wideControlsEnabled = false } ) { +export function BlockAlignmentToolbar( { isCollapsed, value, onChange, controls = DEFAULT_CONTROLS, wideControlsEnabled = false } ) { function applyOrUnset( align ) { return () => onChange( value === align ? undefined : align ); } @@ -40,8 +47,13 @@ export function BlockAlignmentToolbar( { value, onChange, controls = DEFAULT_CON controls : controls.filter( ( control ) => WIDE_CONTROLS.indexOf( control ) === -1 ); + const activeAlignment = BLOCK_ALIGNMENTS_CONTROLS[ value ]; + return ( { return { @@ -55,8 +67,21 @@ export function BlockAlignmentToolbar( { value, onChange, controls = DEFAULT_CON ); } -export default withSelect( - ( select ) => ( { - wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide, - } ) +export default compose( + withBlockEditContext( ( { clientId } ) => { + return { + clientId, + }; + } ), + withViewportMatch( { isLargeViewport: 'medium' } ), + withSelect( ( select, { clientId, isLargeViewport, isCollapsed } ) => { + const { getBlockRootClientId, getEditorSettings } = select( 'core/editor' ); + return { + wideControlsEnabled: select( 'core/editor' ).getEditorSettings().alignWide, + isCollapsed: isCollapsed || ! isLargeViewport || ( + ! getEditorSettings().hasFixedToolbar && + getBlockRootClientId( clientId ) + ), + }; + } ), )( BlockAlignmentToolbar ); diff --git a/packages/editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap b/packages/editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap index 273ad8f56496ee..27fbae699c3bdb 100644 --- a/packages/editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/block-alignment-toolbar/test/__snapshots__/index.js.snap @@ -24,5 +24,7 @@ exports[`BlockAlignmentToolbar should match snapshot 1`] = ` }, ] } + icon="align-left" + label="Change Alignment" /> `; diff --git a/packages/editor/src/components/block-switcher/style.scss b/packages/editor/src/components/block-switcher/style.scss index 39cf29a819b857..a5c2292b01fb9f 100644 --- a/packages/editor/src/components/block-switcher/style.scss +++ b/packages/editor/src/components/block-switcher/style.scss @@ -36,17 +36,8 @@ // Add a dropdown arrow indicator. .editor-block-icon::after { - content: ""; - pointer-events: none; - display: block; - position: absolute; + @include dropdown-arrow(); top: $icon-button-size-small / 2 - 1px; - width: 0; - height: 0; - border-left: 3px solid transparent; - border-right: 3px solid transparent; - border-top: 5px solid currentColor; - right: 6px; } .editor-block-switcher__transform {