From 87cffb4d6d7a9f3f169ba3d929de394094bb140a Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 17 Jun 2026 12:22:35 +0900 Subject: [PATCH 1/3] Grid overlays: Use canvas iframe window for viewport visibility detection Pass the block's canvas iframe window to useBlockVisibility in the grid visualizer and grid child overlays so viewport detection matches the actual block rendering context. Previously these overlays relied only on the deviceType override and read the main editor window, so the grid visualizer and resize handles could disagree with the block's actual hidden state when the canvas was resized (device preview or manual drag). This aligns both overlays with the block.js / use-block-props pattern of passing both deviceType and view. Co-Authored-By: Claude --- packages/block-editor/src/hooks/grid-visualizer.js | 8 ++++++++ packages/block-editor/src/hooks/layout-child.js | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/block-editor/src/hooks/grid-visualizer.js b/packages/block-editor/src/hooks/grid-visualizer.js index 3ef3be958841af..384782f7f30546 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'; @@ -69,9 +70,16 @@ function GridTools( { clientId, layout } ) { [ 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( { blockVisibility, deviceType, + view: canvasView, } ); return ( diff --git a/packages/block-editor/src/hooks/layout-child.js b/packages/block-editor/src/hooks/layout-child.js index 6e6f8d2cb70d1c..0dd49b56c7cc83 100644 --- a/packages/block-editor/src/hooks/layout-child.js +++ b/packages/block-editor/src/hooks/layout-child.js @@ -16,6 +16,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'; @@ -408,16 +409,24 @@ function GridTools( { [ 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: isParentBlockCurrentlyHidden } = useBlockVisibility( { blockVisibility: parentBlockVisibility, deviceType, + view: canvasView, } ); const { isBlockCurrentlyHidden: isBlockItselfCurrentlyHidden } = useBlockVisibility( { blockVisibility: blockBlockVisibility, deviceType, + view: canvasView, } ); // Use useState() instead of useRef() so that GridItemResizer updates when ref is set. From 49ad162b6d75c11452e4cde9240eaa718430f9ba Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 19 Jun 2026 18:24:58 +0900 Subject: [PATCH 2/3] Grid overlays: Hide visualizer when an ancestor of the grid is hidden When a child block inside a grid is selected, layout-child.js renders the grid visualizer for the parent grid. It only checked the parent grid's own viewport visibility, not whether any ancestor of the grid was hidden, so the visualizer kept showing for grids nested under a hidden ancestor. Mirror the ancestor check already used in grid-visualizer.js via isBlockParentHiddenAtViewport so the visualizer is hidden in that case too. Co-Authored-By: Claude --- .../block-editor/src/hooks/layout-child.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/hooks/layout-child.js b/packages/block-editor/src/hooks/layout-child.js index 0dd49b56c7cc83..0d427e06d120d3 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 { @@ -366,6 +367,7 @@ function GridTools( { blockBlockVisibility, deviceType, isChildBlockAGrid, + isAnyAncestorHidden, } = useSelect( ( select ) => { const { @@ -388,9 +390,16 @@ function GridTools( { }; } + const { isBlockParentHiddenAtViewport } = unlock( + select( blockEditorStore ) + ); + const parentAttributes = getBlockAttributes( _rootClientId ); const blockAttributes = getBlockAttributes( clientId ); const settings = getSettings(); + const currentDeviceType = + settings?.[ deviceTypeKey ]?.toLowerCase() || + BLOCK_VISIBILITY_VIEWPORTS.desktop.key; return { rootClientId: _rootClientId, @@ -399,11 +408,13 @@ 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', + isAnyAncestorHidden: isBlockParentHiddenAtViewport( + _rootClientId, + currentDeviceType + ), }; }, [ clientId ] @@ -434,7 +445,7 @@ function GridTools( { const childGridClientId = isChildBlockAGrid ? clientId : undefined; - if ( ! isVisible || isParentBlockCurrentlyHidden ) { + if ( ! isVisible || isParentBlockCurrentlyHidden || isAnyAncestorHidden ) { return null; } From 7d09d9e2cc33e315a0e3a2c5fb19535467f1c504 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 19 Jun 2026 18:38:17 +0900 Subject: [PATCH 3/3] Grid overlays: Detect ancestor visibility using the canvas viewport The ancestor-hidden check used the device type, but blocks are hidden based on the viewport actually detected from the canvas iframe. In Desktop mode with a narrow canvas the two diverge, so the visualizer/resize handles kept showing for a grid (or its child) nested under an ancestor hidden at the real viewport. Move the isAnyAncestorHidden check into a selector keyed on the canvas currentViewport from useBlockVisibility, so it stays consistent with how blocks are actually hidden. Applies to both the grid-selected and child-selected paths. Co-Authored-By: Claude --- .../block-editor/src/hooks/grid-visualizer.js | 98 ++++++++++--------- .../block-editor/src/hooks/layout-child.js | 42 +++++--- 2 files changed, 79 insertions(+), 61 deletions(-) diff --git a/packages/block-editor/src/hooks/grid-visualizer.js b/packages/block-editor/src/hooks/grid-visualizer.js index 384782f7f30546..9fe6d666355e45 100644 --- a/packages/block-editor/src/hooks/grid-visualizer.js +++ b/packages/block-editor/src/hooks/grid-visualizer.js @@ -21,54 +21,45 @@ 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 }; - } - - const { isBlockParentHiddenAtViewport } = unlock( - 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 }; + } - 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, - isAnyAncestorHidden: isBlockParentHiddenAtViewport( - clientId, - currentDeviceType - ), - }; - }, - [ clientId ] - ); + return { + isVisible: true, + blockVisibility: attributes?.metadata?.blockVisibility, + deviceType: currentDeviceType, + }; + }, + [ clientId ] + ); // Get the block's DOM element to derive the canvas iframe window, // so viewport detection matches the actual block rendering context @@ -76,12 +67,27 @@ function GridTools( { clientId, layout } ) { 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 0d427e06d120d3..39601d65e86cdb 100644 --- a/packages/block-editor/src/hooks/layout-child.js +++ b/packages/block-editor/src/hooks/layout-child.js @@ -367,7 +367,6 @@ function GridTools( { blockBlockVisibility, deviceType, isChildBlockAGrid, - isAnyAncestorHidden, } = useSelect( ( select ) => { const { @@ -390,10 +389,6 @@ function GridTools( { }; } - const { isBlockParentHiddenAtViewport } = unlock( - select( blockEditorStore ) - ); - const parentAttributes = getBlockAttributes( _rootClientId ); const blockAttributes = getBlockAttributes( clientId ); const settings = getSettings(); @@ -411,10 +406,6 @@ function GridTools( { deviceType: currentDeviceType, // Check if the selected child block is itself a grid. isChildBlockAGrid: blockAttributes?.layout?.type === 'grid', - isAnyAncestorHidden: isBlockParentHiddenAtViewport( - _rootClientId, - currentDeviceType - ), }; }, [ clientId ] @@ -426,12 +417,33 @@ function GridTools( { const rawCanvasView = blockElement?.ownerDocument?.defaultView; const canvasView = rawCanvasView === null ? undefined : rawCanvasView; - const { isBlockCurrentlyHidden: isParentBlockCurrentlyHidden } = - useBlockVisibility( { - blockVisibility: parentBlockVisibility, - deviceType, - view: canvasView, - } ); + 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( {