@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.
- package/fesm2022/acorex-platform-framework-client-auth.mjs +397 -93
- package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-common.mjs +33 -31
- package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs +12 -4
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
- 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
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-D0jDsA6M.mjs.map +1 -0
- 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
- 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
- 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
- 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
- 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
- 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
- package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +257 -30
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
- 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
- package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-KrDsAa3H.mjs.map +1 -0
- 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
- 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
- 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
- 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
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -1
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4 -4
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
- package/layout/builder/README.md +10 -0
- package/layout/widget-core/README.md +47 -3
- package/layout/widgets/README.md +8 -2
- package/package.json +2 -2
- package/types/acorex-platform-framework-client-auth.d.ts +60 -1
- package/types/acorex-platform-framework-client-common.d.ts +3 -2
- package/types/acorex-platform-framework-client-layout-components.d.ts +3 -1
- package/types/acorex-platform-framework-client-layout-entity.d.ts +8 -1
- package/types/acorex-platform-framework-client-layout-widget-core.d.ts +72 -1
- package/types/acorex-platform-framework-client-layout-widgets.d.ts +25 -5
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BBuYugjl.mjs.map +0 -1
- 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(
|
|
1221
|
-
this.parentPath.set(sections.slice(0, sections.length - 1)
|
|
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 (
|
|
1369
|
-
this.
|
|
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 ||
|
|
3460
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
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
|
-
|
|
4052
|
-
|
|
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
|
-
|
|
4057
|
-
return c.path === path;
|
|
4284
|
+
return c.path === resolved || this.contextStorePathDependsOnChangedPath(resolved, c.path);
|
|
4058
4285
|
});
|
|
4059
4286
|
}));
|
|
4060
4287
|
},
|