@praxisui/page-builder 10.0.0-rc.13 → 10.0.0-rc.14

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-10-14T09:51:43.880Z",
3
+ "generatedAt": "2026-10-14T11:31:33.532Z",
4
4
  "packageName": "@praxisui/page-builder",
5
- "packageVersion": "10.0.0-rc.13",
5
+ "packageVersion": "10.0.0-rc.14",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 1,
@@ -14567,11 +14567,17 @@ const DERIVED_OPERATORS = ['merge-objects', 'pick', 'omit'];
14567
14567
  const MERGE_STRATEGIES = ['replace', 'merge', 'append', 'remove-keys'];
14568
14568
  const VALUE_MODES = ['json', 'string', 'number', 'boolean', 'null'];
14569
14569
  const GROUPING_KINDS = ['section', 'tabs', 'hero', 'rail'];
14570
+ const DATA_SOURCE_KINDS = ['remote-resource', 'stats-query', 'raw-http'];
14571
+ const DATA_SOURCE_METHODS = ['GET', 'POST'];
14572
+ const DATA_SOURCE_ERROR_POLICIES = ['fallback', 'silent', 'propagate'];
14570
14573
  class PageConfigEditorComponent {
14571
14574
  derivedOperators = DERIVED_OPERATORS;
14572
14575
  mergeStrategies = MERGE_STRATEGIES;
14573
14576
  valueModes = VALUE_MODES;
14574
14577
  groupingKinds = GROUPING_KINDS;
14578
+ dataSourceKinds = DATA_SOURCE_KINDS;
14579
+ dataSourceMethods = DATA_SOURCE_METHODS;
14580
+ dataSourceErrorPolicies = DATA_SOURCE_ERROR_POLICIES;
14575
14581
  layoutPresetEntries = Object.values(BUILTIN_PAGE_LAYOUT_PRESETS);
14576
14582
  themePresetEntries = Object.values(BUILTIN_PAGE_THEME_PRESETS);
14577
14583
  page = input(null, ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
@@ -14595,6 +14601,7 @@ class PageConfigEditorComponent {
14595
14601
  groupingNodes = new FormArray([]);
14596
14602
  schemaNodes = new FormArray([]);
14597
14603
  derivedNodes = new FormArray([]);
14604
+ dataSourceNodes = new FormArray([]);
14598
14605
  deviceDesktopForm = this.createDeviceLayoutForm();
14599
14606
  deviceTabletForm = this.createDeviceLayoutForm();
14600
14607
  deviceMobileForm = this.createDeviceLayoutForm();
@@ -14789,6 +14796,14 @@ class PageConfigEditorComponent {
14789
14796
  .pipe(takeUntilDestroyed())
14790
14797
  .subscribe(() => {
14791
14798
  this.stateError.set(null);
14799
+ this.syncDataSourceNodesFromControl();
14800
+ this.refreshPanelState();
14801
+ });
14802
+ this.dataSourceNodes.valueChanges
14803
+ .pipe(takeUntilDestroyed())
14804
+ .subscribe(() => {
14805
+ this.stateError.set(null);
14806
+ this.syncDataSourcesControlFromNodes();
14792
14807
  this.refreshPanelState();
14793
14808
  });
14794
14809
  this.schemaNodes.valueChanges
@@ -14825,6 +14840,12 @@ class PageConfigEditorComponent {
14825
14840
  removeDerivedNode(index) {
14826
14841
  this.derivedNodes.removeAt(index);
14827
14842
  }
14843
+ addDataSourceNode(seed) {
14844
+ this.dataSourceNodes.push(this.createDataSourceNodeGroup(seed));
14845
+ }
14846
+ removeDataSourceNode(index) {
14847
+ this.dataSourceNodes.removeAt(index);
14848
+ }
14828
14849
  addGroupingNode(seed) {
14829
14850
  this.groupingNodes.push(this.createGroupingNodeGroup(seed));
14830
14851
  }
@@ -15049,19 +15070,143 @@ class PageConfigEditorComponent {
15049
15070
  this.replaceDerivedNodes(normalized.derived);
15050
15071
  }
15051
15072
  hydrateDataSourcesEditor(dataSources) {
15073
+ const normalized = this.normalizeDataSourcesArray(dataSources);
15052
15074
  const hasEntries = Array.isArray(dataSources)
15053
15075
  ? dataSources.length > 0
15054
15076
  : !!(dataSources && Object.keys(dataSources).length);
15055
15077
  const nextJson = hasEntries
15056
15078
  ? JSON.stringify(dataSources, null, 2)
15057
15079
  : '';
15058
- if (nextJson !== this.lastDataSources) {
15059
- this.lastDataSources = nextJson;
15060
- this.dataSourcesControl.setValue(nextJson, { emitEvent: false });
15061
- this.stateError.set(null);
15080
+ this.lastDataSources = nextJson;
15081
+ this.dataSourcesControl.setValue(nextJson, { emitEvent: false });
15082
+ this.replaceDataSourceNodes(normalized);
15083
+ this.stateError.set(null);
15084
+ }
15085
+ normalizeDataSourcesArray(dataSources) {
15086
+ if (!dataSources)
15087
+ return [];
15088
+ if (Array.isArray(dataSources))
15089
+ return dataSources;
15090
+ if (typeof dataSources === 'object') {
15091
+ return Object.entries(dataSources).map(([id, def]) => ({
15092
+ ...def,
15093
+ id: def?.id || id,
15094
+ }));
15095
+ }
15096
+ return [];
15097
+ }
15098
+ replaceDataSourceNodes(dataSources) {
15099
+ this.dataSourceNodes.clear({ emitEvent: false });
15100
+ for (const item of dataSources || []) {
15101
+ this.dataSourceNodes.push(this.createDataSourceNodeGroup(item), { emitEvent: false });
15102
+ }
15103
+ }
15104
+ createDataSourceNodeGroup(seed) {
15105
+ return new FormGroup({
15106
+ id: new FormControl(seed?.id || '', { nonNullable: true, validators: [Validators.required] }),
15107
+ kind: new FormControl(seed?.kind || '', { nonNullable: true }),
15108
+ resourcePath: new FormControl(seed?.resourcePath || '', { nonNullable: true, validators: [Validators.required] }),
15109
+ method: new FormControl(seed?.method || '', { nonNullable: true }),
15110
+ targetStatePath: new FormControl(seed?.targetStatePath || '', { nonNullable: true }),
15111
+ errorPolicy: new FormControl(seed?.errorPolicy || '', { nonNullable: true }),
15112
+ refreshIntervalMs: new FormControl(seed?.refreshIntervalMs ?? null),
15113
+ autoFetch: new FormControl(typeof seed?.autoFetch === 'boolean' ? String(seed.autoFetch) : '', { nonNullable: true }),
15114
+ queryJson: new FormControl(seed?.query !== undefined ? JSON.stringify(seed.query, null, 2) : '', { nonNullable: true }),
15115
+ transformJson: new FormControl(seed?.transform !== undefined ? JSON.stringify(seed.transform, null, 2) : '', { nonNullable: true }),
15116
+ fallbackJson: new FormControl(seed?.fallbackValue !== undefined ? JSON.stringify(seed.fallbackValue, null, 2) : '', { nonNullable: true }),
15117
+ description: new FormControl(seed?.description || '', { nonNullable: true }),
15118
+ });
15119
+ }
15120
+ syncDataSourceNodesFromControl() {
15121
+ const raw = this.dataSourcesControl.value.trim();
15122
+ if (!raw) {
15123
+ this.replaceDataSourceNodes([]);
15124
+ return;
15062
15125
  }
15126
+ const parsed = this.parseJson(raw, null);
15127
+ if (parsed && typeof parsed === 'object') {
15128
+ this.replaceDataSourceNodes(this.normalizeDataSourcesArray(parsed));
15129
+ }
15130
+ }
15131
+ syncDataSourcesControlFromNodes() {
15132
+ const built = this.buildDataSourcesFromNodes();
15133
+ if (built === null)
15134
+ return;
15135
+ const json = built.length ? JSON.stringify(built, null, 2) : '';
15136
+ this.lastDataSources = json;
15137
+ this.dataSourcesControl.setValue(json, { emitEvent: false });
15138
+ }
15139
+ buildDataSourcesFromNodes() {
15140
+ const list = [];
15141
+ for (const group of this.dataSourceNodes.controls) {
15142
+ const id = group.controls.id.value.trim();
15143
+ const resourcePath = group.controls.resourcePath.value.trim();
15144
+ if (!id || !resourcePath)
15145
+ continue;
15146
+ const node = { id, resourcePath };
15147
+ const kind = group.controls.kind.value.trim();
15148
+ const method = group.controls.method.value.trim();
15149
+ const targetStatePath = group.controls.targetStatePath.value.trim();
15150
+ const errorPolicy = group.controls.errorPolicy.value.trim();
15151
+ const refreshIntervalMs = group.controls.refreshIntervalMs.value;
15152
+ const autoFetch = group.controls.autoFetch.value.trim();
15153
+ const queryRaw = group.controls.queryJson.value.trim();
15154
+ const transformRaw = group.controls.transformJson.value.trim();
15155
+ const fallbackRaw = group.controls.fallbackJson.value.trim();
15156
+ const description = group.controls.description.value.trim();
15157
+ if (kind)
15158
+ node.kind = kind;
15159
+ if (method)
15160
+ node.method = method;
15161
+ if (targetStatePath)
15162
+ node.targetStatePath = targetStatePath;
15163
+ if (errorPolicy)
15164
+ node.errorPolicy = errorPolicy;
15165
+ if (refreshIntervalMs !== null && refreshIntervalMs !== undefined && !Number.isNaN(Number(refreshIntervalMs))) {
15166
+ node.refreshIntervalMs = Number(refreshIntervalMs);
15167
+ }
15168
+ if (autoFetch === 'true')
15169
+ node.autoFetch = true;
15170
+ if (autoFetch === 'false')
15171
+ node.autoFetch = false;
15172
+ if (queryRaw) {
15173
+ const parsedQuery = this.parseJson(queryRaw, {});
15174
+ if (parsedQuery === null || typeof parsedQuery !== 'object' || Array.isArray(parsedQuery))
15175
+ return null;
15176
+ node.query = parsedQuery;
15177
+ }
15178
+ if (transformRaw) {
15179
+ const parsedTransform = this.parseJson(transformRaw, {});
15180
+ if (parsedTransform === null)
15181
+ return null;
15182
+ node.transform = parsedTransform;
15183
+ }
15184
+ if (fallbackRaw) {
15185
+ try {
15186
+ node.fallbackValue = JSON.parse(fallbackRaw);
15187
+ }
15188
+ catch {
15189
+ return null;
15190
+ }
15191
+ }
15192
+ if (description)
15193
+ node.description = description;
15194
+ list.push(node);
15195
+ }
15196
+ return list;
15063
15197
  }
15064
15198
  buildDataSources() {
15199
+ if (this.dataSourceNodes.length > 0) {
15200
+ const fromNodes = this.buildDataSourcesFromNodes();
15201
+ if (fromNodes === null)
15202
+ return null;
15203
+ if (fromNodes.length > 0) {
15204
+ const json = JSON.stringify(fromNodes, null, 2);
15205
+ this.lastDataSources = json;
15206
+ this.dataSourcesControl.setValue(json, { emitEvent: false });
15207
+ return fromNodes;
15208
+ }
15209
+ }
15065
15210
  const raw = this.dataSourcesControl.value.trim();
15066
15211
  if (!raw)
15067
15212
  return undefined;
@@ -16056,12 +16201,85 @@ class PageConfigEditorComponent {
16056
16201
  }
16057
16202
  </div>
16058
16203
  </div>
16059
- <div class="state-card">
16060
- <div class="state-card-head">
16061
- <div>
16062
- <div class="state-card-title">{{ tx('editor.state.dataSourcesTitle', 'Declarative Data Sources (JSON)') }}</div>
16063
- <div class="state-card-copy">{{ tx('editor.state.dataSourcesCopy', 'Bind HTTP or resource endpoints directly into page state paths with reactive parameters.') }}</div>
16064
- </div>
16204
+
16205
+ <div class="state-group">
16206
+ <div class="state-group-head">
16207
+ <div class="state-group-title">{{ tx('editor.state.dataSourcesTitle', 'Declarative Data Sources') }}</div>
16208
+ <button mat-stroked-button type="button" (click)="addDataSourceNode()">
16209
+ <mat-icon>add</mat-icon>
16210
+ {{ tx('editor.state.addDataSource', 'Add data source') }}
16211
+ </button>
16212
+ </div>
16213
+ <div class="state-group-help">{{ tx('editor.state.dataSourcesCopy', 'Bind HTTP or resource endpoints directly into page state paths with reactive parameters.') }}</div>
16214
+ @if (!dataSourceNodes.length) {
16215
+ <div class="state-empty">{{ tx('editor.state.emptyDataSources', 'No declarative data sources configured.') }}</div>
16216
+ }
16217
+ <div class="state-list">
16218
+ @for (group of dataSourceNodes.controls; track $index) {
16219
+ <div class="state-card" [formGroup]="group">
16220
+ <div class="state-card-head">
16221
+ <div class="state-card-title">{{ tx('editor.state.dataSourceN', 'Data source {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
16222
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeDataSourceNode($index)" [attr.aria-label]="tx('editor.state.removeDataSourceAria', 'Remove data source')"></button>
16223
+ </div>
16224
+ <div class="state-card-grid">
16225
+ <mat-form-field appearance="outline">
16226
+ <mat-label>{{ tx('editor.state.dataSourceId', 'ID') }}</mat-label>
16227
+ <input matInput formControlName="id" />
16228
+ </mat-form-field>
16229
+ <mat-form-field appearance="outline">
16230
+ <mat-label>{{ tx('editor.state.dataSourceKind', 'Kind') }}</mat-label>
16231
+ <mat-select formControlName="kind">
16232
+ <mat-option value="">{{ tx('editor.common.default', 'Default') }}</mat-option>
16233
+ @for (kind of dataSourceKinds; track kind) {
16234
+ <mat-option [value]="kind">{{ kind }}</mat-option>
16235
+ }
16236
+ </mat-select>
16237
+ </mat-form-field>
16238
+ <mat-form-field appearance="outline" class="span-2">
16239
+ <mat-label>{{ tx('editor.state.resourcePath', 'Resource path') }}</mat-label>
16240
+ <input matInput formControlName="resourcePath" />
16241
+ </mat-form-field>
16242
+ <mat-form-field appearance="outline">
16243
+ <mat-label>{{ tx('editor.state.method', 'HTTP method') }}</mat-label>
16244
+ <mat-select formControlName="method">
16245
+ <mat-option value="">{{ tx('editor.common.auto', 'Auto') }}</mat-option>
16246
+ @for (method of dataSourceMethods; track method) {
16247
+ <mat-option [value]="method">{{ method }}</mat-option>
16248
+ }
16249
+ </mat-select>
16250
+ </mat-form-field>
16251
+ <mat-form-field appearance="outline" class="span-2">
16252
+ <mat-label>{{ tx('editor.state.targetStatePath', 'Target state path') }}</mat-label>
16253
+ <input matInput formControlName="targetStatePath" />
16254
+ </mat-form-field>
16255
+ <mat-form-field appearance="outline">
16256
+ <mat-label>{{ tx('editor.state.errorPolicy', 'Error policy') }}</mat-label>
16257
+ <mat-select formControlName="errorPolicy">
16258
+ <mat-option value="">{{ tx('editor.common.default', 'Default') }}</mat-option>
16259
+ @for (policy of dataSourceErrorPolicies; track policy) {
16260
+ <mat-option [value]="policy">{{ policy }}</mat-option>
16261
+ }
16262
+ </mat-select>
16263
+ </mat-form-field>
16264
+ <mat-form-field appearance="outline" class="span-2">
16265
+ <mat-label>{{ tx('editor.state.queryJson', 'Query (JSON)') }}</mat-label>
16266
+ <textarea matInput rows="3" formControlName="queryJson"></textarea>
16267
+ </mat-form-field>
16268
+ <mat-form-field appearance="outline" class="span-2">
16269
+ <mat-label>{{ tx('editor.state.transformJson', 'Transform (JSON)') }}</mat-label>
16270
+ <textarea matInput rows="3" formControlName="transformJson"></textarea>
16271
+ </mat-form-field>
16272
+ <mat-form-field appearance="outline" class="span-2">
16273
+ <mat-label>{{ tx('editor.state.fallbackJson', 'Fallback value (JSON)') }}</mat-label>
16274
+ <textarea matInput rows="3" formControlName="fallbackJson"></textarea>
16275
+ </mat-form-field>
16276
+ <mat-form-field appearance="outline" class="span-2">
16277
+ <mat-label>{{ tx('editor.state.description', 'Description') }}</mat-label>
16278
+ <input matInput formControlName="description" />
16279
+ </mat-form-field>
16280
+ </div>
16281
+ </div>
16282
+ }
16065
16283
  </div>
16066
16284
  <mat-form-field appearance="outline" class="page-context">
16067
16285
  <mat-label>{{ tx('editor.state.dataSourcesJson', 'page.dataSources (JSON)') }}</mat-label>
@@ -16672,12 +16890,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
16672
16890
  }
16673
16891
  </div>
16674
16892
  </div>
16675
- <div class="state-card">
16676
- <div class="state-card-head">
16677
- <div>
16678
- <div class="state-card-title">{{ tx('editor.state.dataSourcesTitle', 'Declarative Data Sources (JSON)') }}</div>
16679
- <div class="state-card-copy">{{ tx('editor.state.dataSourcesCopy', 'Bind HTTP or resource endpoints directly into page state paths with reactive parameters.') }}</div>
16680
- </div>
16893
+
16894
+ <div class="state-group">
16895
+ <div class="state-group-head">
16896
+ <div class="state-group-title">{{ tx('editor.state.dataSourcesTitle', 'Declarative Data Sources') }}</div>
16897
+ <button mat-stroked-button type="button" (click)="addDataSourceNode()">
16898
+ <mat-icon>add</mat-icon>
16899
+ {{ tx('editor.state.addDataSource', 'Add data source') }}
16900
+ </button>
16901
+ </div>
16902
+ <div class="state-group-help">{{ tx('editor.state.dataSourcesCopy', 'Bind HTTP or resource endpoints directly into page state paths with reactive parameters.') }}</div>
16903
+ @if (!dataSourceNodes.length) {
16904
+ <div class="state-empty">{{ tx('editor.state.emptyDataSources', 'No declarative data sources configured.') }}</div>
16905
+ }
16906
+ <div class="state-list">
16907
+ @for (group of dataSourceNodes.controls; track $index) {
16908
+ <div class="state-card" [formGroup]="group">
16909
+ <div class="state-card-head">
16910
+ <div class="state-card-title">{{ tx('editor.state.dataSourceN', 'Data source {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
16911
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeDataSourceNode($index)" [attr.aria-label]="tx('editor.state.removeDataSourceAria', 'Remove data source')"></button>
16912
+ </div>
16913
+ <div class="state-card-grid">
16914
+ <mat-form-field appearance="outline">
16915
+ <mat-label>{{ tx('editor.state.dataSourceId', 'ID') }}</mat-label>
16916
+ <input matInput formControlName="id" />
16917
+ </mat-form-field>
16918
+ <mat-form-field appearance="outline">
16919
+ <mat-label>{{ tx('editor.state.dataSourceKind', 'Kind') }}</mat-label>
16920
+ <mat-select formControlName="kind">
16921
+ <mat-option value="">{{ tx('editor.common.default', 'Default') }}</mat-option>
16922
+ @for (kind of dataSourceKinds; track kind) {
16923
+ <mat-option [value]="kind">{{ kind }}</mat-option>
16924
+ }
16925
+ </mat-select>
16926
+ </mat-form-field>
16927
+ <mat-form-field appearance="outline" class="span-2">
16928
+ <mat-label>{{ tx('editor.state.resourcePath', 'Resource path') }}</mat-label>
16929
+ <input matInput formControlName="resourcePath" />
16930
+ </mat-form-field>
16931
+ <mat-form-field appearance="outline">
16932
+ <mat-label>{{ tx('editor.state.method', 'HTTP method') }}</mat-label>
16933
+ <mat-select formControlName="method">
16934
+ <mat-option value="">{{ tx('editor.common.auto', 'Auto') }}</mat-option>
16935
+ @for (method of dataSourceMethods; track method) {
16936
+ <mat-option [value]="method">{{ method }}</mat-option>
16937
+ }
16938
+ </mat-select>
16939
+ </mat-form-field>
16940
+ <mat-form-field appearance="outline" class="span-2">
16941
+ <mat-label>{{ tx('editor.state.targetStatePath', 'Target state path') }}</mat-label>
16942
+ <input matInput formControlName="targetStatePath" />
16943
+ </mat-form-field>
16944
+ <mat-form-field appearance="outline">
16945
+ <mat-label>{{ tx('editor.state.errorPolicy', 'Error policy') }}</mat-label>
16946
+ <mat-select formControlName="errorPolicy">
16947
+ <mat-option value="">{{ tx('editor.common.default', 'Default') }}</mat-option>
16948
+ @for (policy of dataSourceErrorPolicies; track policy) {
16949
+ <mat-option [value]="policy">{{ policy }}</mat-option>
16950
+ }
16951
+ </mat-select>
16952
+ </mat-form-field>
16953
+ <mat-form-field appearance="outline" class="span-2">
16954
+ <mat-label>{{ tx('editor.state.queryJson', 'Query (JSON)') }}</mat-label>
16955
+ <textarea matInput rows="3" formControlName="queryJson"></textarea>
16956
+ </mat-form-field>
16957
+ <mat-form-field appearance="outline" class="span-2">
16958
+ <mat-label>{{ tx('editor.state.transformJson', 'Transform (JSON)') }}</mat-label>
16959
+ <textarea matInput rows="3" formControlName="transformJson"></textarea>
16960
+ </mat-form-field>
16961
+ <mat-form-field appearance="outline" class="span-2">
16962
+ <mat-label>{{ tx('editor.state.fallbackJson', 'Fallback value (JSON)') }}</mat-label>
16963
+ <textarea matInput rows="3" formControlName="fallbackJson"></textarea>
16964
+ </mat-form-field>
16965
+ <mat-form-field appearance="outline" class="span-2">
16966
+ <mat-label>{{ tx('editor.state.description', 'Description') }}</mat-label>
16967
+ <input matInput formControlName="description" />
16968
+ </mat-form-field>
16969
+ </div>
16970
+ </div>
16971
+ }
16681
16972
  </div>
16682
16973
  <mat-form-field appearance="outline" class="page-context">
16683
16974
  <mat-label>{{ tx('editor.state.dataSourcesJson', 'page.dataSources (JSON)') }}</mat-label>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/page-builder",
3
- "version": "10.0.0-rc.13",
3
+ "version": "10.0.0-rc.14",
4
4
  "description": "Page and widget builder utilities for Praxis UI (grid, dynamic widgets, editors).",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -8,9 +8,9 @@
8
8
  "@angular/forms": "^21.0.0",
9
9
  "@angular/cdk": "^21.0.0",
10
10
  "@angular/material": "^21.0.0",
11
- "@praxisui/ai": "^10.0.0-rc.13",
12
- "@praxisui/core": "^10.0.0-rc.13",
13
- "@praxisui/settings-panel": "^10.0.0-rc.13",
11
+ "@praxisui/ai": "^10.0.0-rc.14",
12
+ "@praxisui/core": "^10.0.0-rc.14",
13
+ "@praxisui/settings-panel": "^10.0.0-rc.14",
14
14
  "rxjs": "~7.8.0"
15
15
  },
16
16
  "dependencies": {
@@ -2,7 +2,7 @@ import { MatDialogRef, MatDialog } from '@angular/material/dialog';
2
2
  import * as _angular_core from '@angular/core';
3
3
  import { EventEmitter, OnInit, WritableSignal, InjectionToken, Provider, OnChanges, OnDestroy, Type, SimpleChanges } from '@angular/core';
4
4
  import * as _praxisui_core from '@praxisui/core';
5
- import { ComponentDocMeta, WidgetShellConfig, ComponentMetadataRegistry, PraxisI18nService, SettingsEditorSection, EndpointRef, PraxisTextValue, WidgetShellPresetCategory, CompositionLink, WidgetShellAction, WidgetPageAuthoringCapabilities, PortContract, ComponentPortPathSegment, LinkIntent, WidgetPageDefinition, SurfaceOpenActionEditorComponent, TransformKind, TransformPipeline, SurfaceOpenPayload, WidgetPageDeviceKind, SettingsValueProvider as SettingsValueProvider$1, WidgetShellPresetDefinition, WidgetPageCanvasLayout, WidgetPageGroupingDefinition, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, WidgetPageDeviceLayouts, WidgetPageSlotAssignments, WidgetPageStateInput, WidgetInstance, TransformOutputHint, TransformSemanticKind, ConfigAuthoringSource, DomainKnowledgeChangeSetRequest, DomainKnowledgeChangeSet, DomainKnowledgeChangeSetTimelineResponse, DomainKnowledgeChangeSetFilters, DomainKnowledgeValidationResponse, DomainKnowledgeStatusTransitionRequest, DomainRuleIntakeRequest, DomainRuleIntakeResponse, DomainRuleDefinitionRequest, DomainRuleDefinition, DomainRuleSimulationRequest, DomainRuleSimulationResponse, DomainRuleStatusTransitionRequest, DomainRulePublicationRequest, DomainRulePublicationResponse, DomainRuleMaterializationFilters, DomainRuleMaterialization, DomainRuleTimelineResponse, PraxisRuntimeComponentObservationEnvelope, ComponentContextPack, ComponentAuthoringManifest, DiagnosticRecord, NestedPortCatalogRegistry, CompositionValidatorService, ApiUrlEntry, DynamicWidgetPageComponent, ConfigDocument, WidgetPagePersistenceState, WidgetEventEnvelope, ObservabilityAgenticTurnMetrics, SettingsPanelBridge, ObservabilityAgenticTurnMetricBucket, RichTimelineItem } from '@praxisui/core';
5
+ import { ComponentDocMeta, WidgetShellConfig, ComponentMetadataRegistry, PraxisI18nService, SettingsEditorSection, EndpointRef, PraxisTextValue, WidgetShellPresetCategory, CompositionLink, WidgetShellAction, WidgetPageAuthoringCapabilities, PortContract, ComponentPortPathSegment, LinkIntent, WidgetPageDefinition, SurfaceOpenActionEditorComponent, TransformKind, TransformPipeline, SurfaceOpenPayload, WidgetPageDeviceKind, SettingsValueProvider as SettingsValueProvider$1, WidgetShellPresetDefinition, WidgetPageCanvasLayout, WidgetPageGroupingDefinition, WidgetPageDataSourceKind, WidgetPageDataSourceMethod, WidgetPageDataSourceErrorPolicy, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, WidgetPageDataSourceDefinition, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, WidgetPageDeviceLayouts, WidgetPageSlotAssignments, WidgetPageStateInput, WidgetInstance, TransformOutputHint, TransformSemanticKind, ConfigAuthoringSource, DomainKnowledgeChangeSetRequest, DomainKnowledgeChangeSet, DomainKnowledgeChangeSetTimelineResponse, DomainKnowledgeChangeSetFilters, DomainKnowledgeValidationResponse, DomainKnowledgeStatusTransitionRequest, DomainRuleIntakeRequest, DomainRuleIntakeResponse, DomainRuleDefinitionRequest, DomainRuleDefinition, DomainRuleSimulationRequest, DomainRuleSimulationResponse, DomainRuleStatusTransitionRequest, DomainRulePublicationRequest, DomainRulePublicationResponse, DomainRuleMaterializationFilters, DomainRuleMaterialization, DomainRuleTimelineResponse, PraxisRuntimeComponentObservationEnvelope, ComponentContextPack, ComponentAuthoringManifest, DiagnosticRecord, NestedPortCatalogRegistry, CompositionValidatorService, ApiUrlEntry, DynamicWidgetPageComponent, ConfigDocument, WidgetPagePersistenceState, WidgetEventEnvelope, ObservabilityAgenticTurnMetrics, SettingsPanelBridge, ObservabilityAgenticTurnMetricBucket, RichTimelineItem } from '@praxisui/core';
6
6
  import * as rxjs from 'rxjs';
7
7
  import { BehaviorSubject, Observable } from 'rxjs';
8
8
  import { FormGroup, FormControl, FormArray, AbstractControl } from '@angular/forms';
@@ -1409,11 +1409,28 @@ type GroupingNodeGroup = FormGroup<{
1409
1409
  side: FormControl<string>;
1410
1410
  tabsJson: FormControl<string>;
1411
1411
  }>;
1412
+ type DataSourceNodeGroup = FormGroup<{
1413
+ id: FormControl<string>;
1414
+ kind: FormControl<string>;
1415
+ resourcePath: FormControl<string>;
1416
+ method: FormControl<string>;
1417
+ targetStatePath: FormControl<string>;
1418
+ errorPolicy: FormControl<string>;
1419
+ refreshIntervalMs: FormControl<number | null>;
1420
+ autoFetch: FormControl<string>;
1421
+ queryJson: FormControl<string>;
1422
+ transformJson: FormControl<string>;
1423
+ fallbackJson: FormControl<string>;
1424
+ description: FormControl<string>;
1425
+ }>;
1412
1426
  declare class PageConfigEditorComponent implements SettingsValueProvider$1 {
1413
1427
  protected readonly derivedOperators: readonly ["merge-objects", "pick", "omit"];
1414
1428
  protected readonly mergeStrategies: readonly ["replace", "merge", "append", "remove-keys"];
1415
1429
  protected readonly valueModes: readonly ["json", "string", "number", "boolean", "null"];
1416
1430
  protected readonly groupingKinds: readonly ["section", "tabs", "hero", "rail"];
1431
+ protected readonly dataSourceKinds: WidgetPageDataSourceKind[];
1432
+ protected readonly dataSourceMethods: WidgetPageDataSourceMethod[];
1433
+ protected readonly dataSourceErrorPolicies: WidgetPageDataSourceErrorPolicy[];
1417
1434
  protected readonly layoutPresetEntries: Array<(typeof BUILTIN_PAGE_LAYOUT_PRESETS)[string]>;
1418
1435
  protected readonly themePresetEntries: Array<(typeof BUILTIN_PAGE_THEME_PRESETS)[string]>;
1419
1436
  readonly page: _angular_core.InputSignal<string | PageDefinition | null>;
@@ -1437,6 +1454,7 @@ declare class PageConfigEditorComponent implements SettingsValueProvider$1 {
1437
1454
  readonly groupingNodes: FormArray<GroupingNodeGroup>;
1438
1455
  readonly schemaNodes: FormArray<SchemaNodeGroup>;
1439
1456
  readonly derivedNodes: FormArray<DerivedNodeGroup>;
1457
+ readonly dataSourceNodes: FormArray<DataSourceNodeGroup>;
1440
1458
  readonly deviceDesktopForm: FormGroup<{
1441
1459
  orientation: FormControl<string>;
1442
1460
  columns: FormControl<number | null>;
@@ -1519,6 +1537,8 @@ declare class PageConfigEditorComponent implements SettingsValueProvider$1 {
1519
1537
  config?: WidgetDerivedStateNode;
1520
1538
  }): void;
1521
1539
  removeDerivedNode(index: number): void;
1540
+ addDataSourceNode(seed?: WidgetPageDataSourceDefinition): void;
1541
+ removeDataSourceNode(index: number): void;
1522
1542
  addGroupingNode(seed?: WidgetPageGroupingDefinition): void;
1523
1543
  removeGroupingNode(index: number): void;
1524
1544
  setActiveTab(tab: EditorTabId): void;
@@ -1536,6 +1556,12 @@ declare class PageConfigEditorComponent implements SettingsValueProvider$1 {
1536
1556
  private buildDeviceLayoutVariant;
1537
1557
  private hydrateStateEditor;
1538
1558
  private hydrateDataSourcesEditor;
1559
+ private normalizeDataSourcesArray;
1560
+ private replaceDataSourceNodes;
1561
+ private createDataSourceNodeGroup;
1562
+ private syncDataSourceNodesFromControl;
1563
+ private syncDataSourcesControlFromNodes;
1564
+ private buildDataSourcesFromNodes;
1539
1565
  private buildDataSources;
1540
1566
  private replaceSchemaNodes;
1541
1567
  private replaceDerivedNodes;