From ea4ff7a5777d51ddaf0c20a2177eab2fc579973b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 27 Sep 2024 11:16:47 +0100 Subject: [PATCH 1/9] Edit Mode: Prevent editable text selection on first click --- packages/block-editor/src/store/reducer.js | 5 +-- packages/block-editor/src/store/selectors.js | 37 ++++++-------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index edae9c392c37de..e48dd68c39aba2 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -2250,8 +2250,6 @@ function getDerivedBlockEditingModesForTree( isNavMode = false, treeClientId = '' ) { - const isZoomedOut = - state?.zoomLevel < 100 || state?.zoomLevel === 'auto-scaled'; const derivedBlockEditingModes = new Map(); // When there are sections, the majority of blocks are disabled, @@ -2267,7 +2265,7 @@ function getDerivedBlockEditingModesForTree( traverseBlockTree( state, treeClientId, ( block ) => { const { clientId, name: blockName } = block; - if ( isZoomedOut || isNavMode ) { + if ( isNavMode ) { // If the root block is the section root set its editing mode to contentOnly. if ( clientId === sectionRootClientId ) { derivedBlockEditingModes.set( clientId, 'contentOnly' ); @@ -2289,7 +2287,6 @@ function getDerivedBlockEditingModesForTree( // disabled. // If the tree root is not in a section, set its editing mode to disabled. if ( - isZoomedOut || ! findParentInClientIdsList( state, clientId, sectionClientIds ) ) { derivedBlockEditingModes.set( clientId, 'disabled' ); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index ed9e859f028a98..8614f9266023e8 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -38,7 +38,6 @@ import { getContentLockingParent, getTemporarilyEditingAsBlocks, getTemporarilyEditingFocusModeToRevert, - getSectionRootClientId, isSectionBlock, getParentSectionBlock, isZoomOut, @@ -2941,33 +2940,18 @@ export const __unstableGetVisibleBlocks = createSelector( ); export function __unstableHasActiveBlockOverlayActive( state, clientId ) { - // Prevent overlay on blocks with a non-default editing mode. If the mdoe is - // 'disabled' then the overlay is redundant since the block can't be - // selected. If the mode is 'contentOnly' then the overlay is redundant - // since there will be no controls to interact with once selected. - if ( getBlockEditingMode( state, clientId ) !== 'default' ) { - return false; - } - // If the block editing is locked, the block overlay is always active. if ( ! canEditBlock( state, clientId ) ) { return true; } - // In zoom-out mode, the block overlay is always active for section level blocks. - if ( isZoomOut( state ) ) { - const sectionRootClientId = getSectionRootClientId( state ); - if ( sectionRootClientId ) { - const sectionClientIds = getBlockOrder( - state, - sectionRootClientId - ); - if ( sectionClientIds?.includes( clientId ) ) { - return true; - } - } else if ( clientId && ! getBlockRootClientId( state, clientId ) ) { - return true; - } + // Section blocks need to be selected first before being able to select their children. + if ( + isSectionBlock( state, clientId ) && + ! isBlockSelected( state, clientId ) && + ! hasSelectedInnerBlock( state, clientId, true ) + ) { + return true; } // In navigation mode, the block overlay is active when the block is not @@ -3043,14 +3027,15 @@ export const getBlockEditingMode = createRegistrySelector( clientId = ''; } - const isNavMode = isNavigationMode( state ); + const isNavModeLike = + isNavigationMode( state ) || isZoomOut( state ); // If the editor is currently not in navigation mode, check if the clientId // has an editing mode set in the regular derived map. // There may be an editing mode set here for synced patterns or in zoomed out // mode. if ( - ! isNavMode && + ! isNavModeLike && state.derivedBlockEditingModes?.has( clientId ) ) { return state.derivedBlockEditingModes.get( clientId ); @@ -3059,7 +3044,7 @@ export const getBlockEditingMode = createRegistrySelector( // If the editor *is* in navigation mode, the block editing mode states // are stored in the derivedNavModeBlockEditingModes map. if ( - isNavMode && + isNavModeLike && state.derivedNavModeBlockEditingModes?.has( clientId ) ) { return state.derivedNavModeBlockEditingModes.get( clientId ); From 4e218fbae9c792c512e29af874750e2635e72cfd Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 27 Sep 2024 12:27:56 +0100 Subject: [PATCH 2/9] Show zoom-out toolbar even when child blocks are selected --- packages/block-editor/src/components/block-tools/index.js | 1 - packages/block-editor/src/store/private-selectors.js | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index e6c49af6b61061..63e1f85d04dd1f 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -63,7 +63,6 @@ export default function BlockTools( { } ) { const { clientId, hasFixedToolbar, isTyping, isZoomOutMode, isDragging } = useSelect( selector, [] ); - const isMatch = useShortcutEventMatch(); const { getBlocksByClientId, diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index c46778d889b3e0..5f2a8cd3cf38b4 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -15,8 +15,8 @@ import { getBlockName, getTemplateLock, getClientIdsWithDescendants, - isNavigationMode, getBlockRootClientId, + __unstableGetEditorMode, } from './selectors'; import { checkAllowListRecursive, @@ -507,7 +507,10 @@ export function isSectionBlock( state, clientId ) { return ( getBlockName( state, clientId ) === 'core/block' || getTemplateLock( state, clientId ) === 'contentOnly' || - ( isNavigationMode( state ) && sectionClientIds.includes( clientId ) ) + ( [ 'navigation', 'zoom-out' ].includes( + __unstableGetEditorMode( state ) + ) && + sectionClientIds.includes( clientId ) ) ); } From 377fb1185f17ed52b9380071c43bd63800fdab67 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 21 Oct 2024 11:23:52 +0100 Subject: [PATCH 3/9] Show slots in zoom-out --- packages/block-editor/src/components/block-toolbar/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index 083d77a694a7b2..dd31abc71d65ab 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -70,7 +70,6 @@ export function PrivateBlockToolbar( { hasParentPattern, hasContentOnlyLocking, showShuffleButton, - showSlots, showGroupButtons, showLockButtons, showSwitchSectionStyleButton, @@ -148,7 +147,6 @@ export function PrivateBlockToolbar( { hasParentPattern: _hasParentPattern, hasContentOnlyLocking: _hasTemplateLock, showShuffleButton: _isZoomOut, - showSlots: ! _isZoomOut, showGroupButtons: ! _isZoomOut, showLockButtons: ! _isZoomOut, showSwitchSectionStyleButton: _isZoomOut, @@ -239,7 +237,7 @@ export function PrivateBlockToolbar( { { showSwitchSectionStyleButton && ( ) } - { shouldShowVisualToolbar && showSlots && ( + { shouldShowVisualToolbar && ( <> Date: Mon, 21 Oct 2024 14:21:01 +0100 Subject: [PATCH 4/9] Fix sections --- packages/block-editor/src/store/private-selectors.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 5f2a8cd3cf38b4..32308ac45bf867 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -507,9 +507,8 @@ export function isSectionBlock( state, clientId ) { return ( getBlockName( state, clientId ) === 'core/block' || getTemplateLock( state, clientId ) === 'contentOnly' || - ( [ 'navigation', 'zoom-out' ].includes( - __unstableGetEditorMode( state ) - ) && + ( ( __unstableGetEditorMode( state ) === 'navigation' || + isZoomOut( state ) ) && sectionClientIds.includes( clientId ) ) ); } From 8647351aefdf4f09b6ed6e1a643aa3ea5547bbbe Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 22 Oct 2024 13:28:09 +0100 Subject: [PATCH 5/9] Fix content only lock behavior --- .../src/components/inner-blocks/index.js | 6 ---- .../components/inner-blocks/index.native.js | 7 ++-- .../use-nested-settings-update.js | 11 ++----- .../src/store/private-selectors.js | 6 +++- packages/block-editor/src/store/selectors.js | 14 ++++++-- .../block-editor/src/store/test/selectors.js | 33 ++++++++++++++++--- .../editor/various/content-only-lock.spec.js | 16 +++++++++ 7 files changed, 66 insertions(+), 27 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index ae587720278200..1684f2c1bc33f6 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -73,13 +73,11 @@ function UncontrolledInnerBlocks( props ) { layout, name, blockType, - parentLock, defaultLayout, } = props; useNestedSettingsUpdate( clientId, - parentLock, allowedBlocks, prioritizedInserterBlocks, defaultBlock, @@ -196,7 +194,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { const { getBlockName, isZoomOut, - getTemplateLock, getBlockRootClientId, getBlockEditingMode, getBlockSettings, @@ -239,7 +236,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { ), name: blockName, blockType: getBlockType( blockName ), - parentLock: getTemplateLock( parentClientId ), parentClientId, isDropZoneDisabled: _isDropZoneDisabled, defaultLayout, @@ -251,7 +247,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { __experimentalCaptureToolbars, name, blockType, - parentLock, parentClientId, isDropZoneDisabled, defaultLayout, @@ -278,7 +273,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { layout, name, blockType, - parentLock, defaultLayout, ...options, }; diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js index 1398a5abd51e4b..5717ac2c96a1e2 100644 --- a/packages/block-editor/src/components/inner-blocks/index.native.js +++ b/packages/block-editor/src/components/inner-blocks/index.native.js @@ -105,13 +105,11 @@ function UncontrolledInnerBlocks( props ) { const context = useBlockContext( clientId ); - const { nestingLevel, parentLock } = useSelect( + const { nestingLevel } = useSelect( ( select ) => { - const { getBlockParents, getTemplateLock, getBlockRootClientId } = - select( blockEditorStore ); + const { getBlockParents } = select( blockEditorStore ); return { nestingLevel: getBlockParents( clientId )?.length, - parentLock: getTemplateLock( getBlockRootClientId( clientId ) ), }; }, [ clientId ] @@ -119,7 +117,6 @@ function UncontrolledInnerBlocks( props ) { useNestedSettingsUpdate( clientId, - parentLock, allowedBlocks, prioritizedInserterBlocks, defaultBlock, diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js index bc07a5a1829e2b..773c9f70cd23a7 100644 --- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js +++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js @@ -41,7 +41,6 @@ function useShallowMemo( value ) { * came from props. * * @param {string} clientId The client ID of the block to update. - * @param {string} parentLock * @param {string[]} allowedBlocks An array of block names which are permitted * in inner blocks. * @param {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}. @@ -63,7 +62,6 @@ function useShallowMemo( value ) { */ export default function useNestedSettingsUpdate( clientId, - parentLock, allowedBlocks, prioritizedInserterBlocks, defaultBlock, @@ -90,16 +88,11 @@ export default function useNestedSettingsUpdate( prioritizedInserterBlocks ); - const _templateLock = - templateLock === undefined || parentLock === 'contentOnly' - ? parentLock - : templateLock; - useLayoutEffect( () => { const newSettings = { allowedBlocks: _allowedBlocks, prioritizedInserterBlocks: _prioritizedInserterBlocks, - templateLock: _templateLock, + templateLock, }; // These values are not defined for RN, so only include them if they @@ -176,7 +169,7 @@ export default function useNestedSettingsUpdate( clientId, _allowedBlocks, _prioritizedInserterBlocks, - _templateLock, + templateLock, defaultBlock, directInsert, __experimentalDefaultBlock, diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 32308ac45bf867..f811e5d1a92da3 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -17,6 +17,7 @@ import { getClientIdsWithDescendants, getBlockRootClientId, __unstableGetEditorMode, + getBlockListSettings, } from './selectors'; import { checkAllowListRecursive, @@ -506,7 +507,10 @@ export function isSectionBlock( state, clientId ) { const sectionClientIds = getBlockOrder( state, sectionRootClientId ); return ( getBlockName( state, clientId ) === 'core/block' || - getTemplateLock( state, clientId ) === 'contentOnly' || + // This is different than getTemplateLock + // because children of sections are not sections automatically. + getBlockListSettings( state, clientId )?.templateLock === + 'contentOnly' || ( ( __unstableGetEditorMode( state ) === 'navigation' || isZoomOut( state ) ) && sectionClientIds.includes( clientId ) ) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 8614f9266023e8..6f9aa8aa36aac5 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1571,7 +1571,17 @@ export function getTemplateLock( state, rootClientId ) { return state.settings.templateLock ?? false; } - return getBlockListSettings( state, rootClientId )?.templateLock ?? false; + const currentLock = getBlockListSettings( + state, + rootClientId + )?.templateLock; + if ( currentLock !== undefined ) { + return currentLock; + } + return getTemplateLock( + state, + getBlockRootClientId( state, rootClientId ) + ); } /** @@ -2954,7 +2964,7 @@ export function __unstableHasActiveBlockOverlayActive( state, clientId ) { return true; } - // In navigation mode, the block overlay is active when the block is not + // For sections, the block overlay is active when the block is not // selected (and doesn't contain a selected child). The same behavior is // also enabled in all modes for blocks that have controlled children // (reusable block, template part, navigation), unless explicitly disabled diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 51949bfd468ca8..9f7875b60119eb 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -3828,28 +3828,45 @@ describe( 'selectors', () => { it( 'should return false if the specified clientId was not found', () => { const state = { - settings: { templateLock: 'all' }, + settings: {}, blockListSettings: { chicken: { templateLock: 'insert', }, + ribs: {}, + }, + blocks: { + parents: new Map( + Object.entries( { + chicken: '', + ribs: '', + } ) + ), }, }; expect( getTemplateLock( state, 'ribs' ) ).toBe( false ); } ); - it( 'should return false if template lock was not set on the specified block', () => { + it( 'should return the parent lock if the specified clientId was not found', () => { const state = { settings: { templateLock: 'all' }, blockListSettings: { chicken: { - test: 'tes1t', + templateLock: 'insert', }, }, + blocks: { + parents: new Map( + Object.entries( { + chicken: '', + ribs: '', + } ) + ), + }, }; - expect( getTemplateLock( state, 'chicken' ) ).toBe( false ); + expect( getTemplateLock( state, 'ribs' ) ).toBe( 'all' ); } ); it( 'should return the template lock for the specified clientId', () => { @@ -3860,6 +3877,14 @@ describe( 'selectors', () => { templateLock: 'insert', }, }, + blocks: { + parents: new Map( + Object.entries( { + chicken: '', + ribs: '', + } ) + ), + }, }; expect( getTemplateLock( state, 'chicken' ) ).toBe( 'insert' ); diff --git a/test/e2e/specs/editor/various/content-only-lock.spec.js b/test/e2e/specs/editor/various/content-only-lock.spec.js index 9784aea1ee068f..03f70b6c8d270f 100644 --- a/test/e2e/specs/editor/various/content-only-lock.spec.js +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -24,6 +24,13 @@ test.describe( 'Content-only lock', () => { ` ); await pageUtils.pressKeys( 'secondary+M' ); + + // First click selects the section. + await editor.canvas + .locator( 'role=document[name="Block: Group"i]' ) + .click(); + + // Second click selects the content. await editor.canvas .locator( 'role=document[name="Block: Paragraph"i]' ) .click(); @@ -50,9 +57,18 @@ test.describe( 'Content-only lock', () => { ` ); await pageUtils.pressKeys( 'secondary+M' ); + + // First click selects the section. + await editor.canvas + .locator( 'role=document[name="Block: Group"i]' ) + .first() + .click(); + + // Second click selects the content. await editor.canvas .locator( 'role=document[name="Block: Paragraph"i]' ) .click(); + await page.keyboard.type( ' WP' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { From b189ab9f2543ee261adc443734ce21a98c9886ae Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 23 Oct 2024 09:53:15 +0100 Subject: [PATCH 6/9] Fix inserter position --- .../block-tools/zoom-out-mode-inserters.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js index 17af902bf9baf2..d9c409098de65a 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserters.js @@ -15,24 +15,29 @@ import { unlock } from '../../lock-unlock'; function ZoomOutModeInserters() { const [ isReady, setIsReady ] = useState( false ); const { - hasSelection, + selectedSectionClientId, blockOrder, setInserterIsOpened, sectionRootClientId, - selectedBlockClientId, } = useSelect( ( select ) => { const { getSettings, getBlockOrder, - getSelectionStart, getSelectedBlockClientId, getSectionRootClientId, + isSectionBlock, + getParentSectionBlock, } = unlock( select( blockEditorStore ) ); const root = getSectionRootClientId(); + const selectionBlockClientId = getSelectedBlockClientId(); + const _selectedSectionClientId = + ! selectionBlockClientId || isSectionBlock( selectionBlockClientId ) + ? selectionBlockClientId + : getParentSectionBlock( selectionBlockClientId ); return { - hasSelection: !! getSelectionStart().clientId, + selectedSectionClientId: _selectedSectionClientId, blockOrder: getBlockOrder( root ), sectionRootClientId: root, setInserterIsOpened: @@ -54,13 +59,13 @@ function ZoomOutModeInserters() { }; }, [] ); - if ( ! isReady || ! hasSelection ) { + if ( ! isReady || ! selectedSectionClientId ) { return null; } - const previousClientId = selectedBlockClientId; + const previousClientId = selectedSectionClientId; const index = blockOrder.findIndex( - ( clientId ) => selectedBlockClientId === clientId + ( clientId ) => selectedSectionClientId === clientId ); const nextClientId = blockOrder[ index + 1 ]; From 82096921d55d1d10f589a12bf5b8a0be381ed029 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 23 Oct 2024 10:14:24 +0100 Subject: [PATCH 7/9] Remove useless change --- packages/block-editor/src/components/block-tools/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 63e1f85d04dd1f..e6c49af6b61061 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -63,6 +63,7 @@ export default function BlockTools( { } ) { const { clientId, hasFixedToolbar, isTyping, isZoomOutMode, isDragging } = useSelect( selector, [] ); + const isMatch = useShortcutEventMatch(); const { getBlocksByClientId, From 683c073863c64387df2d636722310efc02313beb Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 24 Oct 2024 06:32:31 +0100 Subject: [PATCH 8/9] Fix section root case --- packages/block-editor/src/store/selectors.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 6f9aa8aa36aac5..e0e583882f8e74 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2976,7 +2976,8 @@ export function __unstableHasActiveBlockOverlayActive( state, clientId ) { ); const shouldEnableIfUnselected = blockSupportDisable ? false - : areInnerBlocksControlled( state, clientId ); + : areInnerBlocksControlled( state, clientId ) && + clientId !== getSectionRootClientId( state, clientId ); return ( shouldEnableIfUnselected && From b6d3e52e31f5d86439cc92ca37d7b3e3559afc0c Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 11 Dec 2024 11:37:37 +0100 Subject: [PATCH 9/9] Fix post rebase --- packages/block-editor/src/store/selectors.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index e0e583882f8e74..cd8dadb72947bc 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -41,6 +41,7 @@ import { isSectionBlock, getParentSectionBlock, isZoomOut, + getSectionRootClientId, } from './private-selectors'; /**