Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 36x 36x 36x 36x 14x 14x 14x 14x 9x 14x 1236x 36x 1227x 1227x 6x 1227x 6x 1227x 4x 1227x | import {
ReactNode,
createContext,
useCallback,
useContext,
useMemo,
useState,
} from "react";
/**
* TRA013 — mobile scroll header.
*
* On mobile the table must NOT render its own absolute "compact head" bar
* (which used to appear directly below the fixed top bar and get partially
* covered by it). Instead, the compact head content — the page title/stats +
* search + three-dots menu — is handed up to the fixed `MobileNavBar`, which
* expands to show it while the full banner is scrolled out of view.
*
* The Table publishes its compact head node + visibility here; `MobileNavBar`
* consumes it. `isEnabled` tells the Table whether a topbar host actually
* exists — when it is false (e.g. no-sidebar layouts that never render the
* mobile navbar) the Table falls back to its own inline compact bar so the
* search + actions stay reachable.
*
* A table may additionally publish a `compactTitle` — what the page is about,
* shown in the top bar's FIRST row (where the balance normally sits) while the
* compact head is expanded. Used by pages whose subject has a name rather than
* stats (Event Detail, frame 8091-56253): the name goes up beside the menu
* button so the second row is left to the full-width search. Pages that publish
* none keep the balance, unchanged.
*/
type MobileTableHeaderContextType = {
isEnabled: boolean;
compactHead: ReactNode | null;
compactTitle: ReactNode | null;
isActive: boolean;
setCompactHead: (node: ReactNode | null) => void;
setCompactTitle: (node: ReactNode | null) => void;
setActive: (active: boolean) => void;
};
const noop = () => undefined;
const DEFAULT_VALUE: MobileTableHeaderContextType = {
isEnabled: false,
compactHead: null,
compactTitle: null,
isActive: false,
setCompactHead: noop,
setCompactTitle: noop,
setActive: noop,
};
const MobileTableHeaderContext =
createContext<MobileTableHeaderContextType>(DEFAULT_VALUE);
export const MobileTableHeaderProvider = ({
children,
}: {
children: ReactNode;
}) => {
const [compactHead, setCompactHead] = useState<ReactNode | null>(null);
const [compactTitle, setCompactTitle] = useState<ReactNode | null>(null);
const [isActive, setActive] = useState(false);
const value = useMemo<MobileTableHeaderContextType>(
() => ({
isEnabled: true,
compactHead,
compactTitle,
isActive,
setCompactHead,
setCompactTitle,
setActive,
}),
[compactHead, compactTitle, isActive],
);
return (
<MobileTableHeaderContext.Provider value={value}>
{children}
</MobileTableHeaderContext.Provider>
);
};
export const useMobileTableHeader = () => useContext(MobileTableHeaderContext);
/**
* Convenience hook for the Table: keeps setter identities stable so callers can
* safely include them in effect deps.
*/
export const useMobileTableHeaderControls = () => {
const { isEnabled, setCompactHead, setCompactTitle, setActive } =
useMobileTableHeader();
const publish = useCallback(
(node: ReactNode | null) => setCompactHead(node),
[setCompactHead],
);
const publishTitle = useCallback(
(node: ReactNode | null) => setCompactTitle(node),
[setCompactTitle],
);
const activate = useCallback(
(active: boolean) => setActive(active),
[setActive],
);
return { isEnabled, publish, publishTitle, activate };
};
|