@acorex-platform/framework-client 0.0.59 → 0.0.60

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 +165 -28
  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 +39 -0
  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
@@ -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 */ []));
@@ -3429,8 +3452,16 @@ class AXPWidgetRendererDirective {
3429
3452
  /**
3430
3453
  * True when a context store path used in an expression (`context.eval`, `context.options`) is
3431
3454
  * affected by `changedPath`.
3455
+ *
3456
+ * Do not drop `repeaterRowIndexConflicts` or re-enable last-segment matching across rows.
3457
+ * `applicationRoleRows[0].applicationId` and `applicationRoleRows[1].applicationId` share the
3458
+ * suffix `applicationId`; treating that as a match clears sibling repeater fields (roles).
3459
+ * Canonical: `libs/framework/client/layout/widget-core/README.md` (repeater context isolation).
3432
3460
  */
3433
3461
  contextStorePathDependsOnChangedPath(evalPath, changedPath) {
3462
+ if (this.repeaterRowIndexConflicts(evalPath, changedPath)) {
3463
+ return false;
3464
+ }
3434
3465
  const normalizePath = (p) => {
3435
3466
  if (!p)
3436
3467
  return p;
@@ -3454,23 +3485,87 @@ class AXPWidgetRendererDirective {
3454
3485
  }
3455
3486
  return true;
3456
3487
  };
3488
+ const isIndexedOrChildPath = (parent, child) => {
3489
+ if (!parent || !child) {
3490
+ return false;
3491
+ }
3492
+ if (child === parent) {
3493
+ return true;
3494
+ }
3495
+ if (child.startsWith(`${parent}.`) || child.startsWith(`${parent}[`)) {
3496
+ return true;
3497
+ }
3498
+ const wildcardBase = parent.replace(/\.\{[^}]+\}$/, '');
3499
+ if (wildcardBase !== parent) {
3500
+ return child === wildcardBase || child.startsWith(`${wildcardBase}.`) || child.startsWith(`${wildcardBase}[`);
3501
+ }
3502
+ return false;
3503
+ };
3457
3504
  const evalNorm = normalizePath(evalPath);
3458
3505
  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);
3506
+ const rawMatch = evalPath === changedPath ||
3507
+ isIndexedOrChildPath(evalPath, changedPath) ||
3508
+ isIndexedOrChildPath(changedPath, evalPath) ||
3509
+ isSegmentSuffix(evalPath, changedPath) ||
3510
+ isSegmentSuffix(changedPath, evalPath);
3511
+ const normMatch = evalNorm === changedNorm ||
3512
+ isIndexedOrChildPath(evalNorm, changedNorm) ||
3513
+ isIndexedOrChildPath(changedNorm, evalNorm) ||
3514
+ isSegmentSuffix(evalNorm, changedNorm) ||
3515
+ isSegmentSuffix(changedNorm, evalNorm);
3461
3516
  if (rawMatch || normMatch) {
3462
3517
  return true;
3463
3518
  }
3464
3519
  return this.isPathAlias(evalPath, changedPath);
3465
3520
  }
3521
+ /**
3522
+ * Different indexed rows of the same array must not share context events.
3523
+ * `rows[0].applicationId` must not match `rows[1].applicationId` via suffix (`applicationId`).
3524
+ * Bare `rows` (no `[n]`) still matches every indexed child so list-wide expressions can refresh.
3525
+ * Do not remove this gate or “simplify” it back to suffix-only matching.
3526
+ */
3527
+ repeaterRowIndexConflicts(evalPath, changedPath) {
3528
+ const indexByArray = (path) => {
3529
+ const found = new Map();
3530
+ const matcher = /([^.\[]+)\[(\d+)\]/g;
3531
+ let match;
3532
+ while ((match = matcher.exec(path)) !== null) {
3533
+ found.set(match[1], match[2]);
3534
+ }
3535
+ return found;
3536
+ };
3537
+ const left = indexByArray(evalPath);
3538
+ const right = indexByArray(changedPath);
3539
+ for (const [name, index] of left) {
3540
+ const other = right.get(name);
3541
+ if (other != null && other !== index) {
3542
+ return true;
3543
+ }
3544
+ }
3545
+ return false;
3546
+ }
3466
3547
  /**
3467
3548
  * Whether an expression template string reads context store paths affected by `changedPath`.
3549
+ *
3550
+ * Unresolved `>field` (parentPath empty) is `field` only — never suffix-match every `*.field`.
3551
+ * Do not fold `>` back into generic suffix matching.
3468
3552
  */
3469
3553
  expressionTextDependsOnContextPath(expressionValue, changedPath) {
3470
3554
  const contextEvalRegex = /context\.eval\(['"]([^'\"]+)['"]\)/g;
3471
3555
  let match;
3472
3556
  while ((match = contextEvalRegex.exec(expressionValue)) !== null) {
3473
- if (this.contextStorePathDependsOnChangedPath(match[1], changedPath)) {
3557
+ const rawPath = match[1];
3558
+ const evalPath = this.resolveRowRelativeContextPath(rawPath);
3559
+ if (rawPath.startsWith('>')) {
3560
+ const relativeField = rawPath.substring(1);
3561
+ if (evalPath === relativeField) {
3562
+ if (changedPath === evalPath) {
3563
+ return true;
3564
+ }
3565
+ continue;
3566
+ }
3567
+ }
3568
+ if (this.contextStorePathDependsOnChangedPath(evalPath, changedPath)) {
3474
3569
  return true;
3475
3570
  }
3476
3571
  }
@@ -3568,16 +3663,42 @@ class AXPWidgetRendererDirective {
3568
3663
  return null;
3569
3664
  }
3570
3665
  }
3666
+ /**
3667
+ * `events.context('>field')` is this row only (exact resolved path).
3668
+ * Do not use `*field` / suffix matching for row-local reset triggers — that fires on every
3669
+ * repeater row that has a field of the same name.
3670
+ */
3571
3671
  hasTriggerDependency(changedPath) {
3572
3672
  const node = this.node();
3573
3673
  const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
3574
3674
  if (!triggers)
3575
3675
  return false;
3576
- // Check if any trigger event depends on the changed path
3577
3676
  for (const trigger of triggers) {
3578
- if (trigger.event && trigger.event.includes(changedPath)) {
3677
+ const event = typeof trigger.event === 'string' ? trigger.event : '';
3678
+ if (event.includes(changedPath)) {
3579
3679
  return true;
3580
3680
  }
3681
+ const contextEvent = /events\.context\(\s*['"]([^'"]*)['"]\s*\)/g;
3682
+ let match;
3683
+ while ((match = contextEvent.exec(event)) !== null) {
3684
+ const pattern = match[1];
3685
+ if (!pattern) {
3686
+ return true;
3687
+ }
3688
+ if (pattern.endsWith('*') && changedPath.startsWith(pattern.slice(0, -1))) {
3689
+ return true;
3690
+ }
3691
+ if (pattern.startsWith('*') && changedPath.endsWith(pattern.slice(1))) {
3692
+ return true;
3693
+ }
3694
+ if (pattern.startsWith('>')) {
3695
+ return changedPath === this.resolveRowRelativeContextPath(pattern);
3696
+ }
3697
+ const resolved = this.resolveRowRelativeContextPath(pattern);
3698
+ if (this.contextStorePathDependsOnChangedPath(resolved, changedPath)) {
3699
+ return true;
3700
+ }
3701
+ }
3581
3702
  }
3582
3703
  return false;
3583
3704
  }
@@ -3987,6 +4108,20 @@ class AXPWidgetRendererDirective {
3987
4108
  return false;
3988
4109
  }
3989
4110
  }
4111
+ /**
4112
+ * Repeater-relative paths (`>applicationId`) resolve against this widget's parent
4113
+ * row (`applicationRoleRows[0].applicationId`), not every field with the same name.
4114
+ * When `parentPath` is empty the result is the bare field — callers must not treat that
4115
+ * as “any `*.applicationId`”.
4116
+ */
4117
+ resolveRowRelativeContextPath(path) {
4118
+ if (!path.startsWith('>')) {
4119
+ return path;
4120
+ }
4121
+ const rest = path.substring(1);
4122
+ const parent = this.instance?.parentPath?.() ?? '';
4123
+ return parent ? `${parent}.${rest}` : rest;
4124
+ }
3990
4125
  buildExpressionScope() {
3991
4126
  return {
3992
4127
  context: this.getContextScope(),
@@ -3999,10 +4134,7 @@ class AXPWidgetRendererDirective {
3999
4134
  getContextScope() {
4000
4135
  return {
4001
4136
  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;
4137
+ return this.contextService.getValue(this.resolveRowRelativeContextPath(path));
4006
4138
  },
4007
4139
  set: (path, value) => {
4008
4140
  this.contextService.update(path, value, { origin: 'user' });
@@ -4029,6 +4161,10 @@ class AXPWidgetRendererDirective {
4029
4161
  },
4030
4162
  };
4031
4163
  }
4164
+ /**
4165
+ * `events.context('>…')` is exact row path only. Do not OR it with
4166
+ * `contextStorePathDependsOnChangedPath` — that suffix-matches sibling rows.
4167
+ */
4032
4168
  getEventScope() {
4033
4169
  return {
4034
4170
  context: (...paths) => {
@@ -4043,18 +4179,19 @@ class AXPWidgetRendererDirective {
4043
4179
  return false;
4044
4180
  }
4045
4181
  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);
4182
+ if (path.startsWith('>')) {
4183
+ return c.path === this.resolveRowRelativeContextPath(path);
4184
+ }
4185
+ const resolved = this.resolveRowRelativeContextPath(path);
4186
+ if (resolved.endsWith('*')) {
4187
+ const prefix = resolved.substring(0, resolved.length - 1);
4049
4188
  return c.path.startsWith(prefix);
4050
4189
  }
4051
- // Pattern: "*suffix" - matches paths that end with suffix
4052
- if (path.startsWith('*')) {
4053
- const suffix = path.substring(1);
4190
+ if (resolved.startsWith('*')) {
4191
+ const suffix = resolved.substring(1);
4054
4192
  return c.path.endsWith(suffix);
4055
4193
  }
4056
- // Exact match
4057
- return c.path === path;
4194
+ return c.path === resolved || this.contextStorePathDependsOnChangedPath(resolved, c.path);
4058
4195
  });
4059
4196
  }));
4060
4197
  },