Skip to content

Commit

Permalink
Implement the G2 Mover interactions (#20056)
Browse files Browse the repository at this point in the history
Co-authored-by: Joen Asmussen <[email protected]>
Co-authored-by: Riad Benguella <[email protected]>
  • Loading branch information
3 people committed Feb 24, 2020
1 parent 819989b commit e986412
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import { BlockToolbar } from '../';

function BlockContextualToolbar( { focusOnMount, ...props } ) {
return (
<NavigableToolbar
focusOnMount={ focusOnMount }
className="block-editor-block-contextual-toolbar"
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
{ ...props }
>
<BlockToolbar />
</NavigableToolbar>
<div className="block-editor-block-contextual-toolbar-wrapper">
<NavigableToolbar
focusOnMount={ focusOnMount }
className="block-editor-block-contextual-toolbar"
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
{ ...props }
>
<BlockToolbar />
</NavigableToolbar>
</div>
);
}

Expand Down
7 changes: 5 additions & 2 deletions packages/block-editor/src/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@
/**
* Block Toolbar when contextual.
*/
.block-editor-block-contextual-toolbar-wrapper {
padding-left: $block-toolbar-height; // Provide space for the mover control on full-wide items.
}

.block-editor-block-contextual-toolbar {
// Adapt the height of the toolbar items.
Expand Down Expand Up @@ -460,7 +463,7 @@
// The button here has a special style to appear as a toolbar.
.components-button {
font-size: $default-font-size;
height: $grid-unit-60;
height: $block-toolbar-height;
padding: $grid-unit-15 $grid-unit-20;

// Block UI appearance.
Expand Down Expand Up @@ -540,7 +543,7 @@

// @todo It should position the block transform dialog as the left margin of a block. It currently
// positions instead, the mover control.
margin-left: -$grid-unit-60;
margin-left: - $block-toolbar-height - $border-width;
}

.block-editor-block-contextual-toolbar[data-align="full"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
}

.components-button.block-editor-block-switcher__no-switcher-icon {
width: $grid-unit-60;
width: $block-toolbar-height;

.block-editor-blocks-icon {
margin-right: auto;
Expand Down
57 changes: 46 additions & 11 deletions packages/block-editor/src/components/block-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

import { useRef } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
/**
* Internal dependencies
*/
Expand All @@ -11,6 +12,7 @@ import BlockSwitcher from '../block-switcher';
import BlockControls from '../block-controls';
import BlockFormatControls from '../block-format-controls';
import BlockSettingsMenu from '../block-settings-menu';
import { useShowMoversGestures } from './utils';

export default function BlockToolbar( { hideDragHandle } ) {
const {
Expand Down Expand Up @@ -51,25 +53,58 @@ export default function BlockToolbar( { hideDragHandle } ) {
};
}, [] );

const nodeRef = useRef();

const {
showMovers,
gestures: showMoversGestures,
} = useShowMoversGestures( { ref: nodeRef } );

const shouldShowMovers =
useViewportMatch( 'medium', '<' ) || ( showMovers && hasMovers );

if ( blockClientIds.length === 0 ) {
return null;
}

const shouldShowVisualToolbar = isValid && mode === 'visual';
const isMultiToolbar = blockClientIds.length > 1;

const animatedMoverStyles = {
opacity: shouldShowMovers ? 1 : 0,
transform: shouldShowMovers ? 'translateX(0px)' : 'translateX(100%)',
};

return (
<div className="block-editor-block-toolbar">
{ hasMovers && (
<BlockMover
clientIds={ blockClientIds }
__experimentalOrientation={ moverDirection }
hideDragHandle={ hideDragHandle }
/>
) }
{ ( shouldShowVisualToolbar || isMultiToolbar ) && (
<BlockSwitcher clientIds={ blockClientIds } />
) }
<div
className="block-editor-block-toolbar__mover-switcher-container"
ref={ nodeRef }
>
<div
className="block-editor-block-toolbar__mover-trigger-container"
{ ...showMoversGestures }
>
<div
className="block-editor-block-toolbar__mover-trigger-wrapper"
style={ animatedMoverStyles }
>
<BlockMover
clientIds={ blockClientIds }
__experimentalOrientation={ moverDirection }
hideDragHandle={ hideDragHandle }
/>
</div>
</div>
{ ( shouldShowVisualToolbar || isMultiToolbar ) && (
<div
{ ...showMoversGestures }
className="block-editor-block-toolbar__block-switcher-wrapper"
>
<BlockSwitcher clientIds={ blockClientIds } />
</div>
) }
</div>
{ shouldShowVisualToolbar && ! isMultiToolbar && (
<>
<BlockControls.Slot
Expand Down
36 changes: 36 additions & 0 deletions packages/block-editor/src/components/block-toolbar/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,39 @@
display: inline-flex;
}
}

.block-editor-block-toolbar__mover-switcher-container {
display: flex;
}

.block-editor-block-toolbar__mover-trigger-container {
@include break-medium() {
bottom: -1px;
left: -1px;
position: absolute;
top: -1px;
transform: translateX(-48px);
user-select: none;
z-index: -1; // This makes it slide out from underneath the toolbar.
}
}

.block-editor-block-toolbar__mover-trigger-wrapper {
@include break-medium() {
background-color: $white;
border: 1px solid $black;
border-bottom-left-radius: 2px;
border-top-left-radius: 2px;
border-right: none;
height: 100%;
transition: all 60ms linear;
}

@include reduce-motion("transition");
}

.block-editor-block-toolbar__block-switcher-wrapper {
.block-editor-block-switcher {
display: block;
}
}
135 changes: 135 additions & 0 deletions packages/block-editor/src/components/block-toolbar/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* WordPress dependencies
*/
import { useState, useRef, useEffect, useCallback } from '@wordpress/element';

const { clearTimeout, setTimeout } = window;

/**
* Hook that creates a showMover state, as well as debounced show/hide callbacks
*/
export function useDebouncedShowMovers( {
ref,
isFocused,
debounceTimeout = 500,
} ) {
const [ showMovers, setShowMovers ] = useState( false );
const timeoutRef = useRef();

const getIsHovered = () => {
return ref?.current && ref.current.matches( ':hover' );
};

const shouldHideMovers = () => {
const isHovered = getIsHovered();

return ! isFocused && ! isHovered;
};

const debouncedShowMovers = useCallback(
( event ) => {
if ( event ) {
event.stopPropagation();
}

const timeout = timeoutRef.current;

if ( timeout && clearTimeout ) {
clearTimeout( timeout );
}
if ( ! showMovers ) {
setShowMovers( true );
}
},
[ showMovers ]
);

const debouncedHideMovers = useCallback(
( event ) => {
if ( event ) {
event.stopPropagation();
}

timeoutRef.current = setTimeout( () => {
if ( shouldHideMovers() ) {
setShowMovers( false );
}
}, debounceTimeout );
},
[ isFocused ]
);

return {
showMovers,
debouncedShowMovers,
debouncedHideMovers,
};
}

/**
* Hook that provides a showMovers state and gesture events for DOM elements
* that interact with the showMovers state.
*/
export function useShowMoversGestures( { ref, debounceTimeout = 500 } ) {
const [ isFocused, setIsFocused ] = useState( false );
const {
showMovers,
debouncedShowMovers,
debouncedHideMovers,
} = useDebouncedShowMovers( { ref, debounceTimeout, isFocused } );

const registerRef = useRef( false );

const isFocusedWithin = () => {
return ref && ref.current.contains( document.activeElement );
};

useEffect( () => {
const node = ref.current;

const handleOnFocus = () => {
if ( isFocusedWithin() ) {
setIsFocused( true );
debouncedShowMovers();
}
};

const handleOnBlur = () => {
if ( ! isFocusedWithin() ) {
setIsFocused( false );
debouncedHideMovers();
}
};

/**
* Events are added via DOM events (vs. React synthetic events),
* as the child React components swallow mouse events.
*/
if ( node && ! registerRef.current ) {
node.addEventListener( 'focus', handleOnFocus, true );
node.addEventListener( 'blur', handleOnBlur, true );
registerRef.current = true;
}

return () => {
if ( node ) {
node.removeEventListener( 'focus', handleOnFocus );
node.removeEventListener( 'blur', handleOnBlur );
}
};
}, [
ref,
registerRef,
setIsFocused,
debouncedShowMovers,
debouncedHideMovers,
] );

return {
showMovers,
gestures: {
onMouseMove: debouncedShowMovers,
onMouseLeave: debouncedHideMovers,
},
};
}
4 changes: 2 additions & 2 deletions packages/block-editor/src/components/rich-text/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ figcaption.block-editor-rich-text__editable [data-rich-text-placeholder]::before

.components-toolbar__control,
.components-dropdown-menu__toggle {
min-width: $grid-unit-60;
min-height: $grid-unit-60;
min-width: $block-toolbar-height;
min-height: $block-toolbar-height;
padding-left: $grid-unit-15;
padding-right: $grid-unit-15;
}
Expand Down

0 comments on commit e986412

Please sign in to comment.