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

Experiment: support for non-contiguous blocks in selection #3584

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
63944c2
starting to build out non-contiguous selections
ephox-mogran Nov 20, 2017
408872f
ctrl+click sort of working
ephox-mogran Nov 21, 2017
cf676c5
handling shift
ephox-mogran Nov 21, 2017
76f9148
navigating out of a multi-selection
ephox-mogran Nov 21, 2017
204e320
control clicking again
ephox-mogran Nov 21, 2017
67a776a
deselecting with Ctrl+Click
ephox-mogran Nov 21, 2017
887e658
temporarily simplify block uids
ephox-mogran Nov 21, 2017
30107b2
using sort API properly
ephox-mogran Nov 21, 2017
17681cc
starting unit tests
ephox-mogran Nov 22, 2017
327f245
handling basic toggling
ephox-mogran Nov 22, 2017
d3f6f36
toggling off examples
ephox-mogran Nov 22, 2017
7b309f2
incorporate tests
ephox-mogran Nov 22, 2017
dea384b
block selection manipulation
ephox-mogran Nov 22, 2017
bb4fe3f
Ctrl+Click can select a single block
ephox-mogran Nov 22, 2017
2420ab6
Ctrl+click to select when not in multi-selection
ephox-mogran Nov 22, 2017
049172c
changing range does not keep any old part of range
ephox-mogran Nov 22, 2017
9fbf443
starting to look at navigation mode
ephox-mogran Nov 22, 2017
fa0cd6b
adding support for Meta Key for mac
ephox-mogran Nov 23, 2017
7268209
temporary styles
ephox-mogran Nov 23, 2017
8ee8c94
parametering focus with uid
ephox-mogran Nov 23, 2017
585985f
giant focus outline
ephox-mogran Nov 23, 2017
784d3e5
starting to get navigation working
ephox-mogran Nov 23, 2017
8b04a1a
Ctrl+Meta to add to selection, without sets selection
ephox-mogran Nov 23, 2017
6cefaae
Ctrl + not control for keyboard ranges
ephox-mogran Nov 23, 2017
cd7ae98
toggling off a selection
ephox-mogran Nov 23, 2017
a11d496
better handling of navigation focus and multi-control buttons
ephox-mogran Nov 23, 2017
a1d7e34
cleanup
ephox-mogran Nov 23, 2017
373d380
a bit more MAC friendly
ephox-mogran Nov 23, 2017
6b8e937
more useful comment
ephox-mogran Nov 23, 2017
b9939d9
keep focus on the interacted with block
ephox-mogran Nov 24, 2017
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
7 changes: 6 additions & 1 deletion blocks/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { __ } from '@wordpress/i18n';
*/
import { getBlockType } from './registration';

let counter = 0;

/**
* Returns a block object given its type and attributes.
*
Expand All @@ -46,10 +48,13 @@ export function createBlock( name, blockAttributes = {} ) {
return result;
}, {} );

counter++;

// Blocks are stored with a unique ID, the assigned type name,
// and the block attributes.
return {
uid: uuid(),
// uid: uuid(),
uid: counter + '-block',
name,
isValid: true,
attributes,
Expand Down
34 changes: 33 additions & 1 deletion editor/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,44 @@ export function stopMultiSelect() {

export function multiSelect( start, end ) {
return {
type: 'MULTI_SELECT',
type: 'ADD_SELECTION_RANGE',
start,
end,
};
}

export function combineRange( start, end ) {
return {
type: 'COMBINE_SELECTION_RANGE',
start,
end,
};
}

export function setSelection( start, end, selected, focusUid ) {
return {
type: 'SET_SELECTION',
start,
end,
selected,
focusUid,
};
}

export function toggleSelection( uid, focusUid = null ) {
return {
type: 'TOGGLE_SELECTION',
uid,
focusUid,
};
}

export function startNavigation() {
return {
type: 'START_NAVIGATION',
};
}

export function clearSelectedBlock() {
return {
type: 'CLEAR_SELECTED_BLOCK',
Expand Down
9 changes: 6 additions & 3 deletions editor/components/block-mover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { getBlockType } from '@wordpress/blocks';
*/
import './style.scss';
import { getBlockMoverLabel } from './mover-label';
import { isFirstBlock, isLastBlock, getBlockIndex, getBlock } from '../../selectors';
import { isFirstBlock, isLastBlock, getBlockIndex, getBlock, isNavigating } from '../../selectors';
import { selectBlock } from '../../actions';

export function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast, uids, blockType, firstIndex } ) {
Expand All @@ -27,6 +27,7 @@ export function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast, uids, block
return (
<div className="editor-block-mover">
<IconButton
onKeyDown={ ( evt ) => evt.stopPropagation() }
className="editor-block-mover__control"
onClick={ isFirst ? null : onMoveUp }
icon="arrow-up-alt2"
Expand All @@ -42,6 +43,7 @@ export function BlockMover( { onMoveUp, onMoveDown, isFirst, isLast, uids, block
aria-disabled={ isFirst }
/>
<IconButton
onKeyDown={ ( evt ) => evt.stopPropagation() }
className="editor-block-mover__control"
onClick={ isLast ? null : onMoveDown }
icon="arrow-down-alt2"
Expand Down Expand Up @@ -69,11 +71,12 @@ export default connect(
isLast: isLastBlock( state, last( ownProps.uids ) ),
firstIndex: getBlockIndex( state, first( ownProps.uids ) ),
blockType: block ? getBlockType( block.name ) : null,
isNavigating: isNavigating( state ),
} );
},
( dispatch, ownProps ) => ( {
onMoveDown() {
if ( ownProps.uids.length === 1 ) {
if ( ownProps.uids.length === 1 && ! isNavigating ) {
dispatch( selectBlock( first( ownProps.uids ) ) );
}

Expand All @@ -83,7 +86,7 @@ export default connect(
} );
},
onMoveUp() {
if ( ownProps.uids.length === 1 ) {
if ( ownProps.uids.length === 1 && ! isNavigating ) {
dispatch( selectBlock( first( ownProps.uids ) ) );
}

Expand Down
5 changes: 4 additions & 1 deletion editor/components/block-settings-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function BlockSettingsMenu( { uids, onSelect, focus } ) {

return (
<IconButton
onKeyDown={ ( evt ) => evt.stopPropagation() }
className={ toggleClassname }
onClick={ () => {
if ( uids.length === 1 ) {
Expand All @@ -51,7 +52,9 @@ function BlockSettingsMenu( { uids, onSelect, focus } ) {
} }
renderContent={ ( { onClose } ) => (
// Should this just use a DropdownMenu instead of a DropDown ?
<NavigableMenu className="editor-block-settings-menu__content">
<NavigableMenu className="editor-block-settings-menu__content"
onKeyDown={ ( evt ) => evt.stopPropagation() }
>
<BlockInspectorButton onClick={ onClose } />
{ count === 1 && <BlockModeToggle uid={ uids[ 0 ] } onToggle={ onClose } /> }
{ count === 1 && <UnknownConverter uid={ uids[ 0 ] } /> }
Expand Down
3 changes: 2 additions & 1 deletion editor/components/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import { getBlock } from '../../selectors';
*/
const { DOWN } = keycodes;

function BlockSwitcher( { blocks, onTransform } ) {
function BlockSwitcher( { blocks, onTransform, uids } ) {
if ( ! blocks || ! blocks[ 0 ] ) {
return null;
}

const isMultiBlock = blocks.length > 1;
const sourceBlockName = blocks[ 0 ].name;

Expand Down
101 changes: 92 additions & 9 deletions editor/components/writing-flow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { connect } from 'react-redux';
import 'element-closest';
import { find, reverse } from 'lodash';
import { find, reverse, includes } from 'lodash';
/**
* WordPress dependencies
*/
Expand All @@ -25,14 +25,16 @@ import {
getMultiSelectedBlocksStartUid,
getMultiSelectedBlocksEndUid,
getMultiSelectedBlocks,
getMultiSelectedBlockUids,
getSelectedBlock,
isNavigating,
} from '../../selectors';
import { multiSelect } from '../../actions';
import { multiSelect, focusBlock, selectBlock, combineRange, setSelection, toggleSelection } from '../../actions';

/**
* Module Constants
*/
const { UP, DOWN, LEFT, RIGHT } = keycodes;
const { UP, DOWN, LEFT, RIGHT, SPACE, ENTER } = keycodes;

class WritingFlow extends Component {
constructor() {
Expand Down Expand Up @@ -98,6 +100,14 @@ class WritingFlow extends Component {
} );
}

focusBlock( blocks, focusedUid, delta ) {
console.log( 'focusedUid', focusedUid );
const lastIndex = blocks.indexOf( focusedUid );
const nextIndex = Math.max( 0, Math.min( blocks.length - 1, lastIndex + delta ) );
console.log(' blocks', blocks[nextIndex ]);
this.props.focusBlock( blocks[ nextIndex ], { } );
}

expandSelection( blocks, currentStartUid, currentEndUid, delta ) {
const lastIndex = blocks.indexOf( currentEndUid );
const nextIndex = Math.max( 0, Math.min( blocks.length - 1, lastIndex + delta ) );
Expand All @@ -112,7 +122,7 @@ class WritingFlow extends Component {
}

onKeyDown( event ) {
const { selectedBlock, selectionStart, selectionEnd, blocks, hasMultiSelection } = this.props;
const { selectedBlock, selectionStart, selectionEnd, blocks, hasMultiSelection, focusedBlock } = this.props;

const { keyCode, target } = event;
const isUp = keyCode === UP;
Expand All @@ -127,20 +137,46 @@ class WritingFlow extends Component {

const isNavEdge = isVertical ? isVerticalEdge : isHorizontalEdge;

const focusedUid = focusedBlock;

console.log( 'focusedUid', focusedUid, focusedBlock, this.props.stateDump );

const wayward = focusedUid !== selectionEnd;

if ( ! isVertical ) {
this.verticalRect = null;
} else if ( ! this.verticalRect ) {
this.verticalRect = computeCaretRect( target );
}

if ( isNav && isShift && hasMultiSelection ) {
// NOTE: This is going to stop up moving away from blocks (e.g. post title) in navigation mode.

// Also, space and enter on the other buttons are now being intercepted.

if ( focusedUid && isNav && isShift && this.props.inNavigationMode ) {
// Shift key is down and existing block selection
event.preventDefault();
this.expandSelection( blocks, selectionStart, selectionEnd, isReverse ? -1 : +1 );
} else if ( isNav && isShift && this.isEditableEdge( isReverse, target ) && isNavEdge( target, isReverse, true ) ) {

// If the focus has shifted away from the ranged selection, spawn another one.
if ( wayward ) {
const lastIndex = blocks.indexOf( focusedUid );
const nextIndex = Math.max( 0, Math.min( blocks.length - 1, lastIndex + ( isReverse ? -1 : +1 ) ) );
if ( event.ctrlKey || event.metaKey ) {
this.props.combineRange( focusedUid, blocks[ nextIndex ] || focusedUid );
} else {
this.props.setSelection( focusedUid, blocks[ nextIndex ] || focusedUid, [ ], null );
}
} else {
this.expandSelection( blocks, selectionStart, selectionEnd, isReverse ? -1 : +1 );
}
} else if ( focusedUid, isNav && isShift && this.isEditableEdge( isReverse, target ) && isNavEdge( target, isReverse, true ) ) {
// Shift key is down, but no existing block selection
event.preventDefault();
this.expandSelection( blocks, selectedBlock.uid, selectedBlock.uid, isReverse ? -1 : +1 );
this.expandSelection( blocks, focusedUid, focusedUid, isReverse ? -1 : +1 );
} else if ( focusedUid && isNav && ! isShift && this.props.inNavigationMode ) {
console.log('FOCUS ON CURRENTLY', focusedUid );
this.focusBlock( blocks, focusedUid, isReverse ? -1 : 1 );
event.preventDefault();
} else if ( isVertical && isVerticalEdge( target, isReverse, isShift ) ) {
const closestTabbable = this.getClosestTabbable( target, isReverse );
placeCaretAtVerticalEdge( closestTabbable, isReverse, this.verticalRect );
Expand All @@ -149,6 +185,26 @@ class WritingFlow extends Component {
const closestTabbable = this.getClosestTabbable( target, isReverse );
placeCaretAtHorizontalEdge( closestTabbable, isReverse );
event.preventDefault();
} else if ( focusedUid && this.props.inNavigationMode && keyCode === SPACE ) {
if ( event.metaKey || event.ctrlKey ) {
this.props.toggleSelection( focusedUid, focusedUid );
} else if ( includes( this.props.multiSelectedUids, focusedUid ) ) {
this.props.setSelection( focusedUid, null, [ ], focusedUid )

// Can't just always prevent default, because need to fire space on buttons. Or is that
// handled now with stopPropagation in the menus?
event.preventDefault();
} else {
// HERE LIES BAD CODE.
this.props.setSelection( focusedUid, focusedUid, [ ], focusedUid );
}

// event.preventDefault();
event.stopPropagation();
} else if ( focusedUid && this.props.inNavigationMode && keyCode === ENTER ) {
this.props.selectBlock( focusedUid );
// event.preventDefault();
event.stopPropagation();
}
}

Expand Down Expand Up @@ -176,12 +232,39 @@ export default connect(
blocks: getBlockUids( state ),
selectionStart: getMultiSelectedBlocksStartUid( state ),
selectionEnd: getMultiSelectedBlocksEndUid( state ),
hasMultiSelection: getMultiSelectedBlocks( state ).length > 1,

// Temporary hack
focusedBlock: state.blockSelection.focus && state.blockSelection.focus.uid,

multiSelectedUids: getMultiSelectedBlockUids( state ),

hasMultiSelection: getMultiSelectedBlocks( state ).length > 0,
inNavigationMode: isNavigating( state ),
selectedBlock: getSelectedBlock( state ),
stateDump: state,
} ),
( dispatch ) => ( {
onMultiSelect( start, end ) {
dispatch( multiSelect( start, end ) );
},
selectBlock( uid ) {
dispatch( selectBlock( uid ) );
},

focusBlock( uid, config ) {
dispatch( focusBlock( uid, config ) );
},

toggleSelection( uid, focusUid ) {
dispatch( toggleSelection( uid, focusUid ) );
},

setSelection( start, end, selected, focusUid ) {
dispatch( setSelection( start, end, selected, focusUid ) );
},

combineRange( start, end ) {
dispatch( combineRange( start, end ) );
},
} )
)( WritingFlow );
Loading