Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapsible panels for the block's toolbar #9687

Merged
merged 10 commits into from
Sep 14, 2018
13 changes: 13 additions & 0 deletions edit-post/assets/stylesheets/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
64 changes: 36 additions & 28 deletions packages/components/src/dropdown-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
import { flatMap } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -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';

Expand All @@ -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 (
<Dropdown
className="components-dropdown-menu"
className={ classnames( 'components-dropdown-menu', className ) }
contentClassName="components-dropdown-menu__popover"
renderToggle={ ( { isOpen, onToggle } ) => {
const openOnArrowDown = ( event ) => {
Expand All @@ -40,11 +47,7 @@ function DropdownMenu( {
};
return (
<IconButton
className={
classnames( 'components-dropdown-menu__toggle', {
'is-active': isOpen,
} )
}
className="components-dropdown-menu__toggle"
icon={ icon }
onClick={ onToggle }
onKeyDown={ openOnArrowDown }
Expand All @@ -53,7 +56,7 @@ function DropdownMenu( {
label={ label }
tooltip={ label }
>
<Dashicon icon="arrow-down" />
<span className="components-dropdown-menu__indicator" />
</IconButton>
);
} }
Expand All @@ -64,26 +67,31 @@ function DropdownMenu( {
role="menu"
aria-label={ menuLabel }
>
{ controls.map( ( control, index ) => (
<IconButton
key={ index }
onClick={ ( event ) => {
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 }
</IconButton>
{ flatMap( controlSets, ( controlSet, indexOfSet ) => (
controlSet.map( ( control, indexOfControl ) => (
<IconButton
key={ [ indexOfSet, indexOfControl ].join() }
onClick={ ( event ) => {
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 }
</IconButton>
) )
) ) }
</NavigableMenu>
);
Expand Down
66 changes: 50 additions & 16 deletions packages/components/src/dropdown-menu/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
}
}
}
Expand All @@ -45,23 +47,55 @@
}

.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;

.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();
}
}
}

14 changes: 13 additions & 1 deletion packages/components/src/toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand All @@ -55,6 +56,17 @@ function Toolbar( { controls = [], children, className } ) {
controlSets = [ controlSets ];
}

if ( isCollapsed ) {
return (
<DropdownMenu
icon={ icon }
label={ label }
controls={ controlSets }
className={ classnames( 'components-toolbar', className ) }
/>
);
}

return (
<ToolbarContainer className={ classnames( 'components-toolbar', className ) }>
{ flatMap( controlSets, ( controlSet, indexOfSet ) => (
Expand Down
38 changes: 37 additions & 1 deletion packages/editor/src/components/alignment-toolbar/index.js
Original file line number Diff line number Diff line change
@@ -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 = [
{
Expand All @@ -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 (
<Toolbar
isCollapsed={ isCollapsed }
icon={ activeAlignment ? activeAlignment.icon : 'editor-alignleft' }
label={ __( 'Change Text Alignment' ) }
controls={ ALIGNMENT_CONTROLS.map( ( control ) => {
const { align } = control;
const isActive = ( value === align );
Expand All @@ -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 );
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ exports[`AlignmentToolbar should match snapshot 1`] = `
},
]
}
icon="editor-alignleft"
label="Change Text Alignment"
/>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { shallow } from 'enzyme';
/**
* Internal dependencies
*/
import AlignmentToolbar from '../';
import { AlignmentToolbar } from '../';

describe( 'AlignmentToolbar', () => {
const alignment = 'left';
Expand Down
Loading