/** * Which sidebar row is highlighted. * * `nav-active.ts` answers "does this href match the URL, given every other * href". This module answers the harder question the sidebar actually asks: * given a parent row and one of its children, which single row should carry * `data-active`. That needs the sibling set, because a hub URL repeated across * several children would otherwise light all of them at once. * * The predicates are built per URL corpus rather than read from module state, so * two shells in one process cannot overwrite each other's matching. The corpus * changes when tenant products hydrate, which is why it is a factory argument * and not a constant. */ /** Minimum a row needs for an active-state decision. Structural so the shell's render types satisfy it. */ interface NavActiveRow { key: string; url: string; children?: readonly NavActiveRow[]; primaryHubChildKey?: string; } /** * App refinement for a child row after hash and URL eligibility are established. * * Some rows are active on a route the generic rules cannot derive: a list hub * that stays selected across every filter in its query string, for instance, * where the shell would compare the child against its parent's hub path and * miss. Return `undefined` to defer to the remaining child-specific rules. * Returning `false` can suppress an eligible row; it cannot activate a row * whose URL or hash does not match. */ type NavRowActiveOverride = (row: { parentKey: string; childKey: string; }, pathname: string) => boolean | undefined; interface NavActiveStateOptions { /** Every href the sidebar can expose in any product; longest match wins. */ urls: readonly string[]; rowActive?: NavRowActiveOverride; } interface NavActiveState { /** A flat row (no children) against the whole corpus. */ isRowActive: (pathname: string, url: string, locationHash?: string) => boolean; /** A child row, disambiguated against its siblings. */ isChildActive: (pathname: string, parent: NavActiveRow, child: NavActiveRow, locationHash: string) => boolean; /** * A collapsible parent in the **expanded** sidebar. Neutral whenever a child * is active, because the child carries the highlight; lit only when the * parent's own URL matches and no child does. The collapsed icon rail wants * the opposite and uses `isChildActive` across the children instead, since * the parent icon is the only affordance there. */ isParentActive: (pathname: string, parent: NavActiveRow, locationHash: string) => boolean; } declare function createNavActiveState({ urls, rowActive, }: NavActiveStateOptions): NavActiveState; export { type NavActiveRow, type NavActiveState, type NavActiveStateOptions, type NavRowActiveOverride, createNavActiveState };