diff --git a/packages/block-editor/src/hooks/grid-visualizer.js b/packages/block-editor/src/hooks/grid-visualizer.js index 3ef3be958841af..9fe6d666355e45 100644 --- a/packages/block-editor/src/hooks/grid-visualizer.js +++ b/packages/block-editor/src/hooks/grid-visualizer.js @@ -11,6 +11,7 @@ import { useSelect } from '@wordpress/data'; import { GridVisualizer, useGridLayoutSync } from '../components/grid'; import { store as blockEditorStore } from '../store'; import { unlock } from '../lock-unlock'; +import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs'; import useBlockVisibility from '../components/block-visibility/use-block-visibility'; import { deviceTypeKey } from '../store/private-keys'; import { BLOCK_VISIBILITY_VIEWPORTS } from '../components/block-visibility/constants'; @@ -20,60 +21,73 @@ function GridLayoutSync( props ) { } function GridTools( { clientId, layout } ) { - const { isVisible, blockVisibility, deviceType, isAnyAncestorHidden } = - useSelect( - ( select ) => { - const { - isBlockSelected, - hasSelectedInnerBlock, - isDraggingBlocks, - getTemplateLock, - getBlockEditingMode, - getBlockAttributes, - getSettings, - } = select( blockEditorStore ); + const { isVisible, blockVisibility, deviceType } = useSelect( + ( select ) => { + const { + isBlockSelected, + hasSelectedInnerBlock, + isDraggingBlocks, + getTemplateLock, + getBlockEditingMode, + getBlockAttributes, + getSettings, + } = select( blockEditorStore ); - // These calls are purposely ordered from least expensive to most expensive. - // Hides the visualizer in cases where the user is not or cannot interact with it. - // Also hide if a child block is selected, because layout-child.js will render - // the visualizer in that case (with proper childGridClientId handling). - if ( - ( ! isDraggingBlocks() && ! isBlockSelected( clientId ) ) || - getTemplateLock( clientId ) || - getBlockEditingMode( clientId ) !== 'default' || - hasSelectedInnerBlock( clientId ) - ) { - return { isVisible: false }; - } + // These calls are purposely ordered from least expensive to most expensive. + // Hides the visualizer in cases where the user is not or cannot interact with it. + // Also hide if a child block is selected, because layout-child.js will render + // the visualizer in that case (with proper childGridClientId handling). + if ( + ( ! isDraggingBlocks() && ! isBlockSelected( clientId ) ) || + getTemplateLock( clientId ) || + getBlockEditingMode( clientId ) !== 'default' || + hasSelectedInnerBlock( clientId ) + ) { + return { isVisible: false }; + } - const { isBlockParentHiddenAtViewport } = unlock( - select( blockEditorStore ) - ); + const attributes = getBlockAttributes( clientId ); + const settings = getSettings(); + const currentDeviceType = + settings?.[ deviceTypeKey ]?.toLowerCase() || + BLOCK_VISIBILITY_VIEWPORTS.desktop.key; - const attributes = getBlockAttributes( clientId ); - const settings = getSettings(); - const currentDeviceType = - settings?.[ deviceTypeKey ]?.toLowerCase() || - BLOCK_VISIBILITY_VIEWPORTS.desktop.key; + return { + isVisible: true, + blockVisibility: attributes?.metadata?.blockVisibility, + deviceType: currentDeviceType, + }; + }, + [ clientId ] + ); - return { - isVisible: true, - blockVisibility: attributes?.metadata?.blockVisibility, - deviceType: currentDeviceType, - isAnyAncestorHidden: isBlockParentHiddenAtViewport( - clientId, - currentDeviceType - ), - }; - }, - [ clientId ] - ); + // Get the block's DOM element to derive the canvas iframe window, + // so viewport detection matches the actual block rendering context + const blockElement = useBlockElement( clientId ); + const rawCanvasView = blockElement?.ownerDocument?.defaultView; + const canvasView = rawCanvasView === null ? undefined : rawCanvasView; - const { isBlockCurrentlyHidden } = useBlockVisibility( { + const { isBlockCurrentlyHidden, currentViewport } = useBlockVisibility( { blockVisibility, deviceType, + view: canvasView, } ); + // Check whether any ancestor is hidden at the viewport actually detected + // from the canvas, so it stays consistent with how blocks are hidden. + const isAnyAncestorHidden = useSelect( + ( select ) => { + if ( ! isVisible ) { + return false; + } + const { isBlockParentHiddenAtViewport } = unlock( + select( blockEditorStore ) + ); + return isBlockParentHiddenAtViewport( clientId, currentViewport ); + }, + [ clientId, currentViewport, isVisible ] + ); + return ( <> diff --git a/packages/block-editor/src/hooks/layout-child.js b/packages/block-editor/src/hooks/layout-child.js index 6e6f8d2cb70d1c..39601d65e86cdb 100644 --- a/packages/block-editor/src/hooks/layout-child.js +++ b/packages/block-editor/src/hooks/layout-child.js @@ -9,6 +9,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import { store as blockEditorStore } from '../store'; +import { unlock } from '../lock-unlock'; import { useStyleOverride } from './utils'; import { useLayout } from '../components/block-list/layout'; import { @@ -16,6 +17,7 @@ import { GridItemResizer, GridItemMovers, } from '../components/grid'; +import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs'; import useBlockVisibility from '../components/block-visibility/use-block-visibility'; import { deviceTypeKey } from '../store/private-keys'; import { BLOCK_VISIBILITY_VIEWPORTS } from '../components/block-visibility/constants'; @@ -390,6 +392,9 @@ function GridTools( { const parentAttributes = getBlockAttributes( _rootClientId ); const blockAttributes = getBlockAttributes( clientId ); const settings = getSettings(); + const currentDeviceType = + settings?.[ deviceTypeKey ]?.toLowerCase() || + BLOCK_VISIBILITY_VIEWPORTS.desktop.key; return { rootClientId: _rootClientId, @@ -398,9 +403,7 @@ function GridTools( { parentAttributes?.metadata?.blockVisibility, blockBlockVisibility: blockAttributes?.metadata?.blockVisibility, - deviceType: - settings?.[ deviceTypeKey ]?.toLowerCase() || - BLOCK_VISIBILITY_VIEWPORTS.desktop.key, + deviceType: currentDeviceType, // Check if the selected child block is itself a grid. isChildBlockAGrid: blockAttributes?.layout?.type === 'grid', }; @@ -408,16 +411,45 @@ function GridTools( { [ clientId ] ); - const { isBlockCurrentlyHidden: isParentBlockCurrentlyHidden } = - useBlockVisibility( { - blockVisibility: parentBlockVisibility, - deviceType, - } ); + // Get the block's DOM element to derive the canvas iframe window, + // so viewport detection matches the actual block rendering context + const blockElement = useBlockElement( clientId ); + const rawCanvasView = blockElement?.ownerDocument?.defaultView; + const canvasView = rawCanvasView === null ? undefined : rawCanvasView; + + const { + isBlockCurrentlyHidden: isParentBlockCurrentlyHidden, + currentViewport, + } = useBlockVisibility( { + blockVisibility: parentBlockVisibility, + deviceType, + view: canvasView, + } ); + + // Check whether any ancestor of the parent grid is hidden at the viewport + // actually detected from the canvas, so it stays consistent with how + // blocks are hidden. + const isAnyAncestorHidden = useSelect( + ( select ) => { + if ( ! rootClientId ) { + return false; + } + const { isBlockParentHiddenAtViewport } = unlock( + select( blockEditorStore ) + ); + return isBlockParentHiddenAtViewport( + rootClientId, + currentViewport + ); + }, + [ rootClientId, currentViewport ] + ); const { isBlockCurrentlyHidden: isBlockItselfCurrentlyHidden } = useBlockVisibility( { blockVisibility: blockBlockVisibility, deviceType, + view: canvasView, } ); // Use useState() instead of useRef() so that GridItemResizer updates when ref is set. @@ -425,7 +457,7 @@ function GridTools( { const childGridClientId = isChildBlockAGrid ? clientId : undefined; - if ( ! isVisible || isParentBlockCurrentlyHidden ) { + if ( ! isVisible || isParentBlockCurrentlyHidden || isAnyAncestorHidden ) { return null; }