@ethisyscore/plugin-ui 1.23.0 → 1.25.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.
@@ -1,6 +1,7 @@
1
- import { createContext, useContext } from 'react';
2
- import { Box } from '@mui/material';
3
- import { jsx } from 'react/jsx-runtime';
1
+ import { createContext, useContext, useRef, useState, useCallback, useMemo, useEffect } from 'react';
2
+ import { Box, Stack, Typography, Breadcrumbs, Link, Button, Menu, MenuItem, IconButton } from '@mui/material';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { Link as Link$1, useLocation } from 'react-router-dom';
4
5
 
5
6
  // src/components/layout/FillGrid.tsx
6
7
 
@@ -121,7 +122,418 @@ function FillGridItem({ span = { xs: 12 }, className, children }) {
121
122
  const gap = useContext(FillGapContext);
122
123
  return /* @__PURE__ */ jsx(Box, { className: [fillItemClass(span, gap), className].filter(Boolean).join(" "), children });
123
124
  }
125
+ function PageHeader({
126
+ title,
127
+ subtitle,
128
+ primaryAction,
129
+ secondaryAction,
130
+ overflowActions,
131
+ breadcrumbs
132
+ }) {
133
+ const hasActions = primaryAction != null || secondaryAction != null || overflowActions != null && overflowActions.length > 0;
134
+ return /* @__PURE__ */ jsxs(Stack, { direction: "column", spacing: 0.5, className: "mb-4", children: [
135
+ breadcrumbs != null && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Breadcrumbs2, { trail: breadcrumbs }),
136
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "flex-start", spacing: 2, children: [
137
+ /* @__PURE__ */ jsxs(Box, { children: [
138
+ title !== "" && /* @__PURE__ */ jsx(Typography, { variant: "h4", component: "h1", children: title }),
139
+ subtitle != null && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", className: "mt-1", children: subtitle })
140
+ ] }),
141
+ hasActions && /* @__PURE__ */ jsxs(
142
+ Stack,
143
+ {
144
+ direction: "row",
145
+ spacing: 1,
146
+ alignItems: "center",
147
+ "data-testid": "page-header-actions",
148
+ children: [
149
+ overflowActions != null && overflowActions.length > 0 && /* @__PURE__ */ jsx(OverflowMenu, { actions: overflowActions }),
150
+ secondaryAction != null && /* @__PURE__ */ jsx(SecondaryDropdown, { action: secondaryAction }),
151
+ primaryAction != null && /* @__PURE__ */ jsx(PrimaryButton, { action: primaryAction })
152
+ ]
153
+ }
154
+ )
155
+ ] })
156
+ ] });
157
+ }
158
+ function Breadcrumbs2({ trail }) {
159
+ if (trail.length < 2) return null;
160
+ return /* @__PURE__ */ jsx(Breadcrumbs, { "aria-label": "breadcrumb", className: "mb-2 px-1", children: trail.map(
161
+ (crumb, index) => index < trail.length - 1 ? /* @__PURE__ */ jsx(
162
+ Link,
163
+ {
164
+ component: Link$1,
165
+ to: crumb.href ?? "/",
166
+ underline: "hover",
167
+ color: "text.secondary",
168
+ variant: "body2",
169
+ children: crumb.label
170
+ },
171
+ crumb.href ?? crumb.label
172
+ ) : /* @__PURE__ */ jsx(Typography, { color: "text.primary", variant: "body2", children: crumb.label }, crumb.label)
173
+ ) });
174
+ }
175
+ function PrimaryButton({ action }) {
176
+ const common = {
177
+ "data-danger": action.danger ? "true" : void 0,
178
+ disabled: action.disabled
179
+ };
180
+ if (action.href != null) {
181
+ return /* @__PURE__ */ jsx(
182
+ Button,
183
+ {
184
+ component: Link$1,
185
+ to: action.href,
186
+ variant: "contained",
187
+ color: action.danger ? "error" : "primary",
188
+ ...common,
189
+ children: action.label
190
+ }
191
+ );
192
+ }
193
+ return /* @__PURE__ */ jsx(
194
+ Button,
195
+ {
196
+ onClick: action.onClick,
197
+ variant: "contained",
198
+ color: action.danger ? "error" : "primary",
199
+ ...common,
200
+ children: action.label
201
+ }
202
+ );
203
+ }
204
+ function useMenuState() {
205
+ const [open, setOpen] = useState(false);
206
+ const anchorRef = useRef(null);
207
+ return {
208
+ open,
209
+ anchorRef,
210
+ toggle: () => setOpen((v) => !v),
211
+ close: () => setOpen(false)
212
+ };
213
+ }
214
+ function SecondaryDropdown({ action }) {
215
+ const { open, anchorRef, toggle, close } = useMenuState();
216
+ const children = action.children ?? [];
217
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
218
+ /* @__PURE__ */ jsxs(
219
+ Button,
220
+ {
221
+ ref: anchorRef,
222
+ variant: "outlined",
223
+ onClick: toggle,
224
+ disabled: action.disabled || children.length === 0,
225
+ "aria-haspopup": "menu",
226
+ "aria-expanded": open || void 0,
227
+ "aria-label": action.label,
228
+ children: [
229
+ action.label,
230
+ " \u25BE"
231
+ ]
232
+ }
233
+ ),
234
+ /* @__PURE__ */ jsx(Menu, { anchorEl: anchorRef.current, open, onClose: close, children: children.map((child) => (
235
+ // href children render as anchors (component={Link}) to keep
236
+ // middle-click / open-in-new-tab / a11y. `href` and `onClick` are
237
+ // mutually exclusive on ActionDescriptor, so one MenuItem covers both.
238
+ /* @__PURE__ */ jsx(
239
+ MenuItem,
240
+ {
241
+ ...child.href != null ? { component: Link$1, to: child.href } : {},
242
+ onClick: () => {
243
+ close();
244
+ child.onClick?.();
245
+ },
246
+ disabled: child.disabled,
247
+ children: child.label
248
+ },
249
+ child.id
250
+ )
251
+ )) })
252
+ ] });
253
+ }
254
+ function OverflowMenu({ actions }) {
255
+ const { open, anchorRef, toggle, close } = useMenuState();
256
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
257
+ /* @__PURE__ */ jsx(
258
+ IconButton,
259
+ {
260
+ ref: anchorRef,
261
+ size: "small",
262
+ "aria-label": "More actions",
263
+ onClick: toggle,
264
+ children: "\u22EF"
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsx(Menu, { anchorEl: anchorRef.current, open, onClose: close, children: actions.map((a) => (
268
+ // href items render as anchors (component={Link}) — like
269
+ // PrimaryButton — to keep middle-click / open-in-new-tab / a11y.
270
+ // `href` and `onClick` are mutually exclusive on ActionDescriptor,
271
+ // so one MenuItem covers both.
272
+ /* @__PURE__ */ jsx(
273
+ MenuItem,
274
+ {
275
+ ...a.href != null ? { component: Link$1, to: a.href } : {},
276
+ onClick: () => {
277
+ close();
278
+ a.onClick?.();
279
+ },
280
+ disabled: a.disabled,
281
+ sx: a.danger ? { color: "error.main" } : void 0,
282
+ children: a.label
283
+ },
284
+ a.id
285
+ )
286
+ )) })
287
+ ] });
288
+ }
289
+ var PageHeaderContext = createContext(null);
290
+ function usePageHeaderContext() {
291
+ return useContext(PageHeaderContext);
292
+ }
293
+ var SidebarActionContext = createContext({
294
+ triggerAction: () => {
295
+ },
296
+ registerHandler: () => () => {
297
+ },
298
+ activeActions: /* @__PURE__ */ new Set(),
299
+ setActionActive: () => {
300
+ }
301
+ });
302
+ function SidebarActionProvider({ children }) {
303
+ const handlersRef = useRef(/* @__PURE__ */ new Set());
304
+ const [activeActions, setActiveActions] = useState(() => /* @__PURE__ */ new Set());
305
+ const triggerAction = useCallback((actionId) => {
306
+ for (const handler of handlersRef.current) {
307
+ handler(actionId);
308
+ }
309
+ }, []);
310
+ const registerHandler = useCallback((handler) => {
311
+ handlersRef.current.add(handler);
312
+ return () => {
313
+ handlersRef.current.delete(handler);
314
+ };
315
+ }, []);
316
+ const setActionActive = useCallback((actionId, isActive) => {
317
+ setActiveActions((prev) => {
318
+ const has = prev.has(actionId);
319
+ if (isActive === has) return prev;
320
+ const next = new Set(prev);
321
+ if (isActive) next.add(actionId);
322
+ else next.delete(actionId);
323
+ return next;
324
+ });
325
+ }, []);
326
+ const value = useMemo(
327
+ () => ({ triggerAction, registerHandler, activeActions, setActionActive }),
328
+ [triggerAction, registerHandler, activeActions, setActionActive]
329
+ );
330
+ return /* @__PURE__ */ jsx(SidebarActionContext.Provider, { value, children });
331
+ }
332
+ function useTriggerSidebarAction() {
333
+ return useContext(SidebarActionContext).triggerAction;
334
+ }
335
+ function useSidebarAction(handler) {
336
+ const { registerHandler } = useContext(SidebarActionContext);
337
+ useEffect(() => registerHandler(handler), [registerHandler, handler]);
338
+ }
339
+ function useSidebarActions(handlers) {
340
+ const handlersRef = useRef(handlers);
341
+ handlersRef.current = handlers;
342
+ useSidebarAction(
343
+ useCallback((actionId) => {
344
+ handlersRef.current[actionId]?.();
345
+ }, [])
346
+ );
347
+ }
348
+ function useActiveSidebarActions() {
349
+ return useContext(SidebarActionContext).activeActions;
350
+ }
351
+ function useReportSidebarActionActive(actionId, isActive) {
352
+ const { setActionActive } = useContext(SidebarActionContext);
353
+ useEffect(() => {
354
+ setActionActive(actionId, isActive);
355
+ return () => {
356
+ setActionActive(actionId, false);
357
+ };
358
+ }, [actionId, isActive, setActionActive]);
359
+ }
360
+
361
+ // src/components/layout/usePageHeader.ts
362
+ function usePageHeader() {
363
+ const state = usePageHeaderContext();
364
+ const { activeMenuItem, detailContext, title, subtitle, breadcrumbs } = state ?? {};
365
+ const triggerAction = useTriggerSidebarAction();
366
+ return useMemo(() => {
367
+ const sourceActions = detailContext?.actions ?? activeMenuItem?.actions ?? [];
368
+ let primary;
369
+ let secondary;
370
+ const overflow = [];
371
+ for (const action of sourceActions) {
372
+ const descriptor = toDescriptor(action, triggerAction);
373
+ const slot = action.slot ?? "sidebar-only";
374
+ switch (slot) {
375
+ case "primary":
376
+ if (primary == null) {
377
+ primary = descriptor;
378
+ } else {
379
+ console.warn(
380
+ "usePageHeader: multiple primary slots on this page; demoting later ones to overflow",
381
+ { keptId: primary.id, demotedId: descriptor.id }
382
+ );
383
+ overflow.push(descriptor);
384
+ }
385
+ break;
386
+ case "secondary":
387
+ if (secondary == null) {
388
+ secondary = descriptor;
389
+ } else {
390
+ console.warn(
391
+ "usePageHeader: multiple secondary slots on this page; demoting later ones to overflow",
392
+ { keptId: secondary.id, demotedId: descriptor.id }
393
+ );
394
+ overflow.push(descriptor);
395
+ }
396
+ break;
397
+ case "overflow":
398
+ overflow.push(descriptor);
399
+ break;
400
+ }
401
+ }
402
+ return {
403
+ // The plugin's `PageHeader` is the sole header (the host suppresses its own
404
+ // chrome for platform-react surfaces), so it owns the title: a list page
405
+ // shows its menu label; detail/create/edit pages override with their own.
406
+ title: title ?? detailContext?.title ?? activeMenuItem?.label ?? "",
407
+ subtitle,
408
+ primaryAction: primary,
409
+ secondaryAction: secondary,
410
+ overflowActions: overflow.length > 0 ? overflow : void 0,
411
+ breadcrumbs
412
+ };
413
+ }, [activeMenuItem, detailContext, title, subtitle, breadcrumbs, triggerAction]);
414
+ }
415
+ function toDescriptor(raw, triggerAction) {
416
+ const explicitOnClick = raw.onClick;
417
+ const dispatchActionId = raw.actionId;
418
+ const onClick = raw.href != null ? void 0 : explicitOnClick ?? (dispatchActionId ? () => triggerAction(dispatchActionId) : void 0);
419
+ return {
420
+ id: raw.id,
421
+ label: raw.label,
422
+ icon: raw.icon,
423
+ href: raw.href,
424
+ onClick,
425
+ disabled: raw.disabled,
426
+ danger: raw.variant === "danger",
427
+ children: raw.children ? raw.children.map((c) => toDescriptor(c, triggerAction)) : void 0
428
+ };
429
+ }
430
+
431
+ // src/components/layout/breadcrumbs.ts
432
+ var HOME_CRUMB = { label: "Home", href: "/" };
433
+ function stripTrailingSlash(value) {
434
+ return value.length > 1 && value.endsWith("/") ? value.slice(0, -1) : value;
435
+ }
436
+ function sortBySpecificity(routeMeta) {
437
+ return [...routeMeta].sort((a, b) => {
438
+ const aDepth = a.pattern.split("/").length;
439
+ const bDepth = b.pattern.split("/").length;
440
+ if (bDepth !== aDepth) {
441
+ return bDepth - aDepth;
442
+ }
443
+ const aDynamic = (a.pattern.match(/:/g) ?? []).length;
444
+ const bDynamic = (b.pattern.match(/:/g) ?? []).length;
445
+ return aDynamic - bDynamic;
446
+ });
447
+ }
448
+ function matchRoute(pathname, sorted) {
449
+ const pathSegments = stripTrailingSlash(pathname).split("/");
450
+ for (const entry of sorted) {
451
+ const patternSegments = entry.pattern.split("/");
452
+ if (patternSegments.length !== pathSegments.length) {
453
+ continue;
454
+ }
455
+ const params = {};
456
+ let matched = true;
457
+ for (let i = 0; i < patternSegments.length; i++) {
458
+ const pSeg = patternSegments[i];
459
+ const vSeg = pathSegments[i];
460
+ if (pSeg.startsWith(":")) {
461
+ params[pSeg.slice(1)] = vSeg;
462
+ } else if (pSeg !== vSeg) {
463
+ matched = false;
464
+ break;
465
+ }
466
+ }
467
+ if (matched) {
468
+ return { entry, params };
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ function resolvePattern(pattern, params) {
474
+ return pattern.replace(/:([A-Za-z0-9_]+)/g, (_, name) => params[name] ?? `:${name}`);
475
+ }
476
+ function buildBreadcrumbs(pathname, routeMeta) {
477
+ const match = matchRoute(pathname, sortBySpecificity(routeMeta));
478
+ if (match == null) {
479
+ return [HOME_CRUMB];
480
+ }
481
+ const { entry: current, params } = match;
482
+ const chain = [current];
483
+ const seen = /* @__PURE__ */ new Set([current.pattern]);
484
+ let node = current;
485
+ while (node.parent != null) {
486
+ const parent = routeMeta.find((e) => e.pattern === node.parent);
487
+ if (parent == null || seen.has(parent.pattern)) {
488
+ break;
489
+ }
490
+ seen.add(parent.pattern);
491
+ chain.unshift(parent);
492
+ node = parent;
493
+ }
494
+ const crumbs = [HOME_CRUMB];
495
+ chain.forEach((entry, index) => {
496
+ const isLast = index === chain.length - 1;
497
+ crumbs.push(isLast ? { label: entry.label } : { label: entry.label, href: resolvePattern(entry.pattern, params) });
498
+ });
499
+ return crumbs;
500
+ }
501
+ function PageHeaderProvider({
502
+ config,
503
+ routeMeta,
504
+ detailContext,
505
+ children
506
+ }) {
507
+ const { pathname } = useLocation();
508
+ const state = useMemo(() => {
509
+ const activeMenuItem = findByHref(config, normalise(pathname));
510
+ const breadcrumbs = routeMeta != null ? buildBreadcrumbs(pathname, routeMeta) : void 0;
511
+ 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 }) });
515
+ }
516
+ function withPageHeader(config, routeMeta) {
517
+ return (page) => /* @__PURE__ */ jsx(PageHeaderProvider, { config, routeMeta, children: page });
518
+ }
519
+ function normalise(pathname) {
520
+ return pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
521
+ }
522
+ function findByHref(items, pathname) {
523
+ for (const item of items) {
524
+ if (item.href != null && normalise(item.href) === pathname) {
525
+ return item;
526
+ }
527
+ if (item.children) {
528
+ const nested = findByHref(item.children, pathname);
529
+ if (nested) {
530
+ return nested;
531
+ }
532
+ }
533
+ }
534
+ return null;
535
+ }
124
536
 
125
- export { FillGrid, FillGridItem };
537
+ export { FillGrid, FillGridItem, PageHeader, PageHeaderContext, PageHeaderProvider, SidebarActionContext, SidebarActionProvider, buildBreadcrumbs, useActiveSidebarActions, usePageHeader, usePageHeaderContext, useReportSidebarActionActive, useSidebarAction, useSidebarActions, useTriggerSidebarAction, withPageHeader };
126
538
  //# sourceMappingURL=index.js.map
127
539
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/layout/fillGridClasses.ts","../../../src/components/layout/FillGrid.tsx"],"names":[],"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"]}
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"]}
@@ -20,6 +20,10 @@ Object.defineProperty(exports, "Box", {
20
20
  enumerable: true,
21
21
  get: function () { return material.Box; }
22
22
  });
23
+ Object.defineProperty(exports, "Breadcrumbs", {
24
+ enumerable: true,
25
+ get: function () { return material.Breadcrumbs; }
26
+ });
23
27
  Object.defineProperty(exports, "Button", {
24
28
  enumerable: true,
25
29
  get: function () { return material.Button; }
@@ -1,5 +1,5 @@
1
1
  import { DialogProps } from '@mui/material';
2
- export { Alert, AlertTitle, Box, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, SelectChangeEvent, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
2
+ export { Alert, AlertTitle, Box, Breadcrumbs, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, SelectChangeEvent, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
3
3
  import * as react from 'react';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { DialogProps } from '@mui/material';
2
- export { Alert, AlertTitle, Box, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, SelectChangeEvent, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
2
+ export { Alert, AlertTitle, Box, Breadcrumbs, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, SelectChangeEvent, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
3
3
  import * as react from 'react';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { Dialog as Dialog$1 } from '@mui/material';
2
- export { Alert, AlertTitle, Box, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
2
+ export { Alert, AlertTitle, Box, Breadcrumbs, Button, Card, CardActions, CardContent, CardHeader, Chip, CircularProgress, Container, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FilledInput, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid, IconButton, Input, InputAdornment, InputLabel, LinearProgress, Link, ListItemIcon, ListItemText, Menu, MenuItem, OutlinedInput, Paper, Popover, Select, Skeleton, Stack, Switch, Tab, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableHead as TableHeader, TableRow, Tabs, TextField, TextareaAutosize, Tooltip, Typography } from '@mui/material';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
5
5
  // src/components/ui/index.ts