Files
timetracker/ts/elements/play-event-row.ts
T
lukas 83f727ebe5 fix(game): repair played-row dropdown styling and refresh (#70)
The played-row "Played N times" dropdown regressed when it was migrated
from Alpine to a custom element (commit 1258c52): the hover highlight,
the row-filling click target and a consistent pointer cursor were lost
because the interactive <a>/<button> shrank to its text while the <li>
rows stopped carrying hover/click behaviour. Clicking the row's padding
hit the handler-less <li> and was silently swallowed.

Make each menu item the interactive element itself (block w-full + own
padding + hover highlight + pointer cursor), mirroring the status
selector's _SELECTOR_OPTION_CLASS, so the control fills the whole row.

Also refresh the Play Events section in place: the play-event-row now
dispatches a "play-added" event after recording a play, and
#playevents-container re-fetches itself on it (mirroring the history
section's status-changed refresh), so the table and count badge update
without a full reload.

Add e2e regression tests covering hover highlight, full-row pointer
cursor, the row-wide +1 click target, and the in-place table refresh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:00:42 +02:00

48 lines
1.7 KiB
TypeScript

import { readPlayEventRowProps } from "../generated/props.js";
class PlayEventRowElement extends HTMLElement {
connectedCallback(): void {
const props = readPlayEventRowProps(this);
const toggle = this.querySelector<HTMLElement>("[data-toggle]");
const menu = this.querySelector<HTMLElement>("[data-menu]");
const count = this.querySelector<HTMLElement>("[data-count]");
const addPlay = this.querySelector<HTMLElement>("[data-add-play]");
if (!toggle || !menu) return;
const close = () => {
menu.hidden = true;
};
toggle.addEventListener("click", (event) => {
event.stopPropagation();
menu.hidden = !menu.hidden;
});
document.addEventListener("click", (event) => {
if (!this.contains(event.target as Node)) close();
});
addPlay?.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
if (count) count.textContent = String(Number(count.textContent) + 1);
close();
window
.fetchWithHtmxTriggers(props.apiCreateUrl, {
method: "POST",
headers: { "Content-Type": "application/json", "X-CSRFToken": props.csrf },
body: JSON.stringify({ game_id: props.gameId }),
})
.then(() => {
// Refresh the Play Events section (table + count badge) without a
// full reload; #playevents-container listens for this on body.
document.body.dispatchEvent(new CustomEvent("play-added"));
})
.catch(() => {
if (count) count.textContent = String(Number(count.textContent) - 1);
console.error("Failed to record play");
});
});
}
}
customElements.define("play-event-row", PlayEventRowElement);