From a199f5f4e66a8c9fa85c2a1351aec860b438d077 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 16 Jul 2024 16:08:00 +0200 Subject: [PATCH 1/8] grid-specific hug strategy --- .../components/inspector/inspector-common.ts | 22 ++++--- .../hug-contents-basic-strategy.ts | 58 ++++++++++++++++++- .../inspector-strategies.ts | 6 +- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index 3c5fbdacfb8a..753cb94ad950 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -255,24 +255,30 @@ export function detectAreElementsFlexContainers( export const isFlexColumn = (flexDirection: FlexDirection): boolean => flexDirection.startsWith('column') -export const hugContentsApplicableForContainer = ( +export const basicHugContentsApplicableForContainer = ( metadata: ElementInstanceMetadataMap, pathTrees: ElementPathTrees, elementPath: ElementPath, ): boolean => { - return ( + const anyChildrenFixStickyOrAbsolute = mapDropNulls( (path) => MetadataUtils.findElementByElementPath(metadata, path), MetadataUtils.getChildrenPathsOrdered(metadata, pathTrees, elementPath), ).filter( (element) => - !( - MetadataUtils.isPositionFixed(element) || - MetadataUtils.isPositionSticky(element) || - MetadataUtils.isPositionAbsolute(element) - ), + MetadataUtils.isPositionFixed(element) || + MetadataUtils.isPositionSticky(element) || + MetadataUtils.isPositionAbsolute(element), ).length > 0 + + const isElementGrid = MetadataUtils.isGridLayoutedContainer( + MetadataUtils.findElementByElementPath(metadata, elementPath), ) + + if (anyChildrenFixStickyOrAbsolute || isElementGrid) { + return false + } + return true } export const hugContentsApplicableForText = ( @@ -999,7 +1005,7 @@ export function getFixedFillHugOptionsForElement( isGroup ? 'hug-group' : null, 'fixed', hugContentsApplicableForText(metadata, selectedView) || - (!isGroup && hugContentsApplicableForContainer(metadata, pathTrees, selectedView)) + (!isGroup && basicHugContentsApplicableForContainer(metadata, pathTrees, selectedView)) ? 'hug' : null, fillContainerApplicable(metadata, selectedView) ? 'fill' : null, diff --git a/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts b/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts index 70ca1829f219..fabb5155ee0f 100644 --- a/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts +++ b/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts @@ -1,5 +1,6 @@ import type { ElementPathTrees } from '../../../core/shared/element-path-tree' import { MetadataUtils } from '../../../core/model/element-metadata-utils' +import type { GridTemplate } from '../../../core/shared/element-template' import { type ElementInstanceMetadataMap } from '../../../core/shared/element-template' import type { ElementPath } from '../../../core/shared/project-file-types' import * as PP from '../../../core/shared/property-path' @@ -15,7 +16,7 @@ import { cssKeyword } from '../common/css-utils' import type { Axis } from '../inspector-common' import { detectFillHugFixedState, - hugContentsApplicableForContainer, + basicHugContentsApplicableForContainer, hugContentsApplicableForText, MaxContent, nukeSizingPropsForAxisCommand, @@ -28,6 +29,7 @@ import { queueTrueUpElement } from '../../canvas/commands/queue-true-up-command' import { trueUpGroupElementChanged } from '../../../components/editor/store/editor-state' import type { AllElementProps } from '../../../components/editor/store/editor-state' import { convertSizelessDivToFrameCommands } from '../../canvas/canvas-strategies/strategies/group-conversion-helpers' +import { deleteProperties } from '../../canvas/commands/delete-properties-command' const CHILDREN_CONVERTED_TOAST_ID = 'CHILDREN_CONVERTED_TOAST_ID' @@ -87,6 +89,58 @@ function hugContentsSingleElement( ] } +function gridTemplateUsesFr(template: GridTemplate) { + return template.type === 'DIMENSIONS' && template.dimensions.some((d) => d.unit === 'fr') +} + +function elementUsesFrAlongAxis( + axis: Axis, + metadata: ElementInstanceMetadataMap, + elementPath: ElementPath, +) { + const instance = MetadataUtils.findElementByElementPath(metadata, elementPath) + if (instance == null) { + return false + } + const { containerGridProperties } = instance.specialSizeMeasurements + if (axis === 'horizontal' && containerGridProperties.gridTemplateColumns != null) { + return gridTemplateUsesFr(containerGridProperties.gridTemplateColumns) + } + if (axis === 'vertical' && containerGridProperties.gridTemplateRows != null) { + return gridTemplateUsesFr(containerGridProperties.gridTemplateRows) + } + + return true +} + +export const hugContentsGridStrategy = ( + metadata: ElementInstanceMetadataMap, + elementPaths: ElementPath[], + axis: Axis, +): InspectorStrategy => ({ + name: 'Set Grid to Hug', + strategy: () => { + // only run the strategy if all selected elements are grids + const allSelectedElementsGrids = elementPaths.every((e) => + MetadataUtils.isGridLayoutedContainer(MetadataUtils.findElementByElementPath(metadata, e)), + ) + + // only run the strategy if no selected element uses fr along the affected + // axis, because that implies that the container needs to be sized + const anyElementUsesFrAlongAxis = elementPaths.some((e) => + elementUsesFrAlongAxis(axis, metadata, e), + ) + + if (!allSelectedElementsGrids || anyElementUsesFrAlongAxis) { + return null + } + + return elementPaths.flatMap((elementPath) => + deleteProperties('always', elementPath, [PP.create('style', widthHeightFromAxis(axis))]), + ) + }, +}) + export const hugContentsBasicStrategy = ( metadata: ElementInstanceMetadataMap, elementPaths: ElementPath[], @@ -97,7 +151,7 @@ export const hugContentsBasicStrategy = ( strategy: () => { const elements = elementPaths.filter( (path) => - hugContentsApplicableForContainer(metadata, pathTrees, path) || + basicHugContentsApplicableForContainer(metadata, pathTrees, path) || hugContentsApplicableForText(metadata, path), ) diff --git a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts index 82550696f489..e1f0b39523fb 100644 --- a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts +++ b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts @@ -16,6 +16,7 @@ import type { WhenToRun } from '../../../components/canvas/commands/commands' import { hugContentsAbsoluteStrategy, hugContentsBasicStrategy, + hugContentsGridStrategy, } from './hug-contents-basic-strategy' import { fillContainerStrategyFlexParent, @@ -204,7 +205,10 @@ export const setPropHugStrategies = ( elementPaths: ElementPath[], pathTrees: ElementPathTrees, axis: Axis, -): Array => [hugContentsBasicStrategy(metadata, elementPaths, pathTrees, axis)] +): Array => [ + hugContentsGridStrategy(metadata, elementPaths, axis), + hugContentsBasicStrategy(metadata, elementPaths, pathTrees, axis), +] export const setPropHugAbsoluteStrategies = ( metadata: ElementInstanceMetadataMap, From 435bff868ba9ee7686dc1a93647ac982b052129e Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 16 Jul 2024 17:05:32 +0200 Subject: [PATCH 2/8] check props too --- .../hug-contents-basic-strategy.ts | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts b/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts index fabb5155ee0f..8e6395c3cf24 100644 --- a/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts +++ b/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts @@ -30,6 +30,7 @@ import { trueUpGroupElementChanged } from '../../../components/editor/store/edit import type { AllElementProps } from '../../../components/editor/store/editor-state' import { convertSizelessDivToFrameCommands } from '../../canvas/canvas-strategies/strategies/group-conversion-helpers' import { deleteProperties } from '../../canvas/commands/delete-properties-command' +import { assertNever } from '../../../core/shared/utils' const CHILDREN_CONVERTED_TOAST_ID = 'CHILDREN_CONVERTED_TOAST_ID' @@ -89,8 +90,12 @@ function hugContentsSingleElement( ] } -function gridTemplateUsesFr(template: GridTemplate) { - return template.type === 'DIMENSIONS' && template.dimensions.some((d) => d.unit === 'fr') +function gridTemplateUsesFr(template: GridTemplate | null) { + return ( + template != null && + template.type === 'DIMENSIONS' && + template.dimensions.some((d) => d.unit === 'fr') + ) } function elementUsesFrAlongAxis( @@ -102,15 +107,23 @@ function elementUsesFrAlongAxis( if (instance == null) { return false } - const { containerGridProperties } = instance.specialSizeMeasurements - if (axis === 'horizontal' && containerGridProperties.gridTemplateColumns != null) { - return gridTemplateUsesFr(containerGridProperties.gridTemplateColumns) - } - if (axis === 'vertical' && containerGridProperties.gridTemplateRows != null) { - return gridTemplateUsesFr(containerGridProperties.gridTemplateRows) + const { containerGridProperties, containerGridPropertiesFromProps } = + instance.specialSizeMeasurements + + switch (axis) { + case 'horizontal': + return ( + gridTemplateUsesFr(containerGridProperties.gridTemplateColumns) || + gridTemplateUsesFr(containerGridPropertiesFromProps.gridTemplateColumns) + ) + case 'vertical': + return ( + gridTemplateUsesFr(containerGridProperties.gridTemplateRows) || + gridTemplateUsesFr(containerGridPropertiesFromProps.gridTemplateRows) + ) + default: + assertNever(axis) } - - return true } export const hugContentsGridStrategy = ( From 9dabae851abd81bbad51f64586f07120bc8b4d3f Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 16 Jul 2024 17:06:02 +0200 Subject: [PATCH 3/8] better phrased condition --- .../src/components/inspector/inspector-common.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index 753cb94ad950..8d70a5c8dd67 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -260,25 +260,24 @@ export const basicHugContentsApplicableForContainer = ( pathTrees: ElementPathTrees, elementPath: ElementPath, ): boolean => { - const anyChildrenFixStickyOrAbsolute = + const hasNonFixStickyOrAbsolute = mapDropNulls( (path) => MetadataUtils.findElementByElementPath(metadata, path), MetadataUtils.getChildrenPathsOrdered(metadata, pathTrees, elementPath), ).filter( (element) => - MetadataUtils.isPositionFixed(element) || - MetadataUtils.isPositionSticky(element) || - MetadataUtils.isPositionAbsolute(element), + !( + MetadataUtils.isPositionFixed(element) || + MetadataUtils.isPositionSticky(element) || + MetadataUtils.isPositionAbsolute(element) + ), ).length > 0 const isElementGrid = MetadataUtils.isGridLayoutedContainer( MetadataUtils.findElementByElementPath(metadata, elementPath), ) - if (anyChildrenFixStickyOrAbsolute || isElementGrid) { - return false - } - return true + return hasNonFixStickyOrAbsolute && !isElementGrid } export const hugContentsApplicableForText = ( From c063039f086bdba460a3f4b6b2b0c915cd1f3dee Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 16 Jul 2024 17:06:55 +0200 Subject: [PATCH 4/8] tests --- ...ze-bounding-box-strategy.spec.browser2.tsx | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.spec.browser2.tsx b/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.spec.browser2.tsx index 533fb5f8c7ff..9fead6612cd2 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.spec.browser2.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/absolute-resize-bounding-box-strategy.spec.browser2.tsx @@ -3124,6 +3124,96 @@ describe('Double click on resize edge', () => { expect(div.style.width).toEqual(MaxContent) expect(div.style.height).toEqual('445px') }) + + describe('grids', () => { + const project = ({ + rows: rows, + columns: columns, + }: { + rows: string + columns: string + }) => `import * as React from 'react' +import { Scene, Storyboard } from 'utopia-api' + +export var storyboard = ( + + +
+ Utopia logo +
+
+
+) +` + it('removes width when right edge is clicked', async () => { + const editor = await renderTestEditorWithCode( + project({ rows: '66px 66px 66px 66px', columns: '50px 81px 96px 85px' }), + 'await-first-dom-report', + ) + const div = await doDblClickTest(editor, edgeResizeControlTestId(EdgePositionRight)) + expect(div.style.width).toEqual('') // width is removed + expect(div.style.height).toEqual('400px') + }) + it('removes height when bottom edge is clicked', async () => { + const editor = await renderTestEditorWithCode( + project({ rows: '66px 66px 66px 66px', columns: '50px 81px 96px 85px' }), + 'await-first-dom-report', + ) + const div = await doDblClickTest(editor, edgeResizeControlTestId(EdgePositionBottom)) + expect(div.style.width).toEqual('400px') + expect(div.style.height).toEqual('') // height is removed + }) + it("isn't applicable when the selected grid uses fr along the affected axis", async () => { + const editor = await renderTestEditorWithCode( + project({ rows: '66px 1fr 66px 66px', columns: '50px 81px 96px 85px' }), + 'await-first-dom-report', + ) + const div = await doDblClickTest(editor, edgeResizeControlTestId(EdgePositionBottom)) + expect(div.style.width).toEqual('400px') + expect(div.style.height).toEqual('400px') + }) + }) }) describe('double click on resize corner', () => { From 0dcf85de49bb2fe2e2eeb8e31dbf49109f82501b Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Wed, 17 Jul 2024 14:41:23 +0200 Subject: [PATCH 5/8] naming --- editor/src/components/inspector/inspector-common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index 8d70a5c8dd67..1c2b308cc67c 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -273,11 +273,11 @@ export const basicHugContentsApplicableForContainer = ( ), ).length > 0 - const isElementGrid = MetadataUtils.isGridLayoutedContainer( + const isGrid = MetadataUtils.isGridLayoutedContainer( MetadataUtils.findElementByElementPath(metadata, elementPath), ) - return hasNonFixStickyOrAbsolute && !isElementGrid + return hasNonFixStickyOrAbsolute && !isGrid } export const hugContentsApplicableForText = ( From d1bb61b01ac64ba66620fed0f65dfd95510ab77e Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 18 Jul 2024 10:07:50 +0200 Subject: [PATCH 6/8] naming --- editor/src/components/inspector/inspector-common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index 1c2b308cc67c..f30b25e01ada 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -260,7 +260,7 @@ export const basicHugContentsApplicableForContainer = ( pathTrees: ElementPathTrees, elementPath: ElementPath, ): boolean => { - const hasNonFixStickyOrAbsolute = + const isNonFixStickOrAbsolute = mapDropNulls( (path) => MetadataUtils.findElementByElementPath(metadata, path), MetadataUtils.getChildrenPathsOrdered(metadata, pathTrees, elementPath), @@ -277,7 +277,7 @@ export const basicHugContentsApplicableForContainer = ( MetadataUtils.findElementByElementPath(metadata, elementPath), ) - return hasNonFixStickyOrAbsolute && !isGrid + return isNonFixStickOrAbsolute && !isGrid } export const hugContentsApplicableForText = ( From 2731834981717d7df9403fc9f979a960cadf0043 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 18 Jul 2024 10:09:27 +0200 Subject: [PATCH 7/8] file name --- .../{hug-contents-basic-strategy.ts => hug-contents-strategy.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename editor/src/components/inspector/inspector-strategies/{hug-contents-basic-strategy.ts => hug-contents-strategy.ts} (100%) diff --git a/editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts b/editor/src/components/inspector/inspector-strategies/hug-contents-strategy.ts similarity index 100% rename from editor/src/components/inspector/inspector-strategies/hug-contents-basic-strategy.ts rename to editor/src/components/inspector/inspector-strategies/hug-contents-strategy.ts From 917e7767d63d180f9d1dba0b3c5f21fa694109fd Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 18 Jul 2024 10:11:14 +0200 Subject: [PATCH 8/8] update imports --- .../common/shared-strategies/convert-to-flex-strategy.ts | 2 +- editor/src/components/inspector/inspector-common.ts | 2 +- .../inspector/inspector-strategies/inspector-strategies.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 aed823bad9bc..af2fc721ea09 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 @@ -50,7 +50,7 @@ import { onlyChildIsSpan, sizeToVisualDimensions, } from '../../inspector/inspector-common' -import { setHugContentForAxis } from '../../inspector/inspector-strategies/hug-contents-basic-strategy' +import { setHugContentForAxis } from '../../inspector/inspector-strategies/hug-contents-strategy' type FlexDirectionRowColumn = 'row' | 'column' // a limited subset as we won't never guess row-reverse or column-reverse type FlexAlignItems = 'center' | 'flex-end' diff --git a/editor/src/components/inspector/inspector-common.ts b/editor/src/components/inspector/inspector-common.ts index f30b25e01ada..c9163ded2cef 100644 --- a/editor/src/components/inspector/inspector-common.ts +++ b/editor/src/components/inspector/inspector-common.ts @@ -75,7 +75,7 @@ import { import { fixedSizeDimensionHandlingText } from '../text-editor/text-handling' import { convertToAbsolute } from '../canvas/commands/convert-to-absolute-command' import { hugPropertiesFromStyleMap } from '../../core/shared/dom-utils' -import { setHugContentForAxis } from './inspector-strategies/hug-contents-basic-strategy' +import { setHugContentForAxis } from './inspector-strategies/hug-contents-strategy' export type StartCenterEnd = 'flex-start' | 'center' | 'flex-end' diff --git a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts index e1f0b39523fb..471bba6fa6b3 100644 --- a/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts +++ b/editor/src/components/inspector/inspector-strategies/inspector-strategies.ts @@ -17,7 +17,7 @@ import { hugContentsAbsoluteStrategy, hugContentsBasicStrategy, hugContentsGridStrategy, -} from './hug-contents-basic-strategy' +} from './hug-contents-strategy' import { fillContainerStrategyFlexParent, fillContainerStrategyFlow,