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 4030c029d901..3693491527eb 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-helpers.ts @@ -29,7 +29,13 @@ import { } from '../../../../core/shared/math-utils' import * as PP from '../../../../core/shared/property-path' import { absolute } from '../../../../utils/utils' -import { cssNumber, isCSSKeyword } from '../../../inspector/common/css-utils' +import type { GridDimension } from '../../../inspector/common/css-utils' +import { + cssNumber, + gridCSSRepeat, + isCSSKeyword, + isGridCSSRepeat, +} from '../../../inspector/common/css-utils' import type { CanvasCommand } from '../../commands/commands' import { deleteProperties } from '../../commands/delete-properties-command' import { reorderElement } from '../../commands/reorder-element-command' @@ -45,6 +51,8 @@ import { gridCellCoordinates, } from './grid-cell-bounds' import { memoize } from '../../../../core/shared/memoize' +import { mapDropNulls } from '../../../../core/shared/array-utils' +import { assertNever } from '../../../../core/shared/utils' export function runGridRearrangeMove( targetElement: ElementPath, @@ -647,3 +655,173 @@ function gridTemplateToNumbers(gridTemplate: GridTemplate | null): Array return result } + +type DimensionIndexes = { + originalIndex: number // the index of this element in the original values + repeatedIndex: number // the index of this element, if it's generated via a repeat, inside the repeated values array definition +} + +export type ExpandedGridDimension = GridDimension & { + indexes: DimensionIndexes +} + +function expandedGridDimension( + dim: GridDimension, + originalIndex: number, + repeatedIndex: number = 0, +): ExpandedGridDimension { + return { + ...dim, + indexes: { + originalIndex: originalIndex, + repeatedIndex: repeatedIndex, + }, + } +} + +export function expandGridDimensions(template: GridDimension[]): ExpandedGridDimension[] { + // Expanded representation of the original values, where repeated elements are serialized. + // Each element also contains the indexes information to be used later on to build the resized + // template string. + return template.reduce((acc, cur, index) => { + if (isGridCSSRepeat(cur)) { + const repeatGroup = cur.value.map((dim, repeatedIndex) => + expandedGridDimension(dim, index, repeatedIndex), + ) + let expanded: ExpandedGridDimension[] = [] + for (let i = 0; i < cur.times; i++) { + expanded.push(...repeatGroup) + } + return [...acc, ...expanded] + } else { + return [...acc, expandedGridDimension(cur, index)] + } + }, [] as ExpandedGridDimension[]) +} + +function alterGridTemplateDimensions(params: { + originalValues: GridDimension[] + target: ExpandedGridDimension + patch: AlterGridTemplateDimensionPatch +}): GridDimension[] { + return mapDropNulls((dim, index) => { + if (index !== params.target.indexes.originalIndex) { + return dim + } else if (isGridCSSRepeat(dim)) { + const repeatedIndex = params.target.indexes.repeatedIndex ?? 0 + const before = dim.value.slice(0, repeatedIndex) + const after = dim.value.slice(repeatedIndex + 1) + switch (params.patch.type) { + case 'REMOVE': + if (before.length + after.length === 0) { + return null + } + return gridCSSRepeat(dim.times, [...before, ...after]) + case 'REPLACE': + return gridCSSRepeat(dim.times, [...before, params.patch.newValue, ...after]) + default: + assertNever(params.patch) + } + } else { + switch (params.patch.type) { + case 'REPLACE': + return params.patch.newValue + case 'REMOVE': + return null + default: + assertNever(params.patch) + } + } + }, params.originalValues) +} + +export type ReplaceGridDimensionPatch = { + type: 'REPLACE' + newValue: GridDimension +} + +export type RemoveGridDimensionPatch = { + type: 'REMOVE' +} + +export type AlterGridTemplateDimensionPatch = ReplaceGridDimensionPatch | RemoveGridDimensionPatch + +export function replaceGridTemplateDimensionAtIndex( + template: GridDimension[], + expanded: ExpandedGridDimension[], + index: number, + newValue: GridDimension, +): GridDimension[] { + return alterGridTemplateDimensions({ + originalValues: template, + target: expanded[index], + patch: { + type: 'REPLACE', + newValue: newValue, + }, + }) +} + +export function removeGridTemplateDimensionAtIndex( + template: GridDimension[], + expanded: ExpandedGridDimension[], + index: number, +): GridDimension[] { + return alterGridTemplateDimensions({ + originalValues: template, + target: expanded[index], + patch: { + type: 'REMOVE', + }, + }) +} + +// Return an array of related indexes to a given index inside a grid's template dimensions. +export function getGridRelatedIndexes(params: { + template: GridDimension[] + index: number +}): number[] { + let relatedIndexes: number[][][] = [] // This looks scary but it's not! It's just a list of indexes, containing a list of the indexes *per group element*. + // For example, 1fr repeat(3, 10px 20px) 1fr, will be represented as: + /** + * [ + * [ [0] ] + * [ [1, 3] [2, 4] ] + * [ [5] ] + * ] + */ + let elementCount = 0 // basically the expanded index + for (const dim of params.template) { + if (dim.type === 'REPEAT') { + let groupIndexes: number[][] = [] + // for each value push the related indexes as many times as the repeats counter + for (let valueIndex = 0; valueIndex < dim.value.length; valueIndex++) { + let repeatedValueIndexes: number[] = [] + for (let repeatIndex = 0; repeatIndex < dim.times; repeatIndex++) { + repeatedValueIndexes.push(elementCount + valueIndex + repeatIndex * dim.value.length) + } + groupIndexes.push(repeatedValueIndexes) + } + relatedIndexes.push(groupIndexes) + elementCount += dim.value.length * dim.times // advance the counter as many times as the repeated values *combined* + } else { + relatedIndexes.push([[elementCount]]) + elementCount++ + } + } + + // Now, expand the indexes calculated above so they "flatten out" to match the generated values + let expandedRelatedIndexes: number[][] = [] + params.template.forEach((dim, dimIndex) => { + if (dim.type === 'REPEAT') { + for (let repeatIndex = 0; repeatIndex < dim.times * dim.value.length; repeatIndex++) { + const indexes = relatedIndexes[dimIndex][repeatIndex % dim.value.length] + expandedRelatedIndexes.push(indexes) + } + } else { + expandedRelatedIndexes.push(relatedIndexes[dimIndex][0]) + } + }) + + return expandedRelatedIndexes[params.index] ?? [] +} diff --git a/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.ts index 1489e1b20d2b..b99bbaf32607 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.ts @@ -25,9 +25,7 @@ import type { GridDimension } from '../../../../components/inspector/common/css- import { cssNumber, gridCSSNumber, - gridCSSRepeat, isGridCSSNumber, - isGridCSSRepeat, printArrayGridDimensions, } from '../../../../components/inspector/common/css-utils' import { toFirst } from '../../../../core/shared/optics/optic-utilities' @@ -36,6 +34,7 @@ import type { Either } from '../../../../core/shared/either' import { foldEither, isLeft, isRight } from '../../../../core/shared/either' import { roundToNearestWhole } from '../../../../core/shared/math-utils' import type { GridAutoOrTemplateBase } from '../../../../core/shared/element-template' +import { expandGridDimensions, replaceGridTemplateDimensionAtIndex } from './grid-helpers' export const resizeGridStrategy: CanvasStrategyFactory = ( canvasState: InteractionCanvasState, @@ -116,24 +115,7 @@ export const resizeGridStrategy: CanvasStrategyFactory = ( return emptyStrategyApplicationResult } - // Expanded representation of the original values, where repeated elements are serialized. - // Each element also contains the indexes information to be used later on to build the resized - // template string. - const expandedOriginalValues = originalValues.dimensions.reduce((acc, cur, index) => { - if (isGridCSSRepeat(cur)) { - const repeatGroup = cur.value.map((dim, repeatedIndex) => - expandedGridDimension(dim, index, repeatedIndex), - ) - let expanded: ExpandedGridDimension[] = [] - for (let i = 0; i < cur.times; i++) { - expanded.push(...repeatGroup) - } - return [...acc, ...expanded] - } else { - return [...acc, expandedGridDimension(cur, index)] - } - }, [] as ExpandedGridDimension[]) - + const expandedOriginalValues = expandGridDimensions(originalValues.dimensions) const mergedValues: GridAutoOrTemplateBase = { type: calculatedValues.type, dimensions: calculatedValues.dimensions.map((dim, index) => { @@ -181,11 +163,12 @@ export const resizeGridStrategy: CanvasStrategyFactory = ( areaName, ) - const newDimensions = buildResizedDimensions({ - newValue: newValue, - originalValues: originalValues.dimensions, - target: expandedOriginalValues[control.columnOrRow], - }) + const newDimensions = replaceGridTemplateDimensionAtIndex( + originalValues.dimensions, + expandedOriginalValues, + control.columnOrRow, + newValue, + ) const propertyValueAsString = printArrayGridDimensions(newDimensions) @@ -207,51 +190,6 @@ export const resizeGridStrategy: CanvasStrategyFactory = ( } } -type DimensionIndexes = { - originalIndex: number // the index of this element in the original values - repeatedIndex: number // the index of this element, if it's generated via a repeat, inside the repeated values array definition -} - -function expandedGridDimension( - dim: GridDimension, - originalIndex: number, - repeatedIndex: number = 0, -): ExpandedGridDimension { - return { - ...dim, - indexes: { - originalIndex: originalIndex, - repeatedIndex: repeatedIndex, - }, - } -} - -type ExpandedGridDimension = GridDimension & { - indexes: DimensionIndexes -} - -function buildResizedDimensions(params: { - newValue: GridDimension - originalValues: GridDimension[] - target: ExpandedGridDimension -}) { - return params.originalValues.map((dim, index) => { - if (index !== params.target.indexes.originalIndex) { - return dim - } else if (isGridCSSRepeat(dim)) { - const repeatedIndex = params.target.indexes.repeatedIndex ?? 0 - const repeatGroup = [ - ...dim.value.slice(0, repeatedIndex), - params.newValue, - ...dim.value.slice(repeatedIndex + 1), - ] - return gridCSSRepeat(dim.times, repeatGroup) - } else { - return params.newValue - } - }) -} - function getNewDragValue( dragAmount: number, isFractional: boolean, diff --git a/editor/src/components/canvas/controls/grid-controls.tsx b/editor/src/components/canvas/controls/grid-controls.tsx index 5992931ef155..3d64376a27f6 100644 --- a/editor/src/components/canvas/controls/grid-controls.tsx +++ b/editor/src/components/canvas/controls/grid-controls.tsx @@ -89,6 +89,7 @@ import { getGridPlaceholderDomElementFromCoordinates, gridCellTargetId, } from '../canvas-strategies/strategies/grid-cell-bounds' +import { getGridRelatedIndexes } from '../canvas-strategies/strategies/grid-helpers' const CELL_ANIMATION_DURATION = 0.15 // seconds @@ -360,51 +361,10 @@ export const GridResizing = React.memo((props: GridResizingProps) => { if (props.fromPropsAxisValues?.type !== 'DIMENSIONS' || resizingIndex == null) { return [] } - - // Build an array of coresizing indexes per element. - let coresizeIndexes: number[][][] = [] // This looks scary but it's not! It's just a list of indexes, containing a list of the indexes *per group element*. - // For example, 1fr repeat(3, 10px 20px) 1fr, will be represented as: - /** - * [ - * [ [0] ] - * [ [1, 3] [2, 4] ] - * [ [5] ] - * ] - */ - let elementCount = 0 // basically the expanded index - for (const dim of props.fromPropsAxisValues.dimensions) { - if (dim.type === 'REPEAT') { - let groupIndexes: number[][] = [] - // for each value push the coresize indexes as many times as the repeats counter - for (let valueIndex = 0; valueIndex < dim.value.length; valueIndex++) { - let repeatedValueIndexes: number[] = [] - for (let repeatIndex = 0; repeatIndex < dim.times; repeatIndex++) { - repeatedValueIndexes.push(elementCount + valueIndex + repeatIndex * dim.value.length) - } - groupIndexes.push(repeatedValueIndexes) - } - coresizeIndexes.push(groupIndexes) - elementCount += dim.value.length * dim.times // advance the counter as many times as the repeated values *combined* - } else { - coresizeIndexes.push([[elementCount]]) - elementCount++ - } - } - - // Now, expand the indexes calculated above so they "flatten out" to match the generated values - let expandedCoresizeIndexes: number[][] = [] - props.fromPropsAxisValues.dimensions.forEach((dim, dimIndex) => { - if (dim.type === 'REPEAT') { - for (let repeatIndex = 0; repeatIndex < dim.times * dim.value.length; repeatIndex++) { - const indexes = coresizeIndexes[dimIndex][repeatIndex % dim.value.length] - expandedCoresizeIndexes.push(indexes) - } - } else { - expandedCoresizeIndexes.push(coresizeIndexes[dimIndex][0]) - } + return getGridRelatedIndexes({ + template: props.fromPropsAxisValues.dimensions, + index: resizingIndex, }) - - return expandedCoresizeIndexes[resizingIndex] ?? [] }, [props.fromPropsAxisValues, resizingIndex]) if (props.axisValues == null) { diff --git a/editor/src/components/inspector/common/css-utils.ts b/editor/src/components/inspector/common/css-utils.ts index 5bee0ef90a05..1140f4dd6c4d 100644 --- a/editor/src/components/inspector/common/css-utils.ts +++ b/editor/src/components/inspector/common/css-utils.ts @@ -823,14 +823,18 @@ export function printCSSNumber( export function printGridDimension(dimension: GridDimension): string { switch (dimension.type) { - case 'KEYWORD': - return dimension.value.value - case 'NUMBER': + case 'KEYWORD': { + const areaName = dimension.areaName != null ? `[${dimension.areaName}] ` : '' + return `${areaName}${dimension.value.value}` + } + case 'NUMBER': { const printed = printCSSNumber(dimension.value, null) const areaName = dimension.areaName != null ? `[${dimension.areaName}] ` : '' return `${areaName}${printed}` - case 'REPEAT': + } + case 'REPEAT': { return `repeat(${dimension.times}, ${printArrayGridDimensions(dimension.value)})` + } default: assertNever(dimension) } diff --git a/editor/src/components/inspector/flex-section.spec.browser2.tsx b/editor/src/components/inspector/flex-section.spec.browser2.tsx index 61e646a27eca..f8ad05c41e96 100644 --- a/editor/src/components/inspector/flex-section.spec.browser2.tsx +++ b/editor/src/components/inspector/flex-section.spec.browser2.tsx @@ -60,10 +60,24 @@ describe('flex section', () => { ) await selectComponentsForTest(renderResult, [EP.fromString('sb/grid')]) const control = await screen.findByTestId('grid-dimension-column-0') - await typeIntoField(control, '100') + await typeIntoField(control, '100px') const grid = await renderResult.renderedDOM.findByTestId('grid') expect(grid.style.gridTemplateColumns).toEqual('100px auto auto') }) + it('updates a repeated value', async () => { + const renderResult = await renderTestEditorWithCode( + gridProjectWithRepeat, + 'await-first-dom-report', + ) + await selectComponentsForTest(renderResult, [EP.fromString('sb/grid')]) + await typeIntoField(await screen.findByTestId('grid-dimension-column-2'), '42') + + const grid = await renderResult.renderedDOM.findByTestId('grid') + expect(grid.style.gridTemplateColumns).toEqual('[area1] 1fr repeat(2, 10px 42px) 2fr') + + await typeIntoField(await screen.findByTestId('grid-dimension-column-1'), '.5fr') + expect(grid.style.gridTemplateColumns).toEqual('[area1] 1fr repeat(2, 0.5fr 42px) 2fr') + }) }) }) @@ -190,3 +204,62 @@ export var storyboard = ( ) ` + +const gridProjectWithRepeat = ` +import * as React from 'react' +import { Storyboard } from 'utopia-api' + +export var storyboard = ( + +
+
+
+
+ a test +
+
+ +) +` diff --git a/editor/src/components/inspector/flex-section.tsx b/editor/src/components/inspector/flex-section.tsx index 87242505e12f..6172d7d3d9cb 100644 --- a/editor/src/components/inspector/flex-section.tsx +++ b/editor/src/components/inspector/flex-section.tsx @@ -35,6 +35,7 @@ import type { CSSKeyword, CSSNumber, GridAutoFlow, + GridCSSKeyword, GridDiscreteDimension, UnknownOrEmptyInput, ValidGridDimensionKeyword, @@ -51,7 +52,9 @@ import { isEmptyInputValue, isGridCSSKeyword, isGridCSSNumber, + isGridCSSRepeat, isValidGridDimensionKeyword, + printArrayGridDimensions, type GridDimension, } from './common/css-utils' import { applyCommandsAction, transientActions } from '../editor/actions/action-creators' @@ -73,7 +76,12 @@ import { type ElementInstanceMetadata, type GridElementProperties, } from '../../core/shared/element-template' -import { setGridPropsCommands } from '../canvas/canvas-strategies/strategies/grid-helpers' +import { + expandGridDimensions, + removeGridTemplateDimensionAtIndex, + replaceGridTemplateDimensionAtIndex, + setGridPropsCommands, +} from '../canvas/canvas-strategies/strategies/grid-helpers' import { type CanvasCommand } from '../canvas/commands/commands' import type { DropdownMenuItem } from '../../uuiui/radix-components' import { @@ -169,18 +177,7 @@ export const FlexSection = React.memo(() => { }), }) - const reduced = merged.reduce((acc, cur) => { - if (cur.type === 'REPEAT') { - let expanded: GridDiscreteDimension[] = [] - for (let i = 0; i < cur.times; i++) { - expanded.push(...cur.value.filter((v) => v.type !== 'REPEAT')) - } - return acc.concat(...expanded) - } - return acc.concat(cur) - }, [] as GridDiscreteDimension[]) - - return reduced + return merged.filter((v) => v.type !== 'REPEAT') }, [grid]) const rows = React.useMemo((): GridDiscreteDimension[] => { @@ -198,14 +195,7 @@ export const FlexSection = React.memo(() => { }), }) - const reduced = merged.reduce((acc, cur) => { - if (cur.type === 'REPEAT') { - return acc.concat(...cur.value.filter((v) => v.type !== 'REPEAT')) - } - return acc.concat(cur) - }, [] as GridDiscreteDimension[]) - - return reduced + return merged.filter((v) => v.type !== 'REPEAT') }, [grid]) return ( @@ -279,22 +269,60 @@ const TemplateDimensionControl = React.memo( const metadataRef = useRefEditorState((store) => store.editor.jsxMetadata) + const template = React.useMemo(() => { + const fromProps = + axis === 'column' + ? grid.specialSizeMeasurements.containerGridPropertiesFromProps.gridTemplateColumns + : grid.specialSizeMeasurements.containerGridPropertiesFromProps.gridTemplateRows + if (fromProps?.type === 'DIMENSIONS' && fromProps.dimensions.length === 0) { + return { type: 'DIMENSIONS', dimensions: values } + } + return fromProps + }, [grid, axis, values]) + + const expandedTemplate = React.useMemo(() => { + if (template?.type !== 'DIMENSIONS') { + return [] + } + return expandGridDimensions(template.dimensions) + }, [template]) + const onUpdate = React.useCallback( (index: number) => (value: UnknownOrEmptyInput>) => { - const newValues = [...values] - const gridValueAtIndex = values[index] - if (isCSSNumber(value)) { - const maybeUnit = isGridCSSNumber(gridValueAtIndex) ? gridValueAtIndex.value.unit : null - newValues[index] = gridCSSNumber( - cssNumber(value.value, value.unit ?? maybeUnit), - gridValueAtIndex.areaName, - ) - } else if (isCSSKeyword(value)) { - newValues[index] = gridCSSKeyword(value, gridValueAtIndex.areaName) - } else if (isEmptyInputValue(value)) { - newValues[index] = gridCSSKeyword(cssKeyword('auto'), gridValueAtIndex.areaName) + if (template?.type !== 'DIMENSIONS') { + return + } + + function getNewValue() { + const gridValueAtIndex = values[index] + if (isCSSNumber(value)) { + const maybeUnit = isGridCSSNumber(gridValueAtIndex) + ? gridValueAtIndex.value.unit + : null + return gridCSSNumber( + cssNumber(value.value, value.unit ?? maybeUnit), + gridValueAtIndex.areaName, + ) + } else if (isCSSKeyword(value)) { + return gridCSSKeyword(value, gridValueAtIndex.areaName) + } else if (isEmptyInputValue(value)) { + return gridCSSKeyword(cssKeyword('auto'), gridValueAtIndex.areaName) + } else { + return null + } } + const newValue = getNewValue() + if (newValue == null) { + return + } + + const newDimensions = replaceGridTemplateDimensionAtIndex( + template.dimensions, + expandedTemplate, + index, + newValue, + ) dispatch([ applyCommandsAction([ @@ -302,24 +330,32 @@ const TemplateDimensionControl = React.memo( 'always', grid.elementPath, PP.create('style', axis === 'column' ? 'gridTemplateColumns' : 'gridTemplateRows'), - gridNumbersToTemplateString(newValues), + printArrayGridDimensions(newDimensions), ), ]), ]) }, - [grid, values, dispatch, axis], + [grid, values, dispatch, axis, template, expandedTemplate], ) const onRemove = React.useCallback( (index: number) => () => { - const newValues = values.filter((_, idx) => idx !== index) + if (template?.type !== 'DIMENSIONS') { + return + } + + const newValues = removeGridTemplateDimensionAtIndex( + template.dimensions, + expandedTemplate, + index, + ) let commands: CanvasCommand[] = [ setProperty( 'always', grid.elementPath, PP.create('style', axis === 'column' ? 'gridTemplateColumns' : 'gridTemplateRows'), - gridNumbersToTemplateString(newValues), + printArrayGridDimensions(newValues), ), ] @@ -373,25 +409,33 @@ const TemplateDimensionControl = React.memo( dispatch([applyCommandsAction(commands)]) }, - [grid, values, dispatch, axis, metadataRef], + [grid, dispatch, axis, metadataRef, template, expandedTemplate], ) - const onAdd = React.useCallback(() => { - const newValues = values.concat(gridCSSNumber(cssNumber(1, 'fr'), null)) + const onAppend = React.useCallback(() => { + if (template?.type !== 'DIMENSIONS') { + return + } + + const newValues = [...template.dimensions, gridCSSNumber(cssNumber(1, 'fr'), null)] + dispatch([ applyCommandsAction([ setProperty( 'always', grid.elementPath, PP.create('style', axis === 'column' ? 'gridTemplateColumns' : 'gridTemplateRows'), - gridNumbersToTemplateString(newValues), + printArrayGridDimensions(newValues), ), ]), ]) - }, [dispatch, grid, axis, values]) + }, [dispatch, grid, axis, template]) const onRename = React.useCallback( (index: number) => () => { + if (template?.type !== 'DIMENSIONS') { + return + } const container = grid.specialSizeMeasurements.containerGridProperties const dimensions = axis === 'column' ? container.gridTemplateColumns : container.gridTemplateRows @@ -408,22 +452,19 @@ const TemplateDimensionControl = React.memo( const newAreaName: string | null = rawNewAreaName.length === 0 ? null : sanitizeAreaName(rawNewAreaName) - const newValues = values.map((value, idx) => { - if (idx !== index) { - return value - } - return { - ...value, - areaName: newAreaName, - } - }) + const newValues = replaceGridTemplateDimensionAtIndex( + template.dimensions, + expandedTemplate, + index, + { ...values[index], areaName: newAreaName }, + ) let commands: CanvasCommand[] = [ setProperty( 'always', grid.elementPath, PP.create('style', axis === 'column' ? 'gridTemplateColumns' : 'gridTemplateRows'), - gridNumbersToTemplateString(newValues), + printArrayGridDimensions(newValues), ), ] @@ -448,7 +489,7 @@ const TemplateDimensionControl = React.memo( dispatch([applyCommandsAction(commands)]) }, - [grid, axis, values, dispatch, metadataRef], + [grid, axis, values, dispatch, metadataRef, template, expandedTemplate], ) const dropdownMenuItems = React.useCallback( @@ -495,7 +536,7 @@ const TemplateDimensionControl = React.memo(
{title}
- +
{values.map((value, index) => ( @@ -652,26 +693,6 @@ function renameAreaInTemplateAtIndex( } } -function gridNumbersToTemplateString(values: GridDiscreteDimension[]) { - return values - .map((v) => { - function getValue(): string { - switch (v.type) { - case 'KEYWORD': - return v.value.value - case 'NUMBER': - return `${v.value.value}${v.value.unit != null ? `${v.value.unit}` : 'px'}` - default: - assertNever(v) - } - } - const areaName = v.areaName != null ? `[${v.areaName}] ` : '' - const value = getValue() - return `${areaName}${value}` - }) - .join(' ') -} - function getGridTemplateAxisValues(template: { calculated: GridAutoOrTemplateBase | null fromProps: GridAutoOrTemplateBase | null @@ -1065,13 +1086,17 @@ export function mergeGridTemplateValues({ fromProps: GridDimension[] autoValues: GridDimension[] }): GridDimension[] { + const expanded = fromProps.flatMap((v) => { + return isGridCSSRepeat(v) ? Array(v.times).fill(v.value).flat() : v + }) + function getExplicitValue(dimension: GridDimension, index: number): GridDimension { - if (fromProps.length === 0) { + if (expanded.length === 0) { return gridCSSKeyword(cssKeyword('auto'), dimension.areaName) - } else if (fromProps[index] == null) { + } else if (expanded[index] == null) { return dimension } else { - return fromProps[index] + return expanded[index] } } @@ -1082,7 +1107,10 @@ export function mergeGridTemplateValues({ const autoValue = autoValues.at(autoValueIndex) if (isGridCSSKeyword(explicitValue) && explicitValue.value.value === 'auto') { - return autoValue ?? explicitValue + return { + ...(autoValue ?? explicitValue), + areaName: explicitValue.areaName ?? autoValue?.areaName ?? null, + } as GridCSSKeyword } return explicitValue