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

Zoom Out: Only show the inserters when a block is selected or hovered #63668

Merged
merged 14 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,18 @@ _Returns_

- `number`: Number of blocks in the post, or number of blocks with name equal to blockName.

### getHoveredBlockClientId

Returns the currently hovered block.

_Parameters_

- _state_ `Object`: Global application state.

_Returns_

- `Object`: Client Id of the hovered block.

### getInserterItems

Determines the items that appear in the inserter. Includes both static items (e.g. a regular block type) and dynamic items (e.g. a reusable block).
Expand Down Expand Up @@ -1257,6 +1269,18 @@ _Parameters_

Action that hides the insertion point.

### hoverBlock

Returns an action object used in signalling that the block with the specified client ID has been hovered.

_Parameters_

- _clientId_ `string`: Block client ID.

_Returns_

- `Object`: Action object.

### insertAfterBlock

Action that inserts a default block after a given block.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
useFocusHandler( clientId ),
useEventHandlers( { clientId, isSelected } ),
useNavModeExit( clientId ),
useIsHovered(),
useIsHovered( { clientId } ),
useIntersectionObserver(),
useMovingAnimation( { triggerAnimationOnChange: index, clientId } ),
useDisabled( { isDisabled: ! hasOverlay } ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,37 @@
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';
import { useDispatch } from '@wordpress/data';

function listener( event ) {
if ( event.defaultPrevented ) {
return;
}

const action = event.type === 'mouseover' ? 'add' : 'remove';

event.preventDefault();
event.currentTarget.classList[ action ]( 'is-hovered' );
}
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../../store';

/*
* Adds `is-hovered` class when the block is hovered and in navigation or
* outline mode.
*/
export function useIsHovered() {
export function useIsHovered( { clientId } ) {
const { hoverBlock } = useDispatch( blockEditorStore );

function listener( event ) {
if ( event.defaultPrevented ) {
return;
}

const action = event.type === 'mouseover' ? 'add' : 'remove';

event.preventDefault();
event.currentTarget.classList[ action ]( 'is-hovered' );

if ( action === 'add' ) {
hoverBlock( clientId );
} else {
hoverBlock( null );
}
}

return useRefEffect( ( node ) => {
node.addEventListener( 'mouseout', listener );
node.addEventListener( 'mouseover', listener );
Expand All @@ -29,6 +43,7 @@ export function useIsHovered() {

// Remove class in case it lingers.
node.classList.remove( 'is-hovered' );
hoverBlock( null );
};
}, [] );
}
8 changes: 8 additions & 0 deletions packages/block-editor/src/components/block-tools/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,11 @@
border: none;
}
}

.block-editor-block-tools__zoom-out-mode-inserter-button {
visibility: hidden;

&.is-visible {
visibility: visible;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { plus } from '@wordpress/icons';
import { _x } from '@wordpress/i18n';

function ZoomOutModeInserterButton( { isVisible, onClick } ) {
const [
zoomOutModeInserterButtonHovered,
setZoomOutModeInserterButtonHovered,
] = useState( false );

return (
<Button
variant="primary"
icon={ plus }
size="compact"
className={ clsx(
'block-editor-button-pattern-inserter__button',
'block-editor-block-tools__zoom-out-mode-inserter-button',
{
'is-visible': isVisible || zoomOutModeInserterButtonHovered,
}
) }
onClick={ onClick }
onMouseOver={ () => {
setZoomOutModeInserterButtonHovered( true );
} }
onMouseOut={ () => {
setZoomOutModeInserterButtonHovered( false );
} }
label={ _x(
'Add pattern',
'Generic label for pattern inserter button'
) }
/>
);
}

export default ZoomOutModeInserterButton;
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,33 @@
*/
import { useSelect } from '@wordpress/data';
import { useEffect, useRef, useState } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { plus } from '@wordpress/icons';
import { _x } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import BlockPopoverInbetween from '../block-popover/inbetween';
import ZoomOutModeInserterButton from './zoom-out-mode-inserter-button';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';

function ZoomOutModeInserters() {
const [ isReady, setIsReady ] = useState( false );
const {
hasSelection,
blockOrder,
sectionRootClientId,
insertionPoint,
setInserterIsOpened,
hasSelection,
sectionRootClientId,
selectedBlockClientId,
hoveredBlockClientId,
} = useSelect( ( select ) => {
const { getSettings, getBlockOrder, getSelectionStart } =
select( blockEditorStore );
const {
getSettings,
getBlockOrder,
getSelectionStart,
getSelectedBlockClientId,
getHoveredBlockClientId,
} = select( blockEditorStore );
const { sectionRootClientId: root } = unlock( getSettings() );
// To do: move ZoomOutModeInserters to core/editor.
// Or we perhaps we should move the insertion point state to the
Expand All @@ -40,6 +45,8 @@ function ZoomOutModeInserters() {
sectionRootClientId: root,
setInserterIsOpened:
getSettings().__experimentalSetIsInserterOpened,
selectedBlockClientId: getSelectedBlockClientId(),
hoveredBlockClientId: getHoveredBlockClientId(),
};
}, [] );

Expand All @@ -64,18 +71,39 @@ function ZoomOutModeInserters() {
};
}, [] );

if ( ! isReady || ! hasSelection ) {
if ( ! isReady ) {
return null;
}

return [ undefined, ...blockOrder ].map( ( clientId, index ) => {
const shouldRenderInserter = insertionPoint.insertionIndex !== index;

const shouldRenderInsertionPoint =
insertionPoint.insertionIndex === index;

if ( ! shouldRenderInserter && ! shouldRenderInsertionPoint ) {
return null;
}

const previousClientId = clientId;
const nextClientId = blockOrder[ index ];

const isSelected =
hasSelection &&
( selectedBlockClientId === previousClientId ||
selectedBlockClientId === nextClientId );

const isHovered =
hoveredBlockClientId === previousClientId ||
hoveredBlockClientId === nextClientId;

return (
<BlockPopoverInbetween
key={ index }
previousClientId={ clientId }
nextClientId={ blockOrder[ index ] }
previousClientId={ previousClientId }
nextClientId={ nextClientId }
>
{ insertionPoint.insertionIndex === index && (
{ shouldRenderInsertionPoint && (
<div
style={ {
borderRadius: '0',
Expand All @@ -87,12 +115,9 @@ function ZoomOutModeInserters() {
className="block-editor-block-list__insertion-point-indicator"
/>
) }
{ insertionPoint.insertionIndex !== index && (
<Button
variant="primary"
icon={ plus }
size="compact"
className="block-editor-button-pattern-inserter__button"
{ shouldRenderInserter && (
<ZoomOutModeInserterButton
isVisible={ isSelected || isHovered }
onClick={ () => {
setInserterIsOpened( {
rootClientId: sectionRootClientId,
Expand All @@ -101,10 +126,6 @@ function ZoomOutModeInserters() {
category: 'all',
} );
} }
label={ _x(
'Add pattern',
'Generic label for pattern inserter button'
) }
/>
) }
</BlockPopoverInbetween>
Expand Down
15 changes: 15 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ export function selectBlock( clientId, initialPosition = 0 ) {
};
}

/**
* Returns an action object used in signalling that the block with the
* specified client ID has been hovered.
*
* @param {string} clientId Block client ID.
*
* @return {Object} Action object.
*/
export function hoverBlock( clientId ) {
return {
type: 'HOVER_BLOCK',
clientId,
};
}

/**
* Yields action objects used in signalling that the block preceding the given
* clientId (or optionally, its first parent from bottom to top)
Expand Down
18 changes: 18 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,23 @@ export function lastFocus( state = false, action ) {
return state;
}

/**
* Reducer setting currently hovered block.
*
* @param {boolean} state Current state.
* @param {Object} action Dispatched action.
*
* @return {boolean} Updated state.
*/
export function hoveredBlockClientId( state = false, action ) {
switch ( action.type ) {
case 'HOVER_BLOCK':
return action.clientId;
}

return state;
}

const combinedReducers = combineReducers( {
blocks,
isDragging,
Expand Down Expand Up @@ -2100,6 +2117,7 @@ const combinedReducers = combineReducers( {
blockRemovalRules,
openedBlockSettingsMenu,
registeredInserterMediaCategories,
hoveredBlockClientId,
} );

function withAutomaticChangeReset( reducer ) {
Expand Down
10 changes: 10 additions & 0 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2809,6 +2809,16 @@ export function isBlockVisible( state, clientId ) {
return state.blockVisibility?.[ clientId ] ?? true;
}

/**
* Returns the currently hovered block.
*
* @param {Object} state Global application state.
* @return {Object} Client Id of the hovered block.
*/
export function getHoveredBlockClientId( state ) {
return state.hoveredBlockClientId;
}

/**
* Returns the list of all hidden blocks.
*
Expand Down
Loading