Skip to content

Commit

Permalink
Update the block switcher UI to show style variations and previews
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Jun 21, 2018
1 parent 686e02d commit 6173aa8
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 107 deletions.
4 changes: 2 additions & 2 deletions core-blocks/quote/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export const settings = {

transforms: {
styles: [
{ name: 'default', label: '1', isDefault: true, icon: 'format-quote' },
{ name: 'large', label: '2', icon: 'testimonial' },
{ name: 'default', label: '1', isDefault: true },
{ name: 'large', label: '2' },
],
from: [
{
Expand Down
3 changes: 1 addition & 2 deletions edit-post/assets/stylesheets/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ $z-layers: (
'.edit-post-meta-boxes-area.is-loading:before': 1,
'.edit-post-meta-boxes-area .spinner': 5,
'.editor-block-contextual-toolbar': 21,
'.editor-block-switcher__menu': 5,
'.components-popover__close': 5,
'.editor-block-list__insertion-point': 5,
'.core-blocks-gallery-item__inline-menu': 20,
Expand All @@ -30,7 +29,7 @@ $z-layers: (
// Side UI active buttons
'.editor-block-settings-remove': 1,
'.editor-block-mover__control': 1,

// Should have lower index than anything else positioned inside the block containers
'.editor-block-list__block-draggable': 0,

Expand Down
23 changes: 14 additions & 9 deletions editor/components/block-preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,27 @@ import './style.scss';
*
* @return {WPElement} Rendered element.
*/
function BlockPreview( { name, attributes } ) {
const block = createBlock( name, attributes );

function BlockPreview( props ) {
return (
<div className="editor-block-preview">
<div className="editor-block-preview__title">{ __( 'Preview' ) }</div>
<div className="editor-block-preview__content">
<BlockEdit
name={ name }
focus={ false }
attributes={ block.attributes }
setAttributes={ noop }
/>
<BlockPreviewContent { ...props } />
</div>
</div>
);
}

export function BlockPreviewContent( { name, attributes } ) {
const block = createBlock( name, attributes );
return (
<BlockEdit
name={ name }
focus={ false }
attributes={ block.attributes }
setAttributes={ noop }
/>
);
}

export default BlockPreview;
51 changes: 40 additions & 11 deletions editor/components/block-styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { find, compact, get } from 'lodash';
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Toolbar } from '@wordpress/components';
import { compose } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { getBlockType } from '@wordpress/blocks';
import { Button } from '@wordpress/components';

/**
* Internal dependencies
*/
import { BlockPreviewContent } from '../block-preview';

/**
* Returns the active style from the given className.
Expand Down Expand Up @@ -68,7 +72,15 @@ export function replaceActiveStyle( className, activeStyle, newStyle ) {
return updatedClassName;
}

function BlockStyles( { styles, className, onChangeClassName } ) {
function BlockStyles( {
styles,
className,
onChangeClassName,
name,
attributes,
onSwitch,
onHoverClassName,
} ) {
if ( ! styles ) {
return null;
}
Expand All @@ -77,17 +89,32 @@ function BlockStyles( { styles, className, onChangeClassName } ) {
function updateClassName( style ) {
const updatedClassName = replaceActiveStyle( className, activeStyle, style );
onChangeClassName( updatedClassName );
onSwitch();
}

return (
<Toolbar controls={ styles.map( ( variation ) => ( {
icon: variation.icon,
title: sprintf( __( 'Style %s' ), variation.label || variation.name ),
isActive: activeStyle === variation,
onClick() {
updateClassName( variation );
},
} ) ) } />
<div className="editor-block-styles">
{ styles.map( ( style ) => {
const styleClassName = replaceActiveStyle( className, activeStyle, style );
return (
<Button
key={ style.name }
className="editor-block-styles__item"
onClick={ () => updateClassName( style ) }
onMouseEnter={ () => onHoverClassName( styleClassName ) }
onMouseLeave={ () => onHoverClassName( null ) }
>
<BlockPreviewContent
name={ name }
attributes={ {
...attributes,
className: styleClassName,
} }
/>
</Button>
);
} ) }
</div>
);
}

Expand All @@ -96,6 +123,8 @@ export default compose( [
const block = select( 'core/editor' ).getBlock( uid );

return {
name: block.name,
attributes: block.attributes,
className: block.attributes.className || '',
styles: get( getBlockType( block.name ), [ 'transforms', 'styles' ] ),
};
Expand Down
172 changes: 103 additions & 69 deletions editor/components/block-switcher/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Dropdown, Dashicon, IconButton, Toolbar, NavigableMenu } from '@wordpress/components';
import { Dropdown, Dashicon, IconButton, Toolbar, PanelBody } from '@wordpress/components';
import { getBlockType, getPossibleBlockTransformations, switchToBlockType } from '@wordpress/blocks';
import { compose } from '@wordpress/element';
import { compose, Component, Fragment } from '@wordpress/element';
import { keycodes } from '@wordpress/utils';
import { withSelect, withDispatch } from '@wordpress/data';

Expand All @@ -13,87 +18,116 @@ import { withSelect, withDispatch } from '@wordpress/data';
*/
import './style.scss';
import BlockIcon from '../block-icon';
import BlockStyles from '../block-styles';
import BlockPreview from '../block-preview';

/**
* Module Constants
*/
const { DOWN } = keycodes;

export function BlockSwitcher( { blocks, onTransform, isLocked } ) {
const allowedBlocks = getPossibleBlockTransformations( blocks );
export class BlockSwitcher extends Component {
constructor() {
super( ...arguments );
this.state = {
hoveredClassName: null,
};
this.onHoverClassName = this.onHoverClassName.bind( this );
}

if ( isLocked || ! allowedBlocks.length ) {
return null;
onHoverClassName( className ) {
this.setState( { hoveredClassName: className } );
}

const sourceBlockName = blocks[ 0 ].name;
const blockType = getBlockType( sourceBlockName );
render() {
const { blocks, onTransform, isLocked } = this.props;
const { hoveredClassName } = this.state;
const allowedBlocks = getPossibleBlockTransformations( blocks );

return (
<Dropdown
className="editor-block-switcher"
contentClassName="editor-block-switcher__popover"
renderToggle={ ( { onToggle, isOpen } ) => {
const openOnArrowDown = ( event ) => {
if ( ! isOpen && event.keyCode === DOWN ) {
event.preventDefault();
event.stopPropagation();
onToggle();
}
};
const label = __( 'Change block type' );
if ( isLocked || ! allowedBlocks.length ) {
return null;
}

return (
<Toolbar>
<IconButton
className="editor-block-switcher__toggle"
icon={ <BlockIcon icon={ blockType.icon && blockType.icon.src } /> }
onClick={ onToggle }
aria-haspopup="true"
aria-expanded={ isOpen }
label={ label }
tooltip={ label }
onKeyDown={ openOnArrowDown }
>
<Dashicon icon="arrow-down" />
</IconButton>
</Toolbar>
);
} }
renderContent={ ( { onClose } ) => (
<div>
<span
className="editor-block-switcher__menu-title"
>
{ __( 'Transform into:' ) }
</span>
<NavigableMenu
role="menu"
aria-label={ __( 'Block types' ) }
>
{ allowedBlocks.map( ( { name, title, icon } ) => (
const sourceBlockName = blocks[ 0 ].name;
const blockType = getBlockType( sourceBlockName );
const hasStyles = blocks.length === 1 && get( blockType, [ 'transforms', 'styles' ], [] ).length !== 0;

return (
<Dropdown
position="bottom right"
className="editor-block-switcher"
contentClassName="editor-block-switcher__popover"
renderToggle={ ( { onToggle, isOpen } ) => {
const openOnArrowDown = ( event ) => {
if ( ! isOpen && event.keyCode === DOWN ) {
event.preventDefault();
event.stopPropagation();
onToggle();
}
};
const label = __( 'Change block type' );

return (
<Toolbar>
<IconButton
key={ name }
onClick={ () => {
onTransform( blocks, name );
onClose();
} }
className="editor-block-switcher__menu-item"
icon={ (
<span className="editor-block-switcher__block-icon">
<BlockIcon icon={ icon && icon.src } />
</span>
) }
role="menuitem"
className="editor-block-switcher__toggle"
icon={ <BlockIcon icon={ blockType.icon && blockType.icon.src } /> }
onClick={ onToggle }
aria-haspopup="true"
aria-expanded={ isOpen }
label={ label }
tooltip={ label }
onKeyDown={ openOnArrowDown }
>
{ title }
<Dashicon icon="arrow-down" />
</IconButton>
) ) }
</NavigableMenu>
</div>
) }
/>
);
</Toolbar>
);
} }
renderContent={ ( { onClose } ) => (
<Fragment>
{ hasStyles &&
<PanelBody
title={ __( 'Block Styles' ) }
initialOpen
>
<BlockStyles uid={ blocks[ 0 ].uid } onSwitch={ onClose } onHoverClassName={ this.onHoverClassName } />
</PanelBody>
}
<PanelBody
title={ __( 'Block transforms' ) }
initialOpen={ ! hasStyles }
>
{ allowedBlocks.map( ( { name, title, icon } ) => (
<IconButton
key={ name }
onClick={ () => {
onTransform( blocks, name );
onClose();
} }
className="editor-block-switcher__transform"
icon={ (
<span className="editor-block-switcher__block-icon">
<BlockIcon icon={ icon && icon.src } />
</span>
) }
>
{ title }
</IconButton>
) ) }
</PanelBody>

{ ( hoveredClassName !== null ) &&
<BlockPreview
name={ blocks[ 0 ].name }
attributes={ { ...blocks[ 0 ].attributes, className: hoveredClassName } }
/>
}
</Fragment>
) }
/>
);
}
}

export default compose(
Expand Down
Loading

0 comments on commit 6173aa8

Please sign in to comment.