import type { ReactNode } from "react"; export type SortDirection = "ascending" | "descending"; export type SortableTableHeaderProps = { children: ReactNode; className?: string; /** * The direction **this column** is sorted in, or `undefined` when * it is not the sorted one. Undefined is not "ascending by * default": a table has exactly one sorted column and every other * header has to say `aria-sort="none"`, which is what tells a * screen reader which one is live. */ direction?: SortDirection; /** Called with the direction the header is asking to move to. */ onSort: (direction: SortDirection) => void; }; /** * **The header cell, and nothing else.** No `DataTable`, no rows, no * comparator — the plan is explicit about that, and the reason is * that sorting *data* is app logic that differs per column type, * while announcing *that* a column is sorted is markup every table * gets wrong the same way. * * ### `aria-sort` does not exist anywhere in the fleet * * Not in mux-magic, not in rip-deck, not in castkit. mux-magic's * `FileExplorerModal` — the one sortable table there is — renders * the state as a character: * * ```tsx * {sortDirection === "asc" ? "▲" : "▼"} * ``` * * A screen reader announces that as "black up-pointing triangle", if * the font has it at all, and the sandbox's headless Chromium does * not have it — so the same glyph measures blank in a screenshot. * Sighted users see a direction, everyone else gets nothing, and no * automated check anywhere reports it: axe has no rule for a missing * `aria-sort`, because a table with none is a table that is simply * not sorted as far as the accessibility tree knows. * * This is therefore the one component in M6 whose payoff is not * duplication — there is a single site — but a class of failure * that is invisible to every gate the fleet has. * * ### The cell announces, the button acts * * `aria-sort` belongs on the ``; the click target is a `