import { signal } from "../../signals"; import { emit, listen, part, wireLabel } from "../../shared/helpers"; /** PettyCheckbox — tri-state checkbox with label wiring and change events. */ export class PettyCheckbox extends HTMLElement { static observedAttributes = ["checked", "indeterminate", "disabled", "name", "value"]; readonly #checked = signal(false); readonly #indeterminate = signal(false); #cleanup = (): void => {}; get checked(): boolean { return this.#checked.get(); } set checked(v: boolean) { this.#checked.set(v); this.#sync(); } get indeterminate(): boolean { return this.#indeterminate.get(); } set indeterminate(v: boolean) { this.#indeterminate.set(v); this.#sync(); } connectedCallback(): void { const input = this.#input(); if (!input) return; if (this.hasAttribute("checked")) this.#checked.set(true); if (this.hasAttribute("indeterminate")) this.#indeterminate.set(true); wireLabel(input, part(this, "label"), "petty-cb"); this.#sync(); this.#cleanup = listen(input, [["change", this.#handleChange]]); } disconnectedCallback(): void { this.#cleanup(); } attributeChangedCallback(name: string, _old: string | null, next: string | null): void { if (name === "checked") this.#checked.set(next !== null); if (name === "indeterminate") this.#indeterminate.set(next !== null); this.#sync(); } #input(): HTMLInputElement | null { return part(this, "control"); } #sync(): void { const input = this.#input(); if (!input) return; input.checked = this.#checked.get(); input.indeterminate = this.#indeterminate.get(); input.disabled = this.hasAttribute("disabled"); if (this.hasAttribute("name")) input.name = this.getAttribute("name") ?? ""; if (this.hasAttribute("value")) input.value = this.getAttribute("value") ?? "on"; const state = this.#indeterminate.get() ? "indeterminate" : this.#checked.get() ? "checked" : "unchecked"; this.dataset.state = state; } #handleChange = (): void => { const input = this.#input(); if (!input) return; this.#checked.set(input.checked); this.#indeterminate.set(false); this.#sync(); emit(this, "change", { checked: input.checked, indeterminate: false }); }; }