@acorex/platform 20.3.0-next.7 → 20.3.0-next.9

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 (62) hide show
  1. package/common/index.d.ts +0 -2
  2. package/core/index.d.ts +386 -47
  3. package/fesm2022/acorex-platform-common.mjs +5 -10
  4. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-core.mjs +427 -125
  6. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-layout-builder.mjs +422 -26
  8. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-components.mjs +1843 -111
  10. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-designer.mjs +19 -12
  12. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-entity.mjs +463 -510
  14. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-views.mjs +23 -15
  16. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  17. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs → acorex-platform-themes-default-entity-master-create-view.component-Ct-ri59W.mjs} +3 -3
  18. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Ct-ri59W.mjs.map +1 -0
  19. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-gQIK6PIx.mjs → acorex-platform-themes-default-entity-master-list-view.component-7BB4LdjK.mjs} +9 -9
  20. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-7BB4LdjK.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs +101 -0
  22. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-themes-default.mjs +8 -8
  24. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  25. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-KpZWpnOJ.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs} +21 -11
  26. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs → acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs} +2 -2
  28. package/fesm2022/acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-BvOiVCgt.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs} +20 -5
  30. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-BW0rfkjk.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs} +20 -5
  32. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-themes-shared.mjs +205 -38
  34. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  35. package/fesm2022/{acorex-platform-widgets-checkbox-widget-view.component-C-4bWr9G.mjs → acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs} +51 -35
  36. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs → acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs} +3 -3
  38. package/fesm2022/acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs → acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs} +2 -2
  40. package/fesm2022/acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs.map +1 -0
  41. package/fesm2022/{acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs → acorex-platform-widgets-tabular-data-edit-popup.component-CMqq_iOj.mjs} +8 -8
  42. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CMqq_iOj.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-widgets.mjs +4590 -5073
  44. package/fesm2022/acorex-platform-widgets.mjs.map +1 -1
  45. package/layout/builder/index.d.ts +62 -8
  46. package/layout/components/index.d.ts +780 -54
  47. package/layout/designer/index.d.ts +4 -2
  48. package/layout/entity/index.d.ts +53 -10
  49. package/package.json +9 -9
  50. package/widgets/index.d.ts +1441 -433
  51. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs.map +0 -1
  52. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-gQIK6PIx.mjs.map +0 -1
  53. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs +0 -101
  54. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs.map +0 -1
  55. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-KpZWpnOJ.mjs.map +0 -1
  56. package/fesm2022/acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs.map +0 -1
  57. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BvOiVCgt.mjs.map +0 -1
  58. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BW0rfkjk.mjs.map +0 -1
  59. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-C-4bWr9G.mjs.map +0 -1
  60. package/fesm2022/acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs.map +0 -1
  61. package/fesm2022/acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs.map +0 -1
  62. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs.map +0 -1
@@ -284,13 +284,10 @@ const AXPWidgetsCatalog = {
284
284
  color: 'color-editor',
285
285
  contact: 'contact-editor',
286
286
  dateTime: 'date-time-editor',
287
- email: 'email-editor',
288
287
  largeText: 'large-text-editor',
289
- link: 'link-editor',
290
288
  number: 'number-editor',
291
289
  numberUnit: 'number-unit-editor',
292
290
  password: 'password-editor',
293
- phone: 'phone-editor',
294
291
  richText: 'rich-text-editor',
295
292
  select: 'select-editor',
296
293
  selectionList: 'selection-list-editor',
@@ -317,7 +314,7 @@ const AXPWidgetsCatalog = {
317
314
  advancedGridItem: 'advanced-grid-item-layout',
318
315
  grid: 'grid-layout',
319
316
  gridItem: 'grid-item-layout',
320
- gridRow: 'grid-row-layout',
317
+ // gridRow: 'grid-row-layout',
321
318
  widgetSelector: 'widget-selector',
322
319
  template: 'template',
323
320
  templateDesigner: 'template-designer',
@@ -327,6 +324,8 @@ const AXPWidgetsCatalog = {
327
324
  border: 'border',
328
325
  flexLayout: 'flex-layout',
329
326
  flexItem: 'flex-item-layout',
327
+ tableLayout: 'table-layout',
328
+ tableItem: 'table-item-layout',
330
329
  avatar: 'avatar',
331
330
  themePaletteChooser: 'theme-palette-chooser',
332
331
  themeModeChooser: 'theme-mode-chooser',
@@ -469,6 +468,7 @@ class AXPBaseWidgetComponent extends AXPLayoutElement {
469
468
  this.config = this.token.config;
470
469
  this.node = this.token.node;
471
470
  this.name = this.token.node.name;
471
+ this.component = this;
472
472
  this._options = signal(this.token.options ?? {}, ...(ngDevMode ? [{ debugName: "_options" }] : []));
473
473
  this.options = this._options.asReadonly();
474
474
  this.onOptionsChanged = new Subject();
@@ -996,7 +996,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
996
996
  class AXPGridBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
997
997
  constructor() {
998
998
  super(...arguments);
999
- this.grid = computed(() => this.options()?.grid, ...(ngDevMode ? [{ debugName: "grid" }] : []));
999
+ this.grid = computed(() => this.options()?.['grid'], ...(ngDevMode ? [{ debugName: "grid" }] : []));
1000
1000
  this.hostGridStyle = computed(() => {
1001
1001
  const style = { ...this.inlineStyle() };
1002
1002
  const g = this.grid()?.default;
@@ -1072,6 +1072,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
1072
1072
  type: Injectable
1073
1073
  }] });
1074
1074
 
1075
+ class AXPTableBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
1076
+ constructor() {
1077
+ super(...arguments);
1078
+ this.hostTableClass = computed(() => ({
1079
+ ...this.blockClass(),
1080
+ }), ...(ngDevMode ? [{ debugName: "hostTableClass" }] : []));
1081
+ this.hostTableStyle = computed(() => {
1082
+ const style = { ...this.blockStyle() };
1083
+ style['overflow-x'] = 'auto';
1084
+ return style;
1085
+ }, ...(ngDevMode ? [{ debugName: "hostTableStyle" }] : []));
1086
+ this.hostClass = computed(() => this.hostTableClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
1087
+ this.hostStyle = computed(() => this.hostTableStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
1088
+ }
1089
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1090
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent }); }
1091
+ }
1092
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, decorators: [{
1093
+ type: Injectable
1094
+ }] });
1095
+
1096
+ class AXPTableItemOpsBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
1097
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1098
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent }); }
1099
+ }
1100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, decorators: [{
1101
+ type: Injectable
1102
+ }] });
1103
+
1104
+ class AXPTableItemBaseLayoutWidgetComponent extends AXPTableItemOpsBaseLayoutWidgetComponent {
1105
+ constructor() {
1106
+ super(...arguments);
1107
+ this.colSpan = computed(() => {
1108
+ const v = this.options()?.colSpan;
1109
+ return v ? Math.max(1, v) : 1;
1110
+ }, ...(ngDevMode ? [{ debugName: "colSpan" }] : []));
1111
+ this.rowSpan = computed(() => {
1112
+ const v = this.options()?.rowSpan;
1113
+ return v ? Math.max(1, v) : 1;
1114
+ }, ...(ngDevMode ? [{ debugName: "rowSpan" }] : []));
1115
+ this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
1116
+ this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
1117
+ }
1118
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1119
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent }); }
1120
+ }
1121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, decorators: [{
1122
+ type: Injectable
1123
+ }] });
1124
+
1075
1125
  class AXPWidgetRegistryService {
1076
1126
  /**
1077
1127
  *
@@ -1363,6 +1413,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
1363
1413
  }] });
1364
1414
 
1365
1415
  class AXPWidgetRendererDirective {
1416
+ //#endregion
1366
1417
  //#endregion
1367
1418
  constructor() {
1368
1419
  this.parentNode = input(...(ngDevMode ? [undefined, { debugName: "parentNode" }] : []));
@@ -1389,29 +1440,304 @@ class AXPWidgetRendererDirective {
1389
1440
  this.renderTimeoutId = null;
1390
1441
  this.hasInitialRender = false;
1391
1442
  this.onContextChanged = new Subject();
1443
+ //#region ---- Performance Optimization Properties ----
1444
+ this.contextUpdateQueue = new Set();
1445
+ this.contextUpdateTimeout = null;
1446
+ this.CONTEXT_UPDATE_DEBOUNCE_MS = 4; // ~250fps for large forms
1447
+ // Removed visibility detection - not needed for current implementation
1448
+ // Expression result caching
1449
+ this.expressionCache = new Map();
1450
+ this.EXPRESSION_CACHE_TTL = 500; // Cache for 500ms (increased for better hit rate)
1451
+ // Options change tracking
1452
+ this.lastAppliedOptions = null;
1453
+ // Expression result tracking
1454
+ this.lastExpressionResults = new Map();
1455
+ // Buffer for context changes that happen before initial render completes
1456
+ this.preRenderContextQueue = new Set();
1392
1457
  effect(async () => {
1393
1458
  const changed = this.contextService.changeEvent();
1394
1459
  // Don't trigger re-render during initial setup
1395
1460
  if (!this.hasInitialRender) {
1461
+ if (changed.path) {
1462
+ this.preRenderContextQueue.add(changed.path);
1463
+ console.log(`📝 [${this.node().type}] Buffered pre-render context change: ${changed.path}`);
1464
+ }
1396
1465
  return;
1397
1466
  }
1398
- if ((await this.updateOptionsBasedOnContext()) > 0) {
1399
- this.applyOptions();
1400
- }
1401
- if (this.checkFormulaForUpdate(this.node().formula, changed.path)) {
1402
- await this.updateValueBasedOnFormula();
1403
- }
1404
- //
1405
- if (changed.path) {
1406
- this.onContextChanged.next({ path: changed.path });
1467
+ // CRITICAL PERFORMANCE FIX: Only respond to relevant context changes
1468
+ if (changed.path && this.isRelevantContextChange(changed.path)) {
1469
+ console.log(`🎯 [${this.node().type}] Context change detected: ${changed.path}`);
1470
+ this.queueContextUpdate(changed.path);
1407
1471
  }
1408
1472
  });
1409
1473
  this.builderService.onRefresh.pipe(this.unsubscriber.takeUntilDestroy).subscribe(async () => {
1410
- if ((await this.updateOptionsBasedOnContext()) > 0) {
1411
- this.applyOptions();
1474
+ await this.processBatchedUpdates();
1475
+ });
1476
+ }
1477
+ //#region ---- Expression Caching Methods ----
1478
+ getCachedExpressionResult(expressionKey) {
1479
+ const cached = this.expressionCache.get(expressionKey);
1480
+ if (cached && performance.now() - cached.timestamp < this.EXPRESSION_CACHE_TTL) {
1481
+ console.log(`💾 [${this.node().type}] Using cached expression result for '${expressionKey}'`);
1482
+ return cached.value;
1483
+ }
1484
+ return null;
1485
+ }
1486
+ setCachedExpressionResult(expressionKey, value) {
1487
+ this.expressionCache.set(expressionKey, {
1488
+ value,
1489
+ timestamp: performance.now(),
1490
+ });
1491
+ }
1492
+ clearExpressionCache() {
1493
+ this.expressionCache.clear();
1494
+ }
1495
+ isPathAffectingExpressions(changedPath) {
1496
+ console.log(`🔍 [${this.node().type}] Checking if path '${changedPath}' affects expressions. Expression count: ${this.expressionEvaluators.size}`);
1497
+ // If widget has no expressions, no need to clear cache
1498
+ if (this.expressionEvaluators.size === 0) {
1499
+ console.log(`🔍 [${this.node().type}] Path '${changedPath}' - no expressions, keeping cache`);
1500
+ return false;
1501
+ }
1502
+ // Use the same logic as hasExpressionDependency to check for actual dependencies
1503
+ if (this.hasExpressionDependency(changedPath)) {
1504
+ console.log(`🔍 [${this.node().type}] Path '${changedPath}' affects expressions - clearing cache`);
1505
+ return true;
1506
+ }
1507
+ console.log(`🔍 [${this.node().type}] Path '${changedPath}' does not affect expressions - keeping cache`);
1508
+ return false;
1509
+ }
1510
+ //#endregion
1511
+ //#region ---- Context Batching Methods ----
1512
+ queueContextUpdate(path) {
1513
+ if (path) {
1514
+ console.log(`🔄 [${this.node().type}] Queueing context update for path: ${path}`);
1515
+ this.contextUpdateQueue.add(path);
1516
+ // Clear existing timeout
1517
+ if (this.contextUpdateTimeout) {
1518
+ clearTimeout(this.contextUpdateTimeout);
1412
1519
  }
1520
+ // Debounce updates
1521
+ this.contextUpdateTimeout = setTimeout(() => {
1522
+ console.log(`⚡ [${this.node().type}] Processing batched updates for ${this.contextUpdateQueue.size} paths`);
1523
+ this.processBatchedUpdates();
1524
+ }, this.CONTEXT_UPDATE_DEBOUNCE_MS);
1525
+ }
1526
+ }
1527
+ async processBatchedUpdates() {
1528
+ if (this.contextUpdateQueue.size === 0)
1529
+ return;
1530
+ const startTime = performance.now();
1531
+ const paths = Array.from(this.contextUpdateQueue);
1532
+ this.contextUpdateQueue.clear();
1533
+ console.log(`📊 [${this.node().type}] Processing ${paths.length} paths:`, paths);
1534
+ // Clear expression cache only if changed paths affect this widget's expressions
1535
+ const shouldClearCache = paths.some((path) => this.isPathAffectingExpressions(path));
1536
+ if (shouldClearCache) {
1537
+ console.log(`🗑️ [${this.node().type}] Clearing expression cache due to expression-affecting path change`);
1538
+ this.clearExpressionCache();
1539
+ }
1540
+ // Process updates in batches
1541
+ const optionsStartTime = performance.now();
1542
+ const hasOptionsUpdate = await this.updateOptionsBasedOnContext();
1543
+ const optionsTime = performance.now() - optionsStartTime;
1544
+ if (typeof hasOptionsUpdate === 'number' && hasOptionsUpdate > 0) {
1545
+ console.log(`🔧 [${this.node().type}] Options updated (${optionsTime.toFixed(2)}ms)`);
1546
+ this.applyOptions();
1547
+ }
1548
+ // Check formulas for any of the changed paths
1549
+ const formulaStartTime = performance.now();
1550
+ const formulaNeedsUpdate = paths.some((path) => this.checkFormulaForUpdate(this.node().formula, path));
1551
+ if (formulaNeedsUpdate) {
1552
+ console.log(`🧮 [${this.node().type}] Formula needs update`);
1553
+ await this.updateValueBasedOnFormula();
1554
+ }
1555
+ const formulaTime = performance.now() - formulaStartTime;
1556
+ // Emit context changes
1557
+ paths.forEach((path) => {
1558
+ this.onContextChanged.next({ path });
1413
1559
  });
1560
+ const totalTime = performance.now() - startTime;
1561
+ console.log(`✅ [${this.node().type}] Batch processing completed in ${totalTime.toFixed(2)}ms (options: ${optionsTime.toFixed(2)}ms, formula: ${formulaTime.toFixed(2)}ms)`);
1562
+ }
1563
+ //#endregion
1564
+ //#region ---- Context Relevance Filtering ----
1565
+ isRelevantContextChange(changedPath) {
1566
+ const node = this.node();
1567
+ // 1. Direct path match - widget's own field
1568
+ if (node.path === changedPath) {
1569
+ return true;
1570
+ }
1571
+ // 2. Parent path match - widget is inside the changed container
1572
+ if (node.path && changedPath.startsWith(node.path + '.')) {
1573
+ return true;
1574
+ }
1575
+ // 3. Child path match - changed field is inside this widget's container
1576
+ if (node.path && node.path.startsWith(changedPath + '.')) {
1577
+ return true;
1578
+ }
1579
+ // 4. Expression dependency check - if widget has expressions that depend on this path
1580
+ if (this.hasExpressionDependency(changedPath)) {
1581
+ return true;
1582
+ }
1583
+ // 5. Formula dependency check - if widget's formula depends on this path
1584
+ if (node.formula && this.checkFormulaForUpdate(node.formula, changedPath)) {
1585
+ return true;
1586
+ }
1587
+ // 6. Trigger dependency check - if widget has triggers that depend on this path
1588
+ if (this.hasTriggerDependency(changedPath)) {
1589
+ return true;
1590
+ }
1591
+ return false;
1592
+ }
1593
+ hasExpressionDependency(changedPath) {
1594
+ // Check if any cached expressions depend on the changed path
1595
+ for (const [path, evaluator] of this.expressionEvaluators) {
1596
+ // Check if the expression path itself contains the changed path
1597
+ if (path.includes(changedPath)) {
1598
+ return true;
1599
+ }
1600
+ // Parse the actual expression content to check for context.eval() calls
1601
+ // We need to get the original expression string to analyze it
1602
+ const node = this.node();
1603
+ const expressionValue = this.getExpressionValueFromNode(node, path);
1604
+ if (expressionValue && typeof expressionValue === 'string') {
1605
+ // Look for context.eval() calls that reference the changed path
1606
+ const contextEvalRegex = /context\.eval\(['"]([^'\"]+)['"]\)/g;
1607
+ let match;
1608
+ while ((match = contextEvalRegex.exec(expressionValue)) !== null) {
1609
+ const evalPath = match[1];
1610
+ // Normalize Id-suffixed segments to dot-id form (e.g., 'typeId' -> 'type.id', 'party.typeId' -> 'party.type.id')
1611
+ const normalizePath = (p) => {
1612
+ if (!p)
1613
+ return p;
1614
+ const parts = p.split('.');
1615
+ const last = parts[parts.length - 1];
1616
+ if (last && last.endsWith('Id')) {
1617
+ parts.splice(parts.length - 1, 1, last.slice(0, -2), 'id');
1618
+ }
1619
+ return parts.join('.');
1620
+ };
1621
+ const isSegmentSuffix = (a, b) => {
1622
+ if (!a || !b)
1623
+ return false;
1624
+ const pa = a.split('.');
1625
+ const pb = b.split('.');
1626
+ if (pb.length > pa.length)
1627
+ return false;
1628
+ for (let i = 1; i <= pb.length; i++) {
1629
+ if (pa[pa.length - i] !== pb[pb.length - i])
1630
+ return false;
1631
+ }
1632
+ return true;
1633
+ };
1634
+ const evalNorm = normalizePath(evalPath);
1635
+ const changedNorm = normalizePath(changedPath);
1636
+ // Debug log for dependency check
1637
+ console.log(`🧭 [${this.node().type}] dep-check expr='${path}', changed='${changedPath}', eval='${evalPath}', evalNorm='${evalNorm}', changedNorm='${changedNorm}'`);
1638
+ // Generic direct and hierarchical dependency checks (raw and normalized)
1639
+ const rawMatch = evalPath === changedPath ||
1640
+ evalPath.startsWith(changedPath + '.') ||
1641
+ changedPath.startsWith(evalPath + '.') ||
1642
+ isSegmentSuffix(evalPath, changedPath) ||
1643
+ isSegmentSuffix(changedPath, evalPath);
1644
+ const normMatch = evalNorm === changedNorm ||
1645
+ evalNorm.startsWith(changedNorm + '.') ||
1646
+ changedNorm.startsWith(evalNorm + '.') ||
1647
+ isSegmentSuffix(evalNorm, changedNorm) ||
1648
+ isSegmentSuffix(changedNorm, evalNorm);
1649
+ if (rawMatch || normMatch) {
1650
+ console.log(`🔍 [${this.node().type}] Expression '${path}' depends on '${changedPath}' via context.eval('${evalPath}') (generic match)`);
1651
+ return true;
1652
+ }
1653
+ // Check for path aliases/mappings (e.g., typeId.id <-> type.id)
1654
+ if (this.isPathAlias(evalPath, changedPath)) {
1655
+ console.log(`🔍 [${this.node().type}] Expression '${path}' depends on '${changedPath}' via context.eval('${evalPath}') (path alias)`);
1656
+ return true;
1657
+ }
1658
+ }
1659
+ }
1660
+ }
1661
+ return false;
1662
+ }
1663
+ isPathAlias(evalPath, changedPath) {
1664
+ // Dynamic path alias detection based on 'Id' suffix pattern
1665
+ // Examples: typeId.id <-> type.id, categoryId.name <-> category.name
1666
+ // Check if evalPath ends with 'Id' and has a property
1667
+ if (evalPath.endsWith('Id') && evalPath.includes('.')) {
1668
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
1669
+ const property = evalPath.substring(evalPath.lastIndexOf('.') + 1);
1670
+ const mappedPath = `${basePath}.${property}`;
1671
+ if (changedPath === mappedPath) {
1672
+ console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1673
+ return true;
1674
+ }
1675
+ }
1676
+ // Check if changedPath ends with 'Id' and has a property
1677
+ if (changedPath.endsWith('Id') && changedPath.includes('.')) {
1678
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
1679
+ const property = changedPath.substring(changedPath.lastIndexOf('.') + 1);
1680
+ const mappedPath = `${basePath}.${property}`;
1681
+ if (evalPath === mappedPath) {
1682
+ console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1683
+ return true;
1684
+ }
1685
+ }
1686
+ // Check for direct 'Id' suffix mapping (e.g., typeId <-> type.id)
1687
+ if (evalPath.endsWith('Id') && !evalPath.includes('.')) {
1688
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
1689
+ const mappedPath = `${basePath}.id`;
1690
+ if (changedPath === mappedPath) {
1691
+ console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1692
+ return true;
1693
+ }
1694
+ }
1695
+ if (changedPath.endsWith('Id') && !changedPath.includes('.')) {
1696
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
1697
+ const mappedPath = `${basePath}.id`;
1698
+ if (evalPath === mappedPath) {
1699
+ console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1700
+ return true;
1701
+ }
1702
+ }
1703
+ return false;
1414
1704
  }
1705
+ getExpressionValueFromNode(node, path) {
1706
+ try {
1707
+ // Navigate through the node structure to find the expression value
1708
+ const pathParts = path.split('.');
1709
+ let current = node.options || {};
1710
+ // Navigate through the path parts
1711
+ for (const part of pathParts) {
1712
+ if (current && typeof current === 'object' && part in current) {
1713
+ current = current[part];
1714
+ }
1715
+ else {
1716
+ return null;
1717
+ }
1718
+ }
1719
+ return typeof current === 'string' ? current : null;
1720
+ }
1721
+ catch (error) {
1722
+ console.error('Error extracting expression value from node:', error);
1723
+ return null;
1724
+ }
1725
+ }
1726
+ hasTriggerDependency(changedPath) {
1727
+ const node = this.node();
1728
+ const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
1729
+ if (!triggers)
1730
+ return false;
1731
+ // Check if any trigger event depends on the changed path
1732
+ for (const trigger of triggers) {
1733
+ if (trigger.event && trigger.event.includes(changedPath)) {
1734
+ return true;
1735
+ }
1736
+ }
1737
+ return false;
1738
+ }
1739
+ //#endregion
1740
+ // Removed visibility detection methods - not needed for current implementation
1415
1741
  // Detect input changes
1416
1742
  ngOnChanges(changes) {
1417
1743
  if (changes['mode'] || changes['node']) {
@@ -1437,6 +1763,9 @@ class AXPWidgetRendererDirective {
1437
1763
  if (this.renderTimeoutId) {
1438
1764
  clearTimeout(this.renderTimeoutId);
1439
1765
  }
1766
+ if (this.contextUpdateTimeout) {
1767
+ clearTimeout(this.contextUpdateTimeout);
1768
+ }
1440
1769
  if (this.componentRef) {
1441
1770
  this.componentRef.destroy();
1442
1771
  }
@@ -1481,6 +1810,9 @@ class AXPWidgetRendererDirective {
1481
1810
  }
1482
1811
  //
1483
1812
  this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
1813
+ this.expressionEvaluators.clear();
1814
+ // Register expressions from widget defaults and node options to cover related-entity cases
1815
+ this.preprocessAndInitialOptions(cloneDeep(widget?.options));
1484
1816
  this.preprocessAndInitialOptions(cloneDeep(this.node().options));
1485
1817
  await this.updateOptionsBasedOnContext();
1486
1818
  //
@@ -1533,6 +1865,19 @@ class AXPWidgetRendererDirective {
1533
1865
  loadingRef.destroy();
1534
1866
  // Mark that initial render is complete
1535
1867
  this.hasInitialRender = true;
1868
+ // Re-evaluate expressions after initial render to catch late-arriving related context
1869
+ if (this.expressionEvaluators.size > 0) {
1870
+ console.log(`🔄 [${this.node().type}] Re-evaluating expressions after initial render`);
1871
+ await this.updateOptionsBasedOnContext();
1872
+ this.applyOptions();
1873
+ }
1874
+ // Process any buffered pre-render context changes now that the component is ready
1875
+ if (this.preRenderContextQueue.size > 0) {
1876
+ console.log(`🚀 [${this.node().type}] Processing ${this.preRenderContextQueue.size} buffered pre-render changes`);
1877
+ this.preRenderContextQueue.forEach((p) => this.contextUpdateQueue.add(p));
1878
+ this.preRenderContextQueue.clear();
1879
+ await this.processBatchedUpdates();
1880
+ }
1536
1881
  }
1537
1882
  catch (error) {
1538
1883
  console.error('Error loading component:', error);
@@ -1544,9 +1889,26 @@ class AXPWidgetRendererDirective {
1544
1889
  applyOptions() {
1545
1890
  if (!this.instance)
1546
1891
  return;
1547
- this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
1548
- this.instance.setOptions(this.mergedOptions());
1892
+ const currentOptions = this.mergedOptions();
1893
+ // Check if options have actually changed
1894
+ if (this.hasOptionsChanged(currentOptions)) {
1895
+ console.log('applyOptions', this.node().path, '- options changed');
1896
+ this._options.update((val) => ({ ...val, ...currentOptions }));
1897
+ this.instance.setOptions(currentOptions);
1898
+ this.lastAppliedOptions = cloneDeep(currentOptions); // Deep clone using Lodash
1899
+ }
1900
+ else {
1901
+ console.log('applyOptions', this.node().path, '- no changes, skipping');
1902
+ }
1549
1903
  }
1904
+ hasOptionsChanged(newOptions) {
1905
+ if (!this.lastAppliedOptions) {
1906
+ return true; // First time, always apply
1907
+ }
1908
+ // Deep comparison of options using Lodash isEqual
1909
+ return !isEqual(newOptions, this.lastAppliedOptions);
1910
+ }
1911
+ // Removed deepCloneValue method - now using Lodash cloneDeep
1550
1912
  checkFormulaForUpdate(formula, path) {
1551
1913
  if (formula) {
1552
1914
  const regex = /context\.eval\('([^']+)'\)/g;
@@ -1605,24 +1967,58 @@ class AXPWidgetRendererDirective {
1605
1967
  });
1606
1968
  }
1607
1969
  async updateOptionsBasedOnContext() {
1970
+ // Early return if no expressions to evaluate
1971
+ if (this.expressionEvaluators.size === 0) {
1972
+ return 0;
1973
+ }
1974
+ console.log(`🔍 [${this.node().type}] Evaluating ${this.expressionEvaluators.size} expressions`);
1608
1975
  const updatePromises = Array.from(this.expressionEvaluators).map(async ([path, evaluator]) => {
1976
+ // Check cache first
1977
+ const cachedValue = this.getCachedExpressionResult(path);
1978
+ if (cachedValue !== null) {
1979
+ console.log(`💾 [${this.node().type}] Using cached expression result for '${path}'`);
1980
+ return { path, newValue: cachedValue, fromCache: true };
1981
+ }
1982
+ // Evaluate expression if not cached
1983
+ const evalStartTime = performance.now();
1609
1984
  const newValue = await evaluator();
1610
- return { path, newValue };
1985
+ const evalTime = performance.now() - evalStartTime;
1986
+ // Check if result has actually changed using Lodash isEqual
1987
+ const lastValue = this.lastExpressionResults.get(path);
1988
+ const hasChanged = !isEqual(newValue, lastValue);
1989
+ if (hasChanged) {
1990
+ console.log(`📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value changed`);
1991
+ // Cache the result and track it
1992
+ this.setCachedExpressionResult(path, newValue);
1993
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
1994
+ return { path, newValue, fromCache: false, hasChanged: true };
1995
+ }
1996
+ else {
1997
+ console.log(`📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value unchanged, skipping update`);
1998
+ // Cache the result but don't update
1999
+ this.setCachedExpressionResult(path, newValue);
2000
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
2001
+ return { path, newValue, fromCache: false, hasChanged: false };
2002
+ }
1611
2003
  });
1612
2004
  // Wait for all evaluators to complete
1613
2005
  const updates = await Promise.all(updatePromises);
1614
- // Apply updates to mergedOptions
1615
- if (updates.length > 0) {
2006
+ // Filter updates to only include those that have actually changed
2007
+ const changedUpdates = updates.filter((update) => update.hasChanged !== false);
2008
+ // Apply updates to mergedOptions only for changed values
2009
+ if (changedUpdates.length > 0) {
1616
2010
  this.mergedOptions.update((o) => {
1617
2011
  const updatedOptions = { ...o };
1618
- updates.forEach(({ path, newValue }) => {
1619
- // Set the new value in the updatedOptions object by path
1620
- set(updatedOptions, path, newValue); // Assuming 'set' can handle paths like 'property.subproperty'
2012
+ changedUpdates.forEach(({ path, newValue }) => {
2013
+ set(updatedOptions, path, newValue);
1621
2014
  });
1622
2015
  return updatedOptions;
1623
2016
  });
1624
2017
  }
1625
- return updates.length;
2018
+ const cacheHits = updates.filter((update) => update.fromCache).length;
2019
+ const skippedUpdates = updates.length - changedUpdates.length;
2020
+ console.log(`📋 [${this.node().type}] Applied ${changedUpdates.length} expression updates (${cacheHits} cache hits, ${skippedUpdates} skipped)`);
2021
+ return changedUpdates.length;
1626
2022
  }
1627
2023
  async updateValueBasedOnFormula() {
1628
2024
  if (this.node().formula) {
@@ -2100,5 +2496,5 @@ var AXPWidgetGroupEnum;
2100
2496
  * Generated bundle index. Do not edit.
2101
2497
  */
2102
2498
 
2103
- export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPLayoutBuilderContextStore, AXPLayoutBuilderModule, AXPLayoutBuilderService, AXPLayoutContextChangeEvent, AXPLayoutElement, AXPPageStatus, AXPPropertyEditorHelper, AXPValueWidgetComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetStatus, AXPWidgetsCatalog, AXP_WIDGETS_ACTION_CATEGORY, AXP_WIDGETS_ADVANCE_CATEGORY, AXP_WIDGETS_CATEGORIES, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGETS_LAYOUT_CATEGORY, AXP_WIDGET_COLUMN_TOKEN, AXP_WIDGET_TOKEN, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints };
2499
+ export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPLayoutBuilderContextStore, AXPLayoutBuilderModule, AXPLayoutBuilderService, AXPLayoutContextChangeEvent, AXPLayoutElement, AXPPageStatus, AXPPropertyEditorHelper, AXPTableBaseLayoutWidgetComponent, AXPTableItemBaseLayoutWidgetComponent, AXPTableItemOpsBaseLayoutWidgetComponent, AXPValueWidgetComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetStatus, AXPWidgetsCatalog, AXP_WIDGETS_ACTION_CATEGORY, AXP_WIDGETS_ADVANCE_CATEGORY, AXP_WIDGETS_CATEGORIES, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGETS_LAYOUT_CATEGORY, AXP_WIDGET_COLUMN_TOKEN, AXP_WIDGET_TOKEN, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints };
2104
2500
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map