Skip to content

Commit

Permalink
Smooth Native Multi Block Selection (#16835)
Browse files Browse the repository at this point in the history
* Squash:
    7a9ce87 (HEAD -> try/native-multi-select, origin/try/native-multi-select) Fix selection in Firefox
    eb668d3 Fix bad rebase
    71bf361 Fix block deletion e2e test
    386810a Add mouse drag test
    41cb592 Rewrite tests
    c22a36b Polish
    9832732 Remove mover min-height and multi select top border
    cac2c20 Fix cross selection delay in Safari
    116e89f Hide hover effect on mouseleave
    a3f1547 Correct toolbar position
    24f817d contenteditable false when there is multi selection
    4eade53 Fix selection setting
    9182fd3 Remove elements in the way of selection
    98dae74 Reselect block correctly
    f1b08ac Smooth selection
    3fb633a Native Multi Block Selection

* Add comment

* E2e: wait for selection to update

* Extract logic from componentDidUpdate
  • Loading branch information
ellatrix authored Nov 29, 2019
1 parent d385ec1 commit bf6976b
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 266 deletions.
70 changes: 51 additions & 19 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ const preventDrag = ( event ) => {
};

function BlockListBlock( {
blockRef,
mode,
isFocusMode,
hasFixedToolbar,
Expand Down Expand Up @@ -102,16 +101,14 @@ function BlockListBlock( {
enableAnimation,
isNavigationMode,
setNavigationMode,
isMultiSelecting,
} ) {
// Random state used to rerender the component if needed, ideally we don't need this
const [ , updateRerenderState ] = useState( {} );
const rerender = () => updateRerenderState( {} );

// Reference of the wrapper
const wrapper = useRef( null );
useEffect( () => {
blockRef( wrapper.current, clientId );
}, [] );

// Reference to the block edit node
const blockNodeRef = useRef();
Expand Down Expand Up @@ -207,6 +204,19 @@ function BlockListBlock( {
* @param {boolean} ignoreInnerBlocks Should not focus inner blocks.
*/
const focusTabbable = ( ignoreInnerBlocks ) => {
const selection = window.getSelection();

if ( selection.rangeCount && ! selection.isCollapsed ) {
const { startContainer, endContainer } = selection.getRangeAt( 0 );

if (
! blockNodeRef.current.contains( startContainer ) ||
! blockNodeRef.current.contains( endContainer )
) {
selection.removeAllRanges();
}
}

// Focus is captured by the wrapper node, so while focus transition
// should only consider tabbables within editable display, since it
// may be the wrapper itself or a side control which triggered the
Expand Down Expand Up @@ -333,12 +343,14 @@ function BlockListBlock( {
}
};

const isPointerDown = useRef( false );

/**
* Begins tracking cursor multi-selection when clicking down within block.
*
* @param {MouseEvent} event A mousedown event.
*/
const onPointerDown = ( event ) => {
const onMouseDown = ( event ) => {
// Not the main button.
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
if ( event.button !== 0 ) {
Expand All @@ -362,7 +374,7 @@ function BlockListBlock( {
// Avoid triggering multi-selection if we click toolbars/inspectors
// and all elements that are outside the Block Edit DOM tree.
} else if ( blockNodeRef.current.contains( event.target ) ) {
onSelectionStart( clientId );
isPointerDown.current = true;

// Allow user to escape out of a multi-selection to a singular
// selection of a block via click. This is handled here since
Expand All @@ -375,6 +387,20 @@ function BlockListBlock( {
}
};

const onMouseUp = () => {
isPointerDown.current = false;
};

const onMouseLeave = () => {
if ( isPointerDown.current ) {
onSelectionStart( clientId );
}

hideHoverEffects();

isPointerDown.current = false;
};

const selectOnOpen = ( open ) => {
if ( open && ! isSelected ) {
onSelect();
Expand Down Expand Up @@ -412,9 +438,10 @@ function BlockListBlock( {
! showEmptyBlockSideInserter &&
! isPartOfMultiSelection &&
! isTypingWithinBlock;
const shouldShowBreadcrumb =
const shouldShowBreadcrumb = ! isMultiSelecting && (
( isSelected && isNavigationMode ) ||
( ! isNavigationMode && ! isFocusMode && isHovered && ! isEmptyDefaultBlock );
( ! isNavigationMode && ! isFocusMode && isHovered && ! isEmptyDefaultBlock )
);
const shouldShowContextualToolbar =
! isNavigationMode &&
! hasFixedToolbar &&
Expand All @@ -427,9 +454,12 @@ function BlockListBlock( {

// Insertion point can only be made visible if the block is at the
// the extent of a multi-selection, or not in a multi-selection.
const shouldShowInsertionPoint =
const shouldShowInsertionPoint = ! isMultiSelecting && (
( isPartOfMultiSelection && isFirstMultiSelected ) ||
! isPartOfMultiSelection;
! isPartOfMultiSelection
);

const shouldRenderDropzone = shouldShowInsertionPoint;

// The wp-block className is important for editor styles.
// Generate the wrapper class names handling the different states of the block.
Expand Down Expand Up @@ -529,22 +559,22 @@ function BlockListBlock( {
rootClientId={ rootClientId }
/>
) }
<BlockDropZone
{ shouldRenderDropzone && <BlockDropZone
clientId={ clientId }
rootClientId={ rootClientId }
/>
{ isFirstMultiSelected && (
<BlockMultiControls
rootClientId={ rootClientId }
moverDirection={ moverDirection }
/>
) }
/> }
<div
className={ classnames(
'editor-block-list__block-edit block-editor-block-list__block-edit',
{ 'has-mover-inside': moverDirection === 'horizontal' },
) }
>
{ isFirstMultiSelected && (
<BlockMultiControls
rootClientId={ rootClientId }
moverDirection={ moverDirection }
/>
) }
{ shouldRenderMovers && ( moverDirection === 'vertical' ) && blockMover }
{ shouldShowBreadcrumb && (
<BlockBreadcrumb
Expand Down Expand Up @@ -577,7 +607,9 @@ function BlockListBlock( {
<IgnoreNestedEvents
ref={ blockNodeRef }
onDragStart={ preventDrag }
onMouseDown={ onPointerDown }
onMouseDown={ onMouseDown }
onMouseUp={ onMouseUp }
onMouseLeave={ onMouseLeave }
data-block={ clientId }
>
<BlockCrashBoundary onError={ onBlockError }>
Expand Down
Loading

0 comments on commit bf6976b

Please sign in to comment.