@ethisyscore/plugin-ui 1.42.0 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -406,7 +406,9 @@ function usePageHeader() {
406
406
  // chrome for platform-react surfaces), so it owns the title: a list page
407
407
  // shows its menu label; detail/create/edit pages override with their own.
408
408
  title: title ?? detailContext?.title ?? activeMenuItem?.label ?? "",
409
- subtitle,
409
+ // Fall back to the detail context's subtitle so a wrapper-rendered header
410
+ // (no page spreading its own `headerProps`) still surfaces the entity name.
411
+ subtitle: subtitle ?? detailContext?.subtitle,
410
412
  primaryAction: primary,
411
413
  secondaryAction: secondary,
412
414
  overflowActions: overflow.length > 0 ? overflow : void 0,
@@ -500,23 +502,35 @@ function buildBreadcrumbs(pathname, routeMeta) {
500
502
  });
501
503
  return crumbs;
502
504
  }
505
+ function useNoDetailContext() {
506
+ return null;
507
+ }
508
+ function ChromeHeader() {
509
+ return /* @__PURE__ */ jsxRuntime.jsx(PageHeader, { ...usePageHeader() });
510
+ }
503
511
  function PageHeaderProvider({
504
512
  config,
505
513
  routeMeta,
506
514
  detailContext,
515
+ useDetailContext = useNoDetailContext,
507
516
  children
508
517
  }) {
509
518
  const { pathname } = reactRouterDom.useLocation();
519
+ const resolvedFromResolver = useDetailContext();
520
+ const resolvedDetailContext = useDetailContext === useNoDetailContext ? detailContext ?? null : resolvedFromResolver;
510
521
  const state = react.useMemo(() => {
511
522
  const activeMenuItem = findByHref(config, normalise(pathname));
512
523
  const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : void 0;
513
524
  const title = breadcrumbs != null && breadcrumbs.length >= 2 ? breadcrumbs[breadcrumbs.length - 1].label : void 0;
514
- return { activeMenuItem, detailContext, breadcrumbs, title };
515
- }, [config, routeMeta, pathname, detailContext]);
516
- return /* @__PURE__ */ jsxRuntime.jsx(SidebarActionProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(PageHeaderContext.Provider, { value: state, children }) });
525
+ return { activeMenuItem, detailContext: resolvedDetailContext, breadcrumbs, title };
526
+ }, [config, routeMeta, pathname, resolvedDetailContext]);
527
+ return /* @__PURE__ */ jsxRuntime.jsx(SidebarActionProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(PageHeaderContext.Provider, { value: state, children: [
528
+ resolvedDetailContext != null ? /* @__PURE__ */ jsxRuntime.jsx(ChromeHeader, {}) : null,
529
+ children
530
+ ] }) });
517
531
  }
518
- function withPageHeader(config, routeMeta) {
519
- return (page) => /* @__PURE__ */ jsxRuntime.jsx(PageHeaderProvider, { config, routeMeta, children: page });
532
+ function withPageHeader(config, routeMeta, useDetailContext) {
533
+ return (page) => /* @__PURE__ */ jsxRuntime.jsx(PageHeaderProvider, { config, routeMeta, useDetailContext, children: page });
520
534
  }
521
535
  function normalise(pathname) {
522
536
  return pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/layout/fillGridClasses.ts","../../../src/components/layout/FillGrid.tsx","../../../src/components/layout/PageHeader.tsx","../../../src/components/layout/pageHeaderContext.ts","../../../src/components/layout/sidebarAction.tsx","../../../src/components/layout/usePageHeader.ts","../../../src/components/layout/breadcrumbs.ts","../../../src/components/layout/PageHeaderProvider.tsx"],"names":["createContext","jsx","Box","useContext","Stack","Breadcrumbs","jsxs","Typography","Link","Button","useState","useRef","Fragment","Menu","MenuItem","IconButton","useCallback","useMemo","useEffect","useLocation"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Static Tailwind basis classes for {@link FillGridItem}.\n *\n * Tailwind's content scanner extracts class candidates from raw source text and\n * cannot follow string concatenation — `\"sm:\" + \"basis-[...]\"` is invisible to\n * it. So every fully-prefixed class is spelled out as a literal below, indexed\n * by breakpoint → gap → span. `fillItemClass` only ever *looks up* a literal; it\n * never builds one. This is what guarantees the responsive classes survive a\n * production purge (no fragile safelist comment to keep in sync).\n *\n * NOTE: because these literals live in the SDK, a CONSUMER's Tailwind build must\n * scan this package's dist (add it to `content`) or the classes won't generate.\n *\n * basis(n, g) = calc(n/12 * 100% - g * (1 - n/12)), g = gap in rem.\n * gap-5 => g = 1.25rem; gap-2.5 => g = 0.625rem. Span 12 => basis-full.\n */\nexport type Twelfths = 2.4 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12;\nexport type FillGap = 2.5 | 5;\ntype Breakpoint = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type FillSpan = Partial<Record<Breakpoint, Twelfths>>;\n\n/**\n * Full literal class per breakpoint × gap × span. `xs` is unprefixed; `sm`/`md`/\n * `lg` carry the Tailwind prefix. Every string here is a real, scannable literal.\n */\nconst CLASSES: Record<Breakpoint, Record<FillGap, Record<Twelfths, string>>> = {\n xs: {\n 5: {\n 2.4: \"basis-[calc(20%-1rem)]\",\n 3: \"basis-[calc(25%-0.9375rem)]\",\n 4: \"basis-[calc(33.333%-0.8333rem)]\",\n 5: \"basis-[calc(41.667%-0.7292rem)]\",\n 6: \"basis-[calc(50%-0.625rem)]\",\n 7: \"basis-[calc(58.333%-0.5208rem)]\",\n 8: \"basis-[calc(66.667%-0.4167rem)]\",\n 9: \"basis-[calc(75%-0.3125rem)]\",\n 12: \"basis-full\",\n },\n 2.5: {\n 2.4: \"basis-[calc(20%-0.5rem)]\",\n 3: \"basis-[calc(25%-0.46875rem)]\",\n 4: \"basis-[calc(33.333%-0.41667rem)]\",\n 5: \"basis-[calc(41.667%-0.36458rem)]\",\n 6: \"basis-[calc(50%-0.3125rem)]\",\n 7: \"basis-[calc(58.333%-0.26042rem)]\",\n 8: \"basis-[calc(66.667%-0.20833rem)]\",\n 9: \"basis-[calc(75%-0.15625rem)]\",\n 12: \"basis-full\",\n },\n },\n sm: {\n 5: {\n 2.4: \"sm:basis-[calc(20%-1rem)]\",\n 3: \"sm:basis-[calc(25%-0.9375rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"sm:basis-[calc(50%-0.625rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"sm:basis-[calc(75%-0.3125rem)]\",\n 12: \"sm:basis-full\",\n },\n 2.5: {\n 2.4: \"sm:basis-[calc(20%-0.5rem)]\",\n 3: \"sm:basis-[calc(25%-0.46875rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"sm:basis-[calc(50%-0.3125rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"sm:basis-[calc(75%-0.15625rem)]\",\n 12: \"sm:basis-full\",\n },\n },\n md: {\n 5: {\n 2.4: \"md:basis-[calc(20%-1rem)]\",\n 3: \"md:basis-[calc(25%-0.9375rem)]\",\n 4: \"md:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"md:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"md:basis-[calc(50%-0.625rem)]\",\n 7: \"md:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"md:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"md:basis-[calc(75%-0.3125rem)]\",\n 12: \"md:basis-full\",\n },\n 2.5: {\n 2.4: \"md:basis-[calc(20%-0.5rem)]\",\n 3: \"md:basis-[calc(25%-0.46875rem)]\",\n 4: \"md:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"md:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"md:basis-[calc(50%-0.3125rem)]\",\n 7: \"md:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"md:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"md:basis-[calc(75%-0.15625rem)]\",\n 12: \"md:basis-full\",\n },\n },\n lg: {\n 5: {\n 2.4: \"lg:basis-[calc(20%-1rem)]\",\n 3: \"lg:basis-[calc(25%-0.9375rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"lg:basis-[calc(50%-0.625rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"lg:basis-[calc(75%-0.3125rem)]\",\n 12: \"lg:basis-full\",\n },\n 2.5: {\n 2.4: \"lg:basis-[calc(20%-0.5rem)]\",\n 3: \"lg:basis-[calc(25%-0.46875rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"lg:basis-[calc(50%-0.3125rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"lg:basis-[calc(75%-0.15625rem)]\",\n 12: \"lg:basis-full\",\n },\n },\n};\n\nconst BREAKPOINTS: readonly Breakpoint[] = [\"xs\", \"sm\", \"md\", \"lg\"];\n\n/**\n * Full class string for one item: always `min-w-0 grow`, plus the literal basis\n * class for each supplied breakpoint (looked up, never concatenated).\n */\nexport function fillItemClass(span: FillSpan, gap: FillGap): string {\n const parts = [\"min-w-0\", \"grow\"];\n for (const bp of BREAKPOINTS) {\n const n = span[bp];\n if (n !== undefined) parts.push(CLASSES[bp][gap][n]);\n }\n return parts.join(\" \");\n}\n","import { createContext, useContext } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { fillItemClass, type FillGap, type FillSpan } from \"./fillGridClasses\";\n\nconst FillGapContext = createContext<FillGap>(5);\n\ninterface FillGridProps {\n /** Gutter width. 5 = 20px (default), 2.5 = 10px. Matches MUI spacing values. */\n gap?: FillGap;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A flex-wrap row whose items `grow` to fill a partially-filled last row — the\n * dashboard/overview convention. NOT for forms, tables, or single full-width cards.\n *\n * Renders `w-full` so it always spans its container: a `FillGrid` is itself often\n * a flex item of a parent MUI `<Grid container>` / flex row, where without an\n * explicit width it would shrink-wrap to its content instead of filling the row\n * (the behaviour a `<Grid container size={12}>` used to give).\n */\nexport function FillGrid({ gap = 5, className, children }: FillGridProps) {\n const gapClass = gap === 2.5 ? \"gap-2.5\" : \"gap-5\";\n return (\n <FillGapContext.Provider value={gap}>\n <Box className={[\"flex w-full flex-wrap\", gapClass, className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n </FillGapContext.Provider>\n );\n}\n\ninterface FillGridItemProps {\n /** 12-column span per breakpoint, mirroring MUI Grid's `size`. Default { xs: 12 }. */\n span?: FillSpan;\n className?: string;\n children: React.ReactNode;\n}\n\nexport function FillGridItem({ span = { xs: 12 }, className, children }: FillGridItemProps) {\n const gap = useContext(FillGapContext);\n return (\n <Box className={[fillItemClass(span, gap), className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n );\n}\n","import { useState, useRef, type ReactNode } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n Box,\n Breadcrumbs as MuiBreadcrumbs,\n Stack,\n Typography,\n Link as MuiLink,\n Button,\n IconButton,\n Menu,\n MenuItem,\n} from \"../ui\";\nimport type { ActionDescriptor, Breadcrumb } from \"./pageHeaderTypes\";\n\n/**\n * `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the\n * monolith so plugins share one component instead of copying it. In a plugin this\n * IS the page header: the host suppresses its own chrome for platform-react\n * surfaces, so this renders the breadcrumb trail, the title, and the CTAs.\n *\n * `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is\n * appended as the non-link leaf crumb. MUI primitives come from the sibling\n * `../ui` barrel (host externals, never bundled); `Link` is react-router's so\n * links keep middle-click / open-in-new-tab / a11y.\n */\nexport interface PageHeaderProps {\n title: string;\n subtitle?: ReactNode;\n primaryAction?: ActionDescriptor;\n secondaryAction?: ActionDescriptor;\n overflowActions?: ActionDescriptor[];\n /** Root breadcrumb trail; the `title` is appended as the current-page leaf. */\n breadcrumbs?: Breadcrumb[];\n}\n\nexport function PageHeader({\n title,\n subtitle,\n primaryAction,\n secondaryAction,\n overflowActions,\n breadcrumbs,\n}: PageHeaderProps) {\n const hasActions =\n primaryAction != null ||\n secondaryAction != null ||\n (overflowActions != null && overflowActions.length > 0);\n\n return (\n <Stack direction=\"column\" spacing={0.5} className=\"mb-4\">\n {breadcrumbs != null && breadcrumbs.length > 0 && <Breadcrumbs trail={breadcrumbs} />}\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" spacing={2}>\n <Box>\n {/* Title is optional: in a plugin the host renders the surface title,\n so list pages pass an empty title and only their CTA shows. Pages\n that want an in-body title (detail/create/edit) set it explicitly. */}\n {title !== \"\" && (\n <Typography variant=\"h4\" component=\"h1\">\n {title}\n </Typography>\n )}\n {subtitle != null && (\n <Typography variant=\"body2\" color=\"text.secondary\" className=\"mt-1\">\n {subtitle}\n </Typography>\n )}\n </Box>\n {hasActions && (\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n data-testid=\"page-header-actions\"\n >\n {overflowActions != null && overflowActions.length > 0 && (\n <OverflowMenu actions={overflowActions} />\n )}\n {secondaryAction != null && <SecondaryDropdown action={secondaryAction} />}\n {primaryAction != null && <PrimaryButton action={primaryAction} />}\n </Stack>\n )}\n </Stack>\n </Stack>\n );\n}\n\n/**\n * Breadcrumb trail — mirrors the host's `gogo-ui` `PageBreadcrumbs` exactly (MUI\n * `Breadcrumbs` with the same markup/props) so the plugin surface looks identical\n * to host-rendered chrome: `text.secondary` links (`underline=\"hover\"`) up to the\n * current page, a `text.primary` leaf, `variant=\"body2\"`, `mb-2 px-1`.\n */\nfunction Breadcrumbs({ trail }: { trail: Breadcrumb[] }) {\n if (trail.length < 2) return null;\n return (\n <MuiBreadcrumbs aria-label=\"breadcrumb\" className=\"mb-2 px-1\">\n {trail.map((crumb, index) =>\n index < trail.length - 1 ? (\n <MuiLink\n key={crumb.href ?? crumb.label}\n component={Link}\n to={crumb.href ?? \"/\"}\n underline=\"hover\"\n color=\"text.secondary\"\n variant=\"body2\"\n >\n {crumb.label}\n </MuiLink>\n ) : (\n <Typography key={crumb.label} color=\"text.primary\" variant=\"body2\">\n {crumb.label}\n </Typography>\n ),\n )}\n </MuiBreadcrumbs>\n );\n}\n\nfunction PrimaryButton({ action }: { action: ActionDescriptor }) {\n const common = {\n \"data-danger\": action.danger ? \"true\" : undefined,\n disabled: action.disabled,\n } as const;\n if (action.href != null) {\n return (\n <Button\n component={Link}\n to={action.href}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n onClick={action.onClick}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n}\n\nfunction useMenuState() {\n const [open, setOpen] = useState(false);\n const anchorRef = useRef<HTMLButtonElement | null>(null);\n return {\n open,\n anchorRef,\n toggle: () => setOpen((v) => !v),\n close: () => setOpen(false),\n };\n}\n\nfunction SecondaryDropdown({ action }: { action: ActionDescriptor }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n const children = action.children ?? [];\n\n return (\n <>\n <Button\n ref={anchorRef}\n variant=\"outlined\"\n onClick={toggle}\n disabled={action.disabled || children.length === 0}\n aria-haspopup=\"menu\"\n aria-expanded={open || undefined}\n aria-label={action.label}\n >\n {action.label} ▾\n </Button>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {children.map((child) => (\n // href children render as anchors (component={Link}) to keep\n // middle-click / open-in-new-tab / a11y. `href` and `onClick` are\n // mutually exclusive on ActionDescriptor, so one MenuItem covers both.\n <MenuItem\n key={child.id}\n {...(child.href != null ? { component: Link, to: child.href } : {})}\n onClick={() => {\n close();\n child.onClick?.();\n }}\n disabled={child.disabled}\n >\n {child.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n\nfunction OverflowMenu({ actions }: { actions: ActionDescriptor[] }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n return (\n <>\n <IconButton\n ref={anchorRef}\n size=\"small\"\n aria-label=\"More actions\"\n onClick={toggle}\n >\n {\"⋯\"}\n </IconButton>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {actions.map((a) => (\n // href items render as anchors (component={Link}) — like\n // PrimaryButton — to keep middle-click / open-in-new-tab / a11y.\n // `href` and `onClick` are mutually exclusive on ActionDescriptor,\n // so one MenuItem covers both.\n <MenuItem\n key={a.id}\n {...(a.href != null ? { component: Link, to: a.href } : {})}\n onClick={() => {\n close();\n a.onClick?.();\n }}\n disabled={a.disabled}\n sx={a.danger ? { color: \"error.main\" } : undefined}\n >\n {a.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { PageHeaderState } from \"./pageHeaderTypes\";\n\n/**\n * Carries the active menu item / detail context so `usePageHeader` can derive\n * header props. The plugin analogue of the monolith shell's\n * `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.\n *\n * `null` when no provider is mounted (a page rendered standalone, e.g. a test) —\n * consumers treat `null` as \"no header context\" and derive empty props.\n */\nexport const PageHeaderContext = createContext<PageHeaderState | null>(null);\n\n/** Returns the provider-published header state, or `null` when none is mounted. */\nexport function usePageHeaderContext(): PageHeaderState | null {\n return useContext(PageHeaderContext);\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\n/**\n * Sidebar-action dispatch channel — promoted from the monolith's\n * `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an\n * `href`) dispatch through here; a page registers a handler via\n * `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs\n * (\"New Supplier Invoice\", \"New Cost Code\", …) fire when clicked.\n */\n\nexport type ActionHandler = (actionId: string) => void;\n\nexport interface SidebarActionContextValue {\n /** Dispatch an action to all registered page handlers. */\n triggerAction: (actionId: string) => void;\n /** Register a handler that receives sidebar actions. Returns an unregister function. */\n registerHandler: (handler: ActionHandler) => () => void;\n /** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */\n activeActions: ReadonlySet<string>;\n /** Reports whether the owning UI surface for `actionId` is currently open. */\n setActionActive: (actionId: string, isActive: boolean) => void;\n}\n\nexport const SidebarActionContext = createContext<SidebarActionContextValue>({\n triggerAction: () => {},\n registerHandler: () => () => {},\n activeActions: new Set(),\n setActionActive: () => {},\n});\n\nexport function SidebarActionProvider({ children }: { children: ReactNode }) {\n const handlersRef = useRef(new Set<ActionHandler>());\n const [activeActions, setActiveActions] = useState<ReadonlySet<string>>(() => new Set());\n\n const triggerAction = useCallback((actionId: string) => {\n for (const handler of handlersRef.current) {\n handler(actionId);\n }\n }, []);\n\n const registerHandler = useCallback((handler: ActionHandler) => {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n }, []);\n\n const setActionActive = useCallback((actionId: string, isActive: boolean) => {\n setActiveActions((prev) => {\n const has = prev.has(actionId);\n if (isActive === has) return prev;\n const next = new Set(prev);\n if (isActive) next.add(actionId);\n else next.delete(actionId);\n return next;\n });\n }, []);\n\n const value = useMemo(\n () => ({ triggerAction, registerHandler, activeActions, setActionActive }),\n [triggerAction, registerHandler, activeActions, setActionActive],\n );\n\n return <SidebarActionContext.Provider value={value}>{children}</SidebarActionContext.Provider>;\n}\n\n/** Used by the header to dispatch actions to pages. */\nexport function useTriggerSidebarAction(): (actionId: string) => void {\n return useContext(SidebarActionContext).triggerAction;\n}\n\n/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */\nexport function useSidebarAction(handler: ActionHandler): void {\n const { registerHandler } = useContext(SidebarActionContext);\n useEffect(() => registerHandler(handler), [registerHandler, handler]);\n}\n\n/**\n * Registers a map of sidebar-action handlers keyed by action id, dispatching the\n * matching entry when an action fires (unknown ids are ignored). The handler map\n * may change every render — a ref keeps the dispatcher stable so the registration\n * doesn't churn.\n */\nexport function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void {\n const handlersRef = useRef(handlers);\n handlersRef.current = handlers;\n useSidebarAction(\n useCallback((actionId: string) => {\n handlersRef.current[actionId]?.();\n }, []),\n );\n}\n\n/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */\nexport function useActiveSidebarActions(): ReadonlySet<string> {\n return useContext(SidebarActionContext).activeActions;\n}\n\n/**\n * Reports to the channel whether the owning UI surface for `actionId` is open.\n * Pages call this from a hook scope, mirroring their dialog/drawer open state.\n */\nexport function useReportSidebarActionActive(actionId: string, isActive: boolean): void {\n const { setActionActive } = useContext(SidebarActionContext);\n useEffect(() => {\n setActionActive(actionId, isActive);\n return () => {\n setActionActive(actionId, false);\n };\n }, [actionId, isActive, setActionActive]);\n}\n","import { useMemo } from \"react\";\nimport { usePageHeaderContext } from \"./pageHeaderContext\";\nimport { useTriggerSidebarAction } from \"./sidebarAction\";\nimport type {\n ActionDescriptor,\n ActionSlot,\n HeaderAction,\n PageHeaderDerivedProps,\n} from \"./pageHeaderTypes\";\n\n/**\n * Derives `<PageHeader>` props from the active menu item or detail context —\n * promoted verbatim from the monolith's `usePageHeader`, reading the plugin's\n * `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.\n *\n * Outside a mounted `PageHeaderProvider` (or in a test rendering a page\n * standalone) the context is `null` and the hook derives empty header props —\n * same shape, no work. Page hooks spread the result and may override\n * title/subtitle/actions.\n */\nexport function usePageHeader(): PageHeaderDerivedProps {\n const state = usePageHeaderContext();\n const { activeMenuItem, detailContext, title, subtitle, breadcrumbs } = state ?? {};\n // Dispatcher for actionId-only actions — the same channel a page registers\n // against via `useSidebarAction`. Threading it into `toDescriptor` is what\n // makes header buttons like `+ New Supplier Invoice` (which carry an\n // `actionId` and no `href`) open the page-owned dialog when clicked.\n const triggerAction = useTriggerSidebarAction();\n\n return useMemo(() => {\n const sourceActions: HeaderAction[] =\n detailContext?.actions ?? activeMenuItem?.actions ?? [];\n\n let primary: ActionDescriptor | undefined;\n let secondary: ActionDescriptor | undefined;\n const overflow: ActionDescriptor[] = [];\n\n for (const action of sourceActions) {\n const descriptor = toDescriptor(action, triggerAction);\n const slot: ActionSlot = action.slot ?? \"sidebar-only\";\n switch (slot) {\n case \"primary\":\n if (primary == null) {\n primary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple primary slots on this page; demoting later ones to overflow\",\n { keptId: primary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"secondary\":\n if (secondary == null) {\n secondary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple secondary slots on this page; demoting later ones to overflow\",\n { keptId: secondary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"overflow\":\n overflow.push(descriptor);\n break;\n case \"sidebar-only\":\n default:\n // Not header-visible.\n break;\n }\n }\n\n return {\n // The plugin's `PageHeader` is the sole header (the host suppresses its own\n // chrome for platform-react surfaces), so it owns the title: a list page\n // shows its menu label; detail/create/edit pages override with their own.\n title: title ?? detailContext?.title ?? activeMenuItem?.label ?? \"\",\n subtitle,\n primaryAction: primary,\n secondaryAction: secondary,\n overflowActions: overflow.length > 0 ? overflow : undefined,\n breadcrumbs,\n };\n }, [activeMenuItem, detailContext, title, subtitle, breadcrumbs, triggerAction]);\n}\n\n/**\n * Map a raw header source action to `PageHeader`'s `ActionDescriptor`.\n *\n * Resolution order for `onClick`:\n * 1. Explicit `raw.onClick` (detail-context actions can carry one directly).\n * 2. `raw.actionId` → dispatch via the sidebar-action channel. This is what\n * makes menu-config actions like `actionId: \"create-cost-code\"` (no `href`,\n * no `onClick`) fire when the header button is clicked — the page registers\n * a handler via `useSidebarAction`.\n * 3. Neither — the button still renders, but is a no-op (typically paired with\n * `href` so the primary button renders a Link instead).\n *\n * The trigger is threaded down explicitly rather than via a hook because this\n * runs inside `useMemo` and can recurse (for secondary-dropdown children).\n */\nfunction toDescriptor(\n raw: HeaderAction,\n triggerAction: (actionId: string) => void,\n): ActionDescriptor {\n const explicitOnClick = raw.onClick;\n const dispatchActionId = raw.actionId;\n // `href` and `onClick` are mutually exclusive on ActionDescriptor. A navigating\n // action (`href`) must NOT also carry an `onClick`, or the Link-rendering menu\n // items (SecondaryDropdown / OverflowMenu always call `child.onClick`) would fire\n // a sidebar dispatch IN ADDITION to navigating. If a raw action is misconfigured\n // with both `href` and `onClick`/`actionId`, `href` wins and `onClick` is dropped.\n const onClick =\n raw.href != null\n ? undefined\n : explicitOnClick ?? (dispatchActionId ? () => triggerAction(dispatchActionId) : undefined);\n return {\n id: raw.id,\n label: raw.label,\n icon: raw.icon,\n href: raw.href,\n onClick,\n disabled: raw.disabled,\n danger: raw.variant === \"danger\",\n children: raw.children ? raw.children.map((c) => toDescriptor(c, triggerAction)) : undefined,\n };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nfunction sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nfunction matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { PageHeaderContext } from \"./pageHeaderContext\";\nimport { SidebarActionProvider } from \"./sidebarAction\";\nimport { buildBreadcrumbs } from \"./breadcrumbs\";\nimport type {\n HeaderDetailContext,\n HeaderMenuItem,\n PageHeaderState,\n RouteMeta,\n} from \"./pageHeaderTypes\";\n\nexport interface PageHeaderProviderProps {\n /**\n * The plugin's menu-item config — a faithful slice of the monolith's\n * `menuConfig.ts`. The provider matches the active route against each item's\n * `href` (pre-order, so a parent list item wins over its own overview child)\n * and publishes the match so `usePageHeader` can surface its `actions`.\n */\n config: HeaderMenuItem[];\n /**\n * The plugin's breadcrumb metadata — a faithful slice of the monolith's\n * `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives\n * the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like\n * the monolith. The app/module crumb (e.g. \"Finance\") is just its top-level entry.\n * Omit for no breadcrumbs.\n */\n routeMeta?: RouteMeta;\n /** Optional detail-context override for detail/edit routes with no menu item. */\n detailContext?: HeaderDetailContext | null;\n children: ReactNode;\n}\n\n/**\n * Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted\n * once at the plugin's router root, it resolves the active menu item from the\n * current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb\n * trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`\n * header CTAs can dispatch to page-registered handlers.\n *\n * In a plugin this feeds the in-body `PageHeader` that migrated pages render —\n * which IS the surface header (the host suppresses its own chrome for\n * platform-react surfaces).\n */\nexport function PageHeaderProvider({\n config,\n routeMeta,\n detailContext,\n children,\n}: PageHeaderProviderProps) {\n const { pathname } = useLocation();\n\n const state = useMemo<PageHeaderState>(() => {\n const activeMenuItem = findByHref(config, normalise(pathname));\n const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : undefined;\n // Default the in-body title to the breadcrumb LEAF label (the current route's\n // `routeMeta` label) so EVERY routed page shows a title — not just the ones that\n // happen to carry a menu item (list pages with CTAs). Only a real trail (Home +\n // ≥1 crumb) yields a leaf; a bare `[Home]` (no route match) leaves title unset so\n // the header stays chrome-only. A page hook can still override via its own\n // headerProps (e.g. a detail page swapping in the entity name).\n const title =\n breadcrumbs != null && breadcrumbs.length >= 2\n ? breadcrumbs[breadcrumbs.length - 1].label\n : undefined;\n return { activeMenuItem, detailContext, breadcrumbs, title };\n }, [config, routeMeta, pathname, detailContext]);\n\n return (\n <SidebarActionProvider>\n <PageHeaderContext.Provider value={state}>{children}</PageHeaderContext.Provider>\n </SidebarActionProvider>\n );\n}\n\n/**\n * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:\n * `withPageHeader(config, routeMeta, app)` returns\n * `(page) => <PageHeaderProvider …>{page}</>`.\n *\n * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on\n * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`\n * instance, which only holds when both come from this `components/layout` entry.\n * (A bundler inlines a separate context copy into each entry, so wrapping from the\n * `platform-react` entry would give the provider a different context than the\n * hook — the header would render empty.) A plugin passes\n * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`\n * to `createPluginPageDefiner`.\n */\nexport function withPageHeader(\n config: HeaderMenuItem[],\n routeMeta?: RouteMeta,\n): (page: ReactElement) => ReactElement {\n return (page) => (\n <PageHeaderProvider config={config} routeMeta={routeMeta}>\n {page}\n </PageHeaderProvider>\n );\n}\n\n/** Strip a trailing slash (except root) so `/x/` and `/x` match the same item. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/**\n * Pre-order search for the item whose `href` exactly matches `pathname`. Pre-order\n * means a parent list item (which carries the `actions`) is returned before its\n * own same-href overview child, so the CTA surfaces on the list route.\n */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n"]}
1
+ {"version":3,"sources":["../../../src/components/layout/fillGridClasses.ts","../../../src/components/layout/FillGrid.tsx","../../../src/components/layout/PageHeader.tsx","../../../src/components/layout/pageHeaderContext.ts","../../../src/components/layout/sidebarAction.tsx","../../../src/components/layout/usePageHeader.ts","../../../src/components/layout/breadcrumbs.ts","../../../src/components/layout/PageHeaderProvider.tsx"],"names":["createContext","jsx","Box","useContext","Stack","Breadcrumbs","jsxs","Typography","Link","Button","useState","useRef","Fragment","Menu","MenuItem","IconButton","useCallback","useMemo","useEffect","useLocation"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Static Tailwind basis classes for {@link FillGridItem}.\n *\n * Tailwind's content scanner extracts class candidates from raw source text and\n * cannot follow string concatenation — `\"sm:\" + \"basis-[...]\"` is invisible to\n * it. So every fully-prefixed class is spelled out as a literal below, indexed\n * by breakpoint → gap → span. `fillItemClass` only ever *looks up* a literal; it\n * never builds one. This is what guarantees the responsive classes survive a\n * production purge (no fragile safelist comment to keep in sync).\n *\n * NOTE: because these literals live in the SDK, a CONSUMER's Tailwind build must\n * scan this package's dist (add it to `content`) or the classes won't generate.\n *\n * basis(n, g) = calc(n/12 * 100% - g * (1 - n/12)), g = gap in rem.\n * gap-5 => g = 1.25rem; gap-2.5 => g = 0.625rem. Span 12 => basis-full.\n */\nexport type Twelfths = 2.4 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12;\nexport type FillGap = 2.5 | 5;\ntype Breakpoint = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type FillSpan = Partial<Record<Breakpoint, Twelfths>>;\n\n/**\n * Full literal class per breakpoint × gap × span. `xs` is unprefixed; `sm`/`md`/\n * `lg` carry the Tailwind prefix. Every string here is a real, scannable literal.\n */\nconst CLASSES: Record<Breakpoint, Record<FillGap, Record<Twelfths, string>>> = {\n xs: {\n 5: {\n 2.4: \"basis-[calc(20%-1rem)]\",\n 3: \"basis-[calc(25%-0.9375rem)]\",\n 4: \"basis-[calc(33.333%-0.8333rem)]\",\n 5: \"basis-[calc(41.667%-0.7292rem)]\",\n 6: \"basis-[calc(50%-0.625rem)]\",\n 7: \"basis-[calc(58.333%-0.5208rem)]\",\n 8: \"basis-[calc(66.667%-0.4167rem)]\",\n 9: \"basis-[calc(75%-0.3125rem)]\",\n 12: \"basis-full\",\n },\n 2.5: {\n 2.4: \"basis-[calc(20%-0.5rem)]\",\n 3: \"basis-[calc(25%-0.46875rem)]\",\n 4: \"basis-[calc(33.333%-0.41667rem)]\",\n 5: \"basis-[calc(41.667%-0.36458rem)]\",\n 6: \"basis-[calc(50%-0.3125rem)]\",\n 7: \"basis-[calc(58.333%-0.26042rem)]\",\n 8: \"basis-[calc(66.667%-0.20833rem)]\",\n 9: \"basis-[calc(75%-0.15625rem)]\",\n 12: \"basis-full\",\n },\n },\n sm: {\n 5: {\n 2.4: \"sm:basis-[calc(20%-1rem)]\",\n 3: \"sm:basis-[calc(25%-0.9375rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"sm:basis-[calc(50%-0.625rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"sm:basis-[calc(75%-0.3125rem)]\",\n 12: \"sm:basis-full\",\n },\n 2.5: {\n 2.4: \"sm:basis-[calc(20%-0.5rem)]\",\n 3: \"sm:basis-[calc(25%-0.46875rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"sm:basis-[calc(50%-0.3125rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"sm:basis-[calc(75%-0.15625rem)]\",\n 12: \"sm:basis-full\",\n },\n },\n md: {\n 5: {\n 2.4: \"md:basis-[calc(20%-1rem)]\",\n 3: \"md:basis-[calc(25%-0.9375rem)]\",\n 4: \"md:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"md:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"md:basis-[calc(50%-0.625rem)]\",\n 7: \"md:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"md:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"md:basis-[calc(75%-0.3125rem)]\",\n 12: \"md:basis-full\",\n },\n 2.5: {\n 2.4: \"md:basis-[calc(20%-0.5rem)]\",\n 3: \"md:basis-[calc(25%-0.46875rem)]\",\n 4: \"md:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"md:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"md:basis-[calc(50%-0.3125rem)]\",\n 7: \"md:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"md:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"md:basis-[calc(75%-0.15625rem)]\",\n 12: \"md:basis-full\",\n },\n },\n lg: {\n 5: {\n 2.4: \"lg:basis-[calc(20%-1rem)]\",\n 3: \"lg:basis-[calc(25%-0.9375rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"lg:basis-[calc(50%-0.625rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"lg:basis-[calc(75%-0.3125rem)]\",\n 12: \"lg:basis-full\",\n },\n 2.5: {\n 2.4: \"lg:basis-[calc(20%-0.5rem)]\",\n 3: \"lg:basis-[calc(25%-0.46875rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"lg:basis-[calc(50%-0.3125rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"lg:basis-[calc(75%-0.15625rem)]\",\n 12: \"lg:basis-full\",\n },\n },\n};\n\nconst BREAKPOINTS: readonly Breakpoint[] = [\"xs\", \"sm\", \"md\", \"lg\"];\n\n/**\n * Full class string for one item: always `min-w-0 grow`, plus the literal basis\n * class for each supplied breakpoint (looked up, never concatenated).\n */\nexport function fillItemClass(span: FillSpan, gap: FillGap): string {\n const parts = [\"min-w-0\", \"grow\"];\n for (const bp of BREAKPOINTS) {\n const n = span[bp];\n if (n !== undefined) parts.push(CLASSES[bp][gap][n]);\n }\n return parts.join(\" \");\n}\n","import { createContext, useContext } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { fillItemClass, type FillGap, type FillSpan } from \"./fillGridClasses\";\n\nconst FillGapContext = createContext<FillGap>(5);\n\ninterface FillGridProps {\n /** Gutter width. 5 = 20px (default), 2.5 = 10px. Matches MUI spacing values. */\n gap?: FillGap;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A flex-wrap row whose items `grow` to fill a partially-filled last row — the\n * dashboard/overview convention. NOT for forms, tables, or single full-width cards.\n *\n * Renders `w-full` so it always spans its container: a `FillGrid` is itself often\n * a flex item of a parent MUI `<Grid container>` / flex row, where without an\n * explicit width it would shrink-wrap to its content instead of filling the row\n * (the behaviour a `<Grid container size={12}>` used to give).\n */\nexport function FillGrid({ gap = 5, className, children }: FillGridProps) {\n const gapClass = gap === 2.5 ? \"gap-2.5\" : \"gap-5\";\n return (\n <FillGapContext.Provider value={gap}>\n <Box className={[\"flex w-full flex-wrap\", gapClass, className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n </FillGapContext.Provider>\n );\n}\n\ninterface FillGridItemProps {\n /** 12-column span per breakpoint, mirroring MUI Grid's `size`. Default { xs: 12 }. */\n span?: FillSpan;\n className?: string;\n children: React.ReactNode;\n}\n\nexport function FillGridItem({ span = { xs: 12 }, className, children }: FillGridItemProps) {\n const gap = useContext(FillGapContext);\n return (\n <Box className={[fillItemClass(span, gap), className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n );\n}\n","import { useState, useRef, type ReactNode } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n Box,\n Breadcrumbs as MuiBreadcrumbs,\n Stack,\n Typography,\n Link as MuiLink,\n Button,\n IconButton,\n Menu,\n MenuItem,\n} from \"../ui\";\nimport type { ActionDescriptor, Breadcrumb } from \"./pageHeaderTypes\";\n\n/**\n * `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the\n * monolith so plugins share one component instead of copying it. In a plugin this\n * IS the page header: the host suppresses its own chrome for platform-react\n * surfaces, so this renders the breadcrumb trail, the title, and the CTAs.\n *\n * `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is\n * appended as the non-link leaf crumb. MUI primitives come from the sibling\n * `../ui` barrel (host externals, never bundled); `Link` is react-router's so\n * links keep middle-click / open-in-new-tab / a11y.\n */\nexport interface PageHeaderProps {\n title: string;\n subtitle?: ReactNode;\n primaryAction?: ActionDescriptor;\n secondaryAction?: ActionDescriptor;\n overflowActions?: ActionDescriptor[];\n /** Root breadcrumb trail; the `title` is appended as the current-page leaf. */\n breadcrumbs?: Breadcrumb[];\n}\n\nexport function PageHeader({\n title,\n subtitle,\n primaryAction,\n secondaryAction,\n overflowActions,\n breadcrumbs,\n}: PageHeaderProps) {\n const hasActions =\n primaryAction != null ||\n secondaryAction != null ||\n (overflowActions != null && overflowActions.length > 0);\n\n return (\n <Stack direction=\"column\" spacing={0.5} className=\"mb-4\">\n {breadcrumbs != null && breadcrumbs.length > 0 && <Breadcrumbs trail={breadcrumbs} />}\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" spacing={2}>\n <Box>\n {/* Title is optional: in a plugin the host renders the surface title,\n so list pages pass an empty title and only their CTA shows. Pages\n that want an in-body title (detail/create/edit) set it explicitly. */}\n {title !== \"\" && (\n <Typography variant=\"h4\" component=\"h1\">\n {title}\n </Typography>\n )}\n {subtitle != null && (\n <Typography variant=\"body2\" color=\"text.secondary\" className=\"mt-1\">\n {subtitle}\n </Typography>\n )}\n </Box>\n {hasActions && (\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n data-testid=\"page-header-actions\"\n >\n {overflowActions != null && overflowActions.length > 0 && (\n <OverflowMenu actions={overflowActions} />\n )}\n {secondaryAction != null && <SecondaryDropdown action={secondaryAction} />}\n {primaryAction != null && <PrimaryButton action={primaryAction} />}\n </Stack>\n )}\n </Stack>\n </Stack>\n );\n}\n\n/**\n * Breadcrumb trail — mirrors the host's `gogo-ui` `PageBreadcrumbs` exactly (MUI\n * `Breadcrumbs` with the same markup/props) so the plugin surface looks identical\n * to host-rendered chrome: `text.secondary` links (`underline=\"hover\"`) up to the\n * current page, a `text.primary` leaf, `variant=\"body2\"`, `mb-2 px-1`.\n */\nfunction Breadcrumbs({ trail }: { trail: Breadcrumb[] }) {\n if (trail.length < 2) return null;\n return (\n <MuiBreadcrumbs aria-label=\"breadcrumb\" className=\"mb-2 px-1\">\n {trail.map((crumb, index) =>\n index < trail.length - 1 ? (\n <MuiLink\n key={crumb.href ?? crumb.label}\n component={Link}\n to={crumb.href ?? \"/\"}\n underline=\"hover\"\n color=\"text.secondary\"\n variant=\"body2\"\n >\n {crumb.label}\n </MuiLink>\n ) : (\n <Typography key={crumb.label} color=\"text.primary\" variant=\"body2\">\n {crumb.label}\n </Typography>\n ),\n )}\n </MuiBreadcrumbs>\n );\n}\n\nfunction PrimaryButton({ action }: { action: ActionDescriptor }) {\n const common = {\n \"data-danger\": action.danger ? \"true\" : undefined,\n disabled: action.disabled,\n } as const;\n if (action.href != null) {\n return (\n <Button\n component={Link}\n to={action.href}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n onClick={action.onClick}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n}\n\nfunction useMenuState() {\n const [open, setOpen] = useState(false);\n const anchorRef = useRef<HTMLButtonElement | null>(null);\n return {\n open,\n anchorRef,\n toggle: () => setOpen((v) => !v),\n close: () => setOpen(false),\n };\n}\n\nfunction SecondaryDropdown({ action }: { action: ActionDescriptor }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n const children = action.children ?? [];\n\n return (\n <>\n <Button\n ref={anchorRef}\n variant=\"outlined\"\n onClick={toggle}\n disabled={action.disabled || children.length === 0}\n aria-haspopup=\"menu\"\n aria-expanded={open || undefined}\n aria-label={action.label}\n >\n {action.label} ▾\n </Button>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {children.map((child) => (\n // href children render as anchors (component={Link}) to keep\n // middle-click / open-in-new-tab / a11y. `href` and `onClick` are\n // mutually exclusive on ActionDescriptor, so one MenuItem covers both.\n <MenuItem\n key={child.id}\n {...(child.href != null ? { component: Link, to: child.href } : {})}\n onClick={() => {\n close();\n child.onClick?.();\n }}\n disabled={child.disabled}\n >\n {child.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n\nfunction OverflowMenu({ actions }: { actions: ActionDescriptor[] }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n return (\n <>\n <IconButton\n ref={anchorRef}\n size=\"small\"\n aria-label=\"More actions\"\n onClick={toggle}\n >\n {\"⋯\"}\n </IconButton>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {actions.map((a) => (\n // href items render as anchors (component={Link}) — like\n // PrimaryButton — to keep middle-click / open-in-new-tab / a11y.\n // `href` and `onClick` are mutually exclusive on ActionDescriptor,\n // so one MenuItem covers both.\n <MenuItem\n key={a.id}\n {...(a.href != null ? { component: Link, to: a.href } : {})}\n onClick={() => {\n close();\n a.onClick?.();\n }}\n disabled={a.disabled}\n sx={a.danger ? { color: \"error.main\" } : undefined}\n >\n {a.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { PageHeaderState } from \"./pageHeaderTypes\";\n\n/**\n * Carries the active menu item / detail context so `usePageHeader` can derive\n * header props. The plugin analogue of the monolith shell's\n * `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.\n *\n * `null` when no provider is mounted (a page rendered standalone, e.g. a test) —\n * consumers treat `null` as \"no header context\" and derive empty props.\n */\nexport const PageHeaderContext = createContext<PageHeaderState | null>(null);\n\n/** Returns the provider-published header state, or `null` when none is mounted. */\nexport function usePageHeaderContext(): PageHeaderState | null {\n return useContext(PageHeaderContext);\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\n/**\n * Sidebar-action dispatch channel — promoted from the monolith's\n * `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an\n * `href`) dispatch through here; a page registers a handler via\n * `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs\n * (\"New Supplier Invoice\", \"New Cost Code\", …) fire when clicked.\n */\n\nexport type ActionHandler = (actionId: string) => void;\n\nexport interface SidebarActionContextValue {\n /** Dispatch an action to all registered page handlers. */\n triggerAction: (actionId: string) => void;\n /** Register a handler that receives sidebar actions. Returns an unregister function. */\n registerHandler: (handler: ActionHandler) => () => void;\n /** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */\n activeActions: ReadonlySet<string>;\n /** Reports whether the owning UI surface for `actionId` is currently open. */\n setActionActive: (actionId: string, isActive: boolean) => void;\n}\n\nexport const SidebarActionContext = createContext<SidebarActionContextValue>({\n triggerAction: () => {},\n registerHandler: () => () => {},\n activeActions: new Set(),\n setActionActive: () => {},\n});\n\nexport function SidebarActionProvider({ children }: { children: ReactNode }) {\n const handlersRef = useRef(new Set<ActionHandler>());\n const [activeActions, setActiveActions] = useState<ReadonlySet<string>>(() => new Set());\n\n const triggerAction = useCallback((actionId: string) => {\n for (const handler of handlersRef.current) {\n handler(actionId);\n }\n }, []);\n\n const registerHandler = useCallback((handler: ActionHandler) => {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n }, []);\n\n const setActionActive = useCallback((actionId: string, isActive: boolean) => {\n setActiveActions((prev) => {\n const has = prev.has(actionId);\n if (isActive === has) return prev;\n const next = new Set(prev);\n if (isActive) next.add(actionId);\n else next.delete(actionId);\n return next;\n });\n }, []);\n\n const value = useMemo(\n () => ({ triggerAction, registerHandler, activeActions, setActionActive }),\n [triggerAction, registerHandler, activeActions, setActionActive],\n );\n\n return <SidebarActionContext.Provider value={value}>{children}</SidebarActionContext.Provider>;\n}\n\n/** Used by the header to dispatch actions to pages. */\nexport function useTriggerSidebarAction(): (actionId: string) => void {\n return useContext(SidebarActionContext).triggerAction;\n}\n\n/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */\nexport function useSidebarAction(handler: ActionHandler): void {\n const { registerHandler } = useContext(SidebarActionContext);\n useEffect(() => registerHandler(handler), [registerHandler, handler]);\n}\n\n/**\n * Registers a map of sidebar-action handlers keyed by action id, dispatching the\n * matching entry when an action fires (unknown ids are ignored). The handler map\n * may change every render — a ref keeps the dispatcher stable so the registration\n * doesn't churn.\n */\nexport function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void {\n const handlersRef = useRef(handlers);\n handlersRef.current = handlers;\n useSidebarAction(\n useCallback((actionId: string) => {\n handlersRef.current[actionId]?.();\n }, []),\n );\n}\n\n/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */\nexport function useActiveSidebarActions(): ReadonlySet<string> {\n return useContext(SidebarActionContext).activeActions;\n}\n\n/**\n * Reports to the channel whether the owning UI surface for `actionId` is open.\n * Pages call this from a hook scope, mirroring their dialog/drawer open state.\n */\nexport function useReportSidebarActionActive(actionId: string, isActive: boolean): void {\n const { setActionActive } = useContext(SidebarActionContext);\n useEffect(() => {\n setActionActive(actionId, isActive);\n return () => {\n setActionActive(actionId, false);\n };\n }, [actionId, isActive, setActionActive]);\n}\n","import { useMemo } from \"react\";\nimport { usePageHeaderContext } from \"./pageHeaderContext\";\nimport { useTriggerSidebarAction } from \"./sidebarAction\";\nimport type {\n ActionDescriptor,\n ActionSlot,\n HeaderAction,\n PageHeaderDerivedProps,\n} from \"./pageHeaderTypes\";\n\n/**\n * Derives `<PageHeader>` props from the active menu item or detail context —\n * promoted verbatim from the monolith's `usePageHeader`, reading the plugin's\n * `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.\n *\n * Outside a mounted `PageHeaderProvider` (or in a test rendering a page\n * standalone) the context is `null` and the hook derives empty header props —\n * same shape, no work. Page hooks spread the result and may override\n * title/subtitle/actions.\n */\nexport function usePageHeader(): PageHeaderDerivedProps {\n const state = usePageHeaderContext();\n const { activeMenuItem, detailContext, title, subtitle, breadcrumbs } = state ?? {};\n // Dispatcher for actionId-only actions — the same channel a page registers\n // against via `useSidebarAction`. Threading it into `toDescriptor` is what\n // makes header buttons like `+ New Supplier Invoice` (which carry an\n // `actionId` and no `href`) open the page-owned dialog when clicked.\n const triggerAction = useTriggerSidebarAction();\n\n return useMemo(() => {\n const sourceActions: HeaderAction[] =\n detailContext?.actions ?? activeMenuItem?.actions ?? [];\n\n let primary: ActionDescriptor | undefined;\n let secondary: ActionDescriptor | undefined;\n const overflow: ActionDescriptor[] = [];\n\n for (const action of sourceActions) {\n const descriptor = toDescriptor(action, triggerAction);\n const slot: ActionSlot = action.slot ?? \"sidebar-only\";\n switch (slot) {\n case \"primary\":\n if (primary == null) {\n primary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple primary slots on this page; demoting later ones to overflow\",\n { keptId: primary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"secondary\":\n if (secondary == null) {\n secondary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple secondary slots on this page; demoting later ones to overflow\",\n { keptId: secondary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"overflow\":\n overflow.push(descriptor);\n break;\n case \"sidebar-only\":\n default:\n // Not header-visible.\n break;\n }\n }\n\n return {\n // The plugin's `PageHeader` is the sole header (the host suppresses its own\n // chrome for platform-react surfaces), so it owns the title: a list page\n // shows its menu label; detail/create/edit pages override with their own.\n title: title ?? detailContext?.title ?? activeMenuItem?.label ?? \"\",\n // Fall back to the detail context's subtitle so a wrapper-rendered header\n // (no page spreading its own `headerProps`) still surfaces the entity name.\n subtitle: subtitle ?? detailContext?.subtitle,\n primaryAction: primary,\n secondaryAction: secondary,\n overflowActions: overflow.length > 0 ? overflow : undefined,\n breadcrumbs,\n };\n }, [activeMenuItem, detailContext, title, subtitle, breadcrumbs, triggerAction]);\n}\n\n/**\n * Map a raw header source action to `PageHeader`'s `ActionDescriptor`.\n *\n * Resolution order for `onClick`:\n * 1. Explicit `raw.onClick` (detail-context actions can carry one directly).\n * 2. `raw.actionId` → dispatch via the sidebar-action channel. This is what\n * makes menu-config actions like `actionId: \"create-cost-code\"` (no `href`,\n * no `onClick`) fire when the header button is clicked — the page registers\n * a handler via `useSidebarAction`.\n * 3. Neither — the button still renders, but is a no-op (typically paired with\n * `href` so the primary button renders a Link instead).\n *\n * The trigger is threaded down explicitly rather than via a hook because this\n * runs inside `useMemo` and can recurse (for secondary-dropdown children).\n */\nfunction toDescriptor(\n raw: HeaderAction,\n triggerAction: (actionId: string) => void,\n): ActionDescriptor {\n const explicitOnClick = raw.onClick;\n const dispatchActionId = raw.actionId;\n // `href` and `onClick` are mutually exclusive on ActionDescriptor. A navigating\n // action (`href`) must NOT also carry an `onClick`, or the Link-rendering menu\n // items (SecondaryDropdown / OverflowMenu always call `child.onClick`) would fire\n // a sidebar dispatch IN ADDITION to navigating. If a raw action is misconfigured\n // with both `href` and `onClick`/`actionId`, `href` wins and `onClick` is dropped.\n const onClick =\n raw.href != null\n ? undefined\n : explicitOnClick ?? (dispatchActionId ? () => triggerAction(dispatchActionId) : undefined);\n return {\n id: raw.id,\n label: raw.label,\n icon: raw.icon,\n href: raw.href,\n onClick,\n disabled: raw.disabled,\n danger: raw.variant === \"danger\",\n children: raw.children ? raw.children.map((c) => toDescriptor(c, triggerAction)) : undefined,\n };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nfunction sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nfunction matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { PageHeaderContext } from \"./pageHeaderContext\";\nimport { SidebarActionProvider } from \"./sidebarAction\";\nimport { buildBreadcrumbs } from \"./breadcrumbs\";\nimport { PageHeader } from \"./PageHeader\";\nimport { usePageHeader } from \"./usePageHeader\";\nimport type {\n HeaderDetailContext,\n HeaderMenuItem,\n PageHeaderState,\n RouteMeta,\n} from \"./pageHeaderTypes\";\n\n/**\n * Default detail-context resolver: no detail context. Kept as a stable named hook\n * so `PageHeaderProvider` can call the resolver UNCONDITIONALLY (a hook can't be\n * called behind a runtime branch), whether or not a plugin supplied one.\n */\nfunction useNoDetailContext(): HeaderDetailContext | null {\n return null;\n}\n\n/**\n * The wrapper-rendered header. Mounted by `PageHeaderProvider` ONLY when the\n * plugin's `useDetailContext` resolver returns a context for the active route.\n *\n * This is the plugin equivalent of a monolith NESTED-LAYOUT ROUTE — a layout that\n * renders one shared `<PageHeader>` above the `<Outlet>` for a set of content-only\n * sub-pages (e.g. a project/ticket detail subtree). A PlatformReact plugin has no\n * router outlet (each page is its own bundle), so the page-definer wrapper is the\n * only shared seam: the resolver says \"these routes have no self-rendered header,\n * here's their shared header context\" and this renders it. Reads the same\n * `usePageHeader` a self-rendering page would, so title/subtitle/CTAs are derived\n * identically; the resolver's context is already folded into the provider state.\n */\nfunction ChromeHeader(): ReactElement {\n return <PageHeader {...usePageHeader()} />;\n}\n\nexport interface PageHeaderProviderProps {\n /**\n * The plugin's menu-item config — a faithful slice of the monolith's\n * `menuConfig.ts`. The provider matches the active route against each item's\n * `href` (pre-order, so a parent list item wins over its own overview child)\n * and publishes the match so `usePageHeader` can surface its `actions`.\n */\n config: HeaderMenuItem[];\n /**\n * The plugin's breadcrumb metadata — a faithful slice of the monolith's\n * `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives\n * the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like\n * the monolith. The app/module crumb (e.g. \"Finance\") is just its top-level entry.\n * Omit for no breadcrumbs.\n */\n routeMeta?: RouteMeta;\n /** Optional detail-context override for detail/edit routes with no menu item. */\n detailContext?: HeaderDetailContext | null;\n /**\n * Optional per-route detail-context RESOLVER hook. Called every render; when it\n * returns a context, the provider (a) folds it into the header state AND (b)\n * renders the header ITSELF (`<ChromeHeader>`) above the page — for detail\n * routes whose migrated page renders no `<PageHeader>` of its own. Returns\n * `null` for routes the page already headers (list/create/edit self-render), so\n * chrome never double-renders. Takes precedence over the static `detailContext`.\n */\n useDetailContext?: () => HeaderDetailContext | null;\n children: ReactNode;\n}\n\n/**\n * Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted\n * once at the plugin's router root, it resolves the active menu item from the\n * current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb\n * trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`\n * header CTAs can dispatch to page-registered handlers.\n *\n * In a plugin this feeds the in-body `PageHeader` that migrated pages render —\n * which IS the surface header (the host suppresses its own chrome for\n * platform-react surfaces).\n */\nexport function PageHeaderProvider({\n config,\n routeMeta,\n detailContext,\n useDetailContext = useNoDetailContext,\n children,\n}: PageHeaderProviderProps) {\n const { pathname } = useLocation();\n // Resolve the active detail context. The resolver hook is ALWAYS called (the\n // default is a stable no-op) so hook order stays stable. When a resolver was\n // actually supplied it is AUTHORITATIVE: its result wins — including `null`,\n // which means \"suppress chrome for this route\" and must NOT fall back to the\n // static prop. The static `detailContext` is only the fallback when no resolver\n // was passed (the legacy direct-provider API).\n const resolvedFromResolver = useDetailContext();\n const resolvedDetailContext =\n useDetailContext === useNoDetailContext ? detailContext ?? null : resolvedFromResolver;\n\n const state = useMemo<PageHeaderState>(() => {\n const activeMenuItem = findByHref(config, normalise(pathname));\n const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : undefined;\n // Default the in-body title to the breadcrumb LEAF label (the current route's\n // `routeMeta` label) so EVERY routed page shows a title — not just the ones that\n // happen to carry a menu item (list pages with CTAs). Only a real trail (Home +\n // ≥1 crumb) yields a leaf; a bare `[Home]` (no route match) leaves title unset so\n // the header stays chrome-only. A page hook can still override via its own\n // headerProps (e.g. a detail page swapping in the entity name).\n const title =\n breadcrumbs != null && breadcrumbs.length >= 2\n ? breadcrumbs[breadcrumbs.length - 1].label\n : undefined;\n return { activeMenuItem, detailContext: resolvedDetailContext, breadcrumbs, title };\n }, [config, routeMeta, pathname, resolvedDetailContext]);\n\n return (\n <SidebarActionProvider>\n <PageHeaderContext.Provider value={state}>\n {resolvedDetailContext != null ? <ChromeHeader /> : null}\n {children}\n </PageHeaderContext.Provider>\n </SidebarActionProvider>\n );\n}\n\n/**\n * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:\n * `withPageHeader(config, routeMeta, useDetailContext?)` returns\n * `(page) => <PageHeaderProvider …>{page}</PageHeaderProvider>`.\n *\n * Pass the optional `useDetailContext` resolver to have the provider render a\n * shared header for detail routes whose page renders none of its own (see\n * {@link PageHeaderProviderProps.useDetailContext}); omit it for the original\n * behaviour (provider supplies context only, each page self-renders `<PageHeader>`).\n *\n * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on\n * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`\n * instance, which only holds when both come from this `components/layout` entry.\n * (A bundler inlines a separate context copy into each entry, so wrapping from the\n * `platform-react` entry would give the provider a different context than the\n * hook — the header would render empty.) A plugin passes\n * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`\n * (optionally a third resolver arg) to `createPluginPageDefiner`.\n */\nexport function withPageHeader(\n config: HeaderMenuItem[],\n routeMeta?: RouteMeta,\n useDetailContext?: () => HeaderDetailContext | null,\n): (page: ReactElement) => ReactElement {\n return (page) => (\n <PageHeaderProvider config={config} routeMeta={routeMeta} useDetailContext={useDetailContext}>\n {page}\n </PageHeaderProvider>\n );\n}\n\n/** Strip a trailing slash (except root) so `/x/` and `/x` match the same item. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/**\n * Pre-order search for the item whose `href` exactly matches `pathname`. Pre-order\n * means a parent list item (which carries the `actions`) is returned before its\n * own same-href overview child, so the CTA surfaces on the list route.\n */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n"]}
@@ -134,6 +134,13 @@ interface HeaderMenuItem {
134
134
  */
135
135
  interface HeaderDetailContext {
136
136
  title?: string;
137
+ /**
138
+ * Optional subtitle (e.g. the entity name) shown under the title. Read by
139
+ * `usePageHeader` so a wrapper-rendered header (see
140
+ * {@link PageHeaderProviderProps.useDetailContext}) can surface it without the
141
+ * page spreading its own `headerProps`.
142
+ */
143
+ subtitle?: ReactNode;
137
144
  actions?: HeaderAction[];
138
145
  }
139
146
  /** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
@@ -251,6 +258,15 @@ interface PageHeaderProviderProps {
251
258
  routeMeta?: RouteMeta;
252
259
  /** Optional detail-context override for detail/edit routes with no menu item. */
253
260
  detailContext?: HeaderDetailContext | null;
261
+ /**
262
+ * Optional per-route detail-context RESOLVER hook. Called every render; when it
263
+ * returns a context, the provider (a) folds it into the header state AND (b)
264
+ * renders the header ITSELF (`<ChromeHeader>`) above the page — for detail
265
+ * routes whose migrated page renders no `<PageHeader>` of its own. Returns
266
+ * `null` for routes the page already headers (list/create/edit self-render), so
267
+ * chrome never double-renders. Takes precedence over the static `detailContext`.
268
+ */
269
+ useDetailContext?: () => HeaderDetailContext | null;
254
270
  children: ReactNode;
255
271
  }
256
272
  /**
@@ -264,11 +280,16 @@ interface PageHeaderProviderProps {
264
280
  * which IS the surface header (the host suppresses its own chrome for
265
281
  * platform-react surfaces).
266
282
  */
267
- declare function PageHeaderProvider({ config, routeMeta, detailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
283
+ declare function PageHeaderProvider({ config, routeMeta, detailContext, useDetailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
268
284
  /**
269
285
  * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:
270
- * `withPageHeader(config, routeMeta, app)` returns
271
- * `(page) => <PageHeaderProvider …>{page}</>`.
286
+ * `withPageHeader(config, routeMeta, useDetailContext?)` returns
287
+ * `(page) => <PageHeaderProvider …>{page}</PageHeaderProvider>`.
288
+ *
289
+ * Pass the optional `useDetailContext` resolver to have the provider render a
290
+ * shared header for detail routes whose page renders none of its own (see
291
+ * {@link PageHeaderProviderProps.useDetailContext}); omit it for the original
292
+ * behaviour (provider supplies context only, each page self-renders `<PageHeader>`).
272
293
  *
273
294
  * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on
274
295
  * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`
@@ -277,9 +298,9 @@ declare function PageHeaderProvider({ config, routeMeta, detailContext, children
277
298
  * `platform-react` entry would give the provider a different context than the
278
299
  * hook — the header would render empty.) A plugin passes
279
300
  * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`
280
- * to `createPluginPageDefiner`.
301
+ * (optionally a third resolver arg) to `createPluginPageDefiner`.
281
302
  */
282
- declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta): (page: ReactElement) => ReactElement;
303
+ declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta, useDetailContext?: () => HeaderDetailContext | null): (page: ReactElement) => ReactElement;
283
304
 
284
305
  /**
285
306
  * Carries the active menu item / detail context so `usePageHeader` can derive
@@ -134,6 +134,13 @@ interface HeaderMenuItem {
134
134
  */
135
135
  interface HeaderDetailContext {
136
136
  title?: string;
137
+ /**
138
+ * Optional subtitle (e.g. the entity name) shown under the title. Read by
139
+ * `usePageHeader` so a wrapper-rendered header (see
140
+ * {@link PageHeaderProviderProps.useDetailContext}) can surface it without the
141
+ * page spreading its own `headerProps`.
142
+ */
143
+ subtitle?: ReactNode;
137
144
  actions?: HeaderAction[];
138
145
  }
139
146
  /** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
@@ -251,6 +258,15 @@ interface PageHeaderProviderProps {
251
258
  routeMeta?: RouteMeta;
252
259
  /** Optional detail-context override for detail/edit routes with no menu item. */
253
260
  detailContext?: HeaderDetailContext | null;
261
+ /**
262
+ * Optional per-route detail-context RESOLVER hook. Called every render; when it
263
+ * returns a context, the provider (a) folds it into the header state AND (b)
264
+ * renders the header ITSELF (`<ChromeHeader>`) above the page — for detail
265
+ * routes whose migrated page renders no `<PageHeader>` of its own. Returns
266
+ * `null` for routes the page already headers (list/create/edit self-render), so
267
+ * chrome never double-renders. Takes precedence over the static `detailContext`.
268
+ */
269
+ useDetailContext?: () => HeaderDetailContext | null;
254
270
  children: ReactNode;
255
271
  }
256
272
  /**
@@ -264,11 +280,16 @@ interface PageHeaderProviderProps {
264
280
  * which IS the surface header (the host suppresses its own chrome for
265
281
  * platform-react surfaces).
266
282
  */
267
- declare function PageHeaderProvider({ config, routeMeta, detailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
283
+ declare function PageHeaderProvider({ config, routeMeta, detailContext, useDetailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
268
284
  /**
269
285
  * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:
270
- * `withPageHeader(config, routeMeta, app)` returns
271
- * `(page) => <PageHeaderProvider …>{page}</>`.
286
+ * `withPageHeader(config, routeMeta, useDetailContext?)` returns
287
+ * `(page) => <PageHeaderProvider …>{page}</PageHeaderProvider>`.
288
+ *
289
+ * Pass the optional `useDetailContext` resolver to have the provider render a
290
+ * shared header for detail routes whose page renders none of its own (see
291
+ * {@link PageHeaderProviderProps.useDetailContext}); omit it for the original
292
+ * behaviour (provider supplies context only, each page self-renders `<PageHeader>`).
272
293
  *
273
294
  * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on
274
295
  * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`
@@ -277,9 +298,9 @@ declare function PageHeaderProvider({ config, routeMeta, detailContext, children
277
298
  * `platform-react` entry would give the provider a different context than the
278
299
  * hook — the header would render empty.) A plugin passes
279
300
  * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`
280
- * to `createPluginPageDefiner`.
301
+ * (optionally a third resolver arg) to `createPluginPageDefiner`.
281
302
  */
282
- declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta): (page: ReactElement) => ReactElement;
303
+ declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta, useDetailContext?: () => HeaderDetailContext | null): (page: ReactElement) => ReactElement;
283
304
 
284
305
  /**
285
306
  * Carries the active menu item / detail context so `usePageHeader` can derive
@@ -404,7 +404,9 @@ function usePageHeader() {
404
404
  // chrome for platform-react surfaces), so it owns the title: a list page
405
405
  // shows its menu label; detail/create/edit pages override with their own.
406
406
  title: title ?? detailContext?.title ?? activeMenuItem?.label ?? "",
407
- subtitle,
407
+ // Fall back to the detail context's subtitle so a wrapper-rendered header
408
+ // (no page spreading its own `headerProps`) still surfaces the entity name.
409
+ subtitle: subtitle ?? detailContext?.subtitle,
408
410
  primaryAction: primary,
409
411
  secondaryAction: secondary,
410
412
  overflowActions: overflow.length > 0 ? overflow : void 0,
@@ -498,23 +500,35 @@ function buildBreadcrumbs(pathname, routeMeta) {
498
500
  });
499
501
  return crumbs;
500
502
  }
503
+ function useNoDetailContext() {
504
+ return null;
505
+ }
506
+ function ChromeHeader() {
507
+ return /* @__PURE__ */ jsx(PageHeader, { ...usePageHeader() });
508
+ }
501
509
  function PageHeaderProvider({
502
510
  config,
503
511
  routeMeta,
504
512
  detailContext,
513
+ useDetailContext = useNoDetailContext,
505
514
  children
506
515
  }) {
507
516
  const { pathname } = useLocation();
517
+ const resolvedFromResolver = useDetailContext();
518
+ const resolvedDetailContext = useDetailContext === useNoDetailContext ? detailContext ?? null : resolvedFromResolver;
508
519
  const state = useMemo(() => {
509
520
  const activeMenuItem = findByHref(config, normalise(pathname));
510
521
  const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : void 0;
511
522
  const title = breadcrumbs != null && breadcrumbs.length >= 2 ? breadcrumbs[breadcrumbs.length - 1].label : void 0;
512
- return { activeMenuItem, detailContext, breadcrumbs, title };
513
- }, [config, routeMeta, pathname, detailContext]);
514
- return /* @__PURE__ */ jsx(SidebarActionProvider, { children: /* @__PURE__ */ jsx(PageHeaderContext.Provider, { value: state, children }) });
523
+ return { activeMenuItem, detailContext: resolvedDetailContext, breadcrumbs, title };
524
+ }, [config, routeMeta, pathname, resolvedDetailContext]);
525
+ return /* @__PURE__ */ jsx(SidebarActionProvider, { children: /* @__PURE__ */ jsxs(PageHeaderContext.Provider, { value: state, children: [
526
+ resolvedDetailContext != null ? /* @__PURE__ */ jsx(ChromeHeader, {}) : null,
527
+ children
528
+ ] }) });
515
529
  }
516
- function withPageHeader(config, routeMeta) {
517
- return (page) => /* @__PURE__ */ jsx(PageHeaderProvider, { config, routeMeta, children: page });
530
+ function withPageHeader(config, routeMeta, useDetailContext) {
531
+ return (page) => /* @__PURE__ */ jsx(PageHeaderProvider, { config, routeMeta, useDetailContext, children: page });
518
532
  }
519
533
  function normalise(pathname) {
520
534
  return pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/layout/fillGridClasses.ts","../../../src/components/layout/FillGrid.tsx","../../../src/components/layout/PageHeader.tsx","../../../src/components/layout/pageHeaderContext.ts","../../../src/components/layout/sidebarAction.tsx","../../../src/components/layout/usePageHeader.ts","../../../src/components/layout/breadcrumbs.ts","../../../src/components/layout/PageHeaderProvider.tsx"],"names":["jsx","Breadcrumbs","Box","Link","createContext","useContext","useRef","useState","useMemo"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Static Tailwind basis classes for {@link FillGridItem}.\n *\n * Tailwind's content scanner extracts class candidates from raw source text and\n * cannot follow string concatenation — `\"sm:\" + \"basis-[...]\"` is invisible to\n * it. So every fully-prefixed class is spelled out as a literal below, indexed\n * by breakpoint → gap → span. `fillItemClass` only ever *looks up* a literal; it\n * never builds one. This is what guarantees the responsive classes survive a\n * production purge (no fragile safelist comment to keep in sync).\n *\n * NOTE: because these literals live in the SDK, a CONSUMER's Tailwind build must\n * scan this package's dist (add it to `content`) or the classes won't generate.\n *\n * basis(n, g) = calc(n/12 * 100% - g * (1 - n/12)), g = gap in rem.\n * gap-5 => g = 1.25rem; gap-2.5 => g = 0.625rem. Span 12 => basis-full.\n */\nexport type Twelfths = 2.4 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12;\nexport type FillGap = 2.5 | 5;\ntype Breakpoint = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type FillSpan = Partial<Record<Breakpoint, Twelfths>>;\n\n/**\n * Full literal class per breakpoint × gap × span. `xs` is unprefixed; `sm`/`md`/\n * `lg` carry the Tailwind prefix. Every string here is a real, scannable literal.\n */\nconst CLASSES: Record<Breakpoint, Record<FillGap, Record<Twelfths, string>>> = {\n xs: {\n 5: {\n 2.4: \"basis-[calc(20%-1rem)]\",\n 3: \"basis-[calc(25%-0.9375rem)]\",\n 4: \"basis-[calc(33.333%-0.8333rem)]\",\n 5: \"basis-[calc(41.667%-0.7292rem)]\",\n 6: \"basis-[calc(50%-0.625rem)]\",\n 7: \"basis-[calc(58.333%-0.5208rem)]\",\n 8: \"basis-[calc(66.667%-0.4167rem)]\",\n 9: \"basis-[calc(75%-0.3125rem)]\",\n 12: \"basis-full\",\n },\n 2.5: {\n 2.4: \"basis-[calc(20%-0.5rem)]\",\n 3: \"basis-[calc(25%-0.46875rem)]\",\n 4: \"basis-[calc(33.333%-0.41667rem)]\",\n 5: \"basis-[calc(41.667%-0.36458rem)]\",\n 6: \"basis-[calc(50%-0.3125rem)]\",\n 7: \"basis-[calc(58.333%-0.26042rem)]\",\n 8: \"basis-[calc(66.667%-0.20833rem)]\",\n 9: \"basis-[calc(75%-0.15625rem)]\",\n 12: \"basis-full\",\n },\n },\n sm: {\n 5: {\n 2.4: \"sm:basis-[calc(20%-1rem)]\",\n 3: \"sm:basis-[calc(25%-0.9375rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"sm:basis-[calc(50%-0.625rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"sm:basis-[calc(75%-0.3125rem)]\",\n 12: \"sm:basis-full\",\n },\n 2.5: {\n 2.4: \"sm:basis-[calc(20%-0.5rem)]\",\n 3: \"sm:basis-[calc(25%-0.46875rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"sm:basis-[calc(50%-0.3125rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"sm:basis-[calc(75%-0.15625rem)]\",\n 12: \"sm:basis-full\",\n },\n },\n md: {\n 5: {\n 2.4: \"md:basis-[calc(20%-1rem)]\",\n 3: \"md:basis-[calc(25%-0.9375rem)]\",\n 4: \"md:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"md:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"md:basis-[calc(50%-0.625rem)]\",\n 7: \"md:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"md:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"md:basis-[calc(75%-0.3125rem)]\",\n 12: \"md:basis-full\",\n },\n 2.5: {\n 2.4: \"md:basis-[calc(20%-0.5rem)]\",\n 3: \"md:basis-[calc(25%-0.46875rem)]\",\n 4: \"md:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"md:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"md:basis-[calc(50%-0.3125rem)]\",\n 7: \"md:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"md:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"md:basis-[calc(75%-0.15625rem)]\",\n 12: \"md:basis-full\",\n },\n },\n lg: {\n 5: {\n 2.4: \"lg:basis-[calc(20%-1rem)]\",\n 3: \"lg:basis-[calc(25%-0.9375rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"lg:basis-[calc(50%-0.625rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"lg:basis-[calc(75%-0.3125rem)]\",\n 12: \"lg:basis-full\",\n },\n 2.5: {\n 2.4: \"lg:basis-[calc(20%-0.5rem)]\",\n 3: \"lg:basis-[calc(25%-0.46875rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"lg:basis-[calc(50%-0.3125rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"lg:basis-[calc(75%-0.15625rem)]\",\n 12: \"lg:basis-full\",\n },\n },\n};\n\nconst BREAKPOINTS: readonly Breakpoint[] = [\"xs\", \"sm\", \"md\", \"lg\"];\n\n/**\n * Full class string for one item: always `min-w-0 grow`, plus the literal basis\n * class for each supplied breakpoint (looked up, never concatenated).\n */\nexport function fillItemClass(span: FillSpan, gap: FillGap): string {\n const parts = [\"min-w-0\", \"grow\"];\n for (const bp of BREAKPOINTS) {\n const n = span[bp];\n if (n !== undefined) parts.push(CLASSES[bp][gap][n]);\n }\n return parts.join(\" \");\n}\n","import { createContext, useContext } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { fillItemClass, type FillGap, type FillSpan } from \"./fillGridClasses\";\n\nconst FillGapContext = createContext<FillGap>(5);\n\ninterface FillGridProps {\n /** Gutter width. 5 = 20px (default), 2.5 = 10px. Matches MUI spacing values. */\n gap?: FillGap;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A flex-wrap row whose items `grow` to fill a partially-filled last row — the\n * dashboard/overview convention. NOT for forms, tables, or single full-width cards.\n *\n * Renders `w-full` so it always spans its container: a `FillGrid` is itself often\n * a flex item of a parent MUI `<Grid container>` / flex row, where without an\n * explicit width it would shrink-wrap to its content instead of filling the row\n * (the behaviour a `<Grid container size={12}>` used to give).\n */\nexport function FillGrid({ gap = 5, className, children }: FillGridProps) {\n const gapClass = gap === 2.5 ? \"gap-2.5\" : \"gap-5\";\n return (\n <FillGapContext.Provider value={gap}>\n <Box className={[\"flex w-full flex-wrap\", gapClass, className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n </FillGapContext.Provider>\n );\n}\n\ninterface FillGridItemProps {\n /** 12-column span per breakpoint, mirroring MUI Grid's `size`. Default { xs: 12 }. */\n span?: FillSpan;\n className?: string;\n children: React.ReactNode;\n}\n\nexport function FillGridItem({ span = { xs: 12 }, className, children }: FillGridItemProps) {\n const gap = useContext(FillGapContext);\n return (\n <Box className={[fillItemClass(span, gap), className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n );\n}\n","import { useState, useRef, type ReactNode } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n Box,\n Breadcrumbs as MuiBreadcrumbs,\n Stack,\n Typography,\n Link as MuiLink,\n Button,\n IconButton,\n Menu,\n MenuItem,\n} from \"../ui\";\nimport type { ActionDescriptor, Breadcrumb } from \"./pageHeaderTypes\";\n\n/**\n * `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the\n * monolith so plugins share one component instead of copying it. In a plugin this\n * IS the page header: the host suppresses its own chrome for platform-react\n * surfaces, so this renders the breadcrumb trail, the title, and the CTAs.\n *\n * `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is\n * appended as the non-link leaf crumb. MUI primitives come from the sibling\n * `../ui` barrel (host externals, never bundled); `Link` is react-router's so\n * links keep middle-click / open-in-new-tab / a11y.\n */\nexport interface PageHeaderProps {\n title: string;\n subtitle?: ReactNode;\n primaryAction?: ActionDescriptor;\n secondaryAction?: ActionDescriptor;\n overflowActions?: ActionDescriptor[];\n /** Root breadcrumb trail; the `title` is appended as the current-page leaf. */\n breadcrumbs?: Breadcrumb[];\n}\n\nexport function PageHeader({\n title,\n subtitle,\n primaryAction,\n secondaryAction,\n overflowActions,\n breadcrumbs,\n}: PageHeaderProps) {\n const hasActions =\n primaryAction != null ||\n secondaryAction != null ||\n (overflowActions != null && overflowActions.length > 0);\n\n return (\n <Stack direction=\"column\" spacing={0.5} className=\"mb-4\">\n {breadcrumbs != null && breadcrumbs.length > 0 && <Breadcrumbs trail={breadcrumbs} />}\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" spacing={2}>\n <Box>\n {/* Title is optional: in a plugin the host renders the surface title,\n so list pages pass an empty title and only their CTA shows. Pages\n that want an in-body title (detail/create/edit) set it explicitly. */}\n {title !== \"\" && (\n <Typography variant=\"h4\" component=\"h1\">\n {title}\n </Typography>\n )}\n {subtitle != null && (\n <Typography variant=\"body2\" color=\"text.secondary\" className=\"mt-1\">\n {subtitle}\n </Typography>\n )}\n </Box>\n {hasActions && (\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n data-testid=\"page-header-actions\"\n >\n {overflowActions != null && overflowActions.length > 0 && (\n <OverflowMenu actions={overflowActions} />\n )}\n {secondaryAction != null && <SecondaryDropdown action={secondaryAction} />}\n {primaryAction != null && <PrimaryButton action={primaryAction} />}\n </Stack>\n )}\n </Stack>\n </Stack>\n );\n}\n\n/**\n * Breadcrumb trail — mirrors the host's `gogo-ui` `PageBreadcrumbs` exactly (MUI\n * `Breadcrumbs` with the same markup/props) so the plugin surface looks identical\n * to host-rendered chrome: `text.secondary` links (`underline=\"hover\"`) up to the\n * current page, a `text.primary` leaf, `variant=\"body2\"`, `mb-2 px-1`.\n */\nfunction Breadcrumbs({ trail }: { trail: Breadcrumb[] }) {\n if (trail.length < 2) return null;\n return (\n <MuiBreadcrumbs aria-label=\"breadcrumb\" className=\"mb-2 px-1\">\n {trail.map((crumb, index) =>\n index < trail.length - 1 ? (\n <MuiLink\n key={crumb.href ?? crumb.label}\n component={Link}\n to={crumb.href ?? \"/\"}\n underline=\"hover\"\n color=\"text.secondary\"\n variant=\"body2\"\n >\n {crumb.label}\n </MuiLink>\n ) : (\n <Typography key={crumb.label} color=\"text.primary\" variant=\"body2\">\n {crumb.label}\n </Typography>\n ),\n )}\n </MuiBreadcrumbs>\n );\n}\n\nfunction PrimaryButton({ action }: { action: ActionDescriptor }) {\n const common = {\n \"data-danger\": action.danger ? \"true\" : undefined,\n disabled: action.disabled,\n } as const;\n if (action.href != null) {\n return (\n <Button\n component={Link}\n to={action.href}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n onClick={action.onClick}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n}\n\nfunction useMenuState() {\n const [open, setOpen] = useState(false);\n const anchorRef = useRef<HTMLButtonElement | null>(null);\n return {\n open,\n anchorRef,\n toggle: () => setOpen((v) => !v),\n close: () => setOpen(false),\n };\n}\n\nfunction SecondaryDropdown({ action }: { action: ActionDescriptor }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n const children = action.children ?? [];\n\n return (\n <>\n <Button\n ref={anchorRef}\n variant=\"outlined\"\n onClick={toggle}\n disabled={action.disabled || children.length === 0}\n aria-haspopup=\"menu\"\n aria-expanded={open || undefined}\n aria-label={action.label}\n >\n {action.label} ▾\n </Button>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {children.map((child) => (\n // href children render as anchors (component={Link}) to keep\n // middle-click / open-in-new-tab / a11y. `href` and `onClick` are\n // mutually exclusive on ActionDescriptor, so one MenuItem covers both.\n <MenuItem\n key={child.id}\n {...(child.href != null ? { component: Link, to: child.href } : {})}\n onClick={() => {\n close();\n child.onClick?.();\n }}\n disabled={child.disabled}\n >\n {child.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n\nfunction OverflowMenu({ actions }: { actions: ActionDescriptor[] }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n return (\n <>\n <IconButton\n ref={anchorRef}\n size=\"small\"\n aria-label=\"More actions\"\n onClick={toggle}\n >\n {\"⋯\"}\n </IconButton>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {actions.map((a) => (\n // href items render as anchors (component={Link}) — like\n // PrimaryButton — to keep middle-click / open-in-new-tab / a11y.\n // `href` and `onClick` are mutually exclusive on ActionDescriptor,\n // so one MenuItem covers both.\n <MenuItem\n key={a.id}\n {...(a.href != null ? { component: Link, to: a.href } : {})}\n onClick={() => {\n close();\n a.onClick?.();\n }}\n disabled={a.disabled}\n sx={a.danger ? { color: \"error.main\" } : undefined}\n >\n {a.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { PageHeaderState } from \"./pageHeaderTypes\";\n\n/**\n * Carries the active menu item / detail context so `usePageHeader` can derive\n * header props. The plugin analogue of the monolith shell's\n * `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.\n *\n * `null` when no provider is mounted (a page rendered standalone, e.g. a test) —\n * consumers treat `null` as \"no header context\" and derive empty props.\n */\nexport const PageHeaderContext = createContext<PageHeaderState | null>(null);\n\n/** Returns the provider-published header state, or `null` when none is mounted. */\nexport function usePageHeaderContext(): PageHeaderState | null {\n return useContext(PageHeaderContext);\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\n/**\n * Sidebar-action dispatch channel — promoted from the monolith's\n * `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an\n * `href`) dispatch through here; a page registers a handler via\n * `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs\n * (\"New Supplier Invoice\", \"New Cost Code\", …) fire when clicked.\n */\n\nexport type ActionHandler = (actionId: string) => void;\n\nexport interface SidebarActionContextValue {\n /** Dispatch an action to all registered page handlers. */\n triggerAction: (actionId: string) => void;\n /** Register a handler that receives sidebar actions. Returns an unregister function. */\n registerHandler: (handler: ActionHandler) => () => void;\n /** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */\n activeActions: ReadonlySet<string>;\n /** Reports whether the owning UI surface for `actionId` is currently open. */\n setActionActive: (actionId: string, isActive: boolean) => void;\n}\n\nexport const SidebarActionContext = createContext<SidebarActionContextValue>({\n triggerAction: () => {},\n registerHandler: () => () => {},\n activeActions: new Set(),\n setActionActive: () => {},\n});\n\nexport function SidebarActionProvider({ children }: { children: ReactNode }) {\n const handlersRef = useRef(new Set<ActionHandler>());\n const [activeActions, setActiveActions] = useState<ReadonlySet<string>>(() => new Set());\n\n const triggerAction = useCallback((actionId: string) => {\n for (const handler of handlersRef.current) {\n handler(actionId);\n }\n }, []);\n\n const registerHandler = useCallback((handler: ActionHandler) => {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n }, []);\n\n const setActionActive = useCallback((actionId: string, isActive: boolean) => {\n setActiveActions((prev) => {\n const has = prev.has(actionId);\n if (isActive === has) return prev;\n const next = new Set(prev);\n if (isActive) next.add(actionId);\n else next.delete(actionId);\n return next;\n });\n }, []);\n\n const value = useMemo(\n () => ({ triggerAction, registerHandler, activeActions, setActionActive }),\n [triggerAction, registerHandler, activeActions, setActionActive],\n );\n\n return <SidebarActionContext.Provider value={value}>{children}</SidebarActionContext.Provider>;\n}\n\n/** Used by the header to dispatch actions to pages. */\nexport function useTriggerSidebarAction(): (actionId: string) => void {\n return useContext(SidebarActionContext).triggerAction;\n}\n\n/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */\nexport function useSidebarAction(handler: ActionHandler): void {\n const { registerHandler } = useContext(SidebarActionContext);\n useEffect(() => registerHandler(handler), [registerHandler, handler]);\n}\n\n/**\n * Registers a map of sidebar-action handlers keyed by action id, dispatching the\n * matching entry when an action fires (unknown ids are ignored). The handler map\n * may change every render — a ref keeps the dispatcher stable so the registration\n * doesn't churn.\n */\nexport function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void {\n const handlersRef = useRef(handlers);\n handlersRef.current = handlers;\n useSidebarAction(\n useCallback((actionId: string) => {\n handlersRef.current[actionId]?.();\n }, []),\n );\n}\n\n/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */\nexport function useActiveSidebarActions(): ReadonlySet<string> {\n return useContext(SidebarActionContext).activeActions;\n}\n\n/**\n * Reports to the channel whether the owning UI surface for `actionId` is open.\n * Pages call this from a hook scope, mirroring their dialog/drawer open state.\n */\nexport function useReportSidebarActionActive(actionId: string, isActive: boolean): void {\n const { setActionActive } = useContext(SidebarActionContext);\n useEffect(() => {\n setActionActive(actionId, isActive);\n return () => {\n setActionActive(actionId, false);\n };\n }, [actionId, isActive, setActionActive]);\n}\n","import { useMemo } from \"react\";\nimport { usePageHeaderContext } from \"./pageHeaderContext\";\nimport { useTriggerSidebarAction } from \"./sidebarAction\";\nimport type {\n ActionDescriptor,\n ActionSlot,\n HeaderAction,\n PageHeaderDerivedProps,\n} from \"./pageHeaderTypes\";\n\n/**\n * Derives `<PageHeader>` props from the active menu item or detail context —\n * promoted verbatim from the monolith's `usePageHeader`, reading the plugin's\n * `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.\n *\n * Outside a mounted `PageHeaderProvider` (or in a test rendering a page\n * standalone) the context is `null` and the hook derives empty header props —\n * same shape, no work. Page hooks spread the result and may override\n * title/subtitle/actions.\n */\nexport function usePageHeader(): PageHeaderDerivedProps {\n const state = usePageHeaderContext();\n const { activeMenuItem, detailContext, title, subtitle, breadcrumbs } = state ?? {};\n // Dispatcher for actionId-only actions — the same channel a page registers\n // against via `useSidebarAction`. Threading it into `toDescriptor` is what\n // makes header buttons like `+ New Supplier Invoice` (which carry an\n // `actionId` and no `href`) open the page-owned dialog when clicked.\n const triggerAction = useTriggerSidebarAction();\n\n return useMemo(() => {\n const sourceActions: HeaderAction[] =\n detailContext?.actions ?? activeMenuItem?.actions ?? [];\n\n let primary: ActionDescriptor | undefined;\n let secondary: ActionDescriptor | undefined;\n const overflow: ActionDescriptor[] = [];\n\n for (const action of sourceActions) {\n const descriptor = toDescriptor(action, triggerAction);\n const slot: ActionSlot = action.slot ?? \"sidebar-only\";\n switch (slot) {\n case \"primary\":\n if (primary == null) {\n primary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple primary slots on this page; demoting later ones to overflow\",\n { keptId: primary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"secondary\":\n if (secondary == null) {\n secondary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple secondary slots on this page; demoting later ones to overflow\",\n { keptId: secondary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"overflow\":\n overflow.push(descriptor);\n break;\n case \"sidebar-only\":\n default:\n // Not header-visible.\n break;\n }\n }\n\n return {\n // The plugin's `PageHeader` is the sole header (the host suppresses its own\n // chrome for platform-react surfaces), so it owns the title: a list page\n // shows its menu label; detail/create/edit pages override with their own.\n title: title ?? detailContext?.title ?? activeMenuItem?.label ?? \"\",\n subtitle,\n primaryAction: primary,\n secondaryAction: secondary,\n overflowActions: overflow.length > 0 ? overflow : undefined,\n breadcrumbs,\n };\n }, [activeMenuItem, detailContext, title, subtitle, breadcrumbs, triggerAction]);\n}\n\n/**\n * Map a raw header source action to `PageHeader`'s `ActionDescriptor`.\n *\n * Resolution order for `onClick`:\n * 1. Explicit `raw.onClick` (detail-context actions can carry one directly).\n * 2. `raw.actionId` → dispatch via the sidebar-action channel. This is what\n * makes menu-config actions like `actionId: \"create-cost-code\"` (no `href`,\n * no `onClick`) fire when the header button is clicked — the page registers\n * a handler via `useSidebarAction`.\n * 3. Neither — the button still renders, but is a no-op (typically paired with\n * `href` so the primary button renders a Link instead).\n *\n * The trigger is threaded down explicitly rather than via a hook because this\n * runs inside `useMemo` and can recurse (for secondary-dropdown children).\n */\nfunction toDescriptor(\n raw: HeaderAction,\n triggerAction: (actionId: string) => void,\n): ActionDescriptor {\n const explicitOnClick = raw.onClick;\n const dispatchActionId = raw.actionId;\n // `href` and `onClick` are mutually exclusive on ActionDescriptor. A navigating\n // action (`href`) must NOT also carry an `onClick`, or the Link-rendering menu\n // items (SecondaryDropdown / OverflowMenu always call `child.onClick`) would fire\n // a sidebar dispatch IN ADDITION to navigating. If a raw action is misconfigured\n // with both `href` and `onClick`/`actionId`, `href` wins and `onClick` is dropped.\n const onClick =\n raw.href != null\n ? undefined\n : explicitOnClick ?? (dispatchActionId ? () => triggerAction(dispatchActionId) : undefined);\n return {\n id: raw.id,\n label: raw.label,\n icon: raw.icon,\n href: raw.href,\n onClick,\n disabled: raw.disabled,\n danger: raw.variant === \"danger\",\n children: raw.children ? raw.children.map((c) => toDescriptor(c, triggerAction)) : undefined,\n };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nfunction sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nfunction matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { PageHeaderContext } from \"./pageHeaderContext\";\nimport { SidebarActionProvider } from \"./sidebarAction\";\nimport { buildBreadcrumbs } from \"./breadcrumbs\";\nimport type {\n HeaderDetailContext,\n HeaderMenuItem,\n PageHeaderState,\n RouteMeta,\n} from \"./pageHeaderTypes\";\n\nexport interface PageHeaderProviderProps {\n /**\n * The plugin's menu-item config — a faithful slice of the monolith's\n * `menuConfig.ts`. The provider matches the active route against each item's\n * `href` (pre-order, so a parent list item wins over its own overview child)\n * and publishes the match so `usePageHeader` can surface its `actions`.\n */\n config: HeaderMenuItem[];\n /**\n * The plugin's breadcrumb metadata — a faithful slice of the monolith's\n * `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives\n * the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like\n * the monolith. The app/module crumb (e.g. \"Finance\") is just its top-level entry.\n * Omit for no breadcrumbs.\n */\n routeMeta?: RouteMeta;\n /** Optional detail-context override for detail/edit routes with no menu item. */\n detailContext?: HeaderDetailContext | null;\n children: ReactNode;\n}\n\n/**\n * Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted\n * once at the plugin's router root, it resolves the active menu item from the\n * current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb\n * trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`\n * header CTAs can dispatch to page-registered handlers.\n *\n * In a plugin this feeds the in-body `PageHeader` that migrated pages render —\n * which IS the surface header (the host suppresses its own chrome for\n * platform-react surfaces).\n */\nexport function PageHeaderProvider({\n config,\n routeMeta,\n detailContext,\n children,\n}: PageHeaderProviderProps) {\n const { pathname } = useLocation();\n\n const state = useMemo<PageHeaderState>(() => {\n const activeMenuItem = findByHref(config, normalise(pathname));\n const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : undefined;\n // Default the in-body title to the breadcrumb LEAF label (the current route's\n // `routeMeta` label) so EVERY routed page shows a title — not just the ones that\n // happen to carry a menu item (list pages with CTAs). Only a real trail (Home +\n // ≥1 crumb) yields a leaf; a bare `[Home]` (no route match) leaves title unset so\n // the header stays chrome-only. A page hook can still override via its own\n // headerProps (e.g. a detail page swapping in the entity name).\n const title =\n breadcrumbs != null && breadcrumbs.length >= 2\n ? breadcrumbs[breadcrumbs.length - 1].label\n : undefined;\n return { activeMenuItem, detailContext, breadcrumbs, title };\n }, [config, routeMeta, pathname, detailContext]);\n\n return (\n <SidebarActionProvider>\n <PageHeaderContext.Provider value={state}>{children}</PageHeaderContext.Provider>\n </SidebarActionProvider>\n );\n}\n\n/**\n * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:\n * `withPageHeader(config, routeMeta, app)` returns\n * `(page) => <PageHeaderProvider …>{page}</>`.\n *\n * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on\n * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`\n * instance, which only holds when both come from this `components/layout` entry.\n * (A bundler inlines a separate context copy into each entry, so wrapping from the\n * `platform-react` entry would give the provider a different context than the\n * hook — the header would render empty.) A plugin passes\n * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`\n * to `createPluginPageDefiner`.\n */\nexport function withPageHeader(\n config: HeaderMenuItem[],\n routeMeta?: RouteMeta,\n): (page: ReactElement) => ReactElement {\n return (page) => (\n <PageHeaderProvider config={config} routeMeta={routeMeta}>\n {page}\n </PageHeaderProvider>\n );\n}\n\n/** Strip a trailing slash (except root) so `/x/` and `/x` match the same item. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/**\n * Pre-order search for the item whose `href` exactly matches `pathname`. Pre-order\n * means a parent list item (which carries the `actions`) is returned before its\n * own same-href overview child, so the CTA surfaces on the list route.\n */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n"]}
1
+ {"version":3,"sources":["../../../src/components/layout/fillGridClasses.ts","../../../src/components/layout/FillGrid.tsx","../../../src/components/layout/PageHeader.tsx","../../../src/components/layout/pageHeaderContext.ts","../../../src/components/layout/sidebarAction.tsx","../../../src/components/layout/usePageHeader.ts","../../../src/components/layout/breadcrumbs.ts","../../../src/components/layout/PageHeaderProvider.tsx"],"names":["jsx","Breadcrumbs","Box","Link","createContext","useContext","useRef","useState","useMemo","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Static Tailwind basis classes for {@link FillGridItem}.\n *\n * Tailwind's content scanner extracts class candidates from raw source text and\n * cannot follow string concatenation — `\"sm:\" + \"basis-[...]\"` is invisible to\n * it. So every fully-prefixed class is spelled out as a literal below, indexed\n * by breakpoint → gap → span. `fillItemClass` only ever *looks up* a literal; it\n * never builds one. This is what guarantees the responsive classes survive a\n * production purge (no fragile safelist comment to keep in sync).\n *\n * NOTE: because these literals live in the SDK, a CONSUMER's Tailwind build must\n * scan this package's dist (add it to `content`) or the classes won't generate.\n *\n * basis(n, g) = calc(n/12 * 100% - g * (1 - n/12)), g = gap in rem.\n * gap-5 => g = 1.25rem; gap-2.5 => g = 0.625rem. Span 12 => basis-full.\n */\nexport type Twelfths = 2.4 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12;\nexport type FillGap = 2.5 | 5;\ntype Breakpoint = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type FillSpan = Partial<Record<Breakpoint, Twelfths>>;\n\n/**\n * Full literal class per breakpoint × gap × span. `xs` is unprefixed; `sm`/`md`/\n * `lg` carry the Tailwind prefix. Every string here is a real, scannable literal.\n */\nconst CLASSES: Record<Breakpoint, Record<FillGap, Record<Twelfths, string>>> = {\n xs: {\n 5: {\n 2.4: \"basis-[calc(20%-1rem)]\",\n 3: \"basis-[calc(25%-0.9375rem)]\",\n 4: \"basis-[calc(33.333%-0.8333rem)]\",\n 5: \"basis-[calc(41.667%-0.7292rem)]\",\n 6: \"basis-[calc(50%-0.625rem)]\",\n 7: \"basis-[calc(58.333%-0.5208rem)]\",\n 8: \"basis-[calc(66.667%-0.4167rem)]\",\n 9: \"basis-[calc(75%-0.3125rem)]\",\n 12: \"basis-full\",\n },\n 2.5: {\n 2.4: \"basis-[calc(20%-0.5rem)]\",\n 3: \"basis-[calc(25%-0.46875rem)]\",\n 4: \"basis-[calc(33.333%-0.41667rem)]\",\n 5: \"basis-[calc(41.667%-0.36458rem)]\",\n 6: \"basis-[calc(50%-0.3125rem)]\",\n 7: \"basis-[calc(58.333%-0.26042rem)]\",\n 8: \"basis-[calc(66.667%-0.20833rem)]\",\n 9: \"basis-[calc(75%-0.15625rem)]\",\n 12: \"basis-full\",\n },\n },\n sm: {\n 5: {\n 2.4: \"sm:basis-[calc(20%-1rem)]\",\n 3: \"sm:basis-[calc(25%-0.9375rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"sm:basis-[calc(50%-0.625rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"sm:basis-[calc(75%-0.3125rem)]\",\n 12: \"sm:basis-full\",\n },\n 2.5: {\n 2.4: \"sm:basis-[calc(20%-0.5rem)]\",\n 3: \"sm:basis-[calc(25%-0.46875rem)]\",\n 4: \"sm:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"sm:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"sm:basis-[calc(50%-0.3125rem)]\",\n 7: \"sm:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"sm:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"sm:basis-[calc(75%-0.15625rem)]\",\n 12: \"sm:basis-full\",\n },\n },\n md: {\n 5: {\n 2.4: \"md:basis-[calc(20%-1rem)]\",\n 3: \"md:basis-[calc(25%-0.9375rem)]\",\n 4: \"md:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"md:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"md:basis-[calc(50%-0.625rem)]\",\n 7: \"md:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"md:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"md:basis-[calc(75%-0.3125rem)]\",\n 12: \"md:basis-full\",\n },\n 2.5: {\n 2.4: \"md:basis-[calc(20%-0.5rem)]\",\n 3: \"md:basis-[calc(25%-0.46875rem)]\",\n 4: \"md:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"md:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"md:basis-[calc(50%-0.3125rem)]\",\n 7: \"md:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"md:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"md:basis-[calc(75%-0.15625rem)]\",\n 12: \"md:basis-full\",\n },\n },\n lg: {\n 5: {\n 2.4: \"lg:basis-[calc(20%-1rem)]\",\n 3: \"lg:basis-[calc(25%-0.9375rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.8333rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.7292rem)]\",\n 6: \"lg:basis-[calc(50%-0.625rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.5208rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.4167rem)]\",\n 9: \"lg:basis-[calc(75%-0.3125rem)]\",\n 12: \"lg:basis-full\",\n },\n 2.5: {\n 2.4: \"lg:basis-[calc(20%-0.5rem)]\",\n 3: \"lg:basis-[calc(25%-0.46875rem)]\",\n 4: \"lg:basis-[calc(33.333%-0.41667rem)]\",\n 5: \"lg:basis-[calc(41.667%-0.36458rem)]\",\n 6: \"lg:basis-[calc(50%-0.3125rem)]\",\n 7: \"lg:basis-[calc(58.333%-0.26042rem)]\",\n 8: \"lg:basis-[calc(66.667%-0.20833rem)]\",\n 9: \"lg:basis-[calc(75%-0.15625rem)]\",\n 12: \"lg:basis-full\",\n },\n },\n};\n\nconst BREAKPOINTS: readonly Breakpoint[] = [\"xs\", \"sm\", \"md\", \"lg\"];\n\n/**\n * Full class string for one item: always `min-w-0 grow`, plus the literal basis\n * class for each supplied breakpoint (looked up, never concatenated).\n */\nexport function fillItemClass(span: FillSpan, gap: FillGap): string {\n const parts = [\"min-w-0\", \"grow\"];\n for (const bp of BREAKPOINTS) {\n const n = span[bp];\n if (n !== undefined) parts.push(CLASSES[bp][gap][n]);\n }\n return parts.join(\" \");\n}\n","import { createContext, useContext } from \"react\";\nimport { Box } from \"@mui/material\";\nimport { fillItemClass, type FillGap, type FillSpan } from \"./fillGridClasses\";\n\nconst FillGapContext = createContext<FillGap>(5);\n\ninterface FillGridProps {\n /** Gutter width. 5 = 20px (default), 2.5 = 10px. Matches MUI spacing values. */\n gap?: FillGap;\n className?: string;\n children: React.ReactNode;\n}\n\n/**\n * A flex-wrap row whose items `grow` to fill a partially-filled last row — the\n * dashboard/overview convention. NOT for forms, tables, or single full-width cards.\n *\n * Renders `w-full` so it always spans its container: a `FillGrid` is itself often\n * a flex item of a parent MUI `<Grid container>` / flex row, where without an\n * explicit width it would shrink-wrap to its content instead of filling the row\n * (the behaviour a `<Grid container size={12}>` used to give).\n */\nexport function FillGrid({ gap = 5, className, children }: FillGridProps) {\n const gapClass = gap === 2.5 ? \"gap-2.5\" : \"gap-5\";\n return (\n <FillGapContext.Provider value={gap}>\n <Box className={[\"flex w-full flex-wrap\", gapClass, className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n </FillGapContext.Provider>\n );\n}\n\ninterface FillGridItemProps {\n /** 12-column span per breakpoint, mirroring MUI Grid's `size`. Default { xs: 12 }. */\n span?: FillSpan;\n className?: string;\n children: React.ReactNode;\n}\n\nexport function FillGridItem({ span = { xs: 12 }, className, children }: FillGridItemProps) {\n const gap = useContext(FillGapContext);\n return (\n <Box className={[fillItemClass(span, gap), className].filter(Boolean).join(\" \")}>\n {children}\n </Box>\n );\n}\n","import { useState, useRef, type ReactNode } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n Box,\n Breadcrumbs as MuiBreadcrumbs,\n Stack,\n Typography,\n Link as MuiLink,\n Button,\n IconButton,\n Menu,\n MenuItem,\n} from \"../ui\";\nimport type { ActionDescriptor, Breadcrumb } from \"./pageHeaderTypes\";\n\n/**\n * `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the\n * monolith so plugins share one component instead of copying it. In a plugin this\n * IS the page header: the host suppresses its own chrome for platform-react\n * surfaces, so this renders the breadcrumb trail, the title, and the CTAs.\n *\n * `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is\n * appended as the non-link leaf crumb. MUI primitives come from the sibling\n * `../ui` barrel (host externals, never bundled); `Link` is react-router's so\n * links keep middle-click / open-in-new-tab / a11y.\n */\nexport interface PageHeaderProps {\n title: string;\n subtitle?: ReactNode;\n primaryAction?: ActionDescriptor;\n secondaryAction?: ActionDescriptor;\n overflowActions?: ActionDescriptor[];\n /** Root breadcrumb trail; the `title` is appended as the current-page leaf. */\n breadcrumbs?: Breadcrumb[];\n}\n\nexport function PageHeader({\n title,\n subtitle,\n primaryAction,\n secondaryAction,\n overflowActions,\n breadcrumbs,\n}: PageHeaderProps) {\n const hasActions =\n primaryAction != null ||\n secondaryAction != null ||\n (overflowActions != null && overflowActions.length > 0);\n\n return (\n <Stack direction=\"column\" spacing={0.5} className=\"mb-4\">\n {breadcrumbs != null && breadcrumbs.length > 0 && <Breadcrumbs trail={breadcrumbs} />}\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" spacing={2}>\n <Box>\n {/* Title is optional: in a plugin the host renders the surface title,\n so list pages pass an empty title and only their CTA shows. Pages\n that want an in-body title (detail/create/edit) set it explicitly. */}\n {title !== \"\" && (\n <Typography variant=\"h4\" component=\"h1\">\n {title}\n </Typography>\n )}\n {subtitle != null && (\n <Typography variant=\"body2\" color=\"text.secondary\" className=\"mt-1\">\n {subtitle}\n </Typography>\n )}\n </Box>\n {hasActions && (\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n data-testid=\"page-header-actions\"\n >\n {overflowActions != null && overflowActions.length > 0 && (\n <OverflowMenu actions={overflowActions} />\n )}\n {secondaryAction != null && <SecondaryDropdown action={secondaryAction} />}\n {primaryAction != null && <PrimaryButton action={primaryAction} />}\n </Stack>\n )}\n </Stack>\n </Stack>\n );\n}\n\n/**\n * Breadcrumb trail — mirrors the host's `gogo-ui` `PageBreadcrumbs` exactly (MUI\n * `Breadcrumbs` with the same markup/props) so the plugin surface looks identical\n * to host-rendered chrome: `text.secondary` links (`underline=\"hover\"`) up to the\n * current page, a `text.primary` leaf, `variant=\"body2\"`, `mb-2 px-1`.\n */\nfunction Breadcrumbs({ trail }: { trail: Breadcrumb[] }) {\n if (trail.length < 2) return null;\n return (\n <MuiBreadcrumbs aria-label=\"breadcrumb\" className=\"mb-2 px-1\">\n {trail.map((crumb, index) =>\n index < trail.length - 1 ? (\n <MuiLink\n key={crumb.href ?? crumb.label}\n component={Link}\n to={crumb.href ?? \"/\"}\n underline=\"hover\"\n color=\"text.secondary\"\n variant=\"body2\"\n >\n {crumb.label}\n </MuiLink>\n ) : (\n <Typography key={crumb.label} color=\"text.primary\" variant=\"body2\">\n {crumb.label}\n </Typography>\n ),\n )}\n </MuiBreadcrumbs>\n );\n}\n\nfunction PrimaryButton({ action }: { action: ActionDescriptor }) {\n const common = {\n \"data-danger\": action.danger ? \"true\" : undefined,\n disabled: action.disabled,\n } as const;\n if (action.href != null) {\n return (\n <Button\n component={Link}\n to={action.href}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n }\n return (\n <Button\n onClick={action.onClick}\n variant=\"contained\"\n color={action.danger ? \"error\" : \"primary\"}\n {...common}\n >\n {action.label}\n </Button>\n );\n}\n\nfunction useMenuState() {\n const [open, setOpen] = useState(false);\n const anchorRef = useRef<HTMLButtonElement | null>(null);\n return {\n open,\n anchorRef,\n toggle: () => setOpen((v) => !v),\n close: () => setOpen(false),\n };\n}\n\nfunction SecondaryDropdown({ action }: { action: ActionDescriptor }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n const children = action.children ?? [];\n\n return (\n <>\n <Button\n ref={anchorRef}\n variant=\"outlined\"\n onClick={toggle}\n disabled={action.disabled || children.length === 0}\n aria-haspopup=\"menu\"\n aria-expanded={open || undefined}\n aria-label={action.label}\n >\n {action.label} ▾\n </Button>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {children.map((child) => (\n // href children render as anchors (component={Link}) to keep\n // middle-click / open-in-new-tab / a11y. `href` and `onClick` are\n // mutually exclusive on ActionDescriptor, so one MenuItem covers both.\n <MenuItem\n key={child.id}\n {...(child.href != null ? { component: Link, to: child.href } : {})}\n onClick={() => {\n close();\n child.onClick?.();\n }}\n disabled={child.disabled}\n >\n {child.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n\nfunction OverflowMenu({ actions }: { actions: ActionDescriptor[] }) {\n const { open, anchorRef, toggle, close } = useMenuState();\n return (\n <>\n <IconButton\n ref={anchorRef}\n size=\"small\"\n aria-label=\"More actions\"\n onClick={toggle}\n >\n {\"⋯\"}\n </IconButton>\n <Menu anchorEl={anchorRef.current} open={open} onClose={close}>\n {actions.map((a) => (\n // href items render as anchors (component={Link}) — like\n // PrimaryButton — to keep middle-click / open-in-new-tab / a11y.\n // `href` and `onClick` are mutually exclusive on ActionDescriptor,\n // so one MenuItem covers both.\n <MenuItem\n key={a.id}\n {...(a.href != null ? { component: Link, to: a.href } : {})}\n onClick={() => {\n close();\n a.onClick?.();\n }}\n disabled={a.disabled}\n sx={a.danger ? { color: \"error.main\" } : undefined}\n >\n {a.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n","import { createContext, useContext } from \"react\";\nimport type { PageHeaderState } from \"./pageHeaderTypes\";\n\n/**\n * Carries the active menu item / detail context so `usePageHeader` can derive\n * header props. The plugin analogue of the monolith shell's\n * `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.\n *\n * `null` when no provider is mounted (a page rendered standalone, e.g. a test) —\n * consumers treat `null` as \"no header context\" and derive empty props.\n */\nexport const PageHeaderContext = createContext<PageHeaderState | null>(null);\n\n/** Returns the provider-published header state, or `null` when none is mounted. */\nexport function usePageHeaderContext(): PageHeaderState | null {\n return useContext(PageHeaderContext);\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\n/**\n * Sidebar-action dispatch channel — promoted from the monolith's\n * `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an\n * `href`) dispatch through here; a page registers a handler via\n * `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs\n * (\"New Supplier Invoice\", \"New Cost Code\", …) fire when clicked.\n */\n\nexport type ActionHandler = (actionId: string) => void;\n\nexport interface SidebarActionContextValue {\n /** Dispatch an action to all registered page handlers. */\n triggerAction: (actionId: string) => void;\n /** Register a handler that receives sidebar actions. Returns an unregister function. */\n registerHandler: (handler: ActionHandler) => () => void;\n /** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */\n activeActions: ReadonlySet<string>;\n /** Reports whether the owning UI surface for `actionId` is currently open. */\n setActionActive: (actionId: string, isActive: boolean) => void;\n}\n\nexport const SidebarActionContext = createContext<SidebarActionContextValue>({\n triggerAction: () => {},\n registerHandler: () => () => {},\n activeActions: new Set(),\n setActionActive: () => {},\n});\n\nexport function SidebarActionProvider({ children }: { children: ReactNode }) {\n const handlersRef = useRef(new Set<ActionHandler>());\n const [activeActions, setActiveActions] = useState<ReadonlySet<string>>(() => new Set());\n\n const triggerAction = useCallback((actionId: string) => {\n for (const handler of handlersRef.current) {\n handler(actionId);\n }\n }, []);\n\n const registerHandler = useCallback((handler: ActionHandler) => {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n }, []);\n\n const setActionActive = useCallback((actionId: string, isActive: boolean) => {\n setActiveActions((prev) => {\n const has = prev.has(actionId);\n if (isActive === has) return prev;\n const next = new Set(prev);\n if (isActive) next.add(actionId);\n else next.delete(actionId);\n return next;\n });\n }, []);\n\n const value = useMemo(\n () => ({ triggerAction, registerHandler, activeActions, setActionActive }),\n [triggerAction, registerHandler, activeActions, setActionActive],\n );\n\n return <SidebarActionContext.Provider value={value}>{children}</SidebarActionContext.Provider>;\n}\n\n/** Used by the header to dispatch actions to pages. */\nexport function useTriggerSidebarAction(): (actionId: string) => void {\n return useContext(SidebarActionContext).triggerAction;\n}\n\n/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */\nexport function useSidebarAction(handler: ActionHandler): void {\n const { registerHandler } = useContext(SidebarActionContext);\n useEffect(() => registerHandler(handler), [registerHandler, handler]);\n}\n\n/**\n * Registers a map of sidebar-action handlers keyed by action id, dispatching the\n * matching entry when an action fires (unknown ids are ignored). The handler map\n * may change every render — a ref keeps the dispatcher stable so the registration\n * doesn't churn.\n */\nexport function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void {\n const handlersRef = useRef(handlers);\n handlersRef.current = handlers;\n useSidebarAction(\n useCallback((actionId: string) => {\n handlersRef.current[actionId]?.();\n }, []),\n );\n}\n\n/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */\nexport function useActiveSidebarActions(): ReadonlySet<string> {\n return useContext(SidebarActionContext).activeActions;\n}\n\n/**\n * Reports to the channel whether the owning UI surface for `actionId` is open.\n * Pages call this from a hook scope, mirroring their dialog/drawer open state.\n */\nexport function useReportSidebarActionActive(actionId: string, isActive: boolean): void {\n const { setActionActive } = useContext(SidebarActionContext);\n useEffect(() => {\n setActionActive(actionId, isActive);\n return () => {\n setActionActive(actionId, false);\n };\n }, [actionId, isActive, setActionActive]);\n}\n","import { useMemo } from \"react\";\nimport { usePageHeaderContext } from \"./pageHeaderContext\";\nimport { useTriggerSidebarAction } from \"./sidebarAction\";\nimport type {\n ActionDescriptor,\n ActionSlot,\n HeaderAction,\n PageHeaderDerivedProps,\n} from \"./pageHeaderTypes\";\n\n/**\n * Derives `<PageHeader>` props from the active menu item or detail context —\n * promoted verbatim from the monolith's `usePageHeader`, reading the plugin's\n * `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.\n *\n * Outside a mounted `PageHeaderProvider` (or in a test rendering a page\n * standalone) the context is `null` and the hook derives empty header props —\n * same shape, no work. Page hooks spread the result and may override\n * title/subtitle/actions.\n */\nexport function usePageHeader(): PageHeaderDerivedProps {\n const state = usePageHeaderContext();\n const { activeMenuItem, detailContext, title, subtitle, breadcrumbs } = state ?? {};\n // Dispatcher for actionId-only actions — the same channel a page registers\n // against via `useSidebarAction`. Threading it into `toDescriptor` is what\n // makes header buttons like `+ New Supplier Invoice` (which carry an\n // `actionId` and no `href`) open the page-owned dialog when clicked.\n const triggerAction = useTriggerSidebarAction();\n\n return useMemo(() => {\n const sourceActions: HeaderAction[] =\n detailContext?.actions ?? activeMenuItem?.actions ?? [];\n\n let primary: ActionDescriptor | undefined;\n let secondary: ActionDescriptor | undefined;\n const overflow: ActionDescriptor[] = [];\n\n for (const action of sourceActions) {\n const descriptor = toDescriptor(action, triggerAction);\n const slot: ActionSlot = action.slot ?? \"sidebar-only\";\n switch (slot) {\n case \"primary\":\n if (primary == null) {\n primary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple primary slots on this page; demoting later ones to overflow\",\n { keptId: primary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"secondary\":\n if (secondary == null) {\n secondary = descriptor;\n } else {\n console.warn(\n \"usePageHeader: multiple secondary slots on this page; demoting later ones to overflow\",\n { keptId: secondary.id, demotedId: descriptor.id },\n );\n overflow.push(descriptor);\n }\n break;\n case \"overflow\":\n overflow.push(descriptor);\n break;\n case \"sidebar-only\":\n default:\n // Not header-visible.\n break;\n }\n }\n\n return {\n // The plugin's `PageHeader` is the sole header (the host suppresses its own\n // chrome for platform-react surfaces), so it owns the title: a list page\n // shows its menu label; detail/create/edit pages override with their own.\n title: title ?? detailContext?.title ?? activeMenuItem?.label ?? \"\",\n // Fall back to the detail context's subtitle so a wrapper-rendered header\n // (no page spreading its own `headerProps`) still surfaces the entity name.\n subtitle: subtitle ?? detailContext?.subtitle,\n primaryAction: primary,\n secondaryAction: secondary,\n overflowActions: overflow.length > 0 ? overflow : undefined,\n breadcrumbs,\n };\n }, [activeMenuItem, detailContext, title, subtitle, breadcrumbs, triggerAction]);\n}\n\n/**\n * Map a raw header source action to `PageHeader`'s `ActionDescriptor`.\n *\n * Resolution order for `onClick`:\n * 1. Explicit `raw.onClick` (detail-context actions can carry one directly).\n * 2. `raw.actionId` → dispatch via the sidebar-action channel. This is what\n * makes menu-config actions like `actionId: \"create-cost-code\"` (no `href`,\n * no `onClick`) fire when the header button is clicked — the page registers\n * a handler via `useSidebarAction`.\n * 3. Neither — the button still renders, but is a no-op (typically paired with\n * `href` so the primary button renders a Link instead).\n *\n * The trigger is threaded down explicitly rather than via a hook because this\n * runs inside `useMemo` and can recurse (for secondary-dropdown children).\n */\nfunction toDescriptor(\n raw: HeaderAction,\n triggerAction: (actionId: string) => void,\n): ActionDescriptor {\n const explicitOnClick = raw.onClick;\n const dispatchActionId = raw.actionId;\n // `href` and `onClick` are mutually exclusive on ActionDescriptor. A navigating\n // action (`href`) must NOT also carry an `onClick`, or the Link-rendering menu\n // items (SecondaryDropdown / OverflowMenu always call `child.onClick`) would fire\n // a sidebar dispatch IN ADDITION to navigating. If a raw action is misconfigured\n // with both `href` and `onClick`/`actionId`, `href` wins and `onClick` is dropped.\n const onClick =\n raw.href != null\n ? undefined\n : explicitOnClick ?? (dispatchActionId ? () => triggerAction(dispatchActionId) : undefined);\n return {\n id: raw.id,\n label: raw.label,\n icon: raw.icon,\n href: raw.href,\n onClick,\n disabled: raw.disabled,\n danger: raw.variant === \"danger\",\n children: raw.children ? raw.children.map((c) => toDescriptor(c, triggerAction)) : undefined,\n };\n}\n","import type { Breadcrumb, RouteMeta, RouteMetaEntry } from \"./pageHeaderTypes\";\n\n/** Synthesised root crumb — mirrors the monolith's virtual `{ pattern: \"/\", label: \"Home\" }`. */\nconst HOME_CRUMB: Breadcrumb = { label: \"Home\", href: \"/\" };\n\nfunction stripTrailingSlash(value: string): string {\n return value.length > 1 && value.endsWith(\"/\") ? value.slice(0, -1) : value;\n}\n\n/**\n * Orders routes most-specific first: more segments win, then at equal depth prefer\n * literal segments over `:param` ones — identical to the monolith's `SORTED_ROUTE_META`.\n */\nfunction sortBySpecificity(routeMeta: RouteMeta): RouteMetaEntry[] {\n return [...routeMeta].sort((a, b) => {\n const aDepth = a.pattern.split(\"/\").length;\n const bDepth = b.pattern.split(\"/\").length;\n if (bDepth !== aDepth) {\n return bDepth - aDepth;\n }\n const aDynamic = (a.pattern.match(/:/g) ?? []).length;\n const bDynamic = (b.pattern.match(/:/g) ?? []).length;\n return aDynamic - bDynamic;\n });\n}\n\n/**\n * Matches a pathname against the sorted routes segment-by-segment, capturing\n * `:param` values. Returns the first (most-specific) match, or `null`. Port of the\n * monolith's `matchRoute`.\n */\nfunction matchRoute(\n pathname: string,\n sorted: RouteMetaEntry[],\n): { entry: RouteMetaEntry; params: Record<string, string> } | null {\n const pathSegments = stripTrailingSlash(pathname).split(\"/\");\n for (const entry of sorted) {\n const patternSegments = entry.pattern.split(\"/\");\n if (patternSegments.length !== pathSegments.length) {\n continue;\n }\n const params: Record<string, string> = {};\n let matched = true;\n for (let i = 0; i < patternSegments.length; i++) {\n const pSeg = patternSegments[i];\n const vSeg = pathSegments[i];\n if (pSeg.startsWith(\":\")) {\n params[pSeg.slice(1)] = vSeg;\n } else if (pSeg !== vSeg) {\n matched = false;\n break;\n }\n }\n if (matched) {\n return { entry, params };\n }\n }\n return null;\n}\n\n/**\n * Replaces `:param` segments in a pattern with captured values. Port of\n * `resolvePattern`. The param-name class matches the identifiers `matchRoute`\n * captures (letters/digits/underscore, e.g. `:user_id`, `:id2`); when a value is\n * missing the original `:param` token is preserved rather than dropping the colon.\n */\nfunction resolvePattern(pattern: string, params: Record<string, string>): string {\n return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name: string) => params[name] ?? `:${name}`);\n}\n\n/**\n * Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the\n * monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`\n * patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps\n * each ancestor to its resolved-pattern href (the current page gets no href). The\n * app/module crumb (e.g. \"Finance\") is just the top-level `routeMeta` entry — no\n * separate config, exactly like the monolith's `/finance` root entry.\n */\nexport function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[] {\n const match = matchRoute(pathname, sortBySpecificity(routeMeta));\n if (match == null) {\n return [HOME_CRUMB];\n }\n\n const { entry: current, params } = match;\n\n // Walk the parent chain (leaf → root), guarding against cycles.\n const chain: RouteMetaEntry[] = [current];\n const seen = new Set<string>([current.pattern]);\n let node = current;\n while (node.parent != null) {\n const parent = routeMeta.find((e) => e.pattern === node.parent);\n if (parent == null || seen.has(parent.pattern)) {\n break;\n }\n seen.add(parent.pattern);\n chain.unshift(parent);\n node = parent;\n }\n\n const crumbs: Breadcrumb[] = [HOME_CRUMB];\n chain.forEach((entry, index) => {\n const isLast = index === chain.length - 1;\n crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });\n });\n return crumbs;\n}\n","import { useMemo, type ReactElement, type ReactNode } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { PageHeaderContext } from \"./pageHeaderContext\";\nimport { SidebarActionProvider } from \"./sidebarAction\";\nimport { buildBreadcrumbs } from \"./breadcrumbs\";\nimport { PageHeader } from \"./PageHeader\";\nimport { usePageHeader } from \"./usePageHeader\";\nimport type {\n HeaderDetailContext,\n HeaderMenuItem,\n PageHeaderState,\n RouteMeta,\n} from \"./pageHeaderTypes\";\n\n/**\n * Default detail-context resolver: no detail context. Kept as a stable named hook\n * so `PageHeaderProvider` can call the resolver UNCONDITIONALLY (a hook can't be\n * called behind a runtime branch), whether or not a plugin supplied one.\n */\nfunction useNoDetailContext(): HeaderDetailContext | null {\n return null;\n}\n\n/**\n * The wrapper-rendered header. Mounted by `PageHeaderProvider` ONLY when the\n * plugin's `useDetailContext` resolver returns a context for the active route.\n *\n * This is the plugin equivalent of a monolith NESTED-LAYOUT ROUTE — a layout that\n * renders one shared `<PageHeader>` above the `<Outlet>` for a set of content-only\n * sub-pages (e.g. a project/ticket detail subtree). A PlatformReact plugin has no\n * router outlet (each page is its own bundle), so the page-definer wrapper is the\n * only shared seam: the resolver says \"these routes have no self-rendered header,\n * here's their shared header context\" and this renders it. Reads the same\n * `usePageHeader` a self-rendering page would, so title/subtitle/CTAs are derived\n * identically; the resolver's context is already folded into the provider state.\n */\nfunction ChromeHeader(): ReactElement {\n return <PageHeader {...usePageHeader()} />;\n}\n\nexport interface PageHeaderProviderProps {\n /**\n * The plugin's menu-item config — a faithful slice of the monolith's\n * `menuConfig.ts`. The provider matches the active route against each item's\n * `href` (pre-order, so a parent list item wins over its own overview child)\n * and publishes the match so `usePageHeader` can surface its `actions`.\n */\n config: HeaderMenuItem[];\n /**\n * The plugin's breadcrumb metadata — a faithful slice of the monolith's\n * `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives\n * the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like\n * the monolith. The app/module crumb (e.g. \"Finance\") is just its top-level entry.\n * Omit for no breadcrumbs.\n */\n routeMeta?: RouteMeta;\n /** Optional detail-context override for detail/edit routes with no menu item. */\n detailContext?: HeaderDetailContext | null;\n /**\n * Optional per-route detail-context RESOLVER hook. Called every render; when it\n * returns a context, the provider (a) folds it into the header state AND (b)\n * renders the header ITSELF (`<ChromeHeader>`) above the page — for detail\n * routes whose migrated page renders no `<PageHeader>` of its own. Returns\n * `null` for routes the page already headers (list/create/edit self-render), so\n * chrome never double-renders. Takes precedence over the static `detailContext`.\n */\n useDetailContext?: () => HeaderDetailContext | null;\n children: ReactNode;\n}\n\n/**\n * Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted\n * once at the plugin's router root, it resolves the active menu item from the\n * current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb\n * trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`\n * header CTAs can dispatch to page-registered handlers.\n *\n * In a plugin this feeds the in-body `PageHeader` that migrated pages render —\n * which IS the surface header (the host suppresses its own chrome for\n * platform-react surfaces).\n */\nexport function PageHeaderProvider({\n config,\n routeMeta,\n detailContext,\n useDetailContext = useNoDetailContext,\n children,\n}: PageHeaderProviderProps) {\n const { pathname } = useLocation();\n // Resolve the active detail context. The resolver hook is ALWAYS called (the\n // default is a stable no-op) so hook order stays stable. When a resolver was\n // actually supplied it is AUTHORITATIVE: its result wins — including `null`,\n // which means \"suppress chrome for this route\" and must NOT fall back to the\n // static prop. The static `detailContext` is only the fallback when no resolver\n // was passed (the legacy direct-provider API).\n const resolvedFromResolver = useDetailContext();\n const resolvedDetailContext =\n useDetailContext === useNoDetailContext ? detailContext ?? null : resolvedFromResolver;\n\n const state = useMemo<PageHeaderState>(() => {\n const activeMenuItem = findByHref(config, normalise(pathname));\n const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : undefined;\n // Default the in-body title to the breadcrumb LEAF label (the current route's\n // `routeMeta` label) so EVERY routed page shows a title — not just the ones that\n // happen to carry a menu item (list pages with CTAs). Only a real trail (Home +\n // ≥1 crumb) yields a leaf; a bare `[Home]` (no route match) leaves title unset so\n // the header stays chrome-only. A page hook can still override via its own\n // headerProps (e.g. a detail page swapping in the entity name).\n const title =\n breadcrumbs != null && breadcrumbs.length >= 2\n ? breadcrumbs[breadcrumbs.length - 1].label\n : undefined;\n return { activeMenuItem, detailContext: resolvedDetailContext, breadcrumbs, title };\n }, [config, routeMeta, pathname, resolvedDetailContext]);\n\n return (\n <SidebarActionProvider>\n <PageHeaderContext.Provider value={state}>\n {resolvedDetailContext != null ? <ChromeHeader /> : null}\n {children}\n </PageHeaderContext.Provider>\n </SidebarActionProvider>\n );\n}\n\n/**\n * Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:\n * `withPageHeader(config, routeMeta, useDetailContext?)` returns\n * `(page) => <PageHeaderProvider …>{page}</PageHeaderProvider>`.\n *\n * Pass the optional `useDetailContext` resolver to have the provider render a\n * shared header for detail routes whose page renders none of its own (see\n * {@link PageHeaderProviderProps.useDetailContext}); omit it for the original\n * behaviour (provider supplies context only, each page self-renders `<PageHeader>`).\n *\n * Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on\n * purpose: the provider and the hook must resolve to ONE `PageHeaderContext`\n * instance, which only holds when both come from this `components/layout` entry.\n * (A bundler inlines a separate context copy into each entry, so wrapping from the\n * `platform-react` entry would give the provider a different context than the\n * hook — the header would render empty.) A plugin passes\n * `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`\n * (optionally a third resolver arg) to `createPluginPageDefiner`.\n */\nexport function withPageHeader(\n config: HeaderMenuItem[],\n routeMeta?: RouteMeta,\n useDetailContext?: () => HeaderDetailContext | null,\n): (page: ReactElement) => ReactElement {\n return (page) => (\n <PageHeaderProvider config={config} routeMeta={routeMeta} useDetailContext={useDetailContext}>\n {page}\n </PageHeaderProvider>\n );\n}\n\n/** Strip a trailing slash (except root) so `/x/` and `/x` match the same item. */\nfunction normalise(pathname: string): string {\n return pathname.length > 1 && pathname.endsWith(\"/\") ? pathname.slice(0, -1) : pathname;\n}\n\n/**\n * Pre-order search for the item whose `href` exactly matches `pathname`. Pre-order\n * means a parent list item (which carries the `actions`) is returned before its\n * own same-href overview child, so the CTA surfaces on the list route.\n */\nfunction findByHref(items: HeaderMenuItem[], pathname: string): HeaderMenuItem | null {\n for (const item of items) {\n if (item.href != null && normalise(item.href) === pathname) {\n return item;\n }\n if (item.children) {\n const nested = findByHref(item.children, pathname);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethisyscore/plugin-ui",
3
- "version": "1.42.0",
3
+ "version": "1.44.0",
4
4
  "description": "Plugin-UI umbrella SDK: client bridge + a11y/l10n primitives + brokered-MCP client (WI 4858).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",