diff --git a/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.spec.browser2.tsx b/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.spec.browser2.tsx index 5a83237fe0ec..2669d33be3a7 100644 --- a/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.spec.browser2.tsx +++ b/editor/src/components/canvas/canvas-strategies/strategies/resize-grid-strategy.spec.browser2.tsx @@ -144,7 +144,7 @@ describe('resize a grid', () => { await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -259,7 +259,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -320,7 +320,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-row-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-row-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -435,7 +435,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -555,7 +555,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -674,7 +674,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, @@ -792,7 +792,7 @@ export var storyboard = ( await renderResult.dispatch(selectComponents([target], false), true) await renderResult.getDispatchFollowUpActionsFinished() const canvasControlsLayer = renderResult.renderedDOM.getByTestId(CanvasControlsContainerID) - const resizeControl = renderResult.renderedDOM.getByTestId(`grid-column-handle-1`) + const resizeControl = renderResult.renderedDOM.getByTestId(`grid-track-size-column-handle-1`) const resizeControlRect = resizeControl.getBoundingClientRect() const startPoint = canvasPoint({ x: resizeControlRect.x + resizeControlRect.width / 2, 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 c8672ef6c272..a613e847cb17 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 @@ -131,6 +131,7 @@ export const setPaddingStrategy: CanvasStrategyFactory = (canvasState, interacti props: { targets: selectedElements }, key: 'padding-resize-control', show: 'visible-except-when-other-strategy-is-active', + priority: 'bottom', }) const controlsToRender = optionalMap( diff --git a/editor/src/components/canvas/controls/grid-controls-for-strategies.tsx b/editor/src/components/canvas/controls/grid-controls-for-strategies.tsx index d01e6488659a..bf9c276238a9 100644 --- a/editor/src/components/canvas/controls/grid-controls-for-strategies.tsx +++ b/editor/src/components/canvas/controls/grid-controls-for-strategies.tsx @@ -1,18 +1,11 @@ /** @jsxRuntime classic */ /** @jsx jsx */ -import { sides, type Sides } from 'utopia-api/core' import type { ElementPath } from 'utopia-shared/src/types' -import { - isStaticGridRepeat, - parseCSSLength, - printGridAutoOrTemplateBase, -} from '../../inspector/common/css-utils' +import { printGridAutoOrTemplateBase } from '../../inspector/common/css-utils' import { MetadataUtils } from '../../../core/model/element-metadata-utils' import { mapDropNulls } from '../../../core/shared/array-utils' import * as EP from '../../../core/shared/element-path' -import type { BorderWidths } from '../../../core/shared/element-template' import { type GridAutoOrTemplateBase } from '../../../core/shared/element-template' -import type { CanvasRectangle } from '../../../core/shared/math-utils' import { assertNever } from '../../../core/shared/utils' import { Substores, useEditorState } from '../../editor/store/store-hook' import type { @@ -23,7 +16,6 @@ import { controlForStrategyMemoized } from '../canvas-strategies/canvas-strategy import type { GridResizeEdge } from '../canvas-strategies/interaction-state' import type { EdgePosition } from '../canvas-types' import { - CanvasContainerID, EdgePositionBottom, EdgePositionLeft, EdgePositionRight, @@ -35,14 +27,8 @@ import { GridRowColumnResizingControlsComponent, } from './grid-controls' import { isEdgePositionOnSide } from '../canvas-utils' -import { getFromElement } from '../direct-dom-lookups' -import { applicativeSidesPxTransform, getGridContainerProperties } from '../dom-walker' -import { applicative4Either, defaultEither, isRight, mapEither } from '../../../core/shared/either' -import { domRectToScaledCanvasRectangle, getRoundingFn } from '../../../core/shared/dom-utils' -import Utils from '../../../utils/utils' import { useMonitorChangesToElements } from '../../../components/editor/store/store-monitor' import { useKeepReferenceEqualityIfPossible } from '../../../utils/react-performance' -import type { CSSProperties } from 'react' import { gridContainerIdentifier, gridItemIdentifier, @@ -51,29 +37,13 @@ import { } from '../../editor/store/editor-state' import { findOriginalGrid } from '../canvas-strategies/strategies/grid-helpers' import * as React from 'react' -import { addChangeCallback, removeChangeCallback } from '../observers' +import type { GridData } from './grid-measurements' +import { getGridMeasurementHelperData, useObserversToWatch } from './grid-measurements' export const GridMeasurementHelperMap = { current: new WeakMap() } export const GridCellTestId = (elementPath: ElementPath) => `grid-cell-${EP.toString(elementPath)}` -function getCellsCount(template: GridAutoOrTemplateBase | null): number { - if (template == null) { - return 0 - } - - switch (template.type) { - case 'DIMENSIONS': - return template.dimensions.reduce((acc, cur) => { - return acc + (isStaticGridRepeat(cur) ? cur.times : 1) - }, 0) - case 'FALLBACK': - return 0 - default: - assertNever(template) - } -} - export function getNullableAutoOrTemplateBaseString( template: GridAutoOrTemplateBase | null, ): string | undefined { @@ -84,208 +54,6 @@ export function getNullableAutoOrTemplateBaseString( } } -export type GridMeasurementHelperData = { - frame: CanvasRectangle - rows: number - columns: number - cells: number - computedStyling: CSSProperties - gridTemplateColumns: GridAutoOrTemplateBase | null - gridTemplateRows: GridAutoOrTemplateBase | null - gridTemplateColumnsFromProps: GridAutoOrTemplateBase | null - gridTemplateRowsFromProps: GridAutoOrTemplateBase | null - border: BorderWidths - gap: number | null - rowGap: number | null - columnGap: number | null - padding: Sides - element: HTMLElement -} - -export type ElementOrParent = 'parent' | 'element' - -export function getGridMeasurementHelperData( - elementPath: ElementPath, - scale: number, - source: ElementOrParent, -): GridMeasurementHelperData | undefined { - return getFromElement(elementPath, gridMeasurementHelperDataFromElement(scale), source) -} - -function getStylingSubset(styling: CSSStyleDeclaration): CSSProperties { - // Fields chosen to not overlap with any others, so as to not make React complain. - return { - gridAutoFlow: styling.gridAutoFlow, - gridAutoColumns: styling.gridAutoColumns, - gridAutoRows: styling.gridAutoRows, - gridTemplateColumns: styling.gridTemplateColumns, - gridTemplateRows: styling.gridTemplateRows, - gridColumn: styling.gridColumn, - gridRow: styling.gridRow, - gap: styling.gap, - rowGap: styling.rowGap, - columnGap: styling.columnGap, - justifyContent: styling.justifyContent, - alignContent: styling.alignContent, - padding: styling.padding, - paddingTop: styling.paddingTop, - paddingLeft: styling.paddingLeft, - paddingBottom: styling.paddingBottom, - paddingRight: styling.paddingRight, - borderTop: styling.borderTopWidth, - borderLeft: styling.borderLeftWidth, - borderBottom: styling.borderBottomWidth, - borderRight: styling.borderRightWidth, - } -} - -export function gridMeasurementHelperDataFromElement( - scale: number, -): (element: HTMLElement) => GridMeasurementHelperData | undefined { - return (element) => { - const canvasRootContainer = document.getElementById(CanvasContainerID) - if (canvasRootContainer == null) { - return undefined - } - - const computedStyle = getComputedStyle(element) - const boundingRectangle = element.getBoundingClientRect() - - const computedStyling: CSSProperties = getStylingSubset(computedStyle) - - const containerGridProperties = getGridContainerProperties(computedStyle) - const containerGridPropertiesFromProps = getGridContainerProperties(element.style) - - const columns = getCellsCount(containerGridProperties.gridTemplateColumns) - const rows = getCellsCount(containerGridProperties.gridTemplateRows) - const borderTopWidth = parseCSSLength(computedStyle.borderTopWidth) - const borderRightWidth = parseCSSLength(computedStyle.borderRightWidth) - const borderBottomWidth = parseCSSLength(computedStyle.borderBottomWidth) - const borderLeftWidth = parseCSSLength(computedStyle.borderLeftWidth) - const border: BorderWidths = { - top: isRight(borderTopWidth) ? borderTopWidth.value.value : 0, - right: isRight(borderRightWidth) ? borderRightWidth.value.value : 0, - bottom: isRight(borderBottomWidth) ? borderBottomWidth.value.value : 0, - left: isRight(borderLeftWidth) ? borderLeftWidth.value.value : 0, - } - const padding = defaultEither( - sides(undefined, undefined, undefined, undefined), - applicative4Either( - applicativeSidesPxTransform, - parseCSSLength(computedStyle.paddingTop), - parseCSSLength(computedStyle.paddingRight), - parseCSSLength(computedStyle.paddingBottom), - parseCSSLength(computedStyle.paddingLeft), - ), - ) - const gap = defaultEither( - null, - mapEither((n) => n.value, parseCSSLength(computedStyle.gap)), - ) - - const rowGap = defaultEither( - null, - mapEither((n) => n.value, parseCSSLength(computedStyle.rowGap)), - ) - - const columnGap = defaultEither( - null, - mapEither((n) => n.value, parseCSSLength(computedStyle.columnGap)), - ) - - const elementRect = domRectToScaledCanvasRectangle( - boundingRectangle, - 1 / scale, - getRoundingFn('nearest-half'), - ) - const parentRect = domRectToScaledCanvasRectangle( - canvasRootContainer.getBoundingClientRect(), - 1 / scale, - getRoundingFn('nearest-half'), - ) - const frame = Utils.offsetRect(elementRect, Utils.negate(parentRect)) - - return { - frame: frame, - gridTemplateColumns: containerGridProperties.gridTemplateColumns, - gridTemplateRows: containerGridProperties.gridTemplateRows, - gridTemplateColumnsFromProps: containerGridPropertiesFromProps.gridTemplateColumns, - gridTemplateRowsFromProps: containerGridPropertiesFromProps.gridTemplateRows, - border: border, - padding: padding, - gap: gap, - rowGap: rowGap, - columnGap: columnGap, - rows: rows, - columns: columns, - cells: columns * rows, - computedStyling: computedStyling, - element: element, - } - } -} - -function useObserversToWatch(elementPathOrPaths: Array | ElementPath): number { - // Used to trigger extra renders. - const [counter, setCounter] = React.useState(0) - const bumpCounter = React.useCallback(() => { - setCounter((value) => value + 1) - }, []) - - // Need to use the mount count for the callback trigger. - const mountCount = useEditorState( - Substores.canvas, - (store) => store.editor.canvas.mountCount, - 'useObserversToWatch mountCount', - ) - - React.useEffect(() => { - // Add the change callback(s) for the element path or paths. - if (Array.isArray(elementPathOrPaths)) { - for (const elementPath of elementPathOrPaths) { - addChangeCallback(mountCount, elementPath, bumpCounter) - } - } else { - addChangeCallback(mountCount, elementPathOrPaths, bumpCounter) - } - - return function cleanup() { - if (Array.isArray(elementPathOrPaths)) { - for (const elementPath of elementPathOrPaths) { - removeChangeCallback(elementPath, bumpCounter) - } - } else { - removeChangeCallback(elementPathOrPaths, bumpCounter) - } - } - }, [mountCount, elementPathOrPaths, bumpCounter]) - - return counter -} - -export function useGridMeasurementHelperData( - elementPath: ElementPath, - source: ElementOrParent, -): GridMeasurementHelperData | undefined { - const scale = useEditorState( - Substores.canvas, - (store) => store.editor.canvas.scale, - 'useGridMeasurementHelperData scale', - ) - - useMonitorChangesToElements([elementPath]) - - useObserversToWatch(elementPath) - - return useKeepReferenceEqualityIfPossible( - getGridMeasurementHelperData(elementPath, scale, source), - ) -} - -export type GridData = GridMeasurementHelperData & { - identifier: GridIdentifier -} - export function useGridData(gridIdentifiers: GridIdentifier[]): GridData[] { const scale = useEditorState( Substores.canvas, diff --git a/editor/src/components/canvas/controls/grid-controls-helpers.ts b/editor/src/components/canvas/controls/grid-controls-helpers.ts index 46dea6427985..c24340b47410 100644 --- a/editor/src/components/canvas/controls/grid-controls-helpers.ts +++ b/editor/src/components/canvas/controls/grid-controls-helpers.ts @@ -1,5 +1,5 @@ import type { CSSProperties } from 'react' -import type { GridMeasurementHelperData } from './grid-controls-for-strategies' +import type { GridMeasurementHelperData } from './grid-measurements' export function getGridHelperStyleMatchingTargetGrid( grid: GridMeasurementHelperData, diff --git a/editor/src/components/canvas/controls/grid-controls.tsx b/editor/src/components/canvas/controls/grid-controls.tsx index c7d500b72dfc..7992c4155988 100644 --- a/editor/src/components/canvas/controls/grid-controls.tsx +++ b/editor/src/components/canvas/controls/grid-controls.tsx @@ -50,7 +50,13 @@ import { Modifier } from '../../../utils/modifiers' import { when } from '../../../utils/react-conditionals' import { useColorTheme, UtopiaStyles } from '../../../uuiui' import { useDispatch } from '../../editor/store/dispatch-context' -import { Substores, useEditorState, useRefEditorState } from '../../editor/store/store-hook' +import { + EditorStateContext, + HelperControlsStateContext, + Substores, + useEditorState, + useRefEditorState, +} from '../../editor/store/store-hook' import type { GridDimension, GridDiscreteDimension } from '../../inspector/common/css-utils' import { isCSSKeyword, @@ -91,11 +97,7 @@ import { windowToCanvasCoordinates } from '../dom-lookup' import type { Axis } from '../gap-utils' import { useCanvasAnimation } from '../ui-jsx-canvas-renderer/animation-context' import { CanvasOffsetWrapper } from './canvas-offset-wrapper' -import type { - GridControlsProps, - GridData, - GridMeasurementHelperData, -} from './grid-controls-for-strategies' +import type { GridControlsProps } from './grid-controls-for-strategies' import { edgePositionToGridResizeEdge, GridCellTestId, @@ -106,7 +108,6 @@ import { GridMeasurementHelperMap, GridMeasurementHelpersKey, useGridData, - useGridMeasurementHelperData, } from './grid-controls-for-strategies' import { useMaybeHighlightElement } from './select-mode/select-mode-hooks' import { useResizeEdges } from './select-mode/use-resize-edges' @@ -119,6 +120,14 @@ import { styleStringInArray } from '../../../utils/common-constants' import { gridContainerIdentifier, type GridIdentifier } from '../../editor/store/editor-state' import type { RulerMarkerType } from './grid-controls-ruler-markers' import { rulerMarkerIcons } from './grid-controls-ruler-markers' +import type { GridData, GridMeasurementHelperData } from './grid-measurements' +import { + getGridElementMeasurementHelperData, + useGridElementMeasurementHelperData, + useGridMeasurementHelperData, +} from './grid-measurements' +import type { Property } from 'csstype' +import { isInsertMode, isSelectMode } from '../../editor/editor-modes' import { isFeatureEnabled } from '../../../utils/feature-switches' const CELL_ANIMATION_DURATION = 0.15 // seconds @@ -227,7 +236,7 @@ const GridTrackSizeLabel = React.memo((props: GridResizingControlProps) => { [maybeClearHighlightsOnHoverEnd], ) - const labelId = `grid-${props.axis}-handle-${props.dimensionIndex}` + const labelId = `grid-track-size-${props.axis}-handle-${props.dimensionIndex}` const containerId = `${labelId}-container` const cssProp = React.useMemo( @@ -296,7 +305,7 @@ const GridResizingStripedIndicator = React.memo((props: GridResizingControlProps ) const colorTheme = useColorTheme() - const labelId = `grid-${props.axis}-handle-${props.dimensionIndex}` + const labelId = `grid-resizing-${props.axis}-handle-${props.dimensionIndex}` const containerId = `${labelId}-container` return ( @@ -706,25 +715,25 @@ const GridControl = React.memo(({ grid, controlsVisible }) => 'GridControl anyTargetAbsolute', ) + const scale = useEditorState( + Substores.canvas, + (store) => store.editor.canvas.scale, + 'GridControl scale', + ) + const anyTargetNotPinned = useEditorState( Substores.metadata, (store) => - store.editor.selectedViews.some( - (elementPath) => + store.editor.selectedViews.some((elementPath) => { + return ( getGridElementPinState( - MetadataUtils.findElementByElementPath(store.editor.jsxMetadata, elementPath) - ?.specialSizeMeasurements.elementGridPropertiesFromProps ?? null, - ) !== 'pinned', - ), + getGridElementMeasurementHelperData(elementPath, scale)?.gridElementProperties ?? null, + ) !== 'pinned' + ) + }), 'GridControl anyTargetNotPinned', ) - const scale = useEditorState( - Substores.canvas, - (store) => store.editor.canvas.scale, - 'GridControl scale', - ) - const canvasOffsetRef = useRefEditorState((store) => store.editor.canvas.roundedCanvasOffset) const startInteractionWithUid = React.useCallback( @@ -770,30 +779,32 @@ const GridControl = React.memo(({ grid, controlsVisible }) => const countedRow = Math.floor(index / grid.columns) + 1 const countedColumn = Math.floor(index % grid.columns) + 1 - const columnFromProps = cell.specialSizeMeasurements.elementGridProperties.gridColumnStart - const rowFromProps = cell.specialSizeMeasurements.elementGridProperties.gridRowStart - - function getAxisValue(value: GridPositionOrSpan | null, counted: number): number { - if ( - value == null || - isCSSKeyword(value) || - isGridSpan(value) || - value.numericalPosition == null - ) { + const gridElementData = getGridElementMeasurementHelperData(cell.elementPath, scale) + if (gridElementData == null) { + return null + } + const columnFromProps = gridElementData.computedStyling.gridColumnStart + const rowFromProps = gridElementData.computedStyling.gridRowStart + + function getAxisValue( + value: Property.GridColumnStart | Property.GridRowStart | undefined, + counted: number, + ): number { + if (value == null || typeof value !== 'number') { return counted } - return value.numericalPosition + return value } return { elementPath: cell.elementPath, - globalFrame: cell.globalFrame, - borderRadius: cell.specialSizeMeasurements.borderRadius, + globalFrame: gridElementData.frame, + borderRadius: gridElementData.computedStyling.borderRadius, column: getAxisValue(columnFromProps, countedColumn), row: getAxisValue(rowFromProps, countedRow), index: index, } }, children) - }, [grid, jsxMetadata]) + }, [grid.columns, grid.identifier, jsxMetadata, scale]) const dragging = useEditorState( Substores.canvas, @@ -1015,10 +1026,7 @@ const GridControl = React.memo(({ grid, controlsVisible }) => controlsVisible === 'visible' ? '#ffffff66' : 'transparent', - borderRadius: - cell.borderRadius != null - ? `${cell.borderRadius.top}px ${cell.borderRadius.right}px ${cell.borderRadius.bottom}px ${cell.borderRadius.left}px` - : 0, + borderRadius: cell.borderRadius ?? 0, }} /> ) @@ -1037,10 +1045,7 @@ const GridControl = React.memo(({ grid, controlsVisible }) => position: 'absolute', width: shadow.globalFrame.width, height: shadow.globalFrame.height, - borderRadius: - shadow.borderRadius != null - ? `${shadow.borderRadius.top}px ${shadow.borderRadius.right}px ${shadow.borderRadius.bottom}px ${shadow.borderRadius.left}px` - : 0, + borderRadius: shadow.borderRadius ?? 0, backgroundColor: 'black', opacity: 0.1, border: '1px solid white', @@ -1105,6 +1110,7 @@ export const GridMeasurementHelper = React.memo( const uid = UUID() GridMeasurementHelperMap.current.set(gridData.element, uid) + return (
{placeholders.map((cell) => { @@ -1961,19 +1967,9 @@ export interface GridElementContainingBlockProps { gridChild: ElementPath } -function nullHandlingPrintPin( - gridTemplate: GridContainerProperties | null | undefined, - pin: GridPositionOrSpan | null | undefined, - axis: 'row' | 'column', -): string | number | undefined { - if (gridTemplate == null || pin == null) { - return undefined - } - return printPin(gridTemplate, pin, axis) -} - const GridElementContainingBlock = React.memo((props) => { const gridData = useGridMeasurementHelperData(props.gridPath, 'element') + const gridChildData = useGridElementMeasurementHelperData(props.gridChild) const scale = useEditorState( Substores.canvas, (store) => store.editor.canvas.scale, @@ -1990,44 +1986,18 @@ const GridElementContainingBlock = React.memo(( }, 'GridElementContainingBlock position', ) - const gridChildStyle: CSSProperties = useEditorState( - Substores.metadata, - (store) => { - const childMetadata = MetadataUtils.findElementByElementPath( - store.editor.jsxMetadata, - props.gridChild, - ) - if (childMetadata == null) { - return {} - } - const gridFromProps = childMetadata.specialSizeMeasurements.elementGridPropertiesFromProps - const gridComputed = childMetadata.specialSizeMeasurements.elementGridProperties - return { - gridColumnStart: nullHandlingPrintPin( - childMetadata.specialSizeMeasurements.containerGridProperties, - gridFromProps.gridColumnStart ?? gridComputed.gridColumnStart, - 'column', - ), - gridColumnEnd: nullHandlingPrintPin( - childMetadata.specialSizeMeasurements.containerGridProperties, - gridFromProps.gridColumnEnd ?? gridComputed.gridColumnEnd, - 'column', - ), - gridRowStart: nullHandlingPrintPin( - childMetadata.specialSizeMeasurements.containerGridProperties, - gridFromProps.gridRowStart ?? gridComputed.gridRowStart, - 'row', - ), - gridRowEnd: nullHandlingPrintPin( - childMetadata.specialSizeMeasurements.containerGridProperties, - gridFromProps.gridRowEnd ?? gridComputed.gridRowEnd, - 'row', - ), - position: childMetadata.specialSizeMeasurements.position ?? undefined, - } - }, - 'GridElementContainingBlock gridChildStyle', - ) + const gridChildStyle: CSSProperties = React.useMemo(() => { + if (gridChildData == null) { + return {} + } + return { + gridColumnStart: gridChildData.computedStyling.gridColumnStart, + gridColumnEnd: gridChildData.computedStyling.gridColumnEnd, + gridRowStart: gridChildData.computedStyling.gridRowStart, + gridRowEnd: gridChildData.computedStyling.gridRowEnd, + position: gridChildData.computedStyling.position, + } + }, [gridChildData]) const gridChildFrame = useEditorState( Substores.metadata, (store) => { @@ -2500,6 +2470,17 @@ function getCellCanvasHeightFromBounds( }, currentColumn.height) } +export const GridHelperControls = () => { + const helperControlsStore = React.useContext(HelperControlsStateContext) + return ( + + + + + ) +} +GridHelperControls.displayName = 'GridHelperControls' + // This function returns the amount of pixels used to adjust the position of // individual ruler markers, which need specific skews based on their shape. function skewMarkerPosition( diff --git a/editor/src/components/canvas/controls/grid-measurements.tsx b/editor/src/components/canvas/controls/grid-measurements.tsx new file mode 100644 index 000000000000..9dcdf39daf1a --- /dev/null +++ b/editor/src/components/canvas/controls/grid-measurements.tsx @@ -0,0 +1,343 @@ +import type { Property } from 'csstype' +import type { CSSProperties } from 'react' +import type { GridIdentifier } from '../../editor/store/editor-state' +import type { + BorderWidths, + GridAutoOrTemplateBase, + GridElementProperties, +} from '../../../core/shared/element-template' +import type { CanvasRectangle } from '../../../core/shared/math-utils' +import type { Sides } from 'utopia-api/core' +import { sides } from 'utopia-api/core' +import type { ElementPath } from 'utopia-shared/src/types' +import { getFromElement } from '../direct-dom-lookups' +import { CanvasContainerID } from '../canvas-types' +import { + applicativeSidesPxTransform, + getGridContainerProperties, + getGridElementProperties, +} from '../dom-walker' +import { isStaticGridRepeat, parseCSSLength } from '../../inspector/common/css-utils' +import { assertNever } from '../../../core/shared/utils' +import { applicative4Either, defaultEither, isRight, mapEither } from '../../../core/shared/either' +import { domRectToScaledCanvasRectangle, getRoundingFn } from '../../../core/shared/dom-utils' +import Utils from '../../../utils/utils' +import React from 'react' +import { Substores, useEditorState } from '../../editor/store/store-hook' +import { addChangeCallback, removeChangeCallback } from '../observers' +import { useMonitorChangesToElements } from '../../editor/store/store-monitor' +import { useKeepReferenceEqualityIfPossible } from '../../../utils/react-performance' + +export type GridElementMeasurementHelperData = { + frame: CanvasRectangle + computedStyling: CSSProperties + gridElementProperties: GridElementProperties +} + +export type ElementOrParent = 'parent' | 'element' + +export type GridMeasurementHelperData = { + frame: CanvasRectangle + rows: number + columns: number + cells: number + computedStyling: CSSProperties + gridTemplateColumns: GridAutoOrTemplateBase | null + gridTemplateRows: GridAutoOrTemplateBase | null + gridTemplateColumnsFromProps: GridAutoOrTemplateBase | null + gridTemplateRowsFromProps: GridAutoOrTemplateBase | null + border: BorderWidths + gap: number | null + rowGap: number | null + columnGap: number | null + padding: Sides + element: HTMLElement +} + +export type GridData = GridMeasurementHelperData & { + identifier: GridIdentifier +} + +function getPositionFromComputedStyling(position: string): Property.Position | undefined { + switch (position) { + case '-moz-initial': + case 'inherit': + case 'initial': + case 'revert': + case 'unset': + case '-webkit-sticky': + case 'absolute': + case 'fixed': + case 'relative': + case 'static': + case 'sticky': + return position + default: + return undefined + } +} + +function getGridElementStylingSubset(styling: CSSStyleDeclaration): CSSProperties { + // Fields chosen to not overlap with any others, so as to not make React complain. + return { + position: getPositionFromComputedStyling(styling.position), + gridColumnStart: styling.gridColumnStart, + gridColumnEnd: styling.gridColumnEnd, + gridRowStart: styling.gridRowStart, + gridRowEnd: styling.gridRowEnd, + borderRadius: styling.borderRadius, + } +} + +function getGlobalFrame( + canvasRootContainer: HTMLElement, + targetElement: HTMLElement, + scale: number, +): CanvasRectangle { + const boundingRectangle = targetElement.getBoundingClientRect() + const elementRect = domRectToScaledCanvasRectangle( + boundingRectangle, + 1 / scale, + getRoundingFn('nearest-half'), + ) + const parentRect = domRectToScaledCanvasRectangle( + canvasRootContainer.getBoundingClientRect(), + 1 / scale, + getRoundingFn('nearest-half'), + ) + return Utils.offsetRect(elementRect, Utils.negate(parentRect)) +} + +export function gridElementMeasurementHelperDataFromElement( + scale: number, +): (element: HTMLElement) => GridElementMeasurementHelperData | undefined { + return (element) => { + const canvasRootContainer = document.getElementById(CanvasContainerID) + if (canvasRootContainer == null) { + return undefined + } + + const computedStyle = getComputedStyle(element) + const computedStyling: CSSProperties = getGridElementStylingSubset(computedStyle) + + const frame = getGlobalFrame(canvasRootContainer, element, scale) + + const parentElementStyle = + element.parentElement == null ? null : getComputedStyle(element.parentElement) + const gridContainerProperties = getGridContainerProperties(parentElementStyle) + const gridElementProperties = getGridElementProperties(gridContainerProperties, element.style) + + return { + frame: frame, + computedStyling: computedStyling, + gridElementProperties: gridElementProperties, + } + } +} + +export function getGridElementMeasurementHelperData( + elementPath: ElementPath, + scale: number, +): GridElementMeasurementHelperData | undefined { + return getFromElement(elementPath, gridElementMeasurementHelperDataFromElement(scale), 'element') +} + +export function getGridMeasurementHelperData( + elementPath: ElementPath, + scale: number, + source: ElementOrParent, +): GridMeasurementHelperData | undefined { + return getFromElement(elementPath, gridMeasurementHelperDataFromElement(scale), source) +} + +function getGridStylingSubset(styling: CSSStyleDeclaration): CSSProperties { + // Fields chosen to not overlap with any others, so as to not make React complain. + return { + gridAutoFlow: styling.gridAutoFlow, + gridAutoColumns: styling.gridAutoColumns, + gridAutoRows: styling.gridAutoRows, + gridTemplateColumns: styling.gridTemplateColumns, + gridTemplateRows: styling.gridTemplateRows, + gridColumn: styling.gridColumn, + gridRow: styling.gridRow, + gap: styling.gap, + rowGap: styling.rowGap, + columnGap: styling.columnGap, + justifyContent: styling.justifyContent, + alignContent: styling.alignContent, + padding: styling.padding, + paddingTop: styling.paddingTop, + paddingLeft: styling.paddingLeft, + paddingBottom: styling.paddingBottom, + paddingRight: styling.paddingRight, + borderTop: styling.borderTopWidth, + borderLeft: styling.borderLeftWidth, + borderBottom: styling.borderBottomWidth, + borderRight: styling.borderRightWidth, + } +} + +function getCellsCount(template: GridAutoOrTemplateBase | null): number { + if (template == null) { + return 0 + } + + switch (template.type) { + case 'DIMENSIONS': + return template.dimensions.reduce((acc, cur) => { + return acc + (isStaticGridRepeat(cur) ? cur.times : 1) + }, 0) + case 'FALLBACK': + return 0 + default: + assertNever(template) + } +} + +export function gridMeasurementHelperDataFromElement( + scale: number, +): (element: HTMLElement) => GridMeasurementHelperData | undefined { + return (element) => { + const canvasRootContainer = document.getElementById(CanvasContainerID) + if (canvasRootContainer == null) { + return undefined + } + + const computedStyle = getComputedStyle(element) + + const computedStyling: CSSProperties = getGridStylingSubset(computedStyle) + + const containerGridProperties = getGridContainerProperties(computedStyle) + const containerGridPropertiesFromProps = getGridContainerProperties(element.style) + + const columns = getCellsCount(containerGridProperties.gridTemplateColumns) + const rows = getCellsCount(containerGridProperties.gridTemplateRows) + const borderTopWidth = parseCSSLength(computedStyle.borderTopWidth) + const borderRightWidth = parseCSSLength(computedStyle.borderRightWidth) + const borderBottomWidth = parseCSSLength(computedStyle.borderBottomWidth) + const borderLeftWidth = parseCSSLength(computedStyle.borderLeftWidth) + const border: BorderWidths = { + top: isRight(borderTopWidth) ? borderTopWidth.value.value : 0, + right: isRight(borderRightWidth) ? borderRightWidth.value.value : 0, + bottom: isRight(borderBottomWidth) ? borderBottomWidth.value.value : 0, + left: isRight(borderLeftWidth) ? borderLeftWidth.value.value : 0, + } + const padding = defaultEither( + sides(undefined, undefined, undefined, undefined), + applicative4Either( + applicativeSidesPxTransform, + parseCSSLength(computedStyle.paddingTop), + parseCSSLength(computedStyle.paddingRight), + parseCSSLength(computedStyle.paddingBottom), + parseCSSLength(computedStyle.paddingLeft), + ), + ) + const gap = defaultEither( + null, + mapEither((n) => n.value, parseCSSLength(computedStyle.gap)), + ) + + const rowGap = defaultEither( + null, + mapEither((n) => n.value, parseCSSLength(computedStyle.rowGap)), + ) + + const columnGap = defaultEither( + null, + mapEither((n) => n.value, parseCSSLength(computedStyle.columnGap)), + ) + + const frame = getGlobalFrame(canvasRootContainer, element, scale) + + return { + frame: frame, + gridTemplateColumns: containerGridProperties.gridTemplateColumns, + gridTemplateRows: containerGridProperties.gridTemplateRows, + gridTemplateColumnsFromProps: containerGridPropertiesFromProps.gridTemplateColumns, + gridTemplateRowsFromProps: containerGridPropertiesFromProps.gridTemplateRows, + border: border, + padding: padding, + gap: gap, + rowGap: rowGap, + columnGap: columnGap, + rows: rows, + columns: columns, + cells: columns * rows, + computedStyling: computedStyling, + element: element, + } + } +} + +export function useObserversToWatch(elementPathOrPaths: Array | ElementPath): number { + // Used to trigger extra renders. + const [counter, setCounter] = React.useState(0) + const bumpCounter = React.useCallback(() => { + setCounter((value) => value + 1) + }, []) + + // Need to use the mount count for the callback trigger. + const mountCount = useEditorState( + Substores.canvas, + (store) => store.editor.canvas.mountCount, + 'useObserversToWatch mountCount', + ) + + React.useEffect(() => { + // Add the change callback(s) for the element path or paths. + if (Array.isArray(elementPathOrPaths)) { + for (const elementPath of elementPathOrPaths) { + addChangeCallback(mountCount, elementPath, bumpCounter) + } + } else { + addChangeCallback(mountCount, elementPathOrPaths, bumpCounter) + } + + return function cleanup() { + if (Array.isArray(elementPathOrPaths)) { + for (const elementPath of elementPathOrPaths) { + removeChangeCallback(elementPath, bumpCounter) + } + } else { + removeChangeCallback(elementPathOrPaths, bumpCounter) + } + } + }, [mountCount, elementPathOrPaths, bumpCounter]) + + return counter +} + +export function useGridMeasurementHelperData( + elementPath: ElementPath, + source: ElementOrParent, +): GridMeasurementHelperData | undefined { + const scale = useEditorState( + Substores.canvas, + (store) => store.editor.canvas.scale, + 'useGridMeasurementHelperData scale', + ) + + useMonitorChangesToElements([elementPath]) + + useObserversToWatch(elementPath) + + return useKeepReferenceEqualityIfPossible( + getGridMeasurementHelperData(elementPath, scale, source), + ) +} + +export function useGridElementMeasurementHelperData( + elementPath: ElementPath, +): GridElementMeasurementHelperData | undefined { + const scale = useEditorState( + Substores.canvas, + (store) => store.editor.canvas.scale, + 'useGridMeasurementHelperData scale', + ) + + useMonitorChangesToElements([elementPath]) + + useObserversToWatch(elementPath) + + return useKeepReferenceEqualityIfPossible(getGridElementMeasurementHelperData(elementPath, scale)) +} diff --git a/editor/src/components/canvas/controls/new-canvas-controls.tsx b/editor/src/components/canvas/controls/new-canvas-controls.tsx index 6e31c6e04816..9f4a94620c27 100644 --- a/editor/src/components/canvas/controls/new-canvas-controls.tsx +++ b/editor/src/components/canvas/controls/new-canvas-controls.tsx @@ -75,7 +75,11 @@ import { NO_OP } from '../../../core/shared/utils' import { useIsMyProject } from '../../editor/store/collaborative-editing' import { MultiplayerWrapper } from '../../../utils/multiplayer-wrapper' import { MultiplayerPresence } from '../multiplayer-presence' -import { GridElementContainingBlocks, GridMeasurementHelpers } from './grid-controls' +import { + GridElementContainingBlocks, + GridHelperControls, + GridMeasurementHelpers, +} from './grid-controls' export const CanvasControlsContainerID = 'new-canvas-controls-container' @@ -617,11 +621,7 @@ const NewCanvasControlsInner = (props: NewCanvasControlsInnerProps) => { <> {when( isSelectMode(editorMode) || isInsertMode(editorMode), - , - )} - {when( - isSelectMode(editorMode) || isInsertMode(editorMode), - , + , )} {middleStrategyControls.map((c) => ( = (element: HTMLElement) => T diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index a6076342049e..baeb2ca0da52 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -20,6 +20,7 @@ import { domElementMetadata, gridAutoOrTemplateDimensions, isGridSpan, + isGridAutoOrTemplateDimensions, } from '../../core/shared/element-template' import type { ElementPath } from '../../core/shared/project-file-types' import type { ElementCanvasRectangleCache } from '../../core/shared/dom-utils' @@ -625,7 +626,7 @@ function trimDynamicEmptyDimensions( return gridAutoOrTemplateDimensions(template.dimensions.slice(0, lastNonEmptyColumn + 1)) } -function getGridElementProperties( +export function getGridElementProperties( container: GridContainerProperties, elementStyle: CSSStyleDeclaration, ): GridElementProperties { diff --git a/editor/src/components/canvas/gap-utils.ts b/editor/src/components/canvas/gap-utils.ts index af9c5103cba4..b0c227034138 100644 --- a/editor/src/components/canvas/gap-utils.ts +++ b/editor/src/components/canvas/gap-utils.ts @@ -32,8 +32,6 @@ import { isReversedFlexDirection } from '../../core/model/flex-utils' import * as EP from '../../core/shared/element-path' import { treatElementAsFragmentLike } from './canvas-strategies/strategies/fragment-like-helpers' import type { AllElementProps } from '../editor/store/editor-state' -import type { GridData } from './controls/grid-controls-for-strategies' -import { getNullableAutoOrTemplateBaseString } from './controls/grid-controls-for-strategies' import { optionalMap } from '../../core/shared/optional-utils' export interface PathWithBounds { diff --git a/editor/src/components/canvas/ui-jsx.test-utils.tsx b/editor/src/components/canvas/ui-jsx.test-utils.tsx index 1b8796cbcb92..a08f5fed99f1 100644 --- a/editor/src/components/canvas/ui-jsx.test-utils.tsx +++ b/editor/src/components/canvas/ui-jsx.test-utils.tsx @@ -428,6 +428,9 @@ export async function renderTestEditorWithModel( flushSync(() => { canvasStoreHook.setState(patchedStoreFromFullStore(workingEditorState, 'canvas-store')) + helperControlsStoreHook.setState( + patchedStoreFromFullStore(workingEditorState, 'helper-controls-store'), + ) }) // run dom SAMPLER @@ -612,6 +615,10 @@ export async function renderTestEditorWithModel( patchedStoreFromFullStore(initialEditorStore, 'canvas-store'), ) + const helperControlsStoreHook: UtopiaStoreAPI = createStoresAndState( + patchedStoreFromFullStore(initialEditorStore, 'helper-controls-store'), + ) + prevDomWalkerMutableState?.resizeObserver.disconnect() prevDomWalkerMutableState?.mutationObserver.disconnect() const domWalkerMutableState = createDomWalkerMutableState(canvasStoreHook, asyncTestDispatch) @@ -673,6 +680,7 @@ label { dispatch={asyncTestDispatch as EditorDispatch} mainStore={storeHook} canvasStore={canvasStoreHook} + helperControlsStore={helperControlsStoreHook} spyCollector={spyCollector} lowPriorityStore={lowPriorityStoreHook} domWalkerMutableState={domWalkerMutableState} diff --git a/editor/src/components/editor/store/editor-state.ts b/editor/src/components/editor/store/editor-state.ts index fdf25f0bfcd8..4f186b5cf9ba 100644 --- a/editor/src/components/editor/store/editor-state.ts +++ b/editor/src/components/editor/store/editor-state.ts @@ -494,7 +494,7 @@ export type EditorStoreFull = EditorStoreShared & { patchedDerived: DerivedState } -type StoreName = 'editor-store' | 'canvas-store' | 'low-priority-store' +type StoreName = 'editor-store' | 'canvas-store' | 'helper-controls-store' | 'low-priority-store' export type EditorStorePatched = EditorStoreShared & { storeName: StoreName diff --git a/editor/src/components/editor/store/store-hook.ts b/editor/src/components/editor/store/store-hook.ts index ea0824397cfc..172dec350cca 100644 --- a/editor/src/components/editor/store/store-hook.ts +++ b/editor/src/components/editor/store/store-hook.ts @@ -82,6 +82,8 @@ export const EditorStateContext = React.createContext(nul EditorStateContext.displayName = 'EditorStateContext' export const CanvasStateContext = React.createContext(null) CanvasStateContext.displayName = 'CanvasStateContext' +export const HelperControlsStateContext = React.createContext(null) +HelperControlsStateContext.displayName = 'HelperControlsStateContext' export const LowPriorityStateContext = React.createContext(null) LowPriorityStateContext.displayName = 'LowPriorityStateContext' diff --git a/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap b/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap index 85a45178b8d7..2fc81845e032 100644 --- a/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap +++ b/editor/src/core/performance/__snapshots__/performance-regression-tests.spec.tsx.snap @@ -2,6 +2,8 @@ exports[`React Render Count Tests - Changing the selected view with a less simple project 2`] = ` Array [ + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -33,8 +35,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -56,8 +57,9 @@ Array [ "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))//Symbol(react.memo)()", "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers/Symbol(react.memo)()", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers//div", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/null/Symbol(react.memo)()//Symbol(react.memo)()", "/Symbol(react.memo)()///UtopiaSpiedExoticType(Symbol(react.fragment))", "/null/Symbol(react.memo)()//Symbol(react.memo)()", @@ -790,6 +792,8 @@ Array [ exports[`React Render Count Tests - Changing the selected view with a simple project 2`] = ` Array [ + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -821,8 +825,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -844,8 +847,9 @@ Array [ "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))//Symbol(react.memo)()", "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers/Symbol(react.memo)()", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers//div", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/null/Symbol(react.memo)()//Symbol(react.memo)()", "/Symbol(react.memo)()///UtopiaSpiedExoticType(Symbol(react.fragment))", "/null/Symbol(react.memo)()//Symbol(react.memo)()", @@ -1475,6 +1479,8 @@ Array [ "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/Symbol(react.forward_ref)(CanvasContainer)/div:data-testid='canvas-container'", "/Symbol(react.forward_ref)(CanvasContainer)/div/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -1498,8 +1504,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -1511,6 +1516,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", "/div/div/NavigatorComponent/Symbol(react.memo)()", "/div/div/NavigatorComponent/Symbol(react.memo)(NavigatorDragLayer)", @@ -1767,6 +1775,8 @@ Array [ "/UtopiaSpiedFunctionComponent(ComponentRenderer(App))/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)/div", "/UtopiaSpiedFunctionComponent(View)/div/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)", "/div/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)/div", + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -1790,8 +1800,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -1803,8 +1812,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers/Symbol(react.memo)()", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers//div", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/div/Symbol(react.memo)(Symbol(react.forward_ref)(SizeLabel))/Symbol(react.forward_ref)(SizeLabel)/div:data-testid='SizeLabelTestId'", "/div/Symbol(react.memo)(Symbol(react.forward_ref)(SizeLabel))/Symbol(react.forward_ref)(SizeLabel)/div:data-testid='parent-resize-label'", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", @@ -2396,8 +2406,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -2408,6 +2417,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/div:data-testid='new-canvas-controls-container'", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", "/null/RowForControl/RowForBaseControl/Symbol(react.memo)(Symbol(react.forward_ref)())", "/null/RowForControl/RowForBaseControl/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", @@ -2506,8 +2518,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -2518,6 +2529,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/div:data-testid='new-canvas-controls-container'", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", "/null/RowForControl/RowForBaseControl/Symbol(react.memo)(Symbol(react.forward_ref)())", "/null/RowForControl/RowForBaseControl/UtopiaSpiedFunctionComponent(PropertyLabelAndPlusButton)", @@ -2604,6 +2618,8 @@ Array [ "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/Symbol(react.forward_ref)(CanvasContainer)/div:data-testid='canvas-container'", "/Symbol(react.forward_ref)(CanvasContainer)/div/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -2627,8 +2643,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -2640,6 +2655,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", "/div/div/NavigatorComponent/Symbol(react.memo)()", "/div/div/NavigatorComponent/Symbol(react.memo)(NavigatorDragLayer)", @@ -2884,6 +2902,8 @@ Array [ "/UtopiaSpiedFunctionComponent(ComponentRenderer(App))/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)/div", "/UtopiaSpiedFunctionComponent(View)/div/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)", "/div/Symbol(react.forward_ref)(SpyWrapper)/UtopiaSpiedFunctionComponent(View)/div", + "/UtopiaSpiedExoticType(Symbol(react.provider))/Symbol(react.provider)/GridMeasurementHelpers/Symbol(react.memo)()", + "/Symbol(react.provider)/GridMeasurementHelpers//div", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//UtopiaSpiedClass(EditorCanvas)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//Symbol(react.memo)(FloatingPostActionMenu)", "/UtopiaSpiedFunctionComponent(SimpleFlexColumn)/div//div", @@ -2907,8 +2927,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -2920,8 +2939,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", "/div/UtopiaSpiedExoticType(Symbol(react.fragment))//UtopiaSpiedExoticType(Symbol(react.fragment))", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers/Symbol(react.memo)()", - "/UtopiaSpiedExoticType(Symbol(react.fragment))/GridMeasurementHelpers//div", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/div/Symbol(react.memo)(Symbol(react.forward_ref)(SizeLabel))/Symbol(react.forward_ref)(SizeLabel)/div:data-testid='SizeLabelTestId'", "/div/Symbol(react.memo)(Symbol(react.forward_ref)(SizeLabel))/Symbol(react.forward_ref)(SizeLabel)/div:data-testid='parent-resize-label'", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", @@ -3389,8 +3409,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -3401,6 +3420,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/div:data-testid='new-canvas-controls-container'", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(ComponentRenderer(storyboard))/Symbol(react.forward_ref)(SpyWrapper)", "/UtopiaSpiedFunctionComponent(Provider)/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(ComponentRenderer(storyboard))/Symbol(react.forward_ref)(SpyWrapper)", @@ -3441,8 +3463,7 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(GridMeasurementHelpers)", - "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", + "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedFunctionComponent(GridHelperControls)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)()", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", @@ -3453,6 +3474,9 @@ Array [ "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/div:data-testid='new-canvas-controls-container'", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/Symbol(react.memo)(SelectionAreaRectangle)", "/div/div/UtopiaSpiedFunctionComponent(NewCanvasControlsInner)/UtopiaSpiedExoticType(Symbol(react.fragment))", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)(GridMeasurementHelpers)", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/Symbol(react.memo)()", + "/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedExoticType(Symbol(react.fragment))/UtopiaSpiedFunctionComponent(GridHelperControls)/UtopiaSpiedExoticType(Symbol(react.provider))", "/Symbol(react.forward_ref)(Styled(div))/div/Symbol(react.forward_ref)(Styled(div))/div", ] `; diff --git a/editor/src/core/performance/performance-regression-tests.spec.tsx b/editor/src/core/performance/performance-regression-tests.spec.tsx index 313173838176..47cef035e96b 100644 --- a/editor/src/core/performance/performance-regression-tests.spec.tsx +++ b/editor/src/core/performance/performance-regression-tests.spec.tsx @@ -65,7 +65,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`872`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`882`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) @@ -127,7 +127,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`1124`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`1134`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) @@ -183,7 +183,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`658`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`660`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) @@ -249,7 +249,7 @@ describe('React Render Count Tests -', () => { const renderCountAfter = renderResult.getNumberOfRenders() // if this breaks, GREAT NEWS but update the test please :) - expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`783`) + expect(renderCountAfter - renderCountBefore).toMatchInlineSnapshot(`785`) expect(renderResult.getRenderInfo()).toMatchSnapshot() }) }) diff --git a/editor/src/templates/editor.tsx b/editor/src/templates/editor.tsx index 0f5f22c96362..02bb102620c0 100644 --- a/editor/src/templates/editor.tsx +++ b/editor/src/templates/editor.tsx @@ -58,6 +58,7 @@ import { CanvasStateContext, createStoresAndState, EditorStateContext, + HelperControlsStateContext, LowPriorityStateContext, OriginalMainEditorStateContext, } from '../components/editor/store/store-hook' @@ -221,6 +222,7 @@ export class Editor { storedState: EditorStoreFull utopiaStoreHook: UtopiaStoreAPI canvasStore: UtopiaStoreAPI + helperControlsStore: UtopiaStoreAPI lowPriorityStore: UtopiaStoreAPI spyCollector: UiJsxCanvasContextData = emptyUiJsxCanvasContextData() domWalkerMutableState: DomWalkerMutableStateData @@ -247,6 +249,7 @@ export class Editor { this.boundDispatch, this.utopiaStoreHook, this.canvasStore, + this.helperControlsStore, this.lowPriorityStore, this.spyCollector, this.domWalkerMutableState, @@ -298,6 +301,10 @@ export class Editor { patchedStoreFromFullStore(this.storedState, 'canvas-store'), ) + const helperControlsStore = createStoresAndState( + patchedStoreFromFullStore(this.storedState, 'helper-controls-store'), + ) + const lowPriorityStore = createStoresAndState( patchedStoreFromFullStore(this.storedState, 'low-priority-store'), ) @@ -306,6 +313,8 @@ export class Editor { this.canvasStore = canvasStore + this.helperControlsStore = helperControlsStore + this.lowPriorityStore = lowPriorityStore this.domWalkerMutableState = createDomWalkerMutableState( @@ -490,6 +499,9 @@ export class Editor { ReactDOM.flushSync(() => { ReactDOM.unstable_batchedUpdates(() => { this.canvasStore.setState(patchedStoreFromFullStore(this.storedState, 'canvas-store')) + this.helperControlsStore.setState( + patchedStoreFromFullStore(this.storedState, 'helper-controls-store'), + ) }) }) }) @@ -727,6 +739,7 @@ export const EditorRoot: React.FunctionComponent<{ dispatch: EditorDispatch mainStore: UtopiaStoreAPI canvasStore: UtopiaStoreAPI + helperControlsStore: UtopiaStoreAPI lowPriorityStore: UtopiaStoreAPI spyCollector: UiJsxCanvasContextData domWalkerMutableState: DomWalkerMutableStateData @@ -734,6 +747,7 @@ export const EditorRoot: React.FunctionComponent<{ dispatch, mainStore, canvasStore, + helperControlsStore, lowPriorityStore, spyCollector, domWalkerMutableState, @@ -748,15 +762,17 @@ export const EditorRoot: React.FunctionComponent<{ - - - - - - - + + + + + + + + + @@ -773,17 +789,27 @@ export const HotRoot: React.FunctionComponent<{ dispatch: EditorDispatch mainStore: UtopiaStoreAPI canvasStore: UtopiaStoreAPI + helperControlsStore: UtopiaStoreAPI lowPriorityStore: UtopiaStoreAPI spyCollector: UiJsxCanvasContextData domWalkerMutableState: DomWalkerMutableStateData }> = hot( - ({ dispatch, mainStore, canvasStore, lowPriorityStore, spyCollector, domWalkerMutableState }) => { + ({ + dispatch, + mainStore, + canvasStore, + helperControlsStore, + lowPriorityStore, + spyCollector, + domWalkerMutableState, + }) => { return ( @@ -796,6 +822,7 @@ async function renderRootComponent( dispatch: EditorDispatch, mainStore: UtopiaStoreAPI, canvasStore: UtopiaStoreAPI, + helperControlsStore: UtopiaStoreAPI, lowPriorityStore: UtopiaStoreAPI, spyCollector: UiJsxCanvasContextData, domWalkerMutableState: DomWalkerMutableStateData, @@ -813,6 +840,7 @@ async function renderRootComponent( mainStore={mainStore} spyCollector={spyCollector} canvasStore={canvasStore} + helperControlsStore={helperControlsStore} lowPriorityStore={lowPriorityStore} domWalkerMutableState={domWalkerMutableState} />, @@ -824,6 +852,7 @@ async function renderRootComponent( spyCollector={spyCollector} mainStore={mainStore} canvasStore={canvasStore} + helperControlsStore={helperControlsStore} lowPriorityStore={lowPriorityStore} domWalkerMutableState={domWalkerMutableState} />,