Implement missing methods matching CustomElementRegistry

This commit is contained in:
Talia 2025-08-18 11:02:19 +02:00
parent ff0f7a96a7
commit 6d16a74d6e

View file

@ -1,5 +1,7 @@
/** @typedef {Promise & {signal: AbortSignal}} PromiseWithSignal */ /** @typedef {Promise & {signal: AbortSignal}} PromiseWithSignal */
/** @typedef {(element: HTMLElement, detach: PromiseWithSignal) => void} Callback */ /** @typedef {(element: HTMLElement, detached: PromiseWithSignal) => void} Callback */
/** @typedef {new (element: HTMLElement, detached: PromiseWithSignal) => Object} ControllerClass */
/** @typedef {Callback|ControllerClass} Controller */
export class ControllerList { export class ControllerList {
/** @type {HTMLElement} */ /** @type {HTMLElement} */
@ -105,6 +107,11 @@ export class ControllerRegistry {
/** @type {Map<string,Callback>} */ /** @type {Map<string,Callback>} */
#defined = new Map() #defined = new Map()
/** @type {Map<string,Controller>} */
#lookup = new Map()
/** @type {Map<Controller,string>} */
#nameLookup = new Map()
#attribute #attribute
/** @typedef {Document|DocumentFragment|HTMLElement} Root */ /** @typedef {Document|DocumentFragment|HTMLElement} Root */
@ -141,20 +148,25 @@ export class ControllerRegistry {
/** /**
* @param {string} name * @param {string} name
* @param {Callback} callback * @param {Controller} callback
*/ */
define(name, callback) { define(name, callback) {
if (this.#nameLookup.has(callback)) console.warn(`Redefining controller ${this.#nameLookup.get(callback)} under new name ${name}:`, callback)
this.#lookup.set(name, callback)
this.#nameLookup.set(callback, name)
if (("function" == typeof callback) && callback.prototype) { if (("function" == typeof callback) && callback.prototype) {
callback = async (element, disconnected) => { callback = async (element, disconnected) => {
const {proxy, revoke} = Proxy.revocable(element, {}) const {proxy, revoke} = Proxy.revocable(element, {})
const controller = new callback(proxy, disconnected) const controller = new /** @type {ControllerClass} */(callback)(proxy, disconnected)
await disconnected await disconnected
revoke() revoke()
if ("detach" in controller) controller.detach(element) if ("detach" in controller) controller.detach(element)
} }
} }
this.#defined.set(name, callback) this.#defined.set(name, /** @type {Callback} */(callback))
const waitingList = this.#waiting.get(name) const waitingList = this.#waiting.get(name)
@ -162,13 +174,42 @@ export class ControllerRegistry {
this.#attach(element, name) this.#attach(element, name)
} }
this.#waiting.delete(name) this.#waiting.delete(name)
if (this.#whenDefined.has(name)) {
this.#whenDefined.get(name)[1]?.()
}
} }
/** Gets a controller associated with a given name /** Gets a controller associated with a given name
* @param {string} name * @param {string} name
*/ */
get(name) { get(name) {
return this.#defined.get(name) return this.#lookup.get(name)
}
/** Gets the name a controller is registered with
* @param {Controller} controller
*/
getName(controller) {
return this.#nameLookup.get(controller)
}
/** @type {Map<string,[Promise, ()=>void]>} */
#whenDefined = new Map()
/**
* @param {string} name
*/
whenDefined(name) {
if (!this.#whenDefined.has(name)) {
if (this.#defined.has(name)) {
this.#whenDefined.set(name, [Promise.resolve(), undefined])
} else {
let resolve
const promise = new Promise(_resolve => {resolve = _resolve})
this.#whenDefined.set(name, [promise, resolve])
}
}
return this.#whenDefined.get(name)[0]
} }
/** @type {WeakMap<HTMLElement,ControllerList>} */ /** @type {WeakMap<HTMLElement,ControllerList>} */
@ -196,14 +237,6 @@ export class ControllerRegistry {
return [] return []
} }
getName(controller) {
// TODO: Return name of controller
}
whenDefined(name) {
// TODO: Return a promise
}
/** @param {HTMLElement} element */ /** @param {HTMLElement} element */
#update(element) { #update(element) {
const names = this.#getControllerNames(element) const names = this.#getControllerNames(element)
@ -238,7 +271,7 @@ export class ControllerRegistry {
if (!this.#attached.has(element)) this.#attached.set(element, new Map()) if (!this.#attached.has(element)) this.#attached.set(element, new Map())
const attached = this.#attached.get(element) const attached = this.#attached.get(element)
const callback = this.get(name) const callback = this.#defined.get(name)
if (callback) { if (callback) {
if (attached.has("name") && attached.get("name")) return console.warn(`Controller ${name} already fully attached`, element) if (attached.has("name") && attached.get("name")) return console.warn(`Controller ${name} already fully attached`, element)