bringing back scroll constraints debug
This commit is contained in:
parent
4208c97b62
commit
baa7b3293a
@ -51,7 +51,10 @@ import {
|
||||
import { isElementLink } from "@excalidraw/element/elementLink";
|
||||
import { restore, restoreAppState } from "@excalidraw/excalidraw/data/restore";
|
||||
import { newElementWith } from "@excalidraw/element/mutateElement";
|
||||
import { isInitializedImageElement } from "@excalidraw/element/typeChecks";
|
||||
import {
|
||||
isFrameLikeElement,
|
||||
isInitializedImageElement,
|
||||
} from "@excalidraw/element/typeChecks";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
parseLibraryTokensFromUrl,
|
||||
@ -142,6 +145,10 @@ import "./index.scss";
|
||||
|
||||
import type { CollabAPI } from "./collab/Collab";
|
||||
import { getSelectedElements } from "@excalidraw/element/selection";
|
||||
import {
|
||||
decodeConstraints,
|
||||
encodeConstraints,
|
||||
} from "@excalidraw/excalidraw/scene/scrollConstraints";
|
||||
|
||||
polyfill();
|
||||
|
||||
@ -160,19 +167,37 @@ const ConstraintsSettings = ({
|
||||
const [constraints, setConstraints] =
|
||||
useState<DebugScrollConstraints>(initialConstraints);
|
||||
|
||||
const frames = excalidrawAPI
|
||||
.getSceneElements()
|
||||
.filter((e) => isFrameLikeElement(e));
|
||||
const [activeFrameId, setActiveFrameId] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// add JSON-stringified constraints into url hash for easy sharing
|
||||
const hash = new URLSearchParams(window.location.hash.slice(1));
|
||||
hash.set(
|
||||
"constraints",
|
||||
encodeURIComponent(
|
||||
window.btoa(JSON.stringify(constraints)).replace(/=+/, ""),
|
||||
),
|
||||
);
|
||||
hash.set("constraints", encodeConstraints(constraints));
|
||||
window.location.hash = decodeURIComponent(hash.toString());
|
||||
excalidrawAPI.setScrollConstraints(constraints);
|
||||
|
||||
constraints.enabled
|
||||
? excalidrawAPI.setScrollConstraints(constraints)
|
||||
: excalidrawAPI.setScrollConstraints(null);
|
||||
}, [constraints]);
|
||||
|
||||
useEffect(() => {
|
||||
const frame = frames.find((frame) => frame.id === activeFrameId);
|
||||
if (frame) {
|
||||
const { x, y, width, height } = frame;
|
||||
setConstraints((s) => ({
|
||||
x: Math.round(x),
|
||||
y: Math.round(y),
|
||||
width: Math.round(width),
|
||||
height: Math.round(height),
|
||||
enabled: s.enabled,
|
||||
viewportZoomFactor: s.viewportZoomFactor,
|
||||
lockZoom: s.lockZoom,
|
||||
}));
|
||||
}
|
||||
}, [activeFrameId]);
|
||||
|
||||
const [selection, setSelection] = useState<ExcalidrawElement[]>([]);
|
||||
useEffect(() => {
|
||||
return excalidrawAPI.onChange((elements, appState) => {
|
||||
@ -183,13 +208,22 @@ const ConstraintsSettings = ({
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "fixed",
|
||||
bottom: 10,
|
||||
left: "calc(50%)",
|
||||
transform: "translateX(-50%)",
|
||||
gap: "0.6rem",
|
||||
zIndex: 999999,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
gap: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "0.6rem",
|
||||
}}
|
||||
>
|
||||
enabled:{" "}
|
||||
@ -263,6 +297,21 @@ const ConstraintsSettings = ({
|
||||
}))
|
||||
}
|
||||
/>
|
||||
overscrollAllowance:
|
||||
<input
|
||||
placeholder="height"
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={constraints.overscrollAllowance?.toString()}
|
||||
onChange={(e) =>
|
||||
setConstraints((s) => ({
|
||||
...s,
|
||||
overscrollAllowance: parseFloat(e.target.value.toString()) ?? 0.5,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
lockZoom:{" "}
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -270,6 +319,7 @@ const ConstraintsSettings = ({
|
||||
onChange={(e) =>
|
||||
setConstraints((s) => ({ ...s, lockZoom: e.target.checked }))
|
||||
}
|
||||
value={constraints.lockZoom?.toString()}
|
||||
/>
|
||||
{selection.length > 0 && (
|
||||
<button
|
||||
@ -288,6 +338,51 @@ const ConstraintsSettings = ({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{frames.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "0.6rem",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => {
|
||||
const currentIndex = frames.findIndex(
|
||||
(frame) => frame.id === activeFrameId,
|
||||
);
|
||||
|
||||
if (currentIndex === -1) {
|
||||
setActiveFrameId(frames[frames.length - 1].id);
|
||||
} else {
|
||||
const nextIndex =
|
||||
(currentIndex - 1 + frames.length) % frames.length;
|
||||
setActiveFrameId(frames[nextIndex].id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const currentIndex = frames.findIndex(
|
||||
(frame) => frame.id === activeFrameId,
|
||||
);
|
||||
|
||||
if (currentIndex === -1) {
|
||||
setActiveFrameId(frames[0].id);
|
||||
} else {
|
||||
const nextIndex = (currentIndex + 1) % frames.length;
|
||||
setActiveFrameId(frames[nextIndex].id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -909,8 +1004,10 @@ const ExcalidrawWrapper = () => {
|
||||
let storedConstraints = {};
|
||||
if (stored) {
|
||||
try {
|
||||
storedConstraints = JSON.parse(window.atob(stored));
|
||||
} catch {}
|
||||
storedConstraints = decodeConstraints(stored);
|
||||
} catch {
|
||||
console.error("Invalid scroll constraints in URL");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -920,13 +1017,12 @@ const ExcalidrawWrapper = () => {
|
||||
height: document.body.clientHeight,
|
||||
lockZoom: false,
|
||||
viewportZoomFactor: 0.7,
|
||||
overscrollAllowance: 0.5,
|
||||
enabled: true,
|
||||
...storedConstraints,
|
||||
};
|
||||
});
|
||||
|
||||
console.log(constraints);
|
||||
|
||||
// browsers generally prevent infinite self-embedding, there are
|
||||
// cases where it still happens, and while we disallow self-embedding
|
||||
// by not whitelisting our own origin, this serves as an additional guard
|
||||
|
@ -21,7 +21,6 @@ import { getNormalizedZoom } from "./normalize";
|
||||
export const calculateConstrainedScrollCenter = (
|
||||
state: AppState,
|
||||
{ scrollX, scrollY }: Pick<AppState, "scrollX" | "scrollY">,
|
||||
overscrollAllowance?: number,
|
||||
): {
|
||||
scrollX: AppState["scrollX"];
|
||||
scrollY: AppState["scrollY"];
|
||||
@ -59,7 +58,6 @@ export const calculateConstrainedScrollCenter = (
|
||||
height,
|
||||
zoom: _zoom,
|
||||
allowOverscroll: false,
|
||||
overscrollAllowance,
|
||||
});
|
||||
|
||||
return {
|
||||
@ -69,6 +67,8 @@ export const calculateConstrainedScrollCenter = (
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_OVERSCROLL_ALLOWANCE = 0.2;
|
||||
|
||||
interface EncodedConstraints {
|
||||
x: number;
|
||||
y: number;
|
||||
@ -76,6 +76,8 @@ interface EncodedConstraints {
|
||||
h: number;
|
||||
l: boolean;
|
||||
v: number;
|
||||
// overscrollAllowance
|
||||
oa: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,6 +93,7 @@ export const encodeConstraints = (constraints: ScrollConstraints): string => {
|
||||
h: constraints.height,
|
||||
l: !!constraints.lockZoom,
|
||||
v: constraints.viewportZoomFactor ?? 1,
|
||||
oa: constraints.overscrollAllowance ?? DEFAULT_OVERSCROLL_ALLOWANCE,
|
||||
};
|
||||
|
||||
const serialized = JSON.stringify(payload);
|
||||
@ -115,6 +118,7 @@ export const decodeConstraints = (encoded: string): ScrollConstraints => {
|
||||
lockZoom: parsed.l ?? false,
|
||||
viewportZoomFactor: parsed.v ?? 1,
|
||||
animateOnNextUpdate: false,
|
||||
overscrollAllowance: parsed.oa ?? DEFAULT_OVERSCROLL_ALLOWANCE,
|
||||
};
|
||||
} catch (error) {
|
||||
// return safe defaults if decoding fails
|
||||
@ -170,22 +174,21 @@ const calculateConstraints = ({
|
||||
height,
|
||||
zoom,
|
||||
allowOverscroll,
|
||||
overscrollAllowance,
|
||||
}: {
|
||||
scrollConstraints: ScrollConstraints;
|
||||
width: AppState["width"];
|
||||
height: AppState["height"];
|
||||
zoom: AppState["zoom"];
|
||||
allowOverscroll: boolean;
|
||||
overscrollAllowance?: number;
|
||||
}) => {
|
||||
// Validate the overscroll allowance percentage
|
||||
const overscrollAllowance = scrollConstraints.overscrollAllowance;
|
||||
const validatedOverscroll =
|
||||
overscrollAllowance != null &&
|
||||
overscrollAllowance >= 0 &&
|
||||
overscrollAllowance <= 1
|
||||
? overscrollAllowance
|
||||
: 0.2;
|
||||
: DEFAULT_OVERSCROLL_ALLOWANCE;
|
||||
|
||||
/**
|
||||
* Calculates the center position of the constrained scroll area.
|
||||
|
@ -911,6 +911,7 @@ export type ScrollConstraints = {
|
||||
animateOnNextUpdate?: boolean;
|
||||
viewportZoomFactor?: number;
|
||||
lockZoom?: boolean;
|
||||
overscrollAllowance?: number;
|
||||
};
|
||||
export type PendingExcalidrawElements = ExcalidrawElement[];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user