From b0c79feff7523c06c3f2137f459e73eb9e50e8fb Mon Sep 17 00:00:00 2001 From: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:48:30 +0100 Subject: [PATCH] Merge the core of the escape hatch commands (#4360) * chore(editor) Merge the core of the escape hatch commands * chore(tests) Fix tests * fix(editor) Converting to absolute now rounds the frame * chore(tests) Fix tests * chore(tests) Fix tests broken by merge * fix(editor) prune bottom and right pins when fixing size --- ...solute-and-move-strategy.spec.browser2.tsx | 38 +++++- .../convert-to-absolute-and-move-strategy.tsx | 37 +++--- .../reparent-property-changes.ts | 6 +- .../components/editor/global-shortcuts.tsx | 1 - .../components/inspector/inspector-common.ts | 108 +++++++++++++----- .../inspector-strategies.ts | 4 +- ...emove-flex-convert-to-absolute-strategy.ts | 19 +-- .../inspector/resize-to-fit-control.tsx | 1 - 8 files changed, 140 insertions(+), 74 deletions(-) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.spec.browser2.tsx b/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.spec.browser2.tsx index 2b495fcbc37a..44f04cebecde 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.spec.browser2.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.spec.browser2.tsx @@ -1135,7 +1135,7 @@ describe('Convert to absolute/escape hatch', () => { right: 200, bottom: 320, top: 0, - left: 0 + left: 0, }} data-uid='bbb' data-testid='bbb' @@ -1178,7 +1178,14 @@ describe('Convert to absolute/escape hatch', () => { data-uid='aaa' > @@ -1298,7 +1305,14 @@ describe('Convert to absolute/escape hatch', () => { data-uid='aaa' > @@ -1451,7 +1465,14 @@ describe('Convert to absolute/escape hatch', () => { makeTestProjectCodeWithSnippet( ` @@ -1512,7 +1533,14 @@ describe('Convert to absolute/escape hatch', () => { makeTestProjectCodeWithSnippet( ` diff --git a/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.tsx index 03d6d78122f7..2c7899fbeeea 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/convert-to-absolute-and-move-strategy.tsx @@ -79,7 +79,10 @@ import type { ElementPathTrees } from '../../../../core/shared/element-path-tree import { cssPixelLength } from '../../../inspector/common/css-utils' import type { ProjectContentTreeRoot } from '../../../assets' import { showToastCommand } from '../../commands/show-toast-command' -import { sizeToVisualDimensions } from '../../../inspector/inspector-common' +import { + getConvertIndividualElementToAbsoluteCommands, + sizeToVisualDimensions, +} from '../../../inspector/inspector-common' export type SetHuggingParentToFixed = | 'set-hugging-parent-to-fixed' @@ -426,35 +429,29 @@ function collectSetLayoutPropCommands( intendedBounds: Array } { const globalFrame = MetadataUtils.getFrameInCanvasCoords(path, metadata) - if (globalFrame != null && isFiniteRectangle(globalFrame)) { + const element = MetadataUtils.findElementByElementPath(metadata, path) + if (element != null && globalFrame != null && isFiniteRectangle(globalFrame)) { + const updatedGlobalFrame = offsetRect(globalFrame, dragDelta ?? zeroCanvasRect) + const intendedBounds: Array = [ + { frame: updatedGlobalFrame, target: path }, + ] + const newLocalFrame = MetadataUtils.getFrameRelativeToTargetContainingBlock( targetParent ?? EP.parentPath(path), metadata, - globalFrame, + updatedGlobalFrame, ) - const intendedBounds: Array = (() => { - if (globalFrame == null) { - return [] - } else { - const updatedGlobalFrame = offsetRect(globalFrame, dragDelta ?? zeroCanvasRect) - return [{ frame: updatedGlobalFrame, target: path }] - } - })() - if (newLocalFrame != null) { - let commands: Array = [ - ...sizeToVisualDimensions(metadata, canvasState.startingElementPathTree, path), - convertToAbsolute('always', path), - ] - const updatePinsCommands = createUpdatePinsCommands( + const parentFlexDirection = element.specialSizeMeasurements.parentFlexDirection + + let commands: Array = getConvertIndividualElementToAbsoluteCommands( path, metadata, - canvasState, - dragDelta, + canvasState.startingElementPathTree, newLocalFrame, + parentFlexDirection, ) - commands.push(...updatePinsCommands) return { commands: commands, intendedBounds: intendedBounds } } diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-changes.ts b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-changes.ts index 627411b996e7..7aa8ad89e515 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-changes.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-property-changes.ts @@ -55,7 +55,7 @@ import { stripPinsConvertToVisualSize, } from './reparent-property-strategies' import { assertNever } from '../../../../../core/shared/utils' -import { flexChildProps, pruneFlexPropsCommands } from '../../../../inspector/inspector-common' +import { flexChildProps, prunePropsCommands } from '../../../../inspector/inspector-common' import { setCssLengthProperty } from '../../../commands/set-css-length-command' import type { ElementPathTrees } from '../../../../../core/shared/element-path-tree' import { @@ -221,7 +221,7 @@ export function positionElementToCoordinatesCommands( pathLookup: OldPathToNewPathMapping, ): CanvasCommand[] { const basicCommands = [ - ...pruneFlexPropsCommands(flexChildProps, elementToReparent.newPath), + ...prunePropsCommands(flexChildProps, elementToReparent.newPath), setCssLengthProperty( 'always', elementToReparent.newPath, @@ -280,7 +280,7 @@ export function positionElementToCoordinatesCommands( } return [ - ...pruneFlexPropsCommands(flexChildProps, targetPath), + ...prunePropsCommands(flexChildProps, targetPath), setProperty('always', targetPath, PP.create('style', 'position'), 'absolute'), setCssLengthProperty( 'always', diff --git a/editor/src/components/editor/global-shortcuts.tsx b/editor/src/components/editor/global-shortcuts.tsx index 26d16a9025c6..808a67d1c3aa 100644 --- a/editor/src/components/editor/global-shortcuts.tsx +++ b/editor/src/components/editor/global-shortcuts.tsx @@ -134,7 +134,6 @@ import { import { detectAreElementsFlexContainers, nukeAllAbsolutePositioningPropsCommands, - addPositionAbsoluteTopLeft, sizeToVisualDimensions, toggleResizeToFitSetToFixed, isIntrinsicallyInlineElement, diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index 239d5b35ad1a..57f5da18900b 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -45,7 +45,12 @@ import { setPropHugStrategies, } from './inspector-strategies/inspector-strategies' import { commandsForFirstApplicableStrategy } from './inspector-strategies/inspector-strategy' -import { isFiniteRectangle, isInfinityRectangle } from '../../core/shared/math-utils' +import { + isFiniteRectangle, + isInfinityRectangle, + roundRectangleToNearestWhole, + type LocalRectangle, +} from '../../core/shared/math-utils' import { inlineHtmlElements } from '../../utils/html-elements' import { intersection } from '../../core/shared/set-utils' import { showToastCommand } from '../canvas/commands/show-toast-command' @@ -64,6 +69,7 @@ import { groupConversionCommands, } from '../canvas/canvas-strategies/strategies/group-conversion-helpers' import { fixedSizeDimensionHandlingText } from '../text-editor/text-handling' +import { convertToAbsolute } from '../canvas/commands/convert-to-absolute-command' export type StartCenterEnd = 'flex-start' | 'center' | 'flex-end' @@ -384,7 +390,9 @@ export const flexChildProps = [ styleP('flexBasis'), ] -export function pruneFlexPropsCommands( +const flexChildAndBottomRightProps = [...flexChildProps, styleP('bottom'), styleP('right')] + +export function prunePropsCommands( props: PropertyPath[], elementPath: ElementPath, ): Array { @@ -420,21 +428,30 @@ export function sizeToVisualDimensions( pathTrees: ElementPathTrees, elementPath: ElementPath, ): Array { - const element = MetadataUtils.findElementByElementPath(metadata, elementPath) - if (element == null) { + const globalFrame = MetadataUtils.getFrameInCanvasCoords(elementPath, metadata) + if (globalFrame == null || isInfinityRectangle(globalFrame)) { return [] } - const globalFrame = MetadataUtils.getFrameInCanvasCoords(elementPath, metadata) - if (globalFrame == null || isInfinityRectangle(globalFrame)) { + return sizeToDimensionsFromFrame(metadata, pathTrees, elementPath, globalFrame) +} + +function sizeToDimensionsFromFrame( + metadata: ElementInstanceMetadataMap, + pathTrees: ElementPathTrees, + elementPath: ElementPath, + frame: { width: number; height: number }, +): Array { + const element = MetadataUtils.findElementByElementPath(metadata, elementPath) + if (element == null) { return [] } - const width = fixedSizeDimensionHandlingText(metadata, pathTrees, elementPath, globalFrame.width) - const height = globalFrame.height + const width = fixedSizeDimensionHandlingText(metadata, pathTrees, elementPath, frame.width) + const height = frame.height return [ - ...pruneFlexPropsCommands(flexChildProps, elementPath), + ...prunePropsCommands(flexChildAndBottomRightProps, elementPath), setCssLengthProperty( 'always', elementPath, @@ -465,7 +482,7 @@ export const sizeToVisualDimensionsAlongAxisInstance = const value = globalFrame[dimension] return [ - ...pruneFlexPropsCommands(flexChildProps, elementPath), + ...prunePropsCommands(flexChildProps, elementPath), setCssLengthProperty( 'always', elementPath, @@ -831,36 +848,27 @@ export function resizeToFillCommands( return commands } -export function addPositionAbsoluteTopLeft( - metadata: ElementInstanceMetadataMap, +function addPositionAbsoluteTopLeft( elementPath: ElementPath, + localFrame: LocalRectangle, + parentFlexDirection: FlexDirection | null, ): Array { - const element = MetadataUtils.findElementByElementPath(metadata, elementPath) - if (element == null) { - return [] - } - - const left = element.specialSizeMeasurements.offset.x - const top = element.specialSizeMeasurements.offset.y - - const parentFlexDirection = element.specialSizeMeasurements.parentFlexDirection - return [ + convertToAbsolute('always', elementPath), setCssLengthProperty( 'always', elementPath, styleP('left'), - setExplicitCssValue(cssPixelLength(left)), + setExplicitCssValue(cssPixelLength(localFrame.x)), parentFlexDirection, ), setCssLengthProperty( 'always', elementPath, styleP('top'), - setExplicitCssValue(cssPixelLength(top)), + setExplicitCssValue(cssPixelLength(localFrame.y)), parentFlexDirection, ), - setProperty('always', elementPath, styleP('position'), 'absolute'), ] } @@ -1120,12 +1128,54 @@ export function toggleAbsolutePositioningCommands( : []), ] } else { - return [ - ...sizeToVisualDimensions(jsxMetadata, elementPathTree, elementPath), - ...addPositionAbsoluteTopLeft(jsxMetadata, elementPath), - ] + return getConvertIndividualElementToAbsoluteCommandsFromMetadata( + elementPath, + jsxMetadata, + elementPathTree, + ) } }) return commands } + +export function getConvertIndividualElementToAbsoluteCommandsFromMetadata( + target: ElementPath, + jsxMetadata: ElementInstanceMetadataMap, + elementPathTree: ElementPathTrees, +): Array { + const localFrame = MetadataUtils.getFrame(target, jsxMetadata) + if (localFrame == null || isInfinityRectangle(localFrame)) { + return [] + } + + const element = MetadataUtils.findElementByElementPath(jsxMetadata, target) + if (element == null) { + return [] + } + + const parentFlexDirection = element.specialSizeMeasurements.parentFlexDirection + + return getConvertIndividualElementToAbsoluteCommands( + target, + jsxMetadata, + elementPathTree, + localFrame, + parentFlexDirection, + ) +} + +export function getConvertIndividualElementToAbsoluteCommands( + target: ElementPath, + jsxMetadata: ElementInstanceMetadataMap, + elementPathTree: ElementPathTrees, + frame: LocalRectangle, + parentFlexDirection: FlexDirection | null, +): Array { + // First round the frame so that we don't end up with half pixel values + const roundedFrame = roundRectangleToNearestWhole(frame) + return [ + ...sizeToDimensionsFromFrame(jsxMetadata, elementPathTree, target, roundedFrame), + ...addPositionAbsoluteTopLeft(target, roundedFrame, parentFlexDirection), + ] +} diff --git a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts index c0017c5255a4..962d941de634 100644 --- a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts +++ b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts @@ -4,7 +4,7 @@ import type { Axis, FlexAlignment, FlexJustifyContent } from '../inspector-commo import { filterKeepFlexContainers, flexChildProps, - pruneFlexPropsCommands, + prunePropsCommands, sizeToVisualDimensions, } from '../inspector-common' import { MetadataUtils } from '../../../core/model/element-metadata-utils' @@ -115,7 +115,7 @@ export const updateFlexDirectionStrategies = ( return elements.flatMap((path) => [ setProperty('always', path, PP.create('style', 'flexDirection'), flexDirection), ...MetadataUtils.getChildrenPathsOrdered(metadata, pathTrees, path).flatMap((child) => [ - ...pruneFlexPropsCommands(flexChildProps, child), + ...prunePropsCommands(flexChildProps, child), ...sizeToVisualDimensions(metadata, pathTrees, child), ]), ]) diff --git a/editor/src/components/inspector/inspector-strategies/remove-flex-convert-to-absolute-strategy.ts b/editor/src/components/inspector/inspector-strategies/remove-flex-convert-to-absolute-strategy.ts index b9051904b6b9..6113772236fd 100644 --- a/editor/src/components/inspector/inspector-strategies/remove-flex-convert-to-absolute-strategy.ts +++ b/editor/src/components/inspector/inspector-strategies/remove-flex-convert-to-absolute-strategy.ts @@ -3,20 +3,13 @@ import { MetadataUtils } from '../../../core/model/element-metadata-utils' import type { ElementInstanceMetadataMap } from '../../../core/shared/element-template' import type { ElementPath } from '../../../core/shared/project-file-types' import type { CanvasCommand } from '../../canvas/commands/commands' -import { - setCssLengthProperty, - setExplicitCssValue, -} from '../../canvas/commands/set-css-length-command' -import { setProperty } from '../../canvas/commands/set-property-command' -import { cssPixelLength } from '../common/css-utils' import { filterKeepFlexContainers, - flexChildProps, flexContainerProps, nullOrNonEmpty, - addPositionAbsoluteTopLeft, - pruneFlexPropsCommands, + prunePropsCommands, sizeToVisualDimensions, + getConvertIndividualElementToAbsoluteCommandsFromMetadata, } from '../inspector-common' import type { InspectorStrategy } from './inspector-strategy' @@ -27,10 +20,10 @@ function removeFlexConvertToAbsoluteOne( ): Array { const children = MetadataUtils.getChildrenPathsOrdered(metadata, pathTrees, elementPath) return [ - ...pruneFlexPropsCommands(flexContainerProps, elementPath), // flex-related stuff is pruned - ...children.flatMap((c) => addPositionAbsoluteTopLeft(metadata, c)), // all children are converted to absolute, - ...children.flatMap((c) => sizeToVisualDimensions(metadata, pathTrees, c)), // with width/height based on measured dimensions - ...children.flatMap((c) => pruneFlexPropsCommands(flexChildProps, c)), + ...prunePropsCommands(flexContainerProps, elementPath), // flex-related stuff is pruned + ...children.flatMap((c) => + getConvertIndividualElementToAbsoluteCommandsFromMetadata(c, metadata, pathTrees), + ), // all children are converted to absolute, ...sizeToVisualDimensions(metadata, pathTrees, elementPath), // container is sized to keep its visual dimensions ] } diff --git a/editor/src/components/inspector/resize-to-fit-control.tsx b/editor/src/components/inspector/resize-to-fit-control.tsx index 63e9363fd082..68a82b78cb07 100644 --- a/editor/src/components/inspector/resize-to-fit-control.tsx +++ b/editor/src/components/inspector/resize-to-fit-control.tsx @@ -17,7 +17,6 @@ import { getFixedFillHugOptionsForElement, resizeToFillCommands, resizeToFitCommands, - sizeToVisualDimensions, } from './inspector-common' import * as EP from '../../core/shared/element-path' import { assertNever } from '../../core/shared/utils'