@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.
- package/dist/components/layout/index.cjs +426 -0
- package/dist/components/layout/index.cjs.map +1 -1
- package/dist/components/layout/index.d.cts +290 -1
- package/dist/components/layout/index.d.ts +290 -1
- package/dist/components/layout/index.js +416 -4
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/ui/index.cjs +4 -0
- package/dist/components/ui/index.d.cts +1 -1
- package/dist/components/ui/index.d.ts +1 -1
- package/dist/components/ui/index.js +1 -1
- package/dist/platform-react/index.cjs +45 -2
- package/dist/platform-react/index.cjs.map +1 -1
- package/dist/platform-react/index.d.cts +35 -5
- package/dist/platform-react/index.d.ts +35 -5
- package/dist/platform-react/index.js +45 -2
- package/dist/platform-react/index.js.map +1 -1
- package/package.json +7 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Static Tailwind basis classes for {@link FillGridItem}.
|
|
@@ -45,4 +46,292 @@ interface FillGridItemProps {
|
|
|
45
46
|
}
|
|
46
47
|
declare function FillGridItem({ span, className, children }: FillGridItemProps): react.JSX.Element;
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
/**
|
|
50
|
+
* `@ethisyscore/plugin-ui/components/layout` — page-header contracts.
|
|
51
|
+
*
|
|
52
|
+
* Promoted from the monolith's `src/components/layout/types.ts` +
|
|
53
|
+
* `src/hooks/menu/menuConfig.ts` so every plugin shares ONE header layer instead
|
|
54
|
+
* of copying `PageHeader` + `usePageHeader` + these types per plugin. The shapes
|
|
55
|
+
* are the subset the header machinery actually reads — decoupled from the
|
|
56
|
+
* monolith's shell-only `SubMenuProps`/`MenuActionConfig`, which drag in RBAC,
|
|
57
|
+
* notification, and layout state a plugin never has.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Header-placement hint for an action.
|
|
61
|
+
*
|
|
62
|
+
* - "primary" surfaces as the page-header primary CTA.
|
|
63
|
+
* - "secondary" surfaces as the page-header secondary dropdown (at most one per page).
|
|
64
|
+
* - "overflow" surfaces in the page-header ⋯ More menu.
|
|
65
|
+
* - "sidebar-only" not header-visible (default).
|
|
66
|
+
*/
|
|
67
|
+
type ActionSlot = "primary" | "secondary" | "overflow" | "sidebar-only";
|
|
68
|
+
/**
|
|
69
|
+
* Visual variant of a header action. `"danger"` renders the destructive style;
|
|
70
|
+
* `"primary"`/`"secondary"` are sidebar-pill hints.
|
|
71
|
+
*/
|
|
72
|
+
type HeaderActionVariant = "primary" | "secondary" | "danger";
|
|
73
|
+
/**
|
|
74
|
+
* Header-facing descriptor of an action. Built by `usePageHeader` from a menu
|
|
75
|
+
* item / detail context; consumed by `PageHeader`.
|
|
76
|
+
*/
|
|
77
|
+
interface ActionDescriptor {
|
|
78
|
+
id: string;
|
|
79
|
+
label: string;
|
|
80
|
+
icon?: string;
|
|
81
|
+
/** Anchor href. Mutually exclusive with `onClick`. */
|
|
82
|
+
href?: string;
|
|
83
|
+
/** Imperative click handler. Mutually exclusive with `href`. */
|
|
84
|
+
onClick?: () => void;
|
|
85
|
+
/** Render with danger style (Delete / Cancel). */
|
|
86
|
+
danger?: boolean;
|
|
87
|
+
/** Secondary-slot dropdown children (Lifecycle ▾ etc.). */
|
|
88
|
+
children?: ActionDescriptor[];
|
|
89
|
+
/** Optional disabled state (e.g. Lifecycle option not legal in current state). */
|
|
90
|
+
disabled?: boolean;
|
|
91
|
+
/** Optional tooltip / aria-label for icon-only buttons. */
|
|
92
|
+
description?: ReactNode;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A raw header source action, as authored in a plugin's menu config or supplied
|
|
96
|
+
* by a detail context. The union superset of the monolith's `MenuActionConfig`
|
|
97
|
+
* and `SidebarDetailContextNavItem` — only the fields the header reads.
|
|
98
|
+
*/
|
|
99
|
+
interface HeaderAction {
|
|
100
|
+
id: string;
|
|
101
|
+
label: string;
|
|
102
|
+
icon?: string;
|
|
103
|
+
/** Navigates when clicked. Mutually exclusive with `actionId`/`onClick`. */
|
|
104
|
+
href?: string;
|
|
105
|
+
/** Dispatches via the sidebar-action channel instead of navigating. */
|
|
106
|
+
actionId?: string;
|
|
107
|
+
/** Explicit click handler (detail-context actions can carry one directly). */
|
|
108
|
+
onClick?: () => void;
|
|
109
|
+
/** Visual variant — see {@link HeaderActionVariant}. */
|
|
110
|
+
variant?: HeaderActionVariant;
|
|
111
|
+
/** Page-header placement. Defaults to `"sidebar-only"` (not header-visible). */
|
|
112
|
+
slot?: ActionSlot;
|
|
113
|
+
disabled?: boolean;
|
|
114
|
+
/** Secondary-slot dropdown children. */
|
|
115
|
+
children?: HeaderAction[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A menu item the `PageHeaderProvider` matches against the current route. When
|
|
119
|
+
* the active route is this item's `href`, its `actions` feed the header. Plugins
|
|
120
|
+
* author these as a faithful slice of the monolith's `menuConfig.ts`.
|
|
121
|
+
*/
|
|
122
|
+
interface HeaderMenuItem {
|
|
123
|
+
id: string;
|
|
124
|
+
label: string;
|
|
125
|
+
href?: string;
|
|
126
|
+
group?: string;
|
|
127
|
+
actions?: HeaderAction[];
|
|
128
|
+
children?: HeaderMenuItem[];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Detail-page header context — the actions/title a detail or edit page surfaces
|
|
132
|
+
* without a menu item of its own. A page hook publishes this (or, more commonly,
|
|
133
|
+
* spreads its own `headerProps`); parity with the monolith's `detailContext`.
|
|
134
|
+
*/
|
|
135
|
+
interface HeaderDetailContext {
|
|
136
|
+
title?: string;
|
|
137
|
+
actions?: HeaderAction[];
|
|
138
|
+
}
|
|
139
|
+
/** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
|
|
140
|
+
interface Breadcrumb {
|
|
141
|
+
label: string;
|
|
142
|
+
/** Navigates when clicked; omit for the current (leaf) crumb. */
|
|
143
|
+
href?: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Breadcrumb metadata for one route — a faithful shift of the monolith's
|
|
147
|
+
* `routeMeta.ts` entry: a full path `pattern` (`:`-segments are params) with a
|
|
148
|
+
* `label` and the `parent` route's `pattern`. `buildBreadcrumbs` matches the
|
|
149
|
+
* current path against the patterns (most-specific wins), walks the `parent`
|
|
150
|
+
* chain, prepends Home, and resolves each ancestor's pattern with the captured
|
|
151
|
+
* params — identical to the monolith's breadcrumb builder.
|
|
152
|
+
*
|
|
153
|
+
* `pageId` is the one plugin-only addition (the monolith has no page-bundle ids):
|
|
154
|
+
* it links a pattern to its platform-react page so the manifest generator can emit
|
|
155
|
+
* the page's `route`. The SDK breadcrumb builder ignores it.
|
|
156
|
+
*/
|
|
157
|
+
interface RouteMetaEntry {
|
|
158
|
+
/** Full path pattern, e.g. `/extensions/finance/invoices/:id/edit`. */
|
|
159
|
+
pattern: string;
|
|
160
|
+
label: string;
|
|
161
|
+
/** Parent route's `pattern`; omit for a top-level route. */
|
|
162
|
+
parent?: string;
|
|
163
|
+
/** Platform-react page id this pattern maps to (plugin manifest link; SDK ignores). */
|
|
164
|
+
pageId?: string;
|
|
165
|
+
}
|
|
166
|
+
/** The migrated `routeMeta` slice — a flat array, same shape as the monolith. */
|
|
167
|
+
type RouteMeta = RouteMetaEntry[];
|
|
168
|
+
/**
|
|
169
|
+
* What `PageHeaderContext` publishes — the subset of the monolith's `SubMenuProps`
|
|
170
|
+
* that `usePageHeader` reads. `null` when no provider is mounted (a page rendered
|
|
171
|
+
* standalone, e.g. a test) → the hook derives empty header props.
|
|
172
|
+
*/
|
|
173
|
+
interface PageHeaderState {
|
|
174
|
+
activeMenuItem?: HeaderMenuItem | null;
|
|
175
|
+
detailContext?: HeaderDetailContext | null;
|
|
176
|
+
title?: string;
|
|
177
|
+
subtitle?: ReactNode;
|
|
178
|
+
/** Root breadcrumb trail (e.g. `Home / Finance`); `PageHeader` appends the title. */
|
|
179
|
+
breadcrumbs?: Breadcrumb[];
|
|
180
|
+
}
|
|
181
|
+
/** The prop bundle `usePageHeader` returns, ready to spread onto `<PageHeader>`. */
|
|
182
|
+
interface PageHeaderDerivedProps {
|
|
183
|
+
title: string;
|
|
184
|
+
subtitle?: ReactNode;
|
|
185
|
+
primaryAction?: ActionDescriptor;
|
|
186
|
+
secondaryAction?: ActionDescriptor;
|
|
187
|
+
overflowActions?: ActionDescriptor[];
|
|
188
|
+
/** Root breadcrumb trail; `PageHeader` appends the current `title` as the leaf. */
|
|
189
|
+
breadcrumbs?: Breadcrumb[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the
|
|
194
|
+
* monolith so plugins share one component instead of copying it. In a plugin this
|
|
195
|
+
* IS the page header: the host suppresses its own chrome for platform-react
|
|
196
|
+
* surfaces, so this renders the breadcrumb trail, the title, and the CTAs.
|
|
197
|
+
*
|
|
198
|
+
* `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is
|
|
199
|
+
* appended as the non-link leaf crumb. MUI primitives come from the sibling
|
|
200
|
+
* `../ui` barrel (host externals, never bundled); `Link` is react-router's so
|
|
201
|
+
* links keep middle-click / open-in-new-tab / a11y.
|
|
202
|
+
*/
|
|
203
|
+
interface PageHeaderProps {
|
|
204
|
+
title: string;
|
|
205
|
+
subtitle?: ReactNode;
|
|
206
|
+
primaryAction?: ActionDescriptor;
|
|
207
|
+
secondaryAction?: ActionDescriptor;
|
|
208
|
+
overflowActions?: ActionDescriptor[];
|
|
209
|
+
/** Root breadcrumb trail; the `title` is appended as the current-page leaf. */
|
|
210
|
+
breadcrumbs?: Breadcrumb[];
|
|
211
|
+
}
|
|
212
|
+
declare function PageHeader({ title, subtitle, primaryAction, secondaryAction, overflowActions, breadcrumbs, }: PageHeaderProps): react.JSX.Element;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Derives `<PageHeader>` props from the active menu item or detail context —
|
|
216
|
+
* promoted verbatim from the monolith's `usePageHeader`, reading the plugin's
|
|
217
|
+
* `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.
|
|
218
|
+
*
|
|
219
|
+
* Outside a mounted `PageHeaderProvider` (or in a test rendering a page
|
|
220
|
+
* standalone) the context is `null` and the hook derives empty header props —
|
|
221
|
+
* same shape, no work. Page hooks spread the result and may override
|
|
222
|
+
* title/subtitle/actions.
|
|
223
|
+
*/
|
|
224
|
+
declare function usePageHeader(): PageHeaderDerivedProps;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the
|
|
228
|
+
* monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`
|
|
229
|
+
* patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps
|
|
230
|
+
* each ancestor to its resolved-pattern href (the current page gets no href). The
|
|
231
|
+
* app/module crumb (e.g. "Finance") is just the top-level `routeMeta` entry — no
|
|
232
|
+
* separate config, exactly like the monolith's `/finance` root entry.
|
|
233
|
+
*/
|
|
234
|
+
declare function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[];
|
|
235
|
+
|
|
236
|
+
interface PageHeaderProviderProps {
|
|
237
|
+
/**
|
|
238
|
+
* The plugin's menu-item config — a faithful slice of the monolith's
|
|
239
|
+
* `menuConfig.ts`. The provider matches the active route against each item's
|
|
240
|
+
* `href` (pre-order, so a parent list item wins over its own overview child)
|
|
241
|
+
* and publishes the match so `usePageHeader` can surface its `actions`.
|
|
242
|
+
*/
|
|
243
|
+
config: HeaderMenuItem[];
|
|
244
|
+
/**
|
|
245
|
+
* The plugin's breadcrumb metadata — a faithful slice of the monolith's
|
|
246
|
+
* `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives
|
|
247
|
+
* the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like
|
|
248
|
+
* the monolith. The app/module crumb (e.g. "Finance") is just its top-level entry.
|
|
249
|
+
* Omit for no breadcrumbs.
|
|
250
|
+
*/
|
|
251
|
+
routeMeta?: RouteMeta;
|
|
252
|
+
/** Optional detail-context override for detail/edit routes with no menu item. */
|
|
253
|
+
detailContext?: HeaderDetailContext | null;
|
|
254
|
+
children: ReactNode;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted
|
|
258
|
+
* once at the plugin's router root, it resolves the active menu item from the
|
|
259
|
+
* current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb
|
|
260
|
+
* trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`
|
|
261
|
+
* header CTAs can dispatch to page-registered handlers.
|
|
262
|
+
*
|
|
263
|
+
* In a plugin this feeds the in-body `PageHeader` that migrated pages render —
|
|
264
|
+
* which IS the surface header (the host suppresses its own chrome for
|
|
265
|
+
* platform-react surfaces).
|
|
266
|
+
*/
|
|
267
|
+
declare function PageHeaderProvider({ config, routeMeta, detailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
|
|
268
|
+
/**
|
|
269
|
+
* Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:
|
|
270
|
+
* `withPageHeader(config, routeMeta, app)` returns
|
|
271
|
+
* `(page) => <PageHeaderProvider …>{page}</>`.
|
|
272
|
+
*
|
|
273
|
+
* Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on
|
|
274
|
+
* purpose: the provider and the hook must resolve to ONE `PageHeaderContext`
|
|
275
|
+
* instance, which only holds when both come from this `components/layout` entry.
|
|
276
|
+
* (A bundler inlines a separate context copy into each entry, so wrapping from the
|
|
277
|
+
* `platform-react` entry would give the provider a different context than the
|
|
278
|
+
* hook — the header would render empty.) A plugin passes
|
|
279
|
+
* `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`
|
|
280
|
+
* to `createPluginPageDefiner`.
|
|
281
|
+
*/
|
|
282
|
+
declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta): (page: ReactElement) => ReactElement;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Carries the active menu item / detail context so `usePageHeader` can derive
|
|
286
|
+
* header props. The plugin analogue of the monolith shell's
|
|
287
|
+
* `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.
|
|
288
|
+
*
|
|
289
|
+
* `null` when no provider is mounted (a page rendered standalone, e.g. a test) —
|
|
290
|
+
* consumers treat `null` as "no header context" and derive empty props.
|
|
291
|
+
*/
|
|
292
|
+
declare const PageHeaderContext: react.Context<PageHeaderState | null>;
|
|
293
|
+
/** Returns the provider-published header state, or `null` when none is mounted. */
|
|
294
|
+
declare function usePageHeaderContext(): PageHeaderState | null;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Sidebar-action dispatch channel — promoted from the monolith's
|
|
298
|
+
* `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an
|
|
299
|
+
* `href`) dispatch through here; a page registers a handler via
|
|
300
|
+
* `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs
|
|
301
|
+
* ("New Supplier Invoice", "New Cost Code", …) fire when clicked.
|
|
302
|
+
*/
|
|
303
|
+
type ActionHandler = (actionId: string) => void;
|
|
304
|
+
interface SidebarActionContextValue {
|
|
305
|
+
/** Dispatch an action to all registered page handlers. */
|
|
306
|
+
triggerAction: (actionId: string) => void;
|
|
307
|
+
/** Register a handler that receives sidebar actions. Returns an unregister function. */
|
|
308
|
+
registerHandler: (handler: ActionHandler) => () => void;
|
|
309
|
+
/** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */
|
|
310
|
+
activeActions: ReadonlySet<string>;
|
|
311
|
+
/** Reports whether the owning UI surface for `actionId` is currently open. */
|
|
312
|
+
setActionActive: (actionId: string, isActive: boolean) => void;
|
|
313
|
+
}
|
|
314
|
+
declare const SidebarActionContext: react.Context<SidebarActionContextValue>;
|
|
315
|
+
declare function SidebarActionProvider({ children }: {
|
|
316
|
+
children: ReactNode;
|
|
317
|
+
}): react.JSX.Element;
|
|
318
|
+
/** Used by the header to dispatch actions to pages. */
|
|
319
|
+
declare function useTriggerSidebarAction(): (actionId: string) => void;
|
|
320
|
+
/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */
|
|
321
|
+
declare function useSidebarAction(handler: ActionHandler): void;
|
|
322
|
+
/**
|
|
323
|
+
* Registers a map of sidebar-action handlers keyed by action id, dispatching the
|
|
324
|
+
* matching entry when an action fires (unknown ids are ignored). The handler map
|
|
325
|
+
* may change every render — a ref keeps the dispatcher stable so the registration
|
|
326
|
+
* doesn't churn.
|
|
327
|
+
*/
|
|
328
|
+
declare function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void;
|
|
329
|
+
/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */
|
|
330
|
+
declare function useActiveSidebarActions(): ReadonlySet<string>;
|
|
331
|
+
/**
|
|
332
|
+
* Reports to the channel whether the owning UI surface for `actionId` is open.
|
|
333
|
+
* Pages call this from a hook scope, mirroring their dialog/drawer open state.
|
|
334
|
+
*/
|
|
335
|
+
declare function useReportSidebarActionActive(actionId: string, isActive: boolean): void;
|
|
336
|
+
|
|
337
|
+
export { type ActionDescriptor, type ActionHandler, type ActionSlot, type Breadcrumb, type FillGap, FillGrid, FillGridItem, type FillSpan, type HeaderAction, type HeaderActionVariant, type HeaderDetailContext, type HeaderMenuItem, PageHeader, PageHeaderContext, type PageHeaderDerivedProps, type PageHeaderProps, PageHeaderProvider, type PageHeaderProviderProps, type PageHeaderState, type RouteMeta, type RouteMetaEntry, SidebarActionContext, type SidebarActionContextValue, SidebarActionProvider, type Twelfths, buildBreadcrumbs, useActiveSidebarActions, usePageHeader, usePageHeaderContext, useReportSidebarActionActive, useSidebarAction, useSidebarActions, useTriggerSidebarAction, withPageHeader };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Static Tailwind basis classes for {@link FillGridItem}.
|
|
@@ -45,4 +46,292 @@ interface FillGridItemProps {
|
|
|
45
46
|
}
|
|
46
47
|
declare function FillGridItem({ span, className, children }: FillGridItemProps): react.JSX.Element;
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
/**
|
|
50
|
+
* `@ethisyscore/plugin-ui/components/layout` — page-header contracts.
|
|
51
|
+
*
|
|
52
|
+
* Promoted from the monolith's `src/components/layout/types.ts` +
|
|
53
|
+
* `src/hooks/menu/menuConfig.ts` so every plugin shares ONE header layer instead
|
|
54
|
+
* of copying `PageHeader` + `usePageHeader` + these types per plugin. The shapes
|
|
55
|
+
* are the subset the header machinery actually reads — decoupled from the
|
|
56
|
+
* monolith's shell-only `SubMenuProps`/`MenuActionConfig`, which drag in RBAC,
|
|
57
|
+
* notification, and layout state a plugin never has.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Header-placement hint for an action.
|
|
61
|
+
*
|
|
62
|
+
* - "primary" surfaces as the page-header primary CTA.
|
|
63
|
+
* - "secondary" surfaces as the page-header secondary dropdown (at most one per page).
|
|
64
|
+
* - "overflow" surfaces in the page-header ⋯ More menu.
|
|
65
|
+
* - "sidebar-only" not header-visible (default).
|
|
66
|
+
*/
|
|
67
|
+
type ActionSlot = "primary" | "secondary" | "overflow" | "sidebar-only";
|
|
68
|
+
/**
|
|
69
|
+
* Visual variant of a header action. `"danger"` renders the destructive style;
|
|
70
|
+
* `"primary"`/`"secondary"` are sidebar-pill hints.
|
|
71
|
+
*/
|
|
72
|
+
type HeaderActionVariant = "primary" | "secondary" | "danger";
|
|
73
|
+
/**
|
|
74
|
+
* Header-facing descriptor of an action. Built by `usePageHeader` from a menu
|
|
75
|
+
* item / detail context; consumed by `PageHeader`.
|
|
76
|
+
*/
|
|
77
|
+
interface ActionDescriptor {
|
|
78
|
+
id: string;
|
|
79
|
+
label: string;
|
|
80
|
+
icon?: string;
|
|
81
|
+
/** Anchor href. Mutually exclusive with `onClick`. */
|
|
82
|
+
href?: string;
|
|
83
|
+
/** Imperative click handler. Mutually exclusive with `href`. */
|
|
84
|
+
onClick?: () => void;
|
|
85
|
+
/** Render with danger style (Delete / Cancel). */
|
|
86
|
+
danger?: boolean;
|
|
87
|
+
/** Secondary-slot dropdown children (Lifecycle ▾ etc.). */
|
|
88
|
+
children?: ActionDescriptor[];
|
|
89
|
+
/** Optional disabled state (e.g. Lifecycle option not legal in current state). */
|
|
90
|
+
disabled?: boolean;
|
|
91
|
+
/** Optional tooltip / aria-label for icon-only buttons. */
|
|
92
|
+
description?: ReactNode;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A raw header source action, as authored in a plugin's menu config or supplied
|
|
96
|
+
* by a detail context. The union superset of the monolith's `MenuActionConfig`
|
|
97
|
+
* and `SidebarDetailContextNavItem` — only the fields the header reads.
|
|
98
|
+
*/
|
|
99
|
+
interface HeaderAction {
|
|
100
|
+
id: string;
|
|
101
|
+
label: string;
|
|
102
|
+
icon?: string;
|
|
103
|
+
/** Navigates when clicked. Mutually exclusive with `actionId`/`onClick`. */
|
|
104
|
+
href?: string;
|
|
105
|
+
/** Dispatches via the sidebar-action channel instead of navigating. */
|
|
106
|
+
actionId?: string;
|
|
107
|
+
/** Explicit click handler (detail-context actions can carry one directly). */
|
|
108
|
+
onClick?: () => void;
|
|
109
|
+
/** Visual variant — see {@link HeaderActionVariant}. */
|
|
110
|
+
variant?: HeaderActionVariant;
|
|
111
|
+
/** Page-header placement. Defaults to `"sidebar-only"` (not header-visible). */
|
|
112
|
+
slot?: ActionSlot;
|
|
113
|
+
disabled?: boolean;
|
|
114
|
+
/** Secondary-slot dropdown children. */
|
|
115
|
+
children?: HeaderAction[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A menu item the `PageHeaderProvider` matches against the current route. When
|
|
119
|
+
* the active route is this item's `href`, its `actions` feed the header. Plugins
|
|
120
|
+
* author these as a faithful slice of the monolith's `menuConfig.ts`.
|
|
121
|
+
*/
|
|
122
|
+
interface HeaderMenuItem {
|
|
123
|
+
id: string;
|
|
124
|
+
label: string;
|
|
125
|
+
href?: string;
|
|
126
|
+
group?: string;
|
|
127
|
+
actions?: HeaderAction[];
|
|
128
|
+
children?: HeaderMenuItem[];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Detail-page header context — the actions/title a detail or edit page surfaces
|
|
132
|
+
* without a menu item of its own. A page hook publishes this (or, more commonly,
|
|
133
|
+
* spreads its own `headerProps`); parity with the monolith's `detailContext`.
|
|
134
|
+
*/
|
|
135
|
+
interface HeaderDetailContext {
|
|
136
|
+
title?: string;
|
|
137
|
+
actions?: HeaderAction[];
|
|
138
|
+
}
|
|
139
|
+
/** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
|
|
140
|
+
interface Breadcrumb {
|
|
141
|
+
label: string;
|
|
142
|
+
/** Navigates when clicked; omit for the current (leaf) crumb. */
|
|
143
|
+
href?: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Breadcrumb metadata for one route — a faithful shift of the monolith's
|
|
147
|
+
* `routeMeta.ts` entry: a full path `pattern` (`:`-segments are params) with a
|
|
148
|
+
* `label` and the `parent` route's `pattern`. `buildBreadcrumbs` matches the
|
|
149
|
+
* current path against the patterns (most-specific wins), walks the `parent`
|
|
150
|
+
* chain, prepends Home, and resolves each ancestor's pattern with the captured
|
|
151
|
+
* params — identical to the monolith's breadcrumb builder.
|
|
152
|
+
*
|
|
153
|
+
* `pageId` is the one plugin-only addition (the monolith has no page-bundle ids):
|
|
154
|
+
* it links a pattern to its platform-react page so the manifest generator can emit
|
|
155
|
+
* the page's `route`. The SDK breadcrumb builder ignores it.
|
|
156
|
+
*/
|
|
157
|
+
interface RouteMetaEntry {
|
|
158
|
+
/** Full path pattern, e.g. `/extensions/finance/invoices/:id/edit`. */
|
|
159
|
+
pattern: string;
|
|
160
|
+
label: string;
|
|
161
|
+
/** Parent route's `pattern`; omit for a top-level route. */
|
|
162
|
+
parent?: string;
|
|
163
|
+
/** Platform-react page id this pattern maps to (plugin manifest link; SDK ignores). */
|
|
164
|
+
pageId?: string;
|
|
165
|
+
}
|
|
166
|
+
/** The migrated `routeMeta` slice — a flat array, same shape as the monolith. */
|
|
167
|
+
type RouteMeta = RouteMetaEntry[];
|
|
168
|
+
/**
|
|
169
|
+
* What `PageHeaderContext` publishes — the subset of the monolith's `SubMenuProps`
|
|
170
|
+
* that `usePageHeader` reads. `null` when no provider is mounted (a page rendered
|
|
171
|
+
* standalone, e.g. a test) → the hook derives empty header props.
|
|
172
|
+
*/
|
|
173
|
+
interface PageHeaderState {
|
|
174
|
+
activeMenuItem?: HeaderMenuItem | null;
|
|
175
|
+
detailContext?: HeaderDetailContext | null;
|
|
176
|
+
title?: string;
|
|
177
|
+
subtitle?: ReactNode;
|
|
178
|
+
/** Root breadcrumb trail (e.g. `Home / Finance`); `PageHeader` appends the title. */
|
|
179
|
+
breadcrumbs?: Breadcrumb[];
|
|
180
|
+
}
|
|
181
|
+
/** The prop bundle `usePageHeader` returns, ready to spread onto `<PageHeader>`. */
|
|
182
|
+
interface PageHeaderDerivedProps {
|
|
183
|
+
title: string;
|
|
184
|
+
subtitle?: ReactNode;
|
|
185
|
+
primaryAction?: ActionDescriptor;
|
|
186
|
+
secondaryAction?: ActionDescriptor;
|
|
187
|
+
overflowActions?: ActionDescriptor[];
|
|
188
|
+
/** Root breadcrumb trail; `PageHeader` appends the current `title` as the leaf. */
|
|
189
|
+
breadcrumbs?: Breadcrumb[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* `@ethisyscore/plugin-ui/components/layout` — the page-header, promoted from the
|
|
194
|
+
* monolith so plugins share one component instead of copying it. In a plugin this
|
|
195
|
+
* IS the page header: the host suppresses its own chrome for platform-react
|
|
196
|
+
* surfaces, so this renders the breadcrumb trail, the title, and the CTAs.
|
|
197
|
+
*
|
|
198
|
+
* `breadcrumbs` is the ROOT trail (e.g. `Home / Finance`); the current `title` is
|
|
199
|
+
* appended as the non-link leaf crumb. MUI primitives come from the sibling
|
|
200
|
+
* `../ui` barrel (host externals, never bundled); `Link` is react-router's so
|
|
201
|
+
* links keep middle-click / open-in-new-tab / a11y.
|
|
202
|
+
*/
|
|
203
|
+
interface PageHeaderProps {
|
|
204
|
+
title: string;
|
|
205
|
+
subtitle?: ReactNode;
|
|
206
|
+
primaryAction?: ActionDescriptor;
|
|
207
|
+
secondaryAction?: ActionDescriptor;
|
|
208
|
+
overflowActions?: ActionDescriptor[];
|
|
209
|
+
/** Root breadcrumb trail; the `title` is appended as the current-page leaf. */
|
|
210
|
+
breadcrumbs?: Breadcrumb[];
|
|
211
|
+
}
|
|
212
|
+
declare function PageHeader({ title, subtitle, primaryAction, secondaryAction, overflowActions, breadcrumbs, }: PageHeaderProps): react.JSX.Element;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Derives `<PageHeader>` props from the active menu item or detail context —
|
|
216
|
+
* promoted verbatim from the monolith's `usePageHeader`, reading the plugin's
|
|
217
|
+
* `PageHeaderContext` instead of the shell's `SubMenuPropsContext`.
|
|
218
|
+
*
|
|
219
|
+
* Outside a mounted `PageHeaderProvider` (or in a test rendering a page
|
|
220
|
+
* standalone) the context is `null` and the hook derives empty header props —
|
|
221
|
+
* same shape, no work. Page hooks spread the result and may override
|
|
222
|
+
* title/subtitle/actions.
|
|
223
|
+
*/
|
|
224
|
+
declare function usePageHeader(): PageHeaderDerivedProps;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Builds the `Home / … / <page>` breadcrumb trail — a faithful port of the
|
|
228
|
+
* monolith's `buildBreadcrumbs`. Matches the current path against the `routeMeta`
|
|
229
|
+
* patterns (most-specific wins), walks the `parent` chain, prepends Home, and maps
|
|
230
|
+
* each ancestor to its resolved-pattern href (the current page gets no href). The
|
|
231
|
+
* app/module crumb (e.g. "Finance") is just the top-level `routeMeta` entry — no
|
|
232
|
+
* separate config, exactly like the monolith's `/finance` root entry.
|
|
233
|
+
*/
|
|
234
|
+
declare function buildBreadcrumbs(pathname: string, routeMeta: RouteMeta): Breadcrumb[];
|
|
235
|
+
|
|
236
|
+
interface PageHeaderProviderProps {
|
|
237
|
+
/**
|
|
238
|
+
* The plugin's menu-item config — a faithful slice of the monolith's
|
|
239
|
+
* `menuConfig.ts`. The provider matches the active route against each item's
|
|
240
|
+
* `href` (pre-order, so a parent list item wins over its own overview child)
|
|
241
|
+
* and publishes the match so `usePageHeader` can surface its `actions`.
|
|
242
|
+
*/
|
|
243
|
+
config: HeaderMenuItem[];
|
|
244
|
+
/**
|
|
245
|
+
* The plugin's breadcrumb metadata — a faithful slice of the monolith's
|
|
246
|
+
* `routeMeta.ts` (`{ pattern, label, parent }[]`, re-rooted). The provider derives
|
|
247
|
+
* the full `Home / … / <Page>` trail per route via `buildBreadcrumbs`, exactly like
|
|
248
|
+
* the monolith. The app/module crumb (e.g. "Finance") is just its top-level entry.
|
|
249
|
+
* Omit for no breadcrumbs.
|
|
250
|
+
*/
|
|
251
|
+
routeMeta?: RouteMeta;
|
|
252
|
+
/** Optional detail-context override for detail/edit routes with no menu item. */
|
|
253
|
+
detailContext?: HeaderDetailContext | null;
|
|
254
|
+
children: ReactNode;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Plugin analogue of the monolith shell's `SubMenuPropsContext` provider: mounted
|
|
258
|
+
* once at the plugin's router root, it resolves the active menu item from the
|
|
259
|
+
* current route (feeding `usePageHeader`'s title + CTAs) and derives the breadcrumb
|
|
260
|
+
* trail from `routeMeta`. Also mounts the `SidebarActionProvider` so `actionId`
|
|
261
|
+
* header CTAs can dispatch to page-registered handlers.
|
|
262
|
+
*
|
|
263
|
+
* In a plugin this feeds the in-body `PageHeader` that migrated pages render —
|
|
264
|
+
* which IS the surface header (the host suppresses its own chrome for
|
|
265
|
+
* platform-react surfaces).
|
|
266
|
+
*/
|
|
267
|
+
declare function PageHeaderProvider({ config, routeMeta, detailContext, children, }: PageHeaderProviderProps): react.JSX.Element;
|
|
268
|
+
/**
|
|
269
|
+
* Ready-made page wrapper for the PlatformReact page-definer's `wrapPage` option:
|
|
270
|
+
* `withPageHeader(config, routeMeta, app)` returns
|
|
271
|
+
* `(page) => <PageHeaderProvider …>{page}</>`.
|
|
272
|
+
*
|
|
273
|
+
* Kept here — the same module as `PageHeaderProvider` and `usePageHeader` — on
|
|
274
|
+
* purpose: the provider and the hook must resolve to ONE `PageHeaderContext`
|
|
275
|
+
* instance, which only holds when both come from this `components/layout` entry.
|
|
276
|
+
* (A bundler inlines a separate context copy into each entry, so wrapping from the
|
|
277
|
+
* `platform-react` entry would give the provider a different context than the
|
|
278
|
+
* hook — the header would render empty.) A plugin passes
|
|
279
|
+
* `wrapPage: withPageHeader(<feature>MenuConfig, <feature>RouteMeta)`
|
|
280
|
+
* to `createPluginPageDefiner`.
|
|
281
|
+
*/
|
|
282
|
+
declare function withPageHeader(config: HeaderMenuItem[], routeMeta?: RouteMeta): (page: ReactElement) => ReactElement;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Carries the active menu item / detail context so `usePageHeader` can derive
|
|
286
|
+
* header props. The plugin analogue of the monolith shell's
|
|
287
|
+
* `SubMenuPropsContext`: mounted once by `PageHeaderProvider`, read per page.
|
|
288
|
+
*
|
|
289
|
+
* `null` when no provider is mounted (a page rendered standalone, e.g. a test) —
|
|
290
|
+
* consumers treat `null` as "no header context" and derive empty props.
|
|
291
|
+
*/
|
|
292
|
+
declare const PageHeaderContext: react.Context<PageHeaderState | null>;
|
|
293
|
+
/** Returns the provider-published header state, or `null` when none is mounted. */
|
|
294
|
+
declare function usePageHeaderContext(): PageHeaderState | null;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Sidebar-action dispatch channel — promoted from the monolith's
|
|
298
|
+
* `SidebarActionContext`. Header buttons carrying an `actionId` (rather than an
|
|
299
|
+
* `href`) dispatch through here; a page registers a handler via
|
|
300
|
+
* `useSidebarAction` to open its own dialog. This is what makes `actionId` CTAs
|
|
301
|
+
* ("New Supplier Invoice", "New Cost Code", …) fire when clicked.
|
|
302
|
+
*/
|
|
303
|
+
type ActionHandler = (actionId: string) => void;
|
|
304
|
+
interface SidebarActionContextValue {
|
|
305
|
+
/** Dispatch an action to all registered page handlers. */
|
|
306
|
+
triggerAction: (actionId: string) => void;
|
|
307
|
+
/** Register a handler that receives sidebar actions. Returns an unregister function. */
|
|
308
|
+
registerHandler: (handler: ActionHandler) => () => void;
|
|
309
|
+
/** Set of actionIds whose owning UI surface (e.g. a dialog) is currently open. */
|
|
310
|
+
activeActions: ReadonlySet<string>;
|
|
311
|
+
/** Reports whether the owning UI surface for `actionId` is currently open. */
|
|
312
|
+
setActionActive: (actionId: string, isActive: boolean) => void;
|
|
313
|
+
}
|
|
314
|
+
declare const SidebarActionContext: react.Context<SidebarActionContextValue>;
|
|
315
|
+
declare function SidebarActionProvider({ children }: {
|
|
316
|
+
children: ReactNode;
|
|
317
|
+
}): react.JSX.Element;
|
|
318
|
+
/** Used by the header to dispatch actions to pages. */
|
|
319
|
+
declare function useTriggerSidebarAction(): (actionId: string) => void;
|
|
320
|
+
/** Used by pages to register a handler for sidebar actions. Auto-cleans up on unmount. */
|
|
321
|
+
declare function useSidebarAction(handler: ActionHandler): void;
|
|
322
|
+
/**
|
|
323
|
+
* Registers a map of sidebar-action handlers keyed by action id, dispatching the
|
|
324
|
+
* matching entry when an action fires (unknown ids are ignored). The handler map
|
|
325
|
+
* may change every render — a ref keeps the dispatcher stable so the registration
|
|
326
|
+
* doesn't churn.
|
|
327
|
+
*/
|
|
328
|
+
declare function useSidebarActions(handlers: Record<string, (() => void) | undefined>): void;
|
|
329
|
+
/** Reads the set of dispatch-action ids whose owning UI surface is currently open. */
|
|
330
|
+
declare function useActiveSidebarActions(): ReadonlySet<string>;
|
|
331
|
+
/**
|
|
332
|
+
* Reports to the channel whether the owning UI surface for `actionId` is open.
|
|
333
|
+
* Pages call this from a hook scope, mirroring their dialog/drawer open state.
|
|
334
|
+
*/
|
|
335
|
+
declare function useReportSidebarActionActive(actionId: string, isActive: boolean): void;
|
|
336
|
+
|
|
337
|
+
export { type ActionDescriptor, type ActionHandler, type ActionSlot, type Breadcrumb, type FillGap, FillGrid, FillGridItem, type FillSpan, type HeaderAction, type HeaderActionVariant, type HeaderDetailContext, type HeaderMenuItem, PageHeader, PageHeaderContext, type PageHeaderDerivedProps, type PageHeaderProps, PageHeaderProvider, type PageHeaderProviderProps, type PageHeaderState, type RouteMeta, type RouteMetaEntry, SidebarActionContext, type SidebarActionContextValue, SidebarActionProvider, type Twelfths, buildBreadcrumbs, useActiveSidebarActions, usePageHeader, usePageHeaderContext, useReportSidebarActionActive, useSidebarAction, useSidebarActions, useTriggerSidebarAction, withPageHeader };
|