Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unset properties in style plugins #6510

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e273da9
Style IR
bkrmendy Oct 7, 2024
d71097e
plugins
bkrmendy Oct 7, 2024
45dc5b2
plugins
bkrmendy Oct 9, 2024
b2fc141
properties to unset
bkrmendy Oct 10, 2024
67be044
reset propertiesToUnset in resetUnpatchedEditorTransientFields
bkrmendy Oct 10, 2024
fc25282
Merge branch 'master' of https://github.com/concrete-utopia/utopia in…
bkrmendy Oct 10, 2024
e2bde11
cleanup
bkrmendy Oct 10, 2024
19c59fc
remove props in inline style plugin
bkrmendy Oct 10, 2024
44df8a7
tests
bkrmendy Oct 10, 2024
2873981
that was subtle
bkrmendy Oct 10, 2024
c17162f
wip
bkrmendy Oct 10, 2024
7a09633
a band-aid solution
bkrmendy Oct 11, 2024
871a835
Merge branch 'master' of https://github.com/concrete-utopia/utopia in…
bkrmendy Oct 14, 2024
d629bb7
undo bandaid fix
bkrmendy Oct 14, 2024
88efe01
deleteValuesAtPath is no-op if no values are to be deleted
bkrmendy Oct 14, 2024
7ee4189
type-level `deleteValuesAtPath`
bkrmendy Oct 14, 2024
01a4b58
remove flag
bkrmendy Oct 14, 2024
be3e257
normalization step action + application
bkrmendy Oct 14, 2024
9dbcdc3
more permissive transformJSXComponentAtElementPath
bkrmendy Oct 14, 2024
e0d6379
respect properties to unset from previous command
bkrmendy Oct 14, 2024
fbc4f97
normalize in `executeFirstApplicableStrategyForContinuousInteraction`
bkrmendy Oct 14, 2024
e1fd3b6
check tailwind feature flag for tests
bkrmendy Oct 14, 2024
f725e92
update `transformJSXComponentAtPath` tests
bkrmendy Oct 14, 2024
211f57b
whoops
bkrmendy Oct 14, 2024
6cd8360
add todo
bkrmendy Oct 14, 2024
4fc40a5
remove leftover code
bkrmendy Oct 14, 2024
b75db38
more granular propertiesToUnset
bkrmendy Oct 14, 2024
d967696
safe deleteValuesAtPath
bkrmendy Oct 15, 2024
b9d46eb
always apply the unsetProperties patch
bkrmendy Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,14 @@ export var storyboard = (
const div = editor.renderedDOM.getByTestId(DivTestId)
expect(div.className).toEqual('top-10 left-10 absolute flex flex-row gap-16')
})

it('can remove tailwind gap by dragging past threshold', async () => {
const editor = await renderTestEditorWithModel(Project, 'await-first-dom-report')
await selectComponentsForTest(editor, [EP.fromString('sb/scene/div')])
await doGapResize(editor, canvasPoint({ x: -100, y: 0 }))
const div = editor.renderedDOM.getByTestId(DivTestId)
expect(div.className).toEqual('top-10 left-10 absolute flex flex-row')
})
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,12 @@ export function deleteConflictingPropsForWidthHeight(
if (propertiesToDelete.length === 0) {
return editorState
} else {
const { editorStateWithChanges: editorStateWithPropsDeleted } = deleteValuesAtPath(
editorState,
target,
propertiesToDelete,
)
const result = deleteValuesAtPath(editorState, target, propertiesToDelete)

if (result == null) {
return editorState
}

return editorStateWithPropsDeleted
return result.editorStateWithChanges
}
}
107 changes: 98 additions & 9 deletions editor/src/components/canvas/commands/delete-properties-command.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import type { JSXElement } from '../../../core/shared/element-template'
import type { EditorState, EditorStatePatch } from '../../../components/editor/store/editor-state'
import {
emptyComments,
jsExpressionValue,
type JSXElement,
} from '../../../core/shared/element-template'
import type {
EditorState,
EditorStatePatch,
UnsetPropertyValues,
} from '../../../components/editor/store/editor-state'
import { modifyUnderlyingElementForOpenFile } from '../../../components/editor/store/editor-state'
import { foldEither } from '../../../core/shared/either'
import { unsetJSXValuesAtPaths } from '../../../core/shared/jsx-attributes'
import type { ElementPath, PropertyPath } from '../../../core/shared/project-file-types'
import type { BaseCommand, CommandFunction, WhenToRun } from './commands'
import type { BaseCommand, CommandFunctionResult, WhenToRun } from './commands'
import * as EP from '../../../core/shared/element-path'
import * as PP from '../../../core/shared/property-path'
import { patchParseSuccessAtElementPath } from './patch-utils'
import { mapDropNulls, stripNulls } from '../../../core/shared/array-utils'
import { applyValuesAtPath } from './adjust-number-command'

export interface DeleteProperties extends BaseCommand {
type: 'DELETE_PROPERTIES'
Expand All @@ -28,19 +38,82 @@ export function deleteProperties(
}
}

export const runDeleteProperties: CommandFunction<DeleteProperties> = (
type PropertiesToUnsetArray = Array<{
prop: keyof UnsetPropertyValues
value: UnsetPropertyValues[keyof UnsetPropertyValues]
path: PropertyPath
}>

function getUnsetProperties(properties: Array<PropertyPath>): PropertiesToUnsetArray {
return mapDropNulls((property) => {
if (property.propertyElements.at(0) !== 'style') {
return null
}

switch (property.propertyElements.at(1)) {
case 'gap':
return { prop: 'gap', value: '0px', path: property }
default:
return null
}
}, properties)
}

function getPropertiesToUnset(propertiesToUnset: PropertiesToUnsetArray): UnsetPropertyValues {
let result: UnsetPropertyValues = {}
for (const { prop, value } of propertiesToUnset) {
result[prop] = value
}
return result
}

function getPropertiesToUnsetPatches(
editorState: EditorState,
command: DeleteProperties,
) => {
// Apply the update to the properties.
const { editorStatePatch: propertyUpdatePatch } = deleteValuesAtPath(
): EditorStatePatch[] {
const unsetProperties = getUnsetProperties(command.properties)
const partialPropertiesToUnset = getPropertiesToUnset(unsetProperties)
const pathString = EP.toString(command.element)
const unsetPropertiesPatch: EditorStatePatch = {
canvas: {
propertiesToUnset: {
$set: {
...editorState.canvas.propertiesToUnset,
[pathString]: {
...editorState.canvas.propertiesToUnset[pathString],
...partialPropertiesToUnset,
},
},
},
},
}

if (command.whenToRun === 'on-complete') {
return [unsetPropertiesPatch]
}

const { editorStatePatch: setPropertiesToUnsetValuePatch } = applyValuesAtPath(
editorState,
command.element,
command.properties,
unsetProperties.map(({ path, value }) => ({
path: path,
value: jsExpressionValue(value, emptyComments),
})),
)
return [unsetPropertiesPatch, setPropertiesToUnsetValuePatch]
}

export const runDeleteProperties = (
editorState: EditorState,
command: DeleteProperties,
): CommandFunctionResult => {
const result = deleteValuesAtPath(editorState, command.element, command.properties)

const updatedEditorState = result == null ? editorState : result.editorStateWithChanges
const propertiesToUnsetPatches = getPropertiesToUnsetPatches(updatedEditorState, command)

return {
editorStatePatches: [propertyUpdatePatch],
editorStatePatches: stripNulls([result?.editorStatePatch, ...propertiesToUnsetPatches]),
commandDescription: `Delete Properties ${command.properties
.map(PP.toString)
.join(',')} on ${EP.toUid(command.element)}`,
Expand All @@ -51,6 +124,22 @@ export function deleteValuesAtPath(
editorState: EditorState,
target: ElementPath,
properties: Array<PropertyPath>,
): { editorStateWithChanges: EditorState; editorStatePatch: EditorStatePatch } | null {
try {
return deleteValuesAtPathUnsafe(editorState, target, properties)
} catch {
return null
}
}

// This function is unsafe, because it calls
// `transformJSXComponentAtElementPath` internally, and
// `transformJSXComponentAtElementPath` throws an error if it cannot find an
// element at the element path passed to it
function deleteValuesAtPathUnsafe(
editorState: EditorState,
target: ElementPath,
properties: Array<PropertyPath>,
): { editorStateWithChanges: EditorState; editorStatePatch: EditorStatePatch } {
const workingEditorState = modifyUnderlyingElementForOpenFile(
target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export const runBulkUpdateProperties: CommandFunction<UpdateBulkProperties> = (
command.properties,
)
const withSetProps = applyValuesAtPath(
withDeletedProps.editorStateWithChanges,
withDeletedProps == null ? editorState : withDeletedProps.editorStateWithChanges,
command.element,
propsToSet.map((property) => ({
path: property.path,
Expand Down
18 changes: 17 additions & 1 deletion editor/src/components/canvas/plugins/inline-style-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import { getLayoutProperty } from '../../../core/layout/getLayoutProperty'
import type { StyleLayoutProp } from '../../../core/layout/layout-helpers-new'
import { MetadataUtils } from '../../../core/model/element-metadata-utils'
import { defaultEither, isLeft, mapEither, right } from '../../../core/shared/either'
import * as EP from '../../../core/shared/element-path'
import type { JSXElement } from '../../../core/shared/element-template'
import { isJSXElement } from '../../../core/shared/element-template'
import { typedObjectKeys } from '../../../core/shared/object-utils'
import * as PP from '../../../core/shared/property-path'
import { styleStringInArray } from '../../../utils/common-constants'
import type { ParsedCSSProperties } from '../../inspector/common/css-utils'
import { withPropertyTag, type WithPropertyTag } from '../canvas-types'
import { foldAndApplyCommandsSimple } from '../commands/commands'
import { deleteProperties } from '../commands/delete-properties-command'
import type { StylePlugin } from './style-plugins'

function getPropertyFromInstance<P extends StyleLayoutProp, T = ParsedCSSProperties[P]>(
Expand Down Expand Up @@ -37,5 +42,16 @@ export const InlineStylePlugin: StylePlugin = {
flexDirection: flexDirection,
}
},
normalizeFromInlineStyle: (editor) => editor,
normalizeFromInlineStyle: (editor, elementsToNormalize) => {
return foldAndApplyCommandsSimple(
editor,
Object.entries(editor.canvas.propertiesToUnset).map(([pathString, propertiesToUnset]) =>
deleteProperties(
'on-complete',
EP.fromString(pathString),
typedObjectKeys(propertiesToUnset).map((p) => PP.create('style', p)),
),
),
)
},
}
3 changes: 2 additions & 1 deletion editor/src/components/canvas/plugins/style-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getTailwindConfigCached,
isTailwindEnabled,
} from '../../../core/tailwind/tailwind-compilation'
import { isFeatureEnabled } from '../../../utils/feature-switches'

export interface StylePlugin {
name: string
Expand All @@ -23,7 +24,7 @@ export const Plugins = {
} as const

export function getActivePlugin(editorState: EditorState): StylePlugin {
if (isTailwindEnabled()) {
if (isFeatureEnabled('Tailwind') && isTailwindEnabled()) {
return TailwindPlugin(getTailwindConfigCached(editorState))
}
return InlineStylePlugin
Expand Down
Loading
Loading