From 5f3c1b2cd65ee35d6966ae9fe6f43dedc19f6c31 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 15 May 2023 11:02:52 +1000 Subject: [PATCH 01/16] Add ability to prevent editing blocks using useBlockEditingMode() --- .../components/block-editing-mode/index.js | 35 ++++++++++++++++++ .../src/components/block-list/block.js | 14 ++++---- .../src/components/block-list/content.scss | 10 +++--- .../block-list/use-in-between-inserter.js | 11 ++++-- .../src/components/block-toolbar/index.js | 10 +++--- .../block-tools/block-contextual-toolbar.js | 7 ++-- packages/block-editor/src/hooks/align.js | 8 ++++- packages/block-editor/src/hooks/duotone.js | 11 ++++-- packages/block-editor/src/private-apis.js | 2 ++ .../block-editor/src/store/private-actions.js | 15 ++++++++ .../src/store/private-selectors.js | 16 +++++++++ packages/block-editor/src/store/reducer.js | 16 +++++++++ packages/block-library/src/post-title/edit.js | 36 +++++++++++-------- 13 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 packages/block-editor/src/components/block-editing-mode/index.js diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js new file mode 100644 index 00000000000000..ed572f56f73b32 --- /dev/null +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +export function useBlockEditingMode( clientId, mode ) { + const blockEditingMode = useSelect( + ( select ) => + unlock( select( blockEditorStore ) ).getBlockEditingMode( + clientId + ), + [ clientId ] + ); + const { setBlockEditingMode, unsetBlockEditingMode } = unlock( + useDispatch( blockEditorStore ) + ); + useEffect( () => { + if ( mode ) { + setBlockEditingMode( clientId, mode ); + } + return () => { + if ( mode ) { + unsetBlockEditingMode( clientId ); + } + }; + }, [ clientId, mode, setBlockEditingMode, unsetBlockEditingMode ] ); + return blockEditingMode; +} diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 3980dd7b2aead3..964aacae99ac26 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -43,6 +43,8 @@ import BlockHtml from './block-html'; import { useBlockProps } from './use-block-props'; import { store as blockEditorStore } from '../../store'; import { useLayout } from './layout'; +import { useBlockEditingMode } from '../block-editing-mode'; + export const BlockListBlockContext = createContext(); /** @@ -101,14 +103,12 @@ function BlockListBlock( { themeSupportsLayout, hasContentLockedParent, isContentBlock, - isContentLocking, isTemporarilyEditingAsBlocks, } = useSelect( ( select ) => { const { getSettings, __unstableGetContentLockingParent, - getTemplateLock, __unstableGetTemporarilyEditingAsBlocks, } = select( blockEditorStore ); const _hasContentLockedParent = @@ -120,9 +120,6 @@ function BlockListBlock( { name ), hasContentLockedParent: _hasContentLockedParent, - isContentLocking: - getTemplateLock( clientId ) === 'contentOnly' && - ! _hasContentLockedParent, isTemporarilyEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks() === clientId, }; @@ -134,6 +131,8 @@ function BlockListBlock( { const parentLayout = useLayout() || {}; + const blockEditingMode = useBlockEditingMode( clientId ); + // We wrap the BlockEdit component in a div that hides it when editing in // HTML mode. This allows us to render all of the ancillary pieces // (InspectorControls, etc.) which are inside `BlockEdit` but not @@ -234,10 +233,11 @@ function BlockListBlock( { clientId, className: classnames( { - 'is-content-locked': isContentLocking, + 'is-editing-disabled': + blockEditingMode === 'disabled' || + ( hasContentLockedParent && ! isContentBlock ), 'is-content-locked-temporarily-editing-as-blocks': isTemporarilyEditingAsBlocks, - 'is-content-block': hasContentLockedParent && isContentBlock, }, dataAlign && themeSupportsLayout && `align${ dataAlign }`, className diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index 80056f35cf0d6e..dab07ced873e9f 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -161,13 +161,13 @@ padding: 0; } -.is-content-locked { - .block-editor-block-list__block { +.block-editor-block-list__layout, +.block-editor-block-list__block { + pointer-events: initial; + + &.is-editing-disabled { pointer-events: none; } - .is-content-block { - pointer-events: initial; - } } .block-editor-block-list__layout .block-editor-block-list__block { diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index 6e9365ad102242..854d1dc7ca86a1 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -11,6 +11,7 @@ import { isRTL } from '@wordpress/i18n'; */ import { store as blockEditorStore } from '../../store'; import { InsertionPointOpenRef } from '../block-tools/insertion-point'; +import { unlock } from '../../lock-unlock'; export function useInBetweenInserter() { const openRef = useContext( InsertionPointOpenRef ); @@ -29,7 +30,8 @@ export function useInBetweenInserter() { getSelectedBlockClientIds, getTemplateLock, __unstableIsWithinBlockOverlay, - } = useSelect( blockEditorStore ); + getBlockEditingMode, + } = unlock( useSelect( blockEditorStore ) ); const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); @@ -74,8 +76,11 @@ export function useInBetweenInserter() { rootClientId = blockElement.getAttribute( 'data-block' ); } - // Don't set the insertion point if the template is locked. - if ( getTemplateLock( rootClientId ) ) { + // Don't set the insertion point if the root is locked. + if ( + getTemplateLock( rootClientId ) || + getBlockEditingMode( rootClientId ) === 'disabled' + ) { return; } diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index d9799849b13990..af5fe6f509b582 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -32,6 +32,7 @@ import BlockEditVisuallyButton from '../block-edit-visually-button'; import { useShowMoversGestures } from './utils'; import { store as blockEditorStore } from '../../store'; import __unstableBlockNameContext from './block-name-context'; +import { unlock } from '../../lock-unlock'; const BlockToolbar = ( { hideDragHandle } ) => { const { @@ -52,7 +53,8 @@ const BlockToolbar = ( { hideDragHandle } ) => { getBlockRootClientId, getSettings, __unstableGetContentLockingParent, - } = select( blockEditorStore ); + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); const selectedBlockClientIds = getSelectedBlockClientIds(); const selectedBlockClientId = selectedBlockClientIds[ 0 ]; const blockRootClientId = getBlockRootClientId( selectedBlockClientId ); @@ -73,9 +75,9 @@ const BlockToolbar = ( { hideDragHandle } ) => { isVisual: selectedBlockClientIds.every( ( id ) => getBlockMode( id ) === 'visual' ), - isContentLocked: !! __unstableGetContentLockingParent( - selectedBlockClientId - ), + isContentLocked: + !! __unstableGetContentLockingParent( selectedBlockClientId ) || + getBlockEditingMode( selectedBlockClientId ) !== 'default', }; }, [] ); diff --git a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js index 385b120c352d22..d709869de52b2e 100644 --- a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js @@ -25,6 +25,7 @@ import NavigableToolbar from '../navigable-toolbar'; import BlockToolbar from '../block-toolbar'; import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; +import { unlock } from '../../lock-unlock'; function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { // When the toolbar is fixed it can be collapsed @@ -39,7 +40,8 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { getBlockParents, getSelectedBlockClientIds, __unstableGetContentLockingParent, - } = select( blockEditorStore ); + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); const { getBlockType } = select( blocksStore ); const selectedBlockClientIds = getSelectedBlockClientIds(); const _selectedBlockClientId = selectedBlockClientIds[ 0 ]; @@ -64,7 +66,8 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { selectedBlockClientIds.length <= 1 && ! __unstableGetContentLockingParent( _selectedBlockClientId - ), + ) && + getBlockEditingMode( _selectedBlockClientId ) === 'default', }; }, [] ); diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 23e6809685377a..1a8896513ab2d0 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -21,6 +21,7 @@ import { useSelect } from '@wordpress/data'; import { BlockControls, BlockAlignmentControl } from '../components'; import useAvailableAlignments from '../components/block-alignment-control/use-available-alignments'; import { store as blockEditorStore } from '../store'; +import { useBlockEditingMode } from '../components/block-editing-mode'; /** * An array which includes all possible valid alignments, @@ -141,7 +142,12 @@ export const withToolbarControls = createHigherOrderComponent( }, [ props.clientId ] ); - if ( ! validAlignments.length || isContentLocked ) { + const blockEditingMode = useBlockEditingMode( props.clientId ); + if ( + ! validAlignments.length || + isContentLocked || + blockEditingMode !== 'default' + ) { return blockEdit; } diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index c28310cc7b86b0..f018444de0c67f 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -38,6 +38,7 @@ import { scopeSelector } from '../components/global-styles/utils'; import { useBlockSettings } from './utils'; import { store as blockEditorStore } from '../store'; import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel'; +import { useBlockEditingMode } from '../components/block-editing-mode'; const EMPTY_ARRAY = []; @@ -235,15 +236,19 @@ const withDuotoneControls = createHigherOrderComponent( [ props.clientId ] ); + const blockEditingMode = useBlockEditingMode( props.clientId ); + // CAUTION: code added before this line will be executed // for all blocks, not just those that support duotone. Code added // above this line should be carefully evaluated for its impact on // performance. return ( <> - { hasDuotoneSupport && ! isContentLocked && ( - - ) } + { hasDuotoneSupport && + ! isContentLocked && + blockEditingMode === 'default' && ( + + ) } ); diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index a7357311eedbe8..f96130b670e240 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -11,6 +11,7 @@ import { PrivateListView } from './components/list-view'; import BlockInfo from './components/block-info-slot-fill'; import { useShouldContextualToolbarShow } from './utils/use-should-contextual-toolbar-show'; import { cleanEmptyObject } from './hooks/utils'; +import { useBlockEditingMode } from './components/block-editing-mode'; /** * Private @wordpress/block-editor APIs. @@ -26,4 +27,5 @@ lock( privateApis, { BlockInfo, useShouldContextualToolbarShow, cleanEmptyObject, + useBlockEditingMode, } ); diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 2d33ea82cb9b63..496562687118b1 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -66,3 +66,18 @@ export function showBlockInterface() { type: 'SHOW_BLOCK_INTERFACE', }; } + +export function setBlockEditingMode( clientId, mode ) { + return { + type: 'SET_BLOCK_EDITING_MODE', + clientId, + mode, + }; +} + +export function unsetBlockEditingMode( clientId ) { + return { + type: 'UNSET_BLOCK_EDITING_MODE', + clientId, + }; +} diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 60712e6b8eb6e0..8f9af5df0b692e 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -18,3 +18,19 @@ export function isBlockInterfaceHidden( state ) { export function getLastInsertedBlocksClientIds( state ) { return state?.lastBlockInserted?.clientIds; } + +export function getBlockEditingMode( state, clientId ) { + if ( ! clientId ) { + return state.settings.rootBlockEditingMode; + } + while ( + ! state.blockEditingModes.has( clientId ) && + state.blocks.parents.has( clientId ) + ) { + clientId = state.blocks.parents.get( clientId ); + } + return ( + state.blockEditingModes.get( clientId ) ?? + state.settings.rootBlockEditingMode + ); +} diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 4239cb1aba8489..f0c44244371c04 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1834,6 +1834,21 @@ export function temporarilyEditingAsBlocks( state = '', action ) { return state; } +export function blockEditingModes( state = new Map(), action ) { + switch ( action.type ) { + case 'SET_BLOCK_EDITING_MODE': + return new Map( state ).set( action.clientId, action.mode ); + case 'UNSET_BLOCK_EDITING_MODE': { + const newState = new Map( state ); + newState.delete( action.clientId ); + return newState; + } + case 'RESET_BLOCKS': + return new Map(); + } + return state; +} + const combinedReducers = combineReducers( { blocks, isTyping, @@ -1856,6 +1871,7 @@ const combinedReducers = combineReducers( { lastBlockInserted, temporarilyEditingAsBlocks, blockVisibility, + blockEditingModes, } ); function withAutomaticChangeReset( reducer ) { diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js index 8cd71881e06dec..a30b01dee74569 100644 --- a/packages/block-library/src/post-title/edit.js +++ b/packages/block-library/src/post-title/edit.js @@ -12,6 +12,7 @@ import { InspectorControls, useBlockProps, PlainText, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { ToggleControl, TextControl, PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -23,12 +24,16 @@ import { useEntityProp } from '@wordpress/core-data'; */ import HeadingLevelDropdown from '../heading/heading-level-dropdown'; import { useCanEditEntity } from '../utils/hooks'; +import { unlock } from '../private-apis'; + +const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); export default function PostTitleEdit( { attributes: { level, textAlign, isLink, rel, linkTarget }, setAttributes, context: { postType, postId, queryId }, insertBlocksAfter, + clientId, } ) { const TagName = 0 === level ? 'p' : 'h' + level; const isDescendentOfQueryLoop = Number.isFinite( queryId ); @@ -58,6 +63,7 @@ export default function PostTitleEdit( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); + const blockEditingMode = useBlockEditingMode( clientId ); let titleElement = ( { __( 'Post Title' ) } @@ -114,20 +120,22 @@ export default function PostTitleEdit( { return ( <> - - - setAttributes( { level: newLevel } ) - } - /> - { - setAttributes( { textAlign: nextAlign } ); - } } - /> - + { blockEditingMode === 'default' && ( + + + setAttributes( { level: newLevel } ) + } + /> + { + setAttributes( { textAlign: nextAlign } ); + } } + /> + + ) } Date: Mon, 22 May 2023 10:32:13 +1000 Subject: [PATCH 02/16] Make useBlockEditingMode use context --- .../src/components/block-editing-mode/index.js | 8 +++++--- packages/block-editor/src/components/block-list/block.js | 9 +++++---- packages/block-editor/src/hooks/align.js | 2 +- packages/block-editor/src/hooks/duotone.js | 2 +- packages/block-library/src/post-title/edit.js | 3 +-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js index ed572f56f73b32..056721209ddfc8 100644 --- a/packages/block-editor/src/components/block-editing-mode/index.js +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -2,15 +2,17 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect } from '@wordpress/element'; +import { useContext, useEffect } from '@wordpress/element'; /** * Internal dependencies */ import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import { BlockListBlockContext } from '../block-list/block'; -export function useBlockEditingMode( clientId, mode ) { +export function useBlockEditingMode( mode ) { + const { clientId } = useContext( BlockListBlockContext ); const blockEditingMode = useSelect( ( select ) => unlock( select( blockEditorStore ) ).getBlockEditingMode( @@ -30,6 +32,6 @@ export function useBlockEditingMode( clientId, mode ) { unsetBlockEditingMode( clientId ); } }; - }, [ clientId, mode, setBlockEditingMode, unsetBlockEditingMode ] ); + }, [ clientId, mode ] ); return blockEditingMode; } diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 964aacae99ac26..d331fe7e541f30 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -43,7 +43,7 @@ import BlockHtml from './block-html'; import { useBlockProps } from './use-block-props'; import { store as blockEditorStore } from '../../store'; import { useLayout } from './layout'; -import { useBlockEditingMode } from '../block-editing-mode'; +import { unlock } from '../../lock-unlock'; export const BlockListBlockContext = createContext(); @@ -104,13 +104,15 @@ function BlockListBlock( { hasContentLockedParent, isContentBlock, isTemporarilyEditingAsBlocks, + blockEditingMode, } = useSelect( ( select ) => { const { getSettings, __unstableGetContentLockingParent, __unstableGetTemporarilyEditingAsBlocks, - } = select( blockEditorStore ); + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); const _hasContentLockedParent = !! __unstableGetContentLockingParent( clientId ); return { @@ -122,6 +124,7 @@ function BlockListBlock( { hasContentLockedParent: _hasContentLockedParent, isTemporarilyEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks() === clientId, + blockEditingMode: getBlockEditingMode( clientId ), }; }, [ name, clientId ] @@ -131,8 +134,6 @@ function BlockListBlock( { const parentLayout = useLayout() || {}; - const blockEditingMode = useBlockEditingMode( clientId ); - // We wrap the BlockEdit component in a div that hides it when editing in // HTML mode. This allows us to render all of the ancillary pieces // (InspectorControls, etc.) which are inside `BlockEdit` but not diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 1a8896513ab2d0..58c076fd8e21c8 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -142,7 +142,7 @@ export const withToolbarControls = createHigherOrderComponent( }, [ props.clientId ] ); - const blockEditingMode = useBlockEditingMode( props.clientId ); + const blockEditingMode = useBlockEditingMode(); if ( ! validAlignments.length || isContentLocked || diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index f018444de0c67f..42a9374ac098bd 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -236,7 +236,7 @@ const withDuotoneControls = createHigherOrderComponent( [ props.clientId ] ); - const blockEditingMode = useBlockEditingMode( props.clientId ); + const blockEditingMode = useBlockEditingMode(); // CAUTION: code added before this line will be executed // for all blocks, not just those that support duotone. Code added diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js index a30b01dee74569..3b54c58f838ea0 100644 --- a/packages/block-library/src/post-title/edit.js +++ b/packages/block-library/src/post-title/edit.js @@ -33,7 +33,6 @@ export default function PostTitleEdit( { setAttributes, context: { postType, postId, queryId }, insertBlocksAfter, - clientId, } ) { const TagName = 0 === level ? 'p' : 'h' + level; const isDescendentOfQueryLoop = Number.isFinite( queryId ); @@ -63,7 +62,7 @@ export default function PostTitleEdit( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); - const blockEditingMode = useBlockEditingMode( clientId ); + const blockEditingMode = useBlockEditingMode(); let titleElement = ( { __( 'Post Title' ) } From 6ab13d7787725be1a1334693962e802e9ebb7c96 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 11:20:56 +1000 Subject: [PATCH 03/16] Remove rootBlockEditingMode setting --- .../src/components/block-editing-mode/index.js | 2 +- packages/block-editor/src/store/private-actions.js | 4 ++-- packages/block-editor/src/store/private-selectors.js | 10 ++-------- packages/block-editor/src/store/reducer.js | 7 +++++-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js index 056721209ddfc8..82a77811429450 100644 --- a/packages/block-editor/src/components/block-editing-mode/index.js +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -12,7 +12,7 @@ import { unlock } from '../../lock-unlock'; import { BlockListBlockContext } from '../block-list/block'; export function useBlockEditingMode( mode ) { - const { clientId } = useContext( BlockListBlockContext ); + const { clientId = '' } = useContext( BlockListBlockContext ) ?? {}; const blockEditingMode = useSelect( ( select ) => unlock( select( blockEditorStore ) ).getBlockEditingMode( diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 496562687118b1..aa2030c1dcc525 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -67,7 +67,7 @@ export function showBlockInterface() { }; } -export function setBlockEditingMode( clientId, mode ) { +export function setBlockEditingMode( clientId = '', mode ) { return { type: 'SET_BLOCK_EDITING_MODE', clientId, @@ -75,7 +75,7 @@ export function setBlockEditingMode( clientId, mode ) { }; } -export function unsetBlockEditingMode( clientId ) { +export function unsetBlockEditingMode( clientId = '' ) { return { type: 'UNSET_BLOCK_EDITING_MODE', clientId, diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 8f9af5df0b692e..b7b63a9ea875be 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -19,18 +19,12 @@ export function getLastInsertedBlocksClientIds( state ) { return state?.lastBlockInserted?.clientIds; } -export function getBlockEditingMode( state, clientId ) { - if ( ! clientId ) { - return state.settings.rootBlockEditingMode; - } +export function getBlockEditingMode( state, clientId = '' ) { while ( ! state.blockEditingModes.has( clientId ) && state.blocks.parents.has( clientId ) ) { clientId = state.blocks.parents.get( clientId ); } - return ( - state.blockEditingModes.get( clientId ) ?? - state.settings.rootBlockEditingMode - ); + return state.blockEditingModes.get( clientId ) ?? 'default'; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index f0c44244371c04..8bef78a07fa7ae 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1843,8 +1843,11 @@ export function blockEditingModes( state = new Map(), action ) { newState.delete( action.clientId ); return newState; } - case 'RESET_BLOCKS': - return new Map(); + case 'RESET_BLOCKS': { + return state.has( '' ) + ? new Map().set( '', state.get( '' ) ) + : state; + } } return state; } From 6426fca4057250adecd31c8c9d554c07329107da Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 13:57:35 +1000 Subject: [PATCH 04/16] Fix private createRegistrySelector selectors --- packages/data/src/redux-store/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/data/src/redux-store/index.js b/packages/data/src/redux-store/index.js index c4dc12643673b6..50a0ade0d551c9 100644 --- a/packages/data/src/redux-store/index.js +++ b/packages/data/src/redux-store/index.js @@ -221,12 +221,14 @@ export default function createReduxStore( key, options ) { get: ( target, prop ) => { return ( mapSelectors( - mapValues( - privateSelectors, - ( selector ) => - ( state, ...args ) => - selector( state.root, ...args ) - ), + mapValues( privateSelectors, ( selector ) => { + if ( selector.isRegistrySelector ) { + selector.registry = registry; + } + + return ( state, ...args ) => + selector( state.root, ...args ); + } ), store )[ prop ] || selectors[ prop ] ); From bd2f478717f517e72a4dcc505cbef9b0d2f0cdac Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 13:58:45 +1000 Subject: [PATCH 05/16] Consolidate templateLock=contentOnly logic into getBlockEditingMode --- .../src/components/block-list/block.js | 19 +----- .../block-list/use-in-between-inserter.js | 3 +- .../src/components/block-toolbar/index.js | 17 ++--- .../block-tools/block-contextual-toolbar.js | 4 -- .../components/use-block-drop-zone/index.js | 11 ++-- packages/block-editor/src/hooks/align.js | 16 +---- packages/block-editor/src/hooks/duotone.js | 19 +----- packages/block-editor/src/hooks/layout.js | 28 +++----- .../src/store/private-selectors.js | 66 ++++++++++++++++--- packages/block-library/src/image/edit.js | 31 +++++---- packages/block-library/src/image/image.js | 10 +-- packages/block-library/src/media-text/edit.js | 22 ++++--- .../src/media-text/media-container.js | 6 +- 13 files changed, 124 insertions(+), 128 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index d331fe7e541f30..185c8791a27e47 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -18,7 +18,6 @@ import { isUnmodifiedDefaultBlock, serializeRawBlock, switchToBlockType, - store as blocksStore, getDefaultBlockName, isUnmodifiedBlock, } from '@wordpress/blocks'; @@ -101,33 +100,23 @@ function BlockListBlock( { } ) { const { themeSupportsLayout, - hasContentLockedParent, - isContentBlock, isTemporarilyEditingAsBlocks, blockEditingMode, } = useSelect( ( select ) => { const { getSettings, - __unstableGetContentLockingParent, __unstableGetTemporarilyEditingAsBlocks, getBlockEditingMode, } = unlock( select( blockEditorStore ) ); - const _hasContentLockedParent = - !! __unstableGetContentLockingParent( clientId ); return { themeSupportsLayout: getSettings().supportsLayout, - isContentBlock: - select( blocksStore ).__experimentalHasContentRoleAttribute( - name - ), - hasContentLockedParent: _hasContentLockedParent, isTemporarilyEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks() === clientId, blockEditingMode: getBlockEditingMode( clientId ), }; }, - [ name, clientId ] + [ clientId ] ); const { removeBlock } = useDispatch( blockEditorStore ); const onRemove = useCallback( () => removeBlock( clientId ), [ clientId ] ); @@ -160,7 +149,7 @@ function BlockListBlock( { const blockType = getBlockType( name ); - if ( hasContentLockedParent && ! isContentBlock ) { + if ( blockEditingMode === 'disabled' ) { wrapperProps = { ...wrapperProps, tabIndex: -1, @@ -234,9 +223,7 @@ function BlockListBlock( { clientId, className: classnames( { - 'is-editing-disabled': - blockEditingMode === 'disabled' || - ( hasContentLockedParent && ! isContentBlock ), + 'is-editing-disabled': blockEditingMode === 'disabled', 'is-content-locked-temporarily-editing-as-blocks': isTemporarilyEditingAsBlocks, }, diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index 854d1dc7ca86a1..2c1d8736ceefdb 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -76,7 +76,8 @@ export function useInBetweenInserter() { rootClientId = blockElement.getAttribute( 'data-block' ); } - // Don't set the insertion point if the root is locked. + // Don't show the in-between inserter if the container block is + // locked or disabled. if ( getTemplateLock( rootClientId ) || getBlockEditingMode( rootClientId ) === 'disabled' diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index af5fe6f509b582..4dc53aae0afffc 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -43,7 +43,7 @@ const BlockToolbar = ( { hideDragHandle } ) => { isDistractionFree, isValid, isVisual, - isContentLocked, + blockEditingMode, } = useSelect( ( select ) => { const { getBlockName, @@ -52,7 +52,6 @@ const BlockToolbar = ( { hideDragHandle } ) => { isBlockValid, getBlockRootClientId, getSettings, - __unstableGetContentLockingParent, getBlockEditingMode, } = unlock( select( blockEditorStore ) ); const selectedBlockClientIds = getSelectedBlockClientIds(); @@ -75,9 +74,7 @@ const BlockToolbar = ( { hideDragHandle } ) => { isVisual: selectedBlockClientIds.every( ( id ) => getBlockMode( id ) === 'visual' ), - isContentLocked: - !! __unstableGetContentLockingParent( selectedBlockClientId ) || - getBlockEditingMode( selectedBlockClientId ) !== 'default', + blockEditingMode: getBlockEditingMode( selectedBlockClientId ), }; }, [] ); @@ -127,12 +124,12 @@ const BlockToolbar = ( { hideDragHandle } ) => { return (
- { ! isMultiToolbar && isLargeViewport && ! isContentLocked && ( - - ) } + { ! isMultiToolbar && + isLargeViewport && + blockEditingMode === 'default' && }
{ ( shouldShowVisualToolbar || isMultiToolbar ) && - ! isContentLocked && ( + blockEditingMode === 'default' && ( { ! isMultiToolbar && ( @@ -177,7 +174,7 @@ const BlockToolbar = ( { hideDragHandle } ) => { ) } - { ! isContentLocked && ( + { blockEditingMode === 'default' && ( ) }
diff --git a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js index d709869de52b2e..d9c06f0324701c 100644 --- a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js @@ -39,7 +39,6 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { getBlockName, getBlockParents, getSelectedBlockClientIds, - __unstableGetContentLockingParent, getBlockEditingMode, } = unlock( select( blockEditorStore ) ); const { getBlockType } = select( blocksStore ); @@ -64,9 +63,6 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { true ) && selectedBlockClientIds.length <= 1 && - ! __unstableGetContentLockingParent( - _selectedBlockClientId - ) && getBlockEditingMode( _selectedBlockClientId ) === 'default', }; }, [] ); diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index d0700bd8d05abb..75f1de2b03e594 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -19,6 +19,7 @@ import { isPointContainedByRect, } from '../../utils/math'; import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** @typedef {import('../use-on-block-drop/types').WPDropOperation} WPDropOperation */ @@ -150,15 +151,13 @@ export default function useBlockDropZone( { const isDisabled = useSelect( ( select ) => { const { - getTemplateLock, __unstableIsWithinBlockOverlay, __unstableHasActiveBlockOverlayActive, - } = select( blockEditorStore ); - const templateLock = getTemplateLock( targetRootClientId ); + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); + const blockEditingMode = getBlockEditingMode( targetRootClientId ); return ( - [ 'all', 'contentOnly' ].some( - ( lock ) => lock === templateLock - ) || + blockEditingMode !== 'default' || __unstableHasActiveBlockOverlayActive( targetRootClientId ) || __unstableIsWithinBlockOverlay( targetRootClientId ) ); diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 58c076fd8e21c8..a417d11d900431 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -13,14 +13,12 @@ import { getBlockType, hasBlockSupport, } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { BlockControls, BlockAlignmentControl } from '../components'; import useAvailableAlignments from '../components/block-alignment-control/use-available-alignments'; -import { store as blockEditorStore } from '../store'; import { useBlockEditingMode } from '../components/block-editing-mode'; /** @@ -134,20 +132,8 @@ export const withToolbarControls = createHigherOrderComponent( const validAlignments = useAvailableAlignments( blockAllowedAlignments ).map( ( { name } ) => name ); - const isContentLocked = useSelect( - ( select ) => { - return select( - blockEditorStore - ).__unstableGetContentLockingParent( props.clientId ); - }, - [ props.clientId ] - ); const blockEditingMode = useBlockEditingMode(); - if ( - ! validAlignments.length || - isContentLocked || - blockEditingMode !== 'default' - ) { + if ( ! validAlignments.length || blockEditingMode !== 'default' ) { return blockEdit; } diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index 42a9374ac098bd..2ecd35c99ee8c4 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -16,7 +16,6 @@ import { import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { useMemo, useContext, createPortal } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -36,7 +35,6 @@ import { import { getBlockCSSSelector } from '../components/global-styles/get-block-css-selector'; import { scopeSelector } from '../components/global-styles/utils'; import { useBlockSettings } from './utils'; -import { store as blockEditorStore } from '../store'; import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel'; import { useBlockEditingMode } from '../components/block-editing-mode'; @@ -227,15 +225,6 @@ const withDuotoneControls = createHigherOrderComponent( 'filter.duotone' ); - const isContentLocked = useSelect( - ( select ) => { - return select( - blockEditorStore - ).__unstableGetContentLockingParent( props.clientId ); - }, - [ props.clientId ] - ); - const blockEditingMode = useBlockEditingMode(); // CAUTION: code added before this line will be executed @@ -244,11 +233,9 @@ const withDuotoneControls = createHigherOrderComponent( // performance. return ( <> - { hasDuotoneSupport && - ! isContentLocked && - blockEditingMode === 'default' && ( - - ) } + { hasDuotoneSupport && blockEditingMode === 'default' && ( + + ) } ); diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 9b11e991004b39..815b36e785a81d 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -29,6 +29,7 @@ import useSetting from '../components/use-setting'; import { LayoutStyle } from '../components/block-list/layout'; import BlockList from '../components/block-list'; import { getLayoutType, getLayoutTypes } from '../layouts'; +import { useBlockEditingMode } from '../components/block-editing-mode'; const layoutBlockSupportKey = '__experimentalLayout'; @@ -131,25 +132,16 @@ export function useLayoutStyles( blockAttributes = {}, blockName, selector ) { return css; } -function LayoutPanel( { - clientId, - setAttributes, - attributes, - name: blockName, -} ) { +function LayoutPanel( { setAttributes, attributes, name: blockName } ) { const { layout } = attributes; const defaultThemeLayout = useSetting( 'layout' ); - const { themeSupportsLayout, isContentLocked } = useSelect( - ( select ) => { - const { getSettings, __unstableGetContentLockingParent } = - select( blockEditorStore ); - return { - themeSupportsLayout: getSettings().supportsLayout, - isContentLocked: __unstableGetContentLockingParent( clientId ), - }; - }, - [ clientId ] - ); + const { themeSupportsLayout } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return { + themeSupportsLayout: getSettings().supportsLayout, + }; + }, [] ); + const blockEditingMode = useBlockEditingMode(); const layoutBlockSupport = getBlockSupport( blockName, @@ -270,7 +262,7 @@ function LayoutPanel( { ) } - { ! inherit && ! isContentLocked && layoutType && ( + { ! inherit && blockEditingMode === 'default' && layoutType && ( ( state, clientId ) => { + const explicitEditingMode = getExplcitBlockEditingMode( + state, + clientId + ); + const rootClientId = getBlockRootClientId( state, clientId ); + const templateLock = getTemplateLock( state, rootClientId ); + const name = getBlockName( state, clientId ); + const isContent = + select( blocksStore ).__experimentalHasContentRoleAttribute( name ); + if ( + explicitEditingMode === 'disabled' || + ( templateLock === 'contentOnly' && ! isContent ) + ) { + return 'disabled'; + } + if ( + explicitEditingMode === 'contentOnly' || + ( templateLock === 'contentOnly' && isContent ) + ) { + return 'contentOnly'; + } + return 'default'; } - return state.blockEditingModes.get( clientId ) ?? 'default'; -} +); + +const getExplcitBlockEditingMode = createSelector( + ( state, clientId = '' ) => { + while ( + ! state.blockEditingModes.has( clientId ) && + state.blocks.parents.has( clientId ) + ) { + clientId = state.blocks.parents.get( clientId ); + } + return state.blockEditingModes.get( clientId ) ?? 'default'; + }, + ( state ) => [ state.blockEditingModes, state.blocks.parents ] +); diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index c4d8de316ea997..95de37062c09a0 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -17,6 +17,7 @@ import { useBlockProps, store as blockEditorStore, __experimentalUseBorderProps as useBorderProps, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { useEffect, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -27,6 +28,7 @@ import { store as noticesStore } from '@wordpress/notices'; * Internal dependencies */ import Image from './image'; +import { unlock } from '../private-apis'; /** * Module constants @@ -39,6 +41,8 @@ import { ALLOWED_MEDIA_TYPES, } from './constants'; +const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); + export const pickRelevantMediaFiles = ( image, size ) => { const imageProps = Object.fromEntries( Object.entries( image ?? {} ).filter( ( [ key ] ) => @@ -124,20 +128,15 @@ export function ImageEdit( { }, [ caption ] ); const ref = useRef(); - const { imageDefaultSize, mediaUpload, isContentLocked } = useSelect( - ( select ) => { - const { getSettings, __unstableGetContentLockingParent } = - select( blockEditorStore ); - const settings = getSettings(); - return { - imageDefaultSize: settings.imageDefaultSize, - mediaUpload: settings.mediaUpload, - isContentLocked: - !! __unstableGetContentLockingParent( clientId ), - }; - }, - [] - ); + const { imageDefaultSize, mediaUpload } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + const settings = getSettings(); + return { + imageDefaultSize: settings.imageDefaultSize, + mediaUpload: settings.mediaUpload, + }; + }, [] ); + const blockEditingMode = useBlockEditingMode(); const { createErrorNotice } = useDispatch( noticesStore ); function onUploadError( message ) { @@ -366,10 +365,10 @@ export function ImageEdit( { containerRef={ ref } context={ context } clientId={ clientId } - isContentLocked={ isContentLocked } + blockEditingMode={ blockEditingMode } /> ) } - { ! url && ! isContentLocked && ( + { ! url && blockEditingMode === 'default' && ( - { ! isContentLocked && ( + { blockEditingMode === 'default' && ( ) } - { ! isContentLocked && ( + { blockEditingMode === 'default' && ( { setShowCaption( ! showCaption ); diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index f2848a584d0d8a..dcf8eae4ecf5a9 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -18,6 +18,7 @@ import { __experimentalImageURLInputUI as ImageURLInputUI, __experimentalImageSizeControl as ImageSizeControl, store as blockEditorStore, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { PanelBody, @@ -43,6 +44,9 @@ import { LINK_DESTINATION_ATTACHMENT, TEMPLATE, } from './constants'; +import { unlock } from '../private-apis'; + +const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); // this limits the resize to a safe zone to avoid making broken layouts const applyWidthConstraints = ( width ) => @@ -126,7 +130,7 @@ function attributesFromMedia( { }; } -function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { +function MediaTextEdit( { attributes, isSelected, setAttributes } ) { const { focalPoint, href, @@ -147,13 +151,10 @@ function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { } = attributes; const mediaSizeSlug = attributes.mediaSizeSlug || DEFAULT_MEDIA_SIZE_SLUG; - const { imageSizes, image, isContentLocked } = useSelect( + const { imageSizes, image } = useSelect( ( select ) => { - const { __unstableGetContentLockingParent, getSettings } = - select( blockEditorStore ); + const { getSettings } = select( blockEditorStore ); return { - isContentLocked: - !! __unstableGetContentLockingParent( clientId ), image: mediaId && isSelected ? select( coreStore ).getMedia( mediaId, { @@ -163,8 +164,7 @@ function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { imageSizes: getSettings()?.imageSizes, }; }, - - [ isSelected, mediaId, clientId ] + [ isSelected, mediaId ] ); const refMediaContainer = useRef(); @@ -319,11 +319,13 @@ function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { { template: TEMPLATE, allowedBlocks } ); + const blockEditingMode = useBlockEditingMode(); + return ( <> { mediaTextGeneralSettings } - { ! isContentLocked && ( + { blockEditingMode === 'default' && ( <> { mediaPosition !== 'right' &&
} diff --git a/packages/block-library/src/media-text/media-container.js b/packages/block-library/src/media-text/media-container.js index e5a6270bad8dc9..951c0013b76ebc 100644 --- a/packages/block-library/src/media-text/media-container.js +++ b/packages/block-library/src/media-text/media-container.js @@ -109,7 +109,7 @@ function MediaContainer( props, ref ) { mediaWidth, onSelectMedia, onWidthChange, - isContentLocked, + enableResize, } = props; const isTemporaryMedia = ! mediaId && isBlobURL( mediaUrl ); @@ -128,8 +128,8 @@ function MediaContainer( props, ref ) { commitWidthChange( parseInt( elt.style.width ) ); }; const enablePositions = { - right: ! isContentLocked && mediaPosition === 'left', - left: ! isContentLocked && mediaPosition === 'right', + right: enableResize && mediaPosition === 'left', + left: enableResize && mediaPosition === 'right', }; const backgroundStyles = From f00dc257cebcbcf9d3a310c6a4c758f9661623ab Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 14:42:32 +1000 Subject: [PATCH 06/16] Hide disabled blocks from List View --- .../src/components/list-view/block.js | 59 ++++++++----------- .../src/components/list-view/branch.js | 8 +-- .../list-view/use-list-view-client-ids.js | 25 ++++++-- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js index 1afde32cf1ec61..e58c68445ac718 100644 --- a/packages/block-editor/src/components/list-view/block.js +++ b/packages/block-editor/src/components/list-view/block.js @@ -38,6 +38,7 @@ import { getBlockPositionDescription } from './utils'; import { store as blockEditorStore } from '../../store'; import useBlockDisplayInformation from '../use-block-display-information'; import { useBlockLock } from '../block-lock'; +import { unlock } from '../../lock-unlock'; function ListViewBlock( { block: { clientId }, @@ -59,31 +60,13 @@ function ListViewBlock( { const rowRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); - const { isLocked, isContentLocked, canEdit } = useBlockLock( clientId ); - const forceSelectionContentLock = useSelect( - ( select ) => { - if ( isSelected ) { - return false; - } - if ( ! isContentLocked ) { - return false; - } - return select( blockEditorStore ).hasSelectedInnerBlock( - clientId, - true - ); - }, - [ isContentLocked, clientId, isSelected ] - ); + const { isLocked, canEdit } = useBlockLock( clientId ); - const canExpand = isContentLocked ? false : canEdit; const isFirstSelectedBlock = - forceSelectionContentLock || - ( isSelected && selectedClientIds[ 0 ] === clientId ); + isSelected && selectedClientIds[ 0 ] === clientId; const isLastSelectedBlock = - forceSelectionContentLock || - ( isSelected && - selectedClientIds[ selectedClientIds.length - 1 ] === clientId ); + isSelected && + selectedClientIds[ selectedClientIds.length - 1 ] === clientId; const { toggleBlockHighlight } = useDispatch( blockEditorStore ); @@ -97,15 +80,21 @@ function ListViewBlock( { ( select ) => select( blockEditorStore ).getBlockName( clientId ), [ clientId ] ); - - // When a block hides its toolbar it also hides the block settings menu, - // since that menu is part of the toolbar in the editor canvas. - // List View respects this by also hiding the block settings menu. - const showBlockActions = hasBlockSupport( - blockName, - '__experimentalToolbar', - true + const blockEditingMode = useSelect( + ( select ) => + unlock( select( blockEditorStore ) ).getBlockEditingMode( + clientId + ), + [ clientId ] ); + + const showBlockActions = + // When a block hides its toolbar it also hides the block settings menu, + // since that menu is part of the toolbar in the editor canvas. + // List View respects this by also hiding the block settings menu. + hasBlockSupport( blockName, '__experimentalToolbar', true ) && + // Don't show the settings menu if block is disabled or content only. + blockEditingMode === 'default'; const instanceId = useInstanceId( ListViewBlock ); const descriptionId = `list-view-block-select-button__${ instanceId }`; const blockPositionDescription = getBlockPositionDescription( @@ -204,7 +193,7 @@ function ListViewBlock( { } const classes = classnames( { - 'is-selected': isSelected || forceSelectionContentLock, + 'is-selected': isSelected, 'is-first-selected': isFirstSelectedBlock, 'is-last-selected': isLastSelectedBlock, 'is-branch-selected': isBranchSelected, @@ -248,14 +237,14 @@ function ListViewBlock( { path={ path } id={ `list-view-${ listViewInstanceId }-block-${ clientId }` } data-block={ clientId } - data-expanded={ canExpand ? isExpanded : undefined } + data-expanded={ canEdit ? isExpanded : undefined } ref={ rowRef } > { ( { ref, tabIndex, onFocus } ) => (
@@ -272,7 +261,7 @@ function ListViewBlock( { currentlyEditingBlockInCanvas ? 0 : tabIndex } onFocus={ onFocus } - isExpanded={ canExpand ? isExpanded : undefined } + isExpanded={ canEdit ? isExpanded : undefined } selectedClientIds={ selectedClientIds } ariaLabel={ blockAriaLabel } ariaDescribedBy={ descriptionId } @@ -321,7 +310,7 @@ function ListViewBlock( { { showBlockActions && BlockSettingsMenu && ( { ( { ref, tabIndex, onFocus } ) => ( { + return tree.flatMap( ( { clientId, innerBlocks, ...rest } ) => { + if ( getBlockEditingMode( clientId ) === 'disabled' ) { + return removeDisabledBlocks( innerBlocks ); + } + return [ + { + clientId, + innerBlocks: removeDisabledBlocks( innerBlocks ), + ...rest, + }, + ]; + } ); + }; return { selectedClientIds: getSelectedBlockClientIds(), draggedClientIds: getDraggedBlockClientIds(), - clientIdsTree: blocks - ? blocks - : __unstableGetClientIdsTree( rootClientId ), + clientIdsTree: removeDisabledBlocks( + blocks ?? __unstableGetClientIdsTree( rootClientId ) + ), }; }, [ blocks, rootClientId ] From c7e128cc44c843a7e5e54f7aeb901da5b21f6002 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 14:42:56 +1000 Subject: [PATCH 07/16] Hide disabled blocks from breadcrumbs --- .../src/components/block-breadcrumb/index.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-breadcrumb/index.js b/packages/block-editor/src/components/block-breadcrumb/index.js index 419d4c729b17d2..9e4ab5391d8c44 100644 --- a/packages/block-editor/src/components/block-breadcrumb/index.js +++ b/packages/block-editor/src/components/block-breadcrumb/index.js @@ -11,6 +11,7 @@ import { chevronRightSmall, Icon } from '@wordpress/icons'; */ import BlockTitle from '../block-title'; import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; /** * Block breadcrumb component, displaying the hierarchy of the current block selection as a breadcrumb. @@ -22,11 +23,18 @@ import { store as blockEditorStore } from '../../store'; function BlockBreadcrumb( { rootLabelText } ) { const { selectBlock, clearSelectedBlock } = useDispatch( blockEditorStore ); const { clientId, parents, hasSelection } = useSelect( ( select ) => { - const { getSelectionStart, getSelectedBlockClientId, getBlockParents } = - select( blockEditorStore ); + const { + getSelectionStart, + getSelectedBlockClientId, + getBlockParents, + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); const selectedBlockClientId = getSelectedBlockClientId(); return { - parents: getBlockParents( selectedBlockClientId ), + parents: getBlockParents( selectedBlockClientId ).filter( + ( parentClientId ) => + getBlockEditingMode( parentClientId ) !== 'disabled' + ), clientId: selectedBlockClientId, hasSelection: !! getSelectionStart().clientId, }; From 2a9b9e533bb4422a6cdfbdbf63da4e1384ffadf2 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 22 May 2023 15:29:04 +1000 Subject: [PATCH 08/16] Add doc comments --- .../components/block-editing-mode/index.js | 31 ++++++++ .../block-editor/src/store/private-actions.js | 20 +++++ .../src/store/private-selectors.js | 77 +++++++++++++------ packages/block-editor/src/store/reducer.js | 8 ++ 4 files changed, 113 insertions(+), 23 deletions(-) diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js index 82a77811429450..94de18dd9d5620 100644 --- a/packages/block-editor/src/components/block-editing-mode/index.js +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -11,6 +11,37 @@ import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import { BlockListBlockContext } from '../block-list/block'; +/** + * Allows a block to restrict the user interface that is displayed for editing + * that block and its inner blocks. + * + * @example + * ```js + * function MyBlock( { attributes, setAttributes } ) { + * useBlockEditingMode( 'disabled' ); + * return
; + * } + * ``` + * + * `mode` can be one of three options: + * + * - `'disabled'`: Prevents editing the block entirely, i.e. it cannot be + * selected. + * - `'contentOnly'`: Hides all non-content UI, e.g. auxiliary controls in the + * toolbar, the block movers, block settings. + * - `'default'`: Allows editing the block as normal. + * + * The mode is inherited by all of the block's inner blocks, unless they have + * their own mode. + * + * If called outside of a block context, the mode is applied to all blocks. + * + * @param {?string} mode The editing mode to apply. If undefined, the current + * editing mode is not changed. One of `'disabled'`, + * `'contentOnly'`, or `'default'`. + * + * @return {string} The current editing mode. + */ export function useBlockEditingMode( mode ) { const { clientId = '' } = useContext( BlockListBlockContext ) ?? {}; const blockEditingMode = useSelect( diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index aa2030c1dcc525..593086ff120ba6 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -67,6 +67,17 @@ export function showBlockInterface() { }; } +/** + * Sets the block editing mode for a given block. + * + * @see useBlockEditingMode + * + * @param {string} clientId The block client ID, or `''` for the root container. + * @param {string} mode The block editing mode. One of `'disabled'`, + * `'contentOnly'`, or `'default'`. + * + * @return {Object} Action object. + */ export function setBlockEditingMode( clientId = '', mode ) { return { type: 'SET_BLOCK_EDITING_MODE', @@ -75,6 +86,15 @@ export function setBlockEditingMode( clientId = '', mode ) { }; } +/** + * Clears the block editing mode for a given block. + * + * @see useBlockEditingMode + * + * @param {string} clientId The block client ID, or `''` for the root container. + * + * @return {Object} Action object. + */ export function unsetBlockEditingMode( clientId = '' ) { return { type: 'UNSET_BLOCK_EDITING_MODE', diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 6cdb77ec83de6d..f33e9e55e30451 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -39,31 +39,62 @@ export function getLastInsertedBlocksClientIds( state ) { return state?.lastBlockInserted?.clientIds; } +/** + * Returns the block editing mode for a given block. + * + * The mode can be one of three options: + * + * - `'disabled'`: Prevents editing the block entirely, i.e. it cannot be + * selected. + * - `'contentOnly'`: Hides all non-content UI, e.g. auxiliary controls in the + * toolbar, the block movers, block settings. + * - `'default'`: Allows editing the block as normal. + * + * Blocks can set a mode using the `useBlockEditingMode` hook. + * + * The mode is inherited by all of the block's inner blocks, unless they have + * their own mode. + * + * A template lock can also set a mode. If the template lock is `'contentOnly'`, + * the block's mode is overridden to `'contentOnly'` if the block has a content + * role attribute, or `'disabled'` otherwise. + * + * @see useBlockEditingMode + * + * @param {Object} state Global application state. + * @param {string} clientId The block client ID, or `''` for the root container. + * + * @return {string} The block editing mode. One of `'disabled'`, + * `'contentOnly'`, or `'default'`. + */ export const getBlockEditingMode = createRegistrySelector( - ( select ) => ( state, clientId ) => { - const explicitEditingMode = getExplcitBlockEditingMode( - state, - clientId - ); - const rootClientId = getBlockRootClientId( state, clientId ); - const templateLock = getTemplateLock( state, rootClientId ); - const name = getBlockName( state, clientId ); - const isContent = - select( blocksStore ).__experimentalHasContentRoleAttribute( name ); - if ( - explicitEditingMode === 'disabled' || - ( templateLock === 'contentOnly' && ! isContent ) - ) { - return 'disabled'; - } - if ( - explicitEditingMode === 'contentOnly' || - ( templateLock === 'contentOnly' && isContent ) - ) { - return 'contentOnly'; + ( select ) => + ( state, clientId = '' ) => { + const explicitEditingMode = getExplcitBlockEditingMode( + state, + clientId + ); + const rootClientId = getBlockRootClientId( state, clientId ); + const templateLock = getTemplateLock( state, rootClientId ); + const name = getBlockName( state, clientId ); + const isContent = + select( blocksStore ).__experimentalHasContentRoleAttribute( + name + ); + if ( + explicitEditingMode === 'disabled' || + ( templateLock === 'contentOnly' && ! isContent ) + ) { + return 'disabled'; + } + if ( + explicitEditingMode === 'contentOnly' || + ( templateLock === 'contentOnly' && isContent ) + ) { + return 'contentOnly'; + } + return 'default'; } - return 'default'; - } ); const getExplcitBlockEditingMode = createSelector( diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 8bef78a07fa7ae..b316bbeb5079e1 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1834,6 +1834,14 @@ export function temporarilyEditingAsBlocks( state = '', action ) { return state; } +/** + * Reducer returning a map of block client IDs to block editing modes. + * + * @param {Map} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Map} Updated state. + */ export function blockEditingModes( state = new Map(), action ) { switch ( action.type ) { case 'SET_BLOCK_EDITING_MODE': From 692cd768b374ba77516c7ed05afe7a88e54a50cb Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 23 May 2023 11:32:29 +1000 Subject: [PATCH 09/16] Add unit tests --- .../src/store/test/private-actions.js | 33 +++- .../src/store/test/private-selectors.js | 158 ++++++++++++++++++ .../block-editor/src/store/test/reducer.js | 48 ++++++ 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/test/private-actions.js b/packages/block-editor/src/store/test/private-actions.js index c4453547f6ce6a..fdfe993091fef7 100644 --- a/packages/block-editor/src/store/test/private-actions.js +++ b/packages/block-editor/src/store/test/private-actions.js @@ -1,7 +1,12 @@ /** * Internal dependencies */ -import { hideBlockInterface, showBlockInterface } from '../private-actions'; +import { + hideBlockInterface, + showBlockInterface, + setBlockEditingMode, + unsetBlockEditingMode, +} from '../private-actions'; describe( 'private actions', () => { describe( 'hideBlockInterface', () => { @@ -19,4 +24,30 @@ describe( 'private actions', () => { } ); } ); } ); + + describe( 'setBlockEditingMode', () => { + it( 'should return the SET_BLOCK_EDITING_MODE action', () => { + expect( + setBlockEditingMode( + '14501cc2-90a6-4f52-aa36-ab6e896135d1', + 'default' + ) + ).toEqual( { + type: 'SET_BLOCK_EDITING_MODE', + clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1', + mode: 'default', + } ); + } ); + } ); + + describe( 'unsetBlockEditingMode', () => { + it( 'should return the UNSET_BLOCK_EDITING_MODE action', () => { + expect( + unsetBlockEditingMode( '14501cc2-90a6-4f52-aa36-ab6e896135d1' ) + ).toEqual( { + type: 'UNSET_BLOCK_EDITING_MODE', + clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1', + } ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index c5df265f75db35..0dcb89f5363c1c 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -4,6 +4,7 @@ import { isBlockInterfaceHidden, getLastInsertedBlocksClientIds, + getBlockEditingMode, } from '../private-selectors'; describe( 'private selectors', () => { @@ -49,4 +50,161 @@ describe( 'private selectors', () => { ] ); } ); } ); + + describe( 'getBlockEditingMode', () => { + const baseState = { + settings: {}, + blocks: { + byClientId: new Map( [ + [ '6cf70164-9097-4460-bcbf-200560546988', {} ], // Header + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', {} ], // Group + [ 'b26fc763-417d-4f01-b81c-2ec61e14a972', {} ], // | Post Title + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', {} ], // | Post Content + [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', {} ], // | | Paragraph + [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', {} ], // | | Paragraph + ] ), + parents: new Map( [ + [ '6cf70164-9097-4460-bcbf-200560546988', '' ], + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', '' ], + [ + 'b26fc763-417d-4f01-b81c-2ec61e14a972', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + [ + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + ] ), + }, + blockListSettings: { + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337': {}, + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f': {}, + }, + blockEditingModes: new Map( [] ), + }; + + const __experimentalHasContentRoleAttribute = jest.fn( () => false ); + getBlockEditingMode.registry = { + select: jest.fn( () => ( { + __experimentalHasContentRoleAttribute, + } ) ), + }; + + it( 'should return default by default', () => { + expect( + getBlockEditingMode( + baseState, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( 'default' ); + } ); + + [ 'disabled', 'contentOnly' ].forEach( ( mode ) => { + it( `should return ${ mode } if explicitly set`, () => { + const state = { + ...baseState, + blockEditingModes: new Map( [ + [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', mode ], + ] ), + }; + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( mode ); + } ); + + it( `should return ${ mode } if explicitly set on a parent`, () => { + const state = { + ...baseState, + blockEditingModes: new Map( [ + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', mode ], + ] ), + }; + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( mode ); + } ); + + it( `should return ${ mode } if overriden by a parent`, () => { + const state = { + ...baseState, + blockEditingModes: new Map( [ + [ '', mode ], + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', 'default' ], + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', mode ], + ] ), + }; + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( mode ); + } ); + + it( `should return ${ mode } if explicitly set on root`, () => { + const state = { + ...baseState, + blockEditingModes: new Map( [ [ '', mode ] ] ), + }; + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( mode ); + } ); + } ); + + it( 'should return disabled if parent is locked and the block has no content role', () => { + const state = { + ...baseState, + blockListSettings: { + ...baseState.blockListSettings, + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f': { + templateLock: 'contentOnly', + }, + }, + }; + __experimentalHasContentRoleAttribute.mockReturnValueOnce( false ); + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( 'disabled' ); + } ); + + it( 'should return contentOnly if parent is locked and the block has a content role', () => { + const state = { + ...baseState, + blockListSettings: { + ...baseState.blockListSettings, + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f': { + templateLock: 'contentOnly', + }, + }, + }; + __experimentalHasContentRoleAttribute.mockReturnValueOnce( true ); + expect( + getBlockEditingMode( + state, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( 'contentOnly' ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 609cbb59c6e54b..67ed0ae69106d6 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -32,6 +32,7 @@ import { blockListSettings, lastBlockAttributesChange, lastBlockInserted, + blockEditingModes, } from '../reducer'; const noop = () => {}; @@ -3367,4 +3368,51 @@ describe( 'state', () => { expect( state ).toEqual( expectedState ); } ); } ); + + describe( 'blockEditingModes', () => { + it( 'should return an empty map by default', () => { + expect( blockEditingModes( undefined, {} ) ).toEqual( new Map() ); + } ); + + it( 'should set the editing mode for a block', () => { + const state = new Map(); + const newState = blockEditingModes( state, { + type: 'SET_BLOCK_EDITING_MODE', + clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1', + mode: 'default', + } ); + expect( newState ).toEqual( + new Map( [ + [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ], + ] ) + ); + } ); + + it( 'should clear the editing mode for a block', () => { + const state = new Map( [ + [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ], + ] ); + const newState = blockEditingModes( state, { + type: 'UNSET_BLOCK_EDITING_MODE', + clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1', + } ); + expect( newState ).toEqual( new Map() ); + } ); + + it( 'should clear editing modes when blocks are reset', () => { + const state = new Map( [ + [ '', 'disabled' ], + [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ], + ] ); + const newState = blockEditingModes( state, { + type: 'RESET_BLOCKS', + } ); + expect( newState ).toEqual( + new Map( [ + // Root mode should be maintained. + [ '', 'disabled' ], + ] ) + ); + } ); + } ); } ); From 8162afef4f40d8d9305433f626c34d13f08a6bef Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 23 May 2023 11:40:26 +1000 Subject: [PATCH 10/16] Use @typedef to document mode param --- .../src/components/block-editing-mode/index.js | 11 +++++++---- packages/block-editor/src/store/private-actions.js | 10 +++++++--- packages/block-editor/src/store/private-selectors.js | 8 ++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js index 94de18dd9d5620..a9bb85197619bf 100644 --- a/packages/block-editor/src/components/block-editing-mode/index.js +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -11,6 +11,10 @@ import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import { BlockListBlockContext } from '../block-list/block'; +/** + * @typedef {'disabled'|'contentOnly'|'default'} BlockEditingMode + */ + /** * Allows a block to restrict the user interface that is displayed for editing * that block and its inner blocks. @@ -36,11 +40,10 @@ import { BlockListBlockContext } from '../block-list/block'; * * If called outside of a block context, the mode is applied to all blocks. * - * @param {?string} mode The editing mode to apply. If undefined, the current - * editing mode is not changed. One of `'disabled'`, - * `'contentOnly'`, or `'default'`. + * @param {?BlockEditingMode} mode The editing mode to apply. If undefined, the + * current editing mode is not changed. * - * @return {string} The current editing mode. + * @return {BlockEditingMode} The current editing mode. */ export function useBlockEditingMode( mode ) { const { clientId = '' } = useContext( BlockListBlockContext ) ?? {}; diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 593086ff120ba6..0a3484154e5a78 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -67,14 +67,18 @@ export function showBlockInterface() { }; } +/** + * @typedef {import('../components/block-editing-mode').BlockEditingMode} BlockEditingMode + */ + /** * Sets the block editing mode for a given block. * * @see useBlockEditingMode * - * @param {string} clientId The block client ID, or `''` for the root container. - * @param {string} mode The block editing mode. One of `'disabled'`, - * `'contentOnly'`, or `'default'`. + * @param {string} clientId The block client ID, or `''` for the root container. + * @param {BlockEditingMode} mode The block editing mode. One of `'disabled'`, + * `'contentOnly'`, or `'default'`. * * @return {Object} Action object. */ diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index f33e9e55e30451..ce7802036184e5 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -39,6 +39,10 @@ export function getLastInsertedBlocksClientIds( state ) { return state?.lastBlockInserted?.clientIds; } +/** + * @typedef {import('../components/block-editing-mode').BlockEditingMode} BlockEditingMode + */ + /** * Returns the block editing mode for a given block. * @@ -64,8 +68,8 @@ export function getLastInsertedBlocksClientIds( state ) { * @param {Object} state Global application state. * @param {string} clientId The block client ID, or `''` for the root container. * - * @return {string} The block editing mode. One of `'disabled'`, - * `'contentOnly'`, or `'default'`. + * @return {BlockEditingMode} The block editing mode. One of `'disabled'`, + * `'contentOnly'`, or `'default'`. */ export const getBlockEditingMode = createRegistrySelector( ( select ) => From 3d437a49228b1934841a98c991c8bec7a4c6c412 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 23 May 2023 12:42:43 +1000 Subject: [PATCH 11/16] Restore packages/components/package.json from trunk --- packages/components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/package.json b/packages/components/package.json index b4e9713bbd74c0..0339100948a019 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -86,4 +86,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file From 25f3e5828db82bdb8ca9443c9562cf9fbec0838c Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 23 May 2023 12:45:43 +1000 Subject: [PATCH 12/16] Restore packages/block-library/src/post-title/edit.js from trunk --- packages/block-library/src/post-title/edit.js | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js index 3b54c58f838ea0..8cd71881e06dec 100644 --- a/packages/block-library/src/post-title/edit.js +++ b/packages/block-library/src/post-title/edit.js @@ -12,7 +12,6 @@ import { InspectorControls, useBlockProps, PlainText, - privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { ToggleControl, TextControl, PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -24,9 +23,6 @@ import { useEntityProp } from '@wordpress/core-data'; */ import HeadingLevelDropdown from '../heading/heading-level-dropdown'; import { useCanEditEntity } from '../utils/hooks'; -import { unlock } from '../private-apis'; - -const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); export default function PostTitleEdit( { attributes: { level, textAlign, isLink, rel, linkTarget }, @@ -62,7 +58,6 @@ export default function PostTitleEdit( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); - const blockEditingMode = useBlockEditingMode(); let titleElement = ( { __( 'Post Title' ) } @@ -119,22 +114,20 @@ export default function PostTitleEdit( { return ( <> - { blockEditingMode === 'default' && ( - - - setAttributes( { level: newLevel } ) - } - /> - { - setAttributes( { textAlign: nextAlign } ); - } } - /> - - ) } + + + setAttributes( { level: newLevel } ) + } + /> + { + setAttributes( { textAlign: nextAlign } ); + } } + /> + Date: Tue, 23 May 2023 14:52:40 +1000 Subject: [PATCH 13/16] Move BlockListBlockContext out of block.js so that it exists on mobile platforms --- .../src/components/block-editing-mode/index.js | 2 +- .../components/block-list/block-list-block-context.js | 6 ++++++ .../block-editor/src/components/block-list/block.js | 10 ++-------- .../src/components/block-list/use-block-props/index.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 packages/block-editor/src/components/block-list/block-list-block-context.js diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js index a9bb85197619bf..0347a9b7378d0e 100644 --- a/packages/block-editor/src/components/block-editing-mode/index.js +++ b/packages/block-editor/src/components/block-editing-mode/index.js @@ -9,7 +9,7 @@ import { useContext, useEffect } from '@wordpress/element'; */ import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; -import { BlockListBlockContext } from '../block-list/block'; +import { BlockListBlockContext } from '../block-list/block-list-block-context'; /** * @typedef {'disabled'|'contentOnly'|'default'} BlockEditingMode diff --git a/packages/block-editor/src/components/block-list/block-list-block-context.js b/packages/block-editor/src/components/block-list/block-list-block-context.js new file mode 100644 index 00000000000000..6fa09c6969ec59 --- /dev/null +++ b/packages/block-editor/src/components/block-list/block-list-block-context.js @@ -0,0 +1,6 @@ +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +export const BlockListBlockContext = createContext( null ); diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 9304840eef614b..aa1a40ef480579 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -6,12 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { - createContext, - useMemo, - useCallback, - RawHTML, -} from '@wordpress/element'; +import { useMemo, useCallback, RawHTML } from '@wordpress/element'; import { getBlockType, getSaveContent, @@ -43,8 +38,7 @@ import { useBlockProps } from './use-block-props'; import { store as blockEditorStore } from '../../store'; import { useLayout } from './layout'; import { unlock } from '../../lock-unlock'; - -export const BlockListBlockContext = createContext(); +import { BlockListBlockContext } from './block-list-block-context'; /** * Merges wrapper props with special handling for classNames and styles. diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js index ca6bb4355f52db..acc2bd7f510ede 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/index.js +++ b/packages/block-editor/src/components/block-list/use-block-props/index.js @@ -21,7 +21,7 @@ import warning from '@wordpress/warning'; * Internal dependencies */ import useMovingAnimation from '../../use-moving-animation'; -import { BlockListBlockContext } from '../block'; +import { BlockListBlockContext } from '../block-list-block-context'; import { useFocusFirstElement } from './use-focus-first-element'; import { useIsHovered } from './use-is-hovered'; import { useBlockEditContext } from '../../block-edit/context'; From 79a2bdf6e302ef9b2e9bd14bf0dffe233ddddfd3 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Wed, 24 May 2023 10:18:45 +1000 Subject: [PATCH 14/16] DRY up blockEditingMode check --- packages/block-library/src/image/image.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index d06ee479efe946..90850e4253b357 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -156,9 +156,10 @@ export default function Image( { const [ isEditingImage, setIsEditingImage ] = useState( false ); const [ externalBlob, setExternalBlob ] = useState(); const clientWidth = useClientWidth( containerRef, [ align ] ); + const hasNonContentControls = blockEditingMode === 'default'; const isResizable = allowResize && - blockEditingMode === 'default' && + hasNonContentControls && ! ( isWideAligned && isLargeViewport ); const imageSizeOptions = imageSizes .filter( @@ -327,13 +328,13 @@ export default function Image( { const controls = ( <> - { blockEditingMode === 'default' && ( + { hasNonContentControls && ( ) } - { blockEditingMode === 'default' && ( + { hasNonContentControls && ( { setShowCaption( ! showCaption ); From 758b2deedf7ea12808eebda9d95de3c55896eae3 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Wed, 24 May 2023 10:19:24 +1000 Subject: [PATCH 15/16] Fix typo --- packages/block-editor/src/store/test/private-selectors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index 0dcb89f5363c1c..954c8c94c13799 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -138,7 +138,7 @@ describe( 'private selectors', () => { ).toBe( mode ); } ); - it( `should return ${ mode } if overriden by a parent`, () => { + it( `should return ${ mode } if overridden by a parent`, () => { const state = { ...baseState, blockEditingModes: new Map( [ From 7892260d49e836c47c55e9a2b2034247649bfeb8 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Wed, 24 May 2023 10:20:56 +1000 Subject: [PATCH 16/16] Remove unnecessary comment --- .../src/components/block-list/use-in-between-inserter.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index 2c1d8736ceefdb..5081951696d461 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -76,8 +76,6 @@ export function useInBetweenInserter() { rootClientId = blockElement.getAttribute( 'data-block' ); } - // Don't show the in-between inserter if the container block is - // locked or disabled. if ( getTemplateLock( rootClientId ) || getBlockEditingMode( rootClientId ) === 'disabled'