Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8749179
List View: Add multi-select behaviour when shift key is selected
andrewserong Jan 28, 2022
4e75817
Ensure shift clicking a block when no blocks are selected selects tha…
andrewserong Jan 31, 2022
cfba501
Add support for dragging multiple selected blocks
andrewserong Jan 31, 2022
51376c5
Remove duplication by moving multi-select behaviour to the ListView c…
andrewserong Feb 1, 2022
d2fd70d
Add inline comments
andrewserong Feb 1, 2022
b6bb2db
Update documentation, add changelog entry
andrewserong Feb 1, 2022
06c731c
Add e2e test for multi-select in the list view
andrewserong Feb 2, 2022
edcff8b
Remove stray line from changelog
andrewserong Feb 2, 2022
1362dba
Ensure that clicked on blocks that aren't a part of a selection can s…
andrewserong Feb 3, 2022
74602ca
Try a naive approach to keyboard handling for shift + up/down to sele…
andrewserong Feb 3, 2022
62f32e4
Move block selection to its own useBlockSelection hook
andrewserong Feb 7, 2022
8fbb905
Refactor start and end id calculation to its own function, add unit t…
andrewserong Feb 7, 2022
999568e
Move utility function to utils file, add doc comment, update usage
andrewserong Feb 8, 2022
d884e0e
Update multiSelect behavior to support keeping focus within the ListV…
andrewserong Feb 11, 2022
27d63bc
Update unit tests, add additional e2e test for keyboard behaviour
andrewserong Feb 11, 2022
c5853bc
Revert change to focus for when shift key is not held
andrewserong Feb 11, 2022
6cb4fb3
Defer calculation of the next/prev clientId to the TreeGrid component…
andrewserong Feb 14, 2022
38b0e5b
Update initialPosition param to be experimental
andrewserong Feb 15, 2022
025087f
Update TreeGrid readme with documentation for the three callback func…
andrewserong Feb 15, 2022
c3b6a1e
Add changelog entry
andrewserong Feb 15, 2022
b321851
Pass in selectedClientIds to dropdown
andrewserong Feb 16, 2022
db31ffd
Prevent shift+click on expand toggle from opening a new window
andrewserong Feb 16, 2022
4f0e0bb
Try announcing deselected blocks
andrewserong Feb 16, 2022
5511896
Rename onChangeRow to onFocusRow, update changelog entry
andrewserong Feb 18, 2022
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
Prev Previous commit
Next Next commit
Move block selection to its own useBlockSelection hook
  • Loading branch information
andrewserong committed Feb 16, 2022
commit 62f32e442a0ef6ef54b10459c08e030089ee4595
79 changes: 4 additions & 75 deletions packages/block-editor/src/components/list-view/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
__experimentalUseFixedWindowList as useFixedWindowList,
} from '@wordpress/compose';
import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
import { AsyncModeProvider, useDispatch, useSelect } from '@wordpress/data';
import { AsyncModeProvider, useSelect } from '@wordpress/data';
import {
useCallback,
useEffect,
Expand All @@ -23,11 +23,11 @@ import { __ } from '@wordpress/i18n';
import ListViewBranch from './branch';
import { ListViewContext } from './context';
import ListViewDropIndicator from './drop-indicator';
import useBlockSelection from './use-block-selection';
import useListViewClientIds from './use-list-view-client-ids';
import useListViewDropZone from './use-list-view-drop-zone';
import { store as blockEditorStore } from '../../store';

const noop = () => {};
const expanded = ( state, action ) => {
switch ( action.type ) {
case 'expand':
Expand Down Expand Up @@ -57,7 +57,6 @@ const expanded = ( state, action ) => {
function ListView(
{
blocks,
onSelect = noop,
Comment thread
andrewserong marked this conversation as resolved.
__experimentalFeatures,
__experimentalPersistentListViewFeatures,
__experimentalHideContainerBlockActions,
Expand All @@ -72,15 +71,6 @@ function ListView(
draggedClientIds,
selectedClientIds,
} = useListViewClientIds( blocks );
const { clearSelectedBlock, multiSelect, selectBlock } = useDispatch(
blockEditorStore
);
const {
getBlockParents,
getBlockSelectionStart,
hasMultiSelection,
hasSelectedBlock,
} = useSelect( blockEditorStore );

const { visibleBlockCount } = useSelect(
( select ) => {
Expand All @@ -97,70 +87,9 @@ function ListView(
},
[ draggedClientIds ]
);
const selectEditorBlock = useCallback(
async ( clientId, event ) => {
if ( ! event.shiftKey || event.type === 'keydown' ) {
// When this function is called from a 'keydown' event, select
// the block as if it were individually clicked. This allows
// the editor canvas to be responsible for the shift + up/down
// multiple block selection behaviour.
await clearSelectedBlock();
selectBlock( clientId, -1 );
} else if ( event.shiftKey ) {
// To handle multiple block selection via the `SHIFT` key, prevent
// the browser default behavior of opening the link in a new window.
event.preventDefault();

// Select a single block if no block is selected yet.
if ( ! hasSelectedBlock() && ! hasMultiSelection() ) {
selectBlock( clientId, -1 );
return;
}

const blockSelectionStart = getBlockSelectionStart();

// By checking `blockSelectionStart` to be set, we handle the
// case where we select a single block. We also have to check
// the selectionEnd (clientId) not to be included in the
// `blockSelectionStart`'s parents because the click event is
// propagated.
const startParents = getBlockParents( blockSelectionStart );

if (
blockSelectionStart &&
blockSelectionStart !== clientId &&
! startParents?.includes( clientId )
) {
const startPath = [ ...startParents, blockSelectionStart ];
const endPath = [
...getBlockParents( clientId ),
clientId,
];
const depth =
Math.min( startPath.length, endPath.length ) - 1;
const start = startPath[ depth ];
const end = endPath[ depth ];
const { updateBlockSelection } = useBlockSelection();

// Handle the case of having selected a parent block and
// then shift+click on a child.
if ( start !== end ) {
multiSelect( start, end );
}
}
}
onSelect( clientId );
},
[
clearSelectedBlock,
getBlockParents,
getBlockSelectionStart,
hasMultiSelection,
hasSelectedBlock,
multiSelect,
selectBlock,
onSelect,
]
);
const [ expandedState, setExpandedState ] = useReducer( expanded, {} );

const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone();
Expand Down Expand Up @@ -255,7 +184,7 @@ function ListView(
<ListViewContext.Provider value={ contextValue }>
<ListViewBranch
blocks={ clientIdsTree }
selectBlock={ selectEditorBlock }
selectBlock={ updateBlockSelection }
showNestedBlocks={ showNestedBlocks }
showBlockMovers={ showBlockMovers }
fixedListWindow={ fixedListWindow }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';

export default function useBlockSelection() {
const { clearSelectedBlock, multiSelect, selectBlock } = useDispatch(
blockEditorStore
);
const {
getBlockParents,
getBlockSelectionStart,
hasMultiSelection,
hasSelectedBlock,
} = useSelect( blockEditorStore );

const updateBlockSelection = useCallback(
async ( clientId, event ) => {
if ( ! event.shiftKey || event.type === 'keydown' ) {
// When this function is called from a 'keydown' event, select
// the block as if it were individually clicked. This allows
// the editor canvas to be responsible for the shift + up/down
// multiple block selection behaviour.
await clearSelectedBlock();
selectBlock( clientId, -1 );
} else if ( event.shiftKey ) {
// To handle multiple block selection via the `SHIFT` key, prevent
// the browser default behavior of opening the link in a new window.
event.preventDefault();

// Select a single block if no block is selected yet.
if ( ! hasSelectedBlock() && ! hasMultiSelection() ) {
selectBlock( clientId, -1 );
return;
}

const blockSelectionStart = getBlockSelectionStart();

// By checking `blockSelectionStart` to be set, we handle the
// case where we select a single block. We also have to check
// the selectionEnd (clientId) not to be included in the
// `blockSelectionStart`'s parents because the click event is
// propagated.
const startParents = getBlockParents( blockSelectionStart );

if (
blockSelectionStart &&
blockSelectionStart !== clientId &&
! startParents?.includes( clientId )
) {
const startPath = [ ...startParents, blockSelectionStart ];
const endPath = [
...getBlockParents( clientId ),
clientId,
];
const depth =
Math.min( startPath.length, endPath.length ) - 1;
const start = startPath[ depth ];
const end = endPath[ depth ];

// Handle the case of having selected a parent block and
// then shift+click on a child.
if ( start !== end ) {
multiSelect( start, end );
}
}
}
},
[
clearSelectedBlock,
getBlockParents,
getBlockSelectionStart,
hasMultiSelection,
hasSelectedBlock,
multiSelect,
selectBlock,
]
);

return {
updateBlockSelection,
};
}