- "A page with a sticky ColumnHead must declare fadeTop: false" lived in prose: a registry flag on three pages plus an App special case for the Channels card layout, and the next table page would have re-acquired the fade-on-the-header defect with every lane green. ColumnHead now registers itself with the enclosing PageScroller (ui/stickyTop) while mounted; the registry flag, the App branch and the prop plumbing are gone. Measured on localdev: the top mask is 0px under Channels' and Audit's headers, 20px on Home and the Feed. - ui/DataTable mounted its table AND its card list at every width, one hidden by CSS: double DOM and render cost, duplicate ids, two keyboard copies of every row. It now mounts the one the `md` media query calls for (lib/useMediaQuery); the keyboard test asserts one layout at each width. - The Plex module's content width went px → rem with the rest of the page.
18 lines
1001 B
TypeScript
18 lines
1001 B
TypeScript
import { createContext, useContext, useLayoutEffect } from "react";
|
|
|
|
// A sticky element at the very top of the page scroller (a table's sticky header) and the
|
|
// scroller's top fade cannot coexist: the fade sits ON the header and flips on with the first
|
|
// scroll. Pages used to opt out by hand — a `fadeTop: false` in the module registry, plus an App
|
|
// special case for the Channels card layout, which has no sticky header — and the next table page
|
|
// would have re-acquired the defect with every lane green. Now the sticky element says so itself:
|
|
// while it is mounted the enclosing `PageScroller` keeps its top edge crisp.
|
|
const StickyTopContext = createContext<(() => () => void) | null>(null);
|
|
|
|
export const StickyTopProvider = StickyTopContext.Provider;
|
|
|
|
/** Call from a component that renders a sticky element at the top of the page scroller. */
|
|
export function useStickyTop(): void {
|
|
const register = useContext(StickyTopContext);
|
|
useLayoutEffect(() => register?.(), [register]);
|
|
}
|