@praxisui/core 10.0.0-rc.26 → 10.0.0-rc.27
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/README.md +21 -2
- package/ai/component-registry.json +2 -2
- package/fesm2022/praxisui-core.mjs +453 -30
- package/package.json +1 -1
- package/types/praxisui-core-schema.d.ts +4 -0
- package/types/praxisui-core.d.ts +32 -2
package/README.md
CHANGED
|
@@ -57,9 +57,10 @@ const preview = await firstValueFrom(bulkClient.readProposalResults(contract, pr
|
|
|
57
57
|
await session.confirm(idempotencyKeyForThisIntent);
|
|
58
58
|
// On an uncertain HTTP failure, retain this session and retry with the same key.
|
|
59
59
|
// The host schedules further authorized readbacks for an accepted nonterminal execution.
|
|
60
|
-
|
|
60
|
+
const reads = new AbortController(); // This view owns GET lifetime, not confirmation custody.
|
|
61
|
+
if (!session.isTerminal()) await session.refreshExecution(reads.signal);
|
|
61
62
|
if (session.isTerminal()) {
|
|
62
|
-
const results = await session.readResults();
|
|
63
|
+
const results = await session.readResults(200, undefined, reads.signal);
|
|
63
64
|
}
|
|
64
65
|
```
|
|
65
66
|
|
|
@@ -69,6 +70,24 @@ the descriptor. The caller controls when to read status again and presents each
|
|
|
69
70
|
authorized page of results. This example is a service integration sketch, not a
|
|
70
71
|
ready visual workflow or a deployment claim.
|
|
71
72
|
|
|
73
|
+
`refreshExecution(signal?)` and `readResults(size?, after?, signal?)` retain their
|
|
74
|
+
Promise API and optionally accept a standard `AbortSignal`. An already aborted
|
|
75
|
+
signal dispatches no GET; abort unsubscribes the actual HTTP request, releases the
|
|
76
|
+
read slot, and prevents a late Promise continuation from updating the session.
|
|
77
|
+
Abort listeners are removed on success, failure and cancellation. A cancelled GET
|
|
78
|
+
keeps the original proposal, confirmation key and accepted execution; it does not
|
|
79
|
+
mark that execution terminal or prove rollback. A new authorized read may observe
|
|
80
|
+
the same execution. Overlapping execution refreshes remain rejected, while an
|
|
81
|
+
aborted generation cannot release the slot of its successor.
|
|
82
|
+
|
|
83
|
+
The caller owns GET cancellation and its observation budget; the Core session does
|
|
84
|
+
not invent a global read timeout. Use a fresh read lifetime for a fresh consultation
|
|
85
|
+
and abort reads when the owning view closes. Do not race a Promise with a timer:
|
|
86
|
+
connect the signal to the read itself so the HTTP subscription is torn down.
|
|
87
|
+
`evaluate` and `confirm` do not accept this signal: a dispatched POST and its original
|
|
88
|
+
key stay in intent custody across view teardown. GET failure/cancellation must never
|
|
89
|
+
start a new evaluation or confirmation automatically.
|
|
90
|
+
|
|
72
91
|
The backend is the authority on proposal expiry. A client clock hint must not
|
|
73
92
|
block a same-key replay after an uncertain confirmation; the backend may return
|
|
74
93
|
409 or 410 and the host must explain that state without inventing a new intent.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-15T22:42:56.841Z",
|
|
4
4
|
"packageName": "@praxisui/core",
|
|
5
|
-
"packageVersion": "10.0.0-rc.
|
|
5
|
+
"packageVersion": "10.0.0-rc.27",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 4,
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { Component, InjectionToken, Injectable, inject, Inject, Optional, SkipSelf, makeEnvironmentProviders, signal, computed, APP_INITIALIZER, DestroyRef, ENVIRONMENT_INITIALIZER, ErrorHandler, isSignal, Input, Directive, input, booleanAttribute, ChangeDetectionStrategy, EventEmitter, Output, SecurityContext, ViewContainerRef, Injector, effect, SimpleChange, ElementRef, ChangeDetectorRef, afterEveryRender, contentChild, viewChild, HostBinding, output, afterNextRender, ViewChild, ViewEncapsulation, createEnvironmentInjector, EnvironmentInjector, reflectComponentType, HostListener, ViewChildren, TemplateRef, Renderer2 } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common/http';
|
|
4
4
|
import { HttpHeaders, HttpClient, HttpParams, HttpResponse, HttpContextToken, HttpErrorResponse, HTTP_INTERCEPTORS, withInterceptors } from '@angular/common/http';
|
|
5
|
-
import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, finalize as finalize$1, shareReplay as shareReplay$1, map as map$1, timer, switchMap as switchMap$1, catchError as catchError$1, forkJoin, isObservable, combineLatest, merge, Subscription, defaultIfEmpty, timeout as timeout$1 } from 'rxjs';
|
|
5
|
+
import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, finalize as finalize$1, shareReplay as shareReplay$1, map as map$1, Observable, timer, switchMap as switchMap$1, catchError as catchError$1, forkJoin, isObservable, combineLatest, merge, Subscription, defaultIfEmpty, timeout as timeout$1 } from 'rxjs';
|
|
6
6
|
import { switchMap, map, take, catchError, concatMap, tap, shareReplay, takeUntil, toArray, finalize, exhaustMap, filter, timeout } from 'rxjs/operators';
|
|
7
7
|
import * as i1$3 from '@angular/common';
|
|
8
8
|
import { Location, DOCUMENT, NgTemplateOutlet, CommonModule, NgComponentOutlet } from '@angular/common';
|
|
@@ -15529,6 +15529,9 @@ class BulkSyncSession {
|
|
|
15529
15529
|
}, ...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
15530
15530
|
confirmationKey = null;
|
|
15531
15531
|
refreshInFlight = false;
|
|
15532
|
+
refreshGeneration = 0;
|
|
15533
|
+
refreshSignal;
|
|
15534
|
+
resultsGeneration = 0;
|
|
15532
15535
|
ownedContract;
|
|
15533
15536
|
options;
|
|
15534
15537
|
/** Return detached wire data while retaining the original technical error for UX mapping. */
|
|
@@ -15607,17 +15610,25 @@ class BulkSyncSession {
|
|
|
15607
15610
|
throw error;
|
|
15608
15611
|
}
|
|
15609
15612
|
}
|
|
15610
|
-
|
|
15613
|
+
/** One cancellable consultation; the caller owns its deadline. POST custody is unchanged. */
|
|
15614
|
+
async refreshExecution(signal) {
|
|
15615
|
+
this.requireReadLifetime(signal);
|
|
15611
15616
|
const current = this.current();
|
|
15612
15617
|
if (!current.execution)
|
|
15613
15618
|
throw new Error('No accepted execution is known to this session.');
|
|
15614
15619
|
if (isBulkExecutionTerminal(current.execution.status))
|
|
15615
15620
|
return structuredClone(current.execution);
|
|
15616
|
-
if (this.refreshInFlight)
|
|
15621
|
+
if (this.refreshInFlight && !this.refreshSignal?.aborted) {
|
|
15617
15622
|
throw new Error('An execution readback is already in flight.');
|
|
15623
|
+
}
|
|
15624
|
+
const generation = ++this.refreshGeneration;
|
|
15618
15625
|
this.refreshInFlight = true;
|
|
15626
|
+
this.refreshSignal = signal;
|
|
15619
15627
|
try {
|
|
15620
|
-
const execution = await
|
|
15628
|
+
const execution = await this.readValue(() => this.client.readExecution(this.ownedContract, current.execution.executionId, this.options), signal);
|
|
15629
|
+
this.requireReadLifetime(signal);
|
|
15630
|
+
if (generation !== this.refreshGeneration)
|
|
15631
|
+
throw this.readAborted();
|
|
15621
15632
|
if (execution.proposalId !== current.execution.proposalId) {
|
|
15622
15633
|
throw new Error('Execution readback changed the proposal identity.');
|
|
15623
15634
|
}
|
|
@@ -15626,25 +15637,63 @@ class BulkSyncSession {
|
|
|
15626
15637
|
return structuredClone(execution);
|
|
15627
15638
|
}
|
|
15628
15639
|
catch (error) {
|
|
15629
|
-
this.
|
|
15640
|
+
if (generation === this.refreshGeneration && !signal?.aborted)
|
|
15641
|
+
this.fail(error);
|
|
15630
15642
|
throw error;
|
|
15631
15643
|
}
|
|
15632
15644
|
finally {
|
|
15633
|
-
this.
|
|
15645
|
+
if (generation === this.refreshGeneration) {
|
|
15646
|
+
this.refreshInFlight = false;
|
|
15647
|
+
this.refreshSignal = undefined;
|
|
15648
|
+
}
|
|
15634
15649
|
}
|
|
15635
15650
|
}
|
|
15636
|
-
|
|
15651
|
+
/** Cancels only this results GET; an abort never discards the accepted execution. */
|
|
15652
|
+
async readResults(size = 200, after, signal) {
|
|
15653
|
+
this.requireReadLifetime(signal);
|
|
15654
|
+
const generation = ++this.resultsGeneration;
|
|
15637
15655
|
const execution = this.current().execution;
|
|
15638
15656
|
if (!execution)
|
|
15639
15657
|
throw new Error('Results require an accepted execution.');
|
|
15640
15658
|
try {
|
|
15641
|
-
return await
|
|
15659
|
+
return await this.readValue(() => this.client.readResults(this.ownedContract, execution.executionId, size, after, this.options), signal);
|
|
15642
15660
|
}
|
|
15643
15661
|
catch (error) {
|
|
15644
|
-
|
|
15662
|
+
if (generation === this.resultsGeneration && !signal?.aborted) {
|
|
15663
|
+
this.current.set({ ...this.current(), error });
|
|
15664
|
+
}
|
|
15645
15665
|
throw error;
|
|
15646
15666
|
}
|
|
15647
15667
|
}
|
|
15668
|
+
readAborted() {
|
|
15669
|
+
return new DOMException('Bulk observation was cancelled.', 'AbortError');
|
|
15670
|
+
}
|
|
15671
|
+
requireReadLifetime(signal) {
|
|
15672
|
+
if (signal?.aborted)
|
|
15673
|
+
throw this.readAborted();
|
|
15674
|
+
}
|
|
15675
|
+
/** Cancellation owns the HTTP subscription, not just its Promise observer. */
|
|
15676
|
+
async readValue(read, signal) {
|
|
15677
|
+
this.requireReadLifetime(signal);
|
|
15678
|
+
const value = await firstValueFrom(new Observable(subscriber => {
|
|
15679
|
+
if (signal?.aborted) {
|
|
15680
|
+
subscriber.error(this.readAborted());
|
|
15681
|
+
return;
|
|
15682
|
+
}
|
|
15683
|
+
const abort = () => subscriber.error(this.readAborted());
|
|
15684
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
15685
|
+
// defer converts a synchronous client failure into an Observable failure,
|
|
15686
|
+
// so the listener is removed by the same teardown on every path.
|
|
15687
|
+
const subscription = defer(read).subscribe(subscriber);
|
|
15688
|
+
return () => {
|
|
15689
|
+
subscription.unsubscribe();
|
|
15690
|
+
signal?.removeEventListener('abort', abort);
|
|
15691
|
+
};
|
|
15692
|
+
}));
|
|
15693
|
+
// Abort may occur after HTTP next but before this Promise continuation.
|
|
15694
|
+
this.requireReadLifetime(signal);
|
|
15695
|
+
return value;
|
|
15696
|
+
}
|
|
15648
15697
|
fail(error) {
|
|
15649
15698
|
this.current.set({ ...this.current(), phase: 'error', error });
|
|
15650
15699
|
}
|
|
@@ -27004,15 +27053,30 @@ function removeValueAtPath(target, path) {
|
|
|
27004
27053
|
if (!segments.length) {
|
|
27005
27054
|
return;
|
|
27006
27055
|
}
|
|
27056
|
+
const parents = [];
|
|
27007
27057
|
let current = target;
|
|
27008
27058
|
for (const segment of segments.slice(0, -1)) {
|
|
27009
27059
|
const next = current[segment];
|
|
27010
27060
|
if (!next || typeof next !== 'object' || Array.isArray(next)) {
|
|
27011
27061
|
return;
|
|
27012
27062
|
}
|
|
27063
|
+
parents.push({ parent: current, key: segment });
|
|
27013
27064
|
current = next;
|
|
27014
27065
|
}
|
|
27015
27066
|
delete current[segments[segments.length - 1]];
|
|
27067
|
+
for (let i = parents.length - 1; i >= 0; i -= 1) {
|
|
27068
|
+
const { parent, key } = parents[i];
|
|
27069
|
+
const child = parent[key];
|
|
27070
|
+
if (child &&
|
|
27071
|
+
typeof child === 'object' &&
|
|
27072
|
+
!Array.isArray(child) &&
|
|
27073
|
+
Object.keys(child).length === 0) {
|
|
27074
|
+
delete parent[key];
|
|
27075
|
+
}
|
|
27076
|
+
else {
|
|
27077
|
+
break;
|
|
27078
|
+
}
|
|
27079
|
+
}
|
|
27016
27080
|
}
|
|
27017
27081
|
function normalizeEndpoint(endpoint) {
|
|
27018
27082
|
if (endpoint.kind === 'component-port') {
|
|
@@ -39273,7 +39337,10 @@ class WidgetPageCompositionFactory {
|
|
|
39273
39337
|
}
|
|
39274
39338
|
const isStructured = typeof state === 'object'
|
|
39275
39339
|
&& !Array.isArray(state)
|
|
39276
|
-
&& ('values' in state ||
|
|
39340
|
+
&& ('values' in state ||
|
|
39341
|
+
'schema' in state ||
|
|
39342
|
+
'derived' in state ||
|
|
39343
|
+
'transientValues' in state);
|
|
39277
39344
|
if (isStructured) {
|
|
39278
39345
|
const structured = state;
|
|
39279
39346
|
return {
|
|
@@ -39632,7 +39699,10 @@ class WidgetPageStateRuntimeService {
|
|
|
39632
39699
|
return { values: {} };
|
|
39633
39700
|
const isStructured = typeof state === 'object'
|
|
39634
39701
|
&& !Array.isArray(state)
|
|
39635
|
-
&& ('values' in state ||
|
|
39702
|
+
&& ('values' in state ||
|
|
39703
|
+
'schema' in state ||
|
|
39704
|
+
'derived' in state ||
|
|
39705
|
+
'transientValues' in state);
|
|
39636
39706
|
if (isStructured) {
|
|
39637
39707
|
const structured = state;
|
|
39638
39708
|
const normalized = {
|
|
@@ -46069,6 +46139,9 @@ class DynamicWidgetPageComponent {
|
|
|
46069
46139
|
injector = inject(Injector);
|
|
46070
46140
|
settingsSessions = new Map();
|
|
46071
46141
|
widgetSettingsSession;
|
|
46142
|
+
activeSettingsPanelSession;
|
|
46143
|
+
activeSettingsFocusReturnTarget = null;
|
|
46144
|
+
pendingContextNavigationFocusTarget;
|
|
46072
46145
|
settingsIdentityRevision = 0;
|
|
46073
46146
|
acceptedPageIdentity;
|
|
46074
46147
|
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
@@ -46237,13 +46310,15 @@ class DynamicWidgetPageComponent {
|
|
|
46237
46310
|
// Controlled hosts return pageChange through [page]. That acknowledgement
|
|
46238
46311
|
// must not dispose the open settings provider or rebuild its size template.
|
|
46239
46312
|
// Any other input change or different document still invalidates the editor.
|
|
46240
|
-
if (this.widgetSettingsKey &&
|
|
46241
|
-
&&
|
|
46242
|
-
|
|
46313
|
+
if (this.widgetSettingsKey &&
|
|
46314
|
+
changes['page'] &&
|
|
46315
|
+
Object.keys(changes).length === 1 &&
|
|
46316
|
+
this.isAcknowledgedCanonicalPage(incomingPage)) {
|
|
46243
46317
|
return;
|
|
46244
|
-
|
|
46245
|
-
|
|
46246
|
-
|
|
46318
|
+
}
|
|
46319
|
+
const acknowledgedPage = changes['page'] &&
|
|
46320
|
+
Object.keys(changes).length === 1 &&
|
|
46321
|
+
this.isAcknowledgedCanonicalPage(incomingPage);
|
|
46247
46322
|
if (!acknowledgedPage && (changes['page'] || changes['context'] || changes['pageIdentity']
|
|
46248
46323
|
|| changes['componentInstanceId'] || changes['enableCustomization'] || changes['authoringCapabilities'])) {
|
|
46249
46324
|
this.settingsIdentityRevision++;
|
|
@@ -46270,6 +46345,8 @@ class DynamicWidgetPageComponent {
|
|
|
46270
46345
|
if (changes['page'] ||
|
|
46271
46346
|
changes['context'] ||
|
|
46272
46347
|
changes['pageIdentity']) {
|
|
46348
|
+
const previousPageDefinition = this.pageDefinition;
|
|
46349
|
+
const previousPageRuntime = this.pageRuntime;
|
|
46273
46350
|
const parsed = this.parsePage(this.page);
|
|
46274
46351
|
if (changes['page'] || !this.authoredPageDefinition) {
|
|
46275
46352
|
this.authoredPageDefinition = undefined;
|
|
@@ -46280,7 +46357,17 @@ class DynamicWidgetPageComponent {
|
|
|
46280
46357
|
})
|
|
46281
46358
|
: undefined;
|
|
46282
46359
|
}
|
|
46283
|
-
const
|
|
46360
|
+
const shouldPreserveEphemeralState = !this.isHydrating &&
|
|
46361
|
+
!changes['page']?.isFirstChange() &&
|
|
46362
|
+
!changes['pageIdentity'] &&
|
|
46363
|
+
!changes['componentInstanceId'] &&
|
|
46364
|
+
this.samePageContextIdentity(this.pageDefinition, parsed);
|
|
46365
|
+
const runtimeSeedPage = shouldPreserveEphemeralState && parsed
|
|
46366
|
+
? this.preserveEphemeralRuntimeState(parsed)
|
|
46367
|
+
: parsed;
|
|
46368
|
+
const resolvedPage = runtimeSeedPage
|
|
46369
|
+
? this.resolvePagePresets(runtimeSeedPage)
|
|
46370
|
+
: runtimeSeedPage;
|
|
46284
46371
|
this.assertNoLegacyConnections(resolvedPage);
|
|
46285
46372
|
this.widgetDiagnostics = {};
|
|
46286
46373
|
this.widgetDiagnosticsChange.emit({});
|
|
@@ -46300,6 +46387,10 @@ class DynamicWidgetPageComponent {
|
|
|
46300
46387
|
}
|
|
46301
46388
|
this.pageRuntime = this.buildStateRuntime(this.pageDefinition?.state, this.pageDefinition?.context);
|
|
46302
46389
|
this.pageState = this.pageRuntime.state;
|
|
46390
|
+
const resetNonPersistentPaths = this.collectResetNonPersistentStatePaths(previousPageDefinition, previousPageRuntime, this.pageDefinition, this.pageRuntime);
|
|
46391
|
+
if (resetNonPersistentPaths.length > 0) {
|
|
46392
|
+
this.clearMountedSourceSelectionsForNonPersistentState([previousPageDefinition, this.pageDefinition], resetNonPersistentPaths);
|
|
46393
|
+
}
|
|
46303
46394
|
const bootstrapSnapshot = this.bootstrapCompositionAdapter(this.pageDefinition);
|
|
46304
46395
|
const rawWidgets = this.pageDefinition?.widgets || [];
|
|
46305
46396
|
let widgets = this.applyEditShellActions(rawWidgets);
|
|
@@ -48251,6 +48342,10 @@ class DynamicWidgetPageComponent {
|
|
|
48251
48342
|
return session;
|
|
48252
48343
|
}
|
|
48253
48344
|
releaseSettingsSession(session) {
|
|
48345
|
+
if (this.activeSettingsPanelSession === session) {
|
|
48346
|
+
this.activeSettingsPanelSession = undefined;
|
|
48347
|
+
this.activeSettingsFocusReturnTarget = null;
|
|
48348
|
+
}
|
|
48254
48349
|
this.settingsSessions.delete(session);
|
|
48255
48350
|
if (!session.destroyed)
|
|
48256
48351
|
session.destroy();
|
|
@@ -48261,6 +48356,7 @@ class DynamicWidgetPageComponent {
|
|
|
48261
48356
|
this.releaseSettingsSession(session);
|
|
48262
48357
|
}
|
|
48263
48358
|
if (key === undefined) {
|
|
48359
|
+
this.pendingContextNavigationFocusTarget = undefined;
|
|
48264
48360
|
this.configEditorContextRequests.clear();
|
|
48265
48361
|
this.pendingWidgetComponentSettings.set(new Set());
|
|
48266
48362
|
}
|
|
@@ -48295,12 +48391,14 @@ class DynamicWidgetPageComponent {
|
|
|
48295
48391
|
label: this.widgetSettingsLabel(),
|
|
48296
48392
|
icon: 'settings',
|
|
48297
48393
|
disabled: !canWidget,
|
|
48394
|
+
editorGroup: 'widget-shell',
|
|
48298
48395
|
},
|
|
48299
48396
|
{
|
|
48300
48397
|
id: 'size',
|
|
48301
48398
|
label: this.sizeEditorLabel(),
|
|
48302
48399
|
icon: 'straighten',
|
|
48303
48400
|
disabled: !canSize,
|
|
48401
|
+
editorGroup: 'widget-shell',
|
|
48304
48402
|
},
|
|
48305
48403
|
],
|
|
48306
48404
|
activeModeId: activeMode,
|
|
@@ -48344,6 +48442,9 @@ class DynamicWidgetPageComponent {
|
|
|
48344
48442
|
else if (mode === 'size' && !canSize) {
|
|
48345
48443
|
mode = canWidget ? 'widget' : canContent ? 'content' : 'size';
|
|
48346
48444
|
}
|
|
48445
|
+
this.pendingContextNavigationFocusTarget = {
|
|
48446
|
+
target: this.activeSettingsFocusReturnTarget,
|
|
48447
|
+
};
|
|
48347
48448
|
if (mode === 'content') {
|
|
48348
48449
|
void this.openWidgetComponentSettings(targetKey);
|
|
48349
48450
|
}
|
|
@@ -48351,10 +48452,15 @@ class DynamicWidgetPageComponent {
|
|
|
48351
48452
|
this.openWidgetSettingsPanel(targetKey, mode);
|
|
48352
48453
|
}
|
|
48353
48454
|
}
|
|
48455
|
+
resolveSettingsFocusReturnTarget() {
|
|
48456
|
+
const pending = this.pendingContextNavigationFocusTarget;
|
|
48457
|
+
this.pendingContextNavigationFocusTarget = undefined;
|
|
48458
|
+
return pending ? pending.target : this.captureFocusReturnTarget();
|
|
48459
|
+
}
|
|
48354
48460
|
openWidgetSettingsPanel(key, initialTab) {
|
|
48355
48461
|
if (this.destroyed || !this.settingsPanel)
|
|
48356
48462
|
return;
|
|
48357
|
-
const focusReturnTarget = this.
|
|
48463
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48358
48464
|
const page = this.ensurePageDefinition();
|
|
48359
48465
|
const widget = page.widgets.find(item => item.key === key);
|
|
48360
48466
|
if (!widget)
|
|
@@ -48365,6 +48471,10 @@ class DynamicWidgetPageComponent {
|
|
|
48365
48471
|
const session = this.createSettingsSession(key);
|
|
48366
48472
|
const owner = session.get(DestroyRef);
|
|
48367
48473
|
const contextNavigation = this.buildWidgetSettingsContextNavigation(key, initialTab);
|
|
48474
|
+
const previousActiveSession = this.activeSettingsPanelSession;
|
|
48475
|
+
const previousFocusTarget = this.activeSettingsFocusReturnTarget;
|
|
48476
|
+
this.activeSettingsPanelSession = session;
|
|
48477
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48368
48478
|
let ref;
|
|
48369
48479
|
try {
|
|
48370
48480
|
ref = this.settingsPanel.open({
|
|
@@ -48377,7 +48487,10 @@ class DynamicWidgetPageComponent {
|
|
|
48377
48487
|
const page = this.ensurePageDefinition();
|
|
48378
48488
|
const widget = page.widgets.find(item => item.key === key);
|
|
48379
48489
|
const uiShell = this.mergedContext['ui']?.shell || {};
|
|
48490
|
+
this.activeSettingsPanelSession = session;
|
|
48491
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48380
48492
|
this.closeSizeEditor(false);
|
|
48493
|
+
this.selectWidget(key);
|
|
48381
48494
|
this.prepareSizeEditor(key);
|
|
48382
48495
|
this.widgetSettingsKey = key;
|
|
48383
48496
|
this.widgetSettingsSession = session;
|
|
@@ -48407,6 +48520,10 @@ class DynamicWidgetPageComponent {
|
|
|
48407
48520
|
});
|
|
48408
48521
|
}
|
|
48409
48522
|
catch (error) {
|
|
48523
|
+
if (this.activeSettingsPanelSession === session) {
|
|
48524
|
+
this.activeSettingsPanelSession = previousActiveSession;
|
|
48525
|
+
this.activeSettingsFocusReturnTarget = previousFocusTarget;
|
|
48526
|
+
}
|
|
48410
48527
|
if (this.widgetSettingsSession === session)
|
|
48411
48528
|
this.closeSizeEditor(false);
|
|
48412
48529
|
else
|
|
@@ -48416,15 +48533,27 @@ class DynamicWidgetPageComponent {
|
|
|
48416
48533
|
ref.applied$.pipe(takeUntilDestroyed(owner)).subscribe(value => this.commitWidgetSettings(key, value, false));
|
|
48417
48534
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe(value => this.commitWidgetSettings(key, value, true));
|
|
48418
48535
|
ref.closed$?.pipe(take(1), takeUntilDestroyed(owner)).subscribe(() => {
|
|
48419
|
-
const
|
|
48420
|
-
|
|
48536
|
+
const wasActiveWidgetSettings = this.widgetSettingsSession === session;
|
|
48537
|
+
const wasActivePanelSession = this.activeSettingsPanelSession === session;
|
|
48538
|
+
if (wasActivePanelSession &&
|
|
48539
|
+
!wasActiveWidgetSettings &&
|
|
48540
|
+
previousActiveSession &&
|
|
48541
|
+
!previousActiveSession.destroyed) {
|
|
48542
|
+
this.activeSettingsPanelSession = previousActiveSession;
|
|
48543
|
+
this.activeSettingsFocusReturnTarget = previousFocusTarget;
|
|
48544
|
+
this.releaseSettingsSession(session);
|
|
48545
|
+
return;
|
|
48546
|
+
}
|
|
48547
|
+
if (wasActiveWidgetSettings) {
|
|
48421
48548
|
this.widgetSettingsSelectTab = undefined;
|
|
48422
48549
|
this.closeSizeEditor(false);
|
|
48423
48550
|
}
|
|
48424
|
-
else
|
|
48551
|
+
else {
|
|
48425
48552
|
this.releaseSettingsSession(session);
|
|
48426
|
-
|
|
48553
|
+
}
|
|
48554
|
+
if (wasActivePanelSession) {
|
|
48427
48555
|
this.restoreFocus(focusReturnTarget);
|
|
48556
|
+
}
|
|
48428
48557
|
});
|
|
48429
48558
|
}
|
|
48430
48559
|
sizeEditorLayoutItems() {
|
|
@@ -48561,6 +48690,7 @@ class DynamicWidgetPageComponent {
|
|
|
48561
48690
|
} });
|
|
48562
48691
|
}
|
|
48563
48692
|
async openWidgetComponentSettings(key) {
|
|
48693
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48564
48694
|
if (this.destroyed || !this.canOpenWidgetComponentSettings(key))
|
|
48565
48695
|
return;
|
|
48566
48696
|
if (!this.settingsPanel) {
|
|
@@ -48582,7 +48712,6 @@ class DynamicWidgetPageComponent {
|
|
|
48582
48712
|
const materializedInputs = metadata.configEditor?.contextResolver
|
|
48583
48713
|
? this.readMaterializedInputsForWidget(metadata, key)
|
|
48584
48714
|
: undefined;
|
|
48585
|
-
const focusReturnTarget = this.captureFocusReturnTarget();
|
|
48586
48715
|
const session = this.createSettingsSession(key);
|
|
48587
48716
|
const owner = session.get(DestroyRef);
|
|
48588
48717
|
let opened = false;
|
|
@@ -48602,6 +48731,12 @@ class DynamicWidgetPageComponent {
|
|
|
48602
48731
|
const appliedValues = new Subject();
|
|
48603
48732
|
owner.onDestroy(() => appliedValues.complete());
|
|
48604
48733
|
const contextNavigation = this.buildWidgetSettingsContextNavigation(key, 'content');
|
|
48734
|
+
const previousActiveSession = this.activeSettingsPanelSession;
|
|
48735
|
+
const previousFocusTarget = this.activeSettingsFocusReturnTarget;
|
|
48736
|
+
const previousSelectedWidgetKey = this.selectedWidgetKeyState();
|
|
48737
|
+
this.activeSettingsPanelSession = session;
|
|
48738
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48739
|
+
this.selectWidget(key);
|
|
48605
48740
|
const ref = this.settingsPanel.open({
|
|
48606
48741
|
owner,
|
|
48607
48742
|
id: `dynamic-page-component:${key}`,
|
|
@@ -48631,8 +48766,22 @@ class DynamicWidgetPageComponent {
|
|
|
48631
48766
|
});
|
|
48632
48767
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe((result) => this.applyWidgetComponentInputs(key, result, true));
|
|
48633
48768
|
ref.closed$?.pipe(take(1), takeUntilDestroyed(owner)).subscribe(() => {
|
|
48769
|
+
const wasActivePanelSession = this.activeSettingsPanelSession === session;
|
|
48770
|
+
if (wasActivePanelSession &&
|
|
48771
|
+
previousActiveSession &&
|
|
48772
|
+
!previousActiveSession.destroyed) {
|
|
48773
|
+
this.activeSettingsPanelSession = previousActiveSession;
|
|
48774
|
+
this.activeSettingsFocusReturnTarget = previousFocusTarget;
|
|
48775
|
+
if (previousSelectedWidgetKey) {
|
|
48776
|
+
this.selectWidget(previousSelectedWidgetKey);
|
|
48777
|
+
}
|
|
48778
|
+
this.releaseSettingsSession(session);
|
|
48779
|
+
return;
|
|
48780
|
+
}
|
|
48634
48781
|
this.releaseSettingsSession(session);
|
|
48635
|
-
|
|
48782
|
+
if (wasActivePanelSession) {
|
|
48783
|
+
this.restoreFocus(focusReturnTarget);
|
|
48784
|
+
}
|
|
48636
48785
|
});
|
|
48637
48786
|
}
|
|
48638
48787
|
finally {
|
|
@@ -48668,6 +48817,8 @@ class DynamicWidgetPageComponent {
|
|
|
48668
48817
|
restoreFocus(target) {
|
|
48669
48818
|
queueMicrotask(() => {
|
|
48670
48819
|
if (!this.destroyed
|
|
48820
|
+
&& !this.activeSettingsPanelSession
|
|
48821
|
+
&& this.pendingWidgetComponentSettings().size === 0
|
|
48671
48822
|
&& target?.isConnected
|
|
48672
48823
|
&& !target.hasAttribute('disabled')
|
|
48673
48824
|
&& target.getAttribute('aria-disabled') !== 'true') {
|
|
@@ -48977,6 +49128,7 @@ class DynamicWidgetPageComponent {
|
|
|
48977
49128
|
return (endpoint.kind === 'component-port' && endpoint.ref.widget === widgetKey);
|
|
48978
49129
|
}
|
|
48979
49130
|
openPageSettings() {
|
|
49131
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48980
49132
|
if (this.destroyed || !this.canInvokePageSettings())
|
|
48981
49133
|
return;
|
|
48982
49134
|
if (!this.settingsPanel)
|
|
@@ -48991,6 +49143,10 @@ class DynamicWidgetPageComponent {
|
|
|
48991
49143
|
const owner = session.get(DestroyRef);
|
|
48992
49144
|
const appliedValues = new Subject();
|
|
48993
49145
|
owner.onDestroy(() => appliedValues.complete());
|
|
49146
|
+
const previousActiveSession = this.activeSettingsPanelSession;
|
|
49147
|
+
const previousFocusTarget = this.activeSettingsFocusReturnTarget;
|
|
49148
|
+
this.activeSettingsPanelSession = session;
|
|
49149
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48994
49150
|
const ref = this.settingsPanel.open({
|
|
48995
49151
|
owner,
|
|
48996
49152
|
id: 'dynamic-page-settings',
|
|
@@ -49020,7 +49176,21 @@ class DynamicWidgetPageComponent {
|
|
|
49020
49176
|
appliedValues.next(accepted);
|
|
49021
49177
|
});
|
|
49022
49178
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe((result) => this.applyPageLayout(result, true));
|
|
49023
|
-
ref.closed$?.pipe(take(1), takeUntilDestroyed(owner)).subscribe(() =>
|
|
49179
|
+
ref.closed$?.pipe(take(1), takeUntilDestroyed(owner)).subscribe(() => {
|
|
49180
|
+
const wasActivePanelSession = this.activeSettingsPanelSession === session;
|
|
49181
|
+
if (wasActivePanelSession &&
|
|
49182
|
+
previousActiveSession &&
|
|
49183
|
+
!previousActiveSession.destroyed) {
|
|
49184
|
+
this.activeSettingsPanelSession = previousActiveSession;
|
|
49185
|
+
this.activeSettingsFocusReturnTarget = previousFocusTarget;
|
|
49186
|
+
this.releaseSettingsSession(session);
|
|
49187
|
+
return;
|
|
49188
|
+
}
|
|
49189
|
+
this.releaseSettingsSession(session);
|
|
49190
|
+
if (wasActivePanelSession) {
|
|
49191
|
+
this.restoreFocus(focusReturnTarget);
|
|
49192
|
+
}
|
|
49193
|
+
});
|
|
49024
49194
|
}
|
|
49025
49195
|
canInvokePageSettings() {
|
|
49026
49196
|
return this.isAuthoringCapabilityEnabled('pageSettings')
|
|
@@ -49153,11 +49323,227 @@ class DynamicWidgetPageComponent {
|
|
|
49153
49323
|
}, persist);
|
|
49154
49324
|
}
|
|
49155
49325
|
applyAuthoringPageUpdate(next, persist = true, runtime, syncCompositionAdapter = true, skipStateWidgetProjection = false, emitUnchangedCanonicalPage = true) {
|
|
49156
|
-
|
|
49326
|
+
const runtimeSeed = !runtime && !this.isHydrating
|
|
49327
|
+
? (this.preserveEphemeralRuntimeState(next) ?? next)
|
|
49328
|
+
: next;
|
|
49329
|
+
this.applyPageUpdate(runtimeSeed, persist, runtime, syncCompositionAdapter, skipStateWidgetProjection, emitUnchangedCanonicalPage);
|
|
49157
49330
|
if (this.authoredPageDefinition) {
|
|
49158
49331
|
this.pageAuthoringChange.emit(this.clonePageDefinition(this.authoredPageDefinition));
|
|
49159
49332
|
}
|
|
49160
49333
|
}
|
|
49334
|
+
isAcknowledgedCanonicalPage(incomingPage) {
|
|
49335
|
+
if (!incomingPage || !this.authoredPageDefinition) {
|
|
49336
|
+
return false;
|
|
49337
|
+
}
|
|
49338
|
+
if (this.sameJsonStructure(incomingPage, this.authoredPageDefinition)) {
|
|
49339
|
+
return true;
|
|
49340
|
+
}
|
|
49341
|
+
if (this.hasExplicitNonPersistentStateOverride(incomingPage)) {
|
|
49342
|
+
return false;
|
|
49343
|
+
}
|
|
49344
|
+
const normalizedIncoming = this.toPersistedCanonicalPage({
|
|
49345
|
+
...incomingPage,
|
|
49346
|
+
state: this.stateRuntime.normalizeState(incomingPage.state),
|
|
49347
|
+
});
|
|
49348
|
+
return this.sameJsonStructure(normalizedIncoming, this.authoredPageDefinition);
|
|
49349
|
+
}
|
|
49350
|
+
hasExplicitNonPersistentStateOverride(page) {
|
|
49351
|
+
const schema = page?.state?.schema || {};
|
|
49352
|
+
const values = page?.state?.values;
|
|
49353
|
+
if (!values || typeof values !== 'object') {
|
|
49354
|
+
return false;
|
|
49355
|
+
}
|
|
49356
|
+
for (const rawPath of Object.keys(schema)) {
|
|
49357
|
+
const entry = schema[rawPath];
|
|
49358
|
+
if (entry?.persist !== false) {
|
|
49359
|
+
continue;
|
|
49360
|
+
}
|
|
49361
|
+
const path = rawPath.trim();
|
|
49362
|
+
if (!path) {
|
|
49363
|
+
continue;
|
|
49364
|
+
}
|
|
49365
|
+
const value = this.conn.extractByPath(values, path);
|
|
49366
|
+
if (value !== undefined && !this.sameJsonStructure(value, entry.initial)) {
|
|
49367
|
+
return true;
|
|
49368
|
+
}
|
|
49369
|
+
}
|
|
49370
|
+
return false;
|
|
49371
|
+
}
|
|
49372
|
+
collectResetNonPersistentStatePaths(previousPage, previousRuntime, nextPage, nextRuntime) {
|
|
49373
|
+
if (!previousPage || !previousRuntime || !nextRuntime) {
|
|
49374
|
+
return [];
|
|
49375
|
+
}
|
|
49376
|
+
const previousSchema = previousPage.state?.schema ?? {};
|
|
49377
|
+
const nextSchema = nextPage?.state?.schema ?? {};
|
|
49378
|
+
const candidatePaths = new Set([
|
|
49379
|
+
...Object.keys(previousSchema).filter((path) => previousSchema[path]?.persist === false),
|
|
49380
|
+
...Object.keys(nextSchema).filter((path) => nextSchema[path]?.persist === false),
|
|
49381
|
+
]);
|
|
49382
|
+
const resetPaths = [];
|
|
49383
|
+
for (const rawPath of candidatePaths) {
|
|
49384
|
+
const path = rawPath.trim();
|
|
49385
|
+
if (!path) {
|
|
49386
|
+
continue;
|
|
49387
|
+
}
|
|
49388
|
+
const prevEntry = previousSchema[rawPath] ?? nextSchema[rawPath];
|
|
49389
|
+
const nextEntry = nextSchema[rawPath] ?? previousSchema[rawPath];
|
|
49390
|
+
const prevValue = this.conn.extractByPath(previousRuntime.primaryValues, path);
|
|
49391
|
+
const hadLiveSelection = prevValue !== undefined &&
|
|
49392
|
+
!this.sameJsonStructure(prevValue, prevEntry?.initial);
|
|
49393
|
+
if (!hadLiveSelection) {
|
|
49394
|
+
continue;
|
|
49395
|
+
}
|
|
49396
|
+
const nextValue = this.conn.extractByPath(nextRuntime.primaryValues, path);
|
|
49397
|
+
const isNowReset = nextValue === undefined ||
|
|
49398
|
+
this.sameJsonStructure(nextValue, nextEntry?.initial);
|
|
49399
|
+
if (isNowReset) {
|
|
49400
|
+
resetPaths.push(path);
|
|
49401
|
+
}
|
|
49402
|
+
}
|
|
49403
|
+
return [...new Set(resetPaths)];
|
|
49404
|
+
}
|
|
49405
|
+
samePageContextIdentity(left, right) {
|
|
49406
|
+
if (!left || !right) {
|
|
49407
|
+
return false;
|
|
49408
|
+
}
|
|
49409
|
+
const leftPageId = left.context?.['pageId'] ?? null;
|
|
49410
|
+
const rightPageId = right.context?.['pageId'] ?? null;
|
|
49411
|
+
if (leftPageId !== rightPageId) {
|
|
49412
|
+
return false;
|
|
49413
|
+
}
|
|
49414
|
+
if (leftPageId !== null) {
|
|
49415
|
+
return true;
|
|
49416
|
+
}
|
|
49417
|
+
const leftKeys = new Set((left.widgets || []).map((w) => w.key));
|
|
49418
|
+
const rightKeys = (right.widgets || []).map((w) => w.key);
|
|
49419
|
+
if (leftKeys.size === 0 && rightKeys.length === 0) {
|
|
49420
|
+
return true;
|
|
49421
|
+
}
|
|
49422
|
+
return rightKeys.some((key) => leftKeys.has(key));
|
|
49423
|
+
}
|
|
49424
|
+
preserveEphemeralRuntimeState(incomingPage) {
|
|
49425
|
+
if (!incomingPage || !this.pageRuntime || this.isHydrating) {
|
|
49426
|
+
return incomingPage;
|
|
49427
|
+
}
|
|
49428
|
+
const schema = incomingPage.state?.schema || {};
|
|
49429
|
+
const nonPersistentPaths = Object.keys(schema)
|
|
49430
|
+
.filter((path) => schema[path]?.persist === false)
|
|
49431
|
+
.map((path) => path.trim())
|
|
49432
|
+
.filter(Boolean);
|
|
49433
|
+
if (!nonPersistentPaths.length) {
|
|
49434
|
+
return incomingPage;
|
|
49435
|
+
}
|
|
49436
|
+
const incomingWidgets = new Set((incomingPage.widgets || []).map((widget) => widget.key));
|
|
49437
|
+
const incomingLinks = incomingPage.composition?.links || [];
|
|
49438
|
+
const previousLinks = this.pageDefinition?.composition?.links || incomingLinks;
|
|
49439
|
+
let mergedValues = this.cloneStateValues(incomingPage.state?.values || {});
|
|
49440
|
+
let preservedAny = false;
|
|
49441
|
+
for (const path of nonPersistentPaths) {
|
|
49442
|
+
const schemaEntry = schema[path];
|
|
49443
|
+
const incomingValue = this.conn.extractByPath(mergedValues, path);
|
|
49444
|
+
const hasExplicitIncomingOverride = incomingValue !== undefined &&
|
|
49445
|
+
!this.sameJsonStructure(incomingValue, schemaEntry?.initial);
|
|
49446
|
+
if (hasExplicitIncomingOverride) {
|
|
49447
|
+
continue;
|
|
49448
|
+
}
|
|
49449
|
+
const previousWriterWidgets = previousLinks.filter((link) => link.from.kind === 'component-port' &&
|
|
49450
|
+
link.to.kind === 'state' &&
|
|
49451
|
+
this.isSameOrNestedStatePath(link.to.ref.path, path));
|
|
49452
|
+
if (previousWriterWidgets.length > 0) {
|
|
49453
|
+
const hasActiveWriter = incomingLinks.some((link) => link.from.kind === 'component-port' &&
|
|
49454
|
+
link.to.kind === 'state' &&
|
|
49455
|
+
this.isSameOrNestedStatePath(link.to.ref.path, path) &&
|
|
49456
|
+
incomingWidgets.has(link.from.ref.widget));
|
|
49457
|
+
if (!hasActiveWriter) {
|
|
49458
|
+
continue;
|
|
49459
|
+
}
|
|
49460
|
+
}
|
|
49461
|
+
const liveValue = this.conn.extractByPath(this.pageRuntime.primaryValues, path);
|
|
49462
|
+
if (liveValue === undefined) {
|
|
49463
|
+
continue;
|
|
49464
|
+
}
|
|
49465
|
+
mergedValues = this.conn.setValueAtPath(mergedValues, path, this.cloneStateValues(liveValue));
|
|
49466
|
+
preservedAny = true;
|
|
49467
|
+
}
|
|
49468
|
+
if (!preservedAny) {
|
|
49469
|
+
return incomingPage;
|
|
49470
|
+
}
|
|
49471
|
+
return {
|
|
49472
|
+
...incomingPage,
|
|
49473
|
+
state: {
|
|
49474
|
+
...(incomingPage.state || {}),
|
|
49475
|
+
values: mergedValues,
|
|
49476
|
+
},
|
|
49477
|
+
};
|
|
49478
|
+
}
|
|
49479
|
+
isSameOrNestedStatePath(candidate, base) {
|
|
49480
|
+
const normalizedCandidate = String(candidate || '').trim();
|
|
49481
|
+
const normalizedBase = String(base || '').trim();
|
|
49482
|
+
if (!normalizedCandidate || !normalizedBase) {
|
|
49483
|
+
return false;
|
|
49484
|
+
}
|
|
49485
|
+
return (normalizedCandidate === normalizedBase ||
|
|
49486
|
+
normalizedCandidate.startsWith(`${normalizedBase}.`) ||
|
|
49487
|
+
normalizedBase.startsWith(`${normalizedCandidate}.`));
|
|
49488
|
+
}
|
|
49489
|
+
clearMountedSourceSelectionsForNonPersistentState(pages, resetPaths) {
|
|
49490
|
+
if (!this.widgetLoaders) {
|
|
49491
|
+
return;
|
|
49492
|
+
}
|
|
49493
|
+
const matchesResetPath = (path) => !resetPaths || resetPaths.some((resetPath) => this.isSameOrNestedStatePath(path, resetPath));
|
|
49494
|
+
const sourceWidgets = new Set();
|
|
49495
|
+
for (const page of pages) {
|
|
49496
|
+
if (!page) {
|
|
49497
|
+
continue;
|
|
49498
|
+
}
|
|
49499
|
+
const schema = page.state?.schema || {};
|
|
49500
|
+
const nonPersistentPaths = Object.keys(schema)
|
|
49501
|
+
.filter((path) => schema[path]?.persist === false)
|
|
49502
|
+
.map((path) => path.trim())
|
|
49503
|
+
.filter((path) => path && matchesResetPath(path));
|
|
49504
|
+
if (!nonPersistentPaths.length) {
|
|
49505
|
+
continue;
|
|
49506
|
+
}
|
|
49507
|
+
const links = page.composition?.links || [];
|
|
49508
|
+
for (const link of links) {
|
|
49509
|
+
const fromRef = link.from?.ref;
|
|
49510
|
+
const toRef = link.to?.ref;
|
|
49511
|
+
const toPath = typeof toRef?.path === 'string' ? toRef.path : '';
|
|
49512
|
+
if (link.from?.kind === 'component-port' &&
|
|
49513
|
+
link.to?.kind === 'state' &&
|
|
49514
|
+
typeof fromRef?.widget === 'string' &&
|
|
49515
|
+
toPath &&
|
|
49516
|
+
matchesResetPath(toPath) &&
|
|
49517
|
+
nonPersistentPaths.some((path) => this.isSameOrNestedStatePath(toPath, path))) {
|
|
49518
|
+
sourceWidgets.add(fromRef.widget);
|
|
49519
|
+
}
|
|
49520
|
+
}
|
|
49521
|
+
for (const widget of page.widgets || []) {
|
|
49522
|
+
const outputs = widget.definition?.outputs;
|
|
49523
|
+
if (!outputs || typeof outputs !== 'object') {
|
|
49524
|
+
continue;
|
|
49525
|
+
}
|
|
49526
|
+
for (const rawActions of Object.values(outputs)) {
|
|
49527
|
+
const actions = Array.isArray(rawActions) ? rawActions : [rawActions];
|
|
49528
|
+
for (const action of actions) {
|
|
49529
|
+
const candidate = action;
|
|
49530
|
+
if (candidate?.type === 'setState' &&
|
|
49531
|
+
typeof candidate.path === 'string' &&
|
|
49532
|
+
matchesResetPath(candidate.path) &&
|
|
49533
|
+
nonPersistentPaths.some((path) => this.isSameOrNestedStatePath(candidate.path, path))) {
|
|
49534
|
+
sourceWidgets.add(widget.key);
|
|
49535
|
+
}
|
|
49536
|
+
}
|
|
49537
|
+
}
|
|
49538
|
+
}
|
|
49539
|
+
}
|
|
49540
|
+
for (const widgetKey of sourceWidgets) {
|
|
49541
|
+
const loader = this.findWidgetLoader(widgetKey);
|
|
49542
|
+
if (loader && typeof loader.clearSelection === 'function') {
|
|
49543
|
+
loader.clearSelection();
|
|
49544
|
+
}
|
|
49545
|
+
}
|
|
49546
|
+
}
|
|
49161
49547
|
applyPageUpdate(next, persist = true, runtime, syncCompositionAdapter = true, skipStateWidgetProjection = false, emitUnchangedCanonicalPage = true) {
|
|
49162
49548
|
this.assertNoLegacyConnections(next);
|
|
49163
49549
|
if (this.getDataSourcesSignature(next.dataSources) !==
|
|
@@ -49182,7 +49568,7 @@ class DynamicWidgetPageComponent {
|
|
|
49182
49568
|
state: {
|
|
49183
49569
|
primaryValues: this.cloneStateValues(effectiveRuntime.primaryValues),
|
|
49184
49570
|
derivedValues: this.cloneStateValues(effectiveRuntime.derivedValues),
|
|
49185
|
-
transientValues:
|
|
49571
|
+
transientValues: {},
|
|
49186
49572
|
changedPaths: [],
|
|
49187
49573
|
diagnostics: [],
|
|
49188
49574
|
},
|
|
@@ -49266,7 +49652,7 @@ class DynamicWidgetPageComponent {
|
|
|
49266
49652
|
state: {
|
|
49267
49653
|
primaryValues: this.cloneStateValues(nextRuntime.primaryValues),
|
|
49268
49654
|
derivedValues: this.cloneStateValues(nextRuntime.derivedValues),
|
|
49269
|
-
transientValues:
|
|
49655
|
+
transientValues: {},
|
|
49270
49656
|
changedPaths: updates.map((u) => u.targetStatePath),
|
|
49271
49657
|
diagnostics: [],
|
|
49272
49658
|
},
|
|
@@ -52340,6 +52726,7 @@ class DynamicWidgetPageComponent {
|
|
|
52340
52726
|
dataSources: stored.dataSources ?? baseline.dataSources,
|
|
52341
52727
|
}
|
|
52342
52728
|
: (stored ?? defaultPage);
|
|
52729
|
+
this.clearMountedSourceSelectionsForNonPersistentState([loaded]);
|
|
52343
52730
|
this.authoredPageDefinition = this.clonePageDefinition(loaded);
|
|
52344
52731
|
this.applyPageUpdate(loaded, false);
|
|
52345
52732
|
}
|
|
@@ -52414,6 +52801,7 @@ class DynamicWidgetPageComponent {
|
|
|
52414
52801
|
if (mutation.revision === this.persistenceRevision) {
|
|
52415
52802
|
this.isHydrating = true;
|
|
52416
52803
|
try {
|
|
52804
|
+
this.clearMountedSourceSelectionsForNonPersistentState([mutation.defaultPage]);
|
|
52417
52805
|
this.authoredPageDefinition = this.clonePageDefinition(mutation.defaultPage);
|
|
52418
52806
|
this.applyPageUpdate(mutation.defaultPage, false);
|
|
52419
52807
|
}
|
|
@@ -53799,7 +54187,42 @@ class PraxisSurfaceHostComponent {
|
|
|
53799
54187
|
loader.renderNow();
|
|
53800
54188
|
}
|
|
53801
54189
|
enrichWidget(widget, widgetKey) {
|
|
53802
|
-
if (!widget
|
|
54190
|
+
if (!widget) {
|
|
54191
|
+
return widget;
|
|
54192
|
+
}
|
|
54193
|
+
if (widget.id === 'praxis-crud') {
|
|
54194
|
+
const explicitContext = widget.inputs?.['context'] &&
|
|
54195
|
+
typeof widget.inputs['context'] === 'object' &&
|
|
54196
|
+
!Array.isArray(widget.inputs['context'])
|
|
54197
|
+
? widget.inputs['context']
|
|
54198
|
+
: {};
|
|
54199
|
+
const hostContext = this.context &&
|
|
54200
|
+
typeof this.context === 'object' &&
|
|
54201
|
+
!Array.isArray(this.context)
|
|
54202
|
+
? this.context
|
|
54203
|
+
: {};
|
|
54204
|
+
const mergedContext = {
|
|
54205
|
+
...hostContext,
|
|
54206
|
+
...explicitContext,
|
|
54207
|
+
};
|
|
54208
|
+
if ((!mergedContext['surfaceRuntime'] ||
|
|
54209
|
+
typeof mergedContext['surfaceRuntime'] !== 'object') &&
|
|
54210
|
+
hostContext['surfaceRuntime'] &&
|
|
54211
|
+
typeof hostContext['surfaceRuntime'] === 'object') {
|
|
54212
|
+
mergedContext['surfaceRuntime'] = hostContext['surfaceRuntime'];
|
|
54213
|
+
}
|
|
54214
|
+
if (Object.keys(mergedContext).length === 0) {
|
|
54215
|
+
return widget;
|
|
54216
|
+
}
|
|
54217
|
+
return {
|
|
54218
|
+
...widget,
|
|
54219
|
+
inputs: {
|
|
54220
|
+
...(widget.inputs || {}),
|
|
54221
|
+
context: mergedContext,
|
|
54222
|
+
},
|
|
54223
|
+
};
|
|
54224
|
+
}
|
|
54225
|
+
if (widget.id !== 'praxis-rich-content') {
|
|
53803
54226
|
return widget;
|
|
53804
54227
|
}
|
|
53805
54228
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/core",
|
|
3
|
-
"version": "10.0.0-rc.
|
|
3
|
+
"version": "10.0.0-rc.27",
|
|
4
4
|
"description": "Core library for Praxis UI Workspace: types, tokens, services and utilities shared across @praxisui/* packages.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/cdk": "^21.0.0",
|
|
@@ -1885,6 +1885,10 @@ interface FilteringConfig {
|
|
|
1885
1885
|
selectedFieldIds?: string[];
|
|
1886
1886
|
/** Exibir opções avançadas por padrão */
|
|
1887
1887
|
showAdvanced?: boolean;
|
|
1888
|
+
/** Exibir botão "Gerenciar campos" na barra do filtro (padrão: true) */
|
|
1889
|
+
showAddButton?: boolean;
|
|
1890
|
+
/** Exibir botão "Configurações do filtro" na barra quando customização estiver disponível (padrão: true) */
|
|
1891
|
+
showFilterSettings?: boolean;
|
|
1888
1892
|
/** Permitir salvar tags/atalhos de filtro */
|
|
1889
1893
|
allowSaveTags?: boolean;
|
|
1890
1894
|
/** Atalhos governados, somente leitura, aplicados como patch do DTO de filtro */
|
package/types/praxisui-core.d.ts
CHANGED
|
@@ -2959,6 +2959,10 @@ interface FilteringConfig {
|
|
|
2959
2959
|
selectedFieldIds?: string[];
|
|
2960
2960
|
/** Exibir opções avançadas por padrão */
|
|
2961
2961
|
showAdvanced?: boolean;
|
|
2962
|
+
/** Exibir botão "Gerenciar campos" na barra do filtro (padrão: true) */
|
|
2963
|
+
showAddButton?: boolean;
|
|
2964
|
+
/** Exibir botão "Configurações do filtro" na barra quando customização estiver disponível (padrão: true) */
|
|
2965
|
+
showFilterSettings?: boolean;
|
|
2962
2966
|
/** Permitir salvar tags/atalhos de filtro */
|
|
2963
2967
|
allowSaveTags?: boolean;
|
|
2964
2968
|
/** Atalhos governados, somente leitura, aplicados como patch do DTO de filtro */
|
|
@@ -11788,6 +11792,9 @@ declare class BulkSyncSession {
|
|
|
11788
11792
|
private readonly current;
|
|
11789
11793
|
private confirmationKey;
|
|
11790
11794
|
private refreshInFlight;
|
|
11795
|
+
private refreshGeneration;
|
|
11796
|
+
private refreshSignal?;
|
|
11797
|
+
private resultsGeneration;
|
|
11791
11798
|
private readonly ownedContract;
|
|
11792
11799
|
private readonly options?;
|
|
11793
11800
|
/** Return detached wire data while retaining the original technical error for UX mapping. */
|
|
@@ -11804,8 +11811,14 @@ declare class BulkSyncSession {
|
|
|
11804
11811
|
constructor(client: BulkOperationClientService, contract: BulkExecutionContract, options?: ResourceDiscoveryRequestOptions);
|
|
11805
11812
|
evaluate<WI extends BulkWireId>(request: BulkSyncEvaluationRequest<WI>): Promise<BulkProposal>;
|
|
11806
11813
|
confirm(idempotencyKey: string): Promise<BulkExecution>;
|
|
11807
|
-
|
|
11808
|
-
|
|
11814
|
+
/** One cancellable consultation; the caller owns its deadline. POST custody is unchanged. */
|
|
11815
|
+
refreshExecution(signal?: AbortSignal): Promise<BulkExecution>;
|
|
11816
|
+
/** Cancels only this results GET; an abort never discards the accepted execution. */
|
|
11817
|
+
readResults<WI extends BulkWireId>(size?: number, after?: string, signal?: AbortSignal): Promise<BulkCursorPage<BulkItemResult<WI>>>;
|
|
11818
|
+
private readAborted;
|
|
11819
|
+
private requireReadLifetime;
|
|
11820
|
+
/** Cancellation owns the HTTP subscription, not just its Promise observer. */
|
|
11821
|
+
private readValue;
|
|
11809
11822
|
private fail;
|
|
11810
11823
|
}
|
|
11811
11824
|
declare class BulkSyncStateService {
|
|
@@ -14778,6 +14791,12 @@ interface SettingsPanelContextMode {
|
|
|
14778
14791
|
label: string;
|
|
14779
14792
|
icon?: string;
|
|
14780
14793
|
disabled?: boolean;
|
|
14794
|
+
/**
|
|
14795
|
+
* Optional logical group identifier. Modes sharing the same `editorGroup`
|
|
14796
|
+
* operate on the same active editor instance (in-place tab/section switch)
|
|
14797
|
+
* without triggering an intermediate `onApply()` before switching.
|
|
14798
|
+
*/
|
|
14799
|
+
editorGroup?: string;
|
|
14781
14800
|
}
|
|
14782
14801
|
interface SettingsPanelContextNavigation {
|
|
14783
14802
|
targets?: ReadonlyArray<SettingsPanelContextTarget>;
|
|
@@ -18876,6 +18895,9 @@ declare class DynamicWidgetPageComponent implements OnChanges, OnDestroy {
|
|
|
18876
18895
|
private readonly injector;
|
|
18877
18896
|
private readonly settingsSessions;
|
|
18878
18897
|
private widgetSettingsSession?;
|
|
18898
|
+
private activeSettingsPanelSession?;
|
|
18899
|
+
private activeSettingsFocusReturnTarget;
|
|
18900
|
+
private pendingContextNavigationFocusTarget?;
|
|
18879
18901
|
private settingsIdentityRevision;
|
|
18880
18902
|
private acceptedPageIdentity;
|
|
18881
18903
|
private readonly storage;
|
|
@@ -19088,6 +19110,7 @@ declare class DynamicWidgetPageComponent implements OnChanges, OnDestroy {
|
|
|
19088
19110
|
private widgetSettingsSelectTab?;
|
|
19089
19111
|
private buildWidgetSettingsContextNavigation;
|
|
19090
19112
|
private navigateToWidgetSettingsContext;
|
|
19113
|
+
private resolveSettingsFocusReturnTarget;
|
|
19091
19114
|
private openWidgetSettingsPanel;
|
|
19092
19115
|
sizeEditorLayoutItems(): {
|
|
19093
19116
|
key: string;
|
|
@@ -19125,6 +19148,13 @@ declare class DynamicWidgetPageComponent implements OnChanges, OnDestroy {
|
|
|
19125
19148
|
private restoreWidgetShellFields;
|
|
19126
19149
|
private applyPageLayout;
|
|
19127
19150
|
private applyAuthoringPageUpdate;
|
|
19151
|
+
private isAcknowledgedCanonicalPage;
|
|
19152
|
+
private hasExplicitNonPersistentStateOverride;
|
|
19153
|
+
private collectResetNonPersistentStatePaths;
|
|
19154
|
+
private samePageContextIdentity;
|
|
19155
|
+
private preserveEphemeralRuntimeState;
|
|
19156
|
+
private isSameOrNestedStatePath;
|
|
19157
|
+
private clearMountedSourceSelectionsForNonPersistentState;
|
|
19128
19158
|
private applyPageUpdate;
|
|
19129
19159
|
private handleDataSourceUpdate;
|
|
19130
19160
|
private flushPendingDataSourceUpdates;
|