UNPKG

@kitten-science/kitten-scientists

Version:

Add-on for the wonderful incremental browser game: https://kittensgame.com/web/

127 lines 4.47 kB
import { mustExist } from "@oliversalzburg/js-utils/data/nil.js"; import { cl } from "../../tools/Log.js"; export class UiComponent { static nextComponentId = 0; componentId; /** * A reference to the host itself. */ host; parent; options; /** * The main DOM element for this component, in a JQuery wrapper. */ _element; set element(value) { this._element = value; this._element[0].id = `KS${Object.getPrototypeOf(this).constructor.name}#${this.componentId}`; } get element() { return mustExist(this._element); } /** * NOTE: It is intentional that all children are of the most fundamental base type. * If a more specifically typed reference for a child is required, it should be stored * on construction. * The 'children' set is intended only for inter-component orchestration. */ children = new Set(); /** * Constructs the base `UiComponent`. * Subclasses MUST add children from options when `this.element` is constructed. * * @param host A reference to the host. * @param options The options for this component. */ constructor(parent, options) { this.componentId = UiComponent.nextComponentId++; this.host = parent.host; this.options = options; this.parent = parent instanceof UiComponent ? parent : null; this._needsRefresh = false; } _needsRefresh; requestRefresh(withChildren = false, depth = 0, trace = false) { if (trace) { console.debug(...cl(depth < 0 ? "⤒".repeat(depth * -1) : " ".repeat(depth), this.toString(), "requestRefresh()")); } this.options?.onRefreshRequest?.call(this); if (this.parent !== this && !withChildren) { this.parent?.requestRefresh(false, depth - 1, trace); } if (this._needsRefresh) { if (trace) { console.debug(...cl(depth < 0 ? "⤒".repeat(depth * -1) : " ".repeat(depth), this.toString(), "requestRefresh() <already pending>")); } } this._needsRefresh = true; if (withChildren) { if (trace) { console.debug(...cl(depth < 0 ? "⤒".repeat(depth * -1) : " ".repeat(depth), this.toString(), "requestRefresh()", `+ ${this.children.size} children`)); } for (const child of this.children) { child.requestRefresh(true, depth + 1, trace); } } else { if (trace) { console.debug(...cl(depth < 0 ? "⤒".repeat(depth * -1) : " ".repeat(depth), this.toString(), "requestRefresh()", "<queued>")); } } } refresh(force = false, depth = 0) { if (!force && !this._needsRefresh) { if (depth === 0) { console.debug(...cl(this.toString(), "refresh() received and ignored.")); } return; } // WARNING: Enable this section only during refresh logic debugging! // When this was implemented, a full refresh logged 16K messages. // Even when these are filtered from the JS console, there are // noticeable performance issues. DO NOT ENABLE THIS UNLESS YOU NEED TO. /* if (!force) { console.debug( ...cl( depth < 0 ? "⤒".repeat(depth * -1) : " ".repeat(depth), this.toString(), "refresh", typeof this.options?.onRefresh !== "undefined" ? "with onRefresh()" : "", ), ); } */ this.options?.onRefresh?.call(this); for (const child of this.children) { child.refresh(force, depth + 1); } this._needsRefresh = false; } addChild(child) { child.parent = this; this.children.add(child); this.element.append(child.element); return this; } addChildren(children) { for (const child of children ?? []) { this.addChild(child); } return this; } removeChild(child) { if (!this.children.has(child)) { return; } child.element.remove(); this.children.delete(child); } removeChildren(children) { for (const child of children) { this.removeChild(child); } } } //# sourceMappingURL=UiComponent.js.map