Make list-rows.js answer both the old and new row hooks

Missed in the card rewrite: <x-card> now renders data-md-list-row,
data-md-list-open and data-md-list-actionable, but list-rows.js still
queried only the unprefixed data-list-row/-open/-actionable, so a
card's row press and its actionable Enter/Space stopped working.
<x-list-item>, still unrewritten until the next commit, keeps
rendering the old names, so the script answers both for now (the same
transition interaction.css already makes for data-md-dragged), and the
fallback comes out once list-item moves too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 20:31:21 +02:00
co-authored by Claude Sonnet 5
parent f04e31b3aa
commit 5cb11cd127
+24 -13
View File
@@ -1,26 +1,37 @@
/** /**
* A row opens from anywhere on it. * A row opens from anywhere on it.
* *
* A row is `data-list-row` — an `<x-list-item>`, an `<x-card>`, a table row — and exactly one * A row is `data-md-list-row` (or the older, unprefixed `data-list-row`, while a component that
* control inside it is its opener, `data-list-open`: the title button that opens a sheet, or the * has not yet moved off it still renders it) — an `<x-list-item>`, an `<x-card>`, a table row —
* link that goes to the item's page. A click anywhere else on the row is handed to the opener, so * and exactly one control inside it is its opener, `data-md-list-open` (or `data-list-open`): the
* the whole row is the target while the opener stays a real control — the tab stop, the name a * title button that opens a sheet, or the link that goes to the item's page. A click anywhere else
* screen reader reads, and the owner of the `wire:click` or the `href`. The row's other controls * on the row is handed to the opener, so the whole row is the target while the opener stays a real
* keep their own clicks. How a row looks while that happens is resources/css/components/list.css. * control — the tab stop, the name a screen reader reads, and the owner of the `wire:click` or the
* `href`. The row's other controls keep their own clicks. How a row looks while that happens is
* resources/css/components/card.css and list.css.
* *
* Not a stretched link (`::after { inset: 0 }`): Safari makes no containing block of a <tr>, so in * Not a stretched link (`::after { inset: 0 }`): Safari makes no containing block of a <tr>, so in
* a table every overlay would cover the whole table; and not one <button> around the row, which * a table every overlay would cover the whole table; and not one <button> around the row, which
* could hold no other buttons. The listeners sit on `document` and are added once. * could hold no other buttons. The listeners sit on `document` and are added once.
* *
* A row that is also `data-list-actionable` is M3's **directly actionable card** (`<x-card * A row that is also `data-md-list-actionable` (or `data-list-actionable`) is M3's **directly
* actionable>`): the card is the one tab stop, so Enter and Space on the card reach the opener, * actionable card** (`<x-card actionable>`): the card is the one tab stop, so Enter and Space on
* and the card's other actions follow it in the tab order with their own keys. The opener itself * the card reach the opener, and the card's other actions follow it in the tab order with their
* carries `tabindex="-1"`, which the card's caller writes. * own keys. The opener itself carries `tabindex="-1"`, which the card's caller writes.
*/ */
/** Anything that answers a click itself — the row's own controls, and the opener. */ /** Anything that answers a click itself — the row's own controls, and the opener. */
const CONTROLS = 'a, button, input, select, textarea, label, summary, [contenteditable]' const CONTROLS = 'a, button, input, select, textarea, label, summary, [contenteditable]'
/** A row, under either its current or its older hook. */
const ROW = '[data-md-list-row], [data-list-row]'
/** A row's opener, under either its current or its older hook. */
const OPENER = '[data-md-list-open], [data-list-open]'
/** A directly actionable row, under either its current or its older hook. */
const ACTIONABLE = '[data-md-list-actionable], [data-list-actionable]'
/** How far a pointer may travel between press and release and still be a click. */ /** How far a pointer may travel between press and release and still be a click. */
const DRAG_PX = 6 const DRAG_PX = 6
@@ -45,7 +56,7 @@ const openerFor = (event) => {
return null return null
} }
const opener = event.target.closest('[data-list-row]')?.querySelector('[data-list-open]') const opener = event.target.closest(ROW)?.querySelector(OPENER)
if (!opener) { if (!opener) {
return null return null
@@ -111,11 +122,11 @@ document.addEventListener('keydown', (event) => {
return return
} }
if (event.defaultPrevented || !(event.target instanceof Element) || !event.target.matches('[data-list-actionable]')) { if (event.defaultPrevented || !(event.target instanceof Element) || !event.target.matches(ACTIONABLE)) {
return return
} }
const opener = event.target.querySelector('[data-list-open]') const opener = event.target.querySelector(OPENER)
if (!opener) { if (!opener) {
return return