@object-ui/react 16.0.0 → 17.1.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +786 -0
  2. package/README.md +65 -0
  3. package/dist/SchemaRenderer.js +12 -2
  4. package/dist/context/InlineEditContext.d.ts +21 -0
  5. package/dist/context/InlineEditContext.js +7 -2
  6. package/dist/context/NotificationContext.d.ts +245 -17
  7. package/dist/context/NotificationContext.js +222 -20
  8. package/dist/context/SchemaRendererContext.d.ts +10 -1
  9. package/dist/context/SchemaRendererContext.js +7 -2
  10. package/dist/context/ThemeContext.js +4 -8
  11. package/dist/hooks/index.d.ts +2 -1
  12. package/dist/hooks/index.js +3 -1
  13. package/dist/hooks/useAnimation.d.ts +40 -4
  14. package/dist/hooks/useAnimation.js +41 -10
  15. package/dist/hooks/useCapabilityGate.d.ts +16 -0
  16. package/dist/hooks/useCapabilityGate.js +60 -0
  17. package/dist/hooks/useClientNotifications.d.ts +0 -6
  18. package/dist/hooks/useClientNotifications.js +7 -53
  19. package/dist/hooks/useETagCache.js +4 -0
  20. package/dist/hooks/useExpression.d.ts +8 -2
  21. package/dist/hooks/useExpression.js +9 -1
  22. package/dist/hooks/useFilterScope.d.ts +30 -0
  23. package/dist/hooks/useFilterScope.js +55 -0
  24. package/dist/hooks/useNavigationOverlay.d.ts +11 -0
  25. package/dist/hooks/useNavigationOverlay.js +35 -1
  26. package/dist/hooks/usePageVariables.js +23 -11
  27. package/dist/hooks/useRecordSearch.js +100 -5
  28. package/dist/hooks/useViewData.js +10 -2
  29. package/dist/index.d.ts +3 -1
  30. package/dist/index.js +4 -1
  31. package/dist/spec-bridge/SpecBridge.d.ts +0 -4
  32. package/dist/spec-bridge/SpecBridge.js +0 -12
  33. package/dist/spec-bridge/bridges/list-view.js +5 -11
  34. package/dist/spec-bridge/index.d.ts +0 -2
  35. package/dist/spec-bridge/index.js +0 -2
  36. package/dist/utils/error-message.d.ts +52 -0
  37. package/dist/utils/error-message.js +138 -0
  38. package/package.json +7 -7
  39. package/dist/hooks/useColumnSummary.d.ts +0 -38
  40. package/dist/hooks/useColumnSummary.js +0 -54
  41. package/dist/spec-bridge/bridges/dashboard.d.ts +0 -81
  42. package/dist/spec-bridge/bridges/dashboard.js +0 -121
  43. package/dist/spec-bridge/bridges/page.d.ts +0 -58
  44. package/dist/spec-bridge/bridges/page.js +0 -101
package/README.md CHANGED
@@ -81,6 +81,30 @@ function App() {
81
81
  }
82
82
  ```
83
83
 
84
+ ### SchemaRendererProvider
85
+
86
+ Injects the host's data source (and optional capabilities) into every renderer
87
+ below it:
88
+
89
+ ```tsx
90
+ import { SchemaRendererProvider } from '@object-ui/react'
91
+
92
+ <SchemaRendererProvider
93
+ dataSource={adapter}
94
+ // Optional: host-authenticated fetch used by `provider: 'api'` view data
95
+ // sources, so custom endpoints carry the same credentials (Authorization,
96
+ // tenant, locale headers) as the native data channel. When omitted,
97
+ // ApiDataSource falls back to the bare global fetch (cookies only).
98
+ apiFetch={authenticatedFetch}
99
+ >
100
+ <SchemaRenderer schema={schema} />
101
+ </SchemaRendererProvider>
102
+ ```
103
+
104
+ Nested providers inherit `apiFetch` from their parent when they don't supply
105
+ their own, so re-wrapped subtrees (embedded pages, preview surfaces) keep the
106
+ host's authentication.
107
+
84
108
  ## Hooks
85
109
 
86
110
  ### useSchemaContext
@@ -158,6 +182,47 @@ The `previewMode` object contains:
158
182
  | `expiresInSeconds` | `number` | `0` | Session duration (0 = no expiry) |
159
183
  | `bannerMessage` | `string` | — | UI banner message |
160
184
 
185
+ ### useNotifications / useNotificationsByPresentation
186
+
187
+ `NotificationProvider` implements the spec `NotificationSchema`. A notification's
188
+ `severity` picks its icon and tone; its `displayType` picks the **surface** that
189
+ renders it — and each of the five spec types has a distinct one:
190
+
191
+ | `displayType` | Presentation | Rendered by | Auto-dismiss |
192
+ | --- | --- | --- | --- |
193
+ | `toast` | transient overlay | the `onToast` delegate | yes |
194
+ | `snackbar` | bottom-anchored bar, one at a time, one action | `<NotificationSnackbar />` | yes |
195
+ | `banner` | page-width strip in the content flow | `<NotificationBanners />` | no |
196
+ | `alert` | blocking acknowledgement dialog (FIFO) | `<NotificationAlerts />` | no |
197
+ | `inline` | in place, at the raising surface | `<NotificationInline />` | no |
198
+
199
+ The surface components ship in `@object-ui/components`; mount them where they
200
+ belong (a banner is in flow, an inline notification sits next to its raiser).
201
+ `onToast` receives **only** `toast` items — it used to receive all five, which is
202
+ why every type looked like a toast.
203
+
204
+ ```tsx
205
+ const { notify } = useNotifications()
206
+
207
+ notify({ title: 'Saved', severity: 'success' }) // toast (spec default)
208
+ notify({ title: 'Viewing a draft', severity: 'warning', displayType: 'banner' })
209
+ notify({ title: 'Fix 2 fields', severity: 'error', displayType: 'inline', scope: 'contact-form' })
210
+ ```
211
+
212
+ A surface component subscribes with `useNotificationsByPresentation(type, scope?)`,
213
+ which also registers the surface — raising a `banner` with no banner surface
214
+ mounted warns in dev instead of vanishing.
215
+
216
+ `config` is the spec `NotificationConfigSchema` (`defaultPosition`,
217
+ `defaultDuration`, `maxVisible`, `stackDirection`, `pauseOnHover`); the legacy
218
+ `position` / `stacking` spellings still resolve through
219
+ `resolveNotificationConfig`. Three helpers apply it so every surface agrees:
220
+ `resolveNotificationPosition` (a declared position always wins; nothing declared
221
+ leaves the surface on its own anchor), `visibleNotificationStack` (`maxVisible` +
222
+ `stackDirection`), and the context's `pauseAutoDismiss` / `resumeAutoDismiss`
223
+ (`pauseOnHover`). See the
224
+ [notifications guide](https://objectui.org/docs/guide/notifications).
225
+
161
226
  ## API Reference
162
227
 
163
228
  See [full documentation](https://objectui.org/api/react) for detailed API reference.
@@ -136,9 +136,19 @@ export class SchemaErrorBoundary extends Component {
136
136
  return this.props.children;
137
137
  }
138
138
  }
139
+ /**
140
+ * Shared "no data source" fallback. MUST be a module constant: it feeds the
141
+ * `evaluatedSchema` memo below, and a fresh `{}` per render defeats that memo
142
+ * for every SchemaRenderer without a provider above it — re-cloning the schema
143
+ * and re-running the ExpressionEvaluator on each render, and handing a new
144
+ * schema identity to children that memoise on it. For a `kind:'react'` page
145
+ * that identity IS the compile key, so the page was silently remounted and its
146
+ * `useState` wiped (objectui#2954's latent hazard, made real by this line).
147
+ */
148
+ const NO_DATA_SOURCE = {};
139
149
  export const SchemaRenderer = forwardRef(({ schema, ...props }, _ref) => {
140
150
  const context = useContext(SchemaRendererContext);
141
- const dataSource = context?.dataSource || {};
151
+ const dataSource = context?.dataSource || NO_DATA_SOURCE;
142
152
  // Ambient host scope (user / app / features), fed by app-shell's
143
153
  // ExpressionProvider. Threaded into `visible`/expression evaluation so
144
154
  // component predicates can gate on the signed-in user & deployment flags.
@@ -233,7 +243,7 @@ export const SchemaRenderer = forwardRef(({ schema, ...props }, _ref) => {
233
243
  if (newSchema.visibleOn !== undefined) {
234
244
  return !evaluator.evaluateCondition(newSchema.visibleOn);
235
245
  }
236
- // @deprecated ADR-0089 → `visibleWhen` (was PageComponentSchema.visibility,
246
+ // @deprecated ADR-0089 → `visibleWhen` (was PageNodeSchema.visibility,
237
247
  // an ExpressionInput) — show-when-truthy, same semantics as `visibleOn`.
238
248
  if (newSchema.visibility !== undefined) {
239
249
  return !evaluator.evaluateCondition(newSchema.visibility);
@@ -43,6 +43,21 @@ export interface InlineEditContextValue {
43
43
  * renderer DataSource-agnostic. Defaults to `false`.
44
44
  */
45
45
  locked: boolean;
46
+ /**
47
+ * Whether an approval is in flight on this record, INDEPENDENT of whether it
48
+ * locks the record (objectui#2902). An approval node may declare
49
+ * `lockRecord: false`, in which case the backend accepts edits while the node
50
+ * waits — `approvalPending` is true and `locked` is false.
51
+ *
52
+ * Kept separate because the two drive different affordances: the recall
53
+ * button and the "an approval is running" status belong to `approvalPending`,
54
+ * while only `locked` may suppress editing. Consumers that conflate them
55
+ * either hide recall on an editable record or claim a lock that isn't there.
56
+ *
57
+ * `locked` implies an approval is pending, so hosts that thread only `locked`
58
+ * still get a coherent band. Defaults to `false`.
59
+ */
60
+ approvalPending: boolean;
46
61
  /**
47
62
  * Human-readable reason for the approval lock, surfaced as the band's
48
63
  * tooltip. Optional — consumers fall back to their own localized default
@@ -88,6 +103,12 @@ export interface InlineEditProviderProps {
88
103
  * from a record field the backend may not materialize. Defaults to `false`.
89
104
  */
90
105
  locked?: boolean;
106
+ /**
107
+ * Whether an approval is pending on the record, whether or not it locks it
108
+ * (objectui#2902). Omitted ⇒ falls back to `locked`, which keeps hosts that
109
+ * only know about the lock rendering exactly as before.
110
+ */
111
+ approvalPending?: boolean;
91
112
  /** Optional human-readable lock reason, surfaced as the band tooltip. */
92
113
  lockedReason?: string;
93
114
  children: React.ReactNode;
@@ -24,7 +24,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
24
24
  */
25
25
  import React from 'react';
26
26
  const InlineEditContext = React.createContext(null);
27
- export const InlineEditProvider = ({ canEdit = true, locked = false, lockedReason, children, }) => {
27
+ export const InlineEditProvider = ({ canEdit = true, locked = false, approvalPending, lockedReason, children, }) => {
28
+ // A locked record always has an approval running; the converse is what
29
+ // #2902 added. Defaulting this way means an un-migrated host keeps its
30
+ // previous behavior with no code change.
31
+ const pending = approvalPending ?? locked;
28
32
  const [editing, setEditing] = React.useState(false);
29
33
  const [draft, setDraft] = React.useState({});
30
34
  const [autoFocusField, setAutoFocusField] = React.useState(null);
@@ -57,6 +61,7 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, lockedReaso
57
61
  editing,
58
62
  canEdit,
59
63
  locked,
64
+ approvalPending: pending,
60
65
  lockedReason,
61
66
  draft,
62
67
  autoFocusField,
@@ -68,7 +73,7 @@ export const InlineEditProvider = ({ canEdit = true, locked = false, lockedReaso
68
73
  reset: teardown,
69
74
  setSaving,
70
75
  setError,
71
- }), [editing, canEdit, locked, lockedReason, draft, autoFocusField, saving, error, enter, setField, teardown]);
76
+ }), [editing, canEdit, locked, pending, lockedReason, draft, autoFocusField, saving, error, enter, setField, teardown]);
72
77
  return _jsx(InlineEditContext.Provider, { value: value, children: children });
73
78
  };
74
79
  /**
@@ -15,15 +15,100 @@
15
15
  import React from 'react';
16
16
  /** Notification severity levels aligned with NotificationSeveritySchema */
17
17
  export type NotificationSeverityLevel = 'info' | 'success' | 'warning' | 'error';
18
- /** Notification display type aligned with NotificationTypeSchema */
19
- export type NotificationDisplayType = 'toast' | 'banner' | 'snackbar' | 'modal';
20
- /** Notification position aligned with NotificationPositionSchema */
21
- export type NotificationPositionValue = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
18
+ /**
19
+ * Notification presentation — the spec `NotificationTypeSchema`
20
+ * (`ui/notification.zod.ts`: toast / snackbar / banner / alert / inline).
21
+ * Every member has a DISTINCT presentation; see {@link NOTIFICATION_PRESENTATIONS}.
22
+ */
23
+ export type NotificationPresentation = 'toast' | 'snackbar' | 'banner' | 'alert' | 'inline';
24
+ /**
25
+ * Notification display type as AUTHORED. Identical to
26
+ * {@link NotificationPresentation} plus the deprecated renderer-local `modal`
27
+ * spelling, which stored items may still carry (#2942) and which resolves to
28
+ * `alert`. Use {@link resolveNotificationPresentation} to get the presentation.
29
+ */
30
+ export type NotificationDisplayType = NotificationPresentation
31
+ /** @deprecated renderer dialect — never in the spec; presented as `alert` */
32
+ | 'modal';
33
+ /**
34
+ * Where a presentation is rendered.
35
+ *
36
+ * - `delegate` — handed to the provider's `onToast` callback; the host owns the
37
+ * overlay (sonner in the console). Historically EVERY type took this path,
38
+ * which is why a `banner` surfaced as a toast (#3014).
39
+ * - everything else names a surface component that subscribes to the context
40
+ * and renders the items in place. `@object-ui/components` ships one per
41
+ * surface (`NotificationSnackbar` / `NotificationBanners` /
42
+ * `NotificationAlerts` / `NotificationInline`).
43
+ */
44
+ export type NotificationSurface = 'delegate' | 'snackbar' | 'banner' | 'alert' | 'inline';
45
+ export interface NotificationPresentationBehavior {
46
+ /** The surface responsible for rendering this presentation. */
47
+ surface: NotificationSurface;
48
+ /**
49
+ * Whether the presentation auto-dismisses when the raiser declares no
50
+ * `duration`. A toast/snackbar is transient by definition; a banner is a
51
+ * persistent strip and an alert is a blocking acknowledgement, so neither may
52
+ * evaporate on the shared 5s timer. An explicit `duration` always wins.
53
+ */
54
+ transient: boolean;
55
+ }
56
+ /**
57
+ * The presentation table — one entry per spec `NotificationTypeSchema` member.
58
+ *
59
+ * Typed as `Record<NotificationPresentation, …>`, so adding a member to the
60
+ * spec enum fails type-check here until its presentation is decided, rather
61
+ * than silently falling back to a toast.
62
+ */
63
+ export declare const NOTIFICATION_PRESENTATIONS: Record<NotificationPresentation, NotificationPresentationBehavior>;
64
+ /**
65
+ * Resolve an authored `displayType` to the presentation that renders it:
66
+ * the spec default (`toast`) when absent, and `alert` for the deprecated
67
+ * `modal` dialect.
68
+ */
69
+ export declare function resolveNotificationPresentation(displayType?: NotificationDisplayType): NotificationPresentation;
70
+ /**
71
+ * Notification position — the spec `NotificationPositionSchema`
72
+ * (underscore spellings). The hyphen forms are this context's historical
73
+ * dialect, kept accepted for stored items.
74
+ */
75
+ export type NotificationPositionValue = 'top_left' | 'top_center' | 'top_right' | 'bottom_left' | 'bottom_center' | 'bottom_right'
76
+ /** @deprecated legacy spelling — use `top_left` */
77
+ | 'top-left'
78
+ /** @deprecated legacy spelling — use `top_center` */
79
+ | 'top-center'
80
+ /** @deprecated legacy spelling — use `top_right` */
81
+ | 'top-right'
82
+ /** @deprecated legacy spelling — use `bottom_left` */
83
+ | 'bottom-left'
84
+ /** @deprecated legacy spelling — use `bottom_center` */
85
+ | 'bottom-center'
86
+ /** @deprecated legacy spelling — use `bottom_right` */
87
+ | 'bottom-right';
88
+ /**
89
+ * The spec vocabularies this context implements — exported for the parity
90
+ * tests (#2942), which fail the moment `NotificationTypeSchema` /
91
+ * `NotificationPositionSchema` and these sets drift in either direction.
92
+ */
93
+ export declare const SUPPORTED_NOTIFICATION_DISPLAY_TYPES: ReadonlySet<string>;
94
+ export declare const SUPPORTED_NOTIFICATION_POSITIONS: ReadonlySet<string>;
95
+ /**
96
+ * Action button variant — the spec `NotificationActionSchema.variant`
97
+ * (`primary | secondary | link`, default `primary`).
98
+ *
99
+ * This used to be the shadcn Button vocabulary (`default | destructive |
100
+ * outline`) — a renderer-local fork of a spec enum, i.e. a second de-facto
101
+ * contract. Surfaces map these three onto their own button styling instead.
102
+ */
103
+ export type NotificationActionVariant = 'primary' | 'secondary' | 'link';
104
+ /** The spec vocabulary this context implements — exported for the parity test. */
105
+ export declare const SUPPORTED_NOTIFICATION_ACTION_VARIANTS: ReadonlySet<string>;
22
106
  /** Action button on a notification */
23
107
  export interface NotificationActionButton {
24
108
  label: string;
25
109
  onClick: () => void;
26
- variant?: 'default' | 'destructive' | 'outline';
110
+ /** Spec `NotificationActionSchema.variant`. Default: `primary`. */
111
+ variant?: NotificationActionVariant;
27
112
  }
28
113
  /** A single notification item */
29
114
  export interface NotificationItem {
@@ -31,10 +116,40 @@ export interface NotificationItem {
31
116
  title: string;
32
117
  message?: string;
33
118
  severity: NotificationSeverityLevel;
119
+ /**
120
+ * The presentation the raiser asked for. Materialized by `notify()` — stored
121
+ * items always carry a resolved {@link NotificationPresentation}, never
122
+ * `modal` and never `undefined`.
123
+ */
34
124
  displayType?: NotificationDisplayType;
35
125
  actions?: NotificationActionButton[];
36
- /** Duration in ms (0 = persistent). Default: 5000 */
126
+ /**
127
+ * Duration in ms (0 = persistent). Defaults to the configured duration for
128
+ * TRANSIENT presentations (toast / snackbar) and to persistent for the rest —
129
+ * a banner that evaporates after 5s is not a banner.
130
+ */
37
131
  duration?: number;
132
+ /**
133
+ * Whether the surface offers a dismiss control (spec `dismissible`, default
134
+ * `true`). Only meaningful for the persistent presentations — a transient
135
+ * one dismisses itself.
136
+ */
137
+ dismissible?: boolean;
138
+ /**
139
+ * `inline` only — names the surface that raised it, so the matching
140
+ * `<NotificationInline scope="…" />` renders it in place instead of every
141
+ * inline outlet on the page showing every inline notification. Renderer-local
142
+ * routing metadata, not a spec field: the spec describes what a notification
143
+ * IS, not which React subtree hosts it.
144
+ */
145
+ scope?: string;
146
+ /**
147
+ * Spec `NotificationSchema.position` — "Override default position", falling
148
+ * back to `config.defaultPosition`. Honored by the FLOATING presentations
149
+ * (toast, snackbar); `banner` / `inline` / `alert` are anchored by what they
150
+ * are (content top / in place / centered modal) and ignore it.
151
+ */
152
+ position?: NotificationPositionValue;
38
153
  /** Whether the notification has been read */
39
154
  read?: boolean;
40
155
  /** Timestamp */
@@ -42,23 +157,75 @@ export interface NotificationItem {
42
157
  /** Optional icon name (lucide icon) */
43
158
  icon?: string;
44
159
  }
45
- /** Configuration for the notification system */
160
+ /**
161
+ * Configuration for the notification system — the spec
162
+ * `NotificationConfigSchema` (`defaultPosition` / `defaultDuration` /
163
+ * `maxVisible` / `stackDirection` / `pauseOnHover`).
164
+ *
165
+ * The field names used to fork from it (`position`, and a renderer-local
166
+ * `stacking` boolean with no spec counterpart), and only `defaultDuration` was
167
+ * ever read — the other three were stored and ignored. The legacy names are
168
+ * still accepted; see {@link resolveNotificationConfig}.
169
+ */
46
170
  export interface NotificationSystemConfig {
47
- /** Default position for toast notifications */
48
- position?: NotificationPositionValue;
49
- /** Default duration in ms */
171
+ /**
172
+ * Default screen position for the FLOATING presentations (toast, snackbar).
173
+ * A notification's own `position` overrides it.
174
+ *
175
+ * Deliberately undefined by default: "the host didn't say" has to be
176
+ * representable, otherwise a fabricated default silently competes with the
177
+ * host's own toast chrome (which also serves non-notification `toast.*`
178
+ * calls). Declared wins; undeclared defers to the surface's own anchor.
179
+ */
180
+ defaultPosition?: NotificationPositionValue;
181
+ /** Default duration in ms for the transient presentations. */
50
182
  defaultDuration?: number;
51
- /** Maximum number of visible notifications */
183
+ /** Maximum number of notifications a stacking surface renders at once. */
52
184
  maxVisible?: number;
53
- /** Whether to stack notifications */
185
+ /** Which way a stack grows: `down` puts the newest below (spec default). */
186
+ stackDirection?: 'up' | 'down';
187
+ /** Pause a transient notification's auto-dismiss timer while hovered. */
188
+ pauseOnHover?: boolean;
189
+ /** @deprecated renderer dialect — use `defaultPosition` */
190
+ position?: NotificationPositionValue;
191
+ /**
192
+ * @deprecated renderer dialect with no spec counterpart. Read as "show only
193
+ * the newest" (`maxVisible: 1`) when false, so it is honored rather than
194
+ * fossilized; declare `maxVisible` instead.
195
+ */
54
196
  stacking?: boolean;
55
197
  }
198
+ /** Config with the legacy spellings folded in and the spec defaults applied. */
199
+ export interface ResolvedNotificationConfig {
200
+ defaultPosition?: NotificationPositionValue;
201
+ defaultDuration: number;
202
+ maxVisible: number;
203
+ stackDirection: 'up' | 'down';
204
+ pauseOnHover: boolean;
205
+ }
206
+ /**
207
+ * Fold the deprecated spellings into the spec ones and apply the spec defaults
208
+ * (`NotificationConfigSchema`: 5000ms / 5 visible / stack down / pause on
209
+ * hover). `defaultPosition` gets no fabricated default — see the field docs.
210
+ */
211
+ export declare function resolveNotificationConfig(config?: NotificationSystemConfig): ResolvedNotificationConfig;
56
212
  export interface NotificationProviderProps {
57
213
  children: React.ReactNode;
58
214
  /** System configuration */
59
215
  config?: NotificationSystemConfig;
60
- /** External toast handler (e.g., Sonner) for rendering toasts */
61
- onToast?: (notification: NotificationItem) => void;
216
+ /**
217
+ * External toast handler (e.g. Sonner) — the `toast` presentation's surface.
218
+ *
219
+ * Called ONLY for `displayType: 'toast'`. It used to receive every
220
+ * notification, which is why the other four spec types all surfaced as
221
+ * toasts (#3014); they now render through their own surface components,
222
+ * which subscribe via {@link useNotificationsByPresentation}.
223
+ *
224
+ * The resolved config comes with it so the delegate can honor the parts of
225
+ * the contract only it can apply — `defaultPosition` above all, since the
226
+ * toast overlay is the host's.
227
+ */
228
+ onToast?: (notification: NotificationItem, config: ResolvedNotificationConfig) => void;
62
229
  }
63
230
  interface NotificationContextValue {
64
231
  /** All notifications (including history) */
@@ -83,19 +250,44 @@ interface NotificationContextValue {
83
250
  dismiss: (id: string) => void;
84
251
  /** Clear all notifications */
85
252
  clearAll: () => void;
86
- /** System configuration */
87
- config: NotificationSystemConfig;
253
+ /** System configuration, spec-normalized (see {@link resolveNotificationConfig}). */
254
+ config: ResolvedNotificationConfig;
255
+ /**
256
+ * Hold a transient notification's auto-dismiss timer (spec `pauseOnHover`).
257
+ * A surface calls this on pointer-enter; no-op when the notification is
258
+ * persistent or the config opted out.
259
+ */
260
+ pauseAutoDismiss: (id: string) => void;
261
+ /** Resume a held timer with the time it had left. */
262
+ resumeAutoDismiss: (id: string) => void;
263
+ /**
264
+ * Announce that a surface is mounted and will render `surface` items.
265
+ * Returns the unregister callback. Surfaces call this from an effect; the
266
+ * provider uses it only to warn (dev builds) when a notification is raised
267
+ * with nothing on screen able to present it.
268
+ */
269
+ registerSurface: (surface: NotificationSurface) => () => void;
88
270
  }
89
271
  /**
90
272
  * NotificationProvider — Provides a spec-driven notification system.
91
273
  *
274
+ * Each spec display type is presented by its own surface. `toast` is delegated
275
+ * to the host (`onToast`); the other four are rendered by surface components
276
+ * that subscribe to this context — mount them where they belong:
277
+ * `NotificationSnackbar` / `NotificationAlerts` anywhere inside the provider,
278
+ * `NotificationBanners` at the top of the content area, and
279
+ * `NotificationInline` in the surface that raises the notification.
280
+ *
92
281
  * @example
93
282
  * ```tsx
94
283
  * <NotificationProvider
95
- * config={{ position: 'top-right', defaultDuration: 5000, maxVisible: 5 }}
284
+ * config={{ position: 'top_right', defaultDuration: 5000, maxVisible: 5 }}
96
285
  * onToast={(n) => toast[n.severity](n.title, { description: n.message })}
97
286
  * >
287
+ * <NotificationBanners />
98
288
  * <App />
289
+ * <NotificationSnackbar />
290
+ * <NotificationAlerts />
99
291
  * </NotificationProvider>
100
292
  * ```
101
293
  */
@@ -110,4 +302,40 @@ export declare function useNotifications(): NotificationContextValue;
110
302
  * Hook to check if a NotificationProvider is available.
111
303
  */
112
304
  export declare function useHasNotificationProvider(): boolean;
305
+ /**
306
+ * Apply the config's stack rules to a surface's items: keep the newest
307
+ * `maxVisible`, then order them so the stack GROWS in `stackDirection`
308
+ * (`down`, the spec default, puts the newest below the older ones).
309
+ *
310
+ * Shared by the stacking surfaces (banner, inline) so `maxVisible` means one
311
+ * thing. It used to mean nothing at all — the config carried it and no surface
312
+ * read it, while `NotificationBanners` capped at a hard-coded 3 of its own.
313
+ *
314
+ * @param items the surface's notifications, newest-first
315
+ */
316
+ export declare function visibleNotificationStack(items: NotificationItem[], config: Pick<ResolvedNotificationConfig, 'maxVisible' | 'stackDirection'>): NotificationItem[];
317
+ /**
318
+ * The screen position a FLOATING notification (toast, snackbar) should take:
319
+ * its own `position`, else the configured `defaultPosition`, else `undefined`.
320
+ *
321
+ * `undefined` is a meaningful answer, not a missing one — it means "nothing was
322
+ * declared", and the surface should keep its own anchor (a snackbar's bottom
323
+ * edge) or defer to the host's toast chrome, rather than being moved by a
324
+ * default nobody asked for. Always returns the spec (underscore) spelling.
325
+ */
326
+ export declare function resolveNotificationPosition(notification: Pick<NotificationItem, 'position'>, config?: Pick<ResolvedNotificationConfig, 'defaultPosition'>): NotificationPositionValue | undefined;
327
+ /**
328
+ * Subscribe to the live notifications for ONE presentation — the contract a
329
+ * surface component implements (`@object-ui/components` ships one per spec
330
+ * type). Registering also tells the provider the surface exists, so raising a
331
+ * `banner` with no banner surface mounted warns in dev instead of vanishing.
332
+ *
333
+ * Items come back newest-first, matching `notifications`. A surface that wants
334
+ * FIFO order (an `alert` queue) reverses them itself.
335
+ *
336
+ * @param presentation the spec display type this surface renders
337
+ * @param scope optional `inline` routing key — when set, only items raised with
338
+ * the same `scope` are returned; when omitted, only unscoped items are.
339
+ */
340
+ export declare function useNotificationsByPresentation(presentation: NotificationPresentation, scope?: string): NotificationItem[];
113
341
  export {};