@kitten-science/kitten-scientists
Version:
Add-on for the wonderful incremental browser game: https://kittensgame.com/web/
127 lines • 4.47 kB
JavaScript
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