From 3660d869e418908d491932ac6242200c884080aa Mon Sep 17 00:00:00 2001 From: Federico Ruggi <1081051+ruggi@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:20:48 +0200 Subject: [PATCH] Trim dynamic grids' collapsed extra rows/cols from the computed template (#6496) **Problem:** In grid templates when using `repeat` functions and `auto-fit`/`auto-fill`, the computed style might contain extra rows/cols, collapsed to a `0px` size. Normally this is not an issue, but since for dynamic templates we display the grid wireframe based on the computed dimensions, those extra dimensions would appear visible on the canvas as zombies. This behavior of appending extra collapsed tracks is explained in https://www.w3.org/TR/css-grid-1/#repeat-notation. Screenshot 2024-10-08 at 13 55 48 **Fix:** When parsing the templates, first parse the `fromProps` template. Then, after parsing the computed template, if the `fromProps` template it contains dynamic tracks, trim any dangling collapsed elements in the computed template dimensions array. This is not necessarily optimal (if you have an explicit `0px` sized column in your template, it will be hidden from the canvas), but concretely it will do "the right thing" and you can always use the inspector to target any 0-sized cols/rows (which you would have to do anyways). Screenshot 2024-10-08 at 14 00 03 Fixes #6495 --- editor/src/components/canvas/dom-walker.ts | 64 ++++++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 3a0e979335fe..912d3515f34b 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -9,6 +9,7 @@ import type { GridContainerProperties, GridElementProperties, DomElementMetadata, + GridAutoOrTemplateBase, } from '../../core/shared/element-template' import { specialSizeMeasurements, @@ -16,6 +17,7 @@ import { gridElementProperties, gridAutoOrTemplateFallback, domElementMetadata, + gridAutoOrTemplateDimensions, } from '../../core/shared/element-template' import type { ElementPath } from '../../core/shared/project-file-types' import type { ElementCanvasRectangleCache } from '../../core/shared/dom-utils' @@ -52,6 +54,7 @@ import { parseGridAutoOrTemplateBase, parseGridAutoFlow, isCSSKeyword, + isDynamicGridRepeat, } from '../inspector/common/css-utils' import type { UtopiaStoreAPI } from '../editor/store/store-hook' import { @@ -555,6 +558,10 @@ export function collectDomElementMetadataForElement( function getGridContainerProperties( elementStyle: CSSStyleDeclaration | null, + options?: { + dynamicCols: boolean + dynamicRows: boolean + }, ): GridContainerProperties { if (elementStyle == null) { return { @@ -565,14 +572,23 @@ function getGridContainerProperties( gridAutoFlow: null, } } - const gridTemplateColumns = defaultEither( - gridAutoOrTemplateFallback(elementStyle.gridTemplateColumns), - parseGridAutoOrTemplateBase(elementStyle.gridTemplateColumns), + + const gridTemplateColumns = trimDynamicEmptyDimensions( + defaultEither( + gridAutoOrTemplateFallback(elementStyle.gridTemplateColumns), + parseGridAutoOrTemplateBase(elementStyle.gridTemplateColumns), + ), + options?.dynamicCols === true, ) - const gridTemplateRows = defaultEither( - gridAutoOrTemplateFallback(elementStyle.gridTemplateRows), - parseGridAutoOrTemplateBase(elementStyle.gridTemplateRows), + + const gridTemplateRows = trimDynamicEmptyDimensions( + defaultEither( + gridAutoOrTemplateFallback(elementStyle.gridTemplateRows), + parseGridAutoOrTemplateBase(elementStyle.gridTemplateRows), + ), + options?.dynamicRows === true, ) + const gridAutoColumns = defaultEither( gridAutoOrTemplateFallback(elementStyle.gridAutoColumns), parseGridAutoOrTemplateBase(elementStyle.gridAutoColumns), @@ -590,6 +606,23 @@ function getGridContainerProperties( ) } +function trimDynamicEmptyDimensions( + template: GridAutoOrTemplateBase, + isDynamic: boolean, +): GridAutoOrTemplateBase { + if (!isDynamic) { + return template + } + if (template.type !== 'DIMENSIONS') { + return template + } + + const lastNonEmptyColumn = template.dimensions.findLastIndex( + (d) => d.type === 'KEYWORD' || (d.type === 'NUMBER' && d.value.value !== 0), + ) + return gridAutoOrTemplateDimensions(template.dimensions.slice(0, lastNonEmptyColumn + 1)) +} + function getGridElementProperties( container: GridContainerProperties, elementStyle: CSSStyleDeclaration, @@ -882,8 +915,6 @@ function getSpecialMeasurements( const parentContainerGridProperties = getGridContainerProperties(parentElementStyle) - const containerGridProperties = getGridContainerProperties(elementStyle) - const paddingValue = isRight(padding) ? padding.value : sides(undefined, undefined, undefined, undefined) @@ -898,15 +929,20 @@ function getSpecialMeasurements( ) : null - const containerElementProperties = getGridElementProperties( - parentContainerGridProperties, - elementStyle, - ) const containerGridPropertiesFromProps = getGridContainerProperties(element.style) + const containerGridProperties = getGridContainerProperties(elementStyle, { + dynamicCols: isDynamicGridTemplate(containerGridPropertiesFromProps.gridTemplateColumns), + dynamicRows: isDynamicGridTemplate(containerGridPropertiesFromProps.gridTemplateRows), + }) + const containerElementPropertiesFromProps = getGridElementProperties( parentContainerGridProperties, element.style, ) + const containerElementProperties = getGridElementProperties( + parentContainerGridProperties, + elementStyle, + ) return specialSizeMeasurements( offset, @@ -965,6 +1001,10 @@ function getSpecialMeasurements( ) } +function isDynamicGridTemplate(template: GridAutoOrTemplateBase | null) { + return template?.type === 'DIMENSIONS' && template.dimensions.some((d) => isDynamicGridRepeat(d)) +} + function elementContainsOnlyText(element: HTMLElement): boolean { if (element.childNodes.length === 0) { return false