Files
siftlode/frontend/src/components/ui/NavGrid.tsx
T
peter 8cf8c4139c feat(playlists): select rows with a box, Shift for a range, x and Esc
A box first in every row and one over the column for the rows shown, the
selected row tinted. A filter keeps what it hides selected; another list
starts over; a row that left the list drops out. Watch later has none.

A box no longer counts as a typing target: a click on one left the focus
there and the page's keys went dead.
2026-11-01 20:49:59 +01:00

33 lines
1.3 KiB
TypeScript

import { useRef, type ReactNode } from "react";
import { useListNav, type NavSelection } from "../../lib/listNav";
// A plain list of cards or rows the keyboard can walk — `j`/`k`, the arrows, Enter (lib/listNav) — for
// the lists that mount every item (the Plex title, season and episode grids, the download lists, a
// playlist's table body). Nothing to count: the items and the columns per row are measured when a key
// is pressed, so an auto-fill grid's columns follow its width and a column of rows is one per row.
// Each child spreads `navItemAttrs(index)` on its root; a virtualized grid is VirtualGrid's
// `keyboardNav` instead. `as="tbody"` for a table whose rows are the items.
export default function NavGrid({
as: Tag = "div",
className,
onDelete,
selection,
children,
}: {
as?: "div" | "tbody";
className?: string;
/** The Delete key takes the focused item out (lib/listNav): its index among the items. */
onDelete?: (index: number) => void;
/** `x` selects the focused item, Esc clears (lib/listNav). */
selection?: NavSelection;
children: ReactNode;
}) {
const ref = useRef<HTMLDivElement & HTMLTableSectionElement>(null);
useListNav(ref, { enabled: true, onDelete, selection });
return (
<Tag ref={ref} className={className}>
{children}
</Tag>
);
}