From f09a7b0bbab133573a280a78f6908005735428bf Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 11 Apr 2022 18:01:21 +0400 Subject: [PATCH] Try: Reusable block edit locking (#39950) * Introduce canEditBlock selector * Update BlockContentOverlay * Update UI * Fix indeterminate state and e2e tests * Update list view * Update selector * Provide 'isLocked' value via hook --- .../data/data-core-block-editor.md | 13 +++++ .../components/block-content-overlay/index.js | 21 +++++++- .../src/components/block-lock/menu-item.js | 3 +- .../src/components/block-lock/modal.js | 50 ++++++++++++++++--- .../src/components/block-lock/toolbar.js | 4 +- .../components/block-lock/use-block-lock.js | 11 +++- .../list-view/block-select-button.js | 3 +- packages/block-editor/src/store/selectors.js | 20 ++++++++ 8 files changed, 109 insertions(+), 16 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index ba256530cd32a..bdbfc78eb482e 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -19,6 +19,19 @@ _Returns_ - `boolean`: True if the block has controlled inner blocks. +### canEditBlock + +Determines if the given block is allowed to be edited. + +_Parameters_ + +- _state_ `Object`: Editor state. +- _clientId_ `string`: The block client Id. + +_Returns_ + +- `boolean`: Whether the given block is allowed to be edited. + ### canInsertBlocks Determines if the given blocks are allowed to be inserted into the block diff --git a/packages/block-editor/src/components/block-content-overlay/index.js b/packages/block-editor/src/components/block-content-overlay/index.js index 249f6f3bc8e3e..727d2445883ce 100644 --- a/packages/block-editor/src/components/block-content-overlay/index.js +++ b/packages/block-editor/src/components/block-content-overlay/index.js @@ -25,6 +25,7 @@ export default function BlockContentOverlay( { const [ isHovered, setIsHovered ] = useState( false ); const { + canEdit, isParentSelected, hasChildSelected, isDraggingBlocks, @@ -36,8 +37,10 @@ export default function BlockContentOverlay( { hasSelectedInnerBlock, isDraggingBlocks: _isDraggingBlocks, isBlockHighlighted, + canEditBlock, } = select( blockEditorStore ); return { + canEdit: canEditBlock( clientId ), isParentSelected: isBlockSelected( clientId ), hasChildSelected: hasSelectedInnerBlock( clientId, true ), isDraggingBlocks: _isDraggingBlocks(), @@ -59,6 +62,12 @@ export default function BlockContentOverlay( { ); useEffect( () => { + // The overlay is always active when editing is locked. + if ( ! canEdit ) { + setIsOverlayActive( true ); + return; + } + // Reenable when blocks are not in use. if ( ! isParentSelected && ! hasChildSelected && ! isOverlayActive ) { setIsOverlayActive( true ); @@ -75,7 +84,13 @@ export default function BlockContentOverlay( { if ( hasChildSelected && isOverlayActive ) { setIsOverlayActive( false ); } - }, [ isParentSelected, hasChildSelected, isOverlayActive, isHovered ] ); + }, [ + isParentSelected, + hasChildSelected, + isOverlayActive, + isHovered, + canEdit, + ] ); // Disabled because the overlay div doesn't actually have a role or functionality // as far as the a11y is concerned. We're just catching the first click so that @@ -88,7 +103,9 @@ export default function BlockContentOverlay( { onMouseEnter={ () => setIsHovered( true ) } onMouseLeave={ () => setIsHovered( false ) } onMouseUp={ - isOverlayActive ? () => setIsOverlayActive( false ) : undefined + isOverlayActive && canEdit + ? () => setIsOverlayActive( false ) + : undefined } > { wrapperProps?.children } diff --git a/packages/block-editor/src/components/block-lock/menu-item.js b/packages/block-editor/src/components/block-lock/menu-item.js index 7c66038beb6f6..6a7a1517cf409 100644 --- a/packages/block-editor/src/components/block-lock/menu-item.js +++ b/packages/block-editor/src/components/block-lock/menu-item.js @@ -13,8 +13,7 @@ import useBlockLock from './use-block-lock'; import BlockLockModal from './modal'; export default function BlockLockMenuItem( { clientId } ) { - const { canMove, canRemove, canLock } = useBlockLock( clientId, true ); - const isLocked = ! canMove || ! canRemove; + const { canLock, isLocked } = useBlockLock( clientId, true ); const [ isModalOpen, toggleModal ] = useReducer( ( isActive ) => ! isActive, diff --git a/packages/block-editor/src/components/block-lock/modal.js b/packages/block-editor/src/components/block-lock/modal.js index 99ff8f7c55ba9..311254d9c3e75 100644 --- a/packages/block-editor/src/components/block-lock/modal.js +++ b/packages/block-editor/src/components/block-lock/modal.js @@ -13,7 +13,8 @@ import { } from '@wordpress/components'; import { lock as lockIcon, unlock as unlockIcon } from '@wordpress/icons'; import { useInstanceId } from '@wordpress/compose'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { isReusableBlock, getBlockType } from '@wordpress/blocks'; /** * Internal dependencies @@ -24,7 +25,18 @@ import { store as blockEditorStore } from '../../store'; export default function BlockLockModal( { clientId, onClose } ) { const [ lock, setLock ] = useState( { move: false, remove: false } ); - const { canMove, canRemove } = useBlockLock( clientId, true ); + const { canEdit, canMove, canRemove } = useBlockLock( clientId, true ); + const { isReusable } = useSelect( + ( select ) => { + const { getBlockName } = select( blockEditorStore ); + const blockName = getBlockName( clientId ); + + return { + isReusable: isReusableBlock( getBlockType( blockName ) ), + }; + }, + [ clientId ] + ); const { updateBlockAttributes } = useDispatch( blockEditorStore ); const blockInformation = useBlockDisplayInformation( clientId ); const instanceId = useInstanceId( @@ -36,12 +48,12 @@ export default function BlockLockModal( { clientId, onClose } ) { setLock( { move: ! canMove, remove: ! canRemove, + ...( isReusable ? { edit: ! canEdit } : {} ), } ); - }, [ canMove, canRemove ] ); + }, [ canEdit, canMove, canRemove, isReusable ] ); const isAllChecked = Object.values( lock ).every( Boolean ); - const isIndeterminate = - Object.values( lock ).some( Boolean ) && ! isAllChecked; + const isMixed = Object.values( lock ).some( Boolean ) && ! isAllChecked; return ( { __( 'Lock all' ) } } checked={ isAllChecked } - indeterminate={ isIndeterminate } + indeterminate={ isMixed } onChange={ ( newValue ) => setLock( { move: newValue, remove: newValue, + ...( isReusable ? { edit: newValue } : {} ), } ) } />