@praxisui/core 10.0.0-rc.25 → 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 +45 -3
- package/ai/component-registry.json +2 -2
- package/fesm2022/praxisui-core.mjs +753 -65
- package/package.json +1 -1
- package/types/praxisui-core-schema.d.ts +11 -1
- package/types/praxisui-core.d.ts +132 -5
|
@@ -2,8 +2,8 @@ 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 } from 'rxjs';
|
|
6
|
-
import { switchMap, map, take, catchError, concatMap, tap, shareReplay, takeUntil, toArray, finalize,
|
|
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
|
+
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';
|
|
9
9
|
import { Router, ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router';
|
|
@@ -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
|
}
|
|
@@ -15832,21 +15881,18 @@ class PraxisBulkClientService {
|
|
|
15832
15881
|
* Polling utilitario para aguardar a conclusao terminal de uma execucao assincrona.
|
|
15833
15882
|
*/
|
|
15834
15883
|
waitForExecution(executionOp, executionId, pollIntervalMs = 1000, timeoutMs = 60000, options) {
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
|
|
15838
|
-
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
15884
|
+
return timer(0, pollIntervalMs).pipe(
|
|
15885
|
+
// A slow useful GET owns its slot; interval ticks never abort it or queue reads.
|
|
15886
|
+
exhaustMap(() => this.getExecution(executionOp, executionId, options)), filter((exec) => this.isTerminalStatus(exec.status)), take(1),
|
|
15887
|
+
// Only terminal snapshots cross the filter. This deadline therefore starts
|
|
15888
|
+
// per subscription and cannot be renewed by pending responses or a NEVER GET.
|
|
15889
|
+
timeout({
|
|
15890
|
+
first: timeoutMs,
|
|
15891
|
+
with: () => throwError(() => new Error(`Bulk execution ${executionId} timed out after ${timeoutMs}ms.`)),
|
|
15892
|
+
}));
|
|
15843
15893
|
}
|
|
15844
15894
|
isTerminalStatus(status) {
|
|
15845
|
-
return (status
|
|
15846
|
-
status === 'COMPLETED_WITH_ERRORS' ||
|
|
15847
|
-
status === 'CANCELLED' ||
|
|
15848
|
-
status === 'STOPPED' ||
|
|
15849
|
-
status === 'RECONCILIATION_REQUIRED');
|
|
15895
|
+
return isBulkExecutionTerminal(status);
|
|
15850
15896
|
}
|
|
15851
15897
|
resolveUrl(path, options) {
|
|
15852
15898
|
return this.discovery.resolveHref(path, options);
|
|
@@ -27007,15 +27053,30 @@ function removeValueAtPath(target, path) {
|
|
|
27007
27053
|
if (!segments.length) {
|
|
27008
27054
|
return;
|
|
27009
27055
|
}
|
|
27056
|
+
const parents = [];
|
|
27010
27057
|
let current = target;
|
|
27011
27058
|
for (const segment of segments.slice(0, -1)) {
|
|
27012
27059
|
const next = current[segment];
|
|
27013
27060
|
if (!next || typeof next !== 'object' || Array.isArray(next)) {
|
|
27014
27061
|
return;
|
|
27015
27062
|
}
|
|
27063
|
+
parents.push({ parent: current, key: segment });
|
|
27016
27064
|
current = next;
|
|
27017
27065
|
}
|
|
27018
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
|
+
}
|
|
27019
27080
|
}
|
|
27020
27081
|
function normalizeEndpoint(endpoint) {
|
|
27021
27082
|
if (endpoint.kind === 'component-port') {
|
|
@@ -37087,6 +37148,9 @@ class DynamicWidgetLoaderDirective {
|
|
|
37087
37148
|
if (key && (key === 'expr' || key.endsWith('Expr'))) {
|
|
37088
37149
|
return true;
|
|
37089
37150
|
}
|
|
37151
|
+
if (path.includes('componentInputs')) {
|
|
37152
|
+
return true;
|
|
37153
|
+
}
|
|
37090
37154
|
for (let index = 0; index < path.length - 1; index++) {
|
|
37091
37155
|
if ((path[index] === 'action' || path[index] === 'globalAction')
|
|
37092
37156
|
&& path[index + 1] === 'payload') {
|
|
@@ -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 = {
|
|
@@ -39737,6 +39807,13 @@ class WidgetPageStateRuntimeService {
|
|
|
39737
39807
|
computeDerivedNode(node, env, diagnostics, key) {
|
|
39738
39808
|
const compute = node.compute;
|
|
39739
39809
|
if (!compute) {
|
|
39810
|
+
if (this.isPlainObject(node) &&
|
|
39811
|
+
Object.keys(node).length > 0 &&
|
|
39812
|
+
!('dependsOn' in node) &&
|
|
39813
|
+
!('description' in node) &&
|
|
39814
|
+
!('cache' in node)) {
|
|
39815
|
+
return this.evaluateDerivedJsonLogic(node, env, diagnostics, key);
|
|
39816
|
+
}
|
|
39740
39817
|
diagnostics.push(`Derived state '${key}' is missing compute definition.`);
|
|
39741
39818
|
return undefined;
|
|
39742
39819
|
}
|
|
@@ -39995,8 +40072,10 @@ class WidgetPageStateRuntimeService {
|
|
|
39995
40072
|
}
|
|
39996
40073
|
evaluateDerivedJsonLogic(expression, env, diagnostics, key) {
|
|
39997
40074
|
try {
|
|
40075
|
+
const clonedState = this.clone(env.state) || {};
|
|
39998
40076
|
return this.jsonLogic.evaluate(expression, {
|
|
39999
|
-
|
|
40077
|
+
...clonedState,
|
|
40078
|
+
state: clonedState,
|
|
40000
40079
|
context: this.clone(env.context) || {},
|
|
40001
40080
|
}, this.derivedValueRuleOptions);
|
|
40002
40081
|
}
|
|
@@ -40123,24 +40202,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
40123
40202
|
}], ctorParameters: () => [{ type: PraxisJsonLogicService }] });
|
|
40124
40203
|
|
|
40125
40204
|
const DEFAULT_JSON_LOGIC$2 = new PraxisJsonLogicService(null);
|
|
40205
|
+
const CANONICAL_OPERATION_SUFFIXES = {
|
|
40206
|
+
statsGroupBy: 'stats/group-by',
|
|
40207
|
+
'stats/group-by': 'stats/group-by',
|
|
40208
|
+
statsTimeseries: 'stats/timeseries',
|
|
40209
|
+
'stats/timeseries': 'stats/timeseries',
|
|
40210
|
+
statsDistribution: 'stats/distribution',
|
|
40211
|
+
'stats/distribution': 'stats/distribution',
|
|
40212
|
+
statsComparison: 'stats/comparison',
|
|
40213
|
+
'stats/comparison': 'stats/comparison',
|
|
40214
|
+
filter: 'filter',
|
|
40215
|
+
};
|
|
40126
40216
|
class WidgetPageDataSourceRuntimeService {
|
|
40127
40217
|
http = inject(HttpClient, { optional: true });
|
|
40128
40218
|
apiUrlConfig = inject(API_URL, { optional: true });
|
|
40129
40219
|
jsonLogic = inject(PraxisJsonLogicService, { optional: true }) || DEFAULT_JSON_LOGIC$2;
|
|
40130
40220
|
connections = inject(ConnectionManagerService);
|
|
40221
|
+
latestSnapshots = new Map();
|
|
40222
|
+
getLatestSnapshot(id) {
|
|
40223
|
+
return this.latestSnapshots.get(id);
|
|
40224
|
+
}
|
|
40131
40225
|
/**
|
|
40132
40226
|
* Constrói a URL canônica completa para o resourcePath declarado no dataSource.
|
|
40227
|
+
* Quando `endpointKey` identifica uma operação canônica (`statsGroupBy`, `statsTimeseries`,
|
|
40228
|
+
* `statsDistribution`, `statsComparison`, `filter`) e não um host customizado em `API_URL`,
|
|
40229
|
+
* anexa o sufixo da operação ao `resourcePath` caso ainda não esteja presente.
|
|
40133
40230
|
*/
|
|
40134
40231
|
resolveUrl(resourcePath, endpointKey) {
|
|
40135
40232
|
const trimmed = (resourcePath || '').trim();
|
|
40136
40233
|
if (!trimmed)
|
|
40137
40234
|
return '';
|
|
40235
|
+
const key = (endpointKey || 'default').trim() || 'default';
|
|
40236
|
+
const hasCustomEndpointHost = Boolean(this.apiUrlConfig && key in this.apiUrlConfig);
|
|
40237
|
+
const operationSuffix = !hasCustomEndpointHost ? CANONICAL_OPERATION_SUFFIXES[key] : undefined;
|
|
40238
|
+
const appendOperationSuffix = (target) => {
|
|
40239
|
+
if (!operationSuffix)
|
|
40240
|
+
return target;
|
|
40241
|
+
const normalizedTarget = target.replace(/\/+$/, '');
|
|
40242
|
+
if (normalizedTarget === operationSuffix ||
|
|
40243
|
+
normalizedTarget.endsWith(`/${operationSuffix}`)) {
|
|
40244
|
+
return normalizedTarget;
|
|
40245
|
+
}
|
|
40246
|
+
return `${normalizedTarget}/${operationSuffix}`;
|
|
40247
|
+
};
|
|
40138
40248
|
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
40139
|
-
return trimmed;
|
|
40249
|
+
return appendOperationSuffix(trimmed);
|
|
40140
40250
|
}
|
|
40141
40251
|
let base = '';
|
|
40142
40252
|
if (this.apiUrlConfig) {
|
|
40143
|
-
const key = (endpointKey || 'default').trim() || 'default';
|
|
40144
40253
|
const entry = this.apiUrlConfig[key] ||
|
|
40145
40254
|
this.apiUrlConfig['default'] ||
|
|
40146
40255
|
Object.values(this.apiUrlConfig)[0];
|
|
@@ -40148,11 +40257,11 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40148
40257
|
base = buildApiUrl(entry);
|
|
40149
40258
|
}
|
|
40150
40259
|
}
|
|
40260
|
+
const cleanPath = appendOperationSuffix(trimmed.replace(/^\/+/, ''));
|
|
40151
40261
|
if (!base) {
|
|
40152
|
-
return
|
|
40262
|
+
return cleanPath.startsWith('/') ? cleanPath : `/${cleanPath}`;
|
|
40153
40263
|
}
|
|
40154
40264
|
const cleanBase = base.replace(/\/+$/, '');
|
|
40155
|
-
const cleanPath = trimmed.replace(/^\/+/, '');
|
|
40156
40265
|
return `${cleanBase}/${cleanPath}`;
|
|
40157
40266
|
}
|
|
40158
40267
|
/**
|
|
@@ -40192,7 +40301,9 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40192
40301
|
return of(dataSource.fallbackValue ?? null);
|
|
40193
40302
|
}
|
|
40194
40303
|
const method = dataSource.method ??
|
|
40195
|
-
(dataSource.kind === 'stats-query' ||
|
|
40304
|
+
(dataSource.kind === 'stats-query' ||
|
|
40305
|
+
dataSource.resourcePath.includes('/stats/') ||
|
|
40306
|
+
url.includes('/stats/')
|
|
40196
40307
|
? 'POST'
|
|
40197
40308
|
: 'GET');
|
|
40198
40309
|
let request$;
|
|
@@ -40279,6 +40390,7 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40279
40390
|
hasSucceededOnce = true;
|
|
40280
40391
|
skippedWhileHidden = false;
|
|
40281
40392
|
const now = new Date().toISOString();
|
|
40393
|
+
this.latestSnapshots.set(ds.id, { data: this.clone(value), lastUpdated: now });
|
|
40282
40394
|
onDiagnostic?.({
|
|
40283
40395
|
dataSourceId: ds.id,
|
|
40284
40396
|
status: 'success',
|
|
@@ -40292,10 +40404,15 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40292
40404
|
});
|
|
40293
40405
|
return value;
|
|
40294
40406
|
}), catchError((err) => {
|
|
40407
|
+
const errorMsg = err?.message || String(err);
|
|
40408
|
+
this.latestSnapshots.set(ds.id, {
|
|
40409
|
+
...(this.latestSnapshots.get(ds.id) || {}),
|
|
40410
|
+
error: errorMsg,
|
|
40411
|
+
});
|
|
40295
40412
|
onDiagnostic?.({
|
|
40296
40413
|
dataSourceId: ds.id,
|
|
40297
40414
|
status: 'error',
|
|
40298
|
-
error:
|
|
40415
|
+
error: errorMsg,
|
|
40299
40416
|
});
|
|
40300
40417
|
if (!hasSucceededOnce &&
|
|
40301
40418
|
ds.errorPolicy !== 'silent' &&
|
|
@@ -40353,10 +40470,26 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40353
40470
|
destroy$.complete();
|
|
40354
40471
|
};
|
|
40355
40472
|
}
|
|
40473
|
+
/**
|
|
40474
|
+
* Avalia uma transformação declarativa contra uma amostra ou resposta bruta,
|
|
40475
|
+
* preservando `false`, `0` e `null` e reportando erros explícitos sem convertê-los em `0` silencioso.
|
|
40476
|
+
*/
|
|
40477
|
+
previewTransform(rawSample, dataSource) {
|
|
40478
|
+
try {
|
|
40479
|
+
const value = this.transformPayload(rawSample, dataSource, true);
|
|
40480
|
+
return { ok: true, value };
|
|
40481
|
+
}
|
|
40482
|
+
catch (error) {
|
|
40483
|
+
return {
|
|
40484
|
+
ok: false,
|
|
40485
|
+
error: error instanceof Error ? error.message : String(error ?? 'Transform evaluation failed'),
|
|
40486
|
+
};
|
|
40487
|
+
}
|
|
40488
|
+
}
|
|
40356
40489
|
/**
|
|
40357
40490
|
* Transforma a resposta bruta do backend aplicando envelope canônico ou JsonLogic.
|
|
40358
40491
|
*/
|
|
40359
|
-
transformPayload(raw, dataSource) {
|
|
40492
|
+
transformPayload(raw, dataSource, strictErrors = false) {
|
|
40360
40493
|
if (raw == null)
|
|
40361
40494
|
return raw;
|
|
40362
40495
|
let payload = raw;
|
|
@@ -40378,9 +40511,13 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40378
40511
|
const kind = transformObj['kind'];
|
|
40379
40512
|
if (kind === 'path' && typeof transformObj['path'] === 'string') {
|
|
40380
40513
|
const extracted = this.connections.extractByPath(payload, transformObj['path']);
|
|
40381
|
-
|
|
40514
|
+
const resolved = extracted !== undefined
|
|
40382
40515
|
? extracted
|
|
40383
40516
|
: this.connections.extractByPath(raw, transformObj['path']);
|
|
40517
|
+
if (resolved === undefined && strictErrors) {
|
|
40518
|
+
throw new Error(`Transform path '${transformObj['path']}' resolved to undefined.`);
|
|
40519
|
+
}
|
|
40520
|
+
return resolved;
|
|
40384
40521
|
}
|
|
40385
40522
|
if (kind === 'group-by-map') {
|
|
40386
40523
|
const rawBuckets = raw?.buckets ??
|
|
@@ -40389,6 +40526,14 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40389
40526
|
const buckets = Array.isArray(rawBuckets) ? rawBuckets : [];
|
|
40390
40527
|
const mappings = transformObj['mappings'] || {};
|
|
40391
40528
|
const totalField = transformObj['totalField'];
|
|
40529
|
+
const weightedAverageField = transformObj['weightedAverageField'];
|
|
40530
|
+
const weightedAverageDecimals = typeof transformObj['weightedAverageDecimals'] === 'number' &&
|
|
40531
|
+
Number.isFinite(transformObj['weightedAverageDecimals'])
|
|
40532
|
+
? Math.max(0, Math.min(10, Math.trunc(transformObj['weightedAverageDecimals'])))
|
|
40533
|
+
: 2;
|
|
40534
|
+
const ratios = transformObj['ratios'] && typeof transformObj['ratios'] === 'object'
|
|
40535
|
+
? transformObj['ratios']
|
|
40536
|
+
: undefined;
|
|
40392
40537
|
const result = {};
|
|
40393
40538
|
for (const targetField of Object.values(mappings)) {
|
|
40394
40539
|
if (typeof targetField === 'string' && targetField.trim()) {
|
|
@@ -40396,11 +40541,25 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40396
40541
|
}
|
|
40397
40542
|
}
|
|
40398
40543
|
let total = 0;
|
|
40544
|
+
let weightedSum = 0;
|
|
40545
|
+
let totalWeight = 0;
|
|
40399
40546
|
for (const bucket of buckets) {
|
|
40400
|
-
const
|
|
40401
|
-
const
|
|
40547
|
+
const hasExplicitKey = bucket?.key !== undefined;
|
|
40548
|
+
const keyStr = String(hasExplicitKey ? bucket.key : (bucket?.value ?? ''));
|
|
40549
|
+
const val = hasExplicitKey
|
|
40550
|
+
? Number(bucket?.value ?? bucket?.count ?? 0)
|
|
40551
|
+
: Number(bucket?.count ?? bucket?.value ?? 0);
|
|
40402
40552
|
const safeVal = Number.isFinite(val) ? val : 0;
|
|
40403
40553
|
total += safeVal;
|
|
40554
|
+
const rawWeight = bucket?.count !== undefined ? Number(bucket.count) : 1;
|
|
40555
|
+
const safeWeight = Number.isFinite(rawWeight) && rawWeight > 0 ? rawWeight : 0;
|
|
40556
|
+
const bucketMetric = hasExplicitKey
|
|
40557
|
+
? Number(bucket?.value ?? bucket?.key ?? 0)
|
|
40558
|
+
: Number(bucket?.value ?? 0);
|
|
40559
|
+
if (Number.isFinite(bucketMetric) && safeWeight > 0) {
|
|
40560
|
+
weightedSum += bucketMetric * safeWeight;
|
|
40561
|
+
totalWeight += safeWeight;
|
|
40562
|
+
}
|
|
40404
40563
|
const mappedField = mappings[keyStr];
|
|
40405
40564
|
if (mappedField) {
|
|
40406
40565
|
result[mappedField] = (result[mappedField] || 0) + safeVal;
|
|
@@ -40409,6 +40568,48 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40409
40568
|
if (totalField) {
|
|
40410
40569
|
result[totalField] = total;
|
|
40411
40570
|
}
|
|
40571
|
+
if (weightedAverageField && weightedAverageField.trim()) {
|
|
40572
|
+
if (totalWeight > 0) {
|
|
40573
|
+
const avg = weightedSum / totalWeight;
|
|
40574
|
+
const factor = Math.pow(10, weightedAverageDecimals);
|
|
40575
|
+
result[weightedAverageField.trim()] =
|
|
40576
|
+
Math.round((avg + Number.EPSILON) * factor) / factor;
|
|
40577
|
+
}
|
|
40578
|
+
else {
|
|
40579
|
+
result[weightedAverageField.trim()] = 0;
|
|
40580
|
+
}
|
|
40581
|
+
}
|
|
40582
|
+
if (ratios) {
|
|
40583
|
+
for (const [ratioKey, ratioDef] of Object.entries(ratios)) {
|
|
40584
|
+
if (!ratioKey.trim() || !ratioDef || typeof ratioDef !== 'object')
|
|
40585
|
+
continue;
|
|
40586
|
+
const rawNum = ratioDef.numerator ?? ratioDef.numeratorField;
|
|
40587
|
+
const rawDen = ratioDef.denominator ?? ratioDef.denominatorField;
|
|
40588
|
+
const numField = typeof rawNum === 'string' ? rawNum.trim() : '';
|
|
40589
|
+
const denField = typeof rawDen === 'string' ? rawDen.trim() : '';
|
|
40590
|
+
if (!numField || !denField)
|
|
40591
|
+
continue;
|
|
40592
|
+
const numVal = Number(result[numField] ?? 0);
|
|
40593
|
+
const denVal = Number(result[denField] ?? 0);
|
|
40594
|
+
const scale = typeof ratioDef.scale === 'number' && Number.isFinite(ratioDef.scale)
|
|
40595
|
+
? ratioDef.scale
|
|
40596
|
+
: 100;
|
|
40597
|
+
const decimals = typeof ratioDef.decimals === 'number' && Number.isFinite(ratioDef.decimals)
|
|
40598
|
+
? Math.max(0, Math.min(10, Math.trunc(ratioDef.decimals)))
|
|
40599
|
+
: 1;
|
|
40600
|
+
if (Number.isFinite(numVal) && Number.isFinite(denVal) && denVal > 0) {
|
|
40601
|
+
const rawRatio = (numVal / denVal) * scale;
|
|
40602
|
+
const factor = Math.pow(10, decimals);
|
|
40603
|
+
result[ratioKey.trim()] =
|
|
40604
|
+
decimals === 0
|
|
40605
|
+
? Math.round(rawRatio)
|
|
40606
|
+
: Math.round((rawRatio + Number.EPSILON) * factor) / factor;
|
|
40607
|
+
}
|
|
40608
|
+
else {
|
|
40609
|
+
result[ratioKey.trim()] = 0;
|
|
40610
|
+
}
|
|
40611
|
+
}
|
|
40612
|
+
}
|
|
40412
40613
|
return result;
|
|
40413
40614
|
}
|
|
40414
40615
|
if (kind === 'timeseries-last') {
|
|
@@ -40436,6 +40637,9 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40436
40637
|
}
|
|
40437
40638
|
return result;
|
|
40438
40639
|
}
|
|
40640
|
+
if (strictErrors) {
|
|
40641
|
+
throw new Error(`Unsupported transform kind "${String(kind ?? '')}".`);
|
|
40642
|
+
}
|
|
40439
40643
|
}
|
|
40440
40644
|
try {
|
|
40441
40645
|
return this.jsonLogic.evaluate(transform, {
|
|
@@ -40444,7 +40648,10 @@ class WidgetPageDataSourceRuntimeService {
|
|
|
40444
40648
|
result: payload,
|
|
40445
40649
|
});
|
|
40446
40650
|
}
|
|
40447
|
-
catch {
|
|
40651
|
+
catch (error) {
|
|
40652
|
+
if (strictErrors) {
|
|
40653
|
+
throw error;
|
|
40654
|
+
}
|
|
40448
40655
|
return payload;
|
|
40449
40656
|
}
|
|
40450
40657
|
}
|
|
@@ -43839,6 +44046,8 @@ const DYNAMIC_WIDGET_PAGE_I18N_CONFIG = {
|
|
|
43839
44046
|
'size.widthError': 'Informe um número inteiro entre 1 e {{columns}}. O espaço livre também pode limitar a largura.',
|
|
43840
44047
|
'size.heightPixelsHelp': 'Altura do conteúdo em pixels. O cabeçalho e o espaçamento podem aumentar a altura total.',
|
|
43841
44048
|
'size.heightRowsHelp': 'Quantidade de linhas que o widget ocupa.',
|
|
44049
|
+
'size.responsiveSummaryContent': 'Grade de {{columns}} colunas com altura em pixels ajustada ao conteúdo. Em telas menores sem layout próprio, os widgets são reorganizados automaticamente em coluna única.',
|
|
44050
|
+
'size.responsiveSummaryRows': 'Grade de {{columns}} colunas com altura em faixas da grade. Em telas menores sem layout próprio, os widgets são reorganizados automaticamente em coluna única.',
|
|
43842
44051
|
'size.anchorHelp': 'Ao editar largura ou altura, este canto fica no lugar e o lado oposto se move.',
|
|
43843
44052
|
'size.title': 'Tamanho…',
|
|
43844
44053
|
'size.fill': 'Preencher espaço livre',
|
|
@@ -44001,6 +44210,8 @@ const DYNAMIC_WIDGET_PAGE_I18N_CONFIG = {
|
|
|
44001
44210
|
'size.widthError': 'Enter a whole number between 1 and {{columns}}. Free space may also limit the width.',
|
|
44002
44211
|
'size.heightPixelsHelp': 'Content height in pixels. The header and spacing may increase the total height.',
|
|
44003
44212
|
'size.heightRowsHelp': 'Number of rows occupied by the widget.',
|
|
44213
|
+
'size.responsiveSummaryContent': '{{columns}}-column grid with content height in pixels. On smaller screens without a dedicated layout, widgets automatically reflow into a single column.',
|
|
44214
|
+
'size.responsiveSummaryRows': '{{columns}}-column grid with row-span height. On smaller screens without a dedicated layout, widgets automatically reflow into a single column.',
|
|
44004
44215
|
'size.anchorHelp': 'When editing width or height, this corner stays in place and the opposite side moves.',
|
|
44005
44216
|
'size.title': 'Size…',
|
|
44006
44217
|
'size.fill': 'Fill free space',
|
|
@@ -44194,6 +44405,7 @@ class WidgetSettingsEditorComponent {
|
|
|
44194
44405
|
return;
|
|
44195
44406
|
const changed = id !== this.currentSection();
|
|
44196
44407
|
this.selectedSection.set(id);
|
|
44408
|
+
this.data.sectionChanged?.(id);
|
|
44197
44409
|
if (id.startsWith('widget:'))
|
|
44198
44410
|
this.shellProvider?.selectSettingsSection?.(id.slice('widget:'.length));
|
|
44199
44411
|
if (changed)
|
|
@@ -44204,6 +44416,10 @@ class WidgetSettingsEditorComponent {
|
|
|
44204
44416
|
} }, { injector: this.injector });
|
|
44205
44417
|
}
|
|
44206
44418
|
ngAfterViewInit() {
|
|
44419
|
+
this.data.registerTabController?.((tab) => {
|
|
44420
|
+
const targetId = tab === 'size' ? 'size' : (this.sections().find(s => s.id !== 'size')?.id ?? 'widget');
|
|
44421
|
+
this.selectSection(targetId);
|
|
44422
|
+
});
|
|
44207
44423
|
// Let the eager tab contents initialize before observing their providers.
|
|
44208
44424
|
queueMicrotask(() => {
|
|
44209
44425
|
if (this.destroyRef.destroyed)
|
|
@@ -44572,7 +44788,13 @@ class CanvasSizeEditorComponent {
|
|
|
44572
44788
|
showDelay: 500, hideDelay: 0, touchendHideDelay: 1500, disableTooltipInteractivity: true,
|
|
44573
44789
|
} }], usesOnChanges: true, ngImport: i0, template: `
|
|
44574
44790
|
<section class="size-editor" [formGroup]="form" [attr.aria-label]="t('size.title')">
|
|
44575
|
-
<div class="context"
|
|
44791
|
+
<div class="context">
|
|
44792
|
+
<strong>{{ widgetLabel() }}</strong>
|
|
44793
|
+
<span>{{ scopeLabel() }}</span>
|
|
44794
|
+
<p class="hint" data-testid="canvas-size-responsive-summary">
|
|
44795
|
+
{{ t(contentRows() ? 'size.responsiveSummaryContent' : 'size.responsiveSummaryRows', { columns: columns() }) }}
|
|
44796
|
+
</p>
|
|
44797
|
+
</div>
|
|
44576
44798
|
<div class="dimensions">
|
|
44577
44799
|
<mat-form-field appearance="outline" subscriptSizing="dynamic">
|
|
44578
44800
|
<mat-label>{{ t('size.width') }}</mat-label>
|
|
@@ -44696,7 +44918,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
44696
44918
|
showDelay: 500, hideDelay: 0, touchendHideDelay: 1500, disableTooltipInteractivity: true,
|
|
44697
44919
|
} }], changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatRadioModule, MatTooltipModule], template: `
|
|
44698
44920
|
<section class="size-editor" [formGroup]="form" [attr.aria-label]="t('size.title')">
|
|
44699
|
-
<div class="context"
|
|
44921
|
+
<div class="context">
|
|
44922
|
+
<strong>{{ widgetLabel() }}</strong>
|
|
44923
|
+
<span>{{ scopeLabel() }}</span>
|
|
44924
|
+
<p class="hint" data-testid="canvas-size-responsive-summary">
|
|
44925
|
+
{{ t(contentRows() ? 'size.responsiveSummaryContent' : 'size.responsiveSummaryRows', { columns: columns() }) }}
|
|
44926
|
+
</p>
|
|
44927
|
+
</div>
|
|
44700
44928
|
<div class="dimensions">
|
|
44701
44929
|
<mat-form-field appearance="outline" subscriptSizing="dynamic">
|
|
44702
44930
|
<mat-label>{{ t('size.width') }}</mat-label>
|
|
@@ -45911,6 +46139,9 @@ class DynamicWidgetPageComponent {
|
|
|
45911
46139
|
injector = inject(Injector);
|
|
45912
46140
|
settingsSessions = new Map();
|
|
45913
46141
|
widgetSettingsSession;
|
|
46142
|
+
activeSettingsPanelSession;
|
|
46143
|
+
activeSettingsFocusReturnTarget = null;
|
|
46144
|
+
pendingContextNavigationFocusTarget;
|
|
45914
46145
|
settingsIdentityRevision = 0;
|
|
45915
46146
|
acceptedPageIdentity;
|
|
45916
46147
|
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
@@ -46079,13 +46310,15 @@ class DynamicWidgetPageComponent {
|
|
|
46079
46310
|
// Controlled hosts return pageChange through [page]. That acknowledgement
|
|
46080
46311
|
// must not dispose the open settings provider or rebuild its size template.
|
|
46081
46312
|
// Any other input change or different document still invalidates the editor.
|
|
46082
|
-
if (this.widgetSettingsKey &&
|
|
46083
|
-
&&
|
|
46084
|
-
|
|
46313
|
+
if (this.widgetSettingsKey &&
|
|
46314
|
+
changes['page'] &&
|
|
46315
|
+
Object.keys(changes).length === 1 &&
|
|
46316
|
+
this.isAcknowledgedCanonicalPage(incomingPage)) {
|
|
46085
46317
|
return;
|
|
46086
|
-
|
|
46087
|
-
|
|
46088
|
-
|
|
46318
|
+
}
|
|
46319
|
+
const acknowledgedPage = changes['page'] &&
|
|
46320
|
+
Object.keys(changes).length === 1 &&
|
|
46321
|
+
this.isAcknowledgedCanonicalPage(incomingPage);
|
|
46089
46322
|
if (!acknowledgedPage && (changes['page'] || changes['context'] || changes['pageIdentity']
|
|
46090
46323
|
|| changes['componentInstanceId'] || changes['enableCustomization'] || changes['authoringCapabilities'])) {
|
|
46091
46324
|
this.settingsIdentityRevision++;
|
|
@@ -46112,6 +46345,8 @@ class DynamicWidgetPageComponent {
|
|
|
46112
46345
|
if (changes['page'] ||
|
|
46113
46346
|
changes['context'] ||
|
|
46114
46347
|
changes['pageIdentity']) {
|
|
46348
|
+
const previousPageDefinition = this.pageDefinition;
|
|
46349
|
+
const previousPageRuntime = this.pageRuntime;
|
|
46115
46350
|
const parsed = this.parsePage(this.page);
|
|
46116
46351
|
if (changes['page'] || !this.authoredPageDefinition) {
|
|
46117
46352
|
this.authoredPageDefinition = undefined;
|
|
@@ -46122,7 +46357,17 @@ class DynamicWidgetPageComponent {
|
|
|
46122
46357
|
})
|
|
46123
46358
|
: undefined;
|
|
46124
46359
|
}
|
|
46125
|
-
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;
|
|
46126
46371
|
this.assertNoLegacyConnections(resolvedPage);
|
|
46127
46372
|
this.widgetDiagnostics = {};
|
|
46128
46373
|
this.widgetDiagnosticsChange.emit({});
|
|
@@ -46142,6 +46387,10 @@ class DynamicWidgetPageComponent {
|
|
|
46142
46387
|
}
|
|
46143
46388
|
this.pageRuntime = this.buildStateRuntime(this.pageDefinition?.state, this.pageDefinition?.context);
|
|
46144
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
|
+
}
|
|
46145
46394
|
const bootstrapSnapshot = this.bootstrapCompositionAdapter(this.pageDefinition);
|
|
46146
46395
|
const rawWidgets = this.pageDefinition?.widgets || [];
|
|
46147
46396
|
let widgets = this.applyEditShellActions(rawWidgets);
|
|
@@ -48093,6 +48342,10 @@ class DynamicWidgetPageComponent {
|
|
|
48093
48342
|
return session;
|
|
48094
48343
|
}
|
|
48095
48344
|
releaseSettingsSession(session) {
|
|
48345
|
+
if (this.activeSettingsPanelSession === session) {
|
|
48346
|
+
this.activeSettingsPanelSession = undefined;
|
|
48347
|
+
this.activeSettingsFocusReturnTarget = null;
|
|
48348
|
+
}
|
|
48096
48349
|
this.settingsSessions.delete(session);
|
|
48097
48350
|
if (!session.destroyed)
|
|
48098
48351
|
session.destroy();
|
|
@@ -48103,6 +48356,7 @@ class DynamicWidgetPageComponent {
|
|
|
48103
48356
|
this.releaseSettingsSession(session);
|
|
48104
48357
|
}
|
|
48105
48358
|
if (key === undefined) {
|
|
48359
|
+
this.pendingContextNavigationFocusTarget = undefined;
|
|
48106
48360
|
this.configEditorContextRequests.clear();
|
|
48107
48361
|
this.pendingWidgetComponentSettings.set(new Set());
|
|
48108
48362
|
}
|
|
@@ -48111,10 +48365,102 @@ class DynamicWidgetPageComponent {
|
|
|
48111
48365
|
this.setWidgetComponentSettingsPending(key, false);
|
|
48112
48366
|
}
|
|
48113
48367
|
}
|
|
48368
|
+
widgetSettingsSelectTab;
|
|
48369
|
+
buildWidgetSettingsContextNavigation(key, activeMode) {
|
|
48370
|
+
const page = this.ensurePageDefinition();
|
|
48371
|
+
const targets = (page.widgets || []).map((widget) => ({
|
|
48372
|
+
id: widget.key,
|
|
48373
|
+
label: this.widgetContextLabel(widget),
|
|
48374
|
+
}));
|
|
48375
|
+
const canContent = this.canOpenWidgetComponentSettings(key);
|
|
48376
|
+
const canWidget = this.canOpenWidgetShellSettings();
|
|
48377
|
+
const canSize = !!this.resolveSizeEditorTarget(key) ||
|
|
48378
|
+
(this.canCustomizeCanvas() && this.isCanvasMode() && !!this.currentCanvasItem(key));
|
|
48379
|
+
const nav = {
|
|
48380
|
+
targets,
|
|
48381
|
+
activeTargetId: key,
|
|
48382
|
+
modes: [
|
|
48383
|
+
{
|
|
48384
|
+
id: 'content',
|
|
48385
|
+
label: this.componentSettingsLabel(),
|
|
48386
|
+
icon: 'tune',
|
|
48387
|
+
disabled: !canContent,
|
|
48388
|
+
},
|
|
48389
|
+
{
|
|
48390
|
+
id: 'widget',
|
|
48391
|
+
label: this.widgetSettingsLabel(),
|
|
48392
|
+
icon: 'settings',
|
|
48393
|
+
disabled: !canWidget,
|
|
48394
|
+
editorGroup: 'widget-shell',
|
|
48395
|
+
},
|
|
48396
|
+
{
|
|
48397
|
+
id: 'size',
|
|
48398
|
+
label: this.sizeEditorLabel(),
|
|
48399
|
+
icon: 'straighten',
|
|
48400
|
+
disabled: !canSize,
|
|
48401
|
+
editorGroup: 'widget-shell',
|
|
48402
|
+
},
|
|
48403
|
+
],
|
|
48404
|
+
activeModeId: activeMode,
|
|
48405
|
+
onSelectTarget: (targetKey) => {
|
|
48406
|
+
if (!targetKey || targetKey === key || this.destroyed)
|
|
48407
|
+
return;
|
|
48408
|
+
const currentMode = nav.activeModeId || activeMode;
|
|
48409
|
+
this.navigateToWidgetSettingsContext(targetKey, currentMode);
|
|
48410
|
+
},
|
|
48411
|
+
onSelectMode: (modeId) => {
|
|
48412
|
+
if (!modeId || modeId === nav.activeModeId || this.destroyed)
|
|
48413
|
+
return;
|
|
48414
|
+
if ((modeId === 'widget' || modeId === 'size') &&
|
|
48415
|
+
this.widgetSettingsKey === key &&
|
|
48416
|
+
this.widgetSettingsSession &&
|
|
48417
|
+
!this.widgetSettingsSession.destroyed &&
|
|
48418
|
+
this.widgetSettingsSelectTab) {
|
|
48419
|
+
nav.activeModeId = modeId;
|
|
48420
|
+
this.widgetSettingsSelectTab(modeId);
|
|
48421
|
+
return;
|
|
48422
|
+
}
|
|
48423
|
+
if (modeId === 'content' || modeId === 'widget' || modeId === 'size') {
|
|
48424
|
+
this.navigateToWidgetSettingsContext(key, modeId);
|
|
48425
|
+
}
|
|
48426
|
+
},
|
|
48427
|
+
};
|
|
48428
|
+
return nav;
|
|
48429
|
+
}
|
|
48430
|
+
navigateToWidgetSettingsContext(targetKey, requestedMode) {
|
|
48431
|
+
const canContent = this.canOpenWidgetComponentSettings(targetKey);
|
|
48432
|
+
const canWidget = this.canOpenWidgetShellSettings();
|
|
48433
|
+
const canSize = !!this.resolveSizeEditorTarget(targetKey) ||
|
|
48434
|
+
(this.canCustomizeCanvas() && this.isCanvasMode() && !!this.currentCanvasItem(targetKey));
|
|
48435
|
+
let mode = requestedMode;
|
|
48436
|
+
if (mode === 'content' && !canContent) {
|
|
48437
|
+
mode = canWidget ? 'widget' : canSize ? 'size' : 'content';
|
|
48438
|
+
}
|
|
48439
|
+
else if (mode === 'widget' && !canWidget) {
|
|
48440
|
+
mode = canSize ? 'size' : canContent ? 'content' : 'widget';
|
|
48441
|
+
}
|
|
48442
|
+
else if (mode === 'size' && !canSize) {
|
|
48443
|
+
mode = canWidget ? 'widget' : canContent ? 'content' : 'size';
|
|
48444
|
+
}
|
|
48445
|
+
this.pendingContextNavigationFocusTarget = {
|
|
48446
|
+
target: this.activeSettingsFocusReturnTarget,
|
|
48447
|
+
};
|
|
48448
|
+
if (mode === 'content') {
|
|
48449
|
+
void this.openWidgetComponentSettings(targetKey);
|
|
48450
|
+
}
|
|
48451
|
+
else {
|
|
48452
|
+
this.openWidgetSettingsPanel(targetKey, mode);
|
|
48453
|
+
}
|
|
48454
|
+
}
|
|
48455
|
+
resolveSettingsFocusReturnTarget() {
|
|
48456
|
+
const pending = this.pendingContextNavigationFocusTarget;
|
|
48457
|
+
this.pendingContextNavigationFocusTarget = undefined;
|
|
48458
|
+
return pending ? pending.target : this.captureFocusReturnTarget();
|
|
48459
|
+
}
|
|
48114
48460
|
openWidgetSettingsPanel(key, initialTab) {
|
|
48115
48461
|
if (this.destroyed || !this.settingsPanel)
|
|
48116
48462
|
return;
|
|
48117
|
-
const focusReturnTarget = this.
|
|
48463
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48118
48464
|
const page = this.ensurePageDefinition();
|
|
48119
48465
|
const widget = page.widgets.find(item => item.key === key);
|
|
48120
48466
|
if (!widget)
|
|
@@ -48124,6 +48470,11 @@ class DynamicWidgetPageComponent {
|
|
|
48124
48470
|
return;
|
|
48125
48471
|
const session = this.createSettingsSession(key);
|
|
48126
48472
|
const owner = session.get(DestroyRef);
|
|
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;
|
|
48127
48478
|
let ref;
|
|
48128
48479
|
try {
|
|
48129
48480
|
ref = this.settingsPanel.open({
|
|
@@ -48131,14 +48482,19 @@ class DynamicWidgetPageComponent {
|
|
|
48131
48482
|
id: `dynamic-page-widget:${key}`, title: this.t('controls.cardSettings', 'Configurar card'),
|
|
48132
48483
|
previewTarget: () => Array.from(this.pageCanvasHost?.nativeElement.children || [])
|
|
48133
48484
|
.find(element => element.getAttribute('data-widget-key') === key) ?? null,
|
|
48485
|
+
contextNavigation,
|
|
48134
48486
|
content: { component: WidgetSettingsEditorComponent, inputs: () => {
|
|
48135
48487
|
const page = this.ensurePageDefinition();
|
|
48136
48488
|
const widget = page.widgets.find(item => item.key === key);
|
|
48137
48489
|
const uiShell = this.mergedContext['ui']?.shell || {};
|
|
48490
|
+
this.activeSettingsPanelSession = session;
|
|
48491
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48138
48492
|
this.closeSizeEditor(false);
|
|
48493
|
+
this.selectWidget(key);
|
|
48139
48494
|
this.prepareSizeEditor(key);
|
|
48140
48495
|
this.widgetSettingsKey = key;
|
|
48141
48496
|
this.widgetSettingsSession = session;
|
|
48497
|
+
this.widgetSettingsSelectTab = undefined;
|
|
48142
48498
|
const hasSize = this.sizeEditorSeed()?.key === key;
|
|
48143
48499
|
return {
|
|
48144
48500
|
shellComponent,
|
|
@@ -48153,11 +48509,21 @@ class DynamicWidgetPageComponent {
|
|
|
48153
48509
|
appliedValues$: this.widgetSettingsAppliedValues.asObservable(),
|
|
48154
48510
|
widgetChanged: (value, state) => this.previewSettingsShell(key, value, state),
|
|
48155
48511
|
validate: (value) => this.validateWidgetSettings(key, value),
|
|
48512
|
+
sectionChanged: (sectionId) => {
|
|
48513
|
+
contextNavigation.activeModeId = sectionId === 'size' ? 'size' : 'widget';
|
|
48514
|
+
},
|
|
48515
|
+
registerTabController: (selectTab) => {
|
|
48516
|
+
this.widgetSettingsSelectTab = selectTab;
|
|
48517
|
+
},
|
|
48156
48518
|
};
|
|
48157
48519
|
} },
|
|
48158
48520
|
});
|
|
48159
48521
|
}
|
|
48160
48522
|
catch (error) {
|
|
48523
|
+
if (this.activeSettingsPanelSession === session) {
|
|
48524
|
+
this.activeSettingsPanelSession = previousActiveSession;
|
|
48525
|
+
this.activeSettingsFocusReturnTarget = previousFocusTarget;
|
|
48526
|
+
}
|
|
48161
48527
|
if (this.widgetSettingsSession === session)
|
|
48162
48528
|
this.closeSizeEditor(false);
|
|
48163
48529
|
else
|
|
@@ -48167,13 +48533,27 @@ class DynamicWidgetPageComponent {
|
|
|
48167
48533
|
ref.applied$.pipe(takeUntilDestroyed(owner)).subscribe(value => this.commitWidgetSettings(key, value, false));
|
|
48168
48534
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe(value => this.commitWidgetSettings(key, value, true));
|
|
48169
48535
|
ref.closed$?.pipe(take(1), takeUntilDestroyed(owner)).subscribe(() => {
|
|
48170
|
-
const
|
|
48171
|
-
|
|
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) {
|
|
48548
|
+
this.widgetSettingsSelectTab = undefined;
|
|
48172
48549
|
this.closeSizeEditor(false);
|
|
48173
|
-
|
|
48550
|
+
}
|
|
48551
|
+
else {
|
|
48174
48552
|
this.releaseSettingsSession(session);
|
|
48175
|
-
|
|
48553
|
+
}
|
|
48554
|
+
if (wasActivePanelSession) {
|
|
48176
48555
|
this.restoreFocus(focusReturnTarget);
|
|
48556
|
+
}
|
|
48177
48557
|
});
|
|
48178
48558
|
}
|
|
48179
48559
|
sizeEditorLayoutItems() {
|
|
@@ -48310,6 +48690,7 @@ class DynamicWidgetPageComponent {
|
|
|
48310
48690
|
} });
|
|
48311
48691
|
}
|
|
48312
48692
|
async openWidgetComponentSettings(key) {
|
|
48693
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48313
48694
|
if (this.destroyed || !this.canOpenWidgetComponentSettings(key))
|
|
48314
48695
|
return;
|
|
48315
48696
|
if (!this.settingsPanel) {
|
|
@@ -48331,7 +48712,6 @@ class DynamicWidgetPageComponent {
|
|
|
48331
48712
|
const materializedInputs = metadata.configEditor?.contextResolver
|
|
48332
48713
|
? this.readMaterializedInputsForWidget(metadata, key)
|
|
48333
48714
|
: undefined;
|
|
48334
|
-
const focusReturnTarget = this.captureFocusReturnTarget();
|
|
48335
48715
|
const session = this.createSettingsSession(key);
|
|
48336
48716
|
const owner = session.get(DestroyRef);
|
|
48337
48717
|
let opened = false;
|
|
@@ -48350,12 +48730,22 @@ class DynamicWidgetPageComponent {
|
|
|
48350
48730
|
}
|
|
48351
48731
|
const appliedValues = new Subject();
|
|
48352
48732
|
owner.onDestroy(() => appliedValues.complete());
|
|
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);
|
|
48353
48740
|
const ref = this.settingsPanel.open({
|
|
48354
48741
|
owner,
|
|
48355
48742
|
id: `dynamic-page-component:${key}`,
|
|
48356
48743
|
title: metadata.configEditor?.title ||
|
|
48357
48744
|
metadata.friendlyName ||
|
|
48358
48745
|
this.t('controls.componentSettings', 'Configurar conteudo'),
|
|
48746
|
+
previewTarget: () => Array.from(this.pageCanvasHost?.nativeElement.children || [])
|
|
48747
|
+
.find(element => element.getAttribute('data-widget-key') === key) ?? null,
|
|
48748
|
+
contextNavigation,
|
|
48359
48749
|
content: {
|
|
48360
48750
|
component: editor,
|
|
48361
48751
|
inputs: {
|
|
@@ -48376,8 +48766,22 @@ class DynamicWidgetPageComponent {
|
|
|
48376
48766
|
});
|
|
48377
48767
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe((result) => this.applyWidgetComponentInputs(key, result, true));
|
|
48378
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
|
+
}
|
|
48379
48781
|
this.releaseSettingsSession(session);
|
|
48380
|
-
|
|
48782
|
+
if (wasActivePanelSession) {
|
|
48783
|
+
this.restoreFocus(focusReturnTarget);
|
|
48784
|
+
}
|
|
48381
48785
|
});
|
|
48382
48786
|
}
|
|
48383
48787
|
finally {
|
|
@@ -48413,6 +48817,8 @@ class DynamicWidgetPageComponent {
|
|
|
48413
48817
|
restoreFocus(target) {
|
|
48414
48818
|
queueMicrotask(() => {
|
|
48415
48819
|
if (!this.destroyed
|
|
48820
|
+
&& !this.activeSettingsPanelSession
|
|
48821
|
+
&& this.pendingWidgetComponentSettings().size === 0
|
|
48416
48822
|
&& target?.isConnected
|
|
48417
48823
|
&& !target.hasAttribute('disabled')
|
|
48418
48824
|
&& target.getAttribute('aria-disabled') !== 'true') {
|
|
@@ -48722,6 +49128,7 @@ class DynamicWidgetPageComponent {
|
|
|
48722
49128
|
return (endpoint.kind === 'component-port' && endpoint.ref.widget === widgetKey);
|
|
48723
49129
|
}
|
|
48724
49130
|
openPageSettings() {
|
|
49131
|
+
const focusReturnTarget = this.resolveSettingsFocusReturnTarget();
|
|
48725
49132
|
if (this.destroyed || !this.canInvokePageSettings())
|
|
48726
49133
|
return;
|
|
48727
49134
|
if (!this.settingsPanel)
|
|
@@ -48736,6 +49143,10 @@ class DynamicWidgetPageComponent {
|
|
|
48736
49143
|
const owner = session.get(DestroyRef);
|
|
48737
49144
|
const appliedValues = new Subject();
|
|
48738
49145
|
owner.onDestroy(() => appliedValues.complete());
|
|
49146
|
+
const previousActiveSession = this.activeSettingsPanelSession;
|
|
49147
|
+
const previousFocusTarget = this.activeSettingsFocusReturnTarget;
|
|
49148
|
+
this.activeSettingsPanelSession = session;
|
|
49149
|
+
this.activeSettingsFocusReturnTarget = focusReturnTarget;
|
|
48739
49150
|
const ref = this.settingsPanel.open({
|
|
48740
49151
|
owner,
|
|
48741
49152
|
id: 'dynamic-page-settings',
|
|
@@ -48765,7 +49176,21 @@ class DynamicWidgetPageComponent {
|
|
|
48765
49176
|
appliedValues.next(accepted);
|
|
48766
49177
|
});
|
|
48767
49178
|
ref.saved$.pipe(takeUntilDestroyed(owner)).subscribe((result) => this.applyPageLayout(result, true));
|
|
48768
|
-
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
|
+
});
|
|
48769
49194
|
}
|
|
48770
49195
|
canInvokePageSettings() {
|
|
48771
49196
|
return this.isAuthoringCapabilityEnabled('pageSettings')
|
|
@@ -48898,11 +49323,227 @@ class DynamicWidgetPageComponent {
|
|
|
48898
49323
|
}, persist);
|
|
48899
49324
|
}
|
|
48900
49325
|
applyAuthoringPageUpdate(next, persist = true, runtime, syncCompositionAdapter = true, skipStateWidgetProjection = false, emitUnchangedCanonicalPage = true) {
|
|
48901
|
-
|
|
49326
|
+
const runtimeSeed = !runtime && !this.isHydrating
|
|
49327
|
+
? (this.preserveEphemeralRuntimeState(next) ?? next)
|
|
49328
|
+
: next;
|
|
49329
|
+
this.applyPageUpdate(runtimeSeed, persist, runtime, syncCompositionAdapter, skipStateWidgetProjection, emitUnchangedCanonicalPage);
|
|
48902
49330
|
if (this.authoredPageDefinition) {
|
|
48903
49331
|
this.pageAuthoringChange.emit(this.clonePageDefinition(this.authoredPageDefinition));
|
|
48904
49332
|
}
|
|
48905
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
|
+
}
|
|
48906
49547
|
applyPageUpdate(next, persist = true, runtime, syncCompositionAdapter = true, skipStateWidgetProjection = false, emitUnchangedCanonicalPage = true) {
|
|
48907
49548
|
this.assertNoLegacyConnections(next);
|
|
48908
49549
|
if (this.getDataSourcesSignature(next.dataSources) !==
|
|
@@ -48927,7 +49568,7 @@ class DynamicWidgetPageComponent {
|
|
|
48927
49568
|
state: {
|
|
48928
49569
|
primaryValues: this.cloneStateValues(effectiveRuntime.primaryValues),
|
|
48929
49570
|
derivedValues: this.cloneStateValues(effectiveRuntime.derivedValues),
|
|
48930
|
-
transientValues:
|
|
49571
|
+
transientValues: {},
|
|
48931
49572
|
changedPaths: [],
|
|
48932
49573
|
diagnostics: [],
|
|
48933
49574
|
},
|
|
@@ -49011,7 +49652,7 @@ class DynamicWidgetPageComponent {
|
|
|
49011
49652
|
state: {
|
|
49012
49653
|
primaryValues: this.cloneStateValues(nextRuntime.primaryValues),
|
|
49013
49654
|
derivedValues: this.cloneStateValues(nextRuntime.derivedValues),
|
|
49014
|
-
transientValues:
|
|
49655
|
+
transientValues: {},
|
|
49015
49656
|
changedPaths: updates.map((u) => u.targetStatePath),
|
|
49016
49657
|
diagnostics: [],
|
|
49017
49658
|
},
|
|
@@ -52085,6 +52726,7 @@ class DynamicWidgetPageComponent {
|
|
|
52085
52726
|
dataSources: stored.dataSources ?? baseline.dataSources,
|
|
52086
52727
|
}
|
|
52087
52728
|
: (stored ?? defaultPage);
|
|
52729
|
+
this.clearMountedSourceSelectionsForNonPersistentState([loaded]);
|
|
52088
52730
|
this.authoredPageDefinition = this.clonePageDefinition(loaded);
|
|
52089
52731
|
this.applyPageUpdate(loaded, false);
|
|
52090
52732
|
}
|
|
@@ -52159,6 +52801,7 @@ class DynamicWidgetPageComponent {
|
|
|
52159
52801
|
if (mutation.revision === this.persistenceRevision) {
|
|
52160
52802
|
this.isHydrating = true;
|
|
52161
52803
|
try {
|
|
52804
|
+
this.clearMountedSourceSelectionsForNonPersistentState([mutation.defaultPage]);
|
|
52162
52805
|
this.authoredPageDefinition = this.clonePageDefinition(mutation.defaultPage);
|
|
52163
52806
|
this.applyPageUpdate(mutation.defaultPage, false);
|
|
52164
52807
|
}
|
|
@@ -53544,7 +54187,42 @@ class PraxisSurfaceHostComponent {
|
|
|
53544
54187
|
loader.renderNow();
|
|
53545
54188
|
}
|
|
53546
54189
|
enrichWidget(widget, widgetKey) {
|
|
53547
|
-
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') {
|
|
53548
54226
|
return widget;
|
|
53549
54227
|
}
|
|
53550
54228
|
return {
|
|
@@ -56193,7 +56871,7 @@ class ResourceQuickConnectComponent {
|
|
|
56193
56871
|
this.catalogError = false;
|
|
56194
56872
|
this.cdr?.markForCheck();
|
|
56195
56873
|
try {
|
|
56196
|
-
const catalog = await firstValueFrom(this.discovery.getSchemaCatalog().pipe(timeout(30000)));
|
|
56874
|
+
const catalog = await firstValueFrom(this.discovery.getSchemaCatalog().pipe(timeout$1(30000)));
|
|
56197
56875
|
const resources = new Map();
|
|
56198
56876
|
for (const endpoint of catalog.endpoints ?? []) {
|
|
56199
56877
|
// Ground only declared resource-level capability endpoints; never infer user intent.
|
|
@@ -56807,12 +57485,13 @@ function reconcileFormConfig(layout, serverDefs) {
|
|
|
56807
57485
|
}
|
|
56808
57486
|
}
|
|
56809
57487
|
// Orphans: preserve explicit local fields; hide legacy schema fields that no
|
|
56810
|
-
// longer exist on the server.
|
|
57488
|
+
// longer exist on the server. Retain their authored metadata/layout, but do
|
|
57489
|
+
// not submit or validate an invisible control outside the current schema.
|
|
56811
57490
|
for (const [name, localField] of localByName) {
|
|
56812
57491
|
if (!serverByName.has(name)) {
|
|
56813
57492
|
merged.push(isLocalField(localField)
|
|
56814
57493
|
? localField
|
|
56815
|
-
: { ...localField, formHidden: true });
|
|
57494
|
+
: { ...localField, formHidden: true, disabled: true, submitPolicy: 'omit' });
|
|
56816
57495
|
}
|
|
56817
57496
|
}
|
|
56818
57497
|
// Preserve layout (sections/rows/columns) order, only affect fieldMetadata
|
|
@@ -57526,6 +58205,7 @@ class PraxisAppShellComponent {
|
|
|
57526
58205
|
}
|
|
57527
58206
|
showSearch = true;
|
|
57528
58207
|
searchPlaceholder = 'Buscar no sistema ou executar comando...';
|
|
58208
|
+
clearSearchOnBlur = false;
|
|
57529
58209
|
_footerStatus = signal(undefined, ...(ngDevMode ? [{ debugName: "_footerStatus" }] : /* istanbul ignore next */ []));
|
|
57530
58210
|
set footerStatus(val) {
|
|
57531
58211
|
this._footerStatus.set(val);
|
|
@@ -57589,11 +58269,15 @@ class PraxisAppShellComponent {
|
|
|
57589
58269
|
this.searchQuery.set(value);
|
|
57590
58270
|
this.searchChange.emit(value);
|
|
57591
58271
|
}
|
|
58272
|
+
onSearchBlur() {
|
|
58273
|
+
if (this.clearSearchOnBlur) {
|
|
58274
|
+
this.searchQuery.set('');
|
|
58275
|
+
}
|
|
58276
|
+
}
|
|
57592
58277
|
onSearchEnter() {
|
|
57593
58278
|
const query = this.searchQuery().trim();
|
|
57594
|
-
|
|
57595
|
-
|
|
57596
|
-
}
|
|
58279
|
+
this.searchSubmit.emit(query);
|
|
58280
|
+
this.searchQuery.set('');
|
|
57597
58281
|
}
|
|
57598
58282
|
onUserClick() {
|
|
57599
58283
|
if (this.user) {
|
|
@@ -57615,7 +58299,7 @@ class PraxisAppShellComponent {
|
|
|
57615
58299
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
57616
58300
|
}
|
|
57617
58301
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisAppShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
57618
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisAppShellComponent, isStandalone: true, selector: "praxis-app-shell", inputs: { brand: "brand", navigation: "navigation", user: "user", showThemeToggle: "showThemeToggle", showCustomizationToggle: "showCustomizationToggle", isDarkTheme: "isDarkTheme", showSearch: "showSearch", searchPlaceholder: "searchPlaceholder", footerStatus: "footerStatus", isAnyLoading: "isAnyLoading", collapsed: "collapsed" }, outputs: { themeToggle: "themeToggle", customizationToggle: "customizationToggle", searchSubmit: "searchSubmit", searchChange: "searchChange", userProfileClick: "userProfileClick", collapsedChange: "collapsedChange", navItemClick: "navItemClick" }, host: { listeners: { "keydown.escape": "handleEscape()" } }, ngImport: i0, template: `
|
|
58302
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisAppShellComponent, isStandalone: true, selector: "praxis-app-shell", inputs: { brand: "brand", navigation: "navigation", user: "user", showThemeToggle: "showThemeToggle", showCustomizationToggle: "showCustomizationToggle", isDarkTheme: "isDarkTheme", showSearch: "showSearch", searchPlaceholder: "searchPlaceholder", clearSearchOnBlur: "clearSearchOnBlur", footerStatus: "footerStatus", isAnyLoading: "isAnyLoading", collapsed: "collapsed" }, outputs: { themeToggle: "themeToggle", customizationToggle: "customizationToggle", searchSubmit: "searchSubmit", searchChange: "searchChange", userProfileClick: "userProfileClick", collapsedChange: "collapsedChange", navItemClick: "navItemClick" }, host: { listeners: { "keydown.escape": "handleEscape()" } }, ngImport: i0, template: `
|
|
57619
58303
|
<div
|
|
57620
58304
|
class="praxis-shell"
|
|
57621
58305
|
[class.praxis-shell--dark]="isDarkTheme"
|
|
@@ -57812,6 +58496,7 @@ class PraxisAppShellComponent {
|
|
|
57812
58496
|
[value]="searchQuery()"
|
|
57813
58497
|
(input)="onSearchInput($any($event.target).value)"
|
|
57814
58498
|
(keydown.enter)="onSearchEnter()"
|
|
58499
|
+
(blur)="onSearchBlur()"
|
|
57815
58500
|
aria-label="Buscar no sistema"
|
|
57816
58501
|
/>
|
|
57817
58502
|
</div>
|
|
@@ -58154,6 +58839,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
58154
58839
|
[value]="searchQuery()"
|
|
58155
58840
|
(input)="onSearchInput($any($event.target).value)"
|
|
58156
58841
|
(keydown.enter)="onSearchEnter()"
|
|
58842
|
+
(blur)="onSearchBlur()"
|
|
58157
58843
|
aria-label="Buscar no sistema"
|
|
58158
58844
|
/>
|
|
58159
58845
|
</div>
|
|
@@ -58312,6 +58998,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
58312
58998
|
type: Input
|
|
58313
58999
|
}], searchPlaceholder: [{
|
|
58314
59000
|
type: Input
|
|
59001
|
+
}], clearSearchOnBlur: [{
|
|
59002
|
+
type: Input
|
|
58315
59003
|
}], footerStatus: [{
|
|
58316
59004
|
type: Input
|
|
58317
59005
|
}], isAnyLoading: [{
|