@studiometa/js-toolkit
Version:
A set of useful little bits of JavaScript to boost your project! 🚀
164 lines (163 loc) • 4.82 kB
JavaScript
import { AbstractManager } from "./AbstractManager.js";
import { getComponentElements, addToQueue } from "../utils.js";
class ChildrenManager extends AbstractManager {
/**
* Store async component promises to avoid calling them multiple times and
* waiting for them when they are already resolved.
*/
__asyncComponentPromises = /* @__PURE__ */ new WeakMap();
get registeredNames() {
return Object.keys(this.props);
}
/**
* Register instances of all children components.
*/
async registerAll() {
for (const [name, component] of Object.entries(this.__config.components)) {
this.__register(name, component);
}
}
/**
* Mount all child component instances.
*/
async mountAll() {
await this.__triggerHookForAll("$mount");
}
/**
* Update all child component instances.
*/
async updateAll() {
await this.__triggerHookForAll("$update");
}
/**
* Destroy all child component instances.
*/
async destroyAll() {
await this.__triggerHookForAll("$destroy");
}
/**
* Execute the given hook for the given instance.
*
* @param {'$mount'|'$update'|'$destroy'} hook The hook to trigger.
* @param {Base} instance The target instance.
* @param {string} name The name of the component.
* @private
*/
async __triggerHook(hook, instance, name) {
if (hook === "$update" && !instance.$isMounted) {
hook = "$mount";
}
if (hook === "$update" || hook === "$destroy") {
this.__eventsManager.unbindChild(name, instance);
}
if (hook === "$update" || hook === "$mount") {
this.__eventsManager.bindChild(name, instance);
}
await instance[hook]();
}
/**
* Register instance of a child component.
*
* @param {string} name
* The name of the child component.
* @param {BaseConstructor|BaseAsyncConstructor} component
* A Base class or a Promise for async components.
* @private
*/
__register(name, component) {
Object.defineProperty(this.props, name, {
enumerable: true,
configurable: true,
get: () => this.__getValue(name, component)
});
}
/**
* Get children.
* @private
*/
__getValue(name, component) {
const elements = getComponentElements(name, this.__element);
if (elements.length === 0) {
return [];
}
const children = [];
for (const element of elements) {
const child = this.__getChild(element, component);
if (child !== "terminated") {
children.push(child);
}
}
return children;
}
/**
* Get a child component's instance.
*
* @param {BaseEl} el
* The root element of the child component.
* @param {BaseConstructor|BaseAsyncConstructor} ComponentClass
* A Base class or a Promise for async components.
* @return {Base|Promise<Base | 'terminated'>|'terminated'}
* A Base instance or a Promise resolving to a Base instance.
* @private
*/
__getChild(el, ComponentClass) {
const asyncComponentPromise = this.__asyncComponentPromises.get(
ComponentClass
);
if ("$isBase" in ComponentClass || asyncComponentPromise && asyncComponentPromise.status === "resolved") {
let ctor = ComponentClass;
if (asyncComponentPromise) {
ctor = asyncComponentPromise.ctor;
}
if (el.__base__ && el.__base__.has(ctor.config.name)) {
return el.__base__.get(ctor.config.name);
}
const child = new ctor(el);
return child;
}
const promise = asyncComponentPromise ? asyncComponentPromise.promise : ComponentClass(this.__base);
if (!asyncComponentPromise) {
this.__asyncComponentPromises.set(ComponentClass, {
promise,
status: "pending",
ctor: void 0
});
}
return promise.then((module) => {
const ctor = module.default ?? module;
this.__asyncComponentPromises.set(ComponentClass, {
promise,
status: "resolved",
ctor
});
return this.__getChild(el, ctor);
});
}
/**
* Execute the given hook for all children instances.
*
* @param {'$mount'|'$update'|'$destroy'} hook The hook to execute.
* @private
*/
async __triggerHookForAll(hook) {
return addToQueue(() => {
const promises = [];
for (const name of this.registeredNames) {
for (const instance of this.props[name]) {
if (instance instanceof Promise) {
instance.then(
(resolvedInstance) => addToQueue(() => this.__triggerHook(hook, resolvedInstance, name))
);
} else {
promises.push(addToQueue(() => this.__triggerHook(hook, instance, name)));
}
}
}
return Promise.all(promises);
});
}
}
export {
ChildrenManager
};
//# sourceMappingURL=ChildrenManager.js.map