mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
remove shared global scene and attach it to every instance (#1706)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
54f8d8f820
commit
20500b7822
9 changed files with 219 additions and 184 deletions
121
src/scene/Scene.ts
Normal file
121
src/scene/Scene.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import {
|
||||
ExcalidrawElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
NonDeleted,
|
||||
} from "../element/types";
|
||||
import { getNonDeletedElements, isNonDeletedElement } from "../element";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
|
||||
type ElementIdKey = InstanceType<typeof LinearElementEditor>["elementId"];
|
||||
type ElementKey = ExcalidrawElement | ElementIdKey;
|
||||
|
||||
type SceneStateCallback = () => void;
|
||||
type SceneStateCallbackRemover = () => void;
|
||||
|
||||
const isIdKey = (elementKey: ElementKey): elementKey is ElementIdKey => {
|
||||
if (typeof elementKey === "string") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
class Scene {
|
||||
// ---------------------------------------------------------------------------
|
||||
// static methods/props
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private static sceneMapByElement = new WeakMap<ExcalidrawElement, Scene>();
|
||||
private static sceneMapById = new Map<string, Scene>();
|
||||
|
||||
static mapElementToScene(elementKey: ElementKey, scene: Scene) {
|
||||
if (isIdKey(elementKey)) {
|
||||
this.sceneMapById.set(elementKey, scene);
|
||||
} else {
|
||||
this.sceneMapByElement.set(elementKey, scene);
|
||||
}
|
||||
}
|
||||
|
||||
static getScene(elementKey: ElementKey): Scene | null {
|
||||
if (isIdKey(elementKey)) {
|
||||
return this.sceneMapById.get(elementKey) || null;
|
||||
}
|
||||
return this.sceneMapByElement.get(elementKey) || null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// instance methods/props
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private callbacks: Set<SceneStateCallback> = new Set();
|
||||
|
||||
private nonDeletedElements: readonly NonDeletedExcalidrawElement[] = [];
|
||||
private elements: readonly ExcalidrawElement[] = [];
|
||||
private elementsMap = new Map<ExcalidrawElement["id"], ExcalidrawElement>();
|
||||
|
||||
getElementsIncludingDeleted() {
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
getElements(): readonly NonDeletedExcalidrawElement[] {
|
||||
return this.nonDeletedElements;
|
||||
}
|
||||
|
||||
getElement(id: ExcalidrawElement["id"]): ExcalidrawElement | null {
|
||||
return this.elementsMap.get(id) || null;
|
||||
}
|
||||
|
||||
getNonDeletedElement(
|
||||
id: ExcalidrawElement["id"],
|
||||
): NonDeleted<ExcalidrawElement> | null {
|
||||
const element = this.getElement(id);
|
||||
if (element && isNonDeletedElement(element)) {
|
||||
return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
replaceAllElements(nextElements: readonly ExcalidrawElement[]) {
|
||||
this.elements = nextElements;
|
||||
this.elementsMap.clear();
|
||||
nextElements.forEach((element) => {
|
||||
this.elementsMap.set(element.id, element);
|
||||
Scene.mapElementToScene(element, this);
|
||||
});
|
||||
this.nonDeletedElements = getNonDeletedElements(this.elements);
|
||||
this.informMutation();
|
||||
}
|
||||
|
||||
informMutation() {
|
||||
for (const callback of Array.from(this.callbacks)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
addCallback(cb: SceneStateCallback): SceneStateCallbackRemover {
|
||||
if (this.callbacks.has(cb)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
this.callbacks.add(cb);
|
||||
|
||||
return () => {
|
||||
if (!this.callbacks.has(cb)) {
|
||||
throw new Error();
|
||||
}
|
||||
this.callbacks.delete(cb);
|
||||
};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
Scene.sceneMapById.forEach((scene, elementKey) => {
|
||||
if (scene === this) {
|
||||
Scene.sceneMapById.delete(elementKey);
|
||||
}
|
||||
});
|
||||
// done not for memory leaks, but to guard against possible late fires
|
||||
// (I guess?)
|
||||
this.callbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default Scene;
|
|
@ -1,80 +0,0 @@
|
|||
import {
|
||||
ExcalidrawElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
NonDeleted,
|
||||
} from "../element/types";
|
||||
import {
|
||||
getNonDeletedElements,
|
||||
isNonDeletedElement,
|
||||
getElementMap,
|
||||
} from "../element";
|
||||
|
||||
export interface SceneStateCallback {
|
||||
(): void;
|
||||
}
|
||||
|
||||
export interface SceneStateCallbackRemover {
|
||||
(): void;
|
||||
}
|
||||
|
||||
class GlobalScene {
|
||||
private callbacks: Set<SceneStateCallback> = new Set();
|
||||
|
||||
private nonDeletedElements: readonly NonDeletedExcalidrawElement[] = [];
|
||||
private elements: readonly ExcalidrawElement[] = [];
|
||||
private elementsMap: {
|
||||
[id: string]: ExcalidrawElement;
|
||||
} = {};
|
||||
|
||||
getElementsIncludingDeleted() {
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
getElements(): readonly NonDeletedExcalidrawElement[] {
|
||||
return this.nonDeletedElements;
|
||||
}
|
||||
|
||||
getElement(id: ExcalidrawElement["id"]): ExcalidrawElement | null {
|
||||
return this.elementsMap[id] || null;
|
||||
}
|
||||
|
||||
getNonDeletedElement(
|
||||
id: ExcalidrawElement["id"],
|
||||
): NonDeleted<ExcalidrawElement> | null {
|
||||
const element = this.getElement(id);
|
||||
if (element && isNonDeletedElement(element)) {
|
||||
return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
replaceAllElements(nextElements: readonly ExcalidrawElement[]) {
|
||||
this.elements = nextElements;
|
||||
this.elementsMap = getElementMap(nextElements);
|
||||
this.nonDeletedElements = getNonDeletedElements(this.elements);
|
||||
this.informMutation();
|
||||
}
|
||||
|
||||
informMutation() {
|
||||
for (const callback of Array.from(this.callbacks)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
addCallback(cb: SceneStateCallback): SceneStateCallbackRemover {
|
||||
if (this.callbacks.has(cb)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
this.callbacks.add(cb);
|
||||
|
||||
return () => {
|
||||
if (!this.callbacks.has(cb)) {
|
||||
throw new Error();
|
||||
}
|
||||
this.callbacks.delete(cb);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const globalSceneState = new GlobalScene();
|
|
@ -15,4 +15,3 @@ export {
|
|||
hasText,
|
||||
} from "./comparisons";
|
||||
export { getZoomOrigin, getNormalizedZoom } from "./zoom";
|
||||
export { globalSceneState } from "./globalScene";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue