You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
1.8 KiB

2 weeks ago
// @ts-check
/**
* @typedef {Object} AnnotationDescriptor
* @property {string} name - The name of the field to register (e.g. `numberFormat`).
* @property {(element: HTMLElement) => (() => void) | void} onAdd - The function to call when a new element is added to the DOM.
*/
const observer = new MutationObserver(onMutate);
observer.observe(document.body, { childList: true, subtree: true });
/** @type {AnnotationDescriptor[]} */
const descriptors = [];
/** @type {WeakMap<HTMLElement, () => void>} */
const cleanupFunctions = new WeakMap();
/**
* @param {AnnotationDescriptor} descriptor
*/
export function registerElementAnnotatedBy(descriptor) {
descriptors.push(descriptor);
document.querySelectorAll(`[data-${descriptor.name}]`).forEach((element) => {
if (element instanceof HTMLElement) {
handleNewElement(element, descriptor);
}
});
}
/**
* @type {MutationCallback}
*/
function onMutate(mutations) {
const removedNodes = mutations.flatMap((mutation) => Array.from(mutation.removedNodes));
for (const node of removedNodes) {
if (!(node instanceof HTMLElement)) {
continue;
}
const handleRemovedElement = cleanupFunctions.get(node);
if (handleRemovedElement) {
handleRemovedElement();
}
cleanupFunctions.delete(node);
}
const addedNodes = mutations.flatMap((mutation) => Array.from(mutation.addedNodes));
for (const descriptor of descriptors) {
for (const node of addedNodes) {
if (node instanceof HTMLElement && node.hasAttribute(`data-${descriptor.name}`)) {
handleNewElement(node, descriptor);
}
}
}
}
/**
* @param {HTMLElement} element
* @param {AnnotationDescriptor} descriptor
*/
function handleNewElement(element, descriptor) {
const cleanup = descriptor.onAdd(element);
if (cleanup) {
cleanupFunctions.set(element, cleanup);
}
}