2026-04-01 01:42:10 +07:00

62 lines
2.1 KiB
TypeScript

import { wrapIndex } from "../../shared/keyboard";
import { listen } from "../../shared/helpers";
/** PettyToggleGroupItem — single item within a toggle group. */
export class PettyToggleGroupItem extends HTMLElement {
static observedAttributes = ["value", "disabled"];
get value(): string { return this.getAttribute("value") ?? ""; }
get disabled(): boolean { return this.hasAttribute("disabled"); }
#cleanup = (): void => {};
connectedCallback(): void {
this.setAttribute("role", "button");
this.setAttribute("tabindex", "0");
if (this.disabled) this.setAttribute("aria-disabled", "true");
this.#cleanup = listen(this, [["click", this.#handleClick], ["keydown", this.#handleKeydown]]);
}
disconnectedCallback(): void {
this.#cleanup();
}
attributeChangedCallback(name: string): void {
if (name === "disabled") this.setAttribute("aria-disabled", String(this.disabled));
}
#group(): { toggleValue(v: string): void } | null {
return this.closest("petty-toggle-group") as { toggleValue(v: string): void } | null;
}
#siblings(): PettyToggleGroupItem[] {
const group = this.closest("petty-toggle-group");
if (!group) return [];
return Array.from(group.querySelectorAll<PettyToggleGroupItem>("petty-toggle-group-item:not([disabled])"));
}
#handleClick = (): void => {
if (this.disabled) return;
this.#group()?.toggleValue(this.value);
};
#handleKeydown = (e: Event): void => {
const ke = e as KeyboardEvent;
if (ke.key === " " || ke.key === "Enter") {
ke.preventDefault();
this.#handleClick();
return;
}
const isHorizontal = this.closest("petty-toggle-group")?.getAttribute("orientation") !== "vertical";
const prev = isHorizontal ? "ArrowLeft" : "ArrowUp";
const next = isHorizontal ? "ArrowRight" : "ArrowDown";
if (ke.key !== prev && ke.key !== next) return;
ke.preventDefault();
const items = this.#siblings();
const idx = items.indexOf(this);
if (idx === -1) return;
const delta = ke.key === next ? 1 : -1;
items[wrapIndex(idx, delta, items.length)]?.focus();
};
}