Skip to content

Commit 27d9beb

Browse files
authored
ENG-831 - Refactor tldraw maximize to stop canvas jumping around (#423)
* Refactor Tldraw component to handle maximization state and viewport updates - Introduced `handleMaximizedChange` to manage fullscreen toggling and DOM manipulation. - Added `updateViewportScreenBounds` for viewport updates using Tldraw's built-in methods. - Updated styles to support maximized state in Tldraw canvas. - Removed unnecessary `maximized` state from props in `createUiOverrides`. * - Renamed `setMaximized` to `toggleMaximized` for clarity in both Tldraw and uiOverrides components. * Optimize viewport updates in Tldraw component by wrapping DOM measurements in requestAnimationFrame for smoother performance.
1 parent 486be1b commit 27d9beb

File tree

3 files changed

+45
-15
lines changed

3 files changed

+45
-15
lines changed

apps/roam/src/components/canvas/Tldraw.tsx

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
usePreloadAssets,
4343
StateNode,
4444
DefaultSpinner,
45+
Box,
4546
} from "tldraw";
4647
import "tldraw/tldraw.css";
4748
import tldrawStyles from "./tldrawStyles";
@@ -129,9 +130,39 @@ const TldrawCanvas = ({ title }: { title: string }) => {
129130
discourseContext.lastActions,
130131
);
131132

132-
const [maximized, setMaximized] = useState(false);
133133
const [isConvertToDialogOpen, setConvertToDialogOpen] = useState(false);
134134

135+
const updateViewportScreenBounds = (el: HTMLDivElement) => {
136+
// Use tldraw's built-in viewport bounds update with centering
137+
requestAnimationFrame(() => {
138+
const rect = el.getBoundingClientRect();
139+
appRef.current?.updateViewportScreenBounds(
140+
new Box(rect.left, rect.top, rect.width, rect.height),
141+
true,
142+
);
143+
});
144+
};
145+
const handleMaximizedChange = () => {
146+
// Direct DOM manipulation to avoid React re-renders
147+
if (!containerRef.current) return;
148+
const tldrawEl = containerRef.current;
149+
const wrapper = tldrawEl.closest(".roam-article, .rm-sidebar-outline");
150+
151+
if (tldrawEl.classList.contains("relative")) {
152+
// Going to fullscreen
153+
if (wrapper) wrapper.classList.add("dg-tldraw-maximized");
154+
tldrawEl.classList.add("absolute", "inset-0");
155+
tldrawEl.classList.remove("relative");
156+
updateViewportScreenBounds(tldrawEl);
157+
} else {
158+
// Going back to normal
159+
if (wrapper) wrapper.classList.remove("dg-tldraw-maximized");
160+
tldrawEl.classList.add("relative");
161+
tldrawEl.classList.remove("absolute", "inset-0");
162+
updateViewportScreenBounds(tldrawEl);
163+
}
164+
};
165+
135166
// Workaround to avoid a race condition when loading a canvas page directly
136167
// Start false to avoid noisy warnings on first render if timer isn't initialized yet
137168
const [isPluginReady, setIsPluginReady] = useState(false);
@@ -390,8 +421,7 @@ const TldrawCanvas = ({ title }: { title: string }) => {
390421
allNodes,
391422
allRelationNames,
392423
allAddReferencedNodeByAction,
393-
maximized,
394-
setMaximized,
424+
toggleMaximized: handleMaximizedChange,
395425
setConvertToDialogOpen,
396426
discourseContext,
397427
});
@@ -528,16 +558,11 @@ const TldrawCanvas = ({ title }: { title: string }) => {
528558

529559
return (
530560
<div
531-
className={`roamjs-tldraw-canvas-container z-10 h-full w-full overflow-hidden rounded-md border border-gray-300 bg-white ${maximized ? "absolute inset-0" : "relative"}`}
561+
className="roamjs-tldraw-canvas-container relative z-10 h-full w-full overflow-hidden rounded-md border border-gray-300 bg-white"
532562
ref={containerRef}
533563
tabIndex={-1}
534564
>
535565
<style>{tldrawStyles}</style>
536-
<style>
537-
{maximized
538-
? "div.roam-body div.roam-app div.roam-main div.roam-article { position: inherit; }"
539-
: ""}
540-
</style>
541566

542567
{needsUpgrade ? (
543568
<div className="flex h-full items-center justify-center">

apps/roam/src/components/canvas/tldrawStyles.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// tldrawStyles.ts because some of these styles need to be inlined
22
export const TLDRAW_DATA_ATTRIBUTE = "dg-tldraw-canvas-wrapper";
3-
export default `
3+
export default /* css */ `
44
/* Hide Roam Blocks only when canvas is present */
55
.roam-article[${TLDRAW_DATA_ATTRIBUTE}="true"] .rm-block-children {
66
display: none;
@@ -76,4 +76,11 @@ export default `
7676
background-size: contain;
7777
background-repeat: no-repeat;
7878
}
79+
80+
/* Maximize Tldraw Canvas */
81+
/* Used in conjunction with tailwind classes on the canvas container */
82+
.roam-body .roam-app .roam-main .roam-article.dg-tldraw-maximized,
83+
.roam-body .roam-app .roam-main .rm-sidebar-outline.dg-tldraw-maximized {
84+
position: static;
85+
}
7986
`;

apps/roam/src/components/canvas/uiOverrides.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,16 +317,14 @@ export const createUiOverrides = ({
317317
allRelationNames,
318318
allAddReferencedNodeByAction,
319319
discourseContext,
320-
maximized,
321-
setMaximized,
320+
toggleMaximized,
322321
setConvertToDialogOpen,
323322
}: {
324323
allNodes: DiscourseNode[];
325324
allRelationNames: string[];
326325
allAddReferencedNodeByAction: AddReferencedNodeType;
327326
discourseContext: DiscourseContextType;
328-
maximized: boolean;
329-
setMaximized: (maximized: boolean) => void;
327+
toggleMaximized: () => void;
330328
setConvertToDialogOpen: (open: boolean) => void;
331329
}): TLUiOverrides => ({
332330
tools: (editor, tools) => {
@@ -423,7 +421,7 @@ export const createUiOverrides = ({
423421
id: "toggle-full-screen",
424422
label: "action.toggle-full-screen" as TLUiTranslationKey,
425423
kbd: "!3",
426-
onSelect: () => setMaximized(!maximized),
424+
onSelect: () => toggleMaximized(),
427425
readonlyOk: true,
428426
};
429427

0 commit comments

Comments
 (0)