diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 4d5f22e02fa7d..fa8e0a35ff86d 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -111,7 +111,7 @@ $z-layers: ( ".block-editor-block-popover": 31, // Below the block toolbar. - ".block-editor-grid-visualizer": 30, + ".block-editor-grid-visualizer-popover": 30, // Show snackbars above everything (similar to popovers) ".components-snackbar-list": 100000, diff --git a/packages/block-editor/src/components/grid/grid-visualizer.js b/packages/block-editor/src/components/grid/grid-visualizer.js index e1d35f012b4d8..a785fac1ee714 100644 --- a/packages/block-editor/src/components/grid/grid-visualizer.js +++ b/packages/block-editor/src/components/grid/grid-visualizer.js @@ -36,7 +36,7 @@ export function GridVisualizer( { clientId, contentRef, parentLayout } ) { parentLayout?.isManualPlacement && window.__experimentalEnableGridInteractivity; return ( - { const [ gridInfo, setGridInfo ] = useState( () => getGridInfo( gridElement ) ); - const [ isDroppingAllowed, setIsDroppingAllowed ] = useState( false ); useEffect( () => { const observers = []; @@ -68,6 +67,38 @@ const GridVisualizerGrid = forwardRef( }; }, [ gridElement ] ); + return ( + <> + + + + ); + } +); + +/** + * A popover component that renders in a slot over the grid block. + * + * This provides interactive elements of the grid visualization — + * block inserters and drop zones. + * + * @param {Object} props + * @param {string} props.gridClientId + * @param {Object} props.gridInfo + */ +const GridPopover = forwardRef( + ( { gridClientId, gridInfo, isManualGrid }, ref ) => { + const [ isDroppingAllowed, setIsDroppingAllowed ] = useState( false ); + useEffect( () => { function onGlobalDrag() { setIsDroppingAllowed( true ); @@ -85,29 +116,18 @@ const GridVisualizerGrid = forwardRef( return ( -
- { isManualGrid ? ( - + { isManualGrid && ( + - ) : ( - Array.from( { length: gridInfo.numItems }, ( _, i ) => ( - - ) ) ) }
@@ -115,13 +135,13 @@ const GridVisualizerGrid = forwardRef( } ); -function ManualGridVisualizer( { gridClientId, gridInfo } ) { +function ManualGridPopoverItems( { gridClientId, gridInfo } ) { const [ highlightedRect, setHighlightedRect ] = useState( null ); - const gridItems = useSelect( ( select ) => select( blockEditorStore ).getBlocks( gridClientId ), [ gridClientId ] ); + const occupiedRects = useMemo( () => { const rects = []; for ( const block of gridItems ) { @@ -153,11 +173,16 @@ function ManualGridVisualizer( { gridClientId, gridInfo } ) { ); const isHighlighted = highlightedRect?.contains( column, row ) ?? false; + return ( - { isCellOccupied ? ( ) } - + ); } ) ); } -function GridVisualizerCell( { color, children, className } ) { - return ( -
- { children } -
+
+ { Array.from( { length: gridInfo.numItems }, ( _, i ) => ( +
+ ) ) } +
+ ); } diff --git a/packages/block-editor/src/components/grid/style.scss b/packages/block-editor/src/components/grid/style.scss index 6790d683ca7d0..a36573e0868f7 100644 --- a/packages/block-editor/src/components/grid/style.scss +++ b/packages/block-editor/src/components/grid/style.scss @@ -1,7 +1,7 @@ -.block-editor-grid-visualizer { +.block-editor-grid-visualizer-popover { // Specificity to override the z-index and pointer-events set by .components-popover. - &.block-editor-grid-visualizer.block-editor-grid-visualizer { - z-index: z-index(".block-editor-grid-visualizer"); + &.block-editor-grid-visualizer-popover.block-editor-grid-visualizer-popover { + z-index: z-index(".block-editor-grid-visualizer-popover"); .components-popover__content * { pointer-events: none; @@ -18,11 +18,7 @@ } } -.block-editor-grid-visualizer__grid { - display: grid; -} - -.block-editor-grid-visualizer__cell { +.block-editor-grid-visualizer-popover__cell { display: grid; position: relative; @@ -37,7 +33,7 @@ overflow: hidden; .block-editor-grid-visualizer__appender { - box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 20%, #0000); + box-shadow: none; color: inherit; overflow: hidden; height: 100%; @@ -48,17 +44,26 @@ } + .block-editor-grid-visualizer__drop-zone { + opacity: 0; + } + &.is-highlighted { .block-editor-inserter, .block-editor-grid-visualizer__drop-zone { - background: var(--wp-admin-theme-color); + background: rgba(var(--wp-admin-theme-color--rgb), 0.25); + border: $border-width solid var(--wp-admin-theme-color); } } &:hover .block-editor-grid-visualizer__appender, .block-editor-grid-visualizer__appender:focus { opacity: 1; - background-color: color-mix(in srgb, currentColor 20%, #0000); + box-shadow: none; + background-color: rgba(var(--wp-admin-theme-color--rgb), 0.05); + border: $border-width dashed; + border-radius: $radius-small; + color: var(--wp-admin-theme-color); } } @@ -77,7 +82,7 @@ .block-editor-grid-item-resizer { // Specificity to override the z-index and pointer-events set by .components-popover. &.block-editor-grid-item-resizer.block-editor-grid-item-resizer { - z-index: z-index(".block-editor-grid-visualizer"); + z-index: z-index(".block-editor-grid-visualizer-popover"); .components-popover__content * { pointer-events: none; @@ -117,7 +122,7 @@ content: ""; position: absolute; display: block; - border-radius: $radius-block-ui; + border-radius: $radius-small; height: $grid-unit-40; // Position the focus rectangle. diff --git a/packages/block-editor/src/components/grid/utils.js b/packages/block-editor/src/components/grid/utils.js index fc012c645f091..117c8f92e9ba0 100644 --- a/packages/block-editor/src/components/grid/utils.js +++ b/packages/block-editor/src/components/grid/utils.js @@ -172,6 +172,7 @@ export function getGridInfo( gridElement ) { gridTemplateColumns, gridTemplateRows, gap: getComputedCSS( gridElement, 'gap' ), + display: 'grid', padding: getComputedCSS( gridElement, 'padding' ), }, }; diff --git a/packages/block-editor/src/hooks/grid-visualizer.js b/packages/block-editor/src/hooks/grid-visualizer.js index 44102208c4d1d..c71ee410f1486 100644 --- a/packages/block-editor/src/hooks/grid-visualizer.js +++ b/packages/block-editor/src/hooks/grid-visualizer.js @@ -4,6 +4,7 @@ import { createHigherOrderComponent } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { useSelect } from '@wordpress/data'; +import { createContext, useState } from '@wordpress/element'; /** * Internal dependencies @@ -11,28 +12,40 @@ import { useSelect } from '@wordpress/data'; import { GridVisualizer, useGridLayoutSync } from '../components/grid'; import { store as blockEditorStore } from '../store'; +export const GridResizerBoundsContext = createContext(); + function GridLayoutSync( props ) { useGridLayoutSync( props ); } -function GridTools( { clientId, layout } ) { - const { isSelected, isDragging } = useSelect( ( select ) => { - const { isBlockSelected, isDraggingBlocks } = +function GridTools( { clientId, layout, children } ) { + const { hasSelection, isDragging } = useSelect( ( select ) => { + const { isBlockSelected, hasSelectedInnerBlock, isDraggingBlocks } = select( blockEditorStore ); return { - isSelected: isBlockSelected( clientId ), + hasSelection: + isBlockSelected( clientId ) || + hasSelectedInnerBlock( clientId ), isDragging: isDraggingBlocks(), }; } ); + // Use useState() instead of useRef() so that GridItemResizer updates when ref is set. + const [ resizerBounds, setResizerBounds ] = useState(); + return ( - <> + - { ( isSelected || isDragging ) && ( - + { ( hasSelection || isDragging ) && ( + ) } - + { children } + ); } @@ -43,13 +56,12 @@ const addGridVisualizerToBlockEdit = createHigherOrderComponent( } return ( - <> - + - + ); }, 'addGridVisualizerToBlockEdit' diff --git a/packages/block-editor/src/hooks/layout-child.js b/packages/block-editor/src/hooks/layout-child.js index 8beb50c1b8284..19a33aaf48509 100644 --- a/packages/block-editor/src/hooks/layout-child.js +++ b/packages/block-editor/src/hooks/layout-child.js @@ -3,7 +3,7 @@ */ import { useInstanceId } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; -import { useState } from '@wordpress/element'; +import { useContext } from '@wordpress/element'; /** * Internal dependencies @@ -11,11 +11,8 @@ import { useState } from '@wordpress/element'; import { store as blockEditorStore } from '../store'; import { useStyleOverride } from './utils'; import { useLayout } from '../components/block-list/layout'; -import { - GridVisualizer, - GridItemResizer, - GridItemMovers, -} from '../components/grid'; +import { GridItemResizer, GridItemMovers } from '../components/grid'; +import { GridResizerBoundsContext } from './grid-visualizer'; function useBlockPropsChildLayoutStyles( { style } ) { const shouldRenderChildLayoutStyles = useSelect( ( select ) => { @@ -179,8 +176,7 @@ function ChildLayoutControlsPure( { clientId, style, setAttributes } ) { [ clientId ] ); - // Use useState() instead of useRef() so that GridItemResizer updates when ref is set. - const [ resizerBounds, setResizerBounds ] = useState(); + const resizerBounds = useContext( GridResizerBoundsContext ); if ( parentLayoutType !== 'grid' ) { return null; @@ -200,11 +196,6 @@ function ChildLayoutControlsPure( { clientId, style, setAttributes } ) { return ( <> - { allowSizingOnChildren && ( , // To avoid overlaps between the standard HTML attributes and the props // expected by `framer-motion`, omit all framer motion props from popover - // props (except for `animate` and `children`, which are re-defined in `PopoverProps`). - keyof Omit< MotionProps, 'animate' | 'children' > + // props (except for `animate`, `children` and `style`, which are re-defined in `PopoverProps`). + keyof Omit< MotionProps, 'animate' | 'children' | 'style' > >, forwardedRef: ForwardedRef< any > ) => { @@ -138,6 +138,7 @@ const UnforwardedPopover = ( resize = true, shift = false, inline = false, + style: contentStyle, variant, // Deprecated props @@ -370,6 +371,7 @@ const UnforwardedPopover = ( const animationProps: HTMLMotionProps< 'div' > = shouldAnimate ? { style: { + ...contentStyle, ...motionInlineStyles, ...style, }, @@ -378,7 +380,10 @@ const UnforwardedPopover = ( } : { animate: false, - style, + style: { + ...contentStyle, + ...style, + }, }; // When Floating UI has finished positioning and Framer Motion has finished animating diff --git a/packages/components/src/popover/types.ts b/packages/components/src/popover/types.ts index 427f4afb81bfb..b862d1e5d935b 100644 --- a/packages/components/src/popover/types.ts +++ b/packages/components/src/popover/types.ts @@ -160,6 +160,14 @@ export type PopoverProps = { * @default false */ inline?: boolean; + + /** + * Styles to apply to the main popover element. + * + * @default undefined + */ + style?: React.CSSProperties; + // Deprecated props /** * Prevent the popover from flipping and resizing when meeting the viewport