diff --git a/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx b/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx index 896e9abd5155..14af9736db18 100644 --- a/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx +++ b/editor/src/components/canvas/canvas-strategies/canvas-strategies.tsx @@ -5,7 +5,6 @@ import type { ElementInstanceMetadataMap } from '../../../core/shared/element-te import { arrayEqualsByReference, assertNever } from '../../../core/shared/utils' import type { AllElementProps, - DerivedState, EditorState, EditorStorePatched, } from '../../editor/store/editor-state' @@ -63,6 +62,9 @@ import type { InsertionSubject, InsertionSubjectWrapper } from '../../editor/edi import { generateUidWithExistingComponents } from '../../../core/model/element-template-utils' import { retargetStrategyToChildrenOfFragmentLikeElements } from './strategies/fragment-like-helpers' import { MetadataUtils } from '../../../core/model/element-metadata-utils' +import { rearrangeGridSwapStrategy } from './strategies/rearrange-grid-swap-strategy' +import { rearrangeGridMoveStrategy } from './strategies/rearrange-grid-move-strategy' +import { gridCellResizeStrategy } from './strategies/grid-cell-resize-strategy' import { gridRearrangeMoveStrategy } from './strategies/grid-rearrange-move-strategy' import { resizeGridStrategy } from './strategies/resize-grid-strategy' @@ -92,6 +94,9 @@ const moveOrReorderStrategies: MetaCanvasStrategy = ( convertToAbsoluteAndMoveStrategy, convertToAbsoluteAndMoveAndSetParentFixedStrategy, reorderSliderStategy, + rearrangeGridMoveStrategy, + rearrangeGridSwapStrategy, + gridCellResizeStrategy, gridRearrangeMoveStrategy, ], ) @@ -586,7 +591,7 @@ export function useGetApplicableStrategyControls(): Array + MetadataUtils.isGridLayoutedContainer( + MetadataUtils.findElementByElementPath(canvasState.startingMetadata, EP.parentPath(t)), + ), + ) + ) { + return null + } + return { id: DoNothingStrategyID, name: 'No Default Available', diff --git a/editor/src/components/canvas/canvas-strategies/strategies/grid-cell-resize-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/grid-cell-resize-strategy.ts new file mode 100644 index 000000000000..1d2c4585c6c9 --- /dev/null +++ b/editor/src/components/canvas/canvas-strategies/strategies/grid-cell-resize-strategy.ts @@ -0,0 +1,89 @@ +import { MetadataUtils } from '../../../../core/model/element-metadata-utils' +import * as EP from '../../../../core/shared/element-path' +import type { ElementPath } from '../../../../core/shared/project-file-types' +import { create } from '../../../../core/shared/property-path' +import type { CanvasCommand } from '../../commands/commands' +import { setProperty } from '../../commands/set-property-command' +import { GridControls, GridResizeShadow, TargetGridCell } from '../../controls/grid-controls' +import type { CanvasStrategyFactory } from '../canvas-strategies' +import { onlyFitWhenDraggingThisControl } from '../canvas-strategies' +import type { InteractionCanvasState } from '../canvas-strategy-types' +import { + getTargetPathsFromInteractionTarget, + emptyStrategyApplicationResult, + strategyApplicationResult, +} from '../canvas-strategy-types' +import type { InteractionSession } from '../interaction-state' + +export const gridCellResizeStrategy: CanvasStrategyFactory = ( + canvasState: InteractionCanvasState, + interactionSession: InteractionSession | null, +) => { + const selectedElements = getTargetPathsFromInteractionTarget(canvasState.interactionTarget) + if (selectedElements.length !== 1) { + return null + } + + const selectedElement = selectedElements[0] + const ok = MetadataUtils.isGridLayoutedContainer( + MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + EP.parentPath(selectedElement), + ), + ) + if (!ok) { + return null + } + + return { + id: 'grid-cell-resize-strategy', + name: 'Resize Grid Cell', + descriptiveLabel: 'Resize Grid Cell', + icon: { + category: 'tools', + type: 'pointer', + }, + controlsToRender: [ + { + control: GridControls, + props: {}, + key: `grid-controls-${EP.toString(selectedElement)}`, + show: 'always-visible', + }, + { + control: GridResizeShadow, + props: { elementPath: selectedElement }, + key: `grid-resize-shadow-${EP.toString(selectedElement)}`, + show: 'always-visible', + }, + ], + fitness: onlyFitWhenDraggingThisControl(interactionSession, 'GRID_RESIZE_HANDLE', 1), + apply: () => { + if ( + interactionSession == null || + interactionSession.interactionData.type !== 'DRAG' || + interactionSession.interactionData.drag == null || + interactionSession.activeControl.type !== 'GRID_RESIZE_HANDLE' + ) { + return emptyStrategyApplicationResult + } + + return strategyApplicationResult( + resizeGridCellCommands(selectedElement, { + columnEnd: TargetGridCell.current.column + 1, + rowEnd: TargetGridCell.current.row + 1, + }), + ) + }, + } +} + +function resizeGridCellCommands( + elementPath: ElementPath, + { columnEnd, rowEnd }: { columnEnd: number; rowEnd: number }, +): CanvasCommand[] { + return [ + setProperty('always', elementPath, create('style', 'gridColumnEnd'), columnEnd), + setProperty('always', elementPath, create('style', 'gridRowEnd'), rowEnd), + ] +} diff --git a/editor/src/components/canvas/canvas-strategies/strategies/keyboard-absolute-resize-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/keyboard-absolute-resize-strategy.tsx index 536ab0cb0f9e..02a6871d401b 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/keyboard-absolute-resize-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/keyboard-absolute-resize-strategy.tsx @@ -43,6 +43,7 @@ import * as EP from '../../../../core/shared/element-path' import type { ElementInstanceMetadataMap } from '../../../../core/shared/element-template' import type { AllElementProps } from '../../../editor/store/editor-state' import { getDescriptiveStrategyLabelWithRetargetedPaths } from '../canvas-strategies' +import { MetadataUtils } from '../../../../core/model/element-metadata-utils' interface VectorAndEdge { movement: CanvasVector diff --git a/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-move-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-move-strategy.ts new file mode 100644 index 000000000000..abc2ed0da727 --- /dev/null +++ b/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-move-strategy.ts @@ -0,0 +1,128 @@ +import { MetadataUtils } from '../../../../core/model/element-metadata-utils' +import * as EP from '../../../../core/shared/element-path' +import type { GridElementProperties } from '../../../../core/shared/element-template' +import { create } from '../../../../core/shared/property-path' +import type { CanvasCommand } from '../../commands/commands' +import { setProperty } from '../../commands/set-property-command' +import { TargetGridCell, GridControls } from '../../controls/grid-controls' +import type { CanvasStrategyFactory } from '../canvas-strategies' +import { onlyFitWhenDraggingThisControl } from '../canvas-strategies' +import type { InteractionCanvasState } from '../canvas-strategy-types' +import { + getTargetPathsFromInteractionTarget, + emptyStrategyApplicationResult, + strategyApplicationResult, +} from '../canvas-strategy-types' +import type { InteractionSession } from '../interaction-state' + +export const rearrangeGridMoveStrategy: CanvasStrategyFactory = ( + canvasState: InteractionCanvasState, + interactionSession: InteractionSession | null, +) => { + const selectedElements = getTargetPathsFromInteractionTarget(canvasState.interactionTarget) + if (selectedElements.length !== 1) { + return null + } + + const selectedElement = selectedElements[0] + const ok = MetadataUtils.isGridLayoutedContainer( + MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + EP.parentPath(selectedElement), + ), + ) + if (!ok) { + return null + } + + return { + id: 'rearrange-grid-move-strategy', + name: 'Rearrange Grid (Move)', + descriptiveLabel: 'Rearrange Grid (Move)', + icon: { + category: 'tools', + type: 'pointer', + }, + controlsToRender: [ + { + control: GridControls, + props: {}, + key: `grid-controls-${EP.toString(selectedElement)}`, + show: 'always-visible', + }, + ], + fitness: onlyFitWhenDraggingThisControl(interactionSession, 'GRID_CELL_HANDLE', 2), + apply: () => { + if ( + interactionSession == null || + interactionSession.interactionData.type !== 'DRAG' || + interactionSession.interactionData.drag == null || + interactionSession.activeControl.type !== 'GRID_CELL_HANDLE' + ) { + return emptyStrategyApplicationResult + } + + let commands: CanvasCommand[] = [] + + if (TargetGridCell.current.row > 0 && TargetGridCell.current.column > 0) { + const metadata = MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + selectedElement, + ) + + function getGridProperty(field: keyof GridElementProperties, fallback: number) { + const propValue = metadata?.specialSizeMeasurements.elementGridProperties[field] + return propValue == null || propValue === 'auto' + ? 0 + : propValue.numericalPosition ?? fallback + } + + const gridColumnStart = getGridProperty('gridColumnStart', 0) + const gridColumnEnd = getGridProperty('gridColumnEnd', 1) + const gridRowStart = getGridProperty('gridRowStart', 0) + const gridRowEnd = getGridProperty('gridRowEnd', 1) + + if (metadata != null) { + commands.push( + setProperty( + 'always', + selectedElement, + create('style', 'gridColumnStart'), + TargetGridCell.current.column, + ), + setProperty( + 'always', + selectedElement, + create('style', 'gridColumnEnd'), + Math.max( + TargetGridCell.current.column, + TargetGridCell.current.column + (gridColumnEnd - gridColumnStart), + ), + ), + setProperty( + 'always', + selectedElement, + create('style', 'gridRowStart'), + TargetGridCell.current.row, + ), + setProperty( + 'always', + selectedElement, + create('style', 'gridRowEnd'), + Math.max( + TargetGridCell.current.row, + TargetGridCell.current.row + (gridRowEnd - gridRowStart), + ), + ), + ) + } + } + + if (commands == null) { + return emptyStrategyApplicationResult + } + + return strategyApplicationResult(commands) + }, + } +} diff --git a/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-swap-strategy.ts b/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-swap-strategy.ts new file mode 100644 index 000000000000..a9179e482166 --- /dev/null +++ b/editor/src/components/canvas/canvas-strategies/strategies/rearrange-grid-swap-strategy.ts @@ -0,0 +1,216 @@ +import { MetadataUtils } from '../../../../core/model/element-metadata-utils' +import { stripNulls } from '../../../../core/shared/array-utils' +import * as EP from '../../../../core/shared/element-path' +import type { + ElementInstanceMetadata, + GridElementProperties, + GridPosition, +} from '../../../../core/shared/element-template' +import { + isFiniteRectangle, + offsetPoint, + rectContainsPointInclusive, +} from '../../../../core/shared/math-utils' +import { optionalMap } from '../../../../core/shared/optional-utils' +import type { ElementPath } from '../../../../core/shared/project-file-types' +import { create } from '../../../../core/shared/property-path' +import type { CanvasCommand } from '../../commands/commands' +import { deleteProperties } from '../../commands/delete-properties-command' +import { rearrangeChildren } from '../../commands/rearrange-children-command' +import { setProperty } from '../../commands/set-property-command' +import { GridControls } from '../../controls/grid-controls' +import { recurseIntoChildrenOfMapOrFragment } from '../../gap-utils' +import type { CanvasStrategyFactory } from '../canvas-strategies' +import { onlyFitWhenDraggingThisControl } from '../canvas-strategies' +import type { InteractionCanvasState } from '../canvas-strategy-types' +import { + getTargetPathsFromInteractionTarget, + emptyStrategyApplicationResult, + strategyApplicationResult, +} from '../canvas-strategy-types' +import type { InteractionSession } from '../interaction-state' + +export const rearrangeGridSwapStrategy: CanvasStrategyFactory = ( + canvasState: InteractionCanvasState, + interactionSession: InteractionSession | null, +) => { + const selectedElements = getTargetPathsFromInteractionTarget(canvasState.interactionTarget) + if (selectedElements.length !== 1) { + return null + } + + const selectedElement = selectedElements[0] + const ok = MetadataUtils.isGridLayoutedContainer( + MetadataUtils.findElementByElementPath( + canvasState.startingMetadata, + EP.parentPath(selectedElement), + ), + ) + if (!ok) { + return null + } + + const children = recurseIntoChildrenOfMapOrFragment( + canvasState.startingMetadata, + canvasState.startingAllElementProps, + canvasState.startingElementPathTree, + EP.parentPath(selectedElement), + ) + + return { + id: 'rearrange-grid-swap-strategy', + name: 'Rearrange Grid (Swap)', + descriptiveLabel: 'Rearrange Grid (Swap)', + icon: { + category: 'tools', + type: 'pointer', + }, + controlsToRender: [ + { + control: GridControls, + props: {}, + key: `grid-controls-${EP.toString(selectedElement)}`, + show: 'always-visible', + }, + ], + fitness: onlyFitWhenDraggingThisControl(interactionSession, 'GRID_CELL_HANDLE', 1), + apply: () => { + if ( + interactionSession == null || + interactionSession.interactionData.type !== 'DRAG' || + interactionSession.interactionData.drag == null || + interactionSession.activeControl.type !== 'GRID_CELL_HANDLE' + ) { + return emptyStrategyApplicationResult + } + + const pointOnCanvas = offsetPoint( + interactionSession.interactionData.dragStart, + interactionSession.interactionData.drag, + ) + + const pointerOverChild = children.find( + (c) => + c.globalFrame != null && + isFiniteRectangle(c.globalFrame) && + rectContainsPointInclusive(c.globalFrame, pointOnCanvas), + ) + + let commands: CanvasCommand[] = [] + + if ( + pointerOverChild != null && + EP.toUid(pointerOverChild.elementPath) !== interactionSession.activeControl.id + ) { + commands.push( + ...swapChildrenCommands({ + grabbedElementUid: interactionSession.activeControl.id, + swapToElementUid: EP.toUid(pointerOverChild.elementPath), + children: children, + parentPath: EP.parentPath(selectedElement), + }), + ) + } + + if (commands == null) { + return emptyStrategyApplicationResult + } + + return strategyApplicationResult(commands) + }, + } +} + +const GridPositioningProps: Array = [ + 'gridColumn', + 'gridRow', + 'gridColumnStart', + 'gridColumnEnd', + 'gridRowStart', + 'gridRowEnd', +] + +function gridPositionToValue(p: GridPosition | null): string | number | null { + if (p == null) { + return null + } + if (p === 'auto') { + return 'auto' + } + + return p.numericalPosition +} + +function setGridProps(elementPath: ElementPath, gridProps: GridElementProperties): CanvasCommand[] { + return stripNulls([ + optionalMap( + (s) => setProperty('always', elementPath, create('style', 'gridColumnStart'), s), + gridPositionToValue(gridProps.gridColumnStart), + ), + optionalMap( + (s) => setProperty('always', elementPath, create('style', 'gridColumnEnd'), s), + gridPositionToValue(gridProps.gridColumnEnd), + ), + optionalMap( + (s) => setProperty('always', elementPath, create('style', 'gridRowStart'), s), + gridPositionToValue(gridProps.gridRowStart), + ), + optionalMap( + (s) => setProperty('always', elementPath, create('style', 'gridRowEnd'), s), + gridPositionToValue(gridProps.gridRowEnd), + ), + ]) +} + +function swapChildrenCommands({ + grabbedElementUid, + swapToElementUid, + children, + parentPath, +}: { + grabbedElementUid: string + swapToElementUid: string + children: ElementInstanceMetadata[] + parentPath: ElementPath +}): CanvasCommand[] { + const grabbedElement = children.find((c) => EP.toUid(c.elementPath) === grabbedElementUid) + const swapToElement = children.find((c) => EP.toUid(c.elementPath) === swapToElementUid) + + if (grabbedElement == null || swapToElement == null) { + return [] + } + + const rearrangedChildren = children + .map((c) => { + if (EP.pathsEqual(c.elementPath, grabbedElement.elementPath)) { + return swapToElement.elementPath + } + if (EP.pathsEqual(c.elementPath, swapToElement.elementPath)) { + return grabbedElement.elementPath + } + return c.elementPath + }) + .map((path) => EP.dynamicPathToStaticPath(path)) + + return [ + rearrangeChildren('always', parentPath, rearrangedChildren), + deleteProperties( + 'always', + swapToElement.elementPath, + GridPositioningProps.map((p) => create('style', p)), + ), + deleteProperties( + 'always', + grabbedElement.elementPath, + GridPositioningProps.map((p) => create('style', p)), + ), + ...setGridProps( + grabbedElement.elementPath, + swapToElement.specialSizeMeasurements.elementGridProperties, + ), + ...setGridProps( + swapToElement.elementPath, + grabbedElement.specialSizeMeasurements.elementGridProperties, + ), + ] +} diff --git a/editor/src/components/canvas/canvas-strategies/strategies/set-flex-gap-strategy.tsx b/editor/src/components/canvas/canvas-strategies/strategies/set-flex-gap-strategy.tsx index d7d87a7a0ed7..bb162b560a8a 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/set-flex-gap-strategy.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/set-flex-gap-strategy.tsx @@ -199,7 +199,7 @@ export const setFlexGapStrategy: CanvasStrategyFactory = ( } } -function dragFromInteractionSession( +export function dragFromInteractionSession( interactionSession: InteractionSession | null, ): CanvasVector | null { if (interactionSession != null && interactionSession.interactionData.type === 'DRAG') { @@ -208,7 +208,7 @@ function dragFromInteractionSession( return null } -function modifiersFromInteractionSession( +export function modifiersFromInteractionSession( interactionSession: InteractionSession | null, ): Modifiers | null { if (interactionSession != null && interactionSession.interactionData.type === 'DRAG') { @@ -217,7 +217,7 @@ function modifiersFromInteractionSession( return null } -function isDragOverThreshold( +export function isDragOverThreshold( direction: FlexDirection, { gapPx, deltaPx }: { gapPx: number; deltaPx: number }, ): boolean { @@ -277,7 +277,7 @@ function flexGapValueIndicatorProps( } } -function isDragOngoing(interactionSession: InteractionSession | null): boolean { +export function isDragOngoing(interactionSession: InteractionSession | null): boolean { return ( interactionSession != null && interactionSession.activeControl.type === 'FLEX_GAP_HANDLE' && diff --git a/editor/src/components/canvas/controls/grid-controls.tsx b/editor/src/components/canvas/controls/grid-controls.tsx index 66205b64bf91..33c615e1aa52 100644 --- a/editor/src/components/canvas/controls/grid-controls.tsx +++ b/editor/src/components/canvas/controls/grid-controls.tsx @@ -18,9 +18,11 @@ import { distance, getRectCenter, isFiniteRectangle, + isInfinityRectangle, offsetPoint, pointDifference, windowPoint, + zeroRectIfNullOrInfinity, } from '../../../core/shared/math-utils' import { fromArrayIndex, @@ -42,6 +44,7 @@ import { createInteractionViaMouse, gridAxisHandle, gridCellHandle, + gridResizeHandle, } from '../canvas-strategies/interaction-state' import { windowToCanvasCoordinates } from '../dom-lookup' import { CanvasOffsetWrapper } from './canvas-offset-wrapper' @@ -798,3 +801,117 @@ function useMouseMove(activelyDraggingOrResizingCell: string | null) { return { hoveringCell, hoveringStart, mouseCanvasPosition } } + +export const GridResizeShadow = controlForStrategyMemoized( + ({ elementPath }: { elementPath: ElementPath }) => { + const element = useEditorState( + Substores.metadata, + (store) => MetadataUtils.findElementByElementPath(store.editor.jsxMetadata, elementPath), + 'GridResizeShadow element', + ) + + const dispatch = useDispatch() + const canvasOffsetRef = useRefEditorState((store) => store.editor.canvas.roundedCanvasOffset) + const scaleRef = useRefEditorState((store) => store.editor.canvas.scale) + + const dragging = useEditorState( + Substores.canvas, + (store) => + store.editor.canvas.interactionSession != null && + store.editor.canvas.interactionSession.activeControl.type === 'GRID_RESIZE_HANDLE', + '', + ) + const [offset, setOffset] = React.useState<{ width: number; height: number } | null>(null) + const onMouseMove = React.useCallback( + (e: MouseEvent) => { + if (!dragging) { + return + } + + setOffset((o) => + o == null ? null : { width: o.width + e.movementX, height: o.height + e.movementY }, + ) + }, + [dragging], + ) + + const onMouseUp = React.useCallback(() => setOffset(null), []) + + React.useEffect(() => { + window.addEventListener('mousemove', onMouseMove) + window.addEventListener('mouseup', onMouseUp) + return () => { + window.removeEventListener('mousemove', onMouseMove) + window.removeEventListener('mouseup', onMouseUp) + } + }, [onMouseMove, onMouseUp]) + + const startResizeInteractionWithUid = React.useCallback( + (uid: string) => (event: React.MouseEvent) => { + event.stopPropagation() + const frame = zeroRectIfNullOrInfinity(element?.globalFrame ?? null) + setOffset({ width: frame.width, height: frame.height }) + const start = windowToCanvasCoordinates( + scaleRef.current, + canvasOffsetRef.current, + windowPoint({ x: event.nativeEvent.x, y: event.nativeEvent.y }), + ) + dispatch([ + CanvasActions.createInteractionSession( + createInteractionViaMouse( + start.canvasPositionRounded, + Modifier.modifiersForEvent(event), + gridResizeHandle(uid), + 'zero-drag-not-permitted', + ), + ), + ]) + }, + [canvasOffsetRef, dispatch, element?.globalFrame, scaleRef], + ) + + const colorTheme = useColorTheme() + + if ( + element == null || + element.globalFrame == null || + isInfinityRectangle(element.globalFrame) + ) { + return null + } + + return ( + +
+
+
+ + ) + }, +) diff --git a/editor/src/components/canvas/controls/new-canvas-controls.tsx b/editor/src/components/canvas/controls/new-canvas-controls.tsx index 016d35e98cbd..1b6e033b07a0 100644 --- a/editor/src/components/canvas/controls/new-canvas-controls.tsx +++ b/editor/src/components/canvas/controls/new-canvas-controls.tsx @@ -71,10 +71,8 @@ import { useSelectionArea } from './selection-area-hooks' import { RemixSceneLabelControl } from './select-mode/remix-scene-label' import { NO_OP } from '../../../core/shared/utils' import { useIsMyProject } from '../../editor/store/collaborative-editing' -import { useStatus } from '../../../../liveblocks.config' import { MultiplayerWrapper } from '../../../utils/multiplayer-wrapper' import { MultiplayerPresence } from '../multiplayer-presence' -import { isFeatureEnabled } from '../../../utils/feature-switches' export const CanvasControlsContainerID = 'new-canvas-controls-container' diff --git a/editor/src/components/canvas/controls/select-mode/select-mode-hooks.tsx b/editor/src/components/canvas/controls/select-mode/select-mode-hooks.tsx index a226f17ffdcf..bd00515e75a8 100644 --- a/editor/src/components/canvas/controls/select-mode/select-mode-hooks.tsx +++ b/editor/src/components/canvas/controls/select-mode/select-mode-hooks.tsx @@ -743,15 +743,15 @@ function useSelectOrLiveModeSelectAndHover( dispatch(editorActions) }, [ - dispatch, - selectedViewsRef, - findValidTarget, - setSelectedViewsForCanvasControlsOnly, - getSelectableViewsForSelectMode, editorStoreRef, + active, + getSelectableViewsForSelectMode, + findValidTarget, + dispatch, draggingAllowed, windowToCanvasCoordinates, - active, + selectedViewsRef, + setSelectedViewsForCanvasControlsOnly, ], ) diff --git a/editor/src/components/editor/store/store-deep-equality-instances.ts b/editor/src/components/editor/store/store-deep-equality-instances.ts index 8f5cef751b70..7d9a2fc8b417 100644 --- a/editor/src/components/editor/store/store-deep-equality-instances.ts +++ b/editor/src/components/editor/store/store-deep-equality-instances.ts @@ -453,6 +453,7 @@ import type { ResizeHandle, BorderRadiusResizeHandle, ZeroDragPermitted, + GridResizeHandle, GridCellHandle, GridAxisHandle, } from '../../canvas/canvas-strategies/interaction-state' @@ -463,6 +464,7 @@ import { interactionSession, keyboardCatcherControl, resizeHandle, + gridResizeHandle, gridCellHandle, gridAxisHandle, } from '../../canvas/canvas-strategies/interaction-state' @@ -2861,6 +2863,13 @@ export const GridCellHandleKeepDeepEquality: KeepDeepEqualityCall gridCellHandle({ id }), ) +export const GridResizeHandleKeepDeepEquality: KeepDeepEqualityCall = + combine1EqualityCall( + (handle) => handle.id, + createCallWithTripleEquals(), + gridResizeHandle, + ) + export const GridAxisHandleKeepDeepEquality: KeepDeepEqualityCall = combine2EqualityCalls( (handle) => handle.axis, @@ -2915,6 +2924,11 @@ export const CanvasControlTypeKeepDeepEquality: KeepDeepEqualityCall } = { current: [] } export const MOCK_NEXT_GENERATED_UIDS_IDX = { current: 0 } @@ -814,7 +814,7 @@ export function getUtopiaID(element: JSXElementChild | ElementInstanceMetadata): } // The length of element UIDs generated by the editor. -const UID_LENGTH = IS_TEST_ENVIRONMENT ? 3 : 32 // in characters +const UID_LENGTH = IS_TEST_ENVIRONMENT ? 3 : PRODUCTION_ENV ? 32 : 4 // in characters /** * Generate a new UID suitable for elements.