@acorex/platform 20.3.0-next.8 → 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.
- package/common/index.d.ts +0 -2
- package/core/index.d.ts +72 -62
- package/fesm2022/acorex-platform-common.mjs +5 -10
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +115 -115
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +420 -24
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +1266 -153
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +19 -12
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +454 -505
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +14 -21
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- 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
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Ct-ri59W.mjs.map +1 -0
- 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
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-7BB4LdjK.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs +101 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +8 -8
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs → acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs} +2 -2
- package/fesm2022/acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +154 -23
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/{acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs → acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs} +3 -3
- package/fesm2022/acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs → acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs} +2 -2
- package/fesm2022/acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs.map +1 -0
- 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
- package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CMqq_iOj.mjs.map +1 -0
- package/fesm2022/acorex-platform-widgets.mjs +4943 -4858
- package/fesm2022/acorex-platform-widgets.mjs.map +1 -1
- package/layout/builder/index.d.ts +60 -5
- package/layout/components/index.d.ts +501 -54
- package/layout/designer/index.d.ts +4 -2
- package/layout/entity/index.d.ts +45 -9
- package/package.json +9 -9
- package/widgets/index.d.ts +352 -300
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-gQIK6PIx.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs +0 -101
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs.map +0 -1
- 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',
|
|
@@ -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();
|
|
@@ -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
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
-
|
|
1411
|
-
|
|
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
|
-
|
|
1548
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1615
|
-
|
|
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
|
-
|
|
1619
|
-
|
|
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
|
-
|
|
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
|