Skip to content

Commit

Permalink
Adds the block navigation menu to the header (#10545)
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Oct 17, 2018
1 parent 3eaf799 commit e1cc1b2
Show file tree
Hide file tree
Showing 20 changed files with 554 additions and 38 deletions.
13 changes: 13 additions & 0 deletions docs/data/data-core-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,19 @@ exist.

Root client ID, if exists

### getBlockHierarchyRootClientId

Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.

*Parameters*

* state: Editor state.
* clientId: Block from which to find root client ID.

*Returns*

Root client ID

### getAdjacentBlockClientId

Returns the client ID of the block adjacent one at the given reference
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/columns/column.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const settings = {

parent: [ 'core/columns' ],

icon: 'columns',
icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z" /><path d="M11.99 18.54l-7.37-5.73L3 14.07l9 7 9-7-1.63-1.27zM12 16l7.36-5.73L21 9l-9-7-9 7 1.63 1.27L12 16zm0-11.47L17.74 9 12 13.47 6.26 9 12 4.53z" /></svg>,

description: __( 'A single column within a columns block.' ),

Expand Down
5 changes: 4 additions & 1 deletion packages/components/src/menu-group/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export function MenuGroup( {
}

const labelId = `components-menu-group-label-${ instanceId }`;
const classNames = classnames( className, 'components-menu-group' );
const classNames = classnames(
className,
'components-menu-group'
);

return (
<div className={ classNames }>
Expand Down
18 changes: 1 addition & 17 deletions packages/components/src/menu-item/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
width: 100%;
padding: 8px;
text-align: left;
padding-left: 2rem;
color: $dark-gray-500;

// Target plugin icons that can have arbitrary classes by using an aggressive selector.
Expand All @@ -18,10 +17,6 @@
flex: 0 0 auto;
}

&.has-icon {
padding-left: 0.5rem;
}

&:hover:not(:disabled):not([aria-disabled="true"]) {
@include menu-style__hover;
}
Expand All @@ -30,18 +25,6 @@
@include menu-style__focus;
}

// Colorize plugin icons to ensure contrast and cohesion, but allow plugin developers to override.
svg,
svg * {
fill: $dark-gray-500;
}

&:hover svg,
&:hover svg * {
// !important allows icons from plugins to be overriden and given a dark-gray fill
fill: $dark-gray-900 !important;
}

// Don't wrap text until viewport is beyond the mobile breakpoint.
@include break-mobile() {
.components-popover:not(.is-mobile) & {
Expand All @@ -65,3 +48,4 @@
display: inline;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EditorHistoryRedo,
EditorHistoryUndo,
NavigableToolbar,
BlockNavigationDropdown,
} from '@wordpress/editor';

/**
Expand All @@ -36,6 +37,7 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, mode } ) {
<EditorHistoryUndo />
<EditorHistoryRedo />
<TableOfContents />
<BlockNavigationDropdown />
{ hasFixedToolbar && isLargeViewport && (
<div className="edit-post-header-toolbar__block-toolbar">
<BlockToolbar />
Expand Down
8 changes: 8 additions & 0 deletions packages/edit-post/src/components/header/more-menu/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,12 @@
> div:not(:last-child) .components-menu-group {
border-bottom: $border-width solid $light-gray-500;
}

.components-menu-item__button {
padding-left: 2rem;

&.has-icon {
padding-left: 0.5rem;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const globalShortcuts = {
description: __( 'Show or hide the settings sidebar.' ),
ariaLabel: shortcutAriaLabel.primaryShift( ',' ),
},
{
keyCombination: access( 'o' ),
description: __( 'Open the block navigation menu.' ),
},
{
keyCombination: ctrl( '`' ),
description: __( 'Navigate to the next part of the editor.' ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ exports[`KeyboardShortcutHelpModal should match snapshot when the modal is activ
",",
],
},
Object {
"description": "Open the block navigation menu.",
"keyCombination": Array [
"Shift",
"+",
"Alt",
"+",
"O",
],
},
Object {
"ariaLabel": "Control + Backtick",
"description": "Navigate to the next part of the editor.",
Expand Down
20 changes: 10 additions & 10 deletions packages/editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import IgnoreNestedEvents from '../ignore-nested-events';
import InserterWithShortcuts from '../inserter-with-shortcuts';
import Inserter from '../inserter';
import withHoverAreas from './with-hover-areas';
import { isInsideRootBlock } from '../../utils/dom';

export class BlockListBlock extends Component {
constructor() {
Expand Down Expand Up @@ -91,7 +92,7 @@ export class BlockListBlock extends Component {
}

if ( this.props.isSelected && ! prevProps.isSelected ) {
this.focusTabbable();
this.focusTabbable( true );
}
}

Expand All @@ -118,8 +119,10 @@ export class BlockListBlock extends Component {

/**
* When a block becomes selected, transition focus to an inner tabbable.
*
* @param {boolean} ignoreInnerBlocks Should not focus inner blocks.
*/
focusTabbable() {
focusTabbable( ignoreInnerBlocks ) {
const { initialPosition } = this.props;

// Focus is captured by the wrapper node, so while focus transition
Expand All @@ -131,7 +134,11 @@ export class BlockListBlock extends Component {
}

// Find all tabbables within node.
const textInputs = focus.tabbable.find( this.node ).filter( isTextField );
const textInputs = focus.tabbable
.find( this.node )
.filter( isTextField )
// Exclude inner blocks
.filter( ( node ) => ! ignoreInnerBlocks || isInsideRootBlock( this.node, node ) );

// If reversed (e.g. merge via backspace), use the last in the set of
// tabbables.
Expand Down Expand Up @@ -370,9 +377,7 @@ export class BlockListBlock extends Component {
isEmptyDefaultBlock,
isMovable,
isPreviousBlockADefaultEmptyBlock,
hasSelectedInnerBlock,
isParentOfSelectedBlock,
hasMultiSelection,
isDraggable,
} = this.props;
const isHovered = this.state.isHovered && ! isMultiSelecting;
Expand All @@ -390,7 +395,6 @@ export class BlockListBlock extends Component {
const showEmptyBlockSideInserter = ( isSelected || isHovered ) && isEmptyDefaultBlock && isValid;
const showSideInserter = ( isSelected || isHovered ) && isEmptyDefaultBlock;
const shouldAppearSelected = ! isFocusMode && ! hasFixedToolbar && ! showSideInserter && isSelected && ! isTypingWithinBlock;
const shouldAppearSelectedParent = ! isFocusMode && ! hasFixedToolbar && ! showSideInserter && hasSelectedInnerBlock && ! isTypingWithinBlock && ! hasMultiSelection;
const shouldAppearHovered = ! isFocusMode && ! hasFixedToolbar && isHovered && ! isEmptyDefaultBlock;
// We render block movers and block settings to keep them tabbale even if hidden
const shouldRenderMovers = ! isFocusMode && ( isSelected || hoverArea === 'left' ) && ! showEmptyBlockSideInserter && ! isMultiSelecting && ! isPartOfMultiSelection && ! isTypingWithinBlock;
Expand All @@ -410,7 +414,6 @@ export class BlockListBlock extends Component {
'has-warning': ! isValid || !! error || isUnregisteredBlock,
'is-selected': shouldAppearSelected,
'is-multi-selected': isPartOfMultiSelection,
'is-selected-parent': shouldAppearSelectedParent,
'is-hovered': shouldAppearHovered,
'is-reusable': isReusableBlock( blockType ),
'is-dragging': dragging,
Expand Down Expand Up @@ -592,7 +595,6 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
getEditorSettings,
hasSelectedInnerBlock,
getTemplateLock,
hasMultiSelection,
} = select( 'core/editor' );
const isSelected = isBlockSelected( clientId );
const { hasFixedToolbar, focusMode } = getEditorSettings();
Expand All @@ -607,7 +609,6 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
isPartOfMultiSelection: isBlockMultiSelected( clientId ) || isAncestorMultiSelected( clientId ),
isFirstMultiSelected: isFirstMultiSelectedBlock( clientId ),
isMultiSelecting: isMultiSelecting(),
hasSelectedInnerBlock: hasSelectedInnerBlock( clientId, false ),
// We only care about this prop when the block is selected
// Thus to avoid unnecessary rerenders we avoid updating the prop if the block is not selected.
isTypingWithinBlock: ( isSelected || isParentOfSelectedBlock ) && isTyping(),
Expand All @@ -626,7 +627,6 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
block,
isSelected,
isParentOfSelectedBlock,
hasMultiSelection: hasMultiSelection(),
};
} );

Expand Down
1 change: 0 additions & 1 deletion packages/editor/src/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@
}

// Selected style
&.is-selected-parent > .editor-block-list__block-edit::before,
&.is-selected > .editor-block-list__block-edit::before {
// Use opacity to work in various editor styles.
outline: $border-width solid $dark-opacity-light-500;
Expand Down
46 changes: 46 additions & 0 deletions packages/editor/src/components/block-navigation/dropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* WordPress dependencies
*/
import { Fragment } from '@wordpress/element';
import { IconButton, Dropdown, SVG, Path, KeyboardShortcuts } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { rawShortcut } from '@wordpress/keycodes';

/**
* Internal dependencies
*/
import BlockNavigation from './';

const menuIcon = (
<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20">
<Path d="M5 5H3v2h2V5zm3 8h11v-2H8v2zm9-8H6v2h11V5zM7 11H5v2h2v-2zm0 8h2v-2H7v2zm3-2v2h11v-2H10z" />
</SVG>
);

function BlockNavigationDropdown() {
return (
<Dropdown
renderToggle={ ( { isOpen, onToggle } ) => (
<Fragment>
<KeyboardShortcuts
bindGlobal
shortcuts={ {
[ rawShortcut.access( 'o' ) ]: onToggle,
} }
/>
<IconButton
icon={ menuIcon }
aria-expanded={ isOpen }
onClick={ onToggle }
label={ __( 'Block Navigation' ) }
/>
</Fragment>
) }
renderContent={ ( { onClose } ) => (
<BlockNavigation onSelect={ onClose } />
) }
/>
);
}

export default BlockNavigationDropdown;
Loading

0 comments on commit e1cc1b2

Please sign in to comment.