rspace-online/lib/shape-registry.ts

68 lines
1.9 KiB
TypeScript

/**
* Dynamic shape registry — replaces the 300-line switch in canvas.html
* and the 165-line if-chain in community-sync.ts.
*
* Each shape class registers itself with `fromData()` and `applyData()`,
* so creation and sync are fully data-driven.
*/
import type { FolkShape } from "./folk-shape";
import type { ShapeData } from "./community-sync";
export interface ShapeRegistration {
tagName: string;
/** The custom element class (must have fromData/applyData) */
elementClass: typeof HTMLElement & {
fromData?(data: ShapeData): HTMLElement;
};
}
class ShapeRegistry {
#registrations = new Map<string, ShapeRegistration>();
/** Register a shape type. */
register(tagName: string, elementClass: ShapeRegistration["elementClass"]): void {
this.#registrations.set(tagName, { tagName, elementClass });
}
/** Get registration for a tag name. */
getRegistration(tagName: string): ShapeRegistration | undefined {
return this.#registrations.get(tagName);
}
/** Create a new element from ShapeData using the class's static fromData(). */
createElement(data: ShapeData): HTMLElement | null {
const reg = this.#registrations.get(data.type);
if (!reg) return null;
if (typeof reg.elementClass.fromData === "function") {
return reg.elementClass.fromData(data);
}
// Fallback: basic createElement
const el = document.createElement(data.type);
el.id = data.id;
return el;
}
/** Update an existing element using its instance applyData(). */
updateElement(shape: any, data: ShapeData): void {
if (typeof shape.applyData === "function") {
shape.applyData(data);
}
}
/** List all registered tag names. */
listAll(): string[] {
return Array.from(this.#registrations.keys());
}
/** Check if a type is registered. */
has(tagName: string): boolean {
return this.#registrations.has(tagName);
}
}
/** Singleton registry instance. */
export const shapeRegistry = new ShapeRegistry();