refactor(frontend): each module defines itself in its folder; rails load lazily and keep their own state
A page used to be listed three times (lib/modules for its label, icon and gate; shell/moduleRegistry for its page and rail; lib/pageMeta for its title), and App held every rail's layout and collapse flag. Now each module's `module.ts` says it all — label, icon, gate, its lazy page and rail, its width — handed out through its index.ts, and features/navigation/moduleRegistry lists them in nav order for the rail, the palette, the tab title, the reachable set and the shell's render. A contract test holds the registry to the page ids and each definition to its own folder, with no static import of a page. Every rail is lazy now, loaded together with its page so a pinned rail never arrives after it and shoves the content aside; the feed's and the playlists' rails took @dnd-kit off the boot path (boot 428.5 → 393.9 KiB gzip, the App chunk 122 → 32 KiB). A rail reads its own layout and collapse flag (lib/useRail) and writes a layout change to the `me` cache too, since it now mounts anew on each visit. App's session effects, its one-shot entry-URL handlers and the admin nudge move into shell hooks; App is layout. The registry's three layer suppressions are gone: none is left.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { Me } from "./api";
|
||||
import type { Page } from "./urlState";
|
||||
import type { PreloadableComponent } from "./lazyWithPreload";
|
||||
|
||||
// What a module says about itself: one definition per page, in its own folder (`modules/<page>/module.ts`,
|
||||
// handed out through its index.ts), collected in nav order by features/navigation/moduleRegistry. A new
|
||||
// page is its folder, its id in lib/urlState, and one line in that registry.
|
||||
export type ModuleDef = {
|
||||
id: Page;
|
||||
// i18n key for the SHORT display name — the label the nav rail and the palette show.
|
||||
labelKey: string;
|
||||
// The SHORT name under the icon when the user turns the rail labels on (B7): the rail is 3.75rem
|
||||
// wide with labels or without, so it has to fit there ("Alerts", "Listák"). The full `labelKey`
|
||||
// stays the tooltip and the accessible name.
|
||||
railKey: string;
|
||||
// i18n key for the LONGER, descriptive browser-tab title (e.g. "Channel manager" vs the rail's
|
||||
// "Channels"). Absent → the tab title falls back to `labelKey`. Read via `pageTitleKey`.
|
||||
titleKey?: string;
|
||||
icon: LucideIcon;
|
||||
// Admin/system modules render in their own section (below a divider) in the nav rail.
|
||||
system?: boolean;
|
||||
// Carries a live unread/active count in the rail (the number itself is computed in NavSidebar,
|
||||
// which owns the polling queries; this only flags which modules have one).
|
||||
hasBadge?: boolean;
|
||||
// Availability for THIS account, derived from language-independent account state (plex toggle,
|
||||
// admin role). Absent = always available. Drives the reachable set: the rail, the palette, the
|
||||
// `g` chords, and which page a link may land on.
|
||||
gate?: (me: Me) => boolean;
|
||||
// The page itself: lazy, its own chunk, fetched on the first visit or by the idle preload. Prop-free:
|
||||
// it reads `me` and its module's state from hooks.
|
||||
Page: PreloadableComponent<object>;
|
||||
// Its side rail, if it has one: lazy like the page, and prop-free (its layout and collapse flag are
|
||||
// its own, lib/useRail).
|
||||
Rail?: PreloadableComponent<object>;
|
||||
// The content column's max width. The header + toolbar centre on this same width (via --content-mw)
|
||||
// so the floating chrome lines up with the centred content instead of spanning the whole column.
|
||||
// MUST match the max-w the page's own content wrapper uses. Omit for a full-width page (feed).
|
||||
maxWidth?: string;
|
||||
};
|
||||
Reference in New Issue
Block a user