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

Add multi select inspector #1811

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions blocks/library/paragraph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ registerBlockType( 'core/paragraph', {
}
},

multiSelectedControls( { attributes, setAttributes } ) {
const value = attributes.reduce( ( acc, { align } ) => {
return acc === align ? acc : null;
}, attributes[ 0 ].align );

return [
<AlignmentToolbar
key="alignment-toolbar"
value={ value }
onChange={ ( nextAlign ) => {
setAttributes( { align: nextAlign } );
} }
/>,
];
},

edit( { attributes, setAttributes, insertBlocksAfter, focus, setFocus, mergeBlocks, onReplace } ) {
const { align, content, dropCap, placeholder, fontSize, backgroundColor, textColor, width } = attributes;
const toggleDropCap = () => setAttributes( { dropCap: ! dropCap } );
Expand Down
6 changes: 4 additions & 2 deletions editor/block-settings-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ export default connect(
onDelete() {
dispatch( {
type: 'REMOVE_BLOCKS',
uids: [ ownProps.uid ],
uids: ownProps.uids,
} );
},
onSelect() {
dispatch( selectBlock( ownProps.uid ) );
if ( ownProps.uids.length === 1 ) {
dispatch( selectBlock( ownProps.uids[ 0 ] ) );
}
},
setActivePanel() {
dispatch( {
Expand Down
34 changes: 24 additions & 10 deletions editor/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { connect } from 'react-redux';
import { uniq, get, reduce, find } from 'lodash';
import { uniq, get, reduce, find, first, last } from 'lodash';
import clickOutside from 'react-click-outside';

/**
Expand All @@ -17,8 +17,8 @@ import { getBlockType, getBlockTypes, switchToBlockType } from '@wordpress/block
* Internal dependencies
*/
import './style.scss';
import { replaceBlocks } from '../actions';
import { getBlock } from '../selectors';
import { replaceBlocks, multiSelect } from '../actions';
import { getBlocksByUid } from '../selectors';

class BlockSwitcher extends Component {
constructor() {
Expand Down Expand Up @@ -48,15 +48,23 @@ class BlockSwitcher extends Component {
this.setState( {
open: false,
} );
this.props.onTransform( this.props.block, name );
this.props.onTransform( this.props.blocks, name );
};
}

render() {
const blockType = getBlockType( this.props.block.name );
const names = uniq( this.props.blocks.map( ( block ) => block.name ) );

// Blocks do not share the same name.
if ( names.length !== 1 ) {
return null;
}

const blockName = first( names );
const blockType = getBlockType( blockName );
const blocksToBeTransformedFrom = reduce( getBlockTypes(), ( memo, block ) => {
const transformFrom = get( block, 'transforms.from', [] );
const transformation = find( transformFrom, t => t.type === 'block' && t.blocks.indexOf( this.props.block.name ) !== -1 );
const transformation = find( transformFrom, t => t.type === 'block' && t.blocks.indexOf( blockName ) !== -1 );
return transformation ? memo.concat( [ block.name ] ) : memo;
}, [] );
const blocksToBeTransformedTo = get( blockType, 'transforms.to', [] )
Expand Down Expand Up @@ -115,14 +123,20 @@ class BlockSwitcher extends Component {

export default connect(
( state, ownProps ) => ( {
block: getBlock( state, ownProps.uid ),
blocks: getBlocksByUid( state, ownProps.uids ),
} ),
( dispatch, ownProps ) => ( {
onTransform( block, name ) {
onTransform( blocks, name ) {
dispatch( replaceBlocks(
[ ownProps.uid ],
switchToBlockType( block, name )
ownProps.uids,
blocks.reduce( ( acc, block ) => {
return [ ...acc, ...switchToBlockType( block, name ) ];
}, [] ),
) );

if ( blocks.length > 1 ) {
dispatch( multiSelect( first( blocks ).uid, last( blocks ).uid ) );
}
},
} )
)( clickOutside( BlockSwitcher ) );
8 changes: 4 additions & 4 deletions editor/block-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class BlockToolbar extends Component {

render() {
const { showMobileControls } = this.state;
const { uid } = this.props;
const { uids } = this.props;

const toolbarClassname = classnames( 'editor-block-toolbar', {
'is-showing-mobile-controls': showMobileControls,
Expand All @@ -60,7 +60,7 @@ class BlockToolbar extends Component {
<div className={ toolbarClassname }>
<div className="editor-block-toolbar__group">
{ ! showMobileControls && [
<BlockSwitcher key="switcher" uid={ uid } />,
<BlockSwitcher key="switcher" uids={ uids } />,
<Slot key="slot" name="Formatting.Toolbar" />,
] }
<Toolbar className="editor-block-toolbar__mobile-tools">
Expand All @@ -74,8 +74,8 @@ class BlockToolbar extends Component {

{ showMobileControls &&
<div className="editor-block-toolbar__mobile-tools-content">
<BlockMover uids={ [ uid ] } />
<BlockRightMenu uid={ uid } />
<BlockMover uids={ uids } />
<BlockRightMenu uids={ uids } />
</div>
}
</Toolbar>
Expand Down
47 changes: 2 additions & 45 deletions editor/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { connect } from 'react-redux';
/**
* WordPress dependencies
*/
import { sprintf, _n, __ } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';

/**
Expand All @@ -18,53 +18,16 @@ import PublishButton from './publish-button';
import PreviewButton from './preview-button';
import ModeSwitcher from './mode-switcher';
import Inserter from '../inserter';
import { getMultiSelectedBlockUids, hasEditorUndo, hasEditorRedo, isEditorSidebarOpened } from '../selectors';
import { clearSelectedBlock } from '../actions';
import { hasEditorUndo, hasEditorRedo, isEditorSidebarOpened } from '../selectors';

function Header( {
multiSelectedBlockUids,
onRemove,
onDeselect,
undo,
redo,
hasRedo,
hasUndo,
toggleSidebar,
isSidebarOpened,
} ) {
const count = multiSelectedBlockUids.length;

if ( count ) {
return (
<div
role="region"
aria-label={ __( 'Editor toolbar' ) }
className="editor-header editor-header-multi-select"
>
<div className="editor-selected-count">
{ sprintf( _n( '%d block selected', '%d blocks selected', count ), count ) }
</div>
<div className="editor-selected-delete">
<IconButton
icon="trash"
label={ __( 'Delete selected blocks' ) }
onClick={ () => onRemove( multiSelectedBlockUids ) }
focus={ true }
>
{ __( 'Delete' ) }
</IconButton>
</div>
<div className="editor-selected-clear">
<IconButton
icon="no"
label={ __( 'Clear selected blocks' ) }
onClick={ () => onDeselect() }
/>
</div>
</div>
);
}

return (
<div
role="region"
Expand Down Expand Up @@ -102,17 +65,11 @@ function Header( {

export default connect(
( state ) => ( {
multiSelectedBlockUids: getMultiSelectedBlockUids( state ),
hasUndo: hasEditorUndo( state ),
hasRedo: hasEditorRedo( state ),
isSidebarOpened: isEditorSidebarOpened( state ),
} ),
( dispatch ) => ( {
onDeselect: () => dispatch( clearSelectedBlock() ),
onRemove: ( uids ) => dispatch( {
type: 'REMOVE_BLOCKS',
uids,
} ),
undo: () => dispatch( { type: 'UNDO' } ),
redo: () => dispatch( { type: 'REDO' } ),
toggleSidebar: () => dispatch( { type: 'TOGGLE_SIDEBAR' } ),
Expand Down
15 changes: 0 additions & 15 deletions editor/header/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,6 @@
right: $admin-sidebar-width-big;
}

.editor-header-multi-select {
background: $blue-medium-100;
border-bottom: 1px solid $blue-medium-200;
}

.editor-selected-count {
padding-right: $item-spacing;
color: $dark-gray-500;
border-right: 1px solid $light-gray-500;
}

.editor-selected-clear {
margin: 0 0 0 auto;
}

// hide all action buttons except the inserter on mobile
.editor-header__content-tools > .components-button {
display: none;
Expand Down
11 changes: 5 additions & 6 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,11 @@ class VisualEditorBlock extends Component {
>
<BlockDropZone index={ order } />
{ ( showUI || isHovered ) && <BlockMover uids={ [ block.uid ] } /> }
{ ( showUI || isHovered ) && <BlockRightMenu uid={ block.uid } /> }
{ showUI && isValid && <BlockToolbar uid={ block.uid } /> }

{ isFirstMultiSelected && (
<BlockMover uids={ multiSelectedBlockUids } />
) }
{ ( showUI || isHovered ) && <BlockRightMenu uids={ [ block.uid ] } /> }
{ showUI && isValid && <BlockToolbar uids={ [ block.uid ] } /> }
{ isFirstMultiSelected && <BlockMover uids={ multiSelectedBlockUids } /> }
{ isFirstMultiSelected && <BlockRightMenu uids={ multiSelectedBlockUids } /> }
{ isFirstMultiSelected && isValid && <BlockToolbar uids={ multiSelectedBlockUids } /> }
<div
onKeyPress={ this.maybeStartTyping }
onDragStart={ ( event ) => event.preventDefault() }
Expand Down
25 changes: 25 additions & 0 deletions editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,15 @@ export const getBlocks = createSelector(
]
);

export const getBlocksByUid = createSelector(
( state, uids ) => {
return uids.map( ( uid ) => getBlock( state, uid ) );
},
( state ) => [
state.editor.blocksByUid,
]
);

/**
* Returns the number of blocks currently present in the post.
*
Expand All @@ -450,6 +459,22 @@ export function getBlockCount( state ) {
return getBlockUids( state ).length;
}

/**
* Returns the number of blocks currently selected in the post.
*
* @param {Object} state Global application state
* @return {Object} Number of blocks selected in the post
*/
export function getSelectedBlockCount( state ) {
const multiSelectedBlockCount = getMultiSelectedBlockUids( state ).length;

if ( multiSelectedBlockCount ) {
return multiSelectedBlockCount;
}

return state.blockSelection.start ? 1 : 0;
}

/**
* Returns the currently selected block, or null if there is no selected block.
*
Expand Down
6 changes: 3 additions & 3 deletions editor/sidebar/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { connect } from 'react-redux';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { __, _n, sprintf } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';

/**
* Internal Dependencies
*/
import { getActivePanel } from '../selectors';

const SidebarHeader = ( { panel, onSetPanel, toggleSidebar } ) => {
const SidebarHeader = ( { panel, onSetPanel, toggleSidebar, count } ) => {
return (
<div className="components-panel__header editor-sidebar__panel-tabs">
<button
Expand All @@ -29,7 +29,7 @@ const SidebarHeader = ( { panel, onSetPanel, toggleSidebar } ) => {
className={ `editor-sidebar__panel-tab ${ panel === 'block' ? 'is-active' : '' }` }
aria-label={ __( 'Block settings' ) }
>
{ __( 'Block' ) }
{ sprintf( _n( 'Block', '%d Blocks', count ), count ) }
</button>
<IconButton
onClick={ toggleSidebar }
Expand Down
11 changes: 7 additions & 4 deletions editor/sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ import { withFocusReturn } from '@wordpress/components';
import './style.scss';
import PostSettings from './post-settings';
import BlockInspector from './block-inspector';
import MultiBlockInspector from './multi-block-inspector';
import Header from './header';
import { getActivePanel } from '../selectors';
import { getActivePanel, getSelectedBlockCount } from '../selectors';

const Sidebar = ( { panel } ) => {
const Sidebar = ( { panel, selectedBlockCount } ) => {
return (
<div className="editor-sidebar" role="region" aria-label={ __( 'Editor settings' ) }>
<Header />
<Header count={ selectedBlockCount || 1 } />
{ panel === 'document' && <PostSettings /> }
{ panel === 'block' && <BlockInspector /> }
{ panel === 'block' && selectedBlockCount === 1 && <BlockInspector /> }
{ panel === 'block' && selectedBlockCount > 1 && <MultiBlockInspector /> }
</div>
);
};
Expand All @@ -32,6 +34,7 @@ export default connect(
( state ) => {
return {
panel: getActivePanel( state ),
selectedBlockCount: getSelectedBlockCount( state ),
};
}
)( withFocusReturn( Sidebar ) );
Loading