@github/catalyst
Version: 
Helpers for creating HTML Elements as Controllers
93 lines • 3.21 kB
JavaScript
import { attach } from './controllable.js';
import { dasherize, mustDasherize } from './dasherize.js';
import { createMark } from './mark.js';
const Identity = (v) => v;
let setFromMutation = false;
const attrs = new WeakMap();
const handleMutations = (mutations) => {
    for (const mutation of mutations) {
        if (mutation.type === 'attributes') {
            const name = mutation.attributeName;
            const el = mutation.target;
            const key = attrs.get(el)?.get(name);
            if (key) {
                setFromMutation = true;
                el[key] = el.getAttribute(name);
                setFromMutation = false;
            }
        }
    }
};
const observer = new MutationObserver(handleMutations);
const dirty = new WeakMap();
const [attr, getAttr, initializeAttrs] = createMark(({ name }) => mustDasherize(name, '@attr'), (instance, { name, kind, access }) => {
    if (!dirty.has(instance))
        dirty.set(instance, new Set());
    let cast = Identity;
    let initialValue;
    if (access.get) {
        initialValue = access.get.call(instance);
    }
    else if ('value' in access && kind !== 'method') {
        initialValue = access.value;
    }
    let value = initialValue;
    const attributeName = dasherize(name);
    const setCallback = (kind === 'method' ? access.value : access.set) || Identity;
    const getCallback = access.get || (() => value);
    if (!attrs.get(instance))
        attrs.set(instance, new Map());
    attrs.get(instance).set(attributeName, name);
    if (typeof value === 'number') {
        cast = Number;
    }
    else if (typeof value === 'boolean') {
        cast = Boolean;
    }
    else if (typeof value === 'string') {
        cast = String;
    }
    return {
        get() {
            const has = instance.hasAttribute(attributeName);
            if (has) {
                return cast === Boolean ? has : cast(instance.getAttribute(attributeName));
            }
            return cast(getCallback.call(instance));
        },
        set(newValue) {
            const isInitial = newValue === null;
            if (isInitial)
                newValue = initialValue;
            const oldValue = value;
            value = newValue;
            setCallback.call(instance, value);
            if (setFromMutation || isInitial)
                return;
            dirty.get(instance)?.add(name);
            instance.requestUpdate(name, oldValue);
        }
    };
});
export class AttrableController {
    constructor(host) {
        this.host = host;
        this.host.addController(this);
        initializeAttrs(this.host);
        observer.observe(this.host, { attributeFilter: Array.from(getAttr(this.host)).map(dasherize) });
    }
    hostUpdate() {
        for (const name of dirty.get(this.host) || []) {
            const value = this.host[name];
            if (typeof value === 'boolean') {
                this.host.toggleAttribute(dasherize(name), value);
            }
            else {
                this.host.setAttribute(dasherize(name), String(value));
            }
        }
    }
}
export { attr, getAttr };
export const attrable = attach(AttrableController);
//# sourceMappingURL=attrable.js.map