-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Add multi select #896
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 #896
Changes from 1 commit
a687b81
1cb0e46
4ef611a
01d124a
cd04a65
ec368f5
91b577c
d489fce
336eade
236bf6f
6ef9c81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ import { | |
| isBlockHovered, | ||
| isBlockSelected, | ||
| isBlockMultiSelected, | ||
| isFirstSelected, | ||
| isTypingInBlock, | ||
| } from '../../selectors'; | ||
|
|
||
|
|
@@ -105,7 +106,7 @@ class VisualEditorBlock extends wp.element.Component { | |
|
|
||
| // Remove block on backspace | ||
| if ( 8 /* Backspace */ === keyCode && target === this.node ) { | ||
| this.props.onRemove( this.props.uid ); | ||
| this.props.onRemove( [ this.props.uid ] ); | ||
| if ( this.props.previousBlock ) { | ||
| this.props.onFocus( this.props.previousBlock.uid, { offset: -1 } ); | ||
| } | ||
|
|
@@ -165,7 +166,7 @@ class VisualEditorBlock extends wp.element.Component { | |
| } | ||
|
|
||
| render() { | ||
| const { block } = this.props; | ||
| const { block, selectedBlocks } = this.props; | ||
| const settings = wp.blocks.getBlockSettings( block.blockType ); | ||
|
|
||
| let BlockEdit; | ||
|
|
@@ -199,8 +200,6 @@ class VisualEditorBlock extends wp.element.Component { | |
| return ( | ||
| <div | ||
| ref={ this.bindBlockNode } | ||
| onClick={ this.selectAndStopPropagation } | ||
| onFocus={ onSelect } | ||
| onKeyDown={ this.removeOrDeselect } | ||
| onMouseDown={ this.props.onSelectionStart } | ||
| onTouchStart={ this.props.onSelectionStart } | ||
|
|
@@ -218,7 +217,7 @@ class VisualEditorBlock extends wp.element.Component { | |
| tabIndex="0" | ||
| { ...wrapperProps } | ||
| > | ||
| { ( showUI || isHovered ) && <BlockMover uid={ block.uid } /> } | ||
| { ( showUI || isHovered ) && <BlockMover uids={ [ block.uid ] } /> } | ||
| { showUI && | ||
| <CSSTransitionGroup | ||
| transitionName={ { appear: 'is-appearing', appearActive: 'is-appearing-active' } } | ||
|
|
@@ -242,7 +241,26 @@ class VisualEditorBlock extends wp.element.Component { | |
| </div> | ||
| </CSSTransitionGroup> | ||
| } | ||
| <div onKeyPress={ this.maybeStartTyping }> | ||
| { this.props.isFirstSelected && ( | ||
|
||
| <BlockMover uids={ selectedBlocks } /> | ||
| ) } | ||
| { this.props.isFirstSelected && ( | ||
| <div className="editor-visual-editor__block-controls"> | ||
| <Toolbar | ||
| controls={ [ { | ||
| icon: 'trash', | ||
| title: '', | ||
| onClick: () => this.props.onRemove( selectedBlocks ), | ||
| isActive: false, | ||
| } ] } | ||
| /> | ||
| </div> | ||
| ) } | ||
| <div | ||
| onKeyPress={ this.maybeStartTyping } | ||
| onFocus={ onSelect } | ||
| onClick={ this.selectAndStopPropagation } | ||
| > | ||
| <BlockEdit | ||
| focus={ focus } | ||
| attributes={ block.attributes } | ||
|
|
@@ -266,6 +284,7 @@ export default connect( | |
| block: getBlock( state, ownProps.uid ), | ||
| isSelected: isBlockSelected( state, ownProps.uid ), | ||
| isMultiSelected: isBlockMultiSelected( state, ownProps.uid ), | ||
| isFirstSelected: isFirstSelected( state, ownProps.uid ), | ||
| isHovered: isBlockHovered( state, ownProps.uid ), | ||
| focus: getBlockFocus( state, ownProps.uid ), | ||
| isTyping: isTypingInBlock( state, ownProps.uid ), | ||
|
|
@@ -319,10 +338,10 @@ export default connect( | |
| dispatch( focusBlock( ...args ) ); | ||
| }, | ||
|
|
||
| onRemove( uid ) { | ||
| onRemove( uids ) { | ||
| dispatch( { | ||
| type: 'REMOVE_BLOCK', | ||
| uid, | ||
| type: 'REMOVE_BLOCKS', | ||
| uids, | ||
| } ); | ||
| }, | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ | |
| */ | ||
| import { combineReducers, applyMiddleware, createStore } from 'redux'; | ||
| import refx from 'refx'; | ||
| import { keyBy, last, omit, without, flowRight } from 'lodash'; | ||
| import { keyBy, first, last, omit, without, flowRight } from 'lodash'; | ||
|
|
||
| /** | ||
| * Internal dependencies | ||
|
|
@@ -47,10 +47,10 @@ export const editor = combineUndoableReducers( { | |
|
|
||
| case 'UPDATE_BLOCK': | ||
| case 'INSERT_BLOCK': | ||
| case 'MOVE_BLOCK_DOWN': | ||
| case 'MOVE_BLOCK_UP': | ||
| case 'MOVE_BLOCKS_DOWN': | ||
| case 'MOVE_BLOCKS_UP': | ||
| case 'REPLACE_BLOCKS': | ||
| case 'REMOVE_BLOCK': | ||
| case 'REMOVE_BLOCKS': | ||
| case 'EDIT_POST': | ||
| return true; | ||
| } | ||
|
|
@@ -89,59 +89,72 @@ export const editor = combineUndoableReducers( { | |
| }; | ||
| }, omit( state, action.uids ) ); | ||
|
|
||
| case 'REMOVE_BLOCK': | ||
| return omit( state, action.uid ); | ||
| case 'REMOVE_BLOCKS': | ||
| return omit( state, action.uids ); | ||
| } | ||
|
|
||
| return state; | ||
| }, | ||
|
|
||
| blockOrder( state = [], action ) { | ||
| let index; | ||
| let swappedUid; | ||
| switch ( action.type ) { | ||
| case 'RESET_BLOCKS': | ||
| return action.blocks.map( ( { uid } ) => uid ); | ||
|
|
||
| case 'INSERT_BLOCK': | ||
| case 'INSERT_BLOCK': { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious to know why do you wrap
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I'd rather scope the /* ... */
{
let something = 1;
}
/* ... */
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, Thanks for the explanation |
||
| const position = action.after ? state.indexOf( action.after ) + 1 : state.length; | ||
| return [ | ||
| ...state.slice( 0, position ), | ||
| action.block.uid, | ||
| ...state.slice( position ), | ||
| ]; | ||
| } | ||
|
|
||
| case 'MOVE_BLOCKS_UP': { | ||
| const firstUid = first( action.uids ); | ||
| const lastUid = last( action.uids ); | ||
|
|
||
| case 'MOVE_BLOCK_UP': | ||
| if ( action.uid === state[ 0 ] ) { | ||
| if ( ! state.length || firstUid === first( state ) ) { | ||
| return state; | ||
| } | ||
| index = state.indexOf( action.uid ); | ||
| swappedUid = state[ index - 1 ]; | ||
|
|
||
| const firstIndex = state.indexOf( firstUid ); | ||
| const lastIndex = state.indexOf( lastUid ); | ||
| const swappedUid = state[ firstIndex - 1 ]; | ||
|
|
||
| return [ | ||
| ...state.slice( 0, index - 1 ), | ||
| action.uid, | ||
| ...state.slice( 0, firstIndex - 1 ), | ||
| ...action.uids, | ||
| swappedUid, | ||
| ...state.slice( index + 1 ), | ||
| ...state.slice( lastIndex + 1 ), | ||
| ]; | ||
| } | ||
|
|
||
| case 'MOVE_BLOCK_DOWN': | ||
| if ( action.uid === last( state ) ) { | ||
| case 'MOVE_BLOCKS_DOWN': { | ||
| const firstUid = first( action.uids ); | ||
| const lastUid = last( action.uids ); | ||
|
|
||
| if ( ! state.length || lastUid === last( state ) ) { | ||
| return state; | ||
| } | ||
| index = state.indexOf( action.uid ); | ||
| swappedUid = state[ index + 1 ]; | ||
|
|
||
| const firstIndex = state.indexOf( firstUid ); | ||
| const lastIndex = state.indexOf( lastUid ); | ||
| const swappedUid = state[ lastIndex + 1 ]; | ||
|
|
||
| return [ | ||
| ...state.slice( 0, index ), | ||
| ...state.slice( 0, firstIndex ), | ||
| swappedUid, | ||
| action.uid, | ||
| ...state.slice( index + 2 ), | ||
| ...action.uids, | ||
| ...state.slice( lastIndex + 2 ), | ||
| ]; | ||
| } | ||
|
|
||
| case 'REPLACE_BLOCKS': | ||
| if ( ! action.blocks ) { | ||
| return state; | ||
| } | ||
| index = state.indexOf( action.uids[ 0 ] ); | ||
|
|
||
| return state.reduce( ( memo, uid ) => { | ||
| if ( uid === action.uids[ 0 ] ) { | ||
| return memo.concat( action.blocks.map( ( block ) => block.uid ) ); | ||
|
|
@@ -152,8 +165,8 @@ export const editor = combineUndoableReducers( { | |
| return memo; | ||
| }, [] ); | ||
|
|
||
| case 'REMOVE_BLOCK': | ||
| return without( state, action.uid ); | ||
| case 'REMOVE_BLOCKS': | ||
| return without( state, ...action.uids ); | ||
| } | ||
|
|
||
| return state; | ||
|
|
@@ -204,11 +217,13 @@ export function selectedBlock( state = {}, action ) { | |
| case 'CLEAR_SELECTED_BLOCK': | ||
| return {}; | ||
|
|
||
| case 'MOVE_BLOCK_UP': | ||
| case 'MOVE_BLOCK_DOWN': | ||
| return action.uid === state.uid | ||
| case 'MOVE_BLOCKS_UP': | ||
| case 'MOVE_BLOCKS_DOWN': { | ||
| const firstUid = first( action.uids ); | ||
| return firstUid === state.uid | ||
| ? state | ||
| : { uid: action.uid, typing: false, focus: {} }; | ||
| : { uid: firstUid, typing: false, focus: {} }; | ||
| } | ||
|
|
||
| case 'INSERT_BLOCK': | ||
| return { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we drop the
selectedPropsprop and move it to the connect ofVisualEditorBlockinstead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah :)