62 lines
2.1 KiB
TypeScript
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();
|
|
};
|
|
}
|