UNPKG

@joist/element

Version:

Intelligently apply styles to WebComponents

83 lines (69 loc) 2.02 kB
import { metadataStore } from "./metadata.js"; export interface AttrOpts { name?: string; observed?: boolean; reflect?: boolean; } export function attr<This extends HTMLElement>(opts?: AttrOpts) { return function attrDecorator( base: ClassAccessorDecoratorTarget<This, unknown>, ctx: ClassAccessorDecoratorContext<This>, ): ClassAccessorDecoratorResult<This, any> { const attrName = opts?.name ?? parseAttrName(ctx.name); const meta = metadataStore.read<This>(ctx.metadata); const reflect = opts?.reflect ?? true; meta.attrs.set(attrName, { propName: ctx.name, observe: opts?.observed ?? true, reflect, access: base, }); return { init(value: unknown) { if (typeof value === "boolean") { return value; } const attrValue = this.getAttribute(attrName); if (attrValue === null) { return value; } if (typeof value === "number") { return Number(attrValue); } return attrValue; }, set(value: unknown) { if (reflect) { if (value === true) { if (!this.hasAttribute(attrName)) { this.setAttribute(attrName, ""); } } else if (value === false) { if (this.hasAttribute(attrName)) { this.removeAttribute(attrName); } } else { const strValue = String(value); if (this.getAttribute(attrName) !== strValue) { this.setAttribute(attrName, strValue); } } } base.set.call(this, value); }, }; }; } function parseAttrName(val: string | symbol): string { let value: string; if (typeof val === "symbol") { if (val.description) { value = val.description; } else { throw new Error("Cannot handle Symbol property without description"); } } else { value = val; } return value.toLowerCase().replaceAll(" ", "-"); }