import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import type { Logger } from 'roarr'; import type { JsonObject } from 'type-fest'; /** * Base class for all canvas modules. */ export abstract class CanvasModuleBase { /** * The type of the module. */ abstract readonly type: string; /** * The unique identifier of the module. * * If the module is associated with an entity, this should be the entity's id. Otherwise, the id should be based on * the module's type. The `getPrefixedId` utility should be used for generating ids. * * @example * ```ts * this.id = getPrefixedId(this.type); * // this.id -> "raster_layer:aS2NREsrlz" * ``` */ abstract readonly id: string; /** * The path of the module in the canvas module tree. * * Modules should use the manager's `buildPath` method to set this value. * * @example * ```ts * this.path = this.manager.buildPath(this); * // this.path -> ["manager:3PWJWmHbou", "raster_layer:aS2NREsrlz", "entity_renderer:sfLO4j1B0n", "brush_line:Zrsu8gpZMd"] * ``` */ abstract readonly path: string[]; /** * The parent module. This may be the canvas manager or another module. */ abstract readonly parent: CanvasModuleBase; /** * The canvas manager. */ abstract readonly manager: CanvasManager; /** * The logger for the module. The logger must be a `ROARR` logger. * * Modules should use the manager's `buildLogger` method to set this value. * * @example * ```ts * this.log = this.manager.buildLogger(this); * ``` */ abstract readonly log: Logger; /** * An optional method that initializes the module. This method is called after all modules have been created. * * Use this method to perform any setup that requires all modules to be created. For example, setting some initial * state or doing an initial render. */ initialize?: () => void = undefined; /** * Returns a logging context object that includes relevant information about the module. * Canvas modules may override this method to include additional information in the logging context, but should * always include the parent's logging context. * * The default implementation includes the parent context and the module's path as a string. * * @example * ```ts * getLoggingContext = () => { * return { * ...this.parent.getLoggingContext(), * path: this.path.join(' > '), * someImportantValue: this.someImportantValue, * }; * }; * ``` */ getLoggingContext: () => JsonObject = () => { return { ...this.parent.getLoggingContext(), path: this.path.join(' > '), }; }; /** * Cleans up the module when it is disposed. * * Canvas modules may override this method to clean up any loose ends. For example: * - Destroy Konva nodes * - Unsubscribe from any subscriptions * - Abort async operations * - Close websockets * - Terminate workers * * This method is called when the module is disposed. For example: * - When an entity is deleted and its module is destroyed * - When the canvas manager is destroyed * * The default implementation only logs a message. * * @example * ```ts * destroy = () => { * this.log('Destroying module'); * this.subscriptions.forEach((unsubscribe) => unsubscribe()); * this.subscriptions.clear(); * this.konva.group.destroy(); * }; * ``` */ destroy: () => void = () => { this.log('Destroying module'); }; /** * Returns a serializable representation of the module. * Canvas modules may override this method to include additional information in the representation. * The default implementation includes id, type, and path. * * @example * ```ts * repr = () => { * return { * id: this.id, * type: this.type, * path: this.path, * state: deepClone(this.state), * }; * }; * ``` */ repr: () => JsonObject = () => { return { id: this.id, type: this.type, path: this.path, }; }; }