From 8314461da14aab5cdf4eaa1ffbb9e0da10f43ca7 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:29:32 +0200 Subject: [PATCH] Grid reparent uses rearrange helper (#6505) **Description:** Grid reparent uses the grid rearrange helper to set the grid specific properties. When you reparent into a grid container, moving the element to the target cell, setting the grid control properties to support animation, etc. is really similar to grid rearrange. Despite that, the two strategies did not share code. I modified the grid reparent strategy to call runGridRearrangeMove. This automatically added animation to grid reparent, and fixed some jumping around. **Commit Details:** (< vv pls delete this section if's not relevant) - `runGridRearrangeMove` has a new parameter for the grid metadata (before that it could assume that the grid is parent of the selected element, but during reparent this is not true) - some of the `runGridRearrangeMove` which handles multicell elements is not needed for grid reparent, I needed to handle that (e.g. during grid reparent we always put the element into a single target cell, so no need to handle the original size in cells, the offset between the dragged cell and root cell, etc) - Removed `getGridPositioningCommands` which is replaced by `runGridRearrangeMove` **Manual Tests:** I hereby swear that: - [x] I opened a hydrogen project and it loaded - [x] I could navigate to various routes in Play mode --- .../strategies/grid-helpers.ts | 119 +++++++++++------- .../grid-rearrange-move-duplicate-strategy.ts | 9 ++ .../grid-rearrange-move-strategy.ts | 34 ++--- .../strategies/grid-reparent-strategy.tsx | 64 ++-------- 4 files changed, 106 insertions(+), 120 deletions(-) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts b/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts index 4af8c8ee8ee8..bacc17c6ec2a 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts @@ -51,17 +51,13 @@ export function runGridRearrangeMove( selectedElement: ElementPath, jsxMetadata: ElementInstanceMetadataMap, interactionData: DragInteractionData, + grid: ElementInstanceMetadata, ): CanvasCommand[] { if (interactionData.drag == null) { return [] } - const parentGridPath = EP.parentPath(selectedElement) - const grid = MetadataUtils.findElementByElementPath(jsxMetadata, parentGridPath) - - if (grid == null) { - return [] - } + const isReparent = !EP.isParentOf(grid.elementPath, selectedElement) const { gridCellGlobalFrames, containerGridProperties: gridTemplate } = grid.specialSizeMeasurements @@ -76,39 +72,25 @@ export function runGridRearrangeMove( return [] } - const draggingFromCellCoords = getClosestGridCellToPoint( - gridCellGlobalFrames, - interactionData.dragStart, - )?.gridCellCoordinates - if (draggingFromCellCoords == null) { + const originalElementGridConfiguration = isReparent + ? { + originalElementMetadata: null, + originalCellBounds: { width: 1, height: 1 }, //when reparenting, we just put it in a single cell + mouseCellPosInOriginalElement: { row: 0, column: 0 }, + } + : getOriginalElementGridConfiguration( + gridCellGlobalFrames, + interactionData, + jsxMetadata, + selectedElement, + grid, + ) + if (originalElementGridConfiguration == null) { return [] } - const originalElementMetadata = MetadataUtils.findElementByElementPath( - jsxMetadata, - selectedElement, - ) - if (originalElementMetadata == null) { - return [] - } - - // measured cell coord bounds on the canvas, this is the default when the cell position is not explicitly set - const originalElementCellCoordsOnCanvas = getGridChildCellCoordBoundsFromCanvas( - originalElementMetadata, - grid, - ) - - // get the bounds from the props, or the canvas, or just default to the cell of the starting mouse position - const originalCellBounds = getGridChildCellCoordBoundsFromProps( - originalElementMetadata, - originalElementCellCoordsOnCanvas ?? draggingFromCellCoords, - ) - - // the cell position of the mouse relative to the original element (we have to keep this offset while dragging) - const mouseCellPosInOriginalElement = getCellCoordsDelta( - draggingFromCellCoords, - originalCellBounds, - ) + const { originalElementMetadata, originalCellBounds, mouseCellPosInOriginalElement } = + originalElementGridConfiguration // get the new adjusted row const row = targetCellCoords.row - mouseCellPosInOriginalElement.row @@ -135,7 +117,7 @@ export function runGridRearrangeMove( }) // The siblings of the grid element being moved - const siblings = MetadataUtils.getChildrenUnordered(jsxMetadata, EP.parentPath(selectedElement)) + const siblings = MetadataUtils.getChildrenUnordered(jsxMetadata, grid.elementPath) .filter((s) => !EP.pathsEqual(s.elementPath, selectedElement)) .map( (s, index): SortableGridElementProperties => ({ @@ -165,15 +147,18 @@ export function runGridRearrangeMove( EP.pathsEqual(selectedElement, s.path), ) - const moveType = getGridMoveType({ - originalElementMetadata: originalElementMetadata, - possiblyReorderIndex: possiblyReorderIndex, - cellsSortedByPosition: cellsSortedByPosition, - }) + const moveType = + originalElementMetadata == null + ? 'rearrange' + : getGridMoveType({ + originalElementMetadata: originalElementMetadata, + possiblyReorderIndex: possiblyReorderIndex, + cellsSortedByPosition: cellsSortedByPosition, + }) const updateGridControlsCommand = showGridControls( 'mid-interaction', - parentGridPath, + grid.elementPath, targetCellData?.gridCellCoordinates ?? null, gridCellCoordinates(row, column), ) @@ -768,3 +753,51 @@ export function getMetadataWithGridCellBounds( customStrategyState: null, } } + +function getOriginalElementGridConfiguration( + gridCellGlobalFrames: GridCellGlobalFrames, + interactionData: DragInteractionData, + jsxMetadata: ElementInstanceMetadataMap, + selectedElement: ElementPath, + grid: ElementInstanceMetadata, +) { + const draggingFromCellCoords = getClosestGridCellToPoint( + gridCellGlobalFrames, + interactionData.dragStart, + )?.gridCellCoordinates + if (draggingFromCellCoords == null) { + return null + } + + const originalElementMetadata = MetadataUtils.findElementByElementPath( + jsxMetadata, + selectedElement, + ) + if (originalElementMetadata == null) { + return null + } + + // measured cell coord bounds on the canvas, this is the default when the cell position is not explicitly set + const originalElementCellCoordsOnCanvas = getGridChildCellCoordBoundsFromCanvas( + originalElementMetadata, + grid, + ) + + // get the bounds from the props, or the canvas, or just default to the cell of the starting mouse position + const originalCellBounds = getGridChildCellCoordBoundsFromProps( + originalElementMetadata, + originalElementCellCoordsOnCanvas ?? draggingFromCellCoords, + ) + + // the cell position of the mouse relative to the original element (we have to keep this offset while dragging) + const mouseCellPosInOriginalElement = getCellCoordsDelta( + draggingFromCellCoords, + originalCellBounds, + ) + + return { + originalElementMetadata, + originalCellBounds, + mouseCellPosInOriginalElement, + } +} diff --git a/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-duplicate-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-duplicate-strategy.ts index 039583c1077c..18e54ec97035 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-duplicate-strategy.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-rearrange-move-duplicate-strategy.ts @@ -72,11 +72,20 @@ export const gridRearrangeMoveDuplicateStrategy: CanvasStrategyFactory = ( const targetElement = EP.appendToPath(EP.parentPath(selectedElement), newUid) + const grid = MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + EP.parentPath(selectedElement), + ) + if (grid == null) { + return emptyStrategyApplicationResult + } + const moveCommands = runGridRearrangeMove( targetElement, selectedElement, canvasState.startingMetadata, interactionSession.interactionData, + grid, ) if (moveCommands.length === 0) { return emptyStrategyApplicationResult 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 d79b46d21b00..01692a94abed 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 @@ -16,13 +16,7 @@ import { } from '../../commands/set-property-command' import type { CanvasStrategyFactory } from '../canvas-strategies' import { onlyFitWhenDraggingThisControl } from '../canvas-strategies' -import type { - ControlWithProps, - CustomStrategyState, - CustomStrategyStatePatch, - InteractionCanvasState, - InteractionLifecycle, -} from '../canvas-strategy-types' +import type { ControlWithProps, InteractionCanvasState } from '../canvas-strategy-types' import { getTargetPathsFromInteractionTarget, emptyStrategyApplicationResult, @@ -30,20 +24,12 @@ import { } from '../canvas-strategy-types' import type { DragInteractionData, InteractionSession } from '../interaction-state' import { runGridRearrangeMove } from './grid-helpers' -import type { CanvasRectangle } from '../../../../core/shared/math-utils' -import { isInfinityRectangle, offsetPoint } from '../../../../core/shared/math-utils' -import { findReparentStrategies } from './reparent-helpers/reparent-strategy-helpers' -import { applyAbsoluteReparent, controlsForAbsoluteReparent } from './absolute-reparent-strategy' +import { isInfinityRectangle } from '../../../../core/shared/math-utils' import type { CanvasCommand } from '../../commands/commands' -import { applyStaticReparent, controlsForStaticReparent } from './reparent-as-static-strategy' -import type { FindReparentStrategyResult } from './reparent-helpers/reparent-strategy-parent-lookup' -import { applyGridReparent, controlsForGridReparent } from './grid-reparent-strategy' -import { assertNever } from '../../../../core/shared/utils' export const gridRearrangeMoveStrategy: CanvasStrategyFactory = ( canvasState: InteractionCanvasState, interactionSession: InteractionSession | null, - customState: CustomStrategyState, ) => { const selectedElements = getTargetPathsFromInteractionTarget(canvasState.interactionTarget) if ( @@ -129,7 +115,7 @@ export const gridRearrangeMoveStrategy: CanvasStrategyFactory = ( ), ] - const { commands, patch, elementsToRerender } = getCommandsAndPatchForGridRearrange( + const { commands, elementsToRerender } = getCommandsAndPatchForGridRearrange( canvasState, interactionSession.interactionData, selectedElement, @@ -142,7 +128,6 @@ export const gridRearrangeMoveStrategy: CanvasStrategyFactory = ( return strategyApplicationResult( [...midInteractionCommands, ...onCompleteCommands, ...commands], elementsToRerender, - patch, ) }, } @@ -154,11 +139,18 @@ function getCommandsAndPatchForGridRearrange( selectedElement: ElementPath, ): { commands: CanvasCommand[] - patch: CustomStrategyStatePatch elementsToRerender: ElementPath[] } { if (interactionData.drag == null) { - return { commands: [], patch: {}, elementsToRerender: [] } + return { commands: [], elementsToRerender: [] } + } + + const grid = MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + EP.parentPath(selectedElement), + ) + if (grid == null) { + return { commands: [], elementsToRerender: [] } } const commands = runGridRearrangeMove( @@ -166,11 +158,11 @@ function getCommandsAndPatchForGridRearrange( selectedElement, canvasState.startingMetadata, interactionData, + grid, ) return { commands: commands, - patch: {}, elementsToRerender: [EP.parentPath(selectedElement)], } } diff --git a/editor/src/components/canvas/canvas-strategies/strategies/grid-reparent-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/grid-reparent-strategy.tsx index f98ee00f7680..419b1633ca7a 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-reparent-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-reparent-strategy.tsx @@ -5,11 +5,11 @@ import { mapDropNulls } from '../../../../core/shared/array-utils' import * as EP from '../../../../core/shared/element-path' import type { ElementPathTrees } from '../../../../core/shared/element-path-tree' import { - gridPositionValue, + type ElementInstanceMetadata, type ElementInstanceMetadataMap, } from '../../../../core/shared/element-template' import type { CanvasRectangle } from '../../../../core/shared/math-utils' -import { canvasVector, isInfinityRectangle, offsetPoint } from '../../../../core/shared/math-utils' +import { isInfinityRectangle } from '../../../../core/shared/math-utils' import type { ElementPath } from '../../../../core/shared/project-file-types' import * as PP from '../../../../core/shared/property-path' import type { AllElementProps } from '../../../editor/store/editor-state' @@ -39,13 +39,12 @@ import { import type { DragInteractionData, InteractionSession, UpdatedPathMap } from '../interaction-state' import { honoursPropsPosition, shouldKeepMovingDraggedGroupChildren } from './absolute-utils' import { replaceFragmentLikePathsWithTheirChildrenRecursive } from './fragment-like-helpers' -import { getMetadataWithGridCellBounds, setGridPropsCommands } from './grid-helpers' +import { getMetadataWithGridCellBounds, runGridRearrangeMove } from './grid-helpers' import { ifAllowedToReparent, isAllowedToReparent } from './reparent-helpers/reparent-helpers' import { removeAbsolutePositioningProps } from './reparent-helpers/reparent-property-changes' import type { ReparentTarget } from './reparent-helpers/reparent-strategy-helpers' import { getReparentOutcome, pathToReparent } from './reparent-utils' import { flattenSelection } from './shared-move-strategies-helpers' -import { getClosestGridCellToPointFromMetadata, type GridCellCoordinates } from './grid-cell-bounds' export function gridReparentStrategy( reparentTarget: ReparentTarget, @@ -190,16 +189,6 @@ export function applyGridReparent( return emptyStrategyApplicationResult } - const mousePos = offsetPoint( - interactionData.dragStart, - interactionData.drag ?? canvasVector({ x: 0, y: 0 }), - ) - - const targetCellData = getClosestGridCellToPointFromMetadata(grid, mousePos) - - if (targetCellData == null) { - return strategyApplicationResult([], [newParent.intendedParentPath]) - } const outcomes = mapDropNulls( (selectedElement) => gridReparentCommands( @@ -211,7 +200,8 @@ export function applyGridReparent( nodeModules, selectedElement, newParent, - targetCellData.gridCellCoordinates, + interactionData, + grid, ), selectedElements, ) @@ -253,12 +243,6 @@ export function applyGridReparent( gridContainerCommands, updateSelectedViews('always', newPaths), setCursorCommand(CSSCursor.Reparent), - showGridControls( - 'mid-interaction', - reparentTarget.newParent.intendedParentPath, - targetCellData.gridCellCoordinates, - null, - ), ], elementsToRerender, customStrategyStatePatch, @@ -268,35 +252,6 @@ export function applyGridReparent( } } -function getGridPositioningCommands( - jsxMetadata: ElementInstanceMetadataMap, - hoveredCoordinates: GridCellCoordinates, - { - parentPath, - target, - }: { - parentPath: ElementPath - target: ElementPath - }, -) { - const containerMetadata = MetadataUtils.findElementByElementPath(jsxMetadata, parentPath) - if (containerMetadata == null) { - return null - } - const { column, row } = hoveredCoordinates - - const gridTemplate = containerMetadata.specialSizeMeasurements.containerGridProperties - - const gridPropsCommands = setGridPropsCommands(target, gridTemplate, { - gridColumnStart: gridPositionValue(column), - gridColumnEnd: gridPositionValue(column), - gridRowEnd: gridPositionValue(row), - gridRowStart: gridPositionValue(row), - }) - - return gridPropsCommands -} - function gridReparentCommands( jsxMetadata: ElementInstanceMetadataMap, tree: ElementPathTrees, @@ -306,7 +261,8 @@ function gridReparentCommands( nodeModules: NodeModules, target: ElementPath, newParent: InsertionPath, - hoveredCoordinates: GridCellCoordinates, + interactionData: DragInteractionData, + grid: ElementInstanceMetadata, ) { const reparentResult = getReparentOutcome( jsxMetadata, @@ -324,11 +280,7 @@ function gridReparentCommands( if (reparentResult == null) { return null } - - const gridPropsCommands = getGridPositioningCommands(jsxMetadata, hoveredCoordinates, { - parentPath: newParent.intendedParentPath, - target: target, - }) + const gridPropsCommands = runGridRearrangeMove(target, target, jsxMetadata, interactionData, grid) if (gridPropsCommands == null) { return null