From 7f377c9846b2cca836b7df4c5ace3b41a2469156 Mon Sep 17 00:00:00 2001
From: Sean Parsons <217400+seanparsons@users.noreply.github.com>
Date: Fri, 25 Oct 2024 16:15:34 +0100
Subject: [PATCH] fix(canvas) Fixed handling of parent frames to get the right
positioning. (#6586)
- Split `maybeAddContainLayout` into that and another function
`shouldAddContainLayout`.
- Corrected two tests which were inserting into the second flex child
and were also offset similar to how the grid items were.
- `getAbsoluteReparentPropertyChanges` now takes a parameter which
specifies if `contain: 'layout'` will be added to the container.
- `getMoveCommandsForSelectedElement` now has an additional fallback for
`elementParentBounds` when the closest non fragment parent provides
bounds for absolute children.
- `getLocalFrame` now has an optional parameter to specify the parent to
target, to allow for handling the lookups where the element has yet to
be reparented.
---
.../strategies/absolute-reparent-strategy.tsx | 35 +++-
.../absolute-resize-bounding-box-strategy.tsx | 2 +-
.../draw-to-insert-strategy.spec.browser2.tsx | 172 +++++++++++++++++-
...lute-reparent-strategies.spec.browser2.tsx | 4 +-
.../reparent-helpers/reparent-helpers.ts | 4 +-
.../reparent-property-changes.ts | 8 +-
.../reparent-property-strategies.ts | 1 +
.../shared-move-strategies-helpers.ts | 42 ++++-
editor/src/components/canvas/canvas-utils.ts | 5 +-
.../flex-gap-control.test-utils.tsx | 1 +
.../controls/select-mode/flex-gap-control.tsx | 2 +-
.../convert-to-flex-strategy.ts | 2 +-
.../src/components/editor/actions/actions.tsx | 7 +-
.../insert-as-absolute-strategy.tsx | 1 +
...eparent-to-unwrap-as-absolute-strategy.tsx | 1 +
.../components/inspector/inspector-common.ts | 4 +-
...-updating-layout-section.spec.browser2.tsx | 1 +
.../frame-updating-layout-section.tsx | 24 ++-
.../components/text-editor/text-handling.ts | 2 +-
editor/src/core/layout/layout-utils.ts | 2 +-
.../src/core/model/element-metadata-utils.ts | 11 +-
21 files changed, 297 insertions(+), 34 deletions(-)
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/absolute-reparent-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/absolute-reparent-strategy.tsx
index 504a642b1f0d..587de432e8c3 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/absolute-reparent-strategy.tsx
+++ b/editor/src/components/canvas/canvas-strategies/strategies/absolute-reparent-strategy.tsx
@@ -37,6 +37,7 @@ import type { InteractionSession, UpdatedPathMap } from '../interaction-state'
import { absoluteMoveStrategy } from './absolute-move-strategy'
import { honoursPropsPosition, shouldKeepMovingDraggedGroupChildren } from './absolute-utils'
import { replaceFragmentLikePathsWithTheirChildrenRecursive } from './fragment-like-helpers'
+import type { ShouldAddContainLayout } from './reparent-helpers/reparent-helpers'
import { ifAllowedToReparent, isAllowedToReparent } from './reparent-helpers/reparent-helpers'
import type { ForcePins } from './reparent-helpers/reparent-property-changes'
import { getAbsoluteReparentPropertyChanges } from './reparent-helpers/reparent-property-changes'
@@ -184,6 +185,12 @@ export function applyAbsoluteReparent(
projectContents,
nodeModules,
'force-pins',
+ shouldAddContainLayout(
+ canvasState.startingMetadata,
+ canvasState.startingAllElementProps,
+ canvasState.startingElementPathTree,
+ newParent.intendedParentPath,
+ ),
),
selectedElements,
)
@@ -254,6 +261,7 @@ export function createAbsoluteReparentAndOffsetCommands(
projectContents: ProjectContentTreeRoot,
nodeModules: NodeModules,
forcePins: ForcePins,
+ willContainLayoutBeAdded: ShouldAddContainLayout,
) {
const reparentResult = getReparentOutcome(
metadata,
@@ -271,12 +279,13 @@ export function createAbsoluteReparentAndOffsetCommands(
if (reparentResult == null) {
return null
} else {
- const offsetCommands = replaceFragmentLikePathsWithTheirChildrenRecursive(
+ const replacedPaths = replaceFragmentLikePathsWithTheirChildrenRecursive(
metadata,
elementProps,
pathTree,
[target],
- ).flatMap((p) => {
+ )
+ const offsetCommands = replacedPaths.flatMap((p) => {
return getAbsoluteReparentPropertyChanges(
p,
newParent.intendedParentPath,
@@ -284,6 +293,7 @@ export function createAbsoluteReparentAndOffsetCommands(
metadata,
projectContents,
forcePins,
+ willContainLayoutBeAdded,
)
})
@@ -296,12 +306,12 @@ export function createAbsoluteReparentAndOffsetCommands(
}
}
-function maybeAddContainLayout(
+export function shouldAddContainLayout(
metadata: ElementInstanceMetadataMap,
allElementProps: AllElementProps,
pathTrees: ElementPathTrees,
path: ElementPath,
-): CanvasCommand[] {
+): ShouldAddContainLayout {
const closestNonFragmentParent = MetadataUtils.getClosestNonFragmentParent(
metadata,
allElementProps,
@@ -310,14 +320,23 @@ function maybeAddContainLayout(
)
if (EP.isStoryboardPath(closestNonFragmentParent)) {
- return []
+ return 'dont-add-contain-layout'
}
const parentProvidesBoundsForAbsoluteChildren =
MetadataUtils.findElementByElementPath(metadata, closestNonFragmentParent)
?.specialSizeMeasurements.providesBoundsForAbsoluteChildren === true
- return parentProvidesBoundsForAbsoluteChildren
- ? []
- : [setProperty('always', path, PP.create('style', 'contain'), 'layout')]
+ return parentProvidesBoundsForAbsoluteChildren ? 'dont-add-contain-layout' : 'add-contain-layout'
+}
+
+function maybeAddContainLayout(
+ metadata: ElementInstanceMetadataMap,
+ allElementProps: AllElementProps,
+ pathTrees: ElementPathTrees,
+ path: ElementPath,
+): CanvasCommand[] {
+ return shouldAddContainLayout(metadata, allElementProps, pathTrees, path) === 'add-contain-layout'
+ ? [setProperty('always', path, PP.create('style', 'contain'), 'layout')]
+ : []
}
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.tsx
index 8942da7ab335..0409ecb7a166 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.tsx
+++ b/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.tsx
@@ -482,7 +482,7 @@ function getConstrainedSizes(
constraints.right ||
constraints.width
- const localFrame = MetadataUtils.getLocalFrame(element.elementPath, jsxMetadata)
+ const localFrame = MetadataUtils.getLocalFrame(element.elementPath, jsxMetadata, null)
if (
isConstrained &&
localFrame != null &&
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/draw-to-insert-strategy.spec.browser2.tsx b/editor/src/components/canvas/canvas-strategies/strategies/draw-to-insert-strategy.spec.browser2.tsx
index 9c26194be566..07f3ec449d2c 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/draw-to-insert-strategy.spec.browser2.tsx
+++ b/editor/src/components/canvas/canvas-strategies/strategies/draw-to-insert-strategy.spec.browser2.tsx
@@ -16,9 +16,15 @@ import {
import { RightMenuTab } from '../../../editor/store/editor-state'
import { FOR_TESTS_setNextGeneratedUid } from '../../../../core/model/element-template-utils.test-utils'
import type { WindowPoint } from '../../../../core/shared/math-utils'
-import { windowPoint } from '../../../../core/shared/math-utils'
+import {
+ nullIfInfinity,
+ rectangleContainsRectangle,
+ windowPoint,
+} from '../../../../core/shared/math-utils'
import { selectComponentsForTest } from '../../../../utils/utils.test-utils'
import CanvasActions from '../../canvas-actions'
+import { MetadataUtils } from '../../../../core/model/element-metadata-utils'
+import { forceNotNull } from '../../../../core/shared/optional-utils'
function slightlyOffsetWindowPointBecauseVeryWeirdIssue(point: { x: number; y: number }) {
// FIXME when running in headless chrome, the result of getBoundingClientRect will be slightly
@@ -201,6 +207,109 @@ export var storyboard = (
)
+`
+
+ const projectWithGridContent = `import * as React from 'react'
+import { Scene, Storyboard } from 'utopia-api'
+
+export var storyboard = (
+
+
+
+
+
+)
`
it('can click to insert into a grid', async () => {
@@ -377,5 +486,66 @@ export var storyboard = (
width: '20px',
})
})
+
+ it('can draw to insert into an element in a grid', async () => {
+ const editor = await renderTestEditorWithCode(
+ projectWithGridContent,
+ 'await-first-dom-report',
+ )
+
+ await selectComponentsForTest(editor, [EP.fromString('sb/scene/grid/grid-item-8')])
+
+ await pressKey('d')
+ ensureInInsertMode(editor)
+
+ const gridItem = editor.renderedDOM.getByTestId('grid-item-8')
+ const gridBB = gridItem.getBoundingClientRect()
+
+ const target: WindowPoint = windowPoint({
+ x: gridBB.x + 5,
+ y: gridBB.y + 5,
+ })
+
+ const canvasControlsLayer = editor.renderedDOM.getByTestId(CanvasControlsContainerID)
+
+ await mouseMoveToPoint(canvasControlsLayer, target)
+ await mouseDragFromPointToPoint(canvasControlsLayer, target, {
+ x: target.x + 40,
+ y: target.y + 60,
+ })
+ await editor.getDispatchFollowUpActionsFinished()
+
+ const child = gridItem.firstChild
+ if (child == null) {
+ throw new Error('Draw to insert should be able to insert an element')
+ }
+
+ const { position, top, left, width, height } = (child as HTMLElement).style
+
+ expect({ position, top, left, width, height }).toEqual({
+ position: 'absolute',
+ left: '6px',
+ top: '6px',
+ width: '40px',
+ height: '60px',
+ })
+
+ const gridItem8Metadata =
+ editor.getEditorState().editor.jsxMetadata['sb/scene/grid/grid-item-8']
+ const gridItem8GlobalFrame = forceNotNull(
+ 'Item 8 should have a global frame.',
+ nullIfInfinity(gridItem8Metadata.globalFrame),
+ )
+ const gridItem8Children = MetadataUtils.getChildrenUnordered(
+ editor.getEditorState().editor.jsxMetadata,
+ EP.fromString('sb/scene/grid/grid-item-8'),
+ )
+ const gridItem8Child = forceNotNull('Could not find child.', gridItem8Children.at(0))
+ const gridItem8ChildGlobalFrame = forceNotNull(
+ 'Child of item 8 should have a global frame.',
+ nullIfInfinity(gridItem8Child.globalFrame),
+ )
+ expect(rectangleContainsRectangle(gridItem8GlobalFrame, gridItem8ChildGlobalFrame)).toBe(true)
+ })
})
})
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/forced-absolute-reparent-strategies.spec.browser2.tsx b/editor/src/components/canvas/canvas-strategies/strategies/forced-absolute-reparent-strategies.spec.browser2.tsx
index 7c9ec0953a84..a9712b9857f4 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/forced-absolute-reparent-strategies.spec.browser2.tsx
+++ b/editor/src/components/canvas/canvas-strategies/strategies/forced-absolute-reparent-strategies.spec.browser2.tsx
@@ -281,7 +281,7 @@ describe('Fallback Absolute Reparent Strategies', () => {
{
= [
PP.create('style', 'left'),
@@ -90,6 +91,7 @@ export function getAbsoluteReparentPropertyChanges(
newParentStartingMetadata: ElementInstanceMetadataMap,
projectContents: ProjectContentTreeRoot,
forcePins: ForcePins,
+ willContainLayoutBeAdded: ShouldAddContainLayout,
): Array
{
const element: JSXElement | null = getJSXElementFromProjectContents(target, projectContents)
@@ -108,7 +110,10 @@ export function getAbsoluteReparentPropertyChanges(
const currentParentContentBox =
MetadataUtils.getGlobalContentBoxForChildren(originalParentInstance)
- const newParentContentBox = MetadataUtils.getGlobalContentBoxForChildren(newParentInstance)
+ const newParentContentBox =
+ willContainLayoutBeAdded === 'add-contain-layout'
+ ? nullIfInfinity(newParentInstance.globalFrame)
+ : MetadataUtils.getGlobalContentBoxForChildren(newParentInstance)
if (currentParentContentBox == null || newParentContentBox == null) {
return []
@@ -349,6 +354,7 @@ export function getReparentPropertyChanges(
currentMetadata,
projectContents,
'force-pins',
+ 'dont-add-contain-layout',
)
const strategyCommands = runReparentPropertyStrategies([
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-strategies.ts b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-strategies.ts
index 39c06c04f062..526cb7d73a83 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-strategies.ts
+++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-strategies.ts
@@ -313,6 +313,7 @@ export const convertFragmentLikeChildrenToVisualSize =
metadata.currentMetadata,
projectContents,
'force-pins',
+ 'dont-add-contain-layout',
)
} else {
const directions = singleAxisAutoLayoutContainerDirections(
diff --git a/editor/src/components/canvas/canvas-strategies/strategies/shared-move-strategies-helpers.ts b/editor/src/components/canvas/canvas-strategies/strategies/shared-move-strategies-helpers.ts
index 4f93b1e1f635..7ec32d192246 100644
--- a/editor/src/components/canvas/canvas-strategies/strategies/shared-move-strategies-helpers.ts
+++ b/editor/src/components/canvas/canvas-strategies/strategies/shared-move-strategies-helpers.ts
@@ -31,6 +31,7 @@ import {
} from '../../../../core/shared/math-utils'
import type { ElementPath } from '../../../../core/shared/project-file-types'
+import type { AllElementProps } from '../../../editor/store/editor-state'
import { getJSXElementFromProjectContents } from '../../../editor/store/editor-state'
import { stylePropPathMappingFn } from '../../../inspector/common/property-path-hooks'
import { determineConstrainedDragAxis } from '../../canvas-controls-frame'
@@ -78,6 +79,7 @@ import {
} from '../../commands/set-css-length-command'
import type { ActiveFrame, ActiveFrameAction } from '../../commands/set-active-frames-command'
import { activeFrameTargetRect, setActiveFrames } from '../../commands/set-active-frames-command'
+import type { ElementPathTrees } from '../../../../core/shared/element-path-tree'
export interface MoveCommandsOptions {
ignoreLocalFrame?: boolean
@@ -227,6 +229,8 @@ function getAppropriateLocalFrame(
export function getDirectMoveCommandsForSelectedElement(
projectContents: ProjectContentTreeRoot,
startingMetadata: ElementInstanceMetadataMap,
+ startingAllElementProps: AllElementProps,
+ startingElementPathTree: ElementPathTrees,
selectedElement: ElementPath,
mappedPath: ElementPath,
leftOrTop: 'left' | 'top',
@@ -246,6 +250,8 @@ export function getDirectMoveCommandsForSelectedElement(
return getMoveCommandsForSelectedElement(
projectContents,
startingMetadata,
+ startingAllElementProps,
+ startingElementPathTree,
selectedElement,
mappedPath,
drag,
@@ -255,6 +261,8 @@ export function getDirectMoveCommandsForSelectedElement(
export function getMoveCommandsForSelectedElement(
projectContents: ProjectContentTreeRoot,
startingMetadata: ElementInstanceMetadataMap,
+ startingAllElementProps: AllElementProps,
+ startingElementPathTree: ElementPathTrees,
selectedElement: ElementPath,
mappedPath: ElementPath,
drag: CanvasVector,
@@ -273,14 +281,34 @@ export function getMoveCommandsForSelectedElement(
selectedElement,
)
+ const closestNonFragmentParent = MetadataUtils.getClosestNonFragmentParent(
+ startingMetadata,
+ startingAllElementProps,
+ startingElementPathTree,
+ EP.parentPath(mappedPath),
+ )
+ const closestNonFragmentParentMetadata = MetadataUtils.findElementByElementPath(
+ startingMetadata,
+ closestNonFragmentParent,
+ )
+ const providesBoundsForAbsoluteChildren =
+ closestNonFragmentParentMetadata?.specialSizeMeasurements.providesBoundsForAbsoluteChildren ??
+ false
+
const elementParentBounds =
- elementMetadata?.specialSizeMeasurements.coordinateSystemBounds ?? null
+ elementMetadata?.specialSizeMeasurements.coordinateSystemBounds ??
+ (providesBoundsForAbsoluteChildren
+ ? nullIfInfinity(closestNonFragmentParentMetadata?.globalFrame)
+ : null) ??
+ null
const globalFrame = nullIfInfinity(
MetadataUtils.getFrameInCanvasCoords(selectedElement, startingMetadata),
)
- const localFrame = nullIfInfinity(MetadataUtils.getLocalFrame(selectedElement, startingMetadata))
+ const localFrame = nullIfInfinity(
+ MetadataUtils.getLocalFrame(selectedElement, startingMetadata, EP.parentPath(mappedPath)),
+ )
if (element == null) {
return { commands: [], intendedBounds: [] }
@@ -330,6 +358,8 @@ export function getInteractionMoveCommandsForSelectedElement(
return getMoveCommandsForSelectedElement(
canvasState.projectContents,
canvasState.startingMetadata,
+ canvasState.startingAllElementProps,
+ canvasState.startingElementPathTree,
selectedElement,
mappedPath,
drag,
@@ -339,6 +369,8 @@ export function getInteractionMoveCommandsForSelectedElement(
export function moveInspectorStrategy(
metadata: ElementInstanceMetadataMap,
+ allElementProps: AllElementProps,
+ elementPathTree: ElementPathTrees,
selectedElementPaths: ElementPath[],
projectContents: ProjectContentTreeRoot,
movement: CanvasVector,
@@ -352,6 +384,8 @@ export function moveInspectorStrategy(
const moveCommandsResult = getMoveCommandsForSelectedElement(
projectContents,
metadata,
+ allElementProps,
+ elementPathTree,
selectedPath,
selectedPath,
movement,
@@ -368,6 +402,8 @@ export function moveInspectorStrategy(
export function directMoveInspectorStrategy(
metadata: ElementInstanceMetadataMap,
+ allElementProps: AllElementProps,
+ elementPathTree: ElementPathTrees,
selectedElementPaths: ElementPath[],
projectContents: ProjectContentTreeRoot,
leftOrTop: 'left' | 'top',
@@ -382,6 +418,8 @@ export function directMoveInspectorStrategy(
const moveCommandsResult = getDirectMoveCommandsForSelectedElement(
projectContents,
metadata,
+ allElementProps,
+ elementPathTree,
selectedPath,
selectedPath,
leftOrTop,
diff --git a/editor/src/components/canvas/canvas-utils.ts b/editor/src/components/canvas/canvas-utils.ts
index a40fa5ee34b2..2bfd96433fdc 100644
--- a/editor/src/components/canvas/canvas-utils.ts
+++ b/editor/src/components/canvas/canvas-utils.ts
@@ -356,6 +356,7 @@ export function updateFramesOfScenesAndComponents(
const localFrame = MetadataUtils.getLocalFrame(
frameAndTarget.target,
workingEditorState.jsxMetadata,
+ null,
)
const valueFromDOM = getObservableValueForLayoutProp(
elementMetadata,
@@ -439,7 +440,7 @@ export function updateFramesOfScenesAndComponents(
frameAndTarget.frame,
)
const currentLocalFrame = nullIfInfinity(
- MetadataUtils.getLocalFrame(target, workingEditorState.jsxMetadata),
+ MetadataUtils.getLocalFrame(target, workingEditorState.jsxMetadata, null),
)
const currentFullFrame = optionalMap(Frame.getFullFrame, currentLocalFrame)
const fullFrame = Frame.getFullFrame(newLocalFrame)
@@ -935,7 +936,7 @@ export function collectGuidelines(
}
const instance = MetadataUtils.findElementByElementPath(metadata, selectedView)
- const localFrame = MetadataUtils.getLocalFrame(selectedView, metadata)
+ const localFrame = MetadataUtils.getLocalFrame(selectedView, metadata, null)
if (
instance != null &&
MetadataUtils.isImg(instance) &&
diff --git a/editor/src/components/canvas/controls/select-mode/flex-gap-control.test-utils.tsx b/editor/src/components/canvas/controls/select-mode/flex-gap-control.test-utils.tsx
index a17811afdd54..a5d6243d21ae 100644
--- a/editor/src/components/canvas/controls/select-mode/flex-gap-control.test-utils.tsx
+++ b/editor/src/components/canvas/controls/select-mode/flex-gap-control.test-utils.tsx
@@ -38,6 +38,7 @@ export async function checkFlexGapHandlesPositionedCorrectly(
const localFrame = MetadataUtils.getLocalFrame(
selectedElement.elementPath,
editorState.jsxMetadata,
+ null,
)
const selectedElementFrame = zeroRectIfNullOrInfinity(localFrame)
// If this is a flex element and it has a gap specified.
diff --git a/editor/src/components/canvas/controls/select-mode/flex-gap-control.tsx b/editor/src/components/canvas/controls/select-mode/flex-gap-control.tsx
index 950974fdf078..f210ec1e0621 100644
--- a/editor/src/components/canvas/controls/select-mode/flex-gap-control.tsx
+++ b/editor/src/components/canvas/controls/select-mode/flex-gap-control.tsx
@@ -178,7 +178,7 @@ export const FlexGapControl = controlForStrategyMemoized((p
const bounds = boundingRectangleArray(
mapDropNulls((c) => {
- const localFrame = MetadataUtils.getLocalFrame(c.elementPath, metadata)
+ const localFrame = MetadataUtils.getLocalFrame(c.elementPath, metadata, null)
return localFrame != null && isFiniteRectangle(localFrame) ? localFrame : null
}, children),
)
diff --git a/editor/src/components/common/shared-strategies/convert-to-flex-strategy.ts b/editor/src/components/common/shared-strategies/convert-to-flex-strategy.ts
index 812fd1f10dc7..f1963d1ee0c4 100644
--- a/editor/src/components/common/shared-strategies/convert-to-flex-strategy.ts
+++ b/editor/src/components/common/shared-strategies/convert-to-flex-strategy.ts
@@ -174,7 +174,7 @@ function convertThreeElementGroupRow(
).map((element) => {
return {
...element,
- localFrame: MetadataUtils.getLocalFrame(element.elementPath, metadata),
+ localFrame: MetadataUtils.getLocalFrame(element.elementPath, metadata, null),
}
})
if (childrenMetadata.length === 3) {
diff --git a/editor/src/components/editor/actions/actions.tsx b/editor/src/components/editor/actions/actions.tsx
index 05968e0c0bbf..9f5793794639 100644
--- a/editor/src/components/editor/actions/actions.tsx
+++ b/editor/src/components/editor/actions/actions.tsx
@@ -1129,7 +1129,7 @@ function deleteElements(
if (metadata == null || isLeft(metadata.element)) {
return null
}
- const frame = MetadataUtils.getLocalFrame(path, editor.jsxMetadata)
+ const frame = MetadataUtils.getLocalFrame(path, editor.jsxMetadata, null)
if (frame == null || !isFiniteRectangle(frame)) {
return null
}
@@ -3316,7 +3316,7 @@ export const UPDATE_FNS = {
},
RESET_PINS: (action: ResetPins, editor: EditorModel): EditorModel => {
const target = action.target
- const frame = MetadataUtils.getLocalFrame(target, editor.jsxMetadata)
+ const frame = MetadataUtils.getLocalFrame(target, editor.jsxMetadata, null)
if (frame == null || isInfinityRectangle(frame)) {
return editor
@@ -3344,7 +3344,7 @@ export const UPDATE_FNS = {
}
},
UPDATE_FRAME_DIMENSIONS: (action: UpdateFrameDimensions, editor: EditorModel): EditorModel => {
- const initialFrame = MetadataUtils.getLocalFrame(action.element, editor.jsxMetadata)
+ const initialFrame = MetadataUtils.getLocalFrame(action.element, editor.jsxMetadata, null)
if (initialFrame == null || isInfinityRectangle(initialFrame)) {
return editor
@@ -5516,6 +5516,7 @@ export const UPDATE_FNS = {
const localFrame = MetadataUtils.getLocalFrame(
action.insertionPath.intendedParentPath,
editor.jsxMetadata,
+ null,
)
if (group != null && localFrame != null) {
switch (element.type) {
diff --git a/editor/src/components/editor/one-shot-insertion-strategies/insert-as-absolute-strategy.tsx b/editor/src/components/editor/one-shot-insertion-strategies/insert-as-absolute-strategy.tsx
index db58c7c4a590..4358eeae2143 100644
--- a/editor/src/components/editor/one-shot-insertion-strategies/insert-as-absolute-strategy.tsx
+++ b/editor/src/components/editor/one-shot-insertion-strategies/insert-as-absolute-strategy.tsx
@@ -72,6 +72,7 @@ export const insertAsAbsoluteStrategy = (
metadata,
state.projectContents,
'force-pins',
+ 'dont-add-contain-layout',
),
setProperty('always', result.newPath, PP.create('style', 'position'), 'absolute'),
],
diff --git a/editor/src/components/editor/one-shot-unwrap-strategies/reparent-to-unwrap-as-absolute-strategy.tsx b/editor/src/components/editor/one-shot-unwrap-strategies/reparent-to-unwrap-as-absolute-strategy.tsx
index f6fa75be2874..1734b52a4b1d 100644
--- a/editor/src/components/editor/one-shot-unwrap-strategies/reparent-to-unwrap-as-absolute-strategy.tsx
+++ b/editor/src/components/editor/one-shot-unwrap-strategies/reparent-to-unwrap-as-absolute-strategy.tsx
@@ -48,6 +48,7 @@ export const reparentToUnwrapAsAbsoluteStrategy = (
projectContents,
nodeModules,
'do-not-force-pins',
+ 'dont-add-contain-layout',
)
if (result == null) {
diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts
index decc614de98c..e06101e7d485 100644
--- a/editor/src/components/inspector/inspector-common.ts
+++ b/editor/src/components/inspector/inspector-common.ts
@@ -1245,7 +1245,7 @@ export function setParentToFixedIfHugCommands(
return []
}
- const globalFrame = MetadataUtils.getLocalFrame(parentPath, metadata)
+ const globalFrame = MetadataUtils.getLocalFrame(parentPath, metadata, null)
if (globalFrame == null || isInfinityRectangle(globalFrame)) {
return []
}
@@ -1458,7 +1458,7 @@ export function getConvertIndividualElementToAbsoluteCommandsFromMetadata(
jsxMetadata: ElementInstanceMetadataMap,
elementPathTree: ElementPathTrees,
): Array {
- const localFrame = MetadataUtils.getLocalFrame(target, jsxMetadata)
+ const localFrame = MetadataUtils.getLocalFrame(target, jsxMetadata, null)
if (localFrame == null || isInfinityRectangle(localFrame)) {
return []
}
diff --git a/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.spec.browser2.tsx b/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.spec.browser2.tsx
index 3321e3df30c6..4ae0d9813c11 100644
--- a/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.spec.browser2.tsx
+++ b/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.spec.browser2.tsx
@@ -142,6 +142,7 @@ describe('Frame updating layout section', () => {
const actualLocalFrame = MetadataUtils.getLocalFrame(
metadataForElement.elementPath,
metadataMap,
+ null,
)
expect(actualLocalFrame).toEqual(expectedFrame)
}
diff --git a/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.tsx b/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.tsx
index 92d1aaa6f306..d5187fa60c8e 100644
--- a/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.tsx
+++ b/editor/src/components/inspector/sections/layout-section/self-layout-subsection/frame-updating-layout-section.tsx
@@ -36,7 +36,12 @@ import {
import { InspectorContextMenuWrapper } from '../../../../context-menu-wrapper'
import { useDispatch } from '../../../../editor/store/dispatch-context'
import { Substores, useEditorState, useRefEditorState } from '../../../../editor/store/store-hook'
-import { metadataSelector, selectedViewsSelector } from '../../../../inspector/inpector-selectors'
+import {
+ allElementPropsSelector,
+ metadataSelector,
+ pathTreesSelector,
+ selectedViewsSelector,
+} from '../../../../inspector/inpector-selectors'
import { unsetPropertyMenuItem } from '../../../common/context-menu-items'
import {
cssNumber,
@@ -93,6 +98,8 @@ interface LTWHPixelValues {
export const FrameUpdatingLayoutSection = React.memo(() => {
const dispatch = useDispatch()
const metadataRef = useRefEditorState(metadataSelector)
+ const allElementPropsRef = useRefEditorState(allElementPropsSelector)
+ const elementPathTreesRef = useRefEditorState(pathTreesSelector)
const selectedViewsRef = useRefEditorState(selectedViewsSelector)
const projectContentsRef = useRefEditorState((store) => store.editor.projectContents)
const originalGlobalFrame: CanvasRectangle = useEditorState(
@@ -182,6 +189,7 @@ export const FrameUpdatingLayoutSection = React.memo(() => {
const maybeInfinityLocalFrame = MetadataUtils.getLocalFrame(
selectedView,
store.editor.jsxMetadata,
+ null,
)
if (maybeInfinityLocalFrame == null || isInfinityRectangle(maybeInfinityLocalFrame)) {
result.left.push(0)
@@ -217,6 +225,8 @@ export const FrameUpdatingLayoutSection = React.memo(() => {
[
moveInspectorStrategy(
metadataRef.current,
+ allElementPropsRef.current,
+ elementPathTreesRef.current,
selectedViewsRef.current,
projectContentsRef.current,
frameUpdate.edgeMovement,
@@ -252,6 +262,8 @@ export const FrameUpdatingLayoutSection = React.memo(() => {
[
directMoveInspectorStrategy(
metadataRef.current,
+ allElementPropsRef.current,
+ elementPathTreesRef.current,
selectedViewsRef.current,
projectContentsRef.current,
leftOrTop,
@@ -282,7 +294,15 @@ export const FrameUpdatingLayoutSection = React.memo(() => {
assertNever(frameUpdate)
}
},
- [dispatch, metadataRef, originalGlobalFrame, projectContentsRef, selectedViewsRef],
+ [
+ allElementPropsRef,
+ dispatch,
+ elementPathTreesRef,
+ metadataRef,
+ originalGlobalFrame,
+ projectContentsRef,
+ selectedViewsRef,
+ ],
)
const disableXYControls = React.useMemo(() => {
diff --git a/editor/src/components/text-editor/text-handling.ts b/editor/src/components/text-editor/text-handling.ts
index 45797293ed43..bf953188a747 100644
--- a/editor/src/components/text-editor/text-handling.ts
+++ b/editor/src/components/text-editor/text-handling.ts
@@ -280,7 +280,7 @@ export function getLocalRectangleWithFixedWidthHandlingText(
pathTrees: ElementPathTrees,
elementPath: ElementPath,
): MaybeInfinityLocalRectangle | null {
- const localFrame = MetadataUtils.getLocalFrame(elementPath, metadata)
+ const localFrame = MetadataUtils.getLocalFrame(elementPath, metadata, null)
if (
localFrame == null ||
diff --git a/editor/src/core/layout/layout-utils.ts b/editor/src/core/layout/layout-utils.ts
index 940ce6a8e875..f3730d112d5e 100644
--- a/editor/src/core/layout/layout-utils.ts
+++ b/editor/src/core/layout/layout-utils.ts
@@ -148,7 +148,7 @@ export function switchPinnedChildToFlex(
newParentMainAxis: 'horizontal' | 'vertical' | null,
propertyTarget: ReadonlyArray,
): SwitchLayoutTypeResult {
- const currentFrame = MetadataUtils.getLocalFrame(target, targetOriginalContextMetadata)
+ const currentFrame = MetadataUtils.getLocalFrame(target, targetOriginalContextMetadata, null)
const newParent = findJSXElementAtPath(newParentPath, components)
const element = findJSXElementAtPath(target, components)
diff --git a/editor/src/core/model/element-metadata-utils.ts b/editor/src/core/model/element-metadata-utils.ts
index f5f4fc0aca1f..0c5ece97bed1 100644
--- a/editor/src/core/model/element-metadata-utils.ts
+++ b/editor/src/core/model/element-metadata-utils.ts
@@ -1342,7 +1342,7 @@ export const MetadataUtils = {
Utils.fastForEach(targets, (target) => {
const instance = MetadataUtils.findElementByElementPath(metadata, target)
if (instance != null && this.isImg(instance)) {
- const componentFrame = MetadataUtils.getLocalFrame(target, metadata)
+ const componentFrame = MetadataUtils.getLocalFrame(target, metadata, null)
if (componentFrame != null && isFiniteRectangle(componentFrame)) {
const imageSize = getImageSize(allElementProps, instance)
const widthMultiplier = imageSize.width / componentFrame.width
@@ -1535,6 +1535,7 @@ export const MetadataUtils = {
getLocalFrame(
path: ElementPath,
metadata: ElementInstanceMetadataMap,
+ parentToTarget: ElementPath | null,
): MaybeInfinityLocalRectangle | null {
function getNonRootParent(parentOf: ElementPath): ElementPath {
// If the target is the root element of an instance (`a/b/c:root`), then we want to instead
@@ -1553,7 +1554,7 @@ export const MetadataUtils = {
}
const targetGlobalFrame = MetadataUtils.getFrameInCanvasCoords(path, metadata)
- const parentPath = getNonRootParent(path)
+ const parentPath = parentToTarget ?? getNonRootParent(path)
const parentGlobalFrame = MetadataUtils.getFrameInCanvasCoords(parentPath, metadata)
if (targetGlobalFrame == null || parentGlobalFrame == null) {
return null
@@ -1619,7 +1620,7 @@ export const MetadataUtils = {
return aabb
},
getFrameOrZeroRect(path: ElementPath, metadata: ElementInstanceMetadataMap): LocalRectangle {
- const frame = MetadataUtils.getLocalFrame(path, metadata)
+ const frame = MetadataUtils.getLocalFrame(path, metadata, null)
return zeroRectIfNullOrInfinity(frame)
},
getFrameRelativeTo(
@@ -1632,7 +1633,7 @@ export const MetadataUtils = {
} else {
const paths = EP.allPathsForLastPart(parent)
const parentFrames: Array = Utils.stripNulls(
- paths.map((path) => this.getLocalFrame(path, metadata)),
+ paths.map((path) => this.getLocalFrame(path, metadata, null)),
)
return parentFrames.reduce((working, next) => {
if (isInfinityRectangle(next)) {
@@ -2846,7 +2847,7 @@ export function propertyHasSimpleValue(
}
// This function creates a fake metadata for the given element
-// Useful when metadata is needed before the real on is created.
+// Useful when metadata is needed before the real one is created.
export function createFakeMetadataForElement(
path: ElementPath,
element: JSXElementChild,