diff --git a/packages/block-editor/src/components/grid/use-grid-layout-sync.js b/packages/block-editor/src/components/grid/use-grid-layout-sync.js
index a5d6aa4d3fbdd7..ed368714d63d34 100644
--- a/packages/block-editor/src/components/grid/use-grid-layout-sync.js
+++ b/packages/block-editor/src/components/grid/use-grid-layout-sync.js
@@ -2,7 +2,8 @@
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
+import { useEffect, useMemo } from '@wordpress/element';
+import { usePrevious } from '@wordpress/compose';
/**
* Internal dependencies
@@ -11,13 +12,15 @@ import { store as blockEditorStore } from '../../store';
import { GridRect } from './utils';
export function useGridLayoutSync( { clientId: gridClientId } ) {
- const { gridLayout, blockOrder } = useSelect(
+ const { gridLayout, blockOrder, selectedBlockLayout } = useSelect(
( select ) => {
const { getBlockAttributes, getBlockOrder } =
select( blockEditorStore );
+ const selectedBlock = select( blockEditorStore ).getSelectedBlock();
return {
gridLayout: getBlockAttributes( gridClientId ).layout ?? {},
blockOrder: getBlockOrder( gridClientId ),
+ selectedBlockLayout: selectedBlock?.attributes.style?.layout,
};
},
[ gridClientId ]
@@ -27,27 +30,32 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
const { updateBlockAttributes, __unstableMarkNextChangeAsNotPersistent } =
useDispatch( blockEditorStore );
+ const selectedBlockRect = useMemo(
+ () =>
+ selectedBlockLayout ? new GridRect( selectedBlockLayout ) : null,
+ [ selectedBlockLayout ]
+ );
+
+ const previouslySelectedBlockRect = usePrevious( selectedBlockRect );
+
useEffect( () => {
const updates = {};
- const { columnCount, rowCount, isManualPlacement } = gridLayout;
-
- if ( isManualPlacement ) {
- const rects = [];
+ if ( gridLayout.isManualPlacement ) {
+ const occupiedRects = [];
// Respect the position of blocks that already have a columnStart and rowStart value.
for ( const clientId of blockOrder ) {
- const attributes = getBlockAttributes( clientId );
const {
columnStart,
rowStart,
columnSpan = 1,
rowSpan = 1,
- } = attributes.style?.layout || {};
+ } = getBlockAttributes( clientId ).style?.layout ?? {};
if ( ! columnStart || ! rowStart ) {
continue;
}
- rects.push(
+ occupiedRects.push(
new GridRect( {
columnStart,
rowStart,
@@ -65,17 +73,19 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
rowStart,
columnSpan = 1,
rowSpan = 1,
- } = attributes.style?.layout || {};
+ } = attributes.style?.layout ?? {};
if ( columnStart && rowStart ) {
continue;
}
- const [ newColumnStart, newRowStart ] = getFirstEmptyCell(
- rects,
- columnCount,
+ const [ newColumnStart, newRowStart ] = placeBlock(
+ occupiedRects,
+ gridLayout.columnCount,
columnSpan,
- rowSpan
+ rowSpan,
+ previouslySelectedBlockRect?.columnEnd,
+ previouslySelectedBlockRect?.rowEnd
);
- rects.push(
+ occupiedRects.push(
new GridRect( {
columnStart: newColumnStart,
rowStart: newRowStart,
@@ -96,8 +106,13 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
}
// Ensure there's enough rows to fit all blocks.
- const bottomMostRow = Math.max( ...rects.map( ( r ) => r.rowEnd ) );
- if ( ! rowCount || rowCount < bottomMostRow ) {
+ const bottomMostRow = Math.max(
+ ...occupiedRects.map( ( r ) => r.rowEnd )
+ );
+ if (
+ ! gridLayout.rowCount ||
+ gridLayout.rowCount < bottomMostRow
+ ) {
updates[ gridClientId ] = {
layout: {
...gridLayout,
@@ -110,7 +125,7 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
for ( const clientId of blockOrder ) {
const attributes = getBlockAttributes( clientId );
const { columnStart, rowStart, ...layout } =
- attributes.style?.layout || {};
+ attributes.style?.layout ?? {};
// Only update attributes if columnStart or rowStart are set.
if ( columnStart || rowStart ) {
updates[ clientId ] = {
@@ -123,7 +138,7 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
}
// Remove row styles in auto mode
- if ( rowCount ) {
+ if ( gridLayout.rowCount ) {
updates[ gridClientId ] = {
layout: {
...gridLayout,
@@ -146,23 +161,47 @@ export function useGridLayoutSync( { clientId: gridClientId } ) {
gridClientId,
gridLayout,
blockOrder,
- // Needed for linter:
+ previouslySelectedBlockRect,
+ // These won't change, but the linter thinks they might:
__unstableMarkNextChangeAsNotPersistent,
getBlockAttributes,
updateBlockAttributes,
] );
}
-function getFirstEmptyCell( rects, columnCount, columnSpan = 1, rowSpan = 1 ) {
- for ( let row = 1; ; row++ ) {
- for ( let column = 1; column <= columnCount; column++ ) {
- const rect = new GridRect( {
+/**
+ * @param {GridRect[]} occupiedRects
+ * @param {number} gridColumnCount
+ * @param {number} blockColumnSpan
+ * @param {number} blockRowSpan
+ * @param {number?} startColumn
+ * @param {number?} startRow
+ */
+function placeBlock(
+ occupiedRects,
+ gridColumnCount,
+ blockColumnSpan,
+ blockRowSpan,
+ startColumn = 1,
+ startRow = 1
+) {
+ for ( let row = startRow; ; row++ ) {
+ for (
+ let column = row === startRow ? startColumn : 1;
+ column <= gridColumnCount;
+ column++
+ ) {
+ const candidateRect = new GridRect( {
columnStart: column,
rowStart: row,
- columnSpan,
- rowSpan,
+ columnSpan: blockColumnSpan,
+ rowSpan: blockRowSpan,
} );
- if ( ! rects.some( ( r ) => r.intersectsRect( rect ) ) ) {
+ if (
+ ! occupiedRects.some( ( r ) =>
+ r.intersectsRect( candidateRect )
+ )
+ ) {
return [ column, row ];
}
}
diff --git a/packages/block-editor/src/hooks/grid-visualizer.js b/packages/block-editor/src/hooks/grid-visualizer.js
index 42b45952d45d15..ffad5edd9842c4 100644
--- a/packages/block-editor/src/hooks/grid-visualizer.js
+++ b/packages/block-editor/src/hooks/grid-visualizer.js
@@ -26,14 +26,12 @@ function GridTools( { clientId, layout } ) {
};
} );
- if ( ! isSelected && ! isDragging ) {
- return null;
- }
-
return (
<>
-
+ { ( isSelected || isDragging ) && (
+
+ ) }
>
);
}