diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index af09de8fe94bbd..df5cf3864e4e24 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -36,7 +36,6 @@ import { DEFAULT_BLOCK_EDIT_CONTEXT, } from '../block-edit/context'; import { useTypingObserver } from '../observe-typing'; -import { ZoomOutSeparator } from './zoom-out-separator'; import { unlock } from '../../lock-unlock'; export const IntersectionObserver = createContext(); @@ -168,76 +167,68 @@ function Items( { // function on every render. const hasAppender = CustomAppender !== false; const hasCustomAppender = !! CustomAppender; - const { - order, - isZoomOut, - selectedBlocks, - visibleBlocks, - shouldRenderAppender, - } = useSelect( - ( select ) => { - const { - getSettings, - getBlockOrder, - getSelectedBlockClientIds, - __unstableGetVisibleBlocks, - getTemplateLock, - getBlockEditingMode, - isSectionBlock, - isContainerInsertableToInContentOnlyMode, - getBlockName, - isZoomOut: _isZoomOut, - canInsertBlockType, - } = unlock( select( blockEditorStore ) ); + const { order, selectedBlocks, visibleBlocks, shouldRenderAppender } = + useSelect( + ( select ) => { + const { + getSettings, + getBlockOrder, + getSelectedBlockClientIds, + __unstableGetVisibleBlocks, + getTemplateLock, + getBlockEditingMode, + isSectionBlock, + isContainerInsertableToInContentOnlyMode, + getBlockName, + canInsertBlockType, + } = unlock( select( blockEditorStore ) ); - const _order = getBlockOrder( rootClientId ); + const _order = getBlockOrder( rootClientId ); + + if ( getSettings().isPreviewMode ) { + return { + order: _order, + selectedBlocks: EMPTY_ARRAY, + visibleBlocks: EMPTY_SET, + }; + } + + const selectedBlockClientIds = getSelectedBlockClientIds(); + const selectedBlockClientId = selectedBlockClientIds[ 0 ]; + const showRootAppender = + ! rootClientId && + ! selectedBlockClientId && + ( ! _order.length || + ! canInsertBlockType( + getDefaultBlockName(), + rootClientId + ) ); + const hasSelectedRoot = !! ( + rootClientId && + selectedBlockClientId && + rootClientId === selectedBlockClientId + ); - if ( getSettings().isPreviewMode ) { return { order: _order, - selectedBlocks: EMPTY_ARRAY, - visibleBlocks: EMPTY_SET, + selectedBlocks: selectedBlockClientIds, + visibleBlocks: __unstableGetVisibleBlocks(), + shouldRenderAppender: + ( ! isSectionBlock( rootClientId ) || + isContainerInsertableToInContentOnlyMode( + getBlockName( selectedBlockClientId ), + rootClientId + ) ) && + getBlockEditingMode( rootClientId ) !== 'disabled' && + ! getTemplateLock( rootClientId ) && + hasAppender && + ( hasCustomAppender || + hasSelectedRoot || + showRootAppender ), }; - } - - const selectedBlockClientIds = getSelectedBlockClientIds(); - const selectedBlockClientId = selectedBlockClientIds[ 0 ]; - const showRootAppender = - ! rootClientId && - ! selectedBlockClientId && - ( ! _order.length || - ! canInsertBlockType( - getDefaultBlockName(), - rootClientId - ) ); - const hasSelectedRoot = !! ( - rootClientId && - selectedBlockClientId && - rootClientId === selectedBlockClientId - ); - - return { - order: _order, - selectedBlocks: selectedBlockClientIds, - visibleBlocks: __unstableGetVisibleBlocks(), - isZoomOut: _isZoomOut(), - shouldRenderAppender: - ( ! isSectionBlock( rootClientId ) || - isContainerInsertableToInContentOnlyMode( - getBlockName( selectedBlockClientId ), - rootClientId - ) ) && - getBlockEditingMode( rootClientId ) !== 'disabled' && - ! getTemplateLock( rootClientId ) && - hasAppender && - ! _isZoomOut() && - ( hasCustomAppender || - hasSelectedRoot || - showRootAppender ), - }; - }, - [ rootClientId, hasAppender, hasCustomAppender ] - ); + }, + [ rootClientId, hasAppender, hasCustomAppender ] + ); return ( @@ -251,24 +242,10 @@ function Items( { ! selectedBlocks.includes( clientId ) } > - { isZoomOut && ( - - ) } - { isZoomOut && ( - - ) } ) ) } { order.length < 1 && placeholder } diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js index 26f7cca2990d89..f655d16b23cbb6 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js @@ -29,17 +29,13 @@ import { unlock } from '../../../lock-unlock'; */ export function useFocusFirstElement( { clientId, initialPosition } ) { const ref = useRef(); - const { isBlockSelected, isMultiSelecting, isZoomOut } = unlock( + const { isBlockSelected, isMultiSelecting } = unlock( useSelect( blockEditorStore ) ); useEffect( () => { // Check if the block is still selected at the time this effect runs. - if ( - ! isBlockSelected( clientId ) || - isMultiSelecting() || - isZoomOut() - ) { + if ( ! isBlockSelected( clientId ) || isMultiSelecting() ) { return; } 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 71eda2558f7930..32c5b0231729e7 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 @@ -17,8 +17,7 @@ export function useInBetweenInserter() { const openRef = useContext( InsertionPointOpenRef ); const isInBetweenInserterDisabled = useSelect( ( select ) => - select( blockEditorStore ).getSettings().isDistractionFree || - unlock( select( blockEditorStore ) ).isZoomOut(), + select( blockEditorStore ).getSettings().isDistractionFree, [] ); const { diff --git a/packages/block-editor/src/components/block-popover/cover.js b/packages/block-editor/src/components/block-popover/cover.js index 401431defe4fd5..f661086cce750c 100644 --- a/packages/block-editor/src/components/block-popover/cover.js +++ b/packages/block-editor/src/components/block-popover/cover.js @@ -51,13 +51,22 @@ function CoverContainer( { additionalStyles = {}, children, } ) { - const [ width, setWidth ] = useState( selectedElement.offsetWidth ); - const [ height, setHeight ] = useState( selectedElement.offsetHeight ); + const [ width, setWidth ] = useState( () => { + const rect = selectedElement.getBoundingClientRect(); + return rect.width; + } ); + const [ height, setHeight ] = useState( () => { + const rect = selectedElement.getBoundingClientRect(); + return rect.height; + } ); useEffect( () => { const observer = new window.ResizeObserver( () => { - setWidth( selectedElement.offsetWidth ); - setHeight( selectedElement.offsetHeight ); + // Use getBoundingClientRect to get correct sizes when the + // editor is zoomed in or out, accounting for any scaling. + const rect = selectedElement.getBoundingClientRect(); + setWidth( rect.width ); + setHeight( rect.height ); } ); observer.observe( selectedElement, { box: 'border-box' } ); return () => observer.disconnect(); diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index 3db84d56ff227a..46269c60890d10 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -88,7 +88,6 @@ export function BlockSettingsDropdown( { selectedBlockClientIds, openedBlockSettingsMenu, isContentOnly, - isZoomOut, } = useSelect( ( select ) => { const { @@ -99,7 +98,6 @@ export function BlockSettingsDropdown( { getBlockAttributes, getOpenedBlockSettingsMenu, getBlockEditingMode, - isZoomOut: _isZoomOut, } = unlock( select( blockEditorStore ) ); const { getActiveBlockVariation } = select( blocksStore ); @@ -124,7 +122,6 @@ export function BlockSettingsDropdown( { openedBlockSettingsMenu: getOpenedBlockSettingsMenu(), isContentOnly: getBlockEditingMode( firstBlockClientId ) === 'contentOnly', - isZoomOut: _isZoomOut(), }; }, [ firstBlockClientId ] @@ -308,7 +305,7 @@ export function BlockSettingsDropdown( { { __( 'Duplicate' ) } ) } - { canInsertBlock && ! isZoomOut && ( + { canInsertBlock && ( <> - { ! isTyping && ! isZoomOutMode && ( + { ! isTyping && ( @@ -276,7 +270,7 @@ export default function BlockTools( { ) } { /* Used for the inline rich text toolbar. Until this toolbar is combined into BlockToolbar, someone implementing their own BlockToolbar will also need to use this to see the image caption toolbar. */ } - { ! isZoomOutMode && ! hasFixedToolbar && ( + { ! hasFixedToolbar && ( - { isZoomOutMode && ! isDragging && ( - - ) } ); diff --git a/packages/block-editor/src/components/list-view/appender.js b/packages/block-editor/src/components/list-view/appender.js index fb1f08ec8a250c..23db2e116c568f 100644 --- a/packages/block-editor/src/components/list-view/appender.js +++ b/packages/block-editor/src/components/list-view/appender.js @@ -24,13 +24,13 @@ export const Appender = forwardRef( const instanceId = useInstanceId( Appender ); const { directInsert, hideInserter } = useSelect( ( select ) => { - const { getBlockListSettings, getTemplateLock, isZoomOut } = - unlock( select( blockEditorStore ) ); + const { getBlockListSettings, getTemplateLock } = unlock( + select( blockEditorStore ) + ); const settings = getBlockListSettings( clientId ); const directInsertValue = settings?.directInsert || false; - const hideInserterValue = - !! getTemplateLock( clientId ) || isZoomOut(); + const hideInserterValue = !! getTemplateLock( clientId ); return { directInsert: directInsertValue, diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 998d13cfd22247..bda5bd2a7f713b 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -342,6 +342,7 @@ function BlockStyleControls( { }, }, }; + if ( blockEditingMode !== 'default' ) { return null; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 4851d67017bbcd..d9b19988c5bb84 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -2277,14 +2277,8 @@ function hasBindings( block ) { * @return {Map} A Map containing the derived block editing modes, keyed by block client ID. */ function getDerivedBlockEditingModesForTree( state, treeClientId = '' ) { - const isZoomedOut = - state?.zoomLevel < 100 || state?.zoomLevel === 'auto-scaled'; const derivedBlockEditingModes = new Map(); - // When there are sections, the majority of blocks are disabled, - // so the default block editing mode is set to disabled. - const sectionRootClientId = state.settings?.[ sectionRootClientIdKey ]; - const sectionClientIds = state.blocks.order.get( sectionRootClientId ); const hasDisabledBlocks = Array.from( state.blockEditingModes ).some( ( [ , mode ] ) => mode === 'disabled' ); @@ -2362,30 +2356,6 @@ function getDerivedBlockEditingModesForTree( state, treeClientId = '' ) { } } - if ( isZoomedOut ) { - // If the root block is the section root set its editing mode to contentOnly. - if ( clientId === sectionRootClientId ) { - derivedBlockEditingModes.set( clientId, 'contentOnly' ); - return; - } - - // There are no sections, so everything else is disabled. - if ( ! sectionClientIds?.length ) { - derivedBlockEditingModes.set( clientId, 'disabled' ); - return; - } - - if ( sectionClientIds.includes( clientId ) ) { - derivedBlockEditingModes.set( clientId, 'contentOnly' ); - return; - } - - // If zoomed out, all blocks that aren't sections or the section root are - // disabled. - derivedBlockEditingModes.set( clientId, 'disabled' ); - return; - } - if ( syncedPatternClientIds.length ) { // Synced pattern blocks (core/block). if ( syncedPatternClientIds.includes( clientId ) ) { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 9b2bfc4ff8ec30..e19a104512015a 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -39,10 +39,8 @@ import { getContentLockingParent, getTemporarilyEditingAsBlocks, getTemporarilyEditingFocusModeToRevert, - getSectionRootClientId, isSectionBlock, getParentSectionBlock, - isZoomOut, isContainerInsertableToInContentOnlyMode, } from './private-selectors'; @@ -3006,22 +3004,6 @@ export function __unstableHasActiveBlockOverlayActive( 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; - } - } - // In navigation mode, 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 diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 9fe39022f41ae7..d8e64dc9ccbc51 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -3830,7 +3830,7 @@ describe( 'state', () => { expect( derivedBlockEditingModes ).toEqual( new Map() ); } ); - it( 'returns the expected block editing modes for synced patterns when switching to zoomed out mode', () => { + it( 'does not change block editing modes when switching to zoomed out mode', () => { const { derivedBlockEditingModes } = dispatchActions( [ { @@ -3842,18 +3842,15 @@ describe( 'state', () => { initialState ); + // Zoom level is decoupled from editing modes, so switching to + // zoomed out mode should not affect the derived editing modes. + // The modes should remain based on synced patterns and template locking. expect( derivedBlockEditingModes ).toEqual( new Map( Object.entries( { - '': 'contentOnly', // Section root. - 'group-1': 'contentOnly', // Section. - 'paragraph-1': 'disabled', - 'group-2': 'disabled', - 'paragraph-2': 'disabled', - 'root-pattern': 'contentOnly', // Pattern and section. 'pattern-paragraph': 'disabled', 'pattern-group': 'disabled', - 'pattern-paragraph-with-overrides': 'disabled', + 'pattern-paragraph-with-overrides': 'contentOnly', 'nested-pattern': 'disabled', 'nested-paragraph': 'disabled', 'nested-group': 'disabled', @@ -4022,17 +4019,11 @@ describe( 'state', () => { ); } ); - it( 'returns the expected block editing modes', () => { + it( 'returns empty block editing modes since zoom does not affect editing modes', () => { + // Zoom level is decoupled from editing modes, so even when + // zoomed out with sections, no derived editing modes should be set. expect( initialState.derivedBlockEditingModes ).toEqual( - new Map( - Object.entries( { - '': 'contentOnly', // Section root. - 'group-1': 'contentOnly', // Section block. - 'paragraph-1': 'disabled', - 'group-2': 'disabled', - 'paragraph-2': 'disabled', - } ) - ) + new Map() ); } ); @@ -4048,15 +4039,7 @@ describe( 'state', () => { initialState ); - expect( derivedBlockEditingModes ).toEqual( - new Map( - Object.entries( { - '': 'contentOnly', - 'group-1': 'contentOnly', - 'paragraph-1': 'disabled', - } ) - ) - ); + expect( derivedBlockEditingModes ).toEqual( new Map() ); } ); it( 'updates block editing modes when new blocks are inserted', () => { @@ -4092,20 +4075,7 @@ describe( 'state', () => { initialState ); - expect( derivedBlockEditingModes ).toEqual( - new Map( - Object.entries( { - '': 'contentOnly', // Section root. - 'group-1': 'contentOnly', // Section block. - 'paragraph-1': 'disabled', - 'group-2': 'disabled', - 'paragraph-2': 'disabled', - 'group-3': 'contentOnly', // New section block. - 'paragraph-3': 'disabled', - 'group-4': 'disabled', - } ) - ) - ); + expect( derivedBlockEditingModes ).toEqual( new Map() ); } ); it( 'updates block editing modes when blocks are moved to a new position', () => { @@ -4121,17 +4091,9 @@ describe( 'state', () => { testReducer, initialState ); - expect( derivedBlockEditingModes ).toEqual( - new Map( - Object.entries( { - '': 'contentOnly', // Section root. - 'group-1': 'contentOnly', // Section block. - 'paragraph-1': 'disabled', - 'group-2': 'contentOnly', // New section block. - 'paragraph-2': 'disabled', - } ) - ) - ); + // Zoom level is decoupled from editing modes, so moving blocks + // should not create derived editing modes when zoom is set. + expect( derivedBlockEditingModes ).toEqual( new Map() ); } ); it( 'handles changes to the section root', () => { @@ -4148,17 +4110,9 @@ describe( 'state', () => { initialState ); - expect( derivedBlockEditingModes ).toEqual( - new Map( - Object.entries( { - '': 'disabled', - 'group-1': 'contentOnly', // New section root. - 'paragraph-1': 'contentOnly', // Section block. - 'group-2': 'contentOnly', // Section block. - 'paragraph-2': 'disabled', - } ) - ) - ); + // Zoom level is decoupled from editing modes, so changing the + // section root should not affect derived editing modes when in zoom mode. + expect( derivedBlockEditingModes ).toEqual( new Map() ); } ); } ); diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 6bf2b2349d240f..0735ce72600903 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -501,7 +501,7 @@ function Layout( { ? _templateId : null, enablePaddingAppender: - ! isZoomOut() && isRenderingPostOnly && isNotDesignPostType, + isRenderingPostOnly && isNotDesignPostType, isDevicePreview: getDeviceType() !== 'Desktop', }; }, diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index 6a968e920087c4..69b6e121e0330d 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -517,8 +517,10 @@ test.describe( 'Pattern Overrides', () => { await test.step( 'Zoomed out - pattern as a section', async () => { await page.getByLabel( 'Zoom Out' ).click(); - // In zoomed out only the pattern block is editable, - // as in this scenario it's a section. + // Zoom level is now decoupled from editing modes, so the inert + // state should be the same as when zoomed in. + // The child blocks with overrides/bindings are editable, + // and blocks without are inert. await expect( patternBlock ).not.toHaveAttribute( 'inert', 'true' @@ -528,11 +530,11 @@ test.describe( 'Pattern Overrides', () => { // to ensure the click-through behavior isn't interfering. await editor.selectBlocks( patternBlock ); - await expect( blockWithOverrides ).toHaveAttribute( + await expect( blockWithOverrides ).not.toHaveAttribute( 'inert', 'true' ); - await expect( blockWithBindings ).toHaveAttribute( + await expect( blockWithBindings ).not.toHaveAttribute( 'inert', 'true' ); @@ -548,7 +550,9 @@ test.describe( 'Pattern Overrides', () => { await editor.clickBlockOptionsMenuItem( 'Group' ); await test.step( 'Zoomed out - pattern nested in a section', async () => { - // None of the pattern is editable in zoomed out when nested in a section. + // With zoom decoupled from editing modes, the inert state depends on + // whether the blocks are synced patterns (not editable from instance). + // Since this pattern is a synced pattern, all blocks are inert. await expect( blockWithOverrides ).toHaveAttribute( 'inert', 'true' diff --git a/test/e2e/specs/site-editor/zoom-out.spec.js b/test/e2e/specs/site-editor/zoom-out.spec.js index e8b708fc80f3cd..2eff854d4defd1 100644 --- a/test/e2e/specs/site-editor/zoom-out.spec.js +++ b/test/e2e/specs/site-editor/zoom-out.spec.js @@ -234,9 +234,7 @@ test.describe( 'Zoom Out', () => { await expect( fourthSectionStart ).not.toBeInViewport(); } ); - test( 'Zoom out selected section has four items in options menu', async ( { - page, - } ) => { + test( 'Zoom out selected section has options menu', async ( { page } ) => { // open the inserter await page .getByRole( 'button', { @@ -258,13 +256,20 @@ test.describe( 'Zoom Out', () => { // open the block toolbar more settings menu await page.getByLabel( 'Block tools' ).getByLabel( 'Options' ).click(); - // get the length of the options menu + // get the options menu const optionsMenu = page .getByRole( 'menu', { name: 'Options' } ) .getByRole( 'menuitem' ); - // we expect 2 items in the options menu: Duplicate and Delete. - await expect( optionsMenu ).toHaveCount( 2 ); + // With zoom decoupled from editing modes, the pattern is now fully editable + // so more options are available in the menu. We verify that typical options like + // Duplicate and Delete are present. + await expect( + optionsMenu.filter( { hasText: 'Duplicate' } ) + ).toHaveCount( 1 ); + await expect( optionsMenu.filter( { hasText: 'Delete' } ) ).toHaveCount( + 1 + ); } ); test( 'Zoom Out cannot be activated when the section root is missing', async ( {