diff --git a/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx b/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx index 207c1cf6f4b2..e6fe7a6c43b0 100644 --- a/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx +++ b/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx @@ -187,6 +187,7 @@ export function pickCanvasStateFromEditorState( startingMetadata: editorState.jsxMetadata, startingElementPathTree: editorState.elementPathTree, startingAllElementProps: editorState.allElementProps, + propertyControlsInfo: editorState.propertyControlsInfo, } } @@ -207,6 +208,7 @@ export function pickCanvasStateFromEditorStateWithMetadata( startingMetadata: metadata, startingElementPathTree: editorState.elementPathTree, // IMPORTANT! This isn't based on the passed in metadata startingAllElementProps: allElementProps ?? editorState.allElementProps, + propertyControlsInfo: editorState.propertyControlsInfo, } } diff --git a/editor/src/components/canvas/canvas-strategies/canvas-strategy-types.ts b/editor/src/components/canvas/canvas-strategies/canvas-strategy-types.ts index fa2fcf8d83c7..620ca3969e92 100644 --- a/editor/src/components/canvas/canvas-strategies/canvas-strategy-types.ts +++ b/editor/src/components/canvas/canvas-strategies/canvas-strategy-types.ts @@ -9,8 +9,8 @@ import type { InsertionSubject } from '../../editor/editor-modes' import type { CanvasCommand } from '../commands/commands' import type { StrategyApplicationStatus } from './interaction-state' import type { ElementPathTrees } from '../../../core/shared/element-path-tree' -import type { RemixRoutingTable } from '../../editor/store/remix-derived-data' import type { ActiveFrameAction } from '../commands/set-active-frames-command' +import type { PropertyControlsInfo } from '../../custom-code/code-file' // TODO: fill this in, maybe make it an ADT for different strategies export interface CustomStrategyState { @@ -100,6 +100,7 @@ export interface InteractionCanvasState { startingMetadata: ElementInstanceMetadataMap startingElementPathTree: ElementPathTrees startingAllElementProps: AllElementProps + propertyControlsInfo: PropertyControlsInfo } export type InteractionTarget = TargetPaths | InsertionSubjects diff --git a/editor/src/components/canvas/canvas-strategies/post-action-options/navigator-reparent.ts b/editor/src/components/canvas/canvas-strategies/post-action-options/navigator-reparent.ts index f26f09ef0d78..f8e9b8c3fdef 100644 --- a/editor/src/components/canvas/canvas-strategies/post-action-options/navigator-reparent.ts +++ b/editor/src/components/canvas/canvas-strategies/post-action-options/navigator-reparent.ts @@ -58,6 +58,7 @@ function getNavigatorReparentCommands( editor.elementPathTree, wrapperUID, data.dragSources.length, + editor.propertyControlsInfo, ) if (newParentPath == null) { diff --git a/editor/src/components/canvas/canvas-strategies/post-action-options/post-action-paste.ts b/editor/src/components/canvas/canvas-strategies/post-action-options/post-action-paste.ts index 7ef2a55c721f..cbddced76a7c 100644 --- a/editor/src/components/canvas/canvas-strategies/post-action-options/post-action-paste.ts +++ b/editor/src/components/canvas/canvas-strategies/post-action-options/post-action-paste.ts @@ -601,6 +601,7 @@ function getTargetParentForPasteHere( originalContextElementPathTrees: originalPathTree, }, editor.elementPathTree, + editor.propertyControlsInfo, ) if (isLeft(target)) { diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-helpers.ts b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-helpers.ts index 987fb76711b3..bef0a6cc6005 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-helpers.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-helpers.ts @@ -16,6 +16,7 @@ import type { AllElementProps } from '../../../../editor/store/editor-state' import type { InsertionPath } from '../../../../editor/store/insertion-path' import type { ElementPathTrees } from '../../../../../core/shared/element-path-tree' import { assertNever } from '../../../../../core/shared/utils' +import { PropertyControlsInfo } from '../../../../custom-code/code-file' export type ReparentAsAbsolute = 'REPARENT_AS_ABSOLUTE' export type ReparentAsStatic = 'REPARENT_AS_STATIC' @@ -68,6 +69,7 @@ export function findReparentStrategies( canvasState.startingAllElementProps, allowSmallerParent, elementSupportsChildren, + canvasState.propertyControlsInfo, ) if (targetParent == null) { diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-parent-lookup.ts b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-parent-lookup.ts index b0d7f49f685c..e2a94fe44e55 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-parent-lookup.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-helpers/reparent-strategy-parent-lookup.ts @@ -35,6 +35,7 @@ import type { ElementPathTrees } from '../../../../../core/shared/element-path-t import { isConditionalWithEmptyOrTextEditableActiveBranch } from '../../../../../core/model/conditionals' import { getInsertionPathForReparentTarget } from './reparent-helpers' import { treatElementAsGroupLike } from '../group-helpers' +import type { PropertyControlsInfo } from '../../../../custom-code/code-file' export type FindReparentStrategyResult = { strategy: ReparentStrategy @@ -52,6 +53,7 @@ export function getReparentTargetUnified( allElementProps: AllElementProps, allowSmallerParent: AllowSmallerParent, elementSupportsChildren: Array = ['supportsChildren'], + propertyControlsInfo: PropertyControlsInfo, ): ReparentTarget | null { const canvasScale = canvasState.scale @@ -65,6 +67,7 @@ export function getReparentTargetUnified( allElementProps, allowSmallerParent, elementSupportsChildren, + propertyControlsInfo, ) // For Flex parents, we want to be able to insert between two children that don't have a gap between them. @@ -151,6 +154,7 @@ function findValidTargetsUnderPoint( allElementProps: AllElementProps, allowSmallerParent: AllowSmallerParent, elementSupportsChildren: Array = ['supportsChildren'], + propertyControlsInfo: PropertyControlsInfo, ): Array { const projectContents = canvasState.projectContents const openFile = canvasState.openFile ?? null @@ -222,6 +226,7 @@ function findValidTargetsUnderPoint( metadata, target, elementPathTree, + propertyControlsInfo, ), ) ) { diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-metastrategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/reparent-metastrategy.tsx index c41bd5345256..cd7b8b96c523 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-metastrategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-metastrategy.tsx @@ -32,6 +32,7 @@ import { flattenSelection } from './shared-move-strategies-helpers' import type { InsertionPath } from '../../../editor/store/insertion-path' import { childInsertionPath } from '../../../editor/store/insertion-path' import { treatElementAsGroupLike } from './group-helpers' +import { PropertyControlsInfo } from '../../../custom-code/code-file' interface ReparentFactoryAndDetails { targetParent: InsertionPath @@ -158,6 +159,7 @@ function getStartingTargetParentsToFilterOutInner( canvasState.startingAllElementProps, allowSmallerParent, elementSupportsChildren, + canvasState.propertyControlsInfo, ) if (regularReparentTarget != null) { result.push(regularReparentTarget) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/reparent-utils.ts b/editor/src/components/canvas/canvas-strategies/strategies/reparent-utils.ts index 0f8e70c0ec4b..93f98bcc26ab 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/reparent-utils.ts +++ b/editor/src/components/canvas/canvas-strategies/strategies/reparent-utils.ts @@ -5,7 +5,6 @@ import { withUnderlyingTarget, } from '../../../editor/store/editor-state' import { - StaticElementPath, type ElementPath, type Imports, type NodeModules, @@ -45,7 +44,7 @@ import { addElements } from '../../commands/add-elements-command' import type { ElementPathTrees } from '../../../../core/shared/element-path-tree' import { getRequiredGroupTrueUps } from '../../commands/queue-true-up-command' import type { Either } from '../../../../core/shared/either' -import { flatMapEither, foldEither, left, right } from '../../../../core/shared/either' +import { flatMapEither, left, right } from '../../../../core/shared/either' import { maybeBranchConditionalCase } from '../../../../core/model/conditionals' import type { NonEmptyArray } from '../../../../core/shared/array-utils' import { @@ -59,9 +58,9 @@ import { isElementRenderedBySameComponent } from './reparent-helpers/reparent-he import type { ParsedCopyData } from '../../../../utils/clipboard' import { getParseSuccessForFilePath } from '../../canvas-utils' import { renameDuplicateImports } from '../../../../core/shared/import-shared-utils' -import { modify, set } from '../../../../core/shared/optics/optic-utilities' +import { set } from '../../../../core/shared/optics/optic-utilities' import { fromField, fromTypeGuard } from '../../../../core/shared/optics/optic-creators' -import { Optic } from '../../../../core/shared/optics/optics' +import type { PropertyControlsInfo } from '../../../custom-code/code-file' interface GetReparentOutcomeResult { commands: Array @@ -424,6 +423,7 @@ function insertIntoSlot( projectContents: ProjectContentTreeRoot, elementPathTrees: ElementPathTrees, numberOfElementsToInsert: number, + propertyControlsInfo: PropertyControlsInfo, ): ReparentTargetForPaste | null { const targetPath = selectedViews[0] const parentPath = EP.parentPath(targetPath) @@ -448,6 +448,7 @@ function insertIntoSlot( elementPathTrees, wrapperFragmentUID, numberOfElementsToInsert, + propertyControlsInfo, ) if (parentInsertionPath == null) { @@ -540,6 +541,7 @@ function canInsertIntoTarget( elementPathTree: ElementPathTrees, parentTarget: ElementPath, elementsToInsert: JSXElementChild[], + propertyControlsInfo: PropertyControlsInfo, ): boolean { const pastedElementNames = mapDropNulls( (element) => (element.type === 'JSX_ELEMENT' ? element.name : null), @@ -557,6 +559,7 @@ function canInsertIntoTarget( metadata, parentTarget, elementPathTree, + propertyControlsInfo, ) return targetElementSupportsInsertedElement && supportsChildren @@ -568,11 +571,19 @@ function pasteIntoParentOrGrandparent( selectedViews: NonEmptyArray, metadata: ElementInstanceMetadataMap, elementPathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): ReparentTargetForPaste | null { const parentTarget = EP.getCommonParentOfNonemptyPathArray(selectedViews, true) if ( - canInsertIntoTarget(projectContents, metadata, elementPathTree, parentTarget, elementsToInsert) + canInsertIntoTarget( + projectContents, + metadata, + elementPathTree, + parentTarget, + elementsToInsert, + propertyControlsInfo, + ) ) { return { type: 'parent', parentPath: childInsertionPath(parentTarget) } } @@ -585,6 +596,7 @@ function pasteIntoParentOrGrandparent( metadata, parentOfSelected, elementPathTree, + propertyControlsInfo, ) ) { return { type: 'parent', parentPath: childInsertionPath(parentOfSelected) } @@ -603,6 +615,7 @@ export function applyElementCeilingToReparentTarget( elementPathTree: ElementPathTrees, reparentTarget: Either, elementCeiling: ElementPath | null, + propertyControlsInfo: PropertyControlsInfo, ): Either { if (elementCeiling == null) { return reparentTarget @@ -627,6 +640,7 @@ export function applyElementCeilingToReparentTarget( elementPathTree, ceilingStaticPath, elementsToInsert, + propertyControlsInfo, ) ) { return right(set(intendedPathOptic, ceilingStaticPath, targetForPaste)) @@ -656,6 +670,7 @@ export function getTargetParentForOneShotInsertion( elementsToInsert: JSXElementChild[], elementPathTree: ElementPathTrees, insertionCeiling: ElementPath | null, + propertyControlsInfo: PropertyControlsInfo, ): Either { if (!isNonEmptyArray(selectedViews)) { return right({ type: 'parent', parentPath: childInsertionPath(storyboardPath) }) @@ -671,6 +686,7 @@ export function getTargetParentForOneShotInsertion( projectContents, elementPathTree, elementsToInsert.length, + propertyControlsInfo, ) if (insertIntoSlotResult != null) { return right(insertIntoSlotResult) @@ -682,6 +698,7 @@ export function getTargetParentForOneShotInsertion( selectedViews, metadata, elementPathTree, + propertyControlsInfo, ) if (pasteIntoParentOrGrandparentResult != null) { return applyElementCeilingToReparentTarget( @@ -691,6 +708,7 @@ export function getTargetParentForOneShotInsertion( elementPathTree, right(pasteIntoParentOrGrandparentResult), insertionCeiling, + propertyControlsInfo, ) } return left('Cannot find a suitable parent') @@ -703,6 +721,7 @@ export function getTargetParentForPaste( metadata: ElementInstanceMetadataMap, copyData: ParsedCopyData, elementPathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): Either { if (!isNonEmptyArray(selectedViews)) { return right({ type: 'parent', parentPath: childInsertionPath(storyboardPath) }) @@ -725,6 +744,7 @@ export function getTargetParentForPaste( projectContents, elementPathTree, copyData.elementPaste.length, + propertyControlsInfo, ) if (insertIntoSlotResult != null) { return right(insertIntoSlotResult) @@ -745,6 +765,7 @@ export function getTargetParentForPaste( selectedViews, metadata, elementPathTree, + propertyControlsInfo, ) if (pasteIntoParentOrGrandparentResult != null) { return right(pasteIntoParentOrGrandparentResult) diff --git a/editor/src/components/canvas/canvas-strategies/strategies/set-border-radius-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/set-border-radius-strategy.tsx index e27960535979..ba22c7a09ee0 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/set-border-radius-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/set-border-radius-strategy.tsx @@ -105,6 +105,7 @@ export const setBorderRadiusStrategy: CanvasStrategyFactory = ( canvasState.scale, canvasState.startingMetadata, canvasState.startingElementPathTree, + canvasState.propertyControlsInfo, ).has('borderRadius') if (!canShowBorderRadiusControls) { return null diff --git a/editor/src/components/canvas/canvas-strategies/strategies/set-padding-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/set-padding-strategy.tsx index 4c63ec013d7c..6bf2ce4a88db 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/set-padding-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/set-padding-strategy.tsx @@ -106,6 +106,7 @@ export const setPaddingStrategy: CanvasStrategyFactory = (canvasState, interacti canvasState.scale, canvasState.startingMetadata, canvasState.startingElementPathTree, + canvasState.propertyControlsInfo, ).has('padding') if (!canShowPadding) { return null diff --git a/editor/src/components/canvas/commands/add-contain-layout-if-needed-command.ts b/editor/src/components/canvas/commands/add-contain-layout-if-needed-command.ts index d22d20b5341c..19364723ad6d 100644 --- a/editor/src/components/canvas/commands/add-contain-layout-if-needed-command.ts +++ b/editor/src/components/canvas/commands/add-contain-layout-if-needed-command.ts @@ -40,6 +40,7 @@ export const runAddContainLayoutIfNeeded: CommandFunction editor.elementPathTree, wrapperUID, 1, + editor.propertyControlsInfo, ) if (insertionPath == null) { return // maybe this should throw instead? diff --git a/editor/src/components/canvas/controls/insert-mode/insert-mode-hooks.tsx b/editor/src/components/canvas/controls/insert-mode/insert-mode-hooks.tsx index fbb511ad2246..88a71f0f4de5 100644 --- a/editor/src/components/canvas/controls/insert-mode/insert-mode-hooks.tsx +++ b/editor/src/components/canvas/controls/insert-mode/insert-mode-hooks.tsx @@ -6,6 +6,7 @@ import { isInsertMode } from '../../../editor/editor-modes' import { useRefEditorState } from '../../../editor/store/store-hook' import type { MouseCallbacks } from '../select-mode/select-mode-hooks' import { useHighlightCallbacks } from '../select-mode/select-mode-hooks' +import { property } from 'css-tree' function useGetHighlightableViewsForInsertMode() { const storeRef = useRefEditorState((store) => { @@ -19,10 +20,12 @@ function useGetHighlightableViewsForInsertMode() { nodeModules: store.editor.nodeModules.files, remixRoutingTable: store.derived.remixData?.routingTable ?? null, resolve: resolveFn, + propertyControlsInfo: store.editor.propertyControlsInfo, } }) return React.useCallback(() => { - const { componentMetadata, elementPathTree, mode, projectContents } = storeRef.current + const { componentMetadata, elementPathTree, mode, projectContents, propertyControlsInfo } = + storeRef.current if (isInsertMode(mode)) { const allPaths = MetadataUtils.getAllPaths(componentMetadata, elementPathTree) const insertTargets = allPaths.filter((path) => { @@ -31,6 +34,7 @@ function useGetHighlightableViewsForInsertMode() { componentMetadata, path, elementPathTree, + propertyControlsInfo, ) }) return insertTargets diff --git a/editor/src/components/canvas/controls/new-canvas-controls.tsx b/editor/src/components/canvas/controls/new-canvas-controls.tsx index eca9069eef56..9af460e2bd94 100644 --- a/editor/src/components/canvas/controls/new-canvas-controls.tsx +++ b/editor/src/components/canvas/controls/new-canvas-controls.tsx @@ -489,6 +489,7 @@ const NewCanvasControlsInner = (props: NewCanvasControlsInnerProps) => { EP.fromString(p), componentMetadata, pathTrees, + propertyControlsInfo, ), ) ) diff --git a/editor/src/components/canvas/controls/select-mode/controls-common.tsx b/editor/src/components/canvas/controls/select-mode/controls-common.tsx index d30b18a48e65..56fb2a38a81e 100644 --- a/editor/src/components/canvas/controls/select-mode/controls-common.tsx +++ b/editor/src/components/canvas/controls/select-mode/controls-common.tsx @@ -14,6 +14,7 @@ import type { ElementPath } from '../../../../core/shared/project-file-types' import { treatElementAsGroupLikeFromMetadata } from '../../canvas-strategies/strategies/group-helpers' import { assertNever } from '../../../../core/shared/utils' import { mapDropNulls } from '../../../../core/shared/array-utils' +import type { PropertyControlsInfo } from '../../../custom-code/code-file' export const Emdash: string = '\u2014' @@ -202,6 +203,7 @@ export function canShowCanvasPropControl( scale: number, metadata: ElementInstanceMetadataMap, elementPathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): Set { function getControls(element: ElementInstanceMetadata): CanvasPropControl[] { const frame = zeroRectIfNullOrInfinity(element.globalFrame) @@ -225,6 +227,7 @@ export function canShowCanvasPropControl( element.elementPath, metadata, elementPathTree, + propertyControlsInfo, ) ) { return ['borderRadius', 'gap'] diff --git a/editor/src/components/canvas/controls/zero-sized-element-controls.tsx b/editor/src/components/canvas/controls/zero-sized-element-controls.tsx index 4af459baf86d..6b74f22f3837 100644 --- a/editor/src/components/canvas/controls/zero-sized-element-controls.tsx +++ b/editor/src/components/canvas/controls/zero-sized-element-controls.tsx @@ -83,7 +83,7 @@ export const ZeroSizedElementControls = controlForStrategyMemoized( ) const zeroSizeElements = useEditorState( - Substores.metadata, + Substores.metadataAndPropertyControlsInfo, (store) => { if (showAllPossibleElements) { return Object.values(store.editor.jsxMetadata).filter((element) => { @@ -96,6 +96,7 @@ export const ZeroSizedElementControls = controlForStrategyMemoized( element.elementPath, store.editor.jsxMetadata, store.editor.elementPathTree, + store.editor.propertyControlsInfo, ) ) }) @@ -118,6 +119,7 @@ export const ZeroSizedElementControls = controlForStrategyMemoized( child.elementPath, store.editor.jsxMetadata, store.editor.elementPathTree, + store.editor.propertyControlsInfo, ) ) } @@ -341,7 +343,7 @@ export const ZeroSizeResizeControlWrapper = controlForStrategyMemoized( {zeroSizeElements.map((element) => { if (element.globalFrame != null && isFiniteRectangle(element.globalFrame)) { return ( - + = { data.jsxMetadata, path, data.pathTrees, + data.propertyControlsInfo, ) || treatElementAsFragmentLike(data.jsxMetadata, data.allElementProps, data.pathTrees, path), ) diff --git a/editor/src/components/editor/actions/actions.tsx b/editor/src/components/editor/actions/actions.tsx index bf3a6e836a7b..8f861de27d7a 100644 --- a/editor/src/components/editor/actions/actions.tsx +++ b/editor/src/components/editor/actions/actions.tsx @@ -2591,6 +2591,7 @@ export const UPDATE_FNS = { workingEditor.jsxMetadata, target, workingEditor.elementPathTree, + workingEditor.propertyControlsInfo, ) const elementIsFragmentLike = treatElementAsFragmentLike( diff --git a/editor/src/components/editor/actions/wrap-unwrap-helpers.tsx b/editor/src/components/editor/actions/wrap-unwrap-helpers.tsx index 21a10c4032b7..ae47a9237964 100644 --- a/editor/src/components/editor/actions/wrap-unwrap-helpers.tsx +++ b/editor/src/components/editor/actions/wrap-unwrap-helpers.tsx @@ -180,6 +180,7 @@ export function unwrapTextContainingConditional( editor.elementPathTree, wrapperUID, 1, + editor.propertyControlsInfo, ) if (insertionPath == null) { throw new Error('Invalid unwrap insertion path') diff --git a/editor/src/components/editor/convert-callbacks.ts b/editor/src/components/editor/convert-callbacks.ts index 088c2666be71..523f70cf97d1 100644 --- a/editor/src/components/editor/convert-callbacks.ts +++ b/editor/src/components/editor/convert-callbacks.ts @@ -36,6 +36,7 @@ import type { ElementPathTrees } from '../../core/shared/element-path-tree' import type { InsertMenuItem, InsertMenuItemValue } from '../canvas/ui/floating-insert-menu' import { wrapInDivStrategy } from './wrap-in-callbacks' import { commandsForFirstApplicableStrategy } from '../inspector/inspector-strategies/inspector-strategy' +import type { PropertyControlsInfo } from '../custom-code/code-file' export function changeConditionalOrFragment( projectContents: ProjectContentTreeRoot, @@ -45,6 +46,7 @@ export function changeConditionalOrFragment( floatingMenuState: FloatingInsertMenuState, fixedSizeForInsertion: boolean, element: JSXConditionalExpressionWithoutUID | JSXFragmentWithoutUID, + propertyControlsInfo: PropertyControlsInfo, ): Array { let actionsToDispatch: Array = [] const importsToAdd: Imports = @@ -82,6 +84,7 @@ export function changeConditionalOrFragment( elementPathTree, wrapperUID, selectedViews.length, + propertyControlsInfo, ) actionsToDispatch = [ insertInsertable( @@ -120,6 +123,7 @@ export function changeElement( addContentForInsertion: boolean, pickedInsertableComponent: InsertMenuItemValue, source: InsertableComponentGroupType | null, + propertyControlsInfo: PropertyControlsInfo, ): Array { const element = pickedInsertableComponent.element() if (element.type !== 'JSX_ELEMENT') { @@ -136,6 +140,7 @@ export function changeElement( elementPathTree, allElementProps, projectContents, + propertyControlsInfo, ), ]) @@ -189,6 +194,7 @@ export function changeElement( elementPathTree, wrapperUID, selectedViews.length, + propertyControlsInfo, ) // TODO multiselect? actionsToDispatch = [ @@ -231,6 +237,7 @@ export function getActionsToApplyChange( fixedSizeForInsertion: boolean, addContentForInsertion: boolean, insertMenuItemValue: InsertMenuItemValue, + propertyControlsInfo: PropertyControlsInfo, ): Array { const element = insertMenuItemValue.element() switch (element.type) { @@ -244,6 +251,7 @@ export function getActionsToApplyChange( floatingMenuState, fixedSizeForInsertion, element, + propertyControlsInfo, ) case 'JSX_ELEMENT': return changeElement( @@ -257,6 +265,7 @@ export function getActionsToApplyChange( addContentForInsertion, insertMenuItemValue, insertMenuItemValue.source, + propertyControlsInfo, ) case 'JSX_MAP_EXPRESSION': return [] // we don't support converting to maps @@ -273,6 +282,7 @@ export function useConvertTo(): (convertTo: InsertMenuItem | null) => void { const projectContentsRef = useRefEditorState((store) => store.editor.projectContents) const allElementPropsRef = useRefEditorState((store) => store.editor.projectContents) const floatingMenuStateRef = useRefEditorState((store) => store.editor.floatingInsertMenu) + const propertyControlsInfoRef = useRefEditorState((store) => store.editor.propertyControlsInfo) return React.useCallback( (convertToMenuItem: InsertMenuItem | null) => { @@ -288,6 +298,7 @@ export function useConvertTo(): (convertTo: InsertMenuItem | null) => void { false, false, convertTo, + propertyControlsInfoRef.current, ) dispatch(actions, 'everyone') } @@ -300,6 +311,7 @@ export function useConvertTo(): (convertTo: InsertMenuItem | null) => void { jsxMetadataRef, projectContentsRef, selectedViewsRef, + propertyControlsInfoRef, ], ) } diff --git a/editor/src/components/editor/global-shortcuts.tsx b/editor/src/components/editor/global-shortcuts.tsx index daaec29d1e1f..dcd68afa9949 100644 --- a/editor/src/components/editor/global-shortcuts.tsx +++ b/editor/src/components/editor/global-shortcuts.tsx @@ -977,6 +977,7 @@ export function handleKeyDown( editor.elementPathTree, editor.allElementProps, editor.projectContents, + editor.propertyControlsInfo, ), ]) if (commands == null) { diff --git a/editor/src/components/editor/insert-callbacks.ts b/editor/src/components/editor/insert-callbacks.ts index b1ef263adef1..831b4729393a 100644 --- a/editor/src/components/editor/insert-callbacks.ts +++ b/editor/src/components/editor/insert-callbacks.ts @@ -167,6 +167,7 @@ export function useToInsert(): (elementToInsert: InsertMenuItem | null) => void const projectContentsRef = useRefEditorState((store) => store.editor.projectContents) const openFileRef = useRefEditorState((store) => store.editor.canvas.openFile?.filename ?? null) const nodeModulesRef = useRefEditorState((store) => store.editor.nodeModules) + const propertyControlsInfoRef = useRefEditorState((store) => store.editor.propertyControlsInfo) return React.useCallback( (elementToInsert: InsertMenuItem | null) => { @@ -211,6 +212,7 @@ export function useToInsert(): (elementToInsert: InsertMenuItem | null) => void [element.element], elementPathTreeRef.current, elementToInsert.value.insertionCeiling, + propertyControlsInfoRef.current, ) if (isLeft(targetParent)) { @@ -255,6 +257,7 @@ export function useToInsert(): (elementToInsert: InsertMenuItem | null) => void openFileRef, projectContentsRef, selectedViewsRef, + propertyControlsInfoRef, ], ) } diff --git a/editor/src/components/editor/store/insertion-path.ts b/editor/src/components/editor/store/insertion-path.ts index 58c8a19ba5b8..ced153d801ea 100644 --- a/editor/src/components/editor/store/insertion-path.ts +++ b/editor/src/components/editor/store/insertion-path.ts @@ -14,6 +14,7 @@ import { isJSXConditionalExpression } from '../../../core/shared/element-templat import { isRight } from '../../../core/shared/either' import type { ProjectContentTreeRoot } from '../../assets' import type { ElementPathTrees } from '../../../core/shared/element-path-tree' +import type { PropertyControlsInfo } from '../../custom-code/code-file' export type InsertionPath = ChildInsertionPath | ConditionalClauseInsertionPath @@ -211,12 +212,14 @@ export function getInsertionPath( elementPathTree: ElementPathTrees, fragmentWrapperUID: string, numberOfElementsToInsert: number, + propertyControlsInfo: PropertyControlsInfo, ): InsertionPath | null { const targetSupportsChildren = MetadataUtils.targetSupportsChildren( projectContents, metadata, target, elementPathTree, + propertyControlsInfo, ) if (targetSupportsChildren) { diff --git a/editor/src/components/editor/store/store-hook-substore-types.ts b/editor/src/components/editor/store/store-hook-substore-types.ts index bacef5a7e074..99c12e99764f 100644 --- a/editor/src/components/editor/store/store-hook-substore-types.ts +++ b/editor/src/components/editor/store/store-hook-substore-types.ts @@ -30,6 +30,7 @@ export type Substates = { projectServerState: ProjectServerStateSubstate variablesInScope: VariablesInScopeSubstate propertyControlsInfo: PropertyControlsInfoSubstate + metadataAndPropertyControlsInfo: MetadataAndPropertyControlsInfoSubstate } export type StoreKey = keyof Substates @@ -143,6 +144,9 @@ const propertyControlsInfoSubstate = { } as const export type PropertyControlsInfoSubstate = typeof propertyControlsInfoSubstate +export type MetadataAndPropertyControlsInfoSubstate = MetadataSubstate & + PropertyControlsInfoSubstate + export interface DerivedSubstate { derived: DerivedState } diff --git a/editor/src/components/editor/store/store-hook.ts b/editor/src/components/editor/store/store-hook.ts index e39b2a360412..a2819bf14aef 100644 --- a/editor/src/components/editor/store/store-hook.ts +++ b/editor/src/components/editor/store/store-hook.ts @@ -25,6 +25,7 @@ import type { FocusedElementPathSubstate, GithubSubstate, HighlightedHoveredViewsSubstate, + MetadataAndPropertyControlsInfoSubstate, MetadataSubstate, MultiplayerSubstate, NavigatorSubstate, @@ -57,6 +58,7 @@ import { variablesInScopeSubstateKeys, } from './store-hook-substore-types' import { Getter } from '../hook-utils' +import { uniq } from '../../../core/shared/array-utils' // This is how to officially type the store with a subscribeWithSelector middleware as of Zustand 4.1.5 https://github.com/pmndrs/zustand#using-subscribe-with-selector type Store = UseBoundStore, [['zustand/subscribeWithSelector', never]]>> @@ -325,6 +327,16 @@ export const Substores = { propertyControlsInfo: (a: PropertyControlsInfoSubstate, b: PropertyControlsInfoSubstate) => { return keysEquality(propertyControlsInfoSubstateKeys, a.editor, b.editor) }, + metadataAndPropertyControlsInfo: ( + a: MetadataAndPropertyControlsInfoSubstate, + b: MetadataAndPropertyControlsInfoSubstate, + ) => { + return keysEquality( + uniq([...propertyControlsInfoSubstateKeys, ...metadataSubstateKeys]), + a.editor, + b.editor, + ) + }, } as const export const SubstateEqualityFns: { diff --git a/editor/src/components/editor/wrap-in-callbacks.ts b/editor/src/components/editor/wrap-in-callbacks.ts index 07b50ff82710..c81de2b8a740 100644 --- a/editor/src/components/editor/wrap-in-callbacks.ts +++ b/editor/src/components/editor/wrap-in-callbacks.ts @@ -43,6 +43,7 @@ import { absolute } from '../../utils/utils' import { setProperty } from '../canvas/commands/set-property-command' import * as PP from '../../core/shared/property-path' import type { InspectorStrategy } from '../inspector/inspector-strategies/inspector-strategy' +import type { PropertyControlsInfo } from '../custom-code/code-file' type WrapInDivError = | 'No elements selected' @@ -55,6 +56,7 @@ export const wrapInDivStrategy = ( elementPathTrees: ElementPathTrees, allElementProps: AllElementProps, projectContents: ProjectContentTreeRoot, + propertyControlsInfo: PropertyControlsInfo, ): InspectorStrategy => ({ name: 'Wrap in div', strategy: () => { @@ -64,6 +66,7 @@ export const wrapInDivStrategy = ( allElementProps, projectContents, selectedViews, + propertyControlsInfo, ) if (isLeft(result)) { return null @@ -78,6 +81,7 @@ function wrapInDivCommands( allElementProps: AllElementProps, projectContents: ProjectContentTreeRoot, selectedViews: ElementPath[], + propertyControlsInfo: PropertyControlsInfo, ): Either { if (!isNonEmptyArray(selectedViews)) { return left('No elements selected') @@ -108,6 +112,7 @@ function wrapInDivCommands( elementPathTrees, fragmentWrapperUid, 1, + propertyControlsInfo, ) if (insertionPath == null) { return left('Cannot insert into parent of selected elements') @@ -238,6 +243,7 @@ export function useWrapInDiv(): () => void { editorRef.current.allElementProps, editorRef.current.projectContents, editorRef.current.selectedViews, + editorRef.current.propertyControlsInfo, ) if (isLeft(result)) { diff --git a/editor/src/components/navigator/navigator-item/navigator-item-dnd-container.tsx b/editor/src/components/navigator/navigator-item/navigator-item-dnd-container.tsx index af1e8dee1615..7a7b3458180a 100644 --- a/editor/src/components/navigator/navigator-item/navigator-item-dnd-container.tsx +++ b/editor/src/components/navigator/navigator-item/navigator-item-dnd-container.tsx @@ -273,6 +273,7 @@ function canDropInto(editorState: EditorState, moveToEntry: ElementPath): boolea editorState.jsxMetadata, moveToEntry, editorState.elementPathTree, + editorState.propertyControlsInfo, ) return ( diff --git a/editor/src/components/navigator/navigator-item/navigator-item-wrapper.tsx b/editor/src/components/navigator/navigator-item/navigator-item-wrapper.tsx index 902ab97007bc..7d4a00199e75 100644 --- a/editor/src/components/navigator/navigator-item/navigator-item-wrapper.tsx +++ b/editor/src/components/navigator/navigator-item/navigator-item-wrapper.tsx @@ -32,6 +32,7 @@ import type { DerivedSubstate, MetadataSubstate, ProjectContentAndMetadataSubstate, + PropertyControlsInfoSubstate, } from '../../editor/store/store-hook-substore-types' import type { ConditionalClauseNavigatorItemContainerProps, @@ -88,7 +89,15 @@ const elementSupportsChildrenSelector = createCachedSelector( targetElementMetadataSelector, (store: MetadataSubstate) => store.editor.elementPathTree, targetInNavigatorItemsSelector, - (projectContents, metadata, elementMetadata, pathTrees, elementInNavigatorTargets) => { + (store: PropertyControlsInfoSubstate) => store.editor.propertyControlsInfo, + ( + projectContents, + metadata, + elementMetadata, + pathTrees, + elementInNavigatorTargets, + propertyControlsInfo, + ) => { if (!elementInNavigatorTargets || elementMetadata == null) { return false } @@ -97,6 +106,7 @@ const elementSupportsChildrenSelector = createCachedSelector( elementMetadata.elementPath, metadata, pathTrees, + propertyControlsInfo, ) }, )((_, navigatorEntry) => navigatorEntryToKey(navigatorEntry)) diff --git a/editor/src/core/model/element-metadata-utils.spec.tsx b/editor/src/core/model/element-metadata-utils.spec.tsx index d77c25e1124e..d480d04c3bb0 100644 --- a/editor/src/core/model/element-metadata-utils.spec.tsx +++ b/editor/src/core/model/element-metadata-utils.spec.tsx @@ -52,6 +52,10 @@ import { findJSXElementAtStaticPath } from './element-template-utils' import { getUtopiaJSXComponentsFromSuccess } from './project-file-utils' import type { ElementPathTrees } from '../shared/element-path-tree' import { elementPathTree } from '../shared/element-path-tree' +import { + ComponentDescriptorDefaults, + defaultComponentDescriptor, +} from '../../components/custom-code/code-file' const TestScenePath = 'scene-aaa' @@ -426,6 +430,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -440,6 +445,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -454,6 +460,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -468,6 +475,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -482,6 +490,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -511,6 +520,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -527,6 +537,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -543,6 +554,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -559,6 +571,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -596,6 +609,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -610,6 +624,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -624,6 +639,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -638,6 +654,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(true) }) @@ -652,6 +669,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(false) }) @@ -666,6 +684,7 @@ describe('targetElementSupportsChildren', () => { path, { [EP.toString(path)]: element }, {}, + {}, ) expect(actualResult).toEqual(false) }) @@ -711,6 +730,127 @@ export const App = (props) => { path, { [EP.toString(path)]: element }, {}, + {}, + ) + expect(actualResult).toEqual(true) + }) + it('returns false for a component with property controls info including supportsChildren false', () => { + const storyboardCode = `import React from 'react' +import { Scene, Storyboard } from 'utopia-api' +import { App } from '/src/app.js' +export var storyboard = ( + + + + + +` + const storyboardJS = parseResultFromCode(StoryboardFilePath, storyboardCode) + const appCode = `import React from 'react' +export const App = (props) => { + return
+} +` + const appJS = parseResultFromCode('/src/app.js', appCode) + const projectContents = contentsToTree({ + [StoryboardFilePath]: textFile( + textFileContents(storyboardCode, storyboardJS, RevisionsState.BothMatch), + null, + null, + 0, + ), + ['/src/app.js']: textFile( + textFileContents(appCode, appJS, RevisionsState.BothMatch), + null, + null, + 0, + ), + }) + const path = EP.elementPath([[BakedInStoryboardUID, TestScenePath, TestAppUID]]) + const element = dummyInstanceDataForElementType(jsxElementName('App', []), path) + const actualResult = MetadataUtils.targetElementSupportsChildren( + projectContents, + path, + { [EP.toString(path)]: element }, + {}, + { + '/src/app': { + App: { + ...ComponentDescriptorDefaults, + supportsChildren: false, + properties: {}, + inspector: 'all', + emphasis: 'regular', + focus: 'default', + preferredChildComponents: [], + variants: [], + source: defaultComponentDescriptor(), + }, + }, + }, + ) + expect(actualResult).toEqual(false) + }) + it('returns true for a component used from a different file that uses props.children (and explicit supportsChildren=true component annotation)', () => { + const storyboardCode = `import React from 'react' +import { Scene, Storyboard } from 'utopia-api' +import { App } from '/src/app.js' +export var storyboard = ( + + + + + +` + const storyboardJS = parseResultFromCode(StoryboardFilePath, storyboardCode) + const appCode = `import React from 'react' +export const App = (props) => { + return
{props.children}
+} +` + const appJS = parseResultFromCode('/src/app.js', appCode) + const projectContents = contentsToTree({ + [StoryboardFilePath]: textFile( + textFileContents(storyboardCode, storyboardJS, RevisionsState.BothMatch), + null, + null, + 0, + ), + ['/src/app.js']: textFile( + textFileContents(appCode, appJS, RevisionsState.BothMatch), + null, + null, + 0, + ), + }) + const path = EP.elementPath([[BakedInStoryboardUID, TestScenePath, TestAppUID]]) + const element = dummyInstanceDataForElementType(jsxElementName('App', []), path) + const actualResult = MetadataUtils.targetElementSupportsChildren( + projectContents, + path, + { [EP.toString(path)]: element }, + {}, + { + '/src/app': { + App: { + ...ComponentDescriptorDefaults, + supportsChildren: true, + properties: {}, + inspector: 'all', + emphasis: 'regular', + focus: 'default', + preferredChildComponents: [], + variants: [], + source: defaultComponentDescriptor(), + }, + }, + }, ) expect(actualResult).toEqual(true) }) @@ -772,6 +912,7 @@ export const App = (props) => { [EP.toString(path)]: element, }, {}, + {}, ) expect(actualResult).toEqual(false) }) @@ -825,6 +966,7 @@ export const App = (props) => { [EP.toString(path)]: element, }, {}, + {}, ) expect(actualResult).toEqual(false) }) diff --git a/editor/src/core/model/element-metadata-utils.ts b/editor/src/core/model/element-metadata-utils.ts index 17d61b735880..e1e8f50e689b 100644 --- a/editor/src/core/model/element-metadata-utils.ts +++ b/editor/src/core/model/element-metadata-utils.ts @@ -989,10 +989,16 @@ export const MetadataUtils = { path: ElementPath, metadata: ElementInstanceMetadataMap, pathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): boolean { return ( - this.targetElementSupportsChildrenAlsoText(projectContents, path, metadata, pathTree) === - 'supportsChildren' + this.targetElementSupportsChildrenAlsoText( + projectContents, + path, + metadata, + pathTree, + propertyControlsInfo, + ) === 'supportsChildren' ) }, targetElementSupportsChildrenAlsoText( @@ -1000,6 +1006,7 @@ export const MetadataUtils = { path: ElementPath, metadata: ElementInstanceMetadataMap, pathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): ElementSupportsChildren { const instance = MetadataUtils.findElementByElementPath(metadata, path) if (instance == null) { @@ -1017,6 +1024,8 @@ export const MetadataUtils = { path, metadata, pathTree, + projectContents, + propertyControlsInfo, ) if (elementResult != null) { return elementResult @@ -1041,12 +1050,14 @@ export const MetadataUtils = { metadata: ElementInstanceMetadataMap, target: ElementPath | null, pathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): boolean { const targetSupportsChildrenValue = this.targetSupportsChildrenAlsoText( projectContents, metadata, target, pathTree, + propertyControlsInfo, ) return ( targetSupportsChildrenValue !== 'doesNotSupportChildren' && @@ -1058,33 +1069,47 @@ export const MetadataUtils = { metadata: ElementInstanceMetadataMap, target: ElementPath | null, pathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): ElementSupportsChildren { if (target == null) { // Assumed to be reparenting to the canvas root. return 'supportsChildren' - } else { - const instance = MetadataUtils.findElementByElementPath(metadata, target) - if (instance == null) { - return withUnderlyingTarget( - target, - projectContents, - 'doesNotSupportChildren', - (_, element) => { - return ( - elementChildSupportsChildrenAlsoText(element, target, metadata, pathTree) ?? - 'doesNotSupportChildren' - ) - }, - ) - } else { - return MetadataUtils.targetElementSupportsChildrenAlsoText( - projectContents, - target, - metadata, - pathTree, - ) - } } + const componentDescriptor = getComponentDescriptorForTarget( + target, + propertyControlsInfo, + projectContents, + ) + if (componentDescriptor != null && !componentDescriptor.supportsChildren) { + return 'doesNotSupportChildren' + } + const instance = MetadataUtils.findElementByElementPath(metadata, target) + if (instance == null) { + return withUnderlyingTarget( + target, + projectContents, + 'doesNotSupportChildren', + (_, element) => { + return ( + elementChildSupportsChildrenAlsoText( + element, + target, + metadata, + pathTree, + projectContents, + propertyControlsInfo, + ) ?? 'doesNotSupportChildren' + ) + }, + ) + } + return MetadataUtils.targetElementSupportsChildrenAlsoText( + projectContents, + target, + metadata, + pathTree, + propertyControlsInfo, + ) }, targetUsesProperty( projectContents: ProjectContentTreeRoot, diff --git a/editor/src/core/model/element-template-utils.ts b/editor/src/core/model/element-template-utils.ts index 9d67ac47d076..8911f2f32222 100644 --- a/editor/src/core/model/element-template-utils.ts +++ b/editor/src/core/model/element-template-utils.ts @@ -16,11 +16,9 @@ import type { JSXArrayElement, JSXProperty, ElementInstanceMetadataMap, - JSExpressionMapOrOtherJavascript, JSXElementChildWithoutUID, JSIdentifier, JSPropertyAccess, - JSElementAccess, JSExpressionOtherJavaScript, } from '../shared/element-template' import { @@ -42,7 +40,6 @@ import { hasElementsWithin, isUtopiaJSXComponent, isJSPropertyAccessForProperty, - isJSIdentifier, isJSIdentifierForName, isJSExpressionOtherJavaScript, isJSXMapExpression, @@ -86,6 +83,8 @@ import { getAllUniqueUids } from './get-unique-ids' import type { ElementPathTrees } from '../shared/element-path-tree' import { MetadataUtils } from './element-metadata-utils' import { mapValues } from '../shared/object-utils' +import type { PropertyControlsInfo } from '../../components/custom-code/code-file' +import { getComponentDescriptorForTarget } from '../property-controls/property-controls-utils' export function generateUidWithExistingComponents(projectContents: ProjectContentTreeRoot): string { const mockUID = generateMockNextGeneratedUID() @@ -1410,7 +1409,18 @@ export function elementChildSupportsChildrenAlsoText( path: ElementPath, metadata: ElementInstanceMetadataMap, elementPathTree: ElementPathTrees, + projectContents: ProjectContentTreeRoot, + propertyControlsInfo: PropertyControlsInfo, ): ElementSupportsChildren | null { + const componentDescriptor = getComponentDescriptorForTarget( + path, + propertyControlsInfo, + projectContents, + ) + if (componentDescriptor != null && !componentDescriptor.supportsChildren) { + return 'doesNotSupportChildren' + } + if (isJSXConditionalExpression(element)) { if (isTextEditableConditional(path, metadata, elementPathTree)) { return 'conditionalWithText' diff --git a/editor/src/utils/clipboard.ts b/editor/src/utils/clipboard.ts index 3da81746d103..c86cbe57126b 100644 --- a/editor/src/utils/clipboard.ts +++ b/editor/src/utils/clipboard.ts @@ -26,6 +26,7 @@ import { fastForEach } from '../core/shared/utils' import urljoin from 'url-join' import type { ProjectContentTreeRoot } from '../components/assets' import { getProjectFileByFilePath } from '../components/assets' +import type { PropertyControlsInfo } from '../components/custom-code/code-file' import { normalisePathSuccessOrThrowError, normalisePathToUnderlyingTarget, @@ -142,6 +143,7 @@ function getJSXElementPasteActions( originalContextElementPathTrees: clipboardFirstEntry.targetOriginalContextElementPathTrees, }, editor.elementPathTree, + editor.propertyControlsInfo, ) if (isLeft(target)) { @@ -195,6 +197,7 @@ function getFilePasteActions( componentMetadata: ElementInstanceMetadataMap, canvasScale: number, elementPathTree: ElementPathTrees, + propertyControlsInfo: PropertyControlsInfo, ): Array { if (pastedFiles.length == 0) { return [] @@ -213,6 +216,7 @@ function getFilePasteActions( componentMetadata, { elementPaste: [], originalContextMetadata: {}, originalContextElementPathTrees: {} }, // TODO: get rid of this when refactoring pasting images elementPathTree, + propertyControlsInfo, ) if (isLeft(target)) { @@ -271,6 +275,7 @@ export function getActionsForClipboardItems( editor.jsxMetadata, canvasScale, editor.elementPathTree, + editor.propertyControlsInfo, ), ] }