schema: remove frame bg guard flag from runtime

This commit is contained in:
Ryan Di
2026-03-20 01:06:00 +11:00
parent 5bb3046dea
commit 6d3eb16531
11 changed files with 23 additions and 107 deletions
+1 -4
View File
@@ -10,7 +10,6 @@ import {
getFontString,
getUpdatedTimestamp,
getLineHeight,
isTransparent,
} from "@excalidraw/common";
import type { Radians } from "@excalidraw/math";
@@ -186,13 +185,11 @@ export const newFrameElement = (
name?: string;
} & ElementConstructorOpts,
): NonDeleted<ExcalidrawFrameElement> => {
const frameBase = _newElementBase<ExcalidrawFrameElement>("frame", opts);
const frameElement = newElementWith(
{
...frameBase,
..._newElementBase<ExcalidrawFrameElement>("frame", opts),
type: "frame",
name: opts?.name || null,
backgroundEnabled: !isTransparent(frameBase.backgroundColor),
},
{},
);
+1 -1
View File
@@ -787,7 +787,7 @@ export const renderFrameBackground = (
roundCorners?: boolean;
},
) => {
if (!frame.backgroundEnabled || isTransparent(frame.backgroundColor)) {
if (isTransparent(frame.backgroundColor)) {
return;
}
-1
View File
@@ -163,7 +163,6 @@ export type InitializedExcalidrawImageElement = MarkNonNullable<
export type ExcalidrawFrameElement = _ExcalidrawElementBase & {
type: "frame";
name: string | null;
backgroundEnabled: boolean;
};
export type ExcalidrawMagicFrameElement = _ExcalidrawElementBase & {
@@ -420,7 +420,6 @@ export const actionChangeBackgroundColor = register<
if (isFrameElement(el)) {
return newElementWith(el, {
backgroundColor: value.currentItemBackgroundColor,
backgroundEnabled: !isTransparent(value.currentItemBackgroundColor),
});
}
return hasBackground(el.type)
-8
View File
@@ -509,16 +509,8 @@ export const restoreElement = (
name: element.name ?? null,
});
case "frame":
const backgroundEnabled =
typeof (element as any).backgroundEnabled === "boolean"
? (element as any).backgroundEnabled
: false;
return restoreElementWithProperties(element, {
name: element.name ?? null,
backgroundEnabled,
...(!backgroundEnabled
? { backgroundColor: DEFAULT_ELEMENT_PROPS.backgroundColor }
: {}),
});
// Don't use default case so as to catch a missing an element type case.
+7 -6
View File
@@ -13,12 +13,14 @@ describe("schema migration", () => {
type: "frame",
backgroundColor: "#ffc9c9",
});
frame.backgroundEnabled = true;
(frame as any).backgroundEnabled = true;
const migrated = migrateElementsBySchema([frame], SCHEMA_VERSIONS.initial)!;
expect(migrated[0].backgroundColor).toBe(DEFAULT_ELEMENT_PROPS.backgroundColor);
expect((migrated[0] as any).backgroundEnabled).toBe(false);
expect(migrated[0].backgroundColor).toBe(
DEFAULT_ELEMENT_PROPS.backgroundColor,
);
expect((migrated[0] as any).backgroundEnabled).toBeUndefined();
});
it("should keep latest-schema frame backgrounds unchanged", () => {
@@ -26,7 +28,7 @@ describe("schema migration", () => {
type: "frame",
backgroundColor: "#ffc9c9",
});
frame.backgroundEnabled = true;
(frame as any).backgroundEnabled = true;
const migrated = migrateElementsBySchema(
[frame],
@@ -34,7 +36,7 @@ describe("schema migration", () => {
)!;
expect(migrated[0].backgroundColor).toBe("#ffc9c9");
expect((migrated[0] as any).backgroundEnabled).toBe(true);
expect((migrated[0] as any).backgroundEnabled).toBeUndefined();
});
it("should resolve invalid schema versions using fallback", () => {
@@ -47,4 +49,3 @@ describe("schema migration", () => {
expect(resolveSchemaVersion(2, SCHEMA_VERSIONS.initial)).toBe(2);
});
});
+11 -3
View File
@@ -25,7 +25,7 @@ export const migrateElementsBySchema = (
elements: readonly ExcalidrawElement[] | null | undefined,
schemaVersion: number,
) => {
if (!elements || schemaVersion >= SCHEMA_VERSIONS.frameBackgrounds) {
if (!elements) {
return elements;
}
@@ -34,10 +34,18 @@ export const migrateElementsBySchema = (
return element;
}
const { backgroundEnabled: _, ...frameWithoutBackgroundEnabled } =
element as ExcalidrawElement & {
backgroundEnabled?: boolean;
};
if (schemaVersion >= SCHEMA_VERSIONS.frameBackgrounds) {
return frameWithoutBackgroundEnabled;
}
return {
...element,
...frameWithoutBackgroundEnabled,
backgroundColor: DEFAULT_ELEMENT_PROPS.backgroundColor,
backgroundEnabled: false,
} as ExcalidrawElement;
});
};
@@ -336,9 +336,6 @@ const _renderStaticScene = ({
if (!isFrameElement(frame) || renderedFrameBackgrounds.has(frame.id)) {
return;
}
if (!frame.backgroundEnabled) {
return;
}
frameBackgroundByElementId.set(element.id, frame);
renderedFrameBackgrounds.add(frame.id);
@@ -731,7 +731,6 @@ export const renderSceneToSvg = (
const renderFrameBackgroundNode = (frame: ExcalidrawFrameElement) => {
if (
!frame ||
!frame.backgroundEnabled ||
!frame.backgroundColor ||
isTransparent(frame.backgroundColor)
) {
@@ -794,7 +793,6 @@ export const renderSceneToSvg = (
if (
!isFrameElement(frame) ||
renderedFrameBackgrounds.has(frame.id) ||
!frame.backgroundEnabled ||
!frame.backgroundColor ||
isTransparent(frame.backgroundColor)
) {
+3 -57
View File
@@ -11,7 +11,6 @@ import type { LocalPoint } from "@excalidraw/math";
import type {
ExcalidrawArrowElement,
ExcalidrawElement,
ExcalidrawFrameElement,
ExcalidrawFreeDrawElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
@@ -82,72 +81,19 @@ describe("restoreElements", () => {
).toEqual([expect.objectContaining({ isDeleted: true })]);
});
it("should disable frame background for legacy frames missing backgroundEnabled", () => {
it("should restore frame element", () => {
const frame = API.createElement({
type: "frame",
backgroundColor: "#ffc9c9",
});
const legacyFrame = { ...frame } as any;
delete legacyFrame.backgroundEnabled;
const restoredFrame = restore.restoreElements(
[legacyFrame],
null,
)[0] as ExcalidrawFrameElement;
expect(restoredFrame.backgroundColor).toBe("transparent");
expect(restoredFrame.backgroundEnabled).toBe(false);
});
it("should disable frame background when legacy backgroundEnabled is non-boolean", () => {
const frame = API.createElement({
type: "frame",
backgroundColor: "#ffc9c9",
});
const legacyFrame = {
...frame,
backgroundEnabled: undefined,
} as any;
const restoredFrame = restore.restoreElements(
[legacyFrame],
null,
)[0] as ExcalidrawFrameElement;
expect(restoredFrame.backgroundColor).toBe("transparent");
expect(restoredFrame.backgroundEnabled).toBe(false);
});
it("should normalize frame background color when backgroundEnabled is false", () => {
const frame = API.createElement({
type: "frame",
backgroundColor: "#ffc9c9",
});
frame.backgroundEnabled = false;
const restoredFrame = restore.restoreElements(
[frame],
null,
)[0] as ExcalidrawFrameElement;
expect(restoredFrame.backgroundColor).toBe("transparent");
expect(restoredFrame.backgroundEnabled).toBe(false);
});
it("should preserve frame backgroundEnabled when present", () => {
const frame = API.createElement({
type: "frame",
backgroundColor: "#ffc9c9",
});
frame.backgroundEnabled = true;
const restoredFrame = restore.restoreElements(
[frame],
null,
)[0] as ExcalidrawFrameElement;
)[0] as ExcalidrawElement;
expect(restoredFrame.type).toBe("frame");
expect(restoredFrame.backgroundColor).toBe("#ffc9c9");
expect(restoredFrame.backgroundEnabled).toBe(true);
});
it("should restore text element correctly passing value for each attribute", () => {
@@ -364,27 +364,6 @@ describe("exporting frames", () => {
expect(emptyBgSvg.querySelector('rect[fill=""]')).toBeNull();
});
it("should not render SVG frame background when background is disabled", async () => {
const frame = API.createElement({
type: "frame",
width: 100,
height: 100,
x: 0,
y: 0,
backgroundColor: "#ffc9c9",
});
frame.backgroundEnabled = false;
const svg = await exportToSvg({
elements: [frame],
files: null,
exportPadding: 0,
exportingFrame: frame,
});
expect(svg.querySelector('rect[fill="#ffc9c9"]')).toBeNull();
});
it("should filter non-overlapping elements when exporting a frame", async () => {
const frame = API.createElement({
type: "frame",