@acorex/platform 21.0.0-next.39 → 21.0.0-next.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/fesm2022/acorex-platform-common.mjs +6 -2
  2. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-core.mjs +8 -1
  4. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-domain.mjs +3 -0
  6. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-layout-builder.mjs +137 -34
  8. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-components.mjs +25 -13
  10. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-designer.mjs +261 -58
  12. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-entity.mjs +1583 -632
  14. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-widget-core.mjs +169 -85
  16. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  17. package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs} +9 -4
  18. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-layout-widgets.mjs +1053 -409
  20. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-runtime.mjs +120 -9
  22. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  23. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs → acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs} +3 -3
  24. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs.map +1 -0
  25. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs → acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs} +3 -3
  26. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs → acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs} +3 -3
  28. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-themes-default.mjs +10 -10
  30. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  31. package/fesm2022/{acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs → acorex-platform-themes-shared-settings.provider-D13QB3Hr.mjs} +2 -2
  32. package/fesm2022/acorex-platform-themes-shared-settings.provider-D13QB3Hr.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs +94 -0
  34. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs} +38 -16
  36. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-themes-shared.mjs +183 -84
  38. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  39. package/fesm2022/acorex-platform-workflow.mjs +52 -11
  40. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  41. package/package.json +1 -1
  42. package/types/acorex-platform-common.d.ts +14 -10
  43. package/types/acorex-platform-core.d.ts +13 -2
  44. package/types/acorex-platform-domain.d.ts +28 -2
  45. package/types/acorex-platform-layout-builder.d.ts +61 -29
  46. package/types/acorex-platform-layout-designer.d.ts +88 -16
  47. package/types/acorex-platform-layout-entity.d.ts +190 -15
  48. package/types/acorex-platform-layout-widget-core.d.ts +81 -71
  49. package/types/acorex-platform-layout-widgets.d.ts +131 -54
  50. package/types/acorex-platform-runtime.d.ts +156 -61
  51. package/types/acorex-platform-workflow.d.ts +37 -2
  52. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +0 -1
  53. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs.map +0 -1
  54. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs.map +0 -1
  55. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs.map +0 -1
  56. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
  57. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs +0 -65
  58. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs.map +0 -1
  59. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import * as i5 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, inject, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output } from '@angular/core';
4
+ import { Injectable, inject, input, model, signal, computed, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output } from '@angular/core';
5
5
  import { provideCommandSetups } from '@acorex/platform/runtime';
6
6
  import { AXPopupService } from '@acorex/components/popup';
7
7
  import * as i1 from '@acorex/platform/layout/widget-core';
8
8
  import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
9
- import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
9
+ import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
10
10
  import * as i2 from '@acorex/components/form';
11
11
  import { AXFormComponent, AXFormModule } from '@acorex/components/form';
12
12
  import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
@@ -161,6 +161,10 @@ class AXPLayoutConversionService {
161
161
  if (!editorWidget.mode) {
162
162
  editorWidget.mode = fieldMode;
163
163
  }
164
+ const hintOpts = field.description != null &&
165
+ (typeof field.description !== 'string' || field.description.trim().length > 0)
166
+ ? { hint: field.description, hintDisplayMode: 'note' }
167
+ : {};
164
168
  return {
165
169
  type: 'form-field',
166
170
  name: field.path,
@@ -168,8 +172,8 @@ class AXPLayoutConversionService {
168
172
  options: {
169
173
  label: field.title,
170
174
  badge: field.badge,
171
- description: field.description,
172
175
  showLabel: true,
176
+ ...hintOpts,
173
177
  },
174
178
  children: [editorWidget], // The editor widget becomes a child of form-field
175
179
  };
@@ -212,7 +216,7 @@ class AXPLayoutConversionService {
212
216
  path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
213
217
  title: formFieldNode.options?.['label'],
214
218
  badge: formFieldNode.options?.['badge'],
215
- description: formFieldNode.options?.['description'],
219
+ description: formFieldNode.options?.['hint'],
216
220
  widget: editorWidget,
217
221
  mode: formFieldNode.mode,
218
222
  };
@@ -527,11 +531,21 @@ class LayoutBuilder {
527
531
  return this;
528
532
  }
529
533
  build() {
530
- return {
531
- type: this.root.type,
532
- children: this.root.children,
533
- mode: this.root.mode,
534
+ const r = this.root;
535
+ const node = {
536
+ type: r.type,
537
+ ...(r.mode !== undefined ? { mode: r.mode } : {}),
538
+ ...(r.children !== undefined ? { children: r.children } : {}),
539
+ ...(r.options !== undefined ? { options: r.options } : {}),
540
+ ...(r.name !== undefined ? { name: r.name } : {}),
541
+ ...(r.path !== undefined ? { path: r.path } : {}),
542
+ ...(r.visible !== undefined ? { visible: r.visible } : {}),
543
+ ...(r.defaultValue !== undefined ? { defaultValue: r.defaultValue } : {}),
544
+ ...(r.triggers !== undefined ? { triggers: r.triggers } : {}),
545
+ ...(r.meta !== undefined ? { meta: r.meta } : {}),
546
+ ...(r.valueTransforms !== undefined ? { valueTransforms: r.valueTransforms } : {}),
534
547
  };
548
+ return node;
535
549
  }
536
550
  /**
537
551
  * Converts the built widget node to JSON string
@@ -1005,6 +1019,24 @@ class FlexContainerBuilder extends WidgetContainerMixin {
1005
1019
  * Extracts flat grid-item options from AXPGridLayoutOptions for grid-item-layout widget.
1006
1020
  * Uses first available breakpoint (lg, xl, md, sm).
1007
1021
  */
1022
+ /**
1023
+ * Deep-merges grid breakpoint buckets so sequential fluent calls (e.g. setColumns then setGap)
1024
+ * do not wipe sibling keys under options.grid.default.
1025
+ */
1026
+ function mergeAXPGridContainerOptions(prev, patch) {
1027
+ const next = { ...(prev ?? {}), ...patch };
1028
+ if (prev?.grid?.default || patch.grid?.default) {
1029
+ next.grid = {
1030
+ ...(prev?.grid ?? {}),
1031
+ ...(patch.grid ?? {}),
1032
+ default: {
1033
+ ...(prev?.grid?.default ?? {}),
1034
+ ...(patch.grid?.default ?? {}),
1035
+ },
1036
+ };
1037
+ }
1038
+ return next;
1039
+ }
1008
1040
  function toGridItemOptions(layoutOptions) {
1009
1041
  if (!layoutOptions?.positions)
1010
1042
  return { colSpan: 12 };
@@ -1034,7 +1066,7 @@ class GridContainerBuilder extends WidgetContainerMixin {
1034
1066
  super('grid-layout');
1035
1067
  }
1036
1068
  setOptions(options) {
1037
- this.containerState.options = { ...this.containerState.options, ...options };
1069
+ this.containerState.options = mergeAXPGridContainerOptions(this.containerState.options, options);
1038
1070
  return this;
1039
1071
  }
1040
1072
  item(layoutOptions, delegate) {
@@ -1998,6 +2030,11 @@ class AXPLayoutRendererComponent {
1998
2030
  //#endregion
1999
2031
  //#region ---- Widget Tree Conversion ----
2000
2032
  this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : /* istanbul ignore next */ []));
2033
+ /**
2034
+ * Prefer explicit {@link AXPWidgetNode.mode} on the root node (e.g. dialog flex `mode('view')`)
2035
+ * so nested widgets resolve view vs edit correctly; fall back to the layout `mode` input.
2036
+ */
2037
+ this.effectiveRenderMode = computed(() => this.widgetTree()?.mode ?? this.mode(), ...(ngDevMode ? [{ debugName: "effectiveRenderMode" }] : /* istanbul ignore next */ []));
2001
2038
  /**
2002
2039
  * Convert layout data to widget tree when inputs change
2003
2040
  */
@@ -2207,7 +2244,11 @@ class AXPLayoutRendererComponent {
2207
2244
  <ax-form>
2208
2245
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2209
2246
  @if (widgetTree()) {
2210
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2247
+ <ng-container
2248
+ axp-widget-renderer
2249
+ [node]="widgetTree()!"
2250
+ [mode]="effectiveRenderMode()"
2251
+ ></ng-container>
2211
2252
  }
2212
2253
  </axp-widgets-container>
2213
2254
  </ax-form>
@@ -2219,7 +2260,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2219
2260
  <ax-form>
2220
2261
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2221
2262
  @if (widgetTree()) {
2222
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2263
+ <ng-container
2264
+ axp-widget-renderer
2265
+ [node]="widgetTree()!"
2266
+ [mode]="effectiveRenderMode()"
2267
+ ></ng-container>
2223
2268
  }
2224
2269
  </axp-widgets-container>
2225
2270
  </ax-form>
@@ -2328,7 +2373,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2328
2373
  }
2329
2374
  async executeAction(action) {
2330
2375
  const cmd = this.resolveActionCommandName(action.command);
2331
- if (cmd !== 'cancel') {
2376
+ if (this.shouldValidateBeforeAction(cmd)) {
2332
2377
  const isValid = await this.layoutRenderer()?.validate();
2333
2378
  if (!isValid?.result) {
2334
2379
  return;
@@ -2337,7 +2382,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2337
2382
  if (cmd?.startsWith('widget:')) {
2338
2383
  const parsed = this.parseWidgetCommand(cmd);
2339
2384
  if (parsed.widgetName && parsed.action) {
2340
- await this.executeWidgetApi(parsed.widgetName, parsed.action);
2385
+ await this.invokeWidget(parsed.widgetName, parsed.action, {});
2341
2386
  await this.aggregateAndEvaluateActions();
2342
2387
  return;
2343
2388
  }
@@ -2345,17 +2390,15 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2345
2390
  const context = this.context();
2346
2391
  const onAction = this.config?.onAction;
2347
2392
  if (onAction) {
2348
- const dialogRef = {
2349
- close: (res) => this.close(res),
2350
- context: () => this.context(),
2351
- action: () => action.command ?? undefined,
2352
- setLoading: (loading) => this.isDialogLoading.set(loading),
2353
- };
2393
+ const dialogRef = this.createDialogRef(cmd);
2354
2394
  try {
2355
2395
  this.isDialogLoading.set(true);
2356
2396
  const result = await Promise.resolve(onAction(dialogRef));
2397
+ if (result && typeof result === 'object' && result.keepDialogOpen) {
2398
+ return;
2399
+ }
2357
2400
  this.callBack(result);
2358
- this.close(result);
2401
+ await this.closeWithOptionalSkipValidate(result);
2359
2402
  }
2360
2403
  catch {
2361
2404
  // Handler threw: stay open for retry, actions remain clickable
@@ -2373,20 +2416,48 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2373
2416
  this.data.action = result.action;
2374
2417
  }
2375
2418
  this.callBack({
2376
- close: (res) => {
2377
- this.close(res);
2378
- },
2379
- context: () => this.context(),
2419
+ ...this.createDialogRef(cmd),
2380
2420
  action: () => result.action,
2381
- setLoading: (loading) => {
2382
- this.isDialogLoading.set(loading);
2383
- },
2384
2421
  });
2385
2422
  // Without `onAction`, only the configured cancel action dismisses the dialog (not submit/custom).
2386
2423
  if (cmd === 'cancel') {
2387
2424
  await this.close(result);
2388
2425
  }
2389
2426
  }
2427
+ /** Whether the layout form should be validated before running this footer command. */
2428
+ shouldValidateBeforeAction(cmd) {
2429
+ if (!cmd || cmd === 'cancel' || cmd === 'entity-form-done') {
2430
+ return false;
2431
+ }
2432
+ if (cmd.startsWith('widget:')) {
2433
+ return false;
2434
+ }
2435
+ return true;
2436
+ }
2437
+ createDialogRef(actionCmd) {
2438
+ return {
2439
+ close: (res) => {
2440
+ void this.closeWithOptionalSkipValidate(res);
2441
+ },
2442
+ context: () => this.context(),
2443
+ action: () => actionCmd,
2444
+ setLoading: (loading) => this.isDialogLoading.set(loading),
2445
+ patchContext: (partial) => {
2446
+ const merged = merge({}, this.context(), partial);
2447
+ this.context.set(merged);
2448
+ this.layoutRenderer()?.updateContext(merged);
2449
+ },
2450
+ invokeWidget: (widgetName, method, opts) => this.invokeWidget(widgetName, method, opts ?? {}),
2451
+ };
2452
+ }
2453
+ async closeWithOptionalSkipValidate(result) {
2454
+ if (result && typeof result === 'object' && result.skipValidate) {
2455
+ this.result.emit(result);
2456
+ await super.close(result);
2457
+ return;
2458
+ }
2459
+ await this.close(result);
2460
+ }
2390
2461
  /** Resolves footer/widget action command to a string (e.g. `cancel`, `submit`, `widget:...`). */
2391
2462
  resolveActionCommandName(command) {
2392
2463
  if (typeof command === 'string') {
@@ -2407,7 +2478,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2407
2478
  return {};
2408
2479
  return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
2409
2480
  }
2410
- async executeWidgetApi(widgetName, apiMethod) {
2481
+ async invokeWidget(widgetName, apiMethod, opts) {
2411
2482
  if (!this.widgetCoreService)
2412
2483
  return;
2413
2484
  try {
@@ -2417,16 +2488,20 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2417
2488
  if (typeof fn === 'function') {
2418
2489
  await Promise.resolve(fn({
2419
2490
  close: (result) => {
2420
- this.close(result);
2491
+ void this.closeWithOptionalSkipValidate(result);
2421
2492
  },
2422
2493
  context: () => this.context(),
2423
2494
  setLoading: (loading) => {
2424
- this.isDialogLoading.set(loading);
2495
+ (opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
2425
2496
  },
2426
2497
  }));
2498
+ // Footer predicates (e.g. wizard step) must refresh when the widget advances outside executeAction (e.g. dialogRef.invokeWidget after entity-form continue).
2499
+ await this.aggregateAndEvaluateActions();
2427
2500
  }
2428
2501
  }
2429
- catch { }
2502
+ catch {
2503
+ //
2504
+ }
2430
2505
  }
2431
2506
  async close(result) {
2432
2507
  if (result) {
@@ -2476,6 +2551,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2476
2551
  zone: 'footer',
2477
2552
  placement,
2478
2553
  scope: a.scope,
2554
+ predicateApiWidgetName: a.predicateApiWidgetName,
2479
2555
  });
2480
2556
  const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
2481
2557
  const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
@@ -2485,16 +2561,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2485
2561
  const out = [];
2486
2562
  for (const a of actions) {
2487
2563
  const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
2488
- const api = parsed.widgetName ? await this.resolveApi(parsed.widgetName) : undefined;
2564
+ const widgetNameForApi = parsed.widgetName ?? a.predicateApiWidgetName;
2565
+ const api = widgetNameForApi ? await this.resolveApi(widgetNameForApi) : undefined;
2489
2566
  const scope = {
2490
2567
  api,
2491
- widget: { name: parsed.widgetName },
2568
+ widget: { name: widgetNameForApi },
2492
2569
  dialog: { context: this.context() },
2493
2570
  context: this.context(),
2494
2571
  };
2495
2572
  const disabled = await this.evalBool(a.disabled, scope);
2496
2573
  const hidden = await this.evalBool(a.hidden, scope);
2497
- out.push({ ...a, disabled, hidden });
2574
+ const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
2575
+ out.push({ ...a, disabled, hidden, title: resolvedTitle });
2498
2576
  }
2499
2577
  return out;
2500
2578
  }
@@ -2512,6 +2590,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2512
2590
  }
2513
2591
  return value;
2514
2592
  }
2593
+ /** Resolves footer action title when it contains {{ ... }} (e.g. wizard labels from dialog context). */
2594
+ async evalActionTitle(value, scope) {
2595
+ if (value == null || typeof value !== 'string' || !value.includes('{{')) {
2596
+ return value;
2597
+ }
2598
+ try {
2599
+ const result = await this.expressionEvaluator.evaluate(value, scope);
2600
+ if (typeof result === 'string' && result.length > 0) {
2601
+ return result;
2602
+ }
2603
+ if (result != null && result !== false) {
2604
+ return String(result);
2605
+ }
2606
+ }
2607
+ catch {
2608
+ //
2609
+ }
2610
+ return value;
2611
+ }
2515
2612
  async resolveApi(widgetName) {
2516
2613
  try {
2517
2614
  await this.widgetCoreService?.waitForWidget(widgetName, 2000);
@@ -2526,6 +2623,9 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2526
2623
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", outputs: { result: "result" }, providers: [AXPContextStore], viewQueries: [{ propertyName: "layoutRenderer", first: true, predicate: AXPLayoutRendererComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
2527
2624
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2528
2625
  <div class="ax-p-4">
2626
+ @if (config.message) {
2627
+ <p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
2628
+ }
2529
2629
  <axp-layout-renderer
2530
2630
  [layout]="config.definition"
2531
2631
  [context]="context()"
@@ -2599,6 +2699,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2599
2699
  template: `
2600
2700
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2601
2701
  <div class="ax-p-4">
2702
+ @if (config.message) {
2703
+ <p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
2704
+ }
2602
2705
  <axp-layout-renderer
2603
2706
  [layout]="config.definition"
2604
2707
  [context]="context()"