yukey

press-flash.ts

// Activation feedback, shared across widgets: the "sw-pressed" flash class that
// acknowledges a successful activation for --sw-press-ms, so keyboard triggers
// (Enter/Space, hotkeys) are as visible as a mouse press.  The duration is one
// system-wide design token, read from the live cascade at flash time -- so an
// accessibility mode can slow every flash by restyling one custom property.
//
// Consumers and their class carriers:
//   - sw-button flashes its host element (and manages its own pointer-held
//     "sw-held" class beside it); its styles render both.
//   - Megacolumn hosts with plain activatable rows call flashPress(rowElement)
//     when they act on a forwarded activation; the row-flavored .sw-pressed
//     rendering lives in megacolumnStyles, beside the focus washes it must outrank.

export const PRESSED_CLASS = "sw-pressed";

// The flash duration, resolved from the cascade at el.  Throws when the property
// does not resolve (tokens not installed, or a detached element): a feedback
// system that silently picks its own duration would hide exactly the
// misconfiguration it exists to surface.
export function pressMs(el: Element): number {
	const raw = getComputedStyle(el).getPropertyValue("--sw-press-ms").trim();
	const m = /^(\d+(?:\.\d+)?)(m?s)$/.exec(raw);
	if (m === null) {
		throw new Error(
			`press-flash: --sw-press-ms did not resolve on <${el.tagName.toLowerCase()}> `
				+ `(got "${raw}"); are the design tokens installed?`,
		);
	}
	const n = Number(m[1]);
	return m[2] === "s" ? n * 1000 : n;
}

const timers = new WeakMap<HTMLElement, number>();

// Show the pressed look on el for --sw-press-ms.  A re-flash while one is still
// showing re-arms the timer, so rapid activations read as sustained activity
// instead of getting cut short by the first flash's expiry.
export function flashPress(el: HTMLElement): void {
	el.classList.add(PRESSED_CLASS);
	window.clearTimeout(timers.get(el));
	timers.set(
		el,
		window.setTimeout(() => {
			el.classList.remove(PRESSED_CLASS);
			timers.delete(el);
		}, pressMs(el)),
	);
}

packages/yukey/src/press-flash.ts · 2.0 KiB · f2166ce42f