UNPKG

@github/catalyst

Version:

Helpers for creating HTML Elements as Controllers

93 lines 3.21 kB
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