fix(datatable): the sticky header no longer shifts or fades on first scroll
The sticky <thead> jumped up ~8px and picked up the scroller's top fade the moment you scrolled 1px — so it read as 'not fixed'. Two causes: - the table sat under a pt-2 gap, so its natural position was 8px below the sticky top:0 it snapped to. Drop the top padding (the fixed band's controls row already gives the visual gap) → no shift. - the page scroller's top edge fade turned on with the first scroll and masked the header's top. Add a fadeTop flag (PageShell → PageScroller → useScrollFade), off for the Channels + Audit pages whose scroll region starts with a sticky header. Feed/channel keep the top fade (it fades cards under the fixed toolbar, as intended). Verified: header stays at a fixed offset with no fade change from scroll 0 through scrolled, on both the manager and the audit log. Suite 15/15.
This commit is contained in:
@@ -577,7 +577,10 @@ export default function Channels({
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 pb-4 pt-2 max-w-7xl mx-auto">
|
||||
/* No top padding: the sticky header sits flush at the scroller top so it doesn't shift when
|
||||
`position: sticky` engages on the first scroll (the band's controls row above gives the
|
||||
visual gap). */
|
||||
<div className="px-4 pb-4 max-w-7xl mx-auto">
|
||||
{/* Channel table — only the rows scroll; the header row is sticky, and the controls row
|
||||
(status chips + pager) rides in the fixed band right above it (controlsInBand). */}
|
||||
{channelsQuery.isLoading ? (
|
||||
|
||||
@@ -63,11 +63,14 @@ function restoreScroll(el: HTMLElement, top: number): () => void {
|
||||
export default function PageScroller({
|
||||
children,
|
||||
scrollKey,
|
||||
fadeTop = true,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
scrollKey?: string;
|
||||
// Off when the scroller's top is a sticky element (a table's sticky header) — see useScrollFade.
|
||||
fadeTop?: boolean;
|
||||
}) {
|
||||
const fade = useScrollFade();
|
||||
const fade = useScrollFade(20, fadeTop);
|
||||
const fadeRef = fade.ref;
|
||||
const elRef = useRef<HTMLElement | null>(null);
|
||||
// Stable identity: an inline ref callback changes every render, which makes React detach and
|
||||
|
||||
@@ -41,12 +41,15 @@ export default function PageShell({
|
||||
banners,
|
||||
children,
|
||||
scrollKey,
|
||||
fadeTop = true,
|
||||
}: {
|
||||
header?: ReactNode;
|
||||
banners?: ReactNode;
|
||||
children: ReactNode;
|
||||
// Identifies the logical view for the scroll restore/reset policy — see PageScroller.
|
||||
scrollKey?: string;
|
||||
// Off when this page's scroll region starts with a sticky header — see PageScroller.
|
||||
fadeTop?: boolean;
|
||||
}) {
|
||||
const [toolbarSlot, setToolbarSlot] = useState<HTMLDivElement | null>(null);
|
||||
return (
|
||||
@@ -55,7 +58,9 @@ export default function PageShell({
|
||||
<div className={`flex-1 min-w-0 min-h-0 flex flex-col ${header ? "pt-[var(--hdr-h)]" : ""}`}>
|
||||
{banners}
|
||||
<div ref={setToolbarSlot} className="shrink-0" />
|
||||
<PageScroller scrollKey={scrollKey}>{children}</PageScroller>
|
||||
<PageScroller scrollKey={scrollKey} fadeTop={fadeTop}>
|
||||
{children}
|
||||
</PageScroller>
|
||||
</div>
|
||||
</PageToolbarSlot.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user