@acorex-platform/framework-client 0.0.59 → 0.0.61

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 (38) hide show
  1. package/fesm2022/acorex-platform-framework-client-auth.mjs +397 -93
  2. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-framework-client-common.mjs +33 -31
  4. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +12 -4
  6. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
  7. package/fesm2022/{acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BBuYugjl.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-D0jDsA6M.mjs} +80 -13
  8. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-D0jDsA6M.mjs.map +1 -0
  9. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-DZDf_nVq.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-Bdvd1iYt.mjs} +2 -2
  10. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-DZDf_nVq.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-Bdvd1iYt.mjs.map} +1 -1
  11. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-CFFv3jDs.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-CsTP8YHv.mjs} +2 -2
  12. package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-CFFv3jDs.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-CsTP8YHv.mjs.map} +1 -1
  13. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-BDNPJg_6.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-UvMk0GbU.mjs} +2 -2
  14. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-BDNPJg_6.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-UvMk0GbU.mjs.map} +1 -1
  15. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +1 -1
  16. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +257 -30
  17. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
  18. package/fesm2022/{acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-Rv0K9r_e.mjs → acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-KrDsAa3H.mjs} +555 -121
  19. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-KrDsAa3H.mjs.map +1 -0
  20. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Dzqn3rGa.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-BDlo6nTx.mjs} +2 -2
  21. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-Dzqn3rGa.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-BDlo6nTx.mjs.map} +1 -1
  22. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-C97rrrmu.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-By3wntCB.mjs} +2 -2
  23. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-C97rrrmu.mjs.map → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-By3wntCB.mjs.map} +1 -1
  24. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -1
  25. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4 -4
  26. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
  27. package/layout/builder/README.md +10 -0
  28. package/layout/widget-core/README.md +47 -3
  29. package/layout/widgets/README.md +8 -2
  30. package/package.json +2 -2
  31. package/types/acorex-platform-framework-client-auth.d.ts +60 -1
  32. package/types/acorex-platform-framework-client-common.d.ts +3 -2
  33. package/types/acorex-platform-framework-client-layout-components.d.ts +3 -1
  34. package/types/acorex-platform-framework-client-layout-entity.d.ts +8 -1
  35. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +72 -1
  36. package/types/acorex-platform-framework-client-layout-widgets.d.ts +25 -5
  37. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BBuYugjl.mjs.map +0 -1
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-Rv0K9r_e.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, computed, effect, untracked, Injectable, inject, ElementRef, DestroyRef, EventEmitter, Injector, ChangeDetectorRef, ViewChild, Input, Output, ChangeDetectionStrategy, Component, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
2
+ import { InjectionToken, signal, computed, effect, untracked, Injectable, inject, ElementRef, DestroyRef, PLATFORM_ID, EventEmitter, Injector, ChangeDetectorRef, ViewChild, Input, Output, ChangeDetectionStrategy, Component, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
3
3
  import { AXPContextStore, AXPDeviceService, AXPExpressionEvaluatorService, AXPDebugService, FORM_DIRTY_TRACE_NS, OVERLAY_WIDGET_SETTLE_TRACE_NS } from '@acorex-platform/framework-client/core';
4
4
  import { AXPPageStatus, AXPWidgetStatus, AXP_WIDGET_TOKEN, AXP_WIDGET_COLUMN_TOKEN, layoutProfileMediumFromRenderMode, resolveLayoutProfile, createStringProperty, createBooleanProperty, createSelectProperty, createNumberProperty, cloneProperty } from '@acorex-platform/framework-shared/widget-core';
5
5
  import { cloneDeep, set, get, isEqual, isNil, isUndefined, isObjectLike, merge, sum, isEmpty, isString } from 'lodash-es';
@@ -15,7 +15,7 @@ import * as i2 from '@acorex/core/translation';
15
15
  import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
16
16
  import { PortalModule } from '@angular/cdk/portal';
17
17
  import * as i1 from '@angular/common';
18
- import { CommonModule, NgComponentOutlet } from '@angular/common';
18
+ import { isPlatformBrowser, CommonModule, NgComponentOutlet } from '@angular/common';
19
19
  import { AXDataTableColumnComponent, AXBaseDataTable } from '@acorex/components/data-table';
20
20
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
21
21
  import { AXUnsubscriber } from '@acorex/core/utils';
@@ -1190,24 +1190,32 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
1190
1190
  this.contextService.update(path, value, { origin: 'user' });
1191
1191
  this.onValueChanged.next({ sender: this });
1192
1192
  }
1193
+ /**
1194
+ * Repeater clones pass the row index onto every descendant. Attach that index
1195
+ * to the ancestor array path (`rows[0].field`), not after the leaf (`rows.field.[0]`).
1196
+ * Lodash paths need `rows[0].field` — a dotted `.[0].` segment is a literal key.
1197
+ */
1198
+ joinIndexedWidgetPath(sections) {
1199
+ return sections.filter((section) => section != null && section !== '').join('.').replace(/\.\[(\d+)\]/g, '[$1]');
1200
+ }
1193
1201
  detectFullPath() {
1194
1202
  const sections = [];
1195
1203
  const ids = [];
1196
- //
1204
+ const rowIndex = this.index;
1205
+ let attachedRowIndex = false;
1197
1206
  let parent = this;
1198
- //
1199
1207
  while (parent) {
1200
1208
  const isValueWidget = parent instanceof AXPValueWidgetComponent && parent.isValueWidget();
1201
1209
  const valueParent = parent;
1202
1210
  const path = valueParent.path ?? (isValueWidget ? valueParent.name : null);
1203
1211
  const id = valueParent.name;
1204
- //
1205
1212
  if (path) {
1213
+ if (parent !== this && isValueWidget && rowIndex != null && !attachedRowIndex) {
1214
+ sections.push(`[${rowIndex}]`);
1215
+ attachedRowIndex = true;
1216
+ }
1206
1217
  sections.push(path);
1207
1218
  }
1208
- if (parent.index != null && isValueWidget) {
1209
- sections.push(`[${parent.index}]`);
1210
- }
1211
1219
  if (id) {
1212
1220
  ids.push(id);
1213
1221
  if (parent.index != null) {
@@ -1216,9 +1224,9 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
1216
1224
  }
1217
1225
  parent = parent.parent;
1218
1226
  }
1219
- //
1220
- this.fullPath.set(sections.reverse().join('.'));
1221
- this.parentPath.set(sections.slice(0, sections.length - 1).join('.'));
1227
+ sections.reverse();
1228
+ this.fullPath.set(this.joinIndexedWidgetPath(sections));
1229
+ this.parentPath.set(this.joinIndexedWidgetPath(sections.slice(0, Math.max(0, sections.length - 1))));
1222
1230
  this._id = this.name || this.parent ? ids.reverse().join('_') : null;
1223
1231
  if (this._id) {
1224
1232
  this.layoutService.registerWidget(this._id, this);
@@ -1255,6 +1263,8 @@ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
1255
1263
  ...(ngDevMode ? [{ debugName: "textTemplate" }] : /* istanbul ignore next */ []));
1256
1264
  this.dataSource = signal(convertArrayToDataSource([]), /* @ts-ignore */
1257
1265
  ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
1266
+ /** AXDataSource instance from options that the isolated widget copy was built from. */
1267
+ this.boundRemoteDataSource = null;
1258
1268
  /** Query key from options, when options.dataSource is a string or `{ id }` object. */
1259
1269
  this.queryDataSourceName = computed(() => {
1260
1270
  const rawValue = this.options()['dataSource'];
@@ -1365,8 +1375,15 @@ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
1365
1375
  try {
1366
1376
  if (rawValue instanceof AXDataSource) {
1367
1377
  this.loadedQueryDefinition.set(null);
1368
- if (!isEqual(rawValue.config, this.dataSource().config)) {
1369
- this.dataSource.set(rawValue);
1378
+ if (this.boundRemoteDataSource !== rawValue) {
1379
+ this.boundRemoteDataSource = rawValue;
1380
+ const sourceConfig = rawValue.config;
1381
+ this.dataSource.set(new AXDataSource({
1382
+ key: sourceConfig.key,
1383
+ pageSize: sourceConfig.pageSize,
1384
+ load: (event) => sourceConfig.load(event),
1385
+ byKey: sourceConfig.byKey ? (key) => sourceConfig.byKey(key) : undefined,
1386
+ }));
1370
1387
  }
1371
1388
  this.queryDataSourceReady.set(true);
1372
1389
  return;
@@ -1885,6 +1902,12 @@ class AXPGridBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent
1885
1902
  const g = this.resolvedGrid();
1886
1903
  if (g?.gap)
1887
1904
  style['gap'] = g.gap;
1905
+ // Numeric columns cannot rely on dynamic Tailwind `grid-cols-${n}` (often purged).
1906
+ // Mirror fieldset-layout and set the template via inline style.
1907
+ const columns = g?.columns;
1908
+ if (typeof columns === 'number' && columns > 0) {
1909
+ style['grid-template-columns'] = `repeat(${columns}, minmax(0, 1fr))`;
1910
+ }
1888
1911
  return style;
1889
1912
  }, /* @ts-ignore */
1890
1913
  ...(ngDevMode ? [{ debugName: "hostGridStyle" }] : /* istanbul ignore next */ []));
@@ -2100,6 +2123,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2100
2123
  }]
2101
2124
  }] });
2102
2125
 
2126
+ const AXP_WIDGET_COLUMN_AUTO_FIT = new InjectionToken('AXP_WIDGET_COLUMN_AUTO_FIT');
2127
+
2103
2128
  class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2104
2129
  constructor() {
2105
2130
  super(...arguments);
@@ -2108,6 +2133,7 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2108
2133
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
2109
2134
  this.translation = inject(AXTranslationService);
2110
2135
  this.destroyRef = inject(DestroyRef);
2136
+ this.platformId = inject(PLATFORM_ID);
2111
2137
  /** Original band title key; caption becomes resolved text and must not be re-read for i18n. */
2112
2138
  this.bandCaptionI18nKey = null;
2113
2139
  this.bandLocaleWatchStarted = false;
@@ -2120,6 +2146,21 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2120
2146
  this.pendingEvaluations = new Set();
2121
2147
  this.changeDetectionScheduled = false;
2122
2148
  this.MAX_CACHE_SIZE = 1000; // Limit cache size to prevent memory leaks
2149
+ /** Column was configured with `width: 'auto'` and should follow cell content. */
2150
+ this.autoWidthColumn = false;
2151
+ /** Further content passes are ignored after the directive-rendered cell has been measured. */
2152
+ this.contentAutoFitSettled = false;
2153
+ this.contentAutoFitShouldSettle = false;
2154
+ this.contentAutoFitAttempts = 0;
2155
+ this.contentAutoFitTimer = null;
2156
+ /** Matches the cap `ax-data-table` uses for its own auto-width pass. */
2157
+ this.contentAutoFitMaxWidth = 500;
2158
+ this.stopContentAutoFit = this.destroyRef.onDestroy(() => {
2159
+ if (this.contentAutoFitTimer != null) {
2160
+ clearTimeout(this.contentAutoFitTimer);
2161
+ this.contentAutoFitTimer = null;
2162
+ }
2163
+ });
2123
2164
  /** When true, header click cycles sort (handled by parent; synced with list sort state). */
2124
2165
  this.sortEnabled = false;
2125
2166
  this.sortToggle = new EventEmitter();
@@ -2281,8 +2322,10 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2281
2322
  : (this.mergedOptions().width ?? '200px');
2282
2323
  this.allowResizing = this.mergedOptions().allowResizing !== false;
2283
2324
  this.allowSorting = this.sortEnabled;
2325
+ this.syncAutoWidthIntent();
2284
2326
  this.cdr.detectChanges();
2285
2327
  this.fixed = this.columnFixed;
2328
+ this.scheduleContentAutoFit();
2286
2329
  }
2287
2330
  ngAfterContentInit() {
2288
2331
  if (!this.isBand) {
@@ -2302,6 +2345,7 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2302
2345
  if (this.customWidth) {
2303
2346
  this.width = this.customWidth;
2304
2347
  }
2348
+ this.syncAutoWidthIntent();
2305
2349
  if (this.node) {
2306
2350
  this.allowResizing = this.mergedOptions().allowResizing !== false;
2307
2351
  }
@@ -2414,6 +2458,7 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2414
2458
  requestAnimationFrame(() => {
2415
2459
  this.cdr.detectChanges();
2416
2460
  this.changeDetectionScheduled = false;
2461
+ this.scheduleContentAutoFit();
2417
2462
  });
2418
2463
  }
2419
2464
  /**
@@ -2428,6 +2473,52 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2428
2473
  }
2429
2474
  }
2430
2475
  //#endregion
2476
+ /**
2477
+ * Re-measures this column after cell widgets exist.
2478
+ * The table's own pass runs once, about 100ms after rows arrive, which is before
2479
+ * `ngComponentOutlet` / `axp-widget-renderer` have painted.
2480
+ */
2481
+ scheduleContentAutoFit(request) {
2482
+ if (!isPlatformBrowser(this.platformId) || !this.autoWidthColumn || this.contentAutoFitSettled) {
2483
+ return;
2484
+ }
2485
+ this.contentAutoFitShouldSettle = this.contentAutoFitShouldSettle || request?.settle === true;
2486
+ if (this.contentAutoFitTimer != null) {
2487
+ clearTimeout(this.contentAutoFitTimer);
2488
+ }
2489
+ this.contentAutoFitTimer = setTimeout(() => {
2490
+ this.contentAutoFitTimer = null;
2491
+ if (!this.autoWidthColumn || this.contentAutoFitSettled) {
2492
+ return;
2493
+ }
2494
+ if (this.pendingEvaluations.size > 0 && this.contentAutoFitAttempts < 20) {
2495
+ this.contentAutoFitAttempts += 1;
2496
+ this.scheduleContentAutoFit({ settle: this.contentAutoFitShouldSettle });
2497
+ return;
2498
+ }
2499
+ this.contentAutoFitAttempts = 0;
2500
+ if (this.contentAutoFitShouldSettle) {
2501
+ this.contentAutoFitSettled = true;
2502
+ }
2503
+ this.contentAutoFitShouldSettle = false;
2504
+ this.grid.autoFitColumnByName(this.name, { maxWidth: this.contentAutoFitMaxWidth });
2505
+ }, 100);
2506
+ }
2507
+ syncAutoWidthIntent() {
2508
+ if (!this.node || this.isBand) {
2509
+ this.autoWidthColumn = false;
2510
+ }
2511
+ else {
2512
+ const configured = this.customWidth || this.mergedOptions()?.width;
2513
+ this.autoWidthColumn = configured === 'auto' || this.width === 'auto';
2514
+ }
2515
+ if (!this.autoWidthColumn && this.contentAutoFitTimer != null) {
2516
+ clearTimeout(this.contentAutoFitTimer);
2517
+ this.contentAutoFitTimer = null;
2518
+ this.contentAutoFitShouldSettle = false;
2519
+ this.contentAutoFitAttempts = 0;
2520
+ }
2521
+ }
2431
2522
  requireNode() {
2432
2523
  if (!this.node) {
2433
2524
  throw new Error('axp-widget-column-renderer: node is required for leaf columns');
@@ -2462,6 +2553,10 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2462
2553
  provide: AXP_WIDGET_COLUMN_TOKEN,
2463
2554
  useValue: tokenValue,
2464
2555
  },
2556
+ {
2557
+ provide: AXP_WIDGET_COLUMN_AUTO_FIT,
2558
+ useValue: this,
2559
+ },
2465
2560
  ],
2466
2561
  });
2467
2562
  this.evictCacheIfNeeded();
@@ -2497,6 +2592,10 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2497
2592
  provide: AXP_WIDGET_COLUMN_TOKEN,
2498
2593
  useValue: tokenValue,
2499
2594
  },
2595
+ {
2596
+ provide: AXP_WIDGET_COLUMN_AUTO_FIT,
2597
+ useValue: this,
2598
+ },
2500
2599
  ],
2501
2600
  });
2502
2601
  // Replace cached injector to force component recreation on next render
@@ -2525,6 +2624,10 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2525
2624
  provide: AXP_WIDGET_COLUMN_TOKEN,
2526
2625
  useValue: tokenValue,
2527
2626
  },
2627
+ {
2628
+ provide: AXP_WIDGET_COLUMN_AUTO_FIT,
2629
+ useValue: this,
2630
+ },
2528
2631
  ],
2529
2632
  });
2530
2633
  // Cache the initial injector
@@ -2540,6 +2643,10 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
2540
2643
  provide: AXDataTableColumnComponent,
2541
2644
  useExisting: AXPWidgetColumnRendererComponent,
2542
2645
  },
2646
+ {
2647
+ provide: AXP_WIDGET_COLUMN_AUTO_FIT,
2648
+ useExisting: AXPWidgetColumnRendererComponent,
2649
+ },
2543
2650
  ], viewQueries: [{ propertyName: "_contentFooterTemplate", first: true, predicate: ["footer"], descendants: true }, { propertyName: "_contentCellTemplate", first: true, predicate: ["cell"], descendants: true }, { propertyName: "_contentHeaderTemplate", first: true, predicate: ["header"], descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
2544
2651
  <ng-template #header>
2545
2652
  <div
@@ -2784,6 +2891,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
2784
2891
  provide: AXDataTableColumnComponent,
2785
2892
  useExisting: AXPWidgetColumnRendererComponent,
2786
2893
  },
2894
+ {
2895
+ provide: AXP_WIDGET_COLUMN_AUTO_FIT,
2896
+ useExisting: AXPWidgetColumnRendererComponent,
2897
+ },
2787
2898
  ], changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['caption'], standalone: false, styles: ["@layer properties;.axp-widget-column-header--sortable{position:relative;width:100%;min-width:0px;cursor:pointer}.axp-widget-column-header--sortable:hover .axp-widget-column-header__sort,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__sort,.axp-widget-column-header--sortable.axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}.axp-widget-column-header--sortable:hover .axp-widget-column-header__caption,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__caption,.axp-widget-column-header--sortable.axp-widget-column-header--sort-active .axp-widget-column-header__caption{padding-inline-end:calc(var(--spacing, .25rem) * 5)}.axp-widget-column-header--sortable.axp-widget-column-header--sort-at-start:hover .axp-widget-column-header__caption,.axp-widget-column-header--sortable.axp-widget-column-header--sort-at-start:focus-within .axp-widget-column-header__caption,.axp-widget-column-header--sortable.axp-widget-column-header--sort-at-start.axp-widget-column-header--sort-active .axp-widget-column-header__caption{padding-inline-start:calc(var(--spacing, .25rem) * 5);padding-inline-end:0px}.axp-widget-column-header__caption{display:block;width:100%;min-width:0px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-widget-column-header__sort{position:absolute;inset-block:0;inset-inline-end:0;z-index:1;display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * .5);opacity:0;transition:opacity .15s ease}.axp-widget-column-header--sort-at-start .axp-widget-column-header__sort{inset-inline-start:0;inset-inline-end:auto}.axp-widget-column-header__sort i{display:block;--tw-leading: 1;line-height:1;font-size:.625rem}.axp-widget-column-header__sort-priority{--tw-leading: 1;line-height:1}:host-context(.ax-data-table-head-cell.ax-interactive:hover) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell.ax-interactive:focus-within) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell) .axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-leading: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
2788
2899
  }], propDecorators: { customExpandIcon: [{
2789
2900
  type: Input
@@ -3223,6 +3334,7 @@ class AXPWidgetRendererDirective {
3223
3334
  this.widgetService = inject(AXPWidgetRegistryService);
3224
3335
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
3225
3336
  this.viewContainerRef = inject(ViewContainerRef);
3337
+ this.columnAutoFit = inject(AXP_WIDGET_COLUMN_AUTO_FIT, { optional: true });
3226
3338
  this.isLoading = signal(false, /* @ts-ignore */
3227
3339
  ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
3228
3340
  this.isVisible = signal(true, /* @ts-ignore */
@@ -3429,8 +3541,16 @@ class AXPWidgetRendererDirective {
3429
3541
  /**
3430
3542
  * True when a context store path used in an expression (`context.eval`, `context.options`) is
3431
3543
  * affected by `changedPath`.
3544
+ *
3545
+ * Do not drop `repeaterRowIndexConflicts` or re-enable last-segment matching across rows.
3546
+ * `applicationRoleRows[0].applicationId` and `applicationRoleRows[1].applicationId` share the
3547
+ * suffix `applicationId`; treating that as a match clears sibling repeater fields (roles).
3548
+ * Canonical: `libs/framework/client/layout/widget-core/README.md` (repeater context isolation).
3432
3549
  */
3433
3550
  contextStorePathDependsOnChangedPath(evalPath, changedPath) {
3551
+ if (this.repeaterRowIndexConflicts(evalPath, changedPath)) {
3552
+ return false;
3553
+ }
3434
3554
  const normalizePath = (p) => {
3435
3555
  if (!p)
3436
3556
  return p;
@@ -3454,23 +3574,87 @@ class AXPWidgetRendererDirective {
3454
3574
  }
3455
3575
  return true;
3456
3576
  };
3577
+ const isIndexedOrChildPath = (parent, child) => {
3578
+ if (!parent || !child) {
3579
+ return false;
3580
+ }
3581
+ if (child === parent) {
3582
+ return true;
3583
+ }
3584
+ if (child.startsWith(`${parent}.`) || child.startsWith(`${parent}[`)) {
3585
+ return true;
3586
+ }
3587
+ const wildcardBase = parent.replace(/\.\{[^}]+\}$/, '');
3588
+ if (wildcardBase !== parent) {
3589
+ return child === wildcardBase || child.startsWith(`${wildcardBase}.`) || child.startsWith(`${wildcardBase}[`);
3590
+ }
3591
+ return false;
3592
+ };
3457
3593
  const evalNorm = normalizePath(evalPath);
3458
3594
  const changedNorm = normalizePath(changedPath);
3459
- const rawMatch = evalPath === changedPath || evalPath.startsWith(changedPath + '.') || changedPath.startsWith(evalPath + '.') || isSegmentSuffix(evalPath, changedPath) || isSegmentSuffix(changedPath, evalPath);
3460
- const normMatch = evalNorm === changedNorm || evalNorm.startsWith(changedNorm + '.') || changedNorm.startsWith(evalNorm + '.') || isSegmentSuffix(evalNorm, changedNorm) || isSegmentSuffix(changedNorm, evalNorm);
3595
+ const rawMatch = evalPath === changedPath ||
3596
+ isIndexedOrChildPath(evalPath, changedPath) ||
3597
+ isIndexedOrChildPath(changedPath, evalPath) ||
3598
+ isSegmentSuffix(evalPath, changedPath) ||
3599
+ isSegmentSuffix(changedPath, evalPath);
3600
+ const normMatch = evalNorm === changedNorm ||
3601
+ isIndexedOrChildPath(evalNorm, changedNorm) ||
3602
+ isIndexedOrChildPath(changedNorm, evalNorm) ||
3603
+ isSegmentSuffix(evalNorm, changedNorm) ||
3604
+ isSegmentSuffix(changedNorm, evalNorm);
3461
3605
  if (rawMatch || normMatch) {
3462
3606
  return true;
3463
3607
  }
3464
3608
  return this.isPathAlias(evalPath, changedPath);
3465
3609
  }
3610
+ /**
3611
+ * Different indexed rows of the same array must not share context events.
3612
+ * `rows[0].applicationId` must not match `rows[1].applicationId` via suffix (`applicationId`).
3613
+ * Bare `rows` (no `[n]`) still matches every indexed child so list-wide expressions can refresh.
3614
+ * Do not remove this gate or “simplify” it back to suffix-only matching.
3615
+ */
3616
+ repeaterRowIndexConflicts(evalPath, changedPath) {
3617
+ const indexByArray = (path) => {
3618
+ const found = new Map();
3619
+ const matcher = /([^.\[]+)\[(\d+)\]/g;
3620
+ let match;
3621
+ while ((match = matcher.exec(path)) !== null) {
3622
+ found.set(match[1], match[2]);
3623
+ }
3624
+ return found;
3625
+ };
3626
+ const left = indexByArray(evalPath);
3627
+ const right = indexByArray(changedPath);
3628
+ for (const [name, index] of left) {
3629
+ const other = right.get(name);
3630
+ if (other != null && other !== index) {
3631
+ return true;
3632
+ }
3633
+ }
3634
+ return false;
3635
+ }
3466
3636
  /**
3467
3637
  * Whether an expression template string reads context store paths affected by `changedPath`.
3638
+ *
3639
+ * Unresolved `>field` (parentPath empty) is `field` only — never suffix-match every `*.field`.
3640
+ * Do not fold `>` back into generic suffix matching.
3468
3641
  */
3469
3642
  expressionTextDependsOnContextPath(expressionValue, changedPath) {
3470
3643
  const contextEvalRegex = /context\.eval\(['"]([^'\"]+)['"]\)/g;
3471
3644
  let match;
3472
3645
  while ((match = contextEvalRegex.exec(expressionValue)) !== null) {
3473
- if (this.contextStorePathDependsOnChangedPath(match[1], changedPath)) {
3646
+ const rawPath = match[1];
3647
+ const evalPath = this.resolveRowRelativeContextPath(rawPath);
3648
+ if (rawPath.startsWith('>')) {
3649
+ const relativeField = rawPath.substring(1);
3650
+ if (evalPath === relativeField) {
3651
+ if (changedPath === evalPath) {
3652
+ return true;
3653
+ }
3654
+ continue;
3655
+ }
3656
+ }
3657
+ if (this.contextStorePathDependsOnChangedPath(evalPath, changedPath)) {
3474
3658
  return true;
3475
3659
  }
3476
3660
  }
@@ -3568,16 +3752,42 @@ class AXPWidgetRendererDirective {
3568
3752
  return null;
3569
3753
  }
3570
3754
  }
3755
+ /**
3756
+ * `events.context('>field')` is this row only (exact resolved path).
3757
+ * Do not use `*field` / suffix matching for row-local reset triggers — that fires on every
3758
+ * repeater row that has a field of the same name.
3759
+ */
3571
3760
  hasTriggerDependency(changedPath) {
3572
3761
  const node = this.node();
3573
3762
  const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
3574
3763
  if (!triggers)
3575
3764
  return false;
3576
- // Check if any trigger event depends on the changed path
3577
3765
  for (const trigger of triggers) {
3578
- if (trigger.event && trigger.event.includes(changedPath)) {
3766
+ const event = typeof trigger.event === 'string' ? trigger.event : '';
3767
+ if (event.includes(changedPath)) {
3579
3768
  return true;
3580
3769
  }
3770
+ const contextEvent = /events\.context\(\s*['"]([^'"]*)['"]\s*\)/g;
3771
+ let match;
3772
+ while ((match = contextEvent.exec(event)) !== null) {
3773
+ const pattern = match[1];
3774
+ if (!pattern) {
3775
+ return true;
3776
+ }
3777
+ if (pattern.endsWith('*') && changedPath.startsWith(pattern.slice(0, -1))) {
3778
+ return true;
3779
+ }
3780
+ if (pattern.startsWith('*') && changedPath.endsWith(pattern.slice(1))) {
3781
+ return true;
3782
+ }
3783
+ if (pattern.startsWith('>')) {
3784
+ return changedPath === this.resolveRowRelativeContextPath(pattern);
3785
+ }
3786
+ const resolved = this.resolveRowRelativeContextPath(pattern);
3787
+ if (this.contextStorePathDependsOnChangedPath(resolved, changedPath)) {
3788
+ return true;
3789
+ }
3790
+ }
3581
3791
  }
3582
3792
  return false;
3583
3793
  }
@@ -3779,6 +3989,7 @@ class AXPWidgetRendererDirective {
3779
3989
  const loadEvent = { sender: this, widget: this.instance };
3780
3990
  this.onLoadEvent.next(loadEvent);
3781
3991
  this.onLoad.emit(loadEvent);
3992
+ this.columnAutoFit?.scheduleContentAutoFit({ settle: true });
3782
3993
  }
3783
3994
  catch (error) {
3784
3995
  // console.error('Error loading component:', error);
@@ -3987,6 +4198,20 @@ class AXPWidgetRendererDirective {
3987
4198
  return false;
3988
4199
  }
3989
4200
  }
4201
+ /**
4202
+ * Repeater-relative paths (`>applicationId`) resolve against this widget's parent
4203
+ * row (`applicationRoleRows[0].applicationId`), not every field with the same name.
4204
+ * When `parentPath` is empty the result is the bare field — callers must not treat that
4205
+ * as “any `*.applicationId`”.
4206
+ */
4207
+ resolveRowRelativeContextPath(path) {
4208
+ if (!path.startsWith('>')) {
4209
+ return path;
4210
+ }
4211
+ const rest = path.substring(1);
4212
+ const parent = this.instance?.parentPath?.() ?? '';
4213
+ return parent ? `${parent}.${rest}` : rest;
4214
+ }
3990
4215
  buildExpressionScope() {
3991
4216
  return {
3992
4217
  context: this.getContextScope(),
@@ -3999,10 +4224,7 @@ class AXPWidgetRendererDirective {
3999
4224
  getContextScope() {
4000
4225
  return {
4001
4226
  eval: (path) => {
4002
- //TODO: Handle array index
4003
- const fullPath = path.startsWith('>') ? `${this.instance?.parentPath()}.${path.substring(1)}` : path;
4004
- const value = this.contextService.getValue(fullPath);
4005
- return value;
4227
+ return this.contextService.getValue(this.resolveRowRelativeContextPath(path));
4006
4228
  },
4007
4229
  set: (path, value) => {
4008
4230
  this.contextService.update(path, value, { origin: 'user' });
@@ -4029,6 +4251,10 @@ class AXPWidgetRendererDirective {
4029
4251
  },
4030
4252
  };
4031
4253
  }
4254
+ /**
4255
+ * `events.context('>…')` is exact row path only. Do not OR it with
4256
+ * `contextStorePathDependsOnChangedPath` — that suffix-matches sibling rows.
4257
+ */
4032
4258
  getEventScope() {
4033
4259
  return {
4034
4260
  context: (...paths) => {
@@ -4043,18 +4269,19 @@ class AXPWidgetRendererDirective {
4043
4269
  return false;
4044
4270
  }
4045
4271
  return activePaths.some((path) => {
4046
- // Pattern: "prefix*" - matches paths that start with prefix
4047
- if (path.endsWith('*')) {
4048
- const prefix = path.substring(0, path.length - 1);
4272
+ if (path.startsWith('>')) {
4273
+ return c.path === this.resolveRowRelativeContextPath(path);
4274
+ }
4275
+ const resolved = this.resolveRowRelativeContextPath(path);
4276
+ if (resolved.endsWith('*')) {
4277
+ const prefix = resolved.substring(0, resolved.length - 1);
4049
4278
  return c.path.startsWith(prefix);
4050
4279
  }
4051
- // Pattern: "*suffix" - matches paths that end with suffix
4052
- if (path.startsWith('*')) {
4053
- const suffix = path.substring(1);
4280
+ if (resolved.startsWith('*')) {
4281
+ const suffix = resolved.substring(1);
4054
4282
  return c.path.endsWith(suffix);
4055
4283
  }
4056
- // Exact match
4057
- return c.path === path;
4284
+ return c.path === resolved || this.contextStorePathDependsOnChangedPath(resolved, c.path);
4058
4285
  });
4059
4286
  }));
4060
4287
  },