folk-canvas/src/folk-set.ts

81 lines
1.9 KiB
TypeScript

import { ClientRectObserverEntry, ClientRectObserverManager } from './common/client-rect-observer.ts';
const clientRectObserver = new ClientRectObserverManager();
declare global {
interface HTMLElementTagNameMap {
'folk-set': FolkSet;
}
}
const defaultRect = DOMRectReadOnly.fromRect();
export class FolkSet extends HTMLElement {
static tagName = 'folk-set';
static define() {
customElements.define(this.tagName, this);
}
#sources = '';
/** A CSS selector for the sources of the arrow. */
get sources() {
return this.#sources;
}
set sources(sources) {
this.#sources = sources;
this.observeSources();
}
#sourcesMap = new Map<Element, DOMRectReadOnly>();
get sourcesMap() {
return this.#sourcesMap;
}
get sourceElements() {
return Array.from(this.#sourcesMap.keys());
}
#sourcesCallback = (entry: ClientRectObserverEntry) => {
this.#sourcesMap.set(entry.target, entry.contentRect);
this.update();
};
connectedCallback() {
this.sources = this.getAttribute('sources') || this.#sources;
}
disconnectedCallback() {
this.unobserveSources();
}
observeSources() {
const sourceElements = new Set(document.querySelectorAll(this.sources));
const currentElements = new Set(this.#sourcesMap.keys());
const elementsToObserve = sourceElements.difference(currentElements);
const elementsToUnobserve = currentElements.difference(sourceElements);
this.unobserveSources(elementsToUnobserve);
for (const el of elementsToObserve) {
this.#sourcesMap.set(el, defaultRect);
clientRectObserver.observe(el, this.#sourcesCallback);
}
this.update();
}
unobserveSources(elements: Iterable<Element> = this.#sourcesMap.keys()) {
for (const el of elements) {
clientRectObserver.unobserve(el, this.#sourcesCallback);
this.#sourcesMap.delete(el);
}
}
update() {}
}