diff --git a/frontend/src/components/ui/DataTable.tsx b/frontend/src/components/ui/DataTable.tsx index bf179689..463243b4 100644 --- a/frontend/src/components/ui/DataTable.tsx +++ b/frontend/src/components/ui/DataTable.tsx @@ -9,6 +9,7 @@ import { alignClass } from "./tableAlign"; import { columnHideClass, filterRows, type Column, type FilterMap } from "../../lib/tableColumns"; import { navItemAttrs, useListNav } from "../../lib/listNav"; import { useMediaQuery } from "../../lib/useMediaQuery"; +import { withRowOpen } from "./rowOpen"; import { SURFACE } from "./surfaces"; import { HiddenFilterCue } from "./HiddenFilterCue"; import { useProgressiveRows } from "./progressiveRows"; @@ -57,6 +58,7 @@ interface TableRowProps { index: number; columns: Column[]; rowProps?: (row: T) => Record; + rowOpen?: (row: T) => (() => void) | null; rowClassName?: (row: T) => string; keyboardNav: boolean; } @@ -69,14 +71,16 @@ function TableRow({ index, columns, rowProps, + rowOpen, rowClassName, keyboardNav, }: TableRowProps) { + const open = rowOpen?.(row) ?? null; return ( {columns.map((col) => ( ({ emptyText, rowClassName, rowProps, + rowOpen, controlsPosition = "bottom", controlsLeading, controlsInBand = false, @@ -137,6 +142,10 @@ export default function DataTable({ // answer the same gesture. Deliberately not a bag of named callbacks: the table stays domain-free // and never learns what a menu is. rowProps?: (row: T) => Record; + // The row's default action (play, open), or null where a row has none: a press anywhere on the row + // or its card twin runs it, except on a control inside it or at the end of a text selection + // (`rowOpen.ts`). The keyboard's Enter keeps reaching it through the row's `data-nav-open` element. + rowOpen?: (row: T) => (() => void) | null; controlsPosition?: "top" | "bottom" | "both"; controlsLeading?: ReactNode; // Render the controls row (controlsLeading + pager) into the shell's fixed toolbar band instead @@ -414,6 +423,7 @@ export default function DataTable({ index={i} columns={columns} rowProps={rowProps} + rowOpen={rowOpen} rowClassName={rowClassName} keyboardNav={keyboardNav} /> @@ -430,9 +440,9 @@ export default function DataTable({ {shown.map((row, i) => (
{columns .filter((c) => c.cardPrimary) diff --git a/frontend/src/components/ui/README.md b/frontend/src/components/ui/README.md index 8dd13ac4..adfdacc0 100644 --- a/frontend/src/components/ui/README.md +++ b/frontend/src/components/ui/README.md @@ -39,7 +39,7 @@ Counter-examples, deliberately NOT here: `PosterTile` (knows about Plex montage | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | overlays & anchoring | `AnchoredPopover` + `anchoredPopoverHooks` (the popover engine: arrows, typeahead, Escape, anchor-flip), `ActionMenu`, `MembershipMenu` (the body of a "which sets is it in" menu: checkbox rows + a field naming a new set), `ContextMenu` + `useContextMenu` (the SAME menu, opened by right-click / long-press / Shift+F10 / a row's `MoreButton` "…" via `buttonProps`; `anchor` for a panel a row opens; `PageMenu` for a page's background menu), `Modal` (+ `ModalFooter`; `initialFocus` by purpose, C4), `Overlay`, `SidePanel`, `Tooltip`, `Toaster` (+ `toastLevels`, `overlayOrder`; `lib/toastDock` docks one control under the stack, in every host), `CommandPalette`, `KeyboardHelp`, `ConfirmProvider` + `confirmContext` (`useConfirm` / `useChoice`: the app's promise-based confirm, mounted once at the root; a decision with more than two outcomes is one `useChoice`, never two chained confirms), `dialogFocus` (the focus half of the dialog contract: `Modal`'s, and the wizard's own shell), `menuGroups` (one menu from its groups, a rule only between non-empty ones), `pageMenuSlot` + `pageMenuItems` (a page's background menu, mirroring its own toolbar) | | actions & inputs | `Button`, `IconButton`, `Pressable` (`selectable` when its text is a title or a name), `Chip` + `chipStyles`, `DragHandle` + `OrderButtons` / `useOrderItems` (the non-drag reorder, WCAG 2.5.7) (one per button family, docs/DESIGN.md; a raw `