@ethisyscore/plugin-ui 1.44.0 → 1.46.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 +47 -0
- package/dist/components/layout/index.cjs.map +1 -1
- package/dist/components/layout/index.d.cts +19 -151
- package/dist/components/layout/index.d.ts +19 -151
- package/dist/components/layout/index.js +47 -1
- package/dist/components/layout/index.js.map +1 -1
- package/dist/pageHeaderTypes-PWZpiwp6.d.cts +198 -0
- package/dist/pageHeaderTypes-PWZpiwp6.d.ts +198 -0
- package/dist/platform-react/index.cjs +66 -0
- package/dist/platform-react/index.cjs.map +1 -1
- package/dist/platform-react/index.d.cts +52 -2
- package/dist/platform-react/index.d.ts +52 -2
- package/dist/platform-react/index.js +66 -1
- package/dist/platform-react/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@ethisyscore/plugin-ui/components/layout` — page-header contracts.
|
|
5
|
+
*
|
|
6
|
+
* Promoted from the monolith's `src/components/layout/types.ts` +
|
|
7
|
+
* `src/hooks/menu/menuConfig.ts` so every plugin shares ONE header layer instead
|
|
8
|
+
* of copying `PageHeader` + `usePageHeader` + these types per plugin. The shapes
|
|
9
|
+
* are the subset the header machinery actually reads — decoupled from the
|
|
10
|
+
* monolith's shell-only `SubMenuProps`/`MenuActionConfig`, which drag in RBAC,
|
|
11
|
+
* notification, and layout state a plugin never has.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Header-placement hint for an action.
|
|
15
|
+
*
|
|
16
|
+
* - "primary" surfaces as the page-header primary CTA.
|
|
17
|
+
* - "secondary" surfaces as the page-header secondary dropdown (at most one per page).
|
|
18
|
+
* - "overflow" surfaces in the page-header ⋯ More menu.
|
|
19
|
+
* - "sidebar-only" not header-visible (default).
|
|
20
|
+
*/
|
|
21
|
+
type ActionSlot = "primary" | "secondary" | "overflow" | "sidebar-only";
|
|
22
|
+
/**
|
|
23
|
+
* Visual variant of a header action. `"danger"` renders the destructive style;
|
|
24
|
+
* `"primary"`/`"secondary"` are sidebar-pill hints.
|
|
25
|
+
*/
|
|
26
|
+
type HeaderActionVariant = "primary" | "secondary" | "danger";
|
|
27
|
+
/**
|
|
28
|
+
* Header-facing descriptor of an action. Built by `usePageHeader` from a menu
|
|
29
|
+
* item / detail context; consumed by `PageHeader`.
|
|
30
|
+
*/
|
|
31
|
+
interface ActionDescriptor {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
icon?: string;
|
|
35
|
+
/** Anchor href. Mutually exclusive with `onClick`. */
|
|
36
|
+
href?: string;
|
|
37
|
+
/** Imperative click handler. Mutually exclusive with `href`. */
|
|
38
|
+
onClick?: () => void;
|
|
39
|
+
/** Render with danger style (Delete / Cancel). */
|
|
40
|
+
danger?: boolean;
|
|
41
|
+
/** Secondary-slot dropdown children (Lifecycle ▾ etc.). */
|
|
42
|
+
children?: ActionDescriptor[];
|
|
43
|
+
/** Optional disabled state (e.g. Lifecycle option not legal in current state). */
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
/** Optional tooltip / aria-label for icon-only buttons. */
|
|
46
|
+
description?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A raw header source action, as authored in a plugin's menu config or supplied
|
|
50
|
+
* by a detail context. The union superset of the monolith's `MenuActionConfig`
|
|
51
|
+
* and `SidebarDetailContextNavItem` — only the fields the header reads.
|
|
52
|
+
*/
|
|
53
|
+
interface HeaderAction {
|
|
54
|
+
id: string;
|
|
55
|
+
label: string;
|
|
56
|
+
icon?: string;
|
|
57
|
+
/** Navigates when clicked. Mutually exclusive with `actionId`/`onClick`. */
|
|
58
|
+
href?: string;
|
|
59
|
+
/** Dispatches via the sidebar-action channel instead of navigating. */
|
|
60
|
+
actionId?: string;
|
|
61
|
+
/** Explicit click handler (detail-context actions can carry one directly). */
|
|
62
|
+
onClick?: () => void;
|
|
63
|
+
/** Visual variant — see {@link HeaderActionVariant}. */
|
|
64
|
+
variant?: HeaderActionVariant;
|
|
65
|
+
/** Page-header placement. Defaults to `"sidebar-only"` (not header-visible). */
|
|
66
|
+
slot?: ActionSlot;
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
/** Secondary-slot dropdown children. */
|
|
69
|
+
children?: HeaderAction[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A menu item the `PageHeaderProvider` matches against the current route. When
|
|
73
|
+
* the active route is this item's `href`, its `actions` feed the header. Plugins
|
|
74
|
+
* author these as a faithful slice of the monolith's `menuConfig.ts`.
|
|
75
|
+
*/
|
|
76
|
+
interface HeaderMenuItem {
|
|
77
|
+
id: string;
|
|
78
|
+
label: string;
|
|
79
|
+
href?: string;
|
|
80
|
+
group?: string;
|
|
81
|
+
actions?: HeaderAction[];
|
|
82
|
+
children?: HeaderMenuItem[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Detail-page header context — the actions/title a detail or edit page surfaces
|
|
86
|
+
* without a menu item of its own. A page hook publishes this (or, more commonly,
|
|
87
|
+
* spreads its own `headerProps`); parity with the monolith's `detailContext`.
|
|
88
|
+
*/
|
|
89
|
+
interface HeaderDetailContext {
|
|
90
|
+
title?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Optional subtitle (e.g. the entity name) shown under the title. Read by
|
|
93
|
+
* `usePageHeader` so a wrapper-rendered header (see
|
|
94
|
+
* {@link PageHeaderProviderProps.useDetailContext}) can surface it without the
|
|
95
|
+
* page spreading its own `headerProps`.
|
|
96
|
+
*/
|
|
97
|
+
subtitle?: ReactNode;
|
|
98
|
+
actions?: HeaderAction[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One action or nav node in a plugin's DETAIL CONTEXT — the rich, shared shape a
|
|
102
|
+
* plugin's `*DetailContext` hook returns (promoted from the monolith's
|
|
103
|
+
* `SidebarDetailContextNavItem`). One such context drives BOTH surfaces of a
|
|
104
|
+
* detail page: the page header (via {@link useDetailHeaderActions}) and the host
|
|
105
|
+
* left-sidebar Quick-actions panel (via `toHostSidebarInput` +
|
|
106
|
+
* `useHostSidebarActions`). A plugin can pass its own `SidebarDetailContext*`
|
|
107
|
+
* value here without changing imports — it assigns structurally.
|
|
108
|
+
*/
|
|
109
|
+
interface PluginDetailContextItem {
|
|
110
|
+
id: string;
|
|
111
|
+
label: string;
|
|
112
|
+
icon?: string;
|
|
113
|
+
/** Navigates when clicked (plugin-surface-relative or absolute plugin path). */
|
|
114
|
+
href?: string;
|
|
115
|
+
/** Dispatches via the sidebar-action channel to a page's `useSidebarActions` handler. */
|
|
116
|
+
actionId?: string;
|
|
117
|
+
/** Explicit click handler (used when there is no `href`/`actionId`). */
|
|
118
|
+
onClick?: () => void;
|
|
119
|
+
/** `"danger"` renders the destructive style. */
|
|
120
|
+
variant?: "default" | "danger";
|
|
121
|
+
/** Header placement — see {@link ActionSlot}. */
|
|
122
|
+
slot?: ActionSlot;
|
|
123
|
+
disabled?: boolean;
|
|
124
|
+
/** Nested items (e.g. a Lifecycle / Share dropdown). */
|
|
125
|
+
children?: PluginDetailContextItem[];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A plugin detail page's context: title, sub-page nav, back link and the action
|
|
129
|
+
* set. Promoted from the monolith's `SidebarDetailContext`; the SDK reads only the
|
|
130
|
+
* fields both surfaces need, so a plugin's richer value assigns to it structurally.
|
|
131
|
+
*/
|
|
132
|
+
interface PluginDetailContext {
|
|
133
|
+
title: string;
|
|
134
|
+
subtitle?: ReactNode;
|
|
135
|
+
/** Sidebar section heading for the nav items (defaults to "Sections"). */
|
|
136
|
+
navSectionLabel?: string;
|
|
137
|
+
/** Sub-page navigation items (each needs an `href`). */
|
|
138
|
+
navItems: PluginDetailContextItem[];
|
|
139
|
+
/** The Edit/Delete/Add action set surfaced in the header and sidebar. */
|
|
140
|
+
actions?: PluginDetailContextItem[];
|
|
141
|
+
/** Back-link target (plugin-surface path). */
|
|
142
|
+
backHref?: string;
|
|
143
|
+
backLabel?: string;
|
|
144
|
+
}
|
|
145
|
+
/** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
|
|
146
|
+
interface Breadcrumb {
|
|
147
|
+
label: string;
|
|
148
|
+
/** Navigates when clicked; omit for the current (leaf) crumb. */
|
|
149
|
+
href?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Breadcrumb metadata for one route — a faithful shift of the monolith's
|
|
153
|
+
* `routeMeta.ts` entry: a full path `pattern` (`:`-segments are params) with a
|
|
154
|
+
* `label` and the `parent` route's `pattern`. `buildBreadcrumbs` matches the
|
|
155
|
+
* current path against the patterns (most-specific wins), walks the `parent`
|
|
156
|
+
* chain, prepends Home, and resolves each ancestor's pattern with the captured
|
|
157
|
+
* params — identical to the monolith's breadcrumb builder.
|
|
158
|
+
*
|
|
159
|
+
* `pageId` is the one plugin-only addition (the monolith has no page-bundle ids):
|
|
160
|
+
* it links a pattern to its platform-react page so the manifest generator can emit
|
|
161
|
+
* the page's `route`. The SDK breadcrumb builder ignores it.
|
|
162
|
+
*/
|
|
163
|
+
interface RouteMetaEntry {
|
|
164
|
+
/** Full path pattern, e.g. `/extensions/finance/invoices/:id/edit`. */
|
|
165
|
+
pattern: string;
|
|
166
|
+
label: string;
|
|
167
|
+
/** Parent route's `pattern`; omit for a top-level route. */
|
|
168
|
+
parent?: string;
|
|
169
|
+
/** Platform-react page id this pattern maps to (plugin manifest link; SDK ignores). */
|
|
170
|
+
pageId?: string;
|
|
171
|
+
}
|
|
172
|
+
/** The migrated `routeMeta` slice — a flat array, same shape as the monolith. */
|
|
173
|
+
type RouteMeta = RouteMetaEntry[];
|
|
174
|
+
/**
|
|
175
|
+
* What `PageHeaderContext` publishes — the subset of the monolith's `SubMenuProps`
|
|
176
|
+
* that `usePageHeader` reads. `null` when no provider is mounted (a page rendered
|
|
177
|
+
* standalone, e.g. a test) → the hook derives empty header props.
|
|
178
|
+
*/
|
|
179
|
+
interface PageHeaderState {
|
|
180
|
+
activeMenuItem?: HeaderMenuItem | null;
|
|
181
|
+
detailContext?: HeaderDetailContext | null;
|
|
182
|
+
title?: string;
|
|
183
|
+
subtitle?: ReactNode;
|
|
184
|
+
/** Root breadcrumb trail (e.g. `Home / Finance`); `PageHeader` appends the title. */
|
|
185
|
+
breadcrumbs?: Breadcrumb[];
|
|
186
|
+
}
|
|
187
|
+
/** The prop bundle `usePageHeader` returns, ready to spread onto `<PageHeader>`. */
|
|
188
|
+
interface PageHeaderDerivedProps {
|
|
189
|
+
title: string;
|
|
190
|
+
subtitle?: ReactNode;
|
|
191
|
+
primaryAction?: ActionDescriptor;
|
|
192
|
+
secondaryAction?: ActionDescriptor;
|
|
193
|
+
overflowActions?: ActionDescriptor[];
|
|
194
|
+
/** Root breadcrumb trail; `PageHeader` appends the current `title` as the leaf. */
|
|
195
|
+
breadcrumbs?: Breadcrumb[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export type { ActionDescriptor as A, Breadcrumb as B, HeaderMenuItem as H, PageHeaderDerivedProps as P, RouteMeta as R, PluginDetailContextItem as a, HeaderDetailContext as b, PageHeaderState as c, ActionSlot as d, HeaderAction as e, HeaderActionVariant as f, PluginDetailContext as g, RouteMetaEntry as h };
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@ethisyscore/plugin-ui/components/layout` — page-header contracts.
|
|
5
|
+
*
|
|
6
|
+
* Promoted from the monolith's `src/components/layout/types.ts` +
|
|
7
|
+
* `src/hooks/menu/menuConfig.ts` so every plugin shares ONE header layer instead
|
|
8
|
+
* of copying `PageHeader` + `usePageHeader` + these types per plugin. The shapes
|
|
9
|
+
* are the subset the header machinery actually reads — decoupled from the
|
|
10
|
+
* monolith's shell-only `SubMenuProps`/`MenuActionConfig`, which drag in RBAC,
|
|
11
|
+
* notification, and layout state a plugin never has.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Header-placement hint for an action.
|
|
15
|
+
*
|
|
16
|
+
* - "primary" surfaces as the page-header primary CTA.
|
|
17
|
+
* - "secondary" surfaces as the page-header secondary dropdown (at most one per page).
|
|
18
|
+
* - "overflow" surfaces in the page-header ⋯ More menu.
|
|
19
|
+
* - "sidebar-only" not header-visible (default).
|
|
20
|
+
*/
|
|
21
|
+
type ActionSlot = "primary" | "secondary" | "overflow" | "sidebar-only";
|
|
22
|
+
/**
|
|
23
|
+
* Visual variant of a header action. `"danger"` renders the destructive style;
|
|
24
|
+
* `"primary"`/`"secondary"` are sidebar-pill hints.
|
|
25
|
+
*/
|
|
26
|
+
type HeaderActionVariant = "primary" | "secondary" | "danger";
|
|
27
|
+
/**
|
|
28
|
+
* Header-facing descriptor of an action. Built by `usePageHeader` from a menu
|
|
29
|
+
* item / detail context; consumed by `PageHeader`.
|
|
30
|
+
*/
|
|
31
|
+
interface ActionDescriptor {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
icon?: string;
|
|
35
|
+
/** Anchor href. Mutually exclusive with `onClick`. */
|
|
36
|
+
href?: string;
|
|
37
|
+
/** Imperative click handler. Mutually exclusive with `href`. */
|
|
38
|
+
onClick?: () => void;
|
|
39
|
+
/** Render with danger style (Delete / Cancel). */
|
|
40
|
+
danger?: boolean;
|
|
41
|
+
/** Secondary-slot dropdown children (Lifecycle ▾ etc.). */
|
|
42
|
+
children?: ActionDescriptor[];
|
|
43
|
+
/** Optional disabled state (e.g. Lifecycle option not legal in current state). */
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
/** Optional tooltip / aria-label for icon-only buttons. */
|
|
46
|
+
description?: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A raw header source action, as authored in a plugin's menu config or supplied
|
|
50
|
+
* by a detail context. The union superset of the monolith's `MenuActionConfig`
|
|
51
|
+
* and `SidebarDetailContextNavItem` — only the fields the header reads.
|
|
52
|
+
*/
|
|
53
|
+
interface HeaderAction {
|
|
54
|
+
id: string;
|
|
55
|
+
label: string;
|
|
56
|
+
icon?: string;
|
|
57
|
+
/** Navigates when clicked. Mutually exclusive with `actionId`/`onClick`. */
|
|
58
|
+
href?: string;
|
|
59
|
+
/** Dispatches via the sidebar-action channel instead of navigating. */
|
|
60
|
+
actionId?: string;
|
|
61
|
+
/** Explicit click handler (detail-context actions can carry one directly). */
|
|
62
|
+
onClick?: () => void;
|
|
63
|
+
/** Visual variant — see {@link HeaderActionVariant}. */
|
|
64
|
+
variant?: HeaderActionVariant;
|
|
65
|
+
/** Page-header placement. Defaults to `"sidebar-only"` (not header-visible). */
|
|
66
|
+
slot?: ActionSlot;
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
/** Secondary-slot dropdown children. */
|
|
69
|
+
children?: HeaderAction[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A menu item the `PageHeaderProvider` matches against the current route. When
|
|
73
|
+
* the active route is this item's `href`, its `actions` feed the header. Plugins
|
|
74
|
+
* author these as a faithful slice of the monolith's `menuConfig.ts`.
|
|
75
|
+
*/
|
|
76
|
+
interface HeaderMenuItem {
|
|
77
|
+
id: string;
|
|
78
|
+
label: string;
|
|
79
|
+
href?: string;
|
|
80
|
+
group?: string;
|
|
81
|
+
actions?: HeaderAction[];
|
|
82
|
+
children?: HeaderMenuItem[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Detail-page header context — the actions/title a detail or edit page surfaces
|
|
86
|
+
* without a menu item of its own. A page hook publishes this (or, more commonly,
|
|
87
|
+
* spreads its own `headerProps`); parity with the monolith's `detailContext`.
|
|
88
|
+
*/
|
|
89
|
+
interface HeaderDetailContext {
|
|
90
|
+
title?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Optional subtitle (e.g. the entity name) shown under the title. Read by
|
|
93
|
+
* `usePageHeader` so a wrapper-rendered header (see
|
|
94
|
+
* {@link PageHeaderProviderProps.useDetailContext}) can surface it without the
|
|
95
|
+
* page spreading its own `headerProps`.
|
|
96
|
+
*/
|
|
97
|
+
subtitle?: ReactNode;
|
|
98
|
+
actions?: HeaderAction[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One action or nav node in a plugin's DETAIL CONTEXT — the rich, shared shape a
|
|
102
|
+
* plugin's `*DetailContext` hook returns (promoted from the monolith's
|
|
103
|
+
* `SidebarDetailContextNavItem`). One such context drives BOTH surfaces of a
|
|
104
|
+
* detail page: the page header (via {@link useDetailHeaderActions}) and the host
|
|
105
|
+
* left-sidebar Quick-actions panel (via `toHostSidebarInput` +
|
|
106
|
+
* `useHostSidebarActions`). A plugin can pass its own `SidebarDetailContext*`
|
|
107
|
+
* value here without changing imports — it assigns structurally.
|
|
108
|
+
*/
|
|
109
|
+
interface PluginDetailContextItem {
|
|
110
|
+
id: string;
|
|
111
|
+
label: string;
|
|
112
|
+
icon?: string;
|
|
113
|
+
/** Navigates when clicked (plugin-surface-relative or absolute plugin path). */
|
|
114
|
+
href?: string;
|
|
115
|
+
/** Dispatches via the sidebar-action channel to a page's `useSidebarActions` handler. */
|
|
116
|
+
actionId?: string;
|
|
117
|
+
/** Explicit click handler (used when there is no `href`/`actionId`). */
|
|
118
|
+
onClick?: () => void;
|
|
119
|
+
/** `"danger"` renders the destructive style. */
|
|
120
|
+
variant?: "default" | "danger";
|
|
121
|
+
/** Header placement — see {@link ActionSlot}. */
|
|
122
|
+
slot?: ActionSlot;
|
|
123
|
+
disabled?: boolean;
|
|
124
|
+
/** Nested items (e.g. a Lifecycle / Share dropdown). */
|
|
125
|
+
children?: PluginDetailContextItem[];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A plugin detail page's context: title, sub-page nav, back link and the action
|
|
129
|
+
* set. Promoted from the monolith's `SidebarDetailContext`; the SDK reads only the
|
|
130
|
+
* fields both surfaces need, so a plugin's richer value assigns to it structurally.
|
|
131
|
+
*/
|
|
132
|
+
interface PluginDetailContext {
|
|
133
|
+
title: string;
|
|
134
|
+
subtitle?: ReactNode;
|
|
135
|
+
/** Sidebar section heading for the nav items (defaults to "Sections"). */
|
|
136
|
+
navSectionLabel?: string;
|
|
137
|
+
/** Sub-page navigation items (each needs an `href`). */
|
|
138
|
+
navItems: PluginDetailContextItem[];
|
|
139
|
+
/** The Edit/Delete/Add action set surfaced in the header and sidebar. */
|
|
140
|
+
actions?: PluginDetailContextItem[];
|
|
141
|
+
/** Back-link target (plugin-surface path). */
|
|
142
|
+
backHref?: string;
|
|
143
|
+
backLabel?: string;
|
|
144
|
+
}
|
|
145
|
+
/** One node in the header breadcrumb trail (`Home / Finance / <page>`). */
|
|
146
|
+
interface Breadcrumb {
|
|
147
|
+
label: string;
|
|
148
|
+
/** Navigates when clicked; omit for the current (leaf) crumb. */
|
|
149
|
+
href?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Breadcrumb metadata for one route — a faithful shift of the monolith's
|
|
153
|
+
* `routeMeta.ts` entry: a full path `pattern` (`:`-segments are params) with a
|
|
154
|
+
* `label` and the `parent` route's `pattern`. `buildBreadcrumbs` matches the
|
|
155
|
+
* current path against the patterns (most-specific wins), walks the `parent`
|
|
156
|
+
* chain, prepends Home, and resolves each ancestor's pattern with the captured
|
|
157
|
+
* params — identical to the monolith's breadcrumb builder.
|
|
158
|
+
*
|
|
159
|
+
* `pageId` is the one plugin-only addition (the monolith has no page-bundle ids):
|
|
160
|
+
* it links a pattern to its platform-react page so the manifest generator can emit
|
|
161
|
+
* the page's `route`. The SDK breadcrumb builder ignores it.
|
|
162
|
+
*/
|
|
163
|
+
interface RouteMetaEntry {
|
|
164
|
+
/** Full path pattern, e.g. `/extensions/finance/invoices/:id/edit`. */
|
|
165
|
+
pattern: string;
|
|
166
|
+
label: string;
|
|
167
|
+
/** Parent route's `pattern`; omit for a top-level route. */
|
|
168
|
+
parent?: string;
|
|
169
|
+
/** Platform-react page id this pattern maps to (plugin manifest link; SDK ignores). */
|
|
170
|
+
pageId?: string;
|
|
171
|
+
}
|
|
172
|
+
/** The migrated `routeMeta` slice — a flat array, same shape as the monolith. */
|
|
173
|
+
type RouteMeta = RouteMetaEntry[];
|
|
174
|
+
/**
|
|
175
|
+
* What `PageHeaderContext` publishes — the subset of the monolith's `SubMenuProps`
|
|
176
|
+
* that `usePageHeader` reads. `null` when no provider is mounted (a page rendered
|
|
177
|
+
* standalone, e.g. a test) → the hook derives empty header props.
|
|
178
|
+
*/
|
|
179
|
+
interface PageHeaderState {
|
|
180
|
+
activeMenuItem?: HeaderMenuItem | null;
|
|
181
|
+
detailContext?: HeaderDetailContext | null;
|
|
182
|
+
title?: string;
|
|
183
|
+
subtitle?: ReactNode;
|
|
184
|
+
/** Root breadcrumb trail (e.g. `Home / Finance`); `PageHeader` appends the title. */
|
|
185
|
+
breadcrumbs?: Breadcrumb[];
|
|
186
|
+
}
|
|
187
|
+
/** The prop bundle `usePageHeader` returns, ready to spread onto `<PageHeader>`. */
|
|
188
|
+
interface PageHeaderDerivedProps {
|
|
189
|
+
title: string;
|
|
190
|
+
subtitle?: ReactNode;
|
|
191
|
+
primaryAction?: ActionDescriptor;
|
|
192
|
+
secondaryAction?: ActionDescriptor;
|
|
193
|
+
overflowActions?: ActionDescriptor[];
|
|
194
|
+
/** Root breadcrumb trail; `PageHeader` appends the current `title` as the leaf. */
|
|
195
|
+
breadcrumbs?: Breadcrumb[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export type { ActionDescriptor as A, Breadcrumb as B, HeaderMenuItem as H, PageHeaderDerivedProps as P, RouteMeta as R, PluginDetailContextItem as a, HeaderDetailContext as b, PageHeaderState as c, ActionSlot as d, HeaderAction as e, HeaderActionVariant as f, PluginDetailContext as g, RouteMetaEntry as h };
|
|
@@ -648,6 +648,71 @@ function useHostSidebarActions(input) {
|
|
|
648
648
|
}, [compatible, key]);
|
|
649
649
|
}
|
|
650
650
|
|
|
651
|
+
// src/platform-react/toHostSidebarInput.ts
|
|
652
|
+
function toHostSidebarInput(context, opts) {
|
|
653
|
+
if (!context || !opts.entityToken) {
|
|
654
|
+
return { mode: "none" };
|
|
655
|
+
}
|
|
656
|
+
const navItems = context.navItems.map((n) => ({ id: n.id, label: n.label, icon: n.icon, href: slugRelative(n.href, opts.basePath) })).filter((n) => isConfinable(n.href));
|
|
657
|
+
const mappedBack = slugRelative(context.backHref, opts.basePath);
|
|
658
|
+
const backHref = isConfinable(mappedBack) ? mappedBack : opts.backHrefFallback ?? "";
|
|
659
|
+
return {
|
|
660
|
+
mode: "detail",
|
|
661
|
+
entityToken: opts.entityToken,
|
|
662
|
+
title: context.title,
|
|
663
|
+
navSectionLabel: context.navSectionLabel ?? "Sections",
|
|
664
|
+
navItems,
|
|
665
|
+
backHref,
|
|
666
|
+
backLabel: context.backLabel ?? "Back",
|
|
667
|
+
actions: toHostActions(context.actions ?? [], opts)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function slugRelative(href, basePath) {
|
|
671
|
+
if (!href) return "";
|
|
672
|
+
const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
673
|
+
if (href === base) return "";
|
|
674
|
+
const prefix = `${base}/`;
|
|
675
|
+
return href.startsWith(prefix) ? href.slice(prefix.length) : href;
|
|
676
|
+
}
|
|
677
|
+
function isConfinable(slugRelativeHref) {
|
|
678
|
+
return slugRelativeHref.length > 0 && !slugRelativeHref.startsWith("/") && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(slugRelativeHref) && !slugRelativeHref.includes("#");
|
|
679
|
+
}
|
|
680
|
+
function flattenLeaves(items) {
|
|
681
|
+
const out = [];
|
|
682
|
+
for (const item of items) {
|
|
683
|
+
if (item.children?.length) out.push(...flattenLeaves(item.children));
|
|
684
|
+
else out.push(item);
|
|
685
|
+
}
|
|
686
|
+
return out;
|
|
687
|
+
}
|
|
688
|
+
function toHostActions(actions, opts) {
|
|
689
|
+
const out = [];
|
|
690
|
+
for (const item of flattenLeaves(actions)) {
|
|
691
|
+
const mapped = toHostAction(item, opts);
|
|
692
|
+
if (mapped) out.push(mapped);
|
|
693
|
+
}
|
|
694
|
+
const seen = /* @__PURE__ */ new Set();
|
|
695
|
+
return out.filter((a) => seen.has(a.id) ? false : (seen.add(a.id), true));
|
|
696
|
+
}
|
|
697
|
+
function toHostAction(item, opts) {
|
|
698
|
+
const slot = item.slot === "primary" ? "primary" : item.slot === "sidebar-only" ? "sidebar-only" : "overflow";
|
|
699
|
+
const variant = item.variant === "danger" ? "danger" : void 0;
|
|
700
|
+
const shared = { id: item.id, label: item.label, icon: item.icon, slot, variant, disabled: item.disabled };
|
|
701
|
+
if (item.href) {
|
|
702
|
+
const href = slugRelative(item.href, opts.basePath);
|
|
703
|
+
if (!isConfinable(href)) return null;
|
|
704
|
+
return { ...shared, kind: "navigate", href };
|
|
705
|
+
}
|
|
706
|
+
if (item.actionId) {
|
|
707
|
+
const actionId = item.actionId;
|
|
708
|
+
return { ...shared, kind: "dispatch", onSelect: () => opts.onDispatch(actionId) };
|
|
709
|
+
}
|
|
710
|
+
if (item.onClick) {
|
|
711
|
+
return { ...shared, kind: "dispatch", onSelect: item.onClick };
|
|
712
|
+
}
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
|
|
651
716
|
exports.BaseMcpService = BaseMcpService;
|
|
652
717
|
exports.DEFAULT_MAX_UPLOAD_BYTES = DEFAULT_MAX_UPLOAD_BYTES;
|
|
653
718
|
exports.EHX_PLUGIN_ROOT_CLASS = EHX_PLUGIN_ROOT_CLASS;
|
|
@@ -676,6 +741,7 @@ exports.reportHostChromeDiagnostic = reportHostChromeDiagnostic;
|
|
|
676
741
|
exports.resolveSurfaceBase = resolveSurfaceBase;
|
|
677
742
|
exports.setHostChromeDiagnosticSink = setHostChromeDiagnosticSink;
|
|
678
743
|
exports.surfacePathFor = surfacePathFor;
|
|
744
|
+
exports.toHostSidebarInput = toHostSidebarInput;
|
|
679
745
|
exports.useAuthenticatedQueries = useAuthenticatedQueries;
|
|
680
746
|
exports.useAuthenticatedQuery = useAuthenticatedQuery;
|
|
681
747
|
exports.useHostSidebarActions = useHostSidebarActions;
|