Skip to content

Commit 17c6983

Browse files
authored
Update SetCSSLengthCommand to read from StyleInfo (#6651)
## Problem The flex section relies on the `SetCSSLengthCommand` command to update elements, both for reading and writing styles. In order for that to work in Tailwind projects, where elements don't have an inline style prop, `SetCSSLengthCommand` needs to read element styles from the "right" property, otherwise the command wouldn't work as intended. ## Fix Use the StyleInfo system from to read the element style info. Details - Refactored `SetCSSLengthCommand` to read elements styles through styleInfo - Created a bespoke prop, `CSSLengthProperty`, to track which props are addressed by `SetCSSLengthCommand`. This way it's easy to tell which props need to be supported by StyleInfo to have `SetCSSLengthCommand` working well - Extended StyleInfo to support `zIndex` (all other props in `CSSLengthProperty` were already supported by `StyleInfo` - Updated the style plugins to support the new StyleInfo props - update the signatures of `fixlengthCommand` and `getCSSNumberFromStyleInfo` to use narrower type for some of their arguments **Manual Tests:** I hereby swear that: - [x] I opened a hydrogen project and it loaded - [x] I could navigate to various routes in Play mode
1 parent 21678a3 commit 17c6983

File tree

7 files changed

+50
-60
lines changed

7 files changed

+50
-60
lines changed

editor/src/components/canvas/canvas-strategies/strategies/group-helpers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
trueUpChildrenOfGroupChanged,
66
} from '../../../../components/editor/store/editor-state'
77
import { getLayoutProperty } from '../../../../core/layout/getLayoutProperty'
8-
import type { StyleLayoutProp } from '../../../../core/layout/layout-helpers-new'
8+
import type { LayoutPinnedProp, StyleLayoutProp } from '../../../../core/layout/layout-helpers-new'
99
import type { PropsOrJSXAttributes } from '../../../../core/model/element-metadata-utils'
1010
import {
1111
MetadataUtils,
@@ -551,7 +551,7 @@ export function isEmptyGroup(metadata: ElementInstanceMetadataMap, path: Element
551551
)
552552
}
553553

554-
function fixLengthCommand(path: ElementPath, prop: StyleLayoutProp, size: number): CanvasCommand {
554+
function fixLengthCommand(path: ElementPath, prop: LayoutPinnedProp, size: number): CanvasCommand {
555555
return setCssLengthProperty(
556556
'always',
557557
path,

editor/src/components/canvas/canvas-types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ export type HeightInfo = CSSStyleProperty<CSSNumber>
586586
export type FlexBasisInfo = CSSStyleProperty<CSSNumber>
587587
export type PaddingInfo = CSSStyleProperty<CSSPadding>
588588
export type PaddingSideInfo = CSSStyleProperty<CSSNumber>
589+
export type ZIndexInfo = CSSStyleProperty<CSSNumber>
589590

590591
export interface StyleInfo {
591592
gap: FlexGapInfo | null
@@ -602,6 +603,7 @@ export interface StyleInfo {
602603
paddingRight: PaddingSideInfo | null
603604
paddingBottom: PaddingSideInfo | null
604605
paddingLeft: PaddingSideInfo | null
606+
zIndex: ZIndexInfo | null
605607
}
606608

607609
const emptyStyleInfo: StyleInfo = {
@@ -619,6 +621,7 @@ const emptyStyleInfo: StyleInfo = {
619621
paddingRight: null,
620622
paddingBottom: null,
621623
paddingLeft: null,
624+
zIndex: null,
622625
}
623626

624627
export const isStyleInfoKey = (key: string): key is keyof StyleInfo => key in emptyStyleInfo

editor/src/components/canvas/commands/adjust-css-length-command.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { StyleInfo } from '../canvas-types'
1616

1717
export type CreateIfNotExistant = 'create-if-not-existing' | 'do-not-create-if-doesnt-exist'
1818

19-
type LengthProperty = 'left' | 'right' | 'bottom' | 'top' | 'width' | 'height' | 'flexBasis'
19+
export type LengthProperty = 'left' | 'right' | 'bottom' | 'top' | 'width' | 'height' | 'flexBasis'
2020

2121
type LengthPropertyPath = PropertyPath<['style', LengthProperty]>
2222

editor/src/components/canvas/commands/set-css-length-command.ts

+39-52
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,26 @@
11
import { notice } from '../../../components/common/notice'
2-
import { isLeft, isRight, left } from '../../../core/shared/either'
32
import * as EP from '../../../core/shared/element-path'
4-
import {
5-
emptyComments,
6-
isJSXElement,
7-
jsExpressionValue,
8-
} from '../../../core/shared/element-template'
9-
import type { GetModifiableAttributeResult, ValueAtPath } from '../../../core/shared/jsx-attributes'
10-
import {
11-
getModifiableJSXAttributeAtPath,
12-
jsxSimpleAttributeToValue,
13-
} from '../../../core/shared/jsx-attribute-utils'
143
import { roundTo } from '../../../core/shared/math-utils'
154
import type { ElementPath, PropertyPath } from '../../../core/shared/project-file-types'
165
import * as PP from '../../../core/shared/property-path'
17-
import type { EditorState, EditorStatePatch } from '../../editor/store/editor-state'
18-
import { withUnderlyingTargetFromEditorState } from '../../editor/store/editor-state'
6+
import type { EditorState } from '../../editor/store/editor-state'
197
import type { CSSKeyword, CSSNumber, FlexDirection } from '../../inspector/common/css-utils'
208
import {
219
cssPixelLength,
22-
parseCSSPercent,
2310
printCSSNumber,
2411
printCSSNumberOrKeyword,
2512
} from '../../inspector/common/css-utils'
26-
import type { CreateIfNotExistant } from './adjust-css-length-command'
27-
import { deleteConflictingPropsForWidthHeight } from './adjust-css-length-command'
13+
import type { LengthProperty } from './adjust-css-length-command'
14+
import {
15+
deleteConflictingPropsForWidthHeight,
16+
type CreateIfNotExistant,
17+
} from './adjust-css-length-command'
2818
import type { BaseCommand, CommandFunctionResult, WhenToRun } from './commands'
2919
import { addToastPatch } from './show-toast-command'
30-
import { applyValuesAtPath } from './utils/property-utils'
20+
import { getCSSNumberFromStyleInfo } from './utils/property-utils'
3121
import type { InteractionLifecycle } from '../canvas-strategies/canvas-strategy-types'
22+
import type { StyleUpdate } from '../plugins/style-plugins'
23+
import { getActivePlugin, runStyleUpdateForStrategy } from '../plugins/style-plugins'
3224

3325
type CssNumberOrKeepOriginalUnit =
3426
| { type: 'EXPLICIT_CSS_NUMBER'; value: CSSNumber | CSSKeyword }
@@ -45,10 +37,13 @@ export function setValueKeepingOriginalUnit(
4537
return { type: 'KEEP_ORIGINAL_UNIT', valuePx: valuePx, parentDimensionPx: parentDimensionPx }
4638
}
4739

40+
type CSSLengthProperty = LengthProperty | 'zIndex'
41+
type CSSLengthPropertyPath = PropertyPath<['style', CSSLengthProperty]>
42+
4843
export interface SetCssLengthProperty extends BaseCommand {
4944
type: 'SET_CSS_LENGTH_PROPERTY'
5045
target: ElementPath
51-
property: PropertyPath
46+
property: CSSLengthPropertyPath
5247
value: CssNumberOrKeepOriginalUnit
5348
parentFlexDirection: FlexDirection | null
5449
createIfNonExistant: CreateIfNotExistant
@@ -58,7 +53,7 @@ export interface SetCssLengthProperty extends BaseCommand {
5853
export function setCssLengthProperty(
5954
whenToRun: WhenToRun,
6055
target: ElementPath,
61-
property: PropertyPath,
56+
property: CSSLengthPropertyPath,
6257
value: CssNumberOrKeepOriginalUnit,
6358
parentFlexDirection: FlexDirection | null,
6459
createIfNonExistant: CreateIfNotExistant = 'create-if-not-existing', // TODO remove the default value and set it explicitly everywhere
@@ -90,33 +85,22 @@ export const runSetCssLengthProperty = (
9085
command.parentFlexDirection,
9186
)
9287

93-
// Identify the current value, whatever that may be.
94-
const currentValue: GetModifiableAttributeResult = withUnderlyingTargetFromEditorState(
95-
command.target,
96-
editorState,
97-
left(`no target element was found at path ${EP.toString(command.target)}`),
98-
(_, element) => {
99-
if (isJSXElement(element)) {
100-
return getModifiableJSXAttributeAtPath(element.props, command.property)
101-
} else {
102-
return left(`No JSXElement was found at path ${EP.toString(command.target)}`)
103-
}
104-
},
105-
)
106-
if (isLeft(currentValue)) {
88+
const styleInfo = getActivePlugin(editorStateWithPropsDeleted).styleInfoFactory({
89+
projectContents: editorStateWithPropsDeleted.projectContents,
90+
})(command.target)
91+
92+
if (styleInfo == null) {
10793
return {
10894
editorStatePatches: [],
109-
commandDescription: `Set Css Length Prop: ${EP.toUid(command.target)}/${PP.toString(
110-
command.property,
111-
)} not applied as value is not writeable.`,
95+
commandDescription: `Set Css Length Prop: Element not found at ${EP.toUid(command.target)}`,
11296
}
11397
}
114-
const currentModifiableValue = currentValue.value
115-
const simpleValueResult = jsxSimpleAttributeToValue(currentModifiableValue)
11698

117-
const targetPropertyNonExistant: boolean = currentModifiableValue.type === 'ATTRIBUTE_NOT_FOUND'
99+
const property = command.property.propertyElements[1] // the safety of propertyElements[1] is guaranteed by the LengthPropertyPath type
100+
101+
const currentValue = getCSSNumberFromStyleInfo(styleInfo, property)
118102
if (
119-
targetPropertyNonExistant &&
103+
currentValue.type === 'not-found' &&
120104
command.createIfNonExistant === 'do-not-create-if-doesnt-exist'
121105
) {
122106
return {
@@ -127,18 +111,18 @@ export const runSetCssLengthProperty = (
127111
}
128112
}
129113

130-
let propsToUpdate: Array<ValueAtPath> = []
114+
let propsToUpdate: Array<StyleUpdate> = []
131115

132116
let percentageValueWasReplaced: boolean = false
133-
const javascriptExpressionValueWasReplaced: boolean = isLeft(simpleValueResult) // Left for jsxSimpleAttributeToValue means "not simple" which means a javascript expression like `5 + props.hello`
117+
const javascriptExpressionValueWasReplaced = currentValue.type === 'not-css-number'
134118

135-
const parsePercentResult = parseCSSPercent(simpleValueResult.value)
136119
if (
137-
isRight(parsePercentResult) &&
120+
currentValue.type === 'css-number' &&
121+
currentValue.number.unit === '%' &&
138122
command.value.type === 'KEEP_ORIGINAL_UNIT' &&
139123
command.value.parentDimensionPx != null
140124
) {
141-
const currentValuePercent = parsePercentResult.value
125+
const currentValuePercent = currentValue.number
142126
const valueInPercent = roundTo(
143127
(command.value.valuePx / command.value.parentDimensionPx) * 100,
144128
2,
@@ -150,8 +134,9 @@ export const runSetCssLengthProperty = (
150134
const newValue = printCSSNumber(newValueCssNumber, null)
151135

152136
propsToUpdate.push({
153-
path: command.property,
154-
value: jsExpressionValue(newValue, emptyComments),
137+
type: 'set',
138+
property: property,
139+
value: newValue,
155140
})
156141
} else {
157142
const newCssValue =
@@ -161,29 +146,31 @@ export const runSetCssLengthProperty = (
161146

162147
if (
163148
command.whenReplacingPercentageValues === 'warn-about-replacement' &&
164-
isRight(parsePercentResult)
149+
currentValue.type === 'css-number' &&
150+
currentValue.number.unit === '%'
165151
) {
166152
percentageValueWasReplaced = true
167153
}
168154

169155
const printedValue = printCSSNumberOrKeyword(newCssValue, 'px')
170156

171157
propsToUpdate.push({
172-
path: command.property,
173-
value: jsExpressionValue(printedValue, emptyComments),
158+
type: 'set',
159+
property: property,
160+
value: printedValue,
174161
})
175162
}
176163

177164
// Apply the update to the properties.
178-
const { editorStatePatch: propertyUpdatePatch } = applyValuesAtPath(
165+
const { editorStatePatches } = runStyleUpdateForStrategy(
166+
interactionLifecycle,
179167
editorStateWithPropsDeleted,
180168
command.target,
181169
propsToUpdate,
182170
)
183171

184172
// Always include the property update patch, but potentially also include a warning
185173
// that a percentage based property was replaced with a pixel based one.
186-
let editorStatePatches: Array<EditorStatePatch> = [propertyUpdatePatch]
187174
if (percentageValueWasReplaced) {
188175
editorStatePatches.push(
189176
addToastPatch(

editor/src/components/canvas/commands/utils/property-utils.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,8 @@ export type GetCSSNumberFromStyleInfoResult =
110110

111111
export function getCSSNumberFromStyleInfo(
112112
styleInfo: StyleInfo,
113-
property: string,
113+
property: keyof StyleInfo,
114114
): GetCSSNumberFromStyleInfoResult {
115-
if (!isStyleInfoKey(property)) {
116-
return { type: 'not-found' }
117-
}
118-
119115
const prop = styleInfo[property]
120116
if (prop == null || prop.type === 'not-found') {
121117
return { type: 'not-found' }

editor/src/components/canvas/plugins/inline-style-plugin.ts

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export const InlineStylePlugin: StylePlugin = {
7373
const paddingBottom = getPropertyFromInstance('paddingBottom', element.props)
7474
const paddingLeft = getPropertyFromInstance('paddingLeft', element.props)
7575
const paddingRight = getPropertyFromInstance('paddingRight', element.props)
76+
const zIndex = getPropertyFromInstance('zIndex', element.props)
7677

7778
return {
7879
gap: gap,
@@ -89,6 +90,7 @@ export const InlineStylePlugin: StylePlugin = {
8990
paddingBottom: paddingBottom,
9091
paddingLeft: paddingLeft,
9192
paddingRight: paddingRight,
93+
zIndex: zIndex,
9294
}
9395
},
9496
updateStyles: (editorState, elementPath, updates) => {

editor/src/components/canvas/plugins/tailwind-style-plugin.ts

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const TailwindPropertyMapping: Record<string, string> = {
3737
paddingRight: 'paddingRight',
3838
paddingBottom: 'paddingBottom',
3939
paddingLeft: 'paddingLeft',
40+
zIndex: 'zIndex',
4041
}
4142

4243
function isSupportedTailwindProperty(prop: unknown): prop is keyof typeof TailwindPropertyMapping {
@@ -119,6 +120,7 @@ export const TailwindPlugin = (config: Config | null): StylePlugin => ({
119120
mapping[TailwindPropertyMapping.paddingLeft],
120121
cssParsers.paddingLeft,
121122
),
123+
zIndex: parseTailwindProperty(mapping[TailwindPropertyMapping.zIndex], cssParsers.zIndex),
122124
}
123125
},
124126
updateStyles: (editorState, elementPath, updates) => {

0 commit comments

Comments
 (0)