From c4f93e471f1543e6f755d9cff73f69c70afcb4dd Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:03:43 +0200 Subject: [PATCH 01/10] Move the mutation/resize observer to the CanvasWrapperComponent --- .../src/components/canvas/canvas-wrapper-component.tsx | 3 +++ editor/src/components/canvas/dom-walker.ts | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/editor/src/components/canvas/canvas-wrapper-component.tsx b/editor/src/components/canvas/canvas-wrapper-component.tsx index 099a094a4747..4787b8416853 100644 --- a/editor/src/components/canvas/canvas-wrapper-component.tsx +++ b/editor/src/components/canvas/canvas-wrapper-component.tsx @@ -19,6 +19,8 @@ import { Substores, useEditorState } from '../editor/store/store-hook' import { shouldShowErrorOverlay } from './canvas-utils' import { FloatingPostActionMenu } from './controls/select-mode/post-action-menu' +export const CanvasWrapperComponentId = 'canvas-wrapper-component' + export const CanvasWrapperComponent = React.memo(() => { const dispatch = useDispatch() const { editorState, derivedState, userState } = useEditorState( @@ -74,6 +76,7 @@ export const CanvasWrapperComponent = React.memo(() => { return ( { + document.querySelectorAll(`#${CanvasWrapperComponentId} *`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) - domWalkerMutableState.mutationObserver.observe(canvasRootContainer, MutationObserverConfig) + domWalkerMutableState.mutationObserver.observe(canvasWrapperComponent, MutationObserverConfig) } } From ab1d3f14dcc7102ede4d09aa0cbe6ae4b8a69d5b Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:27:57 +0200 Subject: [PATCH 02/10] Use the canvas root container when possible --- editor/src/components/canvas/dom-walker.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 908657f9c5d4..1521040306eb 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -292,17 +292,25 @@ export function resubscribeObservers(domWalkerMutableState: { mutationObserver: MutationObserver resizeObserver: ResizeObserver }) { - const canvasWrapperComponent = document.getElementById(CanvasWrapperComponentId) + let rootElementId = CanvasContainerID + let rootElement = document.getElementById(rootElementId) + + if (rootElement == null) { + // when there is a build error the canvas root container is not rendered, so we attach the observers to the canvas wrapper component + rootElementId = CanvasWrapperComponentId + rootElement = document.getElementById(rootElementId) + } + if ( ObserversAvailable && - canvasWrapperComponent != null && + rootElement != null && domWalkerMutableState.resizeObserver != null && domWalkerMutableState.mutationObserver != null ) { - document.querySelectorAll(`#${CanvasWrapperComponentId} *`).forEach((elem) => { + document.querySelectorAll(`#${rootElementId} *`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) - domWalkerMutableState.mutationObserver.observe(canvasWrapperComponent, MutationObserverConfig) + domWalkerMutableState.mutationObserver.observe(rootElement, MutationObserverConfig) } } From d876c39e9bb6727db21dac7779c0e29a93d04323 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:31:56 +0200 Subject: [PATCH 03/10] check if this fixes the tests --- editor/src/components/canvas/dom-walker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 1521040306eb..084d6a64ee51 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -295,11 +295,11 @@ export function resubscribeObservers(domWalkerMutableState: { let rootElementId = CanvasContainerID let rootElement = document.getElementById(rootElementId) - if (rootElement == null) { - // when there is a build error the canvas root container is not rendered, so we attach the observers to the canvas wrapper component - rootElementId = CanvasWrapperComponentId - rootElement = document.getElementById(rootElementId) - } + // if (rootElement == null) { + // // when there is a build error the canvas root container is not rendered, so we attach the observers to the canvas wrapper component + // rootElementId = CanvasWrapperComponentId + // rootElement = document.getElementById(rootElementId) + // } if ( ObserversAvailable && From cf698cdab1b88e7fc2a1b37311b20ee892da83fd Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:54:31 +0200 Subject: [PATCH 04/10] Another try on the tests --- editor/src/components/canvas/dom-walker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 084d6a64ee51..1521040306eb 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -295,11 +295,11 @@ export function resubscribeObservers(domWalkerMutableState: { let rootElementId = CanvasContainerID let rootElement = document.getElementById(rootElementId) - // if (rootElement == null) { - // // when there is a build error the canvas root container is not rendered, so we attach the observers to the canvas wrapper component - // rootElementId = CanvasWrapperComponentId - // rootElement = document.getElementById(rootElementId) - // } + if (rootElement == null) { + // when there is a build error the canvas root container is not rendered, so we attach the observers to the canvas wrapper component + rootElementId = CanvasWrapperComponentId + rootElement = document.getElementById(rootElementId) + } if ( ObserversAvailable && From 7f310ee0ab72bddf1a25ab933b6bd23e828802c7 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:51:36 +0200 Subject: [PATCH 05/10] Use canvas-container-outer for the observers (and render it all the time) --- .../canvas/canvas-component-entry.tsx | 13 ++++++++---- .../canvas/canvas-wrapper-component.tsx | 21 +++++++++---------- editor/src/components/canvas/dom-walker.ts | 19 +++++++---------- editor/src/templates/editor-canvas.tsx | 16 ++++++++------ 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/editor/src/components/canvas/canvas-component-entry.tsx b/editor/src/components/canvas/canvas-component-entry.tsx index ba919a3f5bbb..e9a4faaf3343 100644 --- a/editor/src/components/canvas/canvas-component-entry.tsx +++ b/editor/src/components/canvas/canvas-component-entry.tsx @@ -25,7 +25,11 @@ import type { } from './ui-jsx-canvas' import { DomWalkerInvalidatePathsCtxAtom, UiJsxCanvas, pickUiJsxCanvasProps } from './ui-jsx-canvas' -interface CanvasComponentEntryProps {} +export const CanvasContainerOuterId = 'canvas-container-outer' + +interface CanvasComponentEntryProps { + shouldRenderCanvas: boolean +} export const CanvasComponentEntry = React.memo((props: CanvasComponentEntryProps) => { const canvasStore = React.useContext(CanvasStateContext) @@ -85,7 +89,7 @@ const CanvasComponentEntryInner = React.memo((props: CanvasComponentEntryProps) <> {when(canvasProps == null, )}
- {canvasProps == null ? null : ( + {props.shouldRenderCanvas && canvasProps != null ? ( - )} + ) : null}
+ , ) }) diff --git a/editor/src/components/canvas/canvas-wrapper-component.tsx b/editor/src/components/canvas/canvas-wrapper-component.tsx index 4787b8416853..c291a863ec8e 100644 --- a/editor/src/components/canvas/canvas-wrapper-component.tsx +++ b/editor/src/components/canvas/canvas-wrapper-component.tsx @@ -88,17 +88,16 @@ export const CanvasWrapperComponent = React.memo(() => { // ^ prevents Monaco from pushing the inspector out }} > - {fatalErrors.length === 0 && !safeMode ? ( - - ) : null} + { + document.querySelectorAll(`#${CanvasContainerOuterId} *`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) - domWalkerMutableState.mutationObserver.observe(rootElement, MutationObserverConfig) + domWalkerMutableState.mutationObserver.observe( + canvasContainerOuterElement, + MutationObserverConfig, + ) } } diff --git a/editor/src/templates/editor-canvas.tsx b/editor/src/templates/editor-canvas.tsx index cbccfb2c07c4..888acce8bc6f 100644 --- a/editor/src/templates/editor-canvas.tsx +++ b/editor/src/templates/editor-canvas.tsx @@ -695,6 +695,7 @@ interface EditorCanvasProps { builtinDependencies: BuiltInDependencies dispatch: EditorDispatch updateCanvasSize: (newValueOrUpdater: Size | ((oldValue: Size) => Size)) => void + shouldRenderCanvas: boolean } export class EditorCanvas extends React.Component { @@ -855,6 +856,10 @@ export class EditorCanvas extends React.Component { // we round the offset here, so all layers, the canvas, and controls use the same rounded value for positioning // instead of letting Chrome do it, because that results in funky artifacts (e.g. while scrolling, the layers don't "jump" pixels at the same time) + if (!this.props.shouldRenderCanvas) { + return + } + const nodeConnectorsDiv = createNodeConnectorsDiv( this.props.model.canvasOffset, this.props.model.scale, @@ -871,10 +876,9 @@ export class EditorCanvas extends React.Component { const canvasIsLive = isLiveMode(this.props.editor.mode) - const canvasControls = React.createElement(NewCanvasControls, { - windowToCanvasPosition: this.getPosition, - cursor, - }) + const canvasControls = ( + + ) const canvasLiveEditingStyle = canvasIsLive ? UtopiaStyles.canvas.live @@ -1070,9 +1074,9 @@ export class EditorCanvas extends React.Component { }, }, nodeConnectorsDiv, - React.createElement(CanvasComponentEntry, {}), + , canvasControls, - React.createElement(CursorComponent, {}), + , , ) } From 436d32dcbc6fed273ff97b4dbfda55f51c8dae45 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:11:13 +0200 Subject: [PATCH 06/10] Remove unnecessary id --- editor/src/components/canvas/canvas-wrapper-component.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/editor/src/components/canvas/canvas-wrapper-component.tsx b/editor/src/components/canvas/canvas-wrapper-component.tsx index c291a863ec8e..d3782584d585 100644 --- a/editor/src/components/canvas/canvas-wrapper-component.tsx +++ b/editor/src/components/canvas/canvas-wrapper-component.tsx @@ -19,8 +19,6 @@ import { Substores, useEditorState } from '../editor/store/store-hook' import { shouldShowErrorOverlay } from './canvas-utils' import { FloatingPostActionMenu } from './controls/select-mode/post-action-menu' -export const CanvasWrapperComponentId = 'canvas-wrapper-component' - export const CanvasWrapperComponent = React.memo(() => { const dispatch = useDispatch() const { editorState, derivedState, userState } = useEditorState( @@ -76,7 +74,6 @@ export const CanvasWrapperComponent = React.memo(() => { return ( Date: Tue, 10 Sep 2024 15:13:04 +0200 Subject: [PATCH 07/10] Cleanup unnecessary changes --- editor/src/components/canvas/dom-walker.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index babb2f3c60b1..1db25e23fc9c 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -60,7 +60,6 @@ import { import { camelCaseToDashed } from '../../core/shared/string-utils' import type { UtopiaStoreAPI } from '../editor/store/store-hook' import { UTOPIA_SCENE_ID_KEY } from '../../core/model/utopia-constants' -import { CanvasContainerID } from './canvas-types' import { emptySet } from '../../core/shared/set-utils' import type { PathWithString } from '../../core/shared/uid-utils' import { getDeepestPathOnDomElement, getPathStringsOnDomElement } from '../../core/shared/uid-utils' @@ -72,7 +71,6 @@ import { pick } from '../../core/shared/object-utils' import { getFlexAlignment, getFlexJustifyContent, MaxContent } from '../inspector/inspector-common' import type { EditorDispatch } from '../editor/action-types' import { runDOMWalker } from '../editor/actions/action-creators' -import { CanvasWrapperComponentId } from './canvas-wrapper-component' import { CanvasContainerOuterId } from './canvas-component-entry' export const ResizeObserver = @@ -293,21 +291,18 @@ export function resubscribeObservers(domWalkerMutableState: { mutationObserver: MutationObserver resizeObserver: ResizeObserver }) { - const canvasContainerOuterElement = document.getElementById(CanvasContainerOuterId) + const canvasRootContainer = document.getElementById(CanvasContainerOuterId) if ( ObserversAvailable && - canvasContainerOuterElement != null && + canvasRootContainer != null && domWalkerMutableState.resizeObserver != null && domWalkerMutableState.mutationObserver != null ) { document.querySelectorAll(`#${CanvasContainerOuterId} *`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) - domWalkerMutableState.mutationObserver.observe( - canvasContainerOuterElement, - MutationObserverConfig, - ) + domWalkerMutableState.mutationObserver.observe(canvasRootContainer, MutationObserverConfig) } } From a15615fa23f70dd466d198300a6267d0ec6deb85 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:46:37 +0200 Subject: [PATCH 08/10] Disconnect observers before observing new (or old) elements --- editor/src/components/canvas/dom-walker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 1db25e23fc9c..81e1aa02302c 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -299,6 +299,8 @@ export function resubscribeObservers(domWalkerMutableState: { domWalkerMutableState.resizeObserver != null && domWalkerMutableState.mutationObserver != null ) { + domWalkerMutableState.resizeObserver.disconnect() + domWalkerMutableState.mutationObserver.disconnect() document.querySelectorAll(`#${CanvasContainerOuterId} *`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) From 9703db8429444d90100732e30210a0a85b7f09a6 Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:05:54 +0200 Subject: [PATCH 09/10] Filter elements to observer to real utopia elements --- editor/src/components/canvas/dom-walker.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/editor/src/components/canvas/dom-walker.ts b/editor/src/components/canvas/dom-walker.ts index 81e1aa02302c..3ff63324bf26 100644 --- a/editor/src/components/canvas/dom-walker.ts +++ b/editor/src/components/canvas/dom-walker.ts @@ -59,7 +59,7 @@ import { } from '../inspector/common/css-utils' import { camelCaseToDashed } from '../../core/shared/string-utils' import type { UtopiaStoreAPI } from '../editor/store/store-hook' -import { UTOPIA_SCENE_ID_KEY } from '../../core/model/utopia-constants' +import { UTOPIA_SCENE_ID_KEY, UTOPIA_UID_KEY } from '../../core/model/utopia-constants' import { emptySet } from '../../core/shared/set-utils' import type { PathWithString } from '../../core/shared/uid-utils' import { getDeepestPathOnDomElement, getPathStringsOnDomElement } from '../../core/shared/uid-utils' @@ -299,9 +299,7 @@ export function resubscribeObservers(domWalkerMutableState: { domWalkerMutableState.resizeObserver != null && domWalkerMutableState.mutationObserver != null ) { - domWalkerMutableState.resizeObserver.disconnect() - domWalkerMutableState.mutationObserver.disconnect() - document.querySelectorAll(`#${CanvasContainerOuterId} *`).forEach((elem) => { + document.querySelectorAll(`#${CanvasContainerOuterId} [${UTOPIA_UID_KEY}]`).forEach((elem) => { domWalkerMutableState.resizeObserver.observe(elem) }) domWalkerMutableState.mutationObserver.observe(canvasRootContainer, MutationObserverConfig) From ee209666bd0331286b77485e5c53a972631a8bff Mon Sep 17 00:00:00 2001 From: Balint Gabor <127662+gbalint@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:38:04 +0200 Subject: [PATCH 10/10] Disconnect previous observers --- editor/src/components/canvas/ui-jsx.test-utils.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/editor/src/components/canvas/ui-jsx.test-utils.tsx b/editor/src/components/canvas/ui-jsx.test-utils.tsx index a95396aafcc8..f1866c8ece55 100644 --- a/editor/src/components/canvas/ui-jsx.test-utils.tsx +++ b/editor/src/components/canvas/ui-jsx.test-utils.tsx @@ -306,6 +306,8 @@ export function optOutFromCheckFileTimestamps() { }) } +let prevDomWalkerMutableState: DomWalkerMutableStateData | null = null + export async function renderTestEditorWithModel( rawModel: PersistentModel, awaitFirstDomReport: 'await-first-dom-report' | 'dont-await-first-dom-report', @@ -600,7 +602,10 @@ export async function renderTestEditorWithModel( patchedStoreFromFullStore(initialEditorStore, 'canvas-store'), ) + prevDomWalkerMutableState?.resizeObserver.disconnect() + prevDomWalkerMutableState?.mutationObserver.disconnect() const domWalkerMutableState = createDomWalkerMutableState(canvasStoreHook, asyncTestDispatch) + prevDomWalkerMutableState = domWalkerMutableState const lowPriorityStoreHook: UtopiaStoreAPI = createStoresAndState( patchedStoreFromFullStore(initialEditorStore, 'low-priority-store'),