From 2010de5c217fee54ef030b92c44609680ba0b819 Mon Sep 17 00:00:00 2001 From: Sean Parsons <217400+seanparsons@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:07:34 +0000 Subject: [PATCH] fix(grids) Handle Spans In Pin Outlines (#6644) - Extracted out the `printPin` function. - Implemented `nullHandlingPrintPin` as a thin wrapper around `printPin`. - `GridElementContainingBlock` now uses `nullHandlingPrintPin` instead of `gridPositionToValue` to build the grid column/row start/end values. --- .../strategies/grid-helpers.ts | 46 ++++--- .../controls/grid-controls.spec.browser2.tsx | 121 ++++++++++++++++++ .../canvas/controls/grid-controls.tsx | 51 +++++--- 3 files changed, 179 insertions(+), 39 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 a8e2b35a4b1f..0710211b683c 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts @@ -77,6 +77,29 @@ export function isAutoGridPin(v: GridPositionOrSpan): boolean { return isCSSKeyword(v) && v.value === 'auto' } +export function printPin( + gridTemplate: GridContainerProperties, + pin: GridPositionOrSpan, + axis: 'row' | 'column', +): string | number { + if (isGridSpan(pin)) { + return stringifyGridSpan(pin) + } + if (isCSSKeyword(pin)) { + return pin.value + } + const tracks = + axis === 'column' ? gridTemplate.gridTemplateColumns : gridTemplate.gridTemplateRows + const maybeLineName = + tracks?.type === 'DIMENSIONS' + ? tracks.dimensions.find((_, index) => index + 1 === pin.numericalPosition)?.lineName + : null + if (maybeLineName != null) { + return maybeLineName + } + return pin.numericalPosition ?? 'auto' +} + export function getCommandsForGridItemPlacement( elementPath: ElementPath, gridTemplate: GridContainerProperties, @@ -94,25 +117,6 @@ export function getCommandsForGridItemPlacement( ]), ] - function printPin(pin: GridPositionOrSpan, axis: 'row' | 'column'): string | number { - if (isGridSpan(pin)) { - return stringifyGridSpan(pin) - } - if (isCSSKeyword(pin)) { - return pin.value - } - const tracks = - axis === 'column' ? gridTemplate.gridTemplateColumns : gridTemplate.gridTemplateRows - const maybeLineName = - tracks?.type === 'DIMENSIONS' - ? tracks.dimensions.find((_, index) => index + 1 === pin.numericalPosition)?.lineName - : null - if (maybeLineName != null) { - return maybeLineName - } - return pin.numericalPosition ?? 'auto' - } - function serializeAxis( startPosition: GridPositionOrSpan, endPosition: GridPositionOrSpan, @@ -127,8 +131,8 @@ export function getCommandsForGridItemPlacement( | 'gridRowEnd' value: string | number } { - const startValue = printPin(startPosition, axis) - const endValue = printPin(endPosition, axis) + const startValue = printPin(gridTemplate, startPosition, axis) + const endValue = printPin(gridTemplate, endPosition, axis) if (isAutoGridPin(startPosition) && !isAutoGridPin(endPosition)) { return { diff --git a/editor/src/components/canvas/controls/grid-controls.spec.browser2.tsx b/editor/src/components/canvas/controls/grid-controls.spec.browser2.tsx index be1be9264dd2..ab6344931042 100644 --- a/editor/src/components/canvas/controls/grid-controls.spec.browser2.tsx +++ b/editor/src/components/canvas/controls/grid-controls.spec.browser2.tsx @@ -615,4 +615,125 @@ describe('Grid Pin Outlines', () => { bottom: { x: 1781.5, y: 555.5, width: 1, height: 5 }, }) }) + + it('pinned top and left, grid rows and columns fully specified and not absolutely positioned, with a row start span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + top: 5, + left: 12, + gridRowStart: 'span 2', + gridColumnStart: 3, + gridRowEnd: 2, + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({}) + }) + it('pinned top and left, grid rows and columns fully specified and absolutely positioned, with a row start span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + position: 'absolute', + top: 5, + left: 12, + gridRowStart: 'span 2', + gridColumnStart: 3, + gridRowEnd: 2, + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({ + left: { x: 688.5, y: 190.5, width: 12, height: 1 }, + top: { x: 725.5, y: 160.5, width: 1, height: 5 }, + }) + }) + it('pinned bottom and right, grid rows and columns fully specified and not absolutely positioned, with a row start span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + bottom: 5, + right: 12, + gridRowStart: 'span 2', + gridColumnStart: 3, + gridRowEnd: 2, + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({}) + }) + it('pinned bottom and right, grid rows and columns fully specified and absolutely positioned, with a row start span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + position: 'absolute', + bottom: 5, + right: 12, + gridRowStart: 'span 2', + gridColumnStart: 3, + gridRowEnd: 2, + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({ + right: { x: 777.5, y: 240.5, width: 12, height: 1 }, + bottom: { x: 751.5, y: 266.5, width: 1, height: 5 }, + }) + }) + it('pinned top and left, grid rows and columns fully specified and not absolutely positioned, with a row end span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + top: 5, + left: 12, + gridRowStart: 2, + gridColumnStart: 3, + gridRowEnd: 'span 2', + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({}) + }) + it('pinned top and left, grid rows and columns fully specified and absolutely positioned, with a row end span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + position: 'absolute', + top: 5, + left: 12, + gridRowStart: 2, + gridColumnStart: 3, + gridRowEnd: 'span 2', + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({ + left: { x: 688.5, y: 315.5, width: 12, height: 1 }, + top: { x: 725.5, y: 285.5, width: 1, height: 5 }, + }) + }) + it('pinned bottom and right, grid rows and columns fully specified and not absolutely positioned, with a row end span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + bottom: 5, + right: 12, + gridRowStart: 2, + gridColumnStart: 3, + gridRowEnd: 'span 2', + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({}) + }) + it('pinned bottom and right, grid rows and columns fully specified and absolutely positioned, with a row end span', async () => { + const result = await testGridOutlines('storyboard/scene/grid/grid-child', { + position: 'absolute', + bottom: 5, + right: 12, + gridRowStart: 2, + gridColumnStart: 3, + gridRowEnd: 'span 2', + gridColumnEnd: 3, + width: 50, + height: 50, + }) + expect(result).toEqual({ + right: { x: 777.5, y: 470.5, width: 12, height: 1 }, + bottom: { x: 751.5, y: 496.5, width: 1, height: 5 }, + }) + }) }) diff --git a/editor/src/components/canvas/controls/grid-controls.tsx b/editor/src/components/canvas/controls/grid-controls.tsx index ac23b73918e0..bf687f20a041 100644 --- a/editor/src/components/canvas/controls/grid-controls.tsx +++ b/editor/src/components/canvas/controls/grid-controls.tsx @@ -17,6 +17,7 @@ import * as EP from '../../../core/shared/element-path' import type { ElementInstanceMetadataMap, GridAutoOrTemplateDimensions, + GridContainerProperties, GridPositionOrSpan, } from '../../../core/shared/element-template' import { @@ -73,6 +74,7 @@ import { getGridRelatedIndexes, getGridElementPinState, gridPositionToValue, + printPin, getGridIdentifierContainerOrComponentPath, gridIdentifierToString, gridIdentifiersSimilar, @@ -1944,6 +1946,17 @@ export interface GridElementContainingBlockProps { gridChild: ElementPath } +function nullHandlingPrintPin( + gridTemplate: GridContainerProperties | null | undefined, + pin: GridPositionOrSpan | null | undefined, + axis: 'row' | 'column', +): string | number | undefined { + if (gridTemplate == null || pin == null) { + return undefined + } + return printPin(gridTemplate, pin, axis) +} + const GridElementContainingBlock = React.memo((props) => { const gridData = useGridMeasurementHelperData(props.gridPath, 'element') const scale = useEditorState( @@ -1975,24 +1988,26 @@ const GridElementContainingBlock = React.memo(( const gridFromProps = childMetadata.specialSizeMeasurements.elementGridPropertiesFromProps const gridComputed = childMetadata.specialSizeMeasurements.elementGridProperties return { - gridColumnStart: - gridPositionToValue( - gridFromProps.gridColumnStart ?? gridComputed.gridColumnStart, - null, - ) ?? undefined, - gridColumnEnd: - gridPositionToValue( - gridFromProps.gridColumnEnd ?? gridComputed.gridColumnEnd, - gridFromProps.gridColumnStart ?? gridComputed.gridColumnStart, - ) ?? undefined, - gridRowStart: - gridPositionToValue(gridFromProps.gridRowStart ?? gridComputed.gridRowStart, null) ?? - undefined, - gridRowEnd: - gridPositionToValue( - gridFromProps.gridRowEnd ?? gridComputed.gridRowEnd, - gridFromProps.gridRowStart ?? gridComputed.gridRowStart, - ) ?? undefined, + gridColumnStart: nullHandlingPrintPin( + childMetadata.specialSizeMeasurements.containerGridProperties, + gridFromProps.gridColumnStart ?? gridComputed.gridColumnStart, + 'column', + ), + gridColumnEnd: nullHandlingPrintPin( + childMetadata.specialSizeMeasurements.containerGridProperties, + gridFromProps.gridColumnEnd ?? gridComputed.gridColumnEnd, + 'column', + ), + gridRowStart: nullHandlingPrintPin( + childMetadata.specialSizeMeasurements.containerGridProperties, + gridFromProps.gridRowStart ?? gridComputed.gridRowStart, + 'row', + ), + gridRowEnd: nullHandlingPrintPin( + childMetadata.specialSizeMeasurements.containerGridProperties, + gridFromProps.gridRowEnd ?? gridComputed.gridRowEnd, + 'row', + ), position: childMetadata.specialSizeMeasurements.position ?? undefined, } },