A fourth level under the open band only — a page of six bands with four blocks each is thirty rows, and the nav stops being somewhere you can find anything. Blocks are named by the same list the picker offers, so a thing is called the same word wherever it appears. Clicking a block in the PAGE now writes that address too, rather than calling the handler directly. Both ways of picking a block are the same act, so neither can disagree with the other about what is selected — and closing it is the band row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
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/galleries` 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 Galleries 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;
|
|
};
|