/* * recenter.ts — the follow/recenter control (UI-SPEC §Follow / Recenter Control). * --------------------------------------------------------------------------------- * One 40x40 floating square plate, bottom-right, machined microswitch: * inline SVG crosshair (2px uniform stroke, square linecaps, orthogonal * geometry — self-hosted inline vector, no Material Symbols CDN per D-14) * plus the 8px center notch. Three instant notch states via data attributes * (colors come from shell.css; swaps are instantaneous, zero easing): * following + fresh → solid signal-orange notch (locked-on register) * free → notch removed (user panned away) * following + lost → soft-beige notch (unlocked, NOT red — red is * reserved for the strip/degraded registers) * Press feedback is the CSS :active invert — instant. The click handler is * an addEventListener wire, never an inline onclick attribute (D-01). */ import { strings, type Locale } from "../i18n/strings.ts"; export function createRecenterControl( host: HTMLElement, locale: Locale, onRecenter: () => void, ): { setMode(mode: "following" | "free"): void; setFixState(state: "fresh" | "lost"): void; setLocale(locale: Locale): void; } { const button = document.createElement("button"); button.type = "button"; button.className = "recenter"; button.dataset.follow = "following"; button.dataset.fix = "fresh"; button.setAttribute("aria-label", strings[locale].ariaRecenter); myLocationGlyph(button); button.addEventListener("click", () => onRecenter()); host.appendChild(button); return { setMode(mode: "following" | "free"): void { button.dataset.follow = mode; }, setFixState(state: "fresh" | "lost"): void { button.dataset.fix = state; }, setLocale(next: Locale): void { button.setAttribute("aria-label", strings[next].ariaRecenter); }, }; } /** my_location silhouette redrawn as an orthogonal glyph: outer square * frame + four orthogonal ticks + a filled 4x4 center dot. stroke-linecap * square, weight 2 — uniform with the marker geometry. currentColor keeps * the plate/glyph pairing theme-driven via CSS alone. */ function myLocationGlyph(button: HTMLButtonElement): void { const svgNs = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(svgNs, "svg"); svg.setAttribute("width", "20"); svg.setAttribute("height", "20"); svg.setAttribute("viewBox", "-12 -12 24 24"); svg.setAttribute("aria-hidden", "true"); const attributes = (el: SVGElement, map: Record): void => { for (const [k, v] of Object.entries(map)) el.setAttribute(k, v); }; const frame = document.createElementNS(svgNs, "rect"); attributes(frame, { x: "-8", y: "-8", width: "16", height: "16", fill: "none", stroke: "currentColor", "stroke-width": "2" }); const tick = (x1: number, y1: number, x2: number, y2: number): SVGElement => { const line = document.createElementNS(svgNs, "line"); attributes(line, { x1: String(x1), y1: String(y1), x2: String(x2), y2: String(y2), stroke: "currentColor", "stroke-width": "2" }); return line; }; const dot = document.createElementNS(svgNs, "rect"); attributes(dot, { x: "-2", y: "-2", width: "4", height: "4", fill: "currentColor" }); svg.append(frame, tick(0, -12, 0, -8), tick(0, 8, 0, 12), tick(-12, 0, -8, 0), tick(8, 0, 12, 0), dot); const notch = document.createElement("span"); notch.className = "notch"; button.append(svg, notch); }