From 846c1ff8b2c6a683b401865866c213e05c03a25e Mon Sep 17 00:00:00 2001 From: Federico Ruggi <1081051+ruggi@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:53:02 +0200 Subject: [PATCH] Freeze grid template during child move (#6201) **Problem:** Moving grid elements if the template is somewhat dynamic will result in the grid elements likely jumping around because their template changes constantly. **Fix:** Freeze the template during the interaction, by using the calculated dimensions, and restore it once the interaction completes. In the example below, one column is made `min-content` with some fixed-sized elements inside. | Before | After | |-------|-------------| | ![Kapture 2024-08-07 at 15 37 27](https://github.com/user-attachments/assets/da2d824a-ad02-494a-8ff6-7cc03e3b5c95) | ![Kapture 2024-08-07 at 15 36 21](https://github.com/user-attachments/assets/283a5fc0-69b9-41e3-85d2-51be14279585) | Fixes #6200 --- .../grid-rearrange-move-strategy.ts | 118 ++++++++++++++++-- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-strategy.ts index d0ce57d61f8c..3bb79670c579 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-strategy.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-strategy.ts @@ -1,5 +1,18 @@ +import type { ElementPath } from 'utopia-shared/src/types' import { MetadataUtils } from '../../../../core/model/element-metadata-utils' import * as EP from '../../../../core/shared/element-path' +import type { + ElementInstanceMetadataMap, + GridAutoOrTemplateBase, +} from '../../../../core/shared/element-template' +import * as PP from '../../../../core/shared/property-path' +import { printGridAutoOrTemplateBase } from '../../../inspector/common/css-utils' +import type { PropertyToUpdate } from '../../commands/set-property-command' +import { + propertyToDelete, + propertyToSet, + updateBulkProperties, +} from '../../commands/set-property-command' import { GridControls } from '../../controls/grid-controls' import type { CanvasStrategyFactory } from '../canvas-strategies' import { onlyFitWhenDraggingThisControl } from '../canvas-strategies' @@ -34,6 +47,13 @@ export const gridRearrangeMoveStrategy: CanvasStrategyFactory = ( return null } + const gridPath = EP.parentPath(selectedElement) + + const initialTemplates = getGridTemplates(canvasState.startingMetadata, gridPath) + if (initialTemplates == null) { + return null + } + return { id: 'rearrange-grid-move-strategy', name: 'Rearrange Grid (Move)', @@ -83,14 +103,98 @@ export const gridRearrangeMoveStrategy: CanvasStrategyFactory = ( return emptyStrategyApplicationResult } - return strategyApplicationResult(moveCommands, { - grid: { - targetCell: targetGridCell, - draggingFromCell: draggingFromCell, - originalRootCell: originalRootCell, - currentRootCell: targetRootCell, + const midInteractionCommands = [ + // during the interaction, freeze the template with the calculated values… + updateBulkProperties('mid-interaction', gridPath, [ + propertyToSet( + PP.create('style', 'gridTemplateColumns'), + printGridAutoOrTemplateBase(initialTemplates.calculated.columns), + ), + propertyToSet( + PP.create('style', 'gridTemplateRows'), + printGridAutoOrTemplateBase(initialTemplates.calculated.rows), + ), + ]), + ] + + const onCompleteCommands = [ + // …eventually, restore the grid template on complete. + updateBulkProperties( + 'on-complete', + gridPath, + restoreGridTemplateFromProps(initialTemplates.fromProps), + ), + ] + + return strategyApplicationResult( + [...moveCommands, ...midInteractionCommands, ...onCompleteCommands], + { + grid: { + targetCell: targetGridCell, + draggingFromCell: draggingFromCell, + originalRootCell: originalRootCell, + currentRootCell: targetRootCell, + }, }, - }) + ) + }, + } +} + +function restoreGridTemplateFromProps(params: { + columns: GridAutoOrTemplateBase + rows: GridAutoOrTemplateBase +}): PropertyToUpdate[] { + let properties: PropertyToUpdate[] = [] + const newCols = printGridAutoOrTemplateBase(params.columns) + const newRows = printGridAutoOrTemplateBase(params.rows) + if (newCols === '') { + properties.push(propertyToDelete(PP.create('style', 'gridTemplateColumns'))) + } else { + properties.push(propertyToSet(PP.create('style', 'gridTemplateColumns'), newCols)) + } + if (newRows === '') { + properties.push(propertyToDelete(PP.create('style', 'gridTemplateRows'))) + } else { + properties.push(propertyToSet(PP.create('style', 'gridTemplateRows'), newRows)) + } + return properties +} + +function getGridTemplates(jsxMetadata: ElementInstanceMetadataMap, gridPath: ElementPath) { + const grid = MetadataUtils.findElementByElementPath(jsxMetadata, gridPath) + if (grid == null) { + return null + } + + const templateFromProps = grid.specialSizeMeasurements.containerGridPropertiesFromProps + const templateRowsFromProps = templateFromProps.gridTemplateRows + if (templateRowsFromProps == null) { + return null + } + const templateColsFromProps = templateFromProps.gridTemplateColumns + if (templateColsFromProps == null) { + return null + } + + const templateCalculated = grid.specialSizeMeasurements.containerGridProperties + const templateRowsCalculated = templateCalculated.gridTemplateRows + if (templateRowsCalculated == null) { + return null + } + const templateColsCalculated = templateCalculated.gridTemplateColumns + if (templateColsCalculated == null) { + return null + } + + return { + calculated: { + columns: templateColsCalculated, + rows: templateRowsCalculated, + }, + fromProps: { + columns: templateColsFromProps, + rows: templateRowsFromProps, }, } }