Event log: an event's ID can be selected without toggling it (closes #348)
check / check (push) Waiting to run

An event's row in the event log was a button element, whose text a browser will not let be selected, so an event's ID could not be copied. The row now has the button role instead: focusable, toggled by Enter and Space, with `aria-expanded` giving its state. A click on its text toggles the event after 500 ms, the usual double-click interval; the second press of a double- or triple-click cancels that, so selecting the ID leaves the event as it was. A drag over text does not toggle, and a click on the caret toggles at once. The browser test clicks as a person does, so toggling on the first click fails it.

Model: opus-5-5
This commit was merged in pull request #471.
This commit is contained in:
2026-10-03 02:32:09 +02:00
parent d2ecb83923
commit f1da5e73dd
4 changed files with 308 additions and 28 deletions
+24
View File
@@ -77,12 +77,36 @@ document.addEventListener("alpine:init", function () {
window.Alpine.data("collapsible", function () {
return {
open: false,
// The timer of the toggle a single click is waiting to make.
pendingToggle: null,
init() {
this.open = this.$root.hasAttribute("data-open");
},
toggle() {
this.open = !this.open;
},
// Toggles on a click, except one that selects text, such as
// selecting an event's ID to copy it. A single click toggles
// only after 500 ms, the usual double-click interval, and the
// second press of a double- or triple-click cancels that (see
// cancelPendingToggle), so nothing moves under the pointer
// while it selects text.
toggleUnlessSelecting(event) {
if (
event.detail === 1 &&
window.getSelection().toString() === ""
) {
this.pendingToggle = setTimeout(() => this.toggle(), 500);
}
},
// Runs when the mouse button goes down, so the second press
// of a double- or triple-click cancels the toggle its first
// click is waiting to make, however long that press lasts.
cancelPendingToggle(event) {
if (event.detail > 1) {
clearTimeout(this.pendingToggle);
}
},
get closed() {
return !this.open;
},