import { useLocation } from 'react-router'; import { DEFAULT_SECTION, isSectionKey, type SectionKey } from './sections'; /** * Where this admin is in the URL, read from the PATH. * * ── The path, not the query string ── * * It was `?section=layout&band=header`. Everything about where you were lived in the query, which made * every link a fresh `URLSearchParams` and every navigation a decision about what to carry — see the * note NavPanel used to hold about parameters leaking between sections. A path has no such question: * `/fofos-admin/layout/header` says where you are and nothing else, and what remains in the query is * genuinely about how you are looking rather than at what. * * The routes themselves are declared in the manifest, so the platform knows they exist — which is what * lets a route carry its own arrangement of panels rather than sharing one with the whole screen. * * ── Why the pathname and not `useParams` ── * * Which parameters exist depends on which route matched: `/fofos-admin/layout/header` has a `band` and * `/fofos-admin/carousels` has nothing. A panel would then have to know its own route to read its own * position, and every panel is rendered on all of them. * * Two segments after the mount, always in the same places. One reader, every route. */ export const MOUNT = '/fofos-admin'; /** * The section and its sub-item, from the two segments after the mount. * * `atIndex` is the bare `/fofos-admin`, which is a real state and not the same as Layout: the panel * redirects, so the address bar ends up saying where you are. `section` is still defaulted, so * everything that only wants to draw a highlight has one answer rather than a branch. */ export const useNav = (): { section: SectionKey; item: string | null; part: string | null; piece: string | null; atIndex: boolean; } => { const { pathname } = useLocation(); const rest = pathname.startsWith(MOUNT) ? pathname.slice(MOUNT.length) : ''; const [first = '', second = '', third = '', fourth = ''] = rest.replace(/^\//, '').split('/'); return { // An unknown segment falls back rather than 404s: the URL is a thing people type and edit. section: isSectionKey(first) ? first : DEFAULT_SECTION, item: second ? decodeURIComponent(second) : null, /** Something inside the item — a band of the open page. */ part: third ? decodeURIComponent(third) : null, /** Something inside THAT — a block in the band. This is what selection writes. */ piece: fourth ? decodeURIComponent(fourth) : null, atIndex: first === '', }; }; /** * A link to a section, or to something inside one. * * The query string is dropped, and that is the point rather than an omission. `?device=tablet` is how * you are looking at the Layout preview; carrying it into Carousels would be state from one screen * deciding something on another. A panel that wants a parameter to survive says so at its own link. */ export const href = ( section: SectionKey, item?: string | null, part?: string | null, piece?: string | null, ): string => { if (!item) return `${MOUNT}/${section}`; let path = `${MOUNT}/${section}/${encodeURIComponent(item)}`; if (!part) return path; path += `/${encodeURIComponent(part)}`; return piece ? `${path}/${encodeURIComponent(piece)}` : path; };