diff --git a/app/designer/designer.tsx b/app/designer/designer.tsx
index fe357f2e38..e26c9e82ba 100644
--- a/app/designer/designer.tsx
+++ b/app/designer/designer.tsx
@@ -228,7 +228,7 @@ export const Designer = ({ config, project }: DesignerProps) => {
height: "100%",
minWidth,
}}
- style={{ width: canvasWidth || "100%" }}
+ style={{ width: canvasWidth }}
/>
diff --git a/app/designer/features/breakpoints/trigger-button.tsx b/app/designer/features/breakpoints/trigger-button.tsx
index 44f3349cbf..b6acdc8518 100644
--- a/app/designer/features/breakpoints/trigger-button.tsx
+++ b/app/designer/features/breakpoints/trigger-button.tsx
@@ -36,7 +36,7 @@ export const TriggerButton = forwardRef<
>((props, ref) => {
const [zoom] = useZoom();
const [breakpoint] = useSelectedBreakpoint();
- const [canvasWidth = 0] = useCanvasWidth();
+ const [canvasWidth] = useCanvasWidth();
if (breakpoint === undefined) return null;
const variant = willRender(breakpoint, canvasWidth) ? "contrast" : "gray";
diff --git a/app/designer/features/breakpoints/use-update-canvas-width.ts b/app/designer/features/breakpoints/use-update-canvas-width.ts
index abed3d20d6..6f358e63cd 100644
--- a/app/designer/features/breakpoints/use-update-canvas-width.ts
+++ b/app/designer/features/breakpoints/use-update-canvas-width.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef } from "react";
+import { useCallback, useEffect } from "react";
import {
useSelectedBreakpoint,
useCanvasWidth,
@@ -8,7 +8,6 @@ import { minWidth } from "./width-setting";
export const useUpdateCanvasWidth = () => {
const [selectedBreakpoint] = useSelectedBreakpoint();
const [canvasWidth, setCanvasWidth] = useCanvasWidth();
- const initialCanvasWidthRef = useRef();
useEffect(() => {
if (selectedBreakpoint === undefined) {
@@ -16,21 +15,27 @@ export const useUpdateCanvasWidth = () => {
}
if (selectedBreakpoint.minWidth === 0) {
- setCanvasWidth(initialCanvasWidthRef.current);
+ setCanvasWidth(minWidth);
return;
}
setCanvasWidth(Math.max(selectedBreakpoint.minWidth, minWidth));
}, [selectedBreakpoint, setCanvasWidth]);
+ // Set the initial canvas width based on the selected breakpoint upper bound, which starts where the next breakpoint begins.
return useCallback(
(iframe: HTMLIFrameElement | null) => {
- if (iframe === null || canvasWidth !== undefined) return;
- initialCanvasWidthRef.current = Math.round(
- iframe.getBoundingClientRect().width
- );
- setCanvasWidth(initialCanvasWidthRef.current);
+ // Once canvasWidth is set, it means we have already set the initial width.
+ if (
+ iframe === null ||
+ selectedBreakpoint === undefined ||
+ canvasWidth !== 0
+ ) {
+ return;
+ }
+
+ setCanvasWidth(minWidth);
},
- [canvasWidth, setCanvasWidth]
+ [canvasWidth, selectedBreakpoint, setCanvasWidth]
);
};
diff --git a/app/designer/features/breakpoints/width-setting.tsx b/app/designer/features/breakpoints/width-setting.tsx
index 3a61e4274f..1a793a1259 100644
--- a/app/designer/features/breakpoints/width-setting.tsx
+++ b/app/designer/features/breakpoints/width-setting.tsx
@@ -1,20 +1,60 @@
-import { useCanvasWidth } from "~/designer/shared/nano-values";
+import { useMemo } from "react";
+import {
+ useCanvasWidth,
+ useIsPreviewMode,
+ useSelectedBreakpoint,
+ useBreakpoints,
+} from "~/designer/shared/nano-values";
import { Slider, Text, Flex } from "~/shared/design-system";
+import { sort } from "./sort";
-// Doesn't make sense to allow resizing the canvas lower than this.
-export const minWidth = 200;
+// Doesn't make sense to allow resizing the canvas lower/higher than this.
+export const minWidth = 360;
export const maxWidth = 3000;
+/**
+ * Return the next breakpoint from the currently selected one, sorted by the `sort()`
+ */
+const useNextBreakpoint = () => {
+ const [selectedBreakpoint] = useSelectedBreakpoint();
+ const [breakpoints] = useBreakpoints();
+
+ const sortedBreakpoints = useMemo(() => sort(breakpoints), [breakpoints]);
+
+ return useMemo(() => {
+ if (selectedBreakpoint === undefined) return;
+ const index = sortedBreakpoints.indexOf(selectedBreakpoint);
+ if (index === -1) return undefined;
+ return sortedBreakpoints[index + 1];
+ }, [sortedBreakpoints, selectedBreakpoint]);
+};
+
export const WidthSetting = () => {
- const [value = 0, setValue] = useCanvasWidth();
+ const [value, setValue] = useCanvasWidth();
+ const [selectedBreakpoint] = useSelectedBreakpoint();
+ const nextBreakpoint = useNextBreakpoint();
+ const [isPreviewMode] = useIsPreviewMode();
+
+ if (selectedBreakpoint === undefined) return null;
+
+ // We want to enable unconstrained resizing in a preview mode
+ const min = isPreviewMode
+ ? minWidth
+ : Math.max(minWidth, selectedBreakpoint.minWidth);
+ const max = isPreviewMode
+ ? maxWidth
+ : Math.min(
+ maxWidth,
+ nextBreakpoint ? nextBreakpoint.minWidth - 1 : maxWidth
+ );
return (
Canvas width
{
setValue(value);
diff --git a/app/designer/features/style-panel/style-panel.tsx b/app/designer/features/style-panel/style-panel.tsx
index 9b75f4b362..858da6d6f6 100644
--- a/app/designer/features/style-panel/style-panel.tsx
+++ b/app/designer/features/style-panel/style-panel.tsx
@@ -33,8 +33,7 @@ export const StylePanel = ({
currentStyle === undefined ||
inheritedStyle === undefined ||
selectedInstanceData === undefined ||
- breakpoint === undefined ||
- canvasWidth === undefined
+ breakpoint === undefined
) {
return null;
}
diff --git a/app/designer/shared/nano-values/index.ts b/app/designer/shared/nano-values/index.ts
index f9b685d095..21f4fb3589 100644
--- a/app/designer/shared/nano-values/index.ts
+++ b/app/designer/shared/nano-values/index.ts
@@ -34,7 +34,7 @@ export const useBreakpoints = () => useValue(breakpointsContainer);
const zoomContainer = createValueContainer(100);
export const useZoom = () => useValue(zoomContainer);
-const canvasWidthContainer = createValueContainer();
+const canvasWidthContainer = createValueContainer(0);
export const useCanvasWidth = () => useValue(canvasWidthContainer);
const syncStatusContainer = createValueContainer("idle");