@powerportalspro/react 5.0.0-beta.3 → 5.0.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/index.d.cts CHANGED
@@ -1,12 +1,159 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ChangeEvent, ReactNode } from 'react';
3
3
  import { components, TableMetadata as TableMetadata$1, ViewMetadata, CurrentUserInfo, Transport, AuthClient, PowerPortalsProClient, LoginRequest, LoginResponse, SwitchIdentityResponse, PowerPortalsProError, TableRecord, RetrieveRecordsResponse, GridDataRequest, GridDataResponse, GridFilterBase, ChartAggregation, ColumnSort, ResolvedColumn, TableSecurityPermission, ChartData, OrganizationRequest } from '@powerportalspro/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
- type EnvironmentFileSettings$2 = components['schemas']['EnvironmentFileSettings'];
7
6
  /**
8
- * Client-side cache for table metadata. Mirrors Blazor's
9
- * `ITableMetadataCache` interface (in `PowerPortalsPro.Common.Services`).
7
+ * Masking strategy applied to a masked text field.
8
+ *
9
+ * Hybrid const-object + literal-union — same pattern other enums in
10
+ * the package follow (e.g. `GridButtonBehavior`).
11
+ */
12
+ declare const MaskMode: {
13
+ /** No masking — value passes through unchanged. */
14
+ readonly None: "none";
15
+ /**
16
+ * Per-character allow filter. Characters that don't match
17
+ * {@link MaskOptions.allowedPattern} are dropped as they're typed or pasted.
18
+ */
19
+ readonly Regex: "regex";
20
+ /**
21
+ * Slotted template (see {@link MaskOptions.pattern}) such as `(000) 000-0000`.
22
+ * `0` = digit, `A` = letter, `*` = alphanumeric; every other character is a
23
+ * literal inserted automatically.
24
+ */
25
+ readonly Pattern: "pattern";
26
+ /**
27
+ * Live numeric grouping driven by {@link MaskOptions.numeric} — thousands
28
+ * separators, an optional decimal portion, and an optional leading sign.
29
+ */
30
+ readonly Numeric: "numeric";
31
+ };
32
+ type MaskMode = (typeof MaskMode)[keyof typeof MaskMode];
33
+ /**
34
+ * Numeric masking options for {@link MaskMode.Numeric}. All separators are
35
+ * supplied by the caller so the field stays culture-correct (a German consumer
36
+ * gets `.` groups and `,` decimals).
37
+ */
38
+ interface NumericOptions {
39
+ /** Character used to separate the whole and fractional parts (e.g. `.`). */
40
+ decimalSeparator: string;
41
+ /** Character inserted between thousands groups (e.g. `,`). Empty disables grouping. */
42
+ groupSeparator: string;
43
+ /** Whether a single leading minus sign is accepted. */
44
+ allowNegative: boolean;
45
+ /** Maximum fractional digits, or `null` for unlimited. */
46
+ maxDecimals: number | null;
47
+ /** Whether a decimal portion is allowed at all (integers set this false). */
48
+ allowDecimal: boolean;
49
+ }
50
+ /** Configuration for a single reformat call. */
51
+ interface MaskOptions {
52
+ mode: MaskMode;
53
+ /** Regex mode: a per-character allow test, e.g. `[0-9.]`. Chars failing it are dropped. */
54
+ allowedPattern: string | null;
55
+ /**
56
+ * Pattern mode: a slotted template, e.g. `(000) 000-0000`. `0`=digit, `A`=letter,
57
+ * `*`=alphanumeric; every other character is a literal inserted automatically.
58
+ */
59
+ pattern: string | null;
60
+ /** Numeric mode: separators, sign, decimal options. */
61
+ numeric: NumericOptions | null;
62
+ /**
63
+ * When true, the value reported by the consumer's `onValueChange` is the
64
+ * masked display string (with literals/separators included); when false, the
65
+ * unmasked logical value (e.g. `5551234567` rather than `(555) 123-4567`).
66
+ * Round-trips correctly either way because reformat is idempotent.
67
+ */
68
+ valueIncludesMask: boolean;
69
+ }
70
+ /** Output of a single reformat call. */
71
+ interface ReformatResult {
72
+ /** The string to display in the input (masked / grouped). */
73
+ display: string;
74
+ /**
75
+ * The logical value reported back to the consumer (group separators stripped
76
+ * for numeric; mask literals stripped for pattern). Drives the bound model.
77
+ */
78
+ raw: string;
79
+ /** Caret offset into `display` after reformatting. */
80
+ caret: number;
81
+ }
82
+ /**
83
+ * Reformat `value` according to `options`. Pure — same input always yields the
84
+ * same `{ display, raw, caret }`. Caller is responsible for actually writing
85
+ * the new display string into the input and calling `setSelectionRange(caret,
86
+ * caret)`. {@link useMaskedTextField} does that wiring for React consumers.
87
+ */
88
+ declare function reformat(value: string, caret: number, options: MaskOptions): ReformatResult;
89
+
90
+ interface UseMaskedTextFieldOptions {
91
+ /**
92
+ * Current logical value (unmasked, the way it should be stored on the
93
+ * bound model). The hook reformats this into the masked `displayValue` it
94
+ * returns. Pass `null` / `undefined` for empty.
95
+ */
96
+ value: string | null | undefined;
97
+ /**
98
+ * Fires on every keystroke that changes the reported value. Receives the
99
+ * unmasked logical value (e.g. `5551234567`) when {@link valueIncludesMask}
100
+ * is `false`, the masked display string (e.g. `(555) 123-4567`) when `true`.
101
+ */
102
+ onValueChange: (raw: string | null) => void;
103
+ /** Masking strategy. Defaults to {@link MaskMode.None}. */
104
+ mode?: MaskMode | undefined;
105
+ /** Slotted template for {@link MaskMode.Pattern} (e.g. `(000) 000-0000`). */
106
+ pattern?: string | null | undefined;
107
+ /** Per-character allow regex for {@link MaskMode.Regex} (e.g. `[A-Za-z]`). */
108
+ allowedPattern?: string | null | undefined;
109
+ /** Numeric options for {@link MaskMode.Numeric}. */
110
+ numeric?: NumericOptions | null | undefined;
111
+ /**
112
+ * When `true`, the value reported by `onValueChange` is the masked display
113
+ * string (with literals/separators); when `false` (default), the unmasked
114
+ * logical value. Either form round-trips correctly because reformat is
115
+ * idempotent.
116
+ */
117
+ valueIncludesMask?: boolean | undefined;
118
+ }
119
+ interface UseMaskedTextFieldReturn {
120
+ /**
121
+ * Masked display string. Pass this to the consumer input's `value` prop
122
+ * the hook re-renders it via React state on every keystroke + external
123
+ * value push.
124
+ */
125
+ displayValue: string;
126
+ /**
127
+ * Input change handler — matches Fluent v9 `<Input onChange>`'s
128
+ * `(event, data: { value })` shape. Reformats the typed value, updates
129
+ * `displayValue`, and reports the logical value via `onValueChange`.
130
+ */
131
+ onChange: (event: ChangeEvent<HTMLInputElement>, data: {
132
+ value: string;
133
+ }) => void;
134
+ /**
135
+ * Callback ref — attach to the underlying `<input>`. The hook needs the
136
+ * element reference both for caret-position bookkeeping and for the
137
+ * focus / blur listeners that gate external value pushes.
138
+ */
139
+ inputRef: (el: HTMLInputElement | null) => void;
140
+ /**
141
+ * Push a value into the field even while it has focus, parking the caret
142
+ * at the end. Used by an owning editor's spinner / stepper controls.
143
+ */
144
+ forceValue: (value: string | null) => void;
145
+ }
146
+ /**
147
+ * React hook that wires caret-safe masking onto a controlled input. Returns
148
+ * `displayValue` + `onChange` to spread onto a Fluent v9 `<Input>` (or any
149
+ * controlled `<input>`), plus an `inputRef` callback to attach to the
150
+ * underlying element and an imperative `forceValue` helper.
151
+ */
152
+ declare function useMaskedTextField(opts: UseMaskedTextFieldOptions): UseMaskedTextFieldReturn;
153
+
154
+ type OrganizationSettings$2 = components['schemas']['OrganizationSettings'];
155
+ /**
156
+ * Client-side cache for table metadata.
10
157
  *
11
158
  * The default implementation lives in-memory (Map) with in-flight-promise
12
159
  * dedup so N parallel `getAsync` calls for the same table share one
@@ -16,7 +163,7 @@ type EnvironmentFileSettings$2 = components['schemas']['EnvironmentFileSettings'
16
163
  *
17
164
  * Why this exists: the inline-editable grid mounts one `<RecordContext>`
18
165
  * per visible row when the user toggles edit mode on. Each RC would
19
- * otherwise fire its own `retrieveTableMetadataAsync` round-trip
166
+ * otherwise fire its own `retrieveTableMetadataAsync` round-trip
20
167
  * thundering-herd against the same endpoint that produces visibly
21
168
  * staggered row rendering as each fetch's response lands. The cache
22
169
  * collapses that to a single fetch shared across all consumers.
@@ -40,7 +187,7 @@ interface TableMetadataCache {
40
187
  }
41
188
  /**
42
189
  * Client-side cache for Dataverse view metadata. Same shape and
43
- * rationale as {@link TableMetadataCache} — mirrors Blazor's
190
+ * rationale as {@link TableMetadataCache}
44
191
  * `IViewMetadataCache` (in `PowerPortalsPro.Common.Services`).
45
192
  *
46
193
  * Keyed by view id (Dataverse GUID, hyphenated, no braces). The default
@@ -83,23 +230,33 @@ interface ViewsForTableCache {
83
230
  clear(): void;
84
231
  }
85
232
  /**
86
- * Singleton cache for the org-wide environment file settings (blocked
87
- * extensions, max upload size). Unlike the keyed caches, there's only
88
- * one `EnvironmentFileSettings` per environment no key parameter.
233
+ * Singleton cache for the org-wide settings sourced from the Dataverse
234
+ * `organization` record default currency plus file-upload constraints.
235
+ * There's only one `OrganizationSettings` per environment, so no key
236
+ * parameter.
237
+ *
238
+ * Two distinct consumer classes share it:
239
+ * - Create-mode `<MoneyEdit>` reads `defaultCurrency` so the right symbol
240
+ * renders on brand-new records before save.
241
+ * - File editors (`<FileEdit>`, `<ImageEdit>`, `<FileGrid>`) read
242
+ * `blockedFileExtensions` + `maxUploadFileSizeInBytes` to reject invalid
243
+ * uploads client-side before the round-trip.
89
244
  *
90
- * Avoids the case where multiple `<FileGrid>`s on the same page each
91
- * fire their own `getEnvironmentFileSettingsAsync` request. The server
92
- * already caches the response, but the client-side cache eliminates
93
- * the duplicate HTTP round-trips entirely.
245
+ * Without the cache, every create-mode `<RecordContext>` mount AND every
246
+ * `<FileGrid>` would fire its own GET multi-form pages and react-router
247
+ * navigations would generate redundant traffic even though the value is
248
+ * stable for the user's whole session. The server already caches the
249
+ * response; this cache eliminates the HTTP trips entirely.
250
+ *
251
+ * Replaces the legacy `EnvironmentFileSettingsCache`.
94
252
  */
95
- interface EnvironmentFileSettingsCache {
253
+ interface OrganizationSettingsCache {
96
254
  /**
97
255
  * Returns the cached settings, fetching from the server on a miss.
98
256
  * Concurrent calls share a single in-flight promise. Returns `null`
99
- * only when the server explicitly returns no settings (rare —
100
- * typically the endpoint always returns a value).
257
+ * only when the server explicitly returns no settings.
101
258
  */
102
- getAsync(): Promise<EnvironmentFileSettings$2 | null>;
259
+ getAsync(): Promise<OrganizationSettings$2 | null>;
103
260
  /**
104
261
  * Drops the cached value. The next `getAsync` re-fetches from the
105
262
  * server.
@@ -128,7 +285,7 @@ type AuthStatus = (typeof AuthStatus)[keyof typeof AuthStatus];
128
285
  * either `anonymous` or `authenticated` for the rest of the app's lifetime (until
129
286
  * `login()` / `logout()` actions transition it).
130
287
  *
131
- * The `authenticated` branch carries the {@link CurrentUserInfo} the server returned
288
+ * The `authenticated` branch carries the {@link CurrentUserInfo} the server returned
132
289
  * the auto-generated DTO from `@powerportalspro/core`, no narrowing applied. Fields
133
290
  * like `roles` arrive as `string[] | undefined` per the wire contract; consumers
134
291
  * coalesce to `[]` at the read site if they want a non-null array.
@@ -165,11 +322,10 @@ interface PowerPortalsProContextValue {
165
322
  */
166
323
  readonly viewsForTableCache: ViewsForTableCache;
167
324
  /**
168
- * Client-side cache for the singleton environment file settings
169
- * (blocked extensions, max upload size). See
170
- * {@link EnvironmentFileSettingsCache}.
325
+ * Client-side cache for the singleton organization settings (default
326
+ * currency plus file-upload constraints). See {@link OrganizationSettingsCache}.
171
327
  */
172
- readonly environmentFileSettingsCache: EnvironmentFileSettingsCache;
328
+ readonly organizationSettingsCache: OrganizationSettingsCache;
173
329
  /** Current auth state. {@link useAuth} unpacks this directly. */
174
330
  readonly authState: AuthState;
175
331
  /** Refreshes the auth state by re-calling `/api/auth/me`. */
@@ -195,7 +351,7 @@ interface PowerPortalsProProviderProps {
195
351
  /**
196
352
  * Optional pre-constructed {@link Transport} for the AuthClient and PowerPortalsProClient
197
353
  * to share. Pass one to set a non-default `baseUrl`, inject a custom `fetch`
198
- * implementation for tests, etc. When omitted, a default transport is constructed
354
+ * implementation for tests, etc. When omitted, a default transport is constructed
199
355
  * same-origin, browser fetch, cookie credentials included.
200
356
  *
201
357
  * The provider holds a stable reference to the transport for its lifetime — passing
@@ -254,10 +410,10 @@ interface PowerPortalsProProviderProps {
254
410
  */
255
411
  viewsForTableCache?: ViewsForTableCache;
256
412
  /**
257
- * Custom {@link EnvironmentFileSettingsCache} implementation. Same
413
+ * Custom {@link OrganizationSettingsCache} implementation. Same
258
414
  * defaulting behavior as {@link tableMetadataCache}.
259
415
  */
260
- environmentFileSettingsCache?: EnvironmentFileSettingsCache;
416
+ organizationSettingsCache?: OrganizationSettingsCache;
261
417
  }
262
418
  /**
263
419
  * Top-level provider for `@powerportalspro/react`. Constructs an {@link AuthClient}
@@ -268,14 +424,14 @@ interface PowerPortalsProProviderProps {
268
424
  * Wrap the app once near the root:
269
425
  * ```tsx
270
426
  * <PowerPortalsProProvider>
271
- * <App />
427
+ * <App />
272
428
  * </PowerPortalsProProvider>
273
429
  * ```
274
430
  *
275
431
  * Hooks ({@link useAuth}, {@link usePowerPortalsPro}, {@link useRecord}, …) read from
276
432
  * this provider's context.
277
433
  */
278
- declare function PowerPortalsProProvider({ children, transport, localizationUrl, localizationUrls, localizationPrefixes, tableMetadataCache, viewMetadataCache, viewsForTableCache, environmentFileSettingsCache, }: PowerPortalsProProviderProps): react_jsx_runtime.JSX.Element;
434
+ declare function PowerPortalsProProvider({ children, transport, localizationUrl, localizationUrls, localizationPrefixes, tableMetadataCache, viewMetadataCache, viewsForTableCache, organizationSettingsCache, }: PowerPortalsProProviderProps): react_jsx_runtime.JSX.Element;
279
435
 
280
436
  /**
281
437
  * Returns the {@link AuthClient}, {@link PowerPortalsProClient}, and underlying
@@ -296,8 +452,8 @@ declare function usePowerPortalsPro(): PowerPortalsProContextValue;
296
452
  * import { AuthStatus, useAuth } from '@powerportalspro/react';
297
453
  *
298
454
  * const auth = useAuth();
299
- * if (auth.status === AuthStatus.Loading) return <Spinner />;
300
- * if (auth.status === AuthStatus.Anonymous) return <SignInButton onClick={() => auth.login(...)} />;
455
+ * if (auth.status === AuthStatus.Loading) return <Spinner />;
456
+ * if (auth.status === AuthStatus.Anonymous) return <SignInButton onClick={() => auth.login(...)} />;
301
457
  * if (auth.status === AuthStatus.Authenticated) return <span>Hi, {auth.user.email}</span>;
302
458
  * ```
303
459
  *
@@ -305,7 +461,7 @@ declare function usePowerPortalsPro(): PowerPortalsProContextValue;
305
461
  * propagates via context, so all `useAuth` calls in the tree see the same value
306
462
  * and re-render on transitions.
307
463
  *
308
- * `login` mirrors {@link AuthClient.loginAsync} — same request/response shapes
464
+ * `login` mirrors {@link AuthClient.loginAsync} — same request/response shapes
309
465
  * but additionally re-fetches `/api/auth/me` on `Success` so the auth state
310
466
  * transitions to `authenticated` without an extra round-trip from the consumer.
311
467
  *
@@ -337,11 +493,11 @@ interface AuthorizedViewProps {
337
493
  children: ReactNode;
338
494
  /**
339
495
  * Optional role gate. When supplied, the principal must hold AT LEAST ONE of
340
- * the listed roles for the children to render — matching Blazor's
496
+ * the listed roles for the children to render
341
497
  * `<AuthorizeView Roles="Admin,Manager">` (comma-separated = OR).
342
498
  *
343
499
  * Roles arrive on `auth.user.roles` from `/api/auth/me`. Per-role claims are
344
- * only emitted for systemuser-backed principals — contacts don't have roles
500
+ * only emitted for systemuser-backed principals — contacts don't have roles
345
501
  * so a role gate also implicitly limits the surface to staff users.
346
502
  */
347
503
  roles?: string | readonly string[];
@@ -352,7 +508,7 @@ interface AuthorizedViewProps {
352
508
  fallback?: ReactNode;
353
509
  }
354
510
  /**
355
- * Headless auth gate — React equivalent of Blazor's `<AuthorizeView>`. Renders
511
+ * Headless auth gate — Renders
356
512
  * {@link AuthorizedViewProps.children} only when the principal is authenticated;
357
513
  * if {@link AuthorizedViewProps.roles} is supplied, the principal must hold at
358
514
  * least one of the listed roles. Otherwise renders {@link AuthorizedViewProps.fallback}
@@ -365,15 +521,15 @@ interface AuthorizedViewProps {
365
521
  * @example
366
522
  * ```tsx
367
523
  * <AuthorizedView>
368
- * <Link to="/Account/Manage">Manage account</Link>
524
+ * <Link to="/Account/Manage">Manage account</Link>
369
525
  * </AuthorizedView>
370
526
  *
371
527
  * <AuthorizedView roles="SystemAdmin">
372
- * <Link to="/SiteAdmin">Site admin</Link>
528
+ * <Link to="/SiteAdmin">Site admin</Link>
373
529
  * </AuthorizedView>
374
530
  *
375
531
  * <AuthorizedView roles={["Admin", "Manager"]} fallback={<span>Restricted</span>}>
376
- * <SensitiveTools />
532
+ * <SensitiveTools />
377
533
  * </AuthorizedView>
378
534
  * ```
379
535
  */
@@ -469,7 +625,7 @@ interface UseFetchRecordsOptions {
469
625
  }
470
626
  /**
471
627
  * Runs a FetchXML query. Re-runs when the `fetchXml` string changes; aborts
472
- * in-flight requests on unmount. The query string is the FetchXML XML literal
628
+ * in-flight requests on unmount. The query string is the FetchXML XML literal
473
629
  * the same format Dataverse natively accepts.
474
630
  *
475
631
  * `data.records` is the matched record list; `data.pagingCookie` and friends drive
@@ -495,10 +651,9 @@ interface UseGridDataOptions {
495
651
  }
496
652
  /**
497
653
  * Grid-data counterpart to {@link useFetchRecords}. Calls the server's
498
- * `POST /api/grids/data` endpoint, which composes the final FetchXML via the
499
- * shared `IFetchXmlQueryComposer` — applying search across the view's
500
- * searchable columns, replacing sorts, AND-merging filters, etc. — so the
501
- * search / sort / paging semantics match the Blazor grid exactly.
654
+ * `POST /api/grids/data` endpoint, which composes the final FetchXML via
655
+ * the shared `IFetchXmlQueryComposer` — applying search across the view's
656
+ * searchable columns, replacing sorts, AND-merging filters, etc.
502
657
  *
503
658
  * Response includes both the row data (`tableRecords`, `pagingInfo`) and a
504
659
  * server-resolved `columns` array — display names already localized, types
@@ -509,8 +664,10 @@ interface UseGridDataOptions {
509
664
  * serialization — cheap, and the request is a small flat object). Aborts
510
665
  * in-flight requests on unmount or when the request changes.
511
666
  *
512
- * Exactly one of `request.viewId` or `request.fetchXml` must be supplied;
513
- * sending both or neither produces a 400 from the server.
667
+ * At least one of `request.viewId` or `request.fetchXml` must be supplied
668
+ * (neither produces a 400 from the server). Both may be supplied together —
669
+ * the `fetchXml` is the query and the `viewId` names the view for localized
670
+ * column-title resolution.
514
671
  */
515
672
  declare function useGridData(request: GridDataRequest, options?: UseGridDataOptions): QueryResult<GridDataResponse>;
516
673
 
@@ -532,12 +689,12 @@ interface ViewDataSourceHighlight {
532
689
  * Options accepted by {@link useViewDataSource}. Split into two roles:
533
690
  *
534
691
  * - **Props** (forwarded straight into every request) — `viewId` /
535
- * `fetchXml` source, `staticFilters` (always-applied, never cleared
536
- * by mutators — the SubGrid relationship-filter case), `aggregations`
537
- * spec, behavior knobs (`enabled`, `keepPreviousData`, `debounceMs`).
538
- * - **Initial state** (seeds once on mount, mutators take over)
539
- * `initialFilter`, `initialSort`, `initialSearch`, `initialPage`,
540
- * `initialPageSize`, `initialHighlight`.
692
+ * `fetchXml` source, `staticFilters` (always-applied, never cleared
693
+ * by mutators — the SubGrid relationship-filter case), `aggregations`
694
+ * spec, behavior knobs (`enabled`, `keepPreviousData`, `debounceMs`).
695
+ * - **Initial state** (seeds once on mount, mutators take over)
696
+ * `initialFilter`, `initialSort`, `initialSearch`, `initialPage`,
697
+ * `initialPageSize`, `initialHighlight`.
541
698
  *
542
699
  * The same datasource instance feeds a `<MainGrid>` and a
543
700
  * `<DataverseChart>` (or two of either) — they all read the same row
@@ -546,14 +703,16 @@ interface ViewDataSourceHighlight {
546
703
  */
547
704
  interface UseViewDataSourceOptions {
548
705
  /**
549
- * Stored Dataverse view to query. Mutually exclusive with
550
- * {@link fetchXml}. Reading the view's columns drives the
551
- * search-column-type dispatch.
706
+ * Stored Dataverse view to query. When set alongside {@link fetchXml},
707
+ * the FetchXML drives the query and this id only supplies the view's
708
+ * identity for view-specific localized column titles (no savedquery is
709
+ * loaded). At least one of {@link viewId} / {@link fetchXml} must be set.
552
710
  */
553
711
  viewId?: string;
554
712
  /**
555
- * Caller-supplied FetchXML to query. Mutually exclusive with
556
- * {@link viewId}. Useful for custom views.
713
+ * Caller-supplied FetchXML to query. Useful for custom (in-memory) views.
714
+ * May be combined with {@link viewId} to give such a view a stable
715
+ * identity for localization (see its docs).
557
716
  */
558
717
  fetchXml?: string;
559
718
  /**
@@ -699,20 +858,20 @@ interface ViewDataSource {
699
858
  *
700
859
  * ```tsx
701
860
  * function OpportunitiesPage() {
702
- * const ds = useViewDataSource({
703
- * viewId: openOpportunitiesViewId,
704
- * aggregations: [
705
- * { groupBy: { column: 'stepname' },
706
- * aggregateColumn: 'estimatedvalue',
707
- * aggregate: AggregateType.Sum },
708
- * ],
709
- * });
710
- * return (
711
- * <>
712
- * <DataverseChart dataSource={ds} type="bar" />
713
- * <MainGrid dataSource={ds} />
714
- * </>
715
- * );
861
+ * const ds = useViewDataSource({
862
+ * viewId: openOpportunitiesViewId,
863
+ * aggregations: [
864
+ * { groupBy: { column: 'stepname' },
865
+ * aggregateColumn: 'estimatedvalue',
866
+ * aggregate: AggregateType.Sum },
867
+ * ],
868
+ * });
869
+ * return (
870
+ * <>
871
+ * <DataverseChart dataSource={ds} type="bar" />
872
+ * <MainGrid dataSource={ds} />
873
+ * </>
874
+ * );
716
875
  * }
717
876
  * ```
718
877
  */
@@ -742,11 +901,9 @@ interface UseTablePermissionsOptions {
742
901
  }
743
902
  /**
744
903
  * Fetches the current user's combined table-level `TableSecurityPermission`
745
- * mask for one Dataverse table. Mirrors the server-side
746
- * `ITablePermissionCache.GetPermissionForUserAsync` lookup the Blazor
747
- * grid buttons already use directly via DI — exposed here so React
748
- * consumers can drive the same "Can the user create / delete on this
749
- * table?" gates without firing a grid query.
904
+ * mask for one Dataverse table exposed so consumers can drive
905
+ * "Can the user create / delete on this table?" gates without firing a
906
+ * grid query.
750
907
  *
751
908
  * When a `<MainGrid>` / `<SubGrid>` is on the page the grid response
752
909
  * already carries the same value as `GridDataResponse.tablePermissions`
@@ -787,24 +944,25 @@ interface UseViewsForTableOptions {
787
944
  */
788
945
  declare function useViewsForTable(tableLogicalName: string, options?: UseViewsForTableOptions): QueryResult<readonly ViewMetadata[]>;
789
946
 
790
- type EnvironmentFileSettings$1 = components['schemas']['EnvironmentFileSettings'];
791
- interface UseEnvironmentFileSettingsOptions {
947
+ type OrganizationSettings$1 = components['schemas']['OrganizationSettings'];
948
+ interface UseOrganizationSettingsOptions {
792
949
  enabled?: boolean;
793
950
  }
794
951
  /**
795
- * Fetches the environment-wide file upload settings (blocked extension
796
- * list, organization-level max upload size). Mirrors the Blazor
797
- * <c>IPowerPortalsProService.GetEnvironmentFileSettingsAsync</c> call.
952
+ * Fetches the org-wide settings (default currency, etc.) sourced from the
953
+ * Dataverse `organization` record.
798
954
  *
799
- * Routed through {@link EnvironmentFileSettingsCache} on the provider
800
- * concurrent callers (e.g. multiple `<FileGrid>`s on the same page)
801
- * share a single fetch, and the resolved value is reused across re-
802
- * mounts for the lifetime of the `PowerPortalsProClient`. The cap
803
- * reported here is the org-wide ceiling — the effective per-upload cap
804
- * also has to account for per-column <c>maxSizeInKB</c> from
805
- * {@link useTableMetadata}.
955
+ * Routed through {@link OrganizationSettingsCache} on the provider
956
+ * concurrent callers (e.g. multiple create-mode `<RecordContext>`s on the
957
+ * same page) share a single fetch, and the resolved value is reused
958
+ * across re-mounts for the lifetime of the `PowerPortalsProClient`.
959
+ *
960
+ * Primary consumer is the create-mode `<RecordContext>` seed: the framework
961
+ * pre-fills `record.currency` with `defaultCurrency` so `<MoneyEdit>`
962
+ * renders the correct symbol on brand-new records before save. Custom
963
+ * components that need other org-level defaults can read them here too.
806
964
  */
807
- declare function useEnvironmentFileSettings(options?: UseEnvironmentFileSettingsOptions): QueryResult<EnvironmentFileSettings$1>;
965
+ declare function useOrganizationSettings(options?: UseOrganizationSettingsOptions): QueryResult<OrganizationSettings$1>;
808
966
 
809
967
  interface UseSupportedLocalesOptions {
810
968
  /**
@@ -826,18 +984,16 @@ interface UseSupportedLocalesOptions {
826
984
  * request on every page navigation in apps where the dropdown sits
827
985
  * inside route-level chrome.
828
986
  *
829
- * Fallback path: when no `DefaultLocalizerProvider` is mounted (rare
987
+ * Fallback path: when no `DefaultLocalizerProvider` is mounted (rare
830
988
  * consumers who wired their own i18next setup via
831
989
  * `localizationUrl={false}`), the hook still issues its own fetch so
832
990
  * the API works in isolation. The fallback uses the same
833
991
  * `useAsyncResource` shape it always did, so the {@link QueryResult}
834
992
  * surface is unchanged.
835
993
  *
836
- * The Blazor side gets this server-side via
837
- * `IOptions<RequestLocalizationOptions>.SupportedUICultures`; in
838
- * React the same configuration is exposed through the localization
839
- * manifest endpoint at `/localizations/version`. Order matches the
840
- * server's configured preference, default culture first.
994
+ * Server-side configuration is exposed through the localization manifest
995
+ * endpoint at `/localizations/version`. Order matches the server's
996
+ * configured preference, default culture first.
841
997
  */
842
998
  declare function useSupportedLocales(options?: UseSupportedLocalesOptions): QueryResult<readonly string[]>;
843
999
 
@@ -864,7 +1020,7 @@ declare function useSupportedLocales(options?: UseSupportedLocalesOptions): Quer
864
1020
  */
865
1021
  declare function useQueryParam(name: string): string | null;
866
1022
 
867
- type EnvironmentFileSettings = components['schemas']['EnvironmentFileSettings'];
1023
+ type OrganizationSettings = components['schemas']['OrganizationSettings'];
868
1024
  /**
869
1025
  * Default in-memory implementation of {@link TableMetadataCache}. Wraps
870
1026
  * a `PowerPortalsProClient.retrieveTableMetadataAsync` call with Map-based
@@ -890,19 +1046,19 @@ declare class InMemoryViewMetadataCache implements ViewMetadataCache {
890
1046
  clear(): void;
891
1047
  }
892
1048
  /**
893
- * Default in-memory implementation of {@link EnvironmentFileSettingsCache}.
1049
+ * Default in-memory implementation of {@link OrganizationSettingsCache}.
894
1050
  * Singleton-shaped: no key, just one stored value + one in-flight promise.
895
1051
  *
896
- * Implemented as a thin wrapper around the keyed {@link InMemoryCache}
897
- * with a fixed sentinel key, so the dedup / fetch / error-eviction
898
- * behaviors stay identical to the metadata caches without re-implementing
899
- * any of that logic.
1052
+ * Implemented as a thin wrapper around the keyed {@link InMemoryCache} with
1053
+ * a fixed sentinel key, so the dedup / fetch / error-eviction behaviors stay
1054
+ * identical to the metadata caches without re-implementing any of that
1055
+ * logic.
900
1056
  */
901
- declare class InMemoryEnvironmentFileSettingsCache implements EnvironmentFileSettingsCache {
1057
+ declare class InMemoryOrganizationSettingsCache implements OrganizationSettingsCache {
902
1058
  private static readonly KEY;
903
1059
  private readonly cache;
904
1060
  constructor(ppp: PowerPortalsProClient);
905
- getAsync(): Promise<EnvironmentFileSettings | null>;
1061
+ getAsync(): Promise<OrganizationSettings | null>;
906
1062
  invalidate(): void;
907
1063
  }
908
1064
  /**
@@ -993,17 +1149,17 @@ interface MainContextValue {
993
1149
  * Transactionally saves the entire descendant subtree:
994
1150
  *
995
1151
  * 1. Calls {@link MainContextProps.onBeforeSave} (if wired). Throwing
996
- * aborts the save before any server call.
1152
+ * aborts the save before any server call.
997
1153
  * 2. Runs {@link validate} across descendants; returns early without
998
- * saving when any descendant reports invalid.
1154
+ * saving when any descendant reports invalid.
999
1155
  * 3. Collects {@link MainContextDescendant.getRequests} from every
1000
- * registered descendant.
1156
+ * registered descendant.
1001
1157
  * 4. If non-empty, ships the combined array in one
1002
- * `executeMultipleAsync` round-trip so the operations land
1003
- * transactionally on the server.
1158
+ * `executeMultipleAsync` round-trip so the operations land
1159
+ * transactionally on the server.
1004
1160
  * 5. Calls {@link refreshAll} so the local state picks up server-applied
1005
- * side-effects (computed columns, plugin defaults, the modifiedon
1006
- * timestamp, etc.).
1161
+ * side-effects (computed columns, plugin defaults, the modifiedon
1162
+ * timestamp, etc.).
1007
1163
  *
1008
1164
  * Throws on the first server-side failure; later steps are skipped.
1009
1165
  */
@@ -1034,10 +1190,9 @@ interface MainContextValue {
1034
1190
  * append extra requests before calling
1035
1191
  * `IPowerPortalsPro.executeMultipleAsync` directly).
1036
1192
  *
1037
- * Mirrors Blazor's public `MainContext.GetRequests()` same walk over
1038
- * registered grids / child contexts / M2M editors, same shape.
1039
- * Consumers that just want to save should call {@link saveAll}; this
1040
- * is the escape hatch for the rarer dry-run / merge-with-extras case.
1193
+ * Walks the registered grids, child contexts, and M2M editors. Consumers
1194
+ * that just want to save should call {@link saveAll}; this is the escape
1195
+ * hatch for the rarer dry-run / merge-with-extras case.
1041
1196
  */
1042
1197
  readonly getRequests: () => OrganizationRequest[];
1043
1198
  /**
@@ -1100,10 +1255,10 @@ interface MainContextProps {
1100
1255
  * Resolution rules:
1101
1256
  * - `true` / `false` — explicit override at this level.
1102
1257
  * - `undefined` (default) — inherit from the parent
1103
- * {@link MainContext} / {@link RecordContext}, falling back to `true` at
1104
- * the root. Set `false` on the outer context of e.g. a demo site to
1105
- * suppress the prompt across the whole subtree without touching every
1106
- * nested context.
1258
+ * {@link MainContext} / {@link RecordContext}, falling back to `true` at
1259
+ * the root. Set `false` on the outer context of e.g. a demo site to
1260
+ * suppress the prompt across the whole subtree without touching every
1261
+ * nested context.
1107
1262
  *
1108
1263
  * Only the **top-most** MainContext / RecordContext on the page actually
1109
1264
  * registers the listener — when contexts nest, the inner ones skip
@@ -1126,8 +1281,8 @@ interface MainContextProps {
1126
1281
  * Resolution rules (same as {@link warnOnUnsavedChanges}):
1127
1282
  * - `true` / `false` — explicit override at this level.
1128
1283
  * - `undefined` (default) — inherit from the parent
1129
- * {@link MainContext} / {@link RecordContext}, falling back to `true`
1130
- * at the root.
1284
+ * {@link MainContext} / {@link RecordContext}, falling back to `true`
1285
+ * at the root.
1131
1286
  */
1132
1287
  forceSuccessfulValidationBeforeSave?: boolean;
1133
1288
  }
@@ -1142,14 +1297,12 @@ interface MainContextProps {
1142
1297
  * register with their nearest ancestor.
1143
1298
  *
1144
1299
  * Deferred surface (and how to layer it on later):
1145
- * - **Dirty tracking.** Blazor's MainContext aggregates `isDirty` across children
1146
- * via `EditContext`. React doesn't have a built-in form layer; consumers bring
1147
- * their own (react-hook-form, formik, vanilla state). When the dirty story
1148
- * solidifies, an `isDirty: () => boolean` getter on `MainContextDescendant`
1149
- * plus an aggregate getter on `MainContextValue` is the natural extension.
1300
+ * - **Dirty tracking.** Consumers bring their own form layer (react-hook-form,
1301
+ * formik, vanilla state). When the dirty story solidifies in a project, an
1302
+ * `isDirty: () => boolean` getter on `MainContextDescendant` plus an aggregate
1303
+ * getter on `MainContextValue` is the natural extension.
1150
1304
  * - **Unsaved-changes warning** dialog on navigation. Driven by dirty tracking.
1151
1305
  * - **Validation orchestration**. Same — needs the form layer.
1152
- * - **Persistent state across server↔WASM**. N/A in pure React.
1153
1306
  */
1154
1307
  declare function MainContext({ children, onBeforeSave, warnOnUnsavedChanges, forceSuccessfulValidationBeforeSave, }: MainContextProps): react_jsx_runtime.JSX.Element;
1155
1308
  /**
@@ -1162,12 +1315,11 @@ declare function useMainContext(): MainContextValue | null;
1162
1315
  /**
1163
1316
  * Provides an existing {@link MainContextValue} to a subtree. Used by
1164
1317
  * {@link RecordContext} to expose itself as a `MainContext` for nested
1165
- * descendants (mirroring Blazor's `RecordContext : MainContext` inheritance)
1166
- * keeps the raw `MainContextContext` internal while still letting framework
1167
- * components compose multiple `MainContext` levels.
1318
+ * descendants keeps the raw `MainContextContext` internal while still
1319
+ * letting framework components compose multiple `MainContext` levels.
1168
1320
  *
1169
- * Not generally needed by application code — use `<MainContext>` instead, which
1170
- * builds the value internally.
1321
+ * Not generally needed by application code — use `<MainContext>` instead,
1322
+ * which builds the value internally.
1171
1323
  */
1172
1324
  declare function MainContextProvider({ value, children, }: {
1173
1325
  value: MainContextValue;
@@ -1210,11 +1362,10 @@ interface RecordContextValue {
1210
1362
  * (when dirty) followed by each nested descendant's `getRequests()`, in
1211
1363
  * registration order. Returns an empty array when nothing is dirty.
1212
1364
  *
1213
- * Mirrors Blazor's public `RecordContext.GetRequests()` same shape, same
1214
- * traversal. Use for previewing the payload {@link save} would ship,
1215
- * for dry-run UIs, or to merge the records into a custom
1216
- * `executeMultiple` batch alongside consumer-supplied requests.
1217
- * Consumers that just want to persist should call {@link save}.
1365
+ * Use for previewing the payload {@link save} would ship, for dry-run
1366
+ * UIs, or to merge the records into a custom `executeMultiple` batch
1367
+ * alongside consumer-supplied requests. Consumers that just want to
1368
+ * persist should call {@link save}.
1218
1369
  */
1219
1370
  readonly getRequests: () => OrganizationRequest[];
1220
1371
  /**
@@ -1315,7 +1466,7 @@ interface RecordContextValue {
1315
1466
  }
1316
1467
  /**
1317
1468
  * `RecordContext` extends {@link MainContextProps} because every `RecordContext`
1318
- * conceptually IS-A `MainContext` — Blazor's `RecordContext.razor.cs` literally
1469
+ * conceptually IS-A `MainContext` — literally
1319
1470
  * inherits from `MainContext.razor.cs` and `override`s `IsDirty` to add the
1320
1471
  * record's own dirty state on top of the base aggregation. Inheriting the props
1321
1472
  * means `onBeforeSave` (and any future cross-cutting `MainContextProps`) is
@@ -1420,8 +1571,7 @@ interface RecordContextProps extends MainContextProps {
1420
1571
  }
1421
1572
  /**
1422
1573
  * Loads a single Dataverse record and provides it to descendants via React
1423
- * context (the analog of Blazor's cascading value). Children call
1424
- * {@link useRecordContext} to read the record + status.
1574
+ * context. Children call {@link useRecordContext} to read the record + status.
1425
1575
  *
1426
1576
  * Also acts as a `MainContext` for its own subtree — nested grids, M2M editors,
1427
1577
  * and child `<RecordContext>`s register with this `RecordContext` instead of
@@ -1435,18 +1585,10 @@ interface RecordContextProps extends MainContextProps {
1435
1585
  * Without a parent, `save()` and `reload()` are still callable directly from
1436
1586
  * descendants via the context.
1437
1587
  *
1438
- * What's deferred from Blazor's RecordContext (the full version is ~700 lines
1439
- * this is the headless coordination skeleton):
1440
- * - **New-record creation** (id absent → empty TableRecord). Will need a
1441
- * `mode: "edit" | "create"` discriminator and dispatching `createRecordAsync`
1442
- * instead of `updateRecordAsync` from `save()`.
1443
- * - **Dirty tracking + EditContext-style validation**. Tied to the form layer.
1588
+ * Deferred surface (the headless coordination skeleton; rounded out later):
1444
1589
  * - **`OnBeforeSave` / `OnBeforeDelete`** cancellable callbacks at this level
1445
- * (MainContext already has `onBeforeSave`).
1590
+ * (MainContext already exposes `onBeforeSave`).
1446
1591
  * - **Delete handler** with confirmation flow.
1447
- * - **URL query-parameter reading** (Blazor's `QueryParameterName`). Consumers can
1448
- * pull the id from `useSearchParams()` themselves and pass it as the `id` prop.
1449
- * - **PersistentComponentState** for server→WASM bridging. N/A in pure React.
1450
1592
  */
1451
1593
  /**
1452
1594
  * Wrapper that auto-mounts a {@link ValidationProvider} around the inner
@@ -1467,10 +1609,10 @@ declare function RecordContext(props: RecordContextProps): react_jsx_runtime.JSX
1467
1609
  * import { QueryStatus, useRecordContext } from '@powerportalspro/react';
1468
1610
  *
1469
1611
  * function ContactDetails() {
1470
- * const { status, record } = useRecordContext();
1471
- * if (status === QueryStatus.Loading) return <Spinner />;
1472
- * if (status === QueryStatus.Error) return <ErrorBanner />;
1473
- * return <h1>{record!.formattedValues?.name}</h1>;
1612
+ * const { status, record } = useRecordContext();
1613
+ * if (status === QueryStatus.Loading) return <Spinner />;
1614
+ * if (status === QueryStatus.Error) return <ErrorBanner />;
1615
+ * return <h1>{record!.formattedValues?.name}</h1>;
1474
1616
  * }
1475
1617
  * ```
1476
1618
  */
@@ -1495,16 +1637,14 @@ interface LookupRecordContextProps {
1495
1637
  * `$type: 6`) — the component reads the lookup's `tableName` + `value`
1496
1638
  * and spawns a nested `<RecordContext>` for the referenced record.
1497
1639
  *
1498
- * Required. Mirrors Blazor's `<LookupRecordContext ColumnName="...">`
1499
- * `ColumnName` parameter.
1640
+ * Required.
1500
1641
  */
1501
1642
  columnName: string;
1502
1643
  /**
1503
1644
  * Optional pre-save cancellable hook forwarded to the nested
1504
1645
  * `<RecordContext>`. Throwing (or returning a rejected promise) from
1505
1646
  * this callback aborts the save before any server call — the same
1506
- * semantic as `<RecordContext onBeforeSave={…}>` directly. Mirrors
1507
- * Blazor's `LookupRecordContext.OnBeforeSave` cancellable hook.
1647
+ * semantic as `<RecordContext onBeforeSave={…}>` directly.
1508
1648
  */
1509
1649
  onBeforeSave?: () => void | Promise<void>;
1510
1650
  /**
@@ -1519,16 +1659,15 @@ interface LookupRecordContextProps {
1519
1659
  /**
1520
1660
  * When `false`, renders nothing. Defaults to `true`. Useful for
1521
1661
  * conditionally hiding the section without unmounting the
1522
- * declaration. Mirrors `IsVisible` on the other framework
1523
- * components.
1662
+ * declaration.
1524
1663
  */
1525
1664
  isVisible?: boolean;
1526
1665
  }
1527
1666
  /**
1528
1667
  * Branches off into a nested `<RecordContext>` for the record referenced
1529
- * by a lookup column on the surrounding `<RecordContext>`. Mirrors
1530
- * Blazor's `<LookupRecordContext>` a thin wrapper that resolves the
1531
- * lookup value, gates on its presence, and spawns the nested context.
1668
+ * by a lookup column on the surrounding `<RecordContext>`. A thin
1669
+ * wrapper that resolves the lookup value, gates on its presence, and
1670
+ * spawns the nested context.
1532
1671
  *
1533
1672
  * Edit components inside the lookup context bind to columns on the
1534
1673
  * LOOKED-UP record's table, not the parent's. Changes flow through the
@@ -1538,46 +1677,44 @@ interface LookupRecordContextProps {
1538
1677
  * `<RecordContext>`-as-MainContext) and its pending edits join the
1539
1678
  * single transactional `executeMultiple` batch on save.
1540
1679
  *
1541
- * **Conditional render (matches Blazor's lazy pattern):** the nested
1542
- * `<RecordContext>` is only instantiated when the lookup actually has
1543
- * a value (non-null + non-empty-GUID + has `tableName`). Empty lookups
1544
- * render nothing — avoids spinning up nested contexts, EditContext
1545
- * validators, descendant registrations, etc. for every empty lookup
1546
- * column on a form. The Blazor source carries an explicit perf note on
1547
- * this: ~10 lookups × per-render lifecycle overhead added up to a
1548
- * measurable freeze before the conditional render was added.
1680
+ * **Conditional render:** the nested `<RecordContext>` is only
1681
+ * instantiated when the lookup actually has a value (non-null +
1682
+ * non-empty-GUID + has `tableName`). Empty lookups render nothing
1683
+ * avoids spinning up nested contexts, EditContext validators,
1684
+ * descendant registrations, etc. for every empty lookup column on a
1685
+ * form. With ~10 lookups on a complex form, the per-render lifecycle
1686
+ * overhead is large enough to produce a measurable freeze without this
1687
+ * guard.
1549
1688
  *
1550
1689
  * ```tsx
1551
1690
  * <RecordContext table="account" id={accountId}>
1552
- * <Section>
1553
- * <SectionColumn header={<Text>Account</Text>}>
1554
- * <TextEdit columnName="name" />
1555
- * <TextEdit columnName="accountnumber" />
1556
- * </SectionColumn>
1557
- *
1558
- * <SectionColumn header={<Text>Primary contact</Text>}>
1559
- * <LookupRecordContext columnName="primarycontactid">
1560
- * {/* These edits bind to the linked CONTACT, not the account *\/}
1561
- * <TextEdit columnName="firstname" />
1562
- * <TextEdit columnName="lastname" />
1563
- * <TextEdit columnName="emailaddress1" type="email" />
1564
- * </LookupRecordContext>
1565
- * </SectionColumn>
1566
- * </Section>
1691
+ * <Section>
1692
+ * <SectionColumn header={<Text>Account</Text>}>
1693
+ * <TextEdit columnName="name" />
1694
+ * <TextEdit columnName="accountnumber" />
1695
+ * </SectionColumn>
1696
+ *
1697
+ * <SectionColumn header={<Text>Primary contact</Text>}>
1698
+ * <LookupRecordContext columnName="primarycontactid">
1699
+ * {/* These edits bind to the linked CONTACT, not the account *\/}
1700
+ * <TextEdit columnName="firstname" />
1701
+ * <TextEdit columnName="lastname" />
1702
+ * <TextEdit columnName="emailaddress1" type="email" />
1703
+ * </LookupRecordContext>
1704
+ * </SectionColumn>
1705
+ * </Section>
1567
1706
  * </RecordContext>
1568
1707
  * ```
1569
1708
  *
1570
1709
  * Renders `null` when:
1571
- * - the surrounding `<RecordContext>` hasn't loaded yet,
1572
- * - the parent column doesn't carry a `LookupValue`,
1573
- * - the lookup is the empty GUID (Dataverse's "no value" sentinel), or
1574
- * - the lookup is missing its `tableName`.
1710
+ * - the surrounding `<RecordContext>` hasn't loaded yet,
1711
+ * - the parent column doesn't carry a `LookupValue`,
1712
+ * - the lookup is the empty GUID (Dataverse's "no value" sentinel), or
1713
+ * - the lookup is missing its `tableName`.
1575
1714
  *
1576
- * `<LookupRecordContext>` deliberately does NOT mirror Blazor's
1577
- * `BaseColumnEdit` inheritance the parent's column read-write loop
1578
- * is owned by `useLookupColumn(columnName)` for consumers who want to
1579
- * change the lookup value programmatically; `<LookupRecordContext>` is
1580
- * pure read-then-branch.
1715
+ * `<LookupRecordContext>` is read-then-branch only consumers who want
1716
+ * to change the lookup value programmatically use
1717
+ * `useLookupColumn(columnName)` from the parent context instead.
1581
1718
  */
1582
1719
  declare function LookupRecordContext({ columnName, onBeforeSave, children, isVisible, }: LookupRecordContextProps): react_jsx_runtime.JSX.Element | null;
1583
1720
 
@@ -1590,10 +1727,9 @@ type ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];
1590
1727
  * - the column doesn't exist on the table.
1591
1728
  *
1592
1729
  * Edit components use this to auto-resolve defaults (`maxLength`, `min`,
1593
- * `max`, `precision`, `required`, `readOnly`) when consumer-passed props are
1594
- * absent. Pass-through props always win — same convention as Blazor's
1595
- * `BaseColumnEdit.OnParametersSetAsync` (which only fills metadata-derived
1596
- * defaults when the consumer hasn't set them explicitly).
1730
+ * `max`, `precision`, `required`, `readOnly`) when consumer-passed props
1731
+ * are absent. Pass-through props always win — metadata-derived defaults
1732
+ * only fill the slots the consumer left unset.
1597
1733
  *
1598
1734
  * Throws if not used inside a `<RecordContext>`, since column metadata only
1599
1735
  * makes sense relative to a record's table.
@@ -1608,7 +1744,7 @@ declare function useColumnMetadata(columnName: string): ColumnMetadataBase | und
1608
1744
  * map for tests, or a server-driven RPC localizer all work the same way.
1609
1745
  *
1610
1746
  * Args use positional indexing (`args[0]` → `{0}` in the template) to match
1611
- * the existing Blazor `IStringLocalizer` JSON format — `.NET String.Format`
1747
+ * the existing JSON format — `.NET String.Format`
1612
1748
  * placeholders. Consumers using a named-args lib (e.g. i18next's `{{name}}`)
1613
1749
  * adapt at the boundary.
1614
1750
  */
@@ -1616,7 +1752,7 @@ interface Localizer {
1616
1752
  t(key: string, args?: readonly unknown[]): string;
1617
1753
  }
1618
1754
  /**
1619
- * Reads the nearest {@link Localizer}. Throws when called outside a provider
1755
+ * Reads the nearest {@link Localizer}. Throws when called outside a provider
1620
1756
  * mirrors `useDialogService`'s strict behavior. The framework auto-mounts a
1621
1757
  * default `<DefaultLocalizerProvider>` inside `<PowerPortalsProProvider>`, so
1622
1758
  * this only fails when neither is present (typically a misconfigured test).
@@ -1629,11 +1765,9 @@ declare function useLocalizer(): Localizer;
1629
1765
  declare function useT(): Localizer['t'];
1630
1766
  /**
1631
1767
  * Returns a {@link Localizer.t} function with every key automatically
1632
- * prepended with `{prefix}.`. React analog of Blazor's
1633
- * `IPrefixedStringLocalizer` / `IStringLocalizer<T>` use when a
1634
- * component consumes strings from a known namespace (typically another
1635
- * framework's class-scoped keys) and you don't want to repeat the
1636
- * prefix at every call site.
1768
+ * prepended with `{prefix}.`. Use when a component consumes strings from
1769
+ * a known namespace and you don't want to repeat the prefix at every
1770
+ * call site.
1637
1771
  *
1638
1772
  * Pure sugar over `useT()` — relies on the same auto-mounted
1639
1773
  * `<DefaultLocalizerProvider>` and inherits all of its behavior
@@ -1643,12 +1777,10 @@ declare function useT(): Localizer['t'];
1643
1777
  *
1644
1778
  * @example
1645
1779
  * ```tsx
1646
- * const t = usePrefixedT(
1647
- * 'components.PowerPortalsPro.Web.Blazor.FluentUI.Components.Theme.ThemeColorSelector',
1648
- * );
1649
- * t('OfficeColor.0');
1780
+ * const t = usePrefixedT('components.MyApp.Pages.Editors.TextEditDemoPage');
1781
+ * t('title');
1650
1782
  * // → looks up
1651
- * // 'components.PowerPortalsPro.Web.Blazor.FluentUI.Components.Theme.ThemeColorSelector.OfficeColor.0'
1783
+ * // 'components.MyApp.Pages.Editors.TextEditDemoPage.title'
1652
1784
  * ```
1653
1785
  *
1654
1786
  * The returned function is stable per `prefix` value (memoized via
@@ -1714,12 +1846,12 @@ declare function useLocalizationPrefetcher(): LocalizationPrefetcher | null;
1714
1846
  * the strings are ready.
1715
1847
  *
1716
1848
  * Accepts prefix or full-key shapes:
1717
- * - `tables.{tablename}` (token) → loads `tables.<tablename>.*` strings
1718
- * - `tables.account.label` (key) → coerced to `tables.account`
1719
- * - `views.{viewId}` (token) → loads `views.<viewId>.*` strings
1720
- * - `tables.account.views.{viewId}.foo` (key) → coerced to `views.<viewId>`
1721
- * - Anything else (`app.*`, `components.*`) → silently dropped; covered
1722
- * by the default bundle loaded on app boot.
1849
+ * - `tables.{tablename}` (token) → loads `tables.<tablename>.*` strings
1850
+ * - `tables.account.label` (key) → coerced to `tables.account`
1851
+ * - `views.{viewId}` (token) → loads `views.<viewId>.*` strings
1852
+ * - `tables.account.views.{viewId}.foo` (key) → coerced to `views.<viewId>`
1853
+ * - Anything else (`app.*`, `components.*`) → silently dropped; covered
1854
+ * by the default bundle loaded on app boot.
1723
1855
  *
1724
1856
  * Re-fires only when `prefixes` changes by VALUE — passing a fresh array
1725
1857
  * with the same content each render doesn't requeue. Already-fetched
@@ -1733,8 +1865,8 @@ declare function useLocalizationPrefetcher(): LocalizationPrefetcher | null;
1733
1865
  * @example
1734
1866
  * ```tsx
1735
1867
  * function MyAccountPage() {
1736
- * useLocalization(['tables.account', 'tables.contact']);
1737
- * return <Form>...</Form>;
1868
+ * useLocalization(['tables.account', 'tables.contact']);
1869
+ * return <Form>...</Form>;
1738
1870
  * }
1739
1871
  * ```
1740
1872
  */
@@ -1752,9 +1884,9 @@ declare function useLocalization(prefixes: readonly string[]): void;
1752
1884
  * @example
1753
1885
  * ```tsx
1754
1886
  * function MyForm() {
1755
- * const ready = useLocalizationLoaded(['tables.account']);
1756
- * if (!ready) return <Spinner />;
1757
- * return <Form>...</Form>;
1887
+ * const ready = useLocalizationLoaded(['tables.account']);
1888
+ * if (!ready) return <Spinner />;
1889
+ * return <Form>...</Form>;
1758
1890
  * }
1759
1891
  * ```
1760
1892
  */
@@ -1770,9 +1902,8 @@ declare function LocalizerProvider({ value, children, }: {
1770
1902
  children: ReactNode;
1771
1903
  }): react_jsx_runtime.JSX.Element;
1772
1904
  /**
1773
- * Flattens a nested object (Blazor's localization JSON shape) into a flat
1774
- * `key.path → value` map. Drops non-string leaves (no use for them in the
1775
- * localizer's lookup).
1905
+ * Flattens a nested object into a flat `key.path value` map. Drops
1906
+ * non-string leaves (no use for them in the localizer's lookup).
1776
1907
  */
1777
1908
  declare function flattenStrings(obj: Record<string, unknown>, prefix?: string): Record<string, string>;
1778
1909
  /**
@@ -1817,9 +1948,9 @@ declare function createLocalizer(strings: Record<string, string>, options?: Crea
1817
1948
  declare function resolveLocalizationUrl(template: string, locale: string): string;
1818
1949
  /**
1819
1950
  * Cookie name ASP.NET Core's `CookieRequestCultureProvider` uses to persist
1820
- * the active culture. Same cookie the Blazor stack sets via the
1821
- * <c>/Culture/{culture}</c> endpoint, so reading/writing it here keeps the
1822
- * React and Blazor stacks in sync when both are hosted side-by-side.
1951
+ * the active culture. Reading and writing this cookie keeps the React app
1952
+ * in sync with any same-origin server-rendered surface that reads its
1953
+ * culture from the same source.
1823
1954
  */
1824
1955
  declare const ASP_NET_CULTURE_COOKIE = ".AspNetCore.Culture";
1825
1956
  /**
@@ -1833,10 +1964,9 @@ declare const ASP_NET_CULTURE_COOKIE = ".AspNetCore.Culture";
1833
1964
  declare function getAspNetCultureCookie(): string | null;
1834
1965
  /**
1835
1966
  * Writes the ASP.NET Core culture cookie with both `c` and `uic` segments
1836
- * set to the same value. Mirrors what the server-side
1837
- * <c>/Culture/{culture}</c> endpoint sets, so a React-side locale change
1838
- * immediately affects subsequent server-rendered Blazor pages on the same
1839
- * origin (and vice versa).
1967
+ * set to the same value. A React-side locale change immediately affects
1968
+ * subsequent same-origin server-rendered pages that read their culture
1969
+ * from the same cookie (and vice versa).
1840
1970
  *
1841
1971
  * Defaults to `path=/`, 1-year expiry, `SameSite=Lax`. Safe to call in
1842
1972
  * non-browser environments (no-op).
@@ -1846,7 +1976,7 @@ declare function setAspNetCultureCookie(culture: string, options?: {
1846
1976
  maxAgeSeconds?: number;
1847
1977
  }): void;
1848
1978
  /**
1849
- * Default regex matching a locale code at the start of `window.location.pathname`
1979
+ * Default regex matching a locale code at the start of `window.location.pathname`
1850
1980
  * shapes like `/en`, `/en/`, `/en-US/page`, `/fr-CA/path/x`. Captures the
1851
1981
  * locale code in group 1. Only 2-letter primary tags + optional 2–4-letter
1852
1982
  * subtag (script / region) are recognized; arbitrary path segments don't
@@ -1861,16 +1991,52 @@ declare const DEFAULT_URL_LOCALE_PATTERN: RegExp;
1861
1991
  * Safe to call in non-browser environments (returns `null`).
1862
1992
  */
1863
1993
  declare function getLocaleFromUrlPath(pattern?: RegExp): string | null;
1994
+ /**
1995
+ * The server's configured URL-culture strategy, surfaced to the client through
1996
+ * the localization manifest (`urlCultureStrategy`). Mirrors the C#
1997
+ * `PowerPortalsPro.Web.Options.UrlCultureStrategy` enum names:
1998
+ *
1999
+ * - `PathPrefix` — default locale bare (`/contacts`), others prefixed (`/fr/contacts`).
2000
+ * - `PathPrefixAll` — every locale prefixed, including the default (`/en/contacts`).
2001
+ * - `CookieOnly` — the locale never appears in the URL; switching is in-place.
2002
+ */
2003
+ type UrlCultureStrategyName = 'PathPrefix' | 'PathPrefixAll' | 'CookieOnly';
2004
+ /**
2005
+ * Whether a URL for `locale` carries a `/{locale}` path prefix under `strategy`.
2006
+ * Runtime mirror of the server-side `CultureUrls.ShouldPrefix` so client-built
2007
+ * links match the shape the LocalizationMiddleware actually serves.
2008
+ */
2009
+ declare function shouldPrefixLocale(strategy: UrlCultureStrategyName, locale: string, defaultLocale: string): boolean;
2010
+ /**
2011
+ * Removes a leading `/{locale}` segment from `pathname` when that segment is one
2012
+ * of `supportedLocales`. Returns the "cultureless" path (always leading-slash,
2013
+ * `/` for root). Mirror of the server-side `StripCulturePrefix`.
2014
+ */
2015
+ declare function stripLocalePrefix(pathname: string, supportedLocales: readonly string[]): string;
2016
+ /**
2017
+ * Builds the path (leading slash, no origin) at which `locale` serves the current
2018
+ * page under `strategy`, given the current `pathname` (which may already carry a
2019
+ * locale prefix) and the server's `supportedLocales`. Runtime mirror of the
2020
+ * server-side `CultureUrls.BuildPath`. Use it to compute the navigation target
2021
+ * when switching locale under a prefix strategy.
2022
+ */
2023
+ declare function buildLocalePath(options: {
2024
+ strategy: UrlCultureStrategyName;
2025
+ locale: string;
2026
+ defaultLocale: string;
2027
+ pathname: string;
2028
+ supportedLocales: readonly string[];
2029
+ }): string;
1864
2030
  /**
1865
2031
  * Resolves the active locale from common sources, in order of precedence:
1866
2032
  *
1867
2033
  * 1. URL path segment (`/en/...`) — picked up via {@link getLocaleFromUrlPath}
1868
- * when `matchUrl` is `true` (default).
2034
+ * when `matchUrl` is `true` (default).
1869
2035
  * 2. ASP.NET culture cookie (`.AspNetCore.Culture`) — picked up via
1870
- * {@link getAspNetCultureCookie}.
2036
+ * {@link getAspNetCultureCookie}.
1871
2037
  * 3. `navigator.language` — when `matchBrowser` is `true` (default), strips
1872
- * the region (e.g. `en-US` → `en`) for compatibility with bundle locale
1873
- * codes which are typically primary-tag only.
2038
+ * the region (e.g. `en-US` → `en`) for compatibility with bundle locale
2039
+ * codes which are typically primary-tag only.
1874
2040
  * 4. The supplied `fallback` (default `'en'`).
1875
2041
  *
1876
2042
  * Used by `<LocaleProvider>` for auto-detection; also exported for consumer
@@ -1888,6 +2054,21 @@ interface LocaleState {
1888
2054
  /** Replace the active locale. Triggers re-fetch of localization JSONs. */
1889
2055
  readonly setLocale: (locale: string) => void;
1890
2056
  }
2057
+ /**
2058
+ * Reads the server's configured {@link UrlCultureStrategyName} from the
2059
+ * {@link LocalizationManifestContext} (populated by
2060
+ * {@link DefaultLocalizerProvider}'s manifest fetch). Returns `undefined`
2061
+ * before the manifest lands, or when no provider is mounted. Consumers
2062
+ * building a custom locale switcher use this to decide whether to navigate
2063
+ * to a `/{locale}` URL or switch in place.
2064
+ */
2065
+ declare function useUrlCultureStrategy(): UrlCultureStrategyName | undefined;
2066
+ /**
2067
+ * Reads the server's default locale — the first entry of the manifest's
2068
+ * supported-locale list (the server emits it default-culture-first). Returns
2069
+ * `undefined` before the manifest lands, or when no provider is mounted.
2070
+ */
2071
+ declare function useDefaultLocale(): string | undefined;
1891
2072
  /**
1892
2073
  * Reads the active locale state, falling back to `{ locale: 'en' }` when no
1893
2074
  * `<LocaleProvider>` is mounted. Apps that don't need locale switching can
@@ -1916,11 +2097,10 @@ interface LocaleProviderProps {
1916
2097
  */
1917
2098
  urlPattern?: RegExp | null;
1918
2099
  /**
1919
- * Persist locale changes to the ASP.NET culture cookie (the same cookie
1920
- * Blazor's locale switcher writes via `/Culture/{culture}`). Default
1921
- * `true` keeps the React + Blazor stacks in sync when both render
1922
- * pages on the same origin. Pass `false` to manage persistence yourself
1923
- * (e.g. via the `onLocaleChange` callback).
2100
+ * Persist locale changes to the ASP.NET culture cookie. Default `true`
2101
+ * keeps any same-origin server-rendered page in sync with the React-side
2102
+ * locale. Pass `false` to manage persistence yourself (e.g. via the
2103
+ * `onLocaleChange` callback).
1924
2104
  */
1925
2105
  persistToCookie?: boolean;
1926
2106
  /**
@@ -1948,9 +2128,9 @@ interface LocaleProviderProps {
1948
2128
  * Example:
1949
2129
  * ```tsx
1950
2130
  * <LocaleProvider initialLocale="en">
1951
- * <PowerPortalsProProvider>
1952
- * <App /> {/* useLocale() inside returns { locale: 'en', setLocale: ... } *\/}
1953
- * </PowerPortalsProProvider>
2131
+ * <PowerPortalsProProvider>
2132
+ * <App /> {/* useLocale() inside returns { locale: 'en', setLocale: ... } *\/}
2133
+ * </PowerPortalsProProvider>
1954
2134
  * </LocaleProvider>
1955
2135
  * ```
1956
2136
  */
@@ -1969,7 +2149,7 @@ interface DefaultLocalizerProviderProps {
1969
2149
  */
1970
2150
  urls?: readonly string[];
1971
2151
  /**
1972
- * @deprecated Use {@link urls} instead. Kept for backwards compatibility
2152
+ * @deprecated Use {@link urls} instead. Kept for backwards compatibility
1973
2153
  * when set, treated as `urls: [url]`.
1974
2154
  */
1975
2155
  url?: string;
@@ -2054,9 +2234,9 @@ declare function DefaultLocalizerProvider({ urls, url, onMissingKey, prefixes, c
2054
2234
  * dismissed the dialog (clicked the secondary button on a confirmation,
2055
2235
  * pressed Escape, clicked the backdrop, or otherwise rejected the action).
2056
2236
  *
2057
- * Mirrors Blazor's `DialogResult` shape — kept as an object rather than a
2058
- * naked boolean so we can extend with `data?` etc. when custom-content
2059
- * dialogs land without a wire-breaking change.
2237
+ * Kept as an object rather than a naked boolean so we can extend with
2238
+ * `data?` etc. when custom-content dialogs land without a wire-breaking
2239
+ * change.
2060
2240
  */
2061
2241
  interface DialogResult {
2062
2242
  /** True when the user dismissed/cancelled, false when they confirmed. */
@@ -2077,19 +2257,17 @@ interface ConfirmationDialogOptions extends DialogOptions {
2077
2257
  secondaryText?: string;
2078
2258
  }
2079
2259
  /**
2080
- * Headless dialog service contract equivalent to Blazor's `IDialogService`.
2081
- * The framework calls these methods to surface success/warning/error/info
2082
- * messages and confirmation prompts; consumers provide the concrete
2083
- * implementation via {@link DialogServiceProvider} (typically the Fluent UI
2084
- * implementation that ships with `@powerportalspro/react-fluent`, but any
2085
- * dialog UI works as long as it satisfies this contract).
2260
+ * Headless dialog service contract. The framework calls these methods to
2261
+ * surface success / warning / error / info messages and confirmation
2262
+ * prompts; consumers provide the concrete implementation via
2263
+ * {@link DialogServiceProvider} (typically the Fluent UI implementation
2264
+ * that ships with `@powerportalspro/react-fluent`, but any dialog UI
2265
+ * works as long as it satisfies this contract).
2086
2266
  *
2087
- * **Message format.** The `message` argument is treated as HTML markup —
2088
- * matching Blazor's `Microsoft.FluentUI.AspNetCore.Components.IDialogService`,
2089
- * which renders messages via `MarkupString`. Tags like `<br/>`, `<strong>`,
2090
- * `<em>` work; framework localization strings (e.g.
2091
- * `app.messages.unsaved-changes`) rely on this. Don't pass unsanitized user
2092
- * input — same constraint as Blazor.
2267
+ * **Message format.** The `message` argument is treated as trusted HTML
2268
+ * markup — the framework's bundled localization strings (e.g.
2269
+ * `app.messages.unsaved-changes`) rely on this for inline `<br/>`,
2270
+ * `<strong>`, `<em>` tags. Don't pass unsanitized user input.
2093
2271
  *
2094
2272
  * Each method returns a {@link DialogResult} so the same shape covers both
2095
2273
  * single-button (always `{ cancelled: false }` once dismissed) and
@@ -2119,18 +2297,18 @@ declare function DialogServiceProvider({ value, children, }: {
2119
2297
  children: ReactNode;
2120
2298
  }): react_jsx_runtime.JSX.Element;
2121
2299
  /**
2122
- * Prompts the user about unsaved changes. Returns `true` if the user wants to
2123
- * continue (discarding their edits), `false` if they want to cancel and keep
2124
- * editing. Mirrors Blazor's `IPowerPortalsProDialogService.WarnAboutUnsavedChangesAsync`.
2300
+ * Prompts the user about unsaved changes. Returns `true` if the user wants
2301
+ * to continue (discarding their edits), `false` if they want to cancel and
2302
+ * keep editing.
2125
2303
  *
2126
- * Used by the framework before navigation, refresh, and any other operation
2127
- * that would discard pending edits. Application code can also call it directly
2128
- * for custom flows.
2304
+ * Used by the framework before navigation, refresh, and any other
2305
+ * operation that would discard pending edits. Application code can also
2306
+ * call it directly for custom flows.
2129
2307
  *
2130
2308
  * Strings come from the supplied {@link Localizer} (default English from
2131
- * the framework's bundled JSON). Keys: `app.messages.unsaved-changes` (body),
2132
- * `app.messages.unsaved-changes-title` (title), `app.buttons.yes.label` /
2133
- * `app.buttons.no.label` (button labels).
2309
+ * the framework's bundled JSON). Keys: `app.messages.unsaved-changes`
2310
+ * (body), `app.messages.unsaved-changes-title` (title),
2311
+ * `app.buttons.yes.label` / `app.buttons.no.label` (button labels).
2134
2312
  */
2135
2313
  declare function warnAboutUnsavedChangesAsync(dialog: DialogService, localizer: Localizer): Promise<boolean>;
2136
2314
  /**
@@ -2162,11 +2340,10 @@ interface OverlayInstance extends OverlayOptions {
2162
2340
  readonly id: string;
2163
2341
  }
2164
2342
  /**
2165
- * Headless overlay service contract equivalent to Blazor's
2166
- * `IOverlayService`. Maintains a per-target stack of overlay instances; the
2167
- * topmost instance for a target is what renders. Concrete implementations
2168
- * (e.g. `<FluentOverlayProvider>` from `@powerportalspro/react-fluent`)
2169
- * provide the actual UI.
2343
+ * Headless overlay service contract. Maintains a per-target stack of
2344
+ * overlay instances; the topmost instance for a target is what renders.
2345
+ * Concrete implementations (e.g. `<FluentOverlayProvider>` from
2346
+ * `@powerportalspro/react-fluent`) provide the actual UI.
2170
2347
  *
2171
2348
  * Reads happen via the {@link useOverlayForTarget} hook — that's how
2172
2349
  * components subscribe to "what overlay should I currently render". The
@@ -2217,9 +2394,7 @@ declare function useOverlayServiceState(): OverlayContextValue;
2217
2394
  /**
2218
2395
  * Run an async operation while showing an overlay. Hides on success or
2219
2396
  * failure (the overlay is dismissed in `finally`, so failures don't leave a
2220
- * stuck spinner). Mirrors the try/finally pattern Blazor's
2221
- * `MainContext.SaveAsync` uses around `_overlayService.ShowOverlayAsync` /
2222
- * `HideOverlayAsync`.
2397
+ * stuck spinner).
2223
2398
  */
2224
2399
  declare function runWithOverlayAsync<T>(service: OverlayService | null, options: OverlayOptions, operation: () => Promise<T>): Promise<T>;
2225
2400
  /**
@@ -2259,12 +2434,12 @@ declare function useUnsavedChangesWarning(): void;
2259
2434
  * guard needs:
2260
2435
  *
2261
2436
  * - `isAnyDirty` — reactive boolean tracking whether any registered
2262
- * top-most {@link MainContext} / {@link RecordContext} on the page is
2263
- * dirty (read-through to {@link useIsAnyDirty}).
2437
+ * top-most {@link MainContext} / {@link RecordContext} on the page is
2438
+ * dirty (read-through to {@link useIsAnyDirty}).
2264
2439
  * - `warn` — async function that opens the framework confirmation dialog
2265
- * and resolves `true` if the user confirmed discarding the edits,
2266
- * `false` if they cancelled (read-through to
2267
- * {@link useWarnAboutUnsavedChanges}).
2440
+ * and resolves `true` if the user confirmed discarding the edits,
2441
+ * `false` if they cancelled (read-through to
2442
+ * {@link useWarnAboutUnsavedChanges}).
2268
2443
  *
2269
2444
  * Router-agnostic — this package has zero router dependencies. Pair it
2270
2445
  * with whichever router blocker your app uses:
@@ -2276,24 +2451,24 @@ declare function useUnsavedChangesWarning(): void;
2276
2451
  * ```tsx
2277
2452
  * import { useBlocker } from 'react-router-dom';
2278
2453
  * function MyGuard() {
2279
- * const guard = useUnsavedChangesGuard();
2280
- * const blocker = useBlocker(
2281
- * ({ currentLocation, nextLocation }) =>
2282
- * guard.isAnyDirty &&
2283
- * currentLocation.pathname !== nextLocation.pathname,
2284
- * );
2285
- * useEffect(() => {
2286
- * if (blocker.state !== 'blocked') return;
2287
- * let cancelled = false;
2288
- * (async () => {
2289
- * const proceed = await guard.warn();
2290
- * if (cancelled) return;
2291
- * if (proceed) blocker.proceed();
2292
- * else blocker.reset();
2293
- * })();
2294
- * return () => { cancelled = true; };
2295
- * }, [blocker, guard]);
2296
- * return null;
2454
+ * const guard = useUnsavedChangesGuard();
2455
+ * const blocker = useBlocker(
2456
+ * ({ currentLocation, nextLocation }) =>
2457
+ * guard.isAnyDirty &&
2458
+ * currentLocation.pathname !== nextLocation.pathname,
2459
+ * );
2460
+ * useEffect(() => {
2461
+ * if (blocker.state !== 'blocked') return;
2462
+ * let cancelled = false;
2463
+ * (async () => {
2464
+ * const proceed = await guard.warn();
2465
+ * if (cancelled) return;
2466
+ * if (proceed) blocker.proceed();
2467
+ * else blocker.reset();
2468
+ * })();
2469
+ * return () => { cancelled = true; };
2470
+ * }, [blocker, guard]);
2471
+ * return null;
2297
2472
  * }
2298
2473
  * ```
2299
2474
  *
@@ -2304,14 +2479,14 @@ declare function useUnsavedChangesWarning(): void;
2304
2479
  * ```tsx
2305
2480
  * import { useBlocker } from '@tanstack/react-router';
2306
2481
  * function MyGuard() {
2307
- * const guard = useUnsavedChangesGuard();
2308
- * useBlocker({
2309
- * blockerFn: async () => {
2310
- * if (!guard.isAnyDirty) return true; // allow
2311
- * return await guard.warn(); // user choice
2312
- * },
2313
- * });
2314
- * return null;
2482
+ * const guard = useUnsavedChangesGuard();
2483
+ * useBlocker({
2484
+ * blockerFn: async () => {
2485
+ * if (!guard.isAnyDirty) return true; // allow
2486
+ * return await guard.warn(); // user choice
2487
+ * },
2488
+ * });
2489
+ * return null;
2315
2490
  * }
2316
2491
  * ```
2317
2492
  *
@@ -2375,11 +2550,11 @@ interface UnsavedChangesReporter {
2375
2550
  * {@link PowerPortalsProProvider}; consumed by:
2376
2551
  *
2377
2552
  * - The auto-registration effect inside topmost
2378
- * {@link MainContext} / {@link RecordContext}, which adds a reporter on
2379
- * mount + removes it on unmount.
2553
+ * {@link MainContext} / {@link RecordContext}, which adds a reporter on
2554
+ * mount + removes it on unmount.
2380
2555
  * - `<RouterUnsavedChangesGuard>` (in `@powerportalspro/react-fluent`),
2381
- * which subscribes via {@link useIsAnyDirty} and gates `useBlocker` on
2382
- * the aggregate.
2556
+ * which subscribes via {@link useIsAnyDirty} and gates `useBlocker` on
2557
+ * the aggregate.
2383
2558
  *
2384
2559
  * Router-agnostic by design — the registry knows nothing about
2385
2560
  * react-router, history, or navigation events. That coupling lives in
@@ -2521,4 +2696,4 @@ declare function ValidationProvider({ children }: {
2521
2696
 
2522
2697
  declare const VERSION = "0.1.0";
2523
2698
 
2524
- export { ASP_NET_CULTURE_COOKIE, type AuthState, AuthStatus, AuthorizedView, type AuthorizedViewProps, type ColumnValidator, type ConfirmationDialogOptions, type CreateLocalizerOptions, DEFAULT_URL_LOCALE_PATTERN, DefaultLocalizerProvider, type DefaultLocalizerProviderProps, type DialogOptions, type DialogResult, type DialogService, DialogServiceProvider, type EnvironmentFileSettingsCache, InMemoryEnvironmentFileSettingsCache, InMemoryTableMetadataCache, InMemoryViewMetadataCache, InMemoryViewsForTableCache, LocaleProvider, type LocaleProviderProps, type LocaleState, type LocalizationPrefetcher, type Localizer, LocalizerProvider, LookupRecordContext, type LookupRecordContextProps, MainContext, type MainContextDescendant, type MainContextProps, MainContextProvider, type MainContextValue, type OverlayInstance, type OverlayOptions, type OverlayService, OverlayServiceProvider, PowerPortalsProContext, type PowerPortalsProContextValue, PowerPortalsProProvider, type PowerPortalsProProviderProps, type QueryResult, QueryStatus, RecordContext, type RecordContextProps, type RecordContextValue, type TableMetadataCache, type UnsavedChangesGuardController, type UnsavedChangesRegistry, UnsavedChangesRegistryProvider, type UnsavedChangesRegistryProviderProps, type UnsavedChangesReporter, type UseAuthResult, type UseEnvironmentFileSettingsOptions, type UseFetchRecordsOptions, type UseGridDataOptions, type UseRecordOptions, type UseSupportedLocalesOptions, type UseTableMetadataOptions, type UseTablePermissionsOptions, type UseViewDataSourceOptions, type UseViewMetadataOptions, type UseViewsForTableOptions, VERSION, type ValidationContextValue, ValidationProvider, type ViewDataSource, type ViewDataSourceHighlight, type ViewMetadataCache, type ViewsForTableCache, createLocalizer, detectLocale, flattenStrings, getAspNetCultureCookie, getLocaleFromUrlPath, interpolate, resolveLocalizationUrl, runWithOverlayAsync, setAspNetCultureCookie, useAuth, useColumnMetadata, useColumnValidationErrors, useDialogService, useEnvironmentFileSettings, useFetchRecords, useGridData, useIsAnyDirty, useLocale, useLocalization, useLocalizationLoaded, useLocalizationPrefetcher, useLocalizer, useMainContext, useOverlayForTarget, useOverlayService, useOverlayServiceState, usePowerPortalsPro, usePrefixedT, useQueryParam, useRecord, useRecordContext, useRecordContextOptional, useRunWithOverlay, useSupportedLocales, useT, useTableMetadata, useTablePermissions, useUnsavedChangesGuard, useUnsavedChangesRegistry, useUnsavedChangesWarning, useValidationContext, useViewDataSource, useViewMetadata, useViewsForTable, useWarnAboutUnsavedChanges, warnAboutUnsavedChangesAsync };
2699
+ export { ASP_NET_CULTURE_COOKIE, type AuthState, AuthStatus, AuthorizedView, type AuthorizedViewProps, type ColumnValidator, type ConfirmationDialogOptions, type CreateLocalizerOptions, DEFAULT_URL_LOCALE_PATTERN, DefaultLocalizerProvider, type DefaultLocalizerProviderProps, type DialogOptions, type DialogResult, type DialogService, DialogServiceProvider, InMemoryOrganizationSettingsCache, InMemoryTableMetadataCache, InMemoryViewMetadataCache, InMemoryViewsForTableCache, LocaleProvider, type LocaleProviderProps, type LocaleState, type LocalizationPrefetcher, type Localizer, LocalizerProvider, LookupRecordContext, type LookupRecordContextProps, MainContext, type MainContextDescendant, type MainContextProps, MainContextProvider, type MainContextValue, MaskMode, type MaskOptions, type NumericOptions, type OrganizationSettingsCache, type OverlayInstance, type OverlayOptions, type OverlayService, OverlayServiceProvider, PowerPortalsProContext, type PowerPortalsProContextValue, PowerPortalsProProvider, type PowerPortalsProProviderProps, type QueryResult, QueryStatus, RecordContext, type RecordContextProps, type RecordContextValue, type ReformatResult, type TableMetadataCache, type UnsavedChangesGuardController, type UnsavedChangesRegistry, UnsavedChangesRegistryProvider, type UnsavedChangesRegistryProviderProps, type UnsavedChangesReporter, type UrlCultureStrategyName, type UseAuthResult, type UseFetchRecordsOptions, type UseGridDataOptions, type UseMaskedTextFieldOptions, type UseMaskedTextFieldReturn, type UseOrganizationSettingsOptions, type UseRecordOptions, type UseSupportedLocalesOptions, type UseTableMetadataOptions, type UseTablePermissionsOptions, type UseViewDataSourceOptions, type UseViewMetadataOptions, type UseViewsForTableOptions, VERSION, type ValidationContextValue, ValidationProvider, type ViewDataSource, type ViewDataSourceHighlight, type ViewMetadataCache, type ViewsForTableCache, buildLocalePath, createLocalizer, detectLocale, flattenStrings, getAspNetCultureCookie, getLocaleFromUrlPath, interpolate, reformat, resolveLocalizationUrl, runWithOverlayAsync, setAspNetCultureCookie, shouldPrefixLocale, stripLocalePrefix, useAuth, useColumnMetadata, useColumnValidationErrors, useDefaultLocale, useDialogService, useFetchRecords, useGridData, useIsAnyDirty, useLocale, useLocalization, useLocalizationLoaded, useLocalizationPrefetcher, useLocalizer, useMainContext, useMaskedTextField, useOrganizationSettings, useOverlayForTarget, useOverlayService, useOverlayServiceState, usePowerPortalsPro, usePrefixedT, useQueryParam, useRecord, useRecordContext, useRecordContextOptional, useRunWithOverlay, useSupportedLocales, useT, useTableMetadata, useTablePermissions, useUnsavedChangesGuard, useUnsavedChangesRegistry, useUnsavedChangesWarning, useUrlCultureStrategy, useValidationContext, useViewDataSource, useViewMetadata, useViewsForTable, useWarnAboutUnsavedChanges, warnAboutUnsavedChangesAsync };