@acorex/platform 21.0.0-next.40 → 21.0.0-next.41
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-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +115 -29
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +59 -4
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +995 -545
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs} +9 -4
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +418 -106
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +2 -1
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-common.d.ts +14 -10
- package/types/acorex-platform-layout-builder.d.ts +20 -2
- package/types/acorex-platform-layout-designer.d.ts +35 -3
- package/types/acorex-platform-layout-entity.d.ts +46 -5
- package/types/acorex-platform-layout-widget-core.d.ts +2 -5
- package/types/acorex-platform-layout-widgets.d.ts +108 -51
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +0 -1
|
@@ -6,7 +6,7 @@ import { provideCommandSetups } from '@acorex/platform/runtime';
|
|
|
6
6
|
import { AXPopupService } from '@acorex/components/popup';
|
|
7
7
|
import * as i1 from '@acorex/platform/layout/widget-core';
|
|
8
8
|
import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
|
|
9
|
-
import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
|
|
9
|
+
import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
|
|
10
10
|
import * as i2 from '@acorex/components/form';
|
|
11
11
|
import { AXFormComponent, AXFormModule } from '@acorex/components/form';
|
|
12
12
|
import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
|
|
@@ -531,11 +531,21 @@ class LayoutBuilder {
|
|
|
531
531
|
return this;
|
|
532
532
|
}
|
|
533
533
|
build() {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
mode:
|
|
534
|
+
const r = this.root;
|
|
535
|
+
const node = {
|
|
536
|
+
type: r.type,
|
|
537
|
+
...(r.mode !== undefined ? { mode: r.mode } : {}),
|
|
538
|
+
...(r.children !== undefined ? { children: r.children } : {}),
|
|
539
|
+
...(r.options !== undefined ? { options: r.options } : {}),
|
|
540
|
+
...(r.name !== undefined ? { name: r.name } : {}),
|
|
541
|
+
...(r.path !== undefined ? { path: r.path } : {}),
|
|
542
|
+
...(r.visible !== undefined ? { visible: r.visible } : {}),
|
|
543
|
+
...(r.defaultValue !== undefined ? { defaultValue: r.defaultValue } : {}),
|
|
544
|
+
...(r.triggers !== undefined ? { triggers: r.triggers } : {}),
|
|
545
|
+
...(r.meta !== undefined ? { meta: r.meta } : {}),
|
|
546
|
+
...(r.valueTransforms !== undefined ? { valueTransforms: r.valueTransforms } : {}),
|
|
538
547
|
};
|
|
548
|
+
return node;
|
|
539
549
|
}
|
|
540
550
|
/**
|
|
541
551
|
* Converts the built widget node to JSON string
|
|
@@ -1009,6 +1019,24 @@ class FlexContainerBuilder extends WidgetContainerMixin {
|
|
|
1009
1019
|
* Extracts flat grid-item options from AXPGridLayoutOptions for grid-item-layout widget.
|
|
1010
1020
|
* Uses first available breakpoint (lg, xl, md, sm).
|
|
1011
1021
|
*/
|
|
1022
|
+
/**
|
|
1023
|
+
* Deep-merges grid breakpoint buckets so sequential fluent calls (e.g. setColumns then setGap)
|
|
1024
|
+
* do not wipe sibling keys under options.grid.default.
|
|
1025
|
+
*/
|
|
1026
|
+
function mergeAXPGridContainerOptions(prev, patch) {
|
|
1027
|
+
const next = { ...(prev ?? {}), ...patch };
|
|
1028
|
+
if (prev?.grid?.default || patch.grid?.default) {
|
|
1029
|
+
next.grid = {
|
|
1030
|
+
...(prev?.grid ?? {}),
|
|
1031
|
+
...(patch.grid ?? {}),
|
|
1032
|
+
default: {
|
|
1033
|
+
...(prev?.grid?.default ?? {}),
|
|
1034
|
+
...(patch.grid?.default ?? {}),
|
|
1035
|
+
},
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
return next;
|
|
1039
|
+
}
|
|
1012
1040
|
function toGridItemOptions(layoutOptions) {
|
|
1013
1041
|
if (!layoutOptions?.positions)
|
|
1014
1042
|
return { colSpan: 12 };
|
|
@@ -1038,7 +1066,7 @@ class GridContainerBuilder extends WidgetContainerMixin {
|
|
|
1038
1066
|
super('grid-layout');
|
|
1039
1067
|
}
|
|
1040
1068
|
setOptions(options) {
|
|
1041
|
-
this.containerState.options =
|
|
1069
|
+
this.containerState.options = mergeAXPGridContainerOptions(this.containerState.options, options);
|
|
1042
1070
|
return this;
|
|
1043
1071
|
}
|
|
1044
1072
|
item(layoutOptions, delegate) {
|
|
@@ -2345,7 +2373,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2345
2373
|
}
|
|
2346
2374
|
async executeAction(action) {
|
|
2347
2375
|
const cmd = this.resolveActionCommandName(action.command);
|
|
2348
|
-
if (cmd
|
|
2376
|
+
if (this.shouldValidateBeforeAction(cmd)) {
|
|
2349
2377
|
const isValid = await this.layoutRenderer()?.validate();
|
|
2350
2378
|
if (!isValid?.result) {
|
|
2351
2379
|
return;
|
|
@@ -2354,7 +2382,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2354
2382
|
if (cmd?.startsWith('widget:')) {
|
|
2355
2383
|
const parsed = this.parseWidgetCommand(cmd);
|
|
2356
2384
|
if (parsed.widgetName && parsed.action) {
|
|
2357
|
-
await this.
|
|
2385
|
+
await this.invokeWidget(parsed.widgetName, parsed.action, {});
|
|
2358
2386
|
await this.aggregateAndEvaluateActions();
|
|
2359
2387
|
return;
|
|
2360
2388
|
}
|
|
@@ -2362,17 +2390,15 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2362
2390
|
const context = this.context();
|
|
2363
2391
|
const onAction = this.config?.onAction;
|
|
2364
2392
|
if (onAction) {
|
|
2365
|
-
const dialogRef =
|
|
2366
|
-
close: (res) => this.close(res),
|
|
2367
|
-
context: () => this.context(),
|
|
2368
|
-
action: () => action.command ?? undefined,
|
|
2369
|
-
setLoading: (loading) => this.isDialogLoading.set(loading),
|
|
2370
|
-
};
|
|
2393
|
+
const dialogRef = this.createDialogRef(cmd);
|
|
2371
2394
|
try {
|
|
2372
2395
|
this.isDialogLoading.set(true);
|
|
2373
2396
|
const result = await Promise.resolve(onAction(dialogRef));
|
|
2397
|
+
if (result && typeof result === 'object' && result.keepDialogOpen) {
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2374
2400
|
this.callBack(result);
|
|
2375
|
-
this.
|
|
2401
|
+
await this.closeWithOptionalSkipValidate(result);
|
|
2376
2402
|
}
|
|
2377
2403
|
catch {
|
|
2378
2404
|
// Handler threw: stay open for retry, actions remain clickable
|
|
@@ -2390,20 +2416,48 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2390
2416
|
this.data.action = result.action;
|
|
2391
2417
|
}
|
|
2392
2418
|
this.callBack({
|
|
2393
|
-
|
|
2394
|
-
this.close(res);
|
|
2395
|
-
},
|
|
2396
|
-
context: () => this.context(),
|
|
2419
|
+
...this.createDialogRef(cmd),
|
|
2397
2420
|
action: () => result.action,
|
|
2398
|
-
setLoading: (loading) => {
|
|
2399
|
-
this.isDialogLoading.set(loading);
|
|
2400
|
-
},
|
|
2401
2421
|
});
|
|
2402
2422
|
// Without `onAction`, only the configured cancel action dismisses the dialog (not submit/custom).
|
|
2403
2423
|
if (cmd === 'cancel') {
|
|
2404
2424
|
await this.close(result);
|
|
2405
2425
|
}
|
|
2406
2426
|
}
|
|
2427
|
+
/** Whether the layout form should be validated before running this footer command. */
|
|
2428
|
+
shouldValidateBeforeAction(cmd) {
|
|
2429
|
+
if (!cmd || cmd === 'cancel' || cmd === 'entity-form-done') {
|
|
2430
|
+
return false;
|
|
2431
|
+
}
|
|
2432
|
+
if (cmd.startsWith('widget:')) {
|
|
2433
|
+
return false;
|
|
2434
|
+
}
|
|
2435
|
+
return true;
|
|
2436
|
+
}
|
|
2437
|
+
createDialogRef(actionCmd) {
|
|
2438
|
+
return {
|
|
2439
|
+
close: (res) => {
|
|
2440
|
+
void this.closeWithOptionalSkipValidate(res);
|
|
2441
|
+
},
|
|
2442
|
+
context: () => this.context(),
|
|
2443
|
+
action: () => actionCmd,
|
|
2444
|
+
setLoading: (loading) => this.isDialogLoading.set(loading),
|
|
2445
|
+
patchContext: (partial) => {
|
|
2446
|
+
const merged = merge({}, this.context(), partial);
|
|
2447
|
+
this.context.set(merged);
|
|
2448
|
+
this.layoutRenderer()?.updateContext(merged);
|
|
2449
|
+
},
|
|
2450
|
+
invokeWidget: (widgetName, method, opts) => this.invokeWidget(widgetName, method, opts ?? {}),
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
async closeWithOptionalSkipValidate(result) {
|
|
2454
|
+
if (result && typeof result === 'object' && result.skipValidate) {
|
|
2455
|
+
this.result.emit(result);
|
|
2456
|
+
await super.close(result);
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
await this.close(result);
|
|
2460
|
+
}
|
|
2407
2461
|
/** Resolves footer/widget action command to a string (e.g. `cancel`, `submit`, `widget:...`). */
|
|
2408
2462
|
resolveActionCommandName(command) {
|
|
2409
2463
|
if (typeof command === 'string') {
|
|
@@ -2424,7 +2478,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2424
2478
|
return {};
|
|
2425
2479
|
return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
|
|
2426
2480
|
}
|
|
2427
|
-
async
|
|
2481
|
+
async invokeWidget(widgetName, apiMethod, opts) {
|
|
2428
2482
|
if (!this.widgetCoreService)
|
|
2429
2483
|
return;
|
|
2430
2484
|
try {
|
|
@@ -2434,16 +2488,20 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2434
2488
|
if (typeof fn === 'function') {
|
|
2435
2489
|
await Promise.resolve(fn({
|
|
2436
2490
|
close: (result) => {
|
|
2437
|
-
this.
|
|
2491
|
+
void this.closeWithOptionalSkipValidate(result);
|
|
2438
2492
|
},
|
|
2439
2493
|
context: () => this.context(),
|
|
2440
2494
|
setLoading: (loading) => {
|
|
2441
|
-
this.isDialogLoading.set(loading);
|
|
2495
|
+
(opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
|
|
2442
2496
|
},
|
|
2443
2497
|
}));
|
|
2498
|
+
// Footer predicates (e.g. wizard step) must refresh when the widget advances outside executeAction (e.g. dialogRef.invokeWidget after entity-form continue).
|
|
2499
|
+
await this.aggregateAndEvaluateActions();
|
|
2444
2500
|
}
|
|
2445
2501
|
}
|
|
2446
|
-
catch {
|
|
2502
|
+
catch {
|
|
2503
|
+
//
|
|
2504
|
+
}
|
|
2447
2505
|
}
|
|
2448
2506
|
async close(result) {
|
|
2449
2507
|
if (result) {
|
|
@@ -2493,6 +2551,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2493
2551
|
zone: 'footer',
|
|
2494
2552
|
placement,
|
|
2495
2553
|
scope: a.scope,
|
|
2554
|
+
predicateApiWidgetName: a.predicateApiWidgetName,
|
|
2496
2555
|
});
|
|
2497
2556
|
const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
|
|
2498
2557
|
const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
|
|
@@ -2502,16 +2561,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2502
2561
|
const out = [];
|
|
2503
2562
|
for (const a of actions) {
|
|
2504
2563
|
const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
|
|
2505
|
-
const
|
|
2564
|
+
const widgetNameForApi = parsed.widgetName ?? a.predicateApiWidgetName;
|
|
2565
|
+
const api = widgetNameForApi ? await this.resolveApi(widgetNameForApi) : undefined;
|
|
2506
2566
|
const scope = {
|
|
2507
2567
|
api,
|
|
2508
|
-
widget: { name:
|
|
2568
|
+
widget: { name: widgetNameForApi },
|
|
2509
2569
|
dialog: { context: this.context() },
|
|
2510
2570
|
context: this.context(),
|
|
2511
2571
|
};
|
|
2512
2572
|
const disabled = await this.evalBool(a.disabled, scope);
|
|
2513
2573
|
const hidden = await this.evalBool(a.hidden, scope);
|
|
2514
|
-
|
|
2574
|
+
const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
|
|
2575
|
+
out.push({ ...a, disabled, hidden, title: resolvedTitle });
|
|
2515
2576
|
}
|
|
2516
2577
|
return out;
|
|
2517
2578
|
}
|
|
@@ -2529,6 +2590,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2529
2590
|
}
|
|
2530
2591
|
return value;
|
|
2531
2592
|
}
|
|
2593
|
+
/** Resolves footer action title when it contains {{ ... }} (e.g. wizard labels from dialog context). */
|
|
2594
|
+
async evalActionTitle(value, scope) {
|
|
2595
|
+
if (value == null || typeof value !== 'string' || !value.includes('{{')) {
|
|
2596
|
+
return value;
|
|
2597
|
+
}
|
|
2598
|
+
try {
|
|
2599
|
+
const result = await this.expressionEvaluator.evaluate(value, scope);
|
|
2600
|
+
if (typeof result === 'string' && result.length > 0) {
|
|
2601
|
+
return result;
|
|
2602
|
+
}
|
|
2603
|
+
if (result != null && result !== false) {
|
|
2604
|
+
return String(result);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
catch {
|
|
2608
|
+
//
|
|
2609
|
+
}
|
|
2610
|
+
return value;
|
|
2611
|
+
}
|
|
2532
2612
|
async resolveApi(widgetName) {
|
|
2533
2613
|
try {
|
|
2534
2614
|
await this.widgetCoreService?.waitForWidget(widgetName, 2000);
|
|
@@ -2543,6 +2623,9 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2543
2623
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", outputs: { result: "result" }, providers: [AXPContextStore], viewQueries: [{ propertyName: "layoutRenderer", first: true, predicate: AXPLayoutRendererComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
2544
2624
|
<axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
|
|
2545
2625
|
<div class="ax-p-4">
|
|
2626
|
+
@if (config.message) {
|
|
2627
|
+
<p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
|
|
2628
|
+
}
|
|
2546
2629
|
<axp-layout-renderer
|
|
2547
2630
|
[layout]="config.definition"
|
|
2548
2631
|
[context]="context()"
|
|
@@ -2616,6 +2699,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2616
2699
|
template: `
|
|
2617
2700
|
<axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
|
|
2618
2701
|
<div class="ax-p-4">
|
|
2702
|
+
@if (config.message) {
|
|
2703
|
+
<p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
|
|
2704
|
+
}
|
|
2619
2705
|
<axp-layout-renderer
|
|
2620
2706
|
[layout]="config.definition"
|
|
2621
2707
|
[context]="context()"
|