@acorex/platform 20.3.0-next.9 → 20.4.0
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 +643 -365
- package/core/index.d.ts +19 -4
- package/fesm2022/acorex-platform-auth.mjs +19 -19
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/acorex-platform-common-common-settings.provider-9OHien_H.mjs +47 -0
- package/fesm2022/acorex-platform-common-common-settings.provider-9OHien_H.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +673 -242
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +58 -46
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-domain.mjs +16 -16
- package/fesm2022/acorex-platform-domain.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +1933 -2330
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +1511 -1626
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +82 -82
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs +52 -0
- package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity.mjs +1371 -917
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +63 -54
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +2758 -0
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs → acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs} +6 -6
- package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs → acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs → acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs → acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs} +9 -9
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs → acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs} +8 -8
- package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-tabular-data-edit-popup.component-CMqq_iOj.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs} +8 -8
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs} +6 -5
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs} +6 -7
- package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs.map +1 -0
- package/fesm2022/{acorex-platform-widgets.mjs → acorex-platform-layout-widgets.mjs} +4153 -3146
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -0
- package/fesm2022/acorex-platform-native.mjs +7 -7
- package/fesm2022/acorex-platform-native.mjs.map +1 -1
- package/fesm2022/acorex-platform-runtime.mjs +40 -40
- package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-B1PT6FtZ.mjs +115 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-B1PT6FtZ.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-7BB4LdjK.mjs → acorex-platform-themes-default-entity-master-list-view.component-rdKxuMC_.mjs} +69 -33
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-rdKxuMC_.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-4g19A3eI.mjs +101 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-4g19A3eI.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs → acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs} +17 -17
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs} +4 -4
- package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs.map → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs} +4 -4
- package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs.map → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs} +4 -4
- package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs.map → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs.map} +1 -1
- package/fesm2022/acorex-platform-themes-default.mjs +117 -51
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs → acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs} +5 -5
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs → acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs} +8 -8
- package/fesm2022/acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs} +5 -5
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs} +5 -5
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +66 -55
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +27 -39
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/layout/builder/README.md +1577 -3
- package/layout/builder/index.d.ts +735 -868
- package/layout/components/index.d.ts +218 -714
- package/layout/designer/index.d.ts +4 -4
- package/layout/entity/index.d.ts +954 -375
- package/layout/views/index.d.ts +13 -14
- package/layout/widget-core/README.md +4 -0
- package/layout/widget-core/index.d.ts +959 -0
- package/layout/widgets/README.md +4 -0
- package/{widgets → layout/widgets}/index.d.ts +426 -365
- package/package.json +18 -14
- package/themes/shared/index.d.ts +2 -2
- package/workflow/index.d.ts +3 -173
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Ct-ri59W.mjs +0 -115
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Ct-ri59W.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-7BB4LdjK.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs +0 -101
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BDJR088o.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-CLUKU4y0.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs +0 -84
- package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-BXPrXy-h.mjs +0 -55
- package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-BXPrXy-h.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs +0 -92
- package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-BVZ7lWm9.mjs +0 -55
- package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-BVZ7lWm9.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-file-list-popup.component-Cmtq2bBV.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-page-widget-designer.component-D8ivmxzT.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs +0 -40
- package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CMqq_iOj.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs.map +0 -1
- package/fesm2022/acorex-platform-widgets.mjs.map +0 -1
- package/widgets/README.md +0 -4
|
@@ -1,2500 +1,2103 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { signal, computed, Injectable, InjectionToken, inject, ElementRef, effect, Injector, ChangeDetectorRef, ViewChild, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, input, output, ViewContainerRef, Directive, Optional, Inject, NgModule } from '@angular/core';
|
|
3
|
-
import { convertArrayToDataSource, AXDataSource } from '@acorex/cdk/common';
|
|
4
|
-
import { setSmart, AXPDataSourceDefinitionProviderService, extractValue, getSmart, AXPExpressionEvaluatorService } from '@acorex/platform/core';
|
|
5
|
-
import { set, cloneDeep, isEqual, get, merge, isNil, isUndefined, isObjectLike, sum, isEmpty, isString } from 'lodash-es';
|
|
6
|
-
import { Subject, BehaviorSubject, filter } from 'rxjs';
|
|
7
|
-
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
|
|
8
|
-
import * as i1$1 from '@acorex/components/skeleton';
|
|
9
|
-
import { AXSkeletonModule } from '@acorex/components/skeleton';
|
|
10
|
-
import * as i2 from '@acorex/core/translation';
|
|
11
|
-
import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
|
|
12
|
-
import { PortalModule } from '@angular/cdk/portal';
|
|
13
|
-
import * as i1 from '@angular/common';
|
|
1
|
+
import * as i1$1 from '@angular/common';
|
|
14
2
|
import { CommonModule } from '@angular/common';
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
AXPPageStatus["Error"] = "error";
|
|
35
|
-
})(AXPPageStatus || (AXPPageStatus = {}));
|
|
36
|
-
var AXPWidgetStatus;
|
|
37
|
-
(function (AXPWidgetStatus) {
|
|
38
|
-
// Rendering statuses
|
|
39
|
-
AXPWidgetStatus["Rendering"] = "rendering";
|
|
40
|
-
AXPWidgetStatus["Rendered"] = "rendered";
|
|
41
|
-
// Processing statuses
|
|
42
|
-
AXPWidgetStatus["Processing"] = "processing";
|
|
43
|
-
// Error handling
|
|
44
|
-
AXPWidgetStatus["Error"] = "error";
|
|
45
|
-
})(AXPWidgetStatus || (AXPWidgetStatus = {}));
|
|
46
|
-
|
|
47
|
-
class AXPLayoutElement {
|
|
48
|
-
api() {
|
|
49
|
-
return {};
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
class AXPLayoutBuilderService {
|
|
53
|
-
constructor() {
|
|
54
|
-
this.variables$ = signal({}, ...(ngDevMode ? [{ debugName: "variables$" }] : []));
|
|
55
|
-
this.functions$ = signal({}, ...(ngDevMode ? [{ debugName: "functions$" }] : []));
|
|
56
|
-
this.onRefresh = new Subject();
|
|
57
|
-
this.widgets = new Map();
|
|
58
|
-
this.onWidgetRegistered = new Subject();
|
|
59
|
-
this.status$ = signal(AXPPageStatus.Rendering, ...(ngDevMode ? [{ debugName: "status$" }] : []));
|
|
60
|
-
this.status = this.status$.asReadonly();
|
|
61
|
-
this.isBusy = computed(() => {
|
|
62
|
-
return [AXPPageStatus.Processing, AXPPageStatus.Submitting, AXPPageStatus.Rendering].includes(this.status());
|
|
63
|
-
}, ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
|
|
64
|
-
}
|
|
65
|
-
get variables() {
|
|
66
|
-
return this.variables$();
|
|
67
|
-
}
|
|
68
|
-
get functions() {
|
|
69
|
-
return this.functions$();
|
|
70
|
-
}
|
|
71
|
-
updateStatus() {
|
|
72
|
-
this.status$.update(() => this.detectStatus());
|
|
73
|
-
}
|
|
74
|
-
detectStatus() {
|
|
75
|
-
const statuses = Array.from(this.widgets.values()).map((c) => c.status());
|
|
76
|
-
// Rendering statuses
|
|
77
|
-
if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
|
|
78
|
-
return AXPPageStatus.Rendering;
|
|
79
|
-
}
|
|
80
|
-
if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
|
|
81
|
-
return AXPPageStatus.Rendered;
|
|
82
|
-
}
|
|
83
|
-
// Processing statuses
|
|
84
|
-
if (statuses.some((status) => status === AXPWidgetStatus.Processing)) {
|
|
85
|
-
return AXPPageStatus.Processing;
|
|
86
|
-
}
|
|
87
|
-
// Error handling
|
|
88
|
-
if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
|
|
89
|
-
return AXPPageStatus.Error;
|
|
90
|
-
}
|
|
91
|
-
return AXPPageStatus.Rendered; // Default to Loaded when all widgets are in a completed state
|
|
92
|
-
}
|
|
93
|
-
refresh() {
|
|
94
|
-
setTimeout(() => {
|
|
95
|
-
this.onRefresh.next();
|
|
96
|
-
}, 0);
|
|
97
|
-
}
|
|
98
|
-
setStatus(status) {
|
|
99
|
-
this.status$.set(status);
|
|
100
|
-
}
|
|
101
|
-
setVariables(...args) {
|
|
102
|
-
if (args.length == 0)
|
|
103
|
-
return;
|
|
104
|
-
else if (args.length == 1)
|
|
105
|
-
this.variables$.set(args[0]);
|
|
106
|
-
else if (args.length == 2) {
|
|
107
|
-
this.variables$.update((v) => set(v, args[0], args[1]));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
setFunctions(...args) {
|
|
111
|
-
if (args.length == 0)
|
|
112
|
-
return;
|
|
113
|
-
else if (args.length == 1)
|
|
114
|
-
this.functions$.set(args[0]);
|
|
115
|
-
else if (args.length == 2) {
|
|
116
|
-
this.functions$.update((v) => set(v, args[0], args[1]));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
registerWidget(id, widget) {
|
|
120
|
-
this.widgets.set(id, widget);
|
|
121
|
-
this.onWidgetRegistered.next({ id, widget });
|
|
122
|
-
}
|
|
123
|
-
getWidget(id) {
|
|
124
|
-
return this.widgets.get(id);
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Waits until a widget with the given id is registered, then resolves with it.
|
|
128
|
-
* If the widget is already registered, resolves immediately.
|
|
129
|
-
* Optionally accepts a timeout (in ms) after which it resolves with undefined.
|
|
130
|
-
*/
|
|
131
|
-
async waitForWidget(id, timeoutMs) {
|
|
132
|
-
const existing = this.widgets.get(id);
|
|
133
|
-
if (existing) {
|
|
134
|
-
return existing;
|
|
135
|
-
}
|
|
136
|
-
return new Promise((resolve) => {
|
|
137
|
-
let resolved = false;
|
|
138
|
-
let timer = null;
|
|
139
|
-
const sub = this.onWidgetRegistered.subscribe(({ id: registeredId, widget }) => {
|
|
140
|
-
if (registeredId === id && !resolved) {
|
|
141
|
-
resolved = true;
|
|
142
|
-
sub.unsubscribe();
|
|
143
|
-
if (timer) {
|
|
144
|
-
clearTimeout(timer);
|
|
145
|
-
}
|
|
146
|
-
resolve(widget);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
if (timeoutMs != null && timeoutMs > 0) {
|
|
150
|
-
timer = setTimeout(() => {
|
|
151
|
-
if (!resolved) {
|
|
152
|
-
resolved = true;
|
|
153
|
-
sub.unsubscribe();
|
|
154
|
-
resolve(undefined);
|
|
155
|
-
}
|
|
156
|
-
}, timeoutMs);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
ngOnDestroy() { }
|
|
161
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
162
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService }); }
|
|
163
|
-
}
|
|
164
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
|
|
165
|
-
type: Injectable
|
|
166
|
-
}] });
|
|
167
|
-
|
|
168
|
-
class AXPLayoutContextChangeEvent {
|
|
169
|
-
}
|
|
170
|
-
const AXPLayoutBuilderContextStore = signalStore(
|
|
171
|
-
// Initial State
|
|
172
|
-
withState(() => ({
|
|
173
|
-
data: {}, // Shared context data
|
|
174
|
-
state: 'initiated', // Current state
|
|
175
|
-
initialSnapshot: {}, // Snapshot of the first initialized state
|
|
176
|
-
previousSnapshot: {}, // Snapshot of the previous state
|
|
177
|
-
lastChange: {
|
|
178
|
-
state: 'initiated',
|
|
179
|
-
}, // Last change event
|
|
180
|
-
})),
|
|
181
|
-
// Computed Signals
|
|
182
|
-
withComputed(({ data, state, lastChange, initialSnapshot, previousSnapshot }) => ({
|
|
183
|
-
isChanged: computed(() => state() === 'changed'),
|
|
184
|
-
isReset: computed(() => state() === 'restored'),
|
|
185
|
-
isInitiated: computed(() => state() === 'initiated'),
|
|
186
|
-
isEmpty: computed(() => Object.keys(data()).length === 0),
|
|
187
|
-
isDirty: computed(() => !isEqual(data(), previousSnapshot())),
|
|
188
|
-
snapshot: computed(() => cloneDeep(data())), // Current data snapshot
|
|
189
|
-
initial: computed(() => cloneDeep(initialSnapshot())), // Initial snapshot
|
|
190
|
-
previous: computed(() => cloneDeep(previousSnapshot())), // Previous snapshot
|
|
191
|
-
changeEvent: computed(() => lastChange()), // Reactive last change event
|
|
192
|
-
})),
|
|
193
|
-
// Methods for State Management
|
|
194
|
-
withMethods((store) => ({
|
|
195
|
-
// Update a specific value
|
|
196
|
-
update(path, value) {
|
|
197
|
-
const currentData = cloneDeep(store.data());
|
|
198
|
-
const oldValue = get(currentData, path);
|
|
199
|
-
// Skip if the value hasn't changed
|
|
200
|
-
if (isEqual(oldValue, value)) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// Update the value and prepare the change event
|
|
204
|
-
const updatedData = setSmart(currentData, path, value);
|
|
205
|
-
const changeEvent = {
|
|
206
|
-
oldValue,
|
|
207
|
-
newValue: value,
|
|
208
|
-
path,
|
|
209
|
-
state: 'changed',
|
|
210
|
-
data: updatedData,
|
|
211
|
-
};
|
|
212
|
-
// Patch the state
|
|
213
|
-
patchState(store, {
|
|
214
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
215
|
-
data: updatedData,
|
|
216
|
-
state: 'changed',
|
|
217
|
-
lastChange: changeEvent,
|
|
218
|
-
});
|
|
219
|
-
},
|
|
220
|
-
patch(context) {
|
|
221
|
-
const currentData = cloneDeep(store.data());
|
|
222
|
-
// Update the value and prepare the change event
|
|
223
|
-
const updatedData = { ...currentData, ...context };
|
|
224
|
-
const changeEvent = {
|
|
225
|
-
state: 'patch',
|
|
226
|
-
data: updatedData,
|
|
227
|
-
};
|
|
228
|
-
// Patch the state
|
|
229
|
-
patchState(store, {
|
|
230
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
231
|
-
data: updatedData,
|
|
232
|
-
state: 'changed',
|
|
233
|
-
lastChange: changeEvent,
|
|
234
|
-
});
|
|
235
|
-
},
|
|
236
|
-
// Reset to the initial state
|
|
237
|
-
reset() {
|
|
238
|
-
const initialData = store.initial();
|
|
239
|
-
const changeEvent = {
|
|
240
|
-
oldValue: cloneDeep(store.data()), // Current data becomes old value
|
|
241
|
-
newValue: cloneDeep(initialData), // Reset to the initial state
|
|
242
|
-
path: '',
|
|
243
|
-
state: 'restored',
|
|
244
|
-
data: initialData,
|
|
245
|
-
};
|
|
246
|
-
patchState(store, {
|
|
247
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
248
|
-
data: initialData,
|
|
249
|
-
state: 'restored',
|
|
250
|
-
lastChange: changeEvent,
|
|
251
|
-
});
|
|
252
|
-
},
|
|
253
|
-
// Initialize the state
|
|
254
|
-
set(initialData) {
|
|
255
|
-
const currentData = store.data();
|
|
256
|
-
if (isEqual(currentData, initialData)) {
|
|
257
|
-
return; // Skip if the current state matches the initial state
|
|
258
|
-
}
|
|
259
|
-
const changeEvent = {
|
|
260
|
-
oldValue: null,
|
|
261
|
-
newValue: cloneDeep(initialData),
|
|
262
|
-
path: '',
|
|
263
|
-
state: 'initiated',
|
|
264
|
-
data: initialData,
|
|
265
|
-
};
|
|
266
|
-
patchState(store, {
|
|
267
|
-
initialSnapshot: cloneDeep(initialData), // Save the initial state
|
|
268
|
-
previousSnapshot: store.snapshot(), // Save the current state as the previous
|
|
269
|
-
data: initialData,
|
|
270
|
-
state: 'initiated',
|
|
271
|
-
lastChange: changeEvent,
|
|
272
|
-
});
|
|
273
|
-
},
|
|
274
|
-
// Get a specific value
|
|
275
|
-
getValue(path) {
|
|
276
|
-
return get(store.data(), path);
|
|
277
|
-
},
|
|
278
|
-
})));
|
|
279
|
-
|
|
280
|
-
const AXPWidgetsCatalog = {
|
|
281
|
-
timeDuration: 'time-duration',
|
|
282
|
-
tagable: 'tagable-editor',
|
|
283
|
-
checkbox: 'checkbox-editor',
|
|
284
|
-
color: 'color-editor',
|
|
285
|
-
contact: 'contact-editor',
|
|
286
|
-
dateTime: 'date-time-editor',
|
|
287
|
-
largeText: 'large-text-editor',
|
|
288
|
-
number: 'number-editor',
|
|
289
|
-
numberUnit: 'number-unit-editor',
|
|
290
|
-
password: 'password-editor',
|
|
291
|
-
richText: 'rich-text-editor',
|
|
292
|
-
select: 'select-editor',
|
|
293
|
-
selectionList: 'selection-list-editor',
|
|
294
|
-
text: 'text-editor',
|
|
295
|
-
table: 'table-editor',
|
|
296
|
-
toggle: 'toggle-editor',
|
|
297
|
-
blockLayout: 'block-layout',
|
|
298
|
-
pageLayout: 'page-layout',
|
|
299
|
-
repeaterLayout: 'repeater-layout',
|
|
300
|
-
textBlockLayout: 'text-block-layout',
|
|
301
|
-
fileUploader: 'file-uploader',
|
|
302
|
-
fileTypeExtension: 'file-type-extension',
|
|
303
|
-
map: 'map',
|
|
304
|
-
imageMarker: 'image-marker',
|
|
305
|
-
image: 'image',
|
|
306
|
-
gallery: 'gallery',
|
|
307
|
-
signature: 'signature',
|
|
308
|
-
buttonAction: 'button-action',
|
|
309
|
-
document: 'document-layout',
|
|
310
|
-
lookup: 'lookup-editor',
|
|
311
|
-
formField: 'form-field',
|
|
312
|
-
qrcode: 'qrcode',
|
|
313
|
-
advancedGrid: 'advanced-grid-layout',
|
|
314
|
-
advancedGridItem: 'advanced-grid-item-layout',
|
|
315
|
-
grid: 'grid-layout',
|
|
316
|
-
gridItem: 'grid-item-layout',
|
|
317
|
-
// gridRow: 'grid-row-layout',
|
|
318
|
-
widgetSelector: 'widget-selector',
|
|
319
|
-
template: 'template',
|
|
320
|
-
templateDesigner: 'template-designer',
|
|
321
|
-
cronJob: 'cron-job',
|
|
322
|
-
spacing: 'spacing',
|
|
323
|
-
direction: 'direction',
|
|
324
|
-
border: 'border',
|
|
325
|
-
flexLayout: 'flex-layout',
|
|
326
|
-
flexItem: 'flex-item-layout',
|
|
327
|
-
tableLayout: 'table-layout',
|
|
328
|
-
tableItem: 'table-item-layout',
|
|
329
|
-
avatar: 'avatar',
|
|
330
|
-
themePaletteChooser: 'theme-palette-chooser',
|
|
331
|
-
themeModeChooser: 'theme-mode-chooser',
|
|
332
|
-
menuOrientationChooser: 'menu-orientation-chooser',
|
|
333
|
-
fontStyleChooser: 'font-style-chooser',
|
|
334
|
-
fontSizeChooser: 'font-size-chooser',
|
|
335
|
-
iconChooser: 'icon-chooser',
|
|
336
|
-
themeColorChooser: 'theme-color-chooser',
|
|
337
|
-
gridOptions: 'grid-options',
|
|
338
|
-
gridItemOptions: 'grid-item-options',
|
|
339
|
-
advancedGridOptions: 'advanced-grid-options',
|
|
340
|
-
stringFilter: 'string-filter',
|
|
341
|
-
numberFilter: 'number-filter',
|
|
342
|
-
dateTimeFilter: 'datetime-filter',
|
|
343
|
-
booleanFilter: 'boolean-filter',
|
|
344
|
-
lookupFilter: 'lookup-filter',
|
|
345
|
-
flexOptions: 'flex-options',
|
|
346
|
-
flexItemOptions: 'flex-item-options',
|
|
347
|
-
selectFilter: 'select-filter',
|
|
348
|
-
requiredValidation: 'required-validation',
|
|
349
|
-
regularExpressionValidation: 'regular-expression-validation',
|
|
350
|
-
minLengthValidation: 'min-length-validation',
|
|
351
|
-
maxLengthValidation: 'max-length-validation',
|
|
352
|
-
lessThanValidation: 'less-than-validation',
|
|
353
|
-
greaterThanValidation: 'greater-than-validation',
|
|
354
|
-
betweenValidation: 'between-validation',
|
|
355
|
-
equalValidation: 'equal-validation',
|
|
356
|
-
callbackValidation: 'callback-validation',
|
|
357
|
-
donutChart: 'donut-chart',
|
|
358
|
-
lineChart: 'line-chart',
|
|
359
|
-
barChart: 'bar-chart',
|
|
360
|
-
gaugeChart: 'gauge-chart',
|
|
361
|
-
stickyNote: 'sticky-note',
|
|
362
|
-
clockCalendar: 'clock-calendar',
|
|
363
|
-
analogClock: 'analog-clock',
|
|
364
|
-
weather: 'weather',
|
|
365
|
-
minimalWeather: 'minimal-weather',
|
|
366
|
-
advancedWeather: 'advanced-weather',
|
|
367
|
-
metaData: 'meta-data-editor',
|
|
368
|
-
templateEditor: 'template-box-editor',
|
|
369
|
-
panel: 'panel',
|
|
370
|
-
notification: 'notification',
|
|
371
|
-
taskBoard: 'task-board',
|
|
372
|
-
comment: 'comment',
|
|
373
|
-
list: 'list',
|
|
374
|
-
listToolbar: 'list-toolbar',
|
|
375
|
-
entityList: 'entity-list',
|
|
376
|
-
documentUploader: 'document-uploader',
|
|
377
|
-
};
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, Injectable, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output, Input } from '@angular/core';
|
|
5
|
+
import { AXPopupService } from '@acorex/components/popup';
|
|
6
|
+
import * as i2 from '@acorex/components/form';
|
|
7
|
+
import { AXFormComponent, AXFormModule } from '@acorex/components/form';
|
|
8
|
+
import { AXPExpressionEvaluatorService } from '@acorex/platform/core';
|
|
9
|
+
import * as i1 from '@acorex/platform/layout/widget-core';
|
|
10
|
+
import { AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
|
|
11
|
+
import { isEqual, get, cloneDeep } from 'lodash-es';
|
|
12
|
+
import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
|
|
13
|
+
import * as i2$1 from '@acorex/components/button';
|
|
14
|
+
import { AXButtonModule } from '@acorex/components/button';
|
|
15
|
+
import * as i3 from '@acorex/components/decorators';
|
|
16
|
+
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
17
|
+
import * as i4 from '@acorex/components/loading';
|
|
18
|
+
import { AXLoadingModule } from '@acorex/components/loading';
|
|
19
|
+
import { AXBasePageComponent } from '@acorex/components/page';
|
|
20
|
+
import * as i5 from '@acorex/core/translation';
|
|
21
|
+
import { AXTranslationModule } from '@acorex/core/translation';
|
|
378
22
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
23
|
+
//#region ---- Inheritance Utilities ----
|
|
24
|
+
/**
|
|
25
|
+
* Resolves inherited properties from context and local values
|
|
26
|
+
*/
|
|
27
|
+
function resolveInheritedProperties(context, localValues = {}) {
|
|
383
28
|
return {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
defaultValue: ctor.defaultValue,
|
|
390
|
-
interface: {
|
|
391
|
-
name: ctor.name,
|
|
392
|
-
path: ctor.path ?? ctor.name,
|
|
393
|
-
type: AXPWidgetsCatalog.text,
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
visible: !isNil(ctor.visible) ? ctor.visible : true,
|
|
29
|
+
mode: localValues.mode ?? context.mode ?? 'edit',
|
|
30
|
+
disabled: localValues.disabled ?? context.disabled,
|
|
31
|
+
readonly: localValues.readonly ?? context.readonly,
|
|
32
|
+
direction: localValues.direction ?? context.direction,
|
|
33
|
+
visible: localValues.visible ?? context.visible,
|
|
397
34
|
};
|
|
398
35
|
}
|
|
399
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Merges inheritance context with local overrides
|
|
38
|
+
*/
|
|
39
|
+
function mergeInheritanceContext(parentContext, localOverrides = {}) {
|
|
400
40
|
return {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
group: ctor.group,
|
|
404
|
-
schema: {
|
|
405
|
-
dataType: 'number',
|
|
406
|
-
defaultValue: ctor.defaultValue,
|
|
407
|
-
interface: {
|
|
408
|
-
name: ctor.name,
|
|
409
|
-
path: ctor.path ?? ctor.name,
|
|
410
|
-
type: AXPWidgetsCatalog.number,
|
|
411
|
-
options: ctor.options,
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
visible: !isNil(ctor.visible) ? ctor.visible : true,
|
|
41
|
+
...parentContext,
|
|
42
|
+
...localOverrides,
|
|
415
43
|
};
|
|
416
44
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
schema: {
|
|
423
|
-
dataType: 'boolean',
|
|
424
|
-
defaultValue: ctor.defaultValue ?? false,
|
|
425
|
-
interface: {
|
|
426
|
-
name: ctor.name,
|
|
427
|
-
path: ctor.path ?? ctor.name,
|
|
428
|
-
type: AXPWidgetsCatalog.toggle,
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
visible: ctor.visible ?? true,
|
|
432
|
-
};
|
|
45
|
+
/**
|
|
46
|
+
* Generates a random string for path/name generation
|
|
47
|
+
*/
|
|
48
|
+
function generateRandomId() {
|
|
49
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
433
50
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
: typeof ctor.defaultValue === 'string'
|
|
444
|
-
? { id: ctor.defaultValue, title: ctor.defaultValue }
|
|
445
|
-
: ctor.defaultValue,
|
|
446
|
-
interface: {
|
|
447
|
-
name: ctor.name,
|
|
448
|
-
path: ctor.path ?? ctor.name,
|
|
449
|
-
type: AXPWidgetsCatalog.select,
|
|
450
|
-
options: {
|
|
451
|
-
dataSource: ctor.dataSource.map((item) => (typeof item === 'string' ? { id: item, title: item } : item)),
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
visible: ctor.visible ?? true,
|
|
456
|
-
};
|
|
51
|
+
/**
|
|
52
|
+
* Converts label to a valid path/name
|
|
53
|
+
*/
|
|
54
|
+
function labelToPath(label) {
|
|
55
|
+
return label
|
|
56
|
+
.toLowerCase()
|
|
57
|
+
.replace(/[^a-z0-9\s]/g, '') // Remove special characters
|
|
58
|
+
.replace(/\s+/g, '_') // Replace spaces with underscores
|
|
59
|
+
.substring(0, 50); // Limit length
|
|
457
60
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
this.host = inject(ElementRef).nativeElement;
|
|
466
|
-
this.layoutService = inject(AXPLayoutBuilderService);
|
|
467
|
-
this.contextService = inject(AXPLayoutBuilderContextStore);
|
|
468
|
-
this.config = this.token.config;
|
|
469
|
-
this.node = this.token.node;
|
|
470
|
-
this.name = this.token.node.name;
|
|
471
|
-
this.component = this;
|
|
472
|
-
this._options = signal(this.token.options ?? {}, ...(ngDevMode ? [{ debugName: "_options" }] : []));
|
|
473
|
-
this.options = this._options.asReadonly();
|
|
474
|
-
this.onOptionsChanged = new Subject();
|
|
475
|
-
this._status = signal(AXPWidgetStatus.Rendering, ...(ngDevMode ? [{ debugName: "_status" }] : []));
|
|
476
|
-
this.status = this._status.asReadonly();
|
|
477
|
-
this.onStatusChanged = new BehaviorSubject(this._status());
|
|
478
|
-
this.#statusEffect = effect(() => {
|
|
479
|
-
this.onStatusChanged.next(this.status());
|
|
480
|
-
}, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : []));
|
|
481
|
-
this.isBusy = computed(() => [AXPWidgetStatus.Rendering, AXPWidgetStatus.Processing].includes(this.status()), ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
|
|
482
|
-
this._children = signal(this.token.node.children ?? [], ...(ngDevMode ? [{ debugName: "_children" }] : []));
|
|
483
|
-
this.children = this._children.asReadonly();
|
|
484
|
-
}
|
|
485
|
-
get id() {
|
|
486
|
-
return this._id;
|
|
487
|
-
}
|
|
488
|
-
#statusEffect;
|
|
489
|
-
outputs() {
|
|
490
|
-
return [];
|
|
61
|
+
/**
|
|
62
|
+
* Generates path for value widgets based on hierarchy
|
|
63
|
+
*/
|
|
64
|
+
function generateValueWidgetPath(widgetName, formFieldName, formFieldLabel) {
|
|
65
|
+
// Priority: widget name -> form field name -> generated from label -> random
|
|
66
|
+
if (widgetName) {
|
|
67
|
+
return widgetName;
|
|
491
68
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
this.onAdded();
|
|
495
|
-
}
|
|
496
|
-
this.setStatus(AXPWidgetStatus.Rendered);
|
|
497
|
-
}
|
|
498
|
-
setStatus(status) {
|
|
499
|
-
this._status.set(status);
|
|
500
|
-
this.layoutService.updateStatus();
|
|
501
|
-
}
|
|
502
|
-
setOptions(values) {
|
|
503
|
-
const oldValue = this.options();
|
|
504
|
-
const value = cloneDeep(values);
|
|
505
|
-
this._options.set({ ...oldValue, ...value });
|
|
506
|
-
this.onOptionsChanged.next({ sender: this });
|
|
507
|
-
}
|
|
508
|
-
output(name) {
|
|
509
|
-
const outputs = this.outputs().map((c) => (typeof c == 'string' ? { name: c, value: c } : c));
|
|
510
|
-
if (outputs.some((c) => c.name == name)) {
|
|
511
|
-
const opt = get(this, name);
|
|
512
|
-
if (typeof opt == 'function') {
|
|
513
|
-
return opt();
|
|
514
|
-
}
|
|
515
|
-
return opt;
|
|
516
|
-
}
|
|
517
|
-
return null;
|
|
69
|
+
if (formFieldName) {
|
|
70
|
+
return formFieldName;
|
|
518
71
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
72
|
+
if (formFieldLabel) {
|
|
73
|
+
return labelToPath(formFieldLabel);
|
|
74
|
+
}
|
|
75
|
+
return generateRandomId();
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region ---- Service Implementation ----
|
|
79
|
+
class AXPLayoutBuilderService {
|
|
80
|
+
constructor() {
|
|
81
|
+
this.popupService = inject(AXPopupService);
|
|
524
82
|
}
|
|
525
|
-
|
|
526
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Create a new layout builder
|
|
85
|
+
*/
|
|
86
|
+
create() {
|
|
87
|
+
return new LayoutBuilder(this.popupService);
|
|
527
88
|
}
|
|
528
|
-
|
|
529
|
-
static { this.ɵ
|
|
530
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBaseWidgetComponent }); }
|
|
89
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
90
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
|
|
531
91
|
}
|
|
532
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
533
|
-
type: Injectable
|
|
92
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
|
|
93
|
+
type: Injectable,
|
|
94
|
+
args: [{
|
|
95
|
+
providedIn: 'root',
|
|
96
|
+
}]
|
|
534
97
|
}] });
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region ---- Layout Builder Implementation ----
|
|
100
|
+
/**
|
|
101
|
+
* Main layout builder - Single Responsibility: Layout orchestration
|
|
102
|
+
* Open/Closed: Extensible through container delegates
|
|
103
|
+
*/
|
|
104
|
+
class LayoutBuilder {
|
|
105
|
+
constructor(popupService) {
|
|
106
|
+
this.popupService = popupService;
|
|
107
|
+
this.root = {
|
|
108
|
+
children: [],
|
|
109
|
+
mode: 'edit',
|
|
110
|
+
type: 'flex-layout',
|
|
111
|
+
};
|
|
112
|
+
this.inheritanceContext = {
|
|
113
|
+
mode: 'edit',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Predefined layout containers at root level - delegate pattern required
|
|
117
|
+
grid(delegate) {
|
|
118
|
+
const container = new GridContainerBuilder();
|
|
119
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
120
|
+
if (delegate) {
|
|
121
|
+
delegate(container);
|
|
122
|
+
}
|
|
123
|
+
//this.state.children!.push(container.build());
|
|
124
|
+
this.root = container.build();
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
flex(delegate) {
|
|
128
|
+
const container = new FlexContainerBuilder();
|
|
129
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
130
|
+
if (delegate) {
|
|
131
|
+
delegate(container);
|
|
132
|
+
}
|
|
133
|
+
this.root.children.push(container.build());
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
panel(delegate) {
|
|
137
|
+
const container = new PanelContainerBuilder();
|
|
138
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
139
|
+
if (delegate) {
|
|
140
|
+
delegate(container);
|
|
141
|
+
}
|
|
142
|
+
this.root.children.push(container.build());
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
page(delegate) {
|
|
146
|
+
const container = new PageContainerBuilder();
|
|
147
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
148
|
+
if (delegate) {
|
|
149
|
+
delegate(container);
|
|
150
|
+
}
|
|
151
|
+
this.root.children.push(container.build());
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
tabset(delegate) {
|
|
155
|
+
const container = new TabsetContainerBuilder();
|
|
156
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
157
|
+
if (delegate) {
|
|
158
|
+
delegate(container);
|
|
159
|
+
}
|
|
160
|
+
this.root.children.push(container.build());
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
fieldset(delegate) {
|
|
164
|
+
const container = new FieldsetContainerBuilder();
|
|
165
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
166
|
+
if (delegate) {
|
|
167
|
+
delegate(container);
|
|
168
|
+
}
|
|
169
|
+
this.root.children.push(container.build());
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
dialog(delegate) {
|
|
173
|
+
const container = new DialogContainerBuilder(this.popupService);
|
|
174
|
+
if (delegate) {
|
|
175
|
+
delegate(container);
|
|
176
|
+
}
|
|
177
|
+
return container;
|
|
178
|
+
}
|
|
179
|
+
formField(label, delegate) {
|
|
180
|
+
const field = new FormFieldBuilder(label);
|
|
181
|
+
field.withInheritanceContext(this.inheritanceContext);
|
|
182
|
+
if (delegate) {
|
|
183
|
+
delegate(field);
|
|
184
|
+
}
|
|
185
|
+
this.root.children.push(field.build());
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
build() {
|
|
189
|
+
return {
|
|
190
|
+
type: this.root.type,
|
|
191
|
+
children: this.root.children,
|
|
192
|
+
mode: this.root.mode,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
538
195
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
this.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
this.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region ---- Container Builder Implementation ----
|
|
198
|
+
/**
|
|
199
|
+
* Abstract base container builder - Open/Closed Principle
|
|
200
|
+
* Provides common functionality for all container types
|
|
201
|
+
*/
|
|
202
|
+
class BaseContainerBuilder {
|
|
203
|
+
constructor(containerType) {
|
|
204
|
+
this.containerState = {
|
|
205
|
+
mode: 'edit',
|
|
206
|
+
type: 'flex-layout',
|
|
207
|
+
children: [],
|
|
208
|
+
};
|
|
209
|
+
this.inheritanceContext = {};
|
|
210
|
+
this.containerState.type = containerType;
|
|
211
|
+
}
|
|
212
|
+
// Base methods shared by all containers
|
|
213
|
+
ensureChildren() {
|
|
214
|
+
this.containerState.children = this.containerState.children || [];
|
|
215
|
+
}
|
|
216
|
+
addWidget(type, options) {
|
|
217
|
+
const child = new WidgetBuilder();
|
|
218
|
+
child.type(type);
|
|
219
|
+
// For value widgets, ensure path is provided
|
|
220
|
+
const widgetOptions = options ?? {};
|
|
221
|
+
const path = widgetOptions.path;
|
|
222
|
+
const name = widgetOptions.name;
|
|
223
|
+
if (this.isValueWidget(type) && !path) {
|
|
224
|
+
throw new Error(`Value widget '${type}' requires a 'path' property`);
|
|
225
|
+
}
|
|
226
|
+
// Set name and path in widget state, not options
|
|
227
|
+
if (name) {
|
|
228
|
+
child.name(name);
|
|
229
|
+
}
|
|
230
|
+
if (path) {
|
|
231
|
+
child.path(path);
|
|
232
|
+
}
|
|
233
|
+
// Remove name and path from options
|
|
234
|
+
const { name: _, path: __, ...cleanOptions } = widgetOptions;
|
|
235
|
+
// IMPORTANT: Apply inheritance context BEFORE setting options
|
|
236
|
+
// This ensures that inherited properties (readonly, disabled, etc.) are applied first
|
|
237
|
+
// Then user-provided options will override them if explicitly set
|
|
238
|
+
child.withInheritanceContext(this.inheritanceContext);
|
|
239
|
+
child.options(cleanOptions);
|
|
240
|
+
this.ensureChildren();
|
|
241
|
+
this.containerState.children.push(child.build());
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
isValueWidget(type) {
|
|
245
|
+
const valueWidgetTypes = [
|
|
246
|
+
'text-editor',
|
|
247
|
+
'large-text-editor',
|
|
248
|
+
'rich-text-editor',
|
|
249
|
+
'password-editor',
|
|
250
|
+
'number-editor',
|
|
251
|
+
'select-editor',
|
|
252
|
+
'lookup-editor',
|
|
253
|
+
'selection-list-editor',
|
|
254
|
+
'date-time-editor',
|
|
255
|
+
'toggle-editor',
|
|
256
|
+
'color-editor',
|
|
257
|
+
];
|
|
258
|
+
return valueWidgetTypes.includes(type);
|
|
259
|
+
}
|
|
260
|
+
build() {
|
|
261
|
+
const result = {
|
|
262
|
+
type: this.containerState.type,
|
|
263
|
+
children: this.containerState.children,
|
|
264
|
+
options: this.containerState.options,
|
|
265
|
+
mode: this.containerState.mode,
|
|
266
|
+
visible: this.containerState.visible,
|
|
267
|
+
};
|
|
268
|
+
// Add name with _form_field suffix for form fields
|
|
269
|
+
if (this.containerState.type === 'form-field') {
|
|
270
|
+
if (this.containerState.name) {
|
|
271
|
+
result.name = this.containerState.name;
|
|
559
272
|
}
|
|
560
|
-
|
|
561
|
-
.
|
|
562
|
-
.map((c) => ({
|
|
563
|
-
rule: c.rule,
|
|
564
|
-
message: c.message,
|
|
565
|
-
options: c.options,
|
|
566
|
-
}));
|
|
567
|
-
}, ...(ngDevMode ? [{ debugName: "validationRules" }] : []));
|
|
568
|
-
}
|
|
569
|
-
ngOnInit() {
|
|
570
|
-
this._isValueWidget = this.config.properties?.some((c) => c.name == 'path') ?? false;
|
|
571
|
-
if (this.isValueWidget()) {
|
|
572
|
-
this.detectFullPath();
|
|
573
|
-
if (!isNil(this.defaultValue) && isNil(this.getValue())) {
|
|
574
|
-
this.setValue(this.defaultValue);
|
|
273
|
+
else if (this.containerState.options?.['label']) {
|
|
274
|
+
result.name = labelToPath(this.containerState.options['label']) + '_form_field';
|
|
575
275
|
}
|
|
276
|
+
// Form fields don't have path
|
|
576
277
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const rawValue = this.contextService.getValue(path);
|
|
582
|
-
if (this.node.valueTransforms?.getter) {
|
|
583
|
-
return this.node.valueTransforms?.getter(rawValue);
|
|
584
|
-
}
|
|
585
|
-
return rawValue;
|
|
586
|
-
}
|
|
587
|
-
setValue(value) {
|
|
588
|
-
if (this.node.valueTransforms?.setter) {
|
|
589
|
-
value = this.node.valueTransforms?.setter(value);
|
|
590
|
-
}
|
|
591
|
-
const oldValue = this.getValue();
|
|
592
|
-
value = isUndefined(value) ? null : value;
|
|
593
|
-
if (isNil(value) && isNil(oldValue)) {
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
if (isEqual(oldValue, value)) {
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
if (this.fullPath()) {
|
|
600
|
-
this.contextService.update(this.fullPath(), value);
|
|
601
|
-
this.onValueChanged.next({ sender: this });
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
detectFullPath() {
|
|
605
|
-
const sections = [];
|
|
606
|
-
const ids = [];
|
|
607
|
-
//
|
|
608
|
-
let parent = this;
|
|
609
|
-
//
|
|
610
|
-
while (parent) {
|
|
611
|
-
const isValueWidget = parent instanceof AXPValueWidgetComponent && parent.isValueWidget();
|
|
612
|
-
const valueParent = parent;
|
|
613
|
-
const path = valueParent.path ?? (isValueWidget ? valueParent.name : null);
|
|
614
|
-
const id = valueParent.name;
|
|
615
|
-
//
|
|
616
|
-
if (path) {
|
|
617
|
-
sections.push(path);
|
|
278
|
+
else {
|
|
279
|
+
// Other containers can have name and path
|
|
280
|
+
if (this.containerState.name) {
|
|
281
|
+
result.name = this.containerState.name;
|
|
618
282
|
}
|
|
619
|
-
if (
|
|
620
|
-
|
|
283
|
+
if (this.containerState.path) {
|
|
284
|
+
result.path = this.containerState.path;
|
|
621
285
|
}
|
|
622
|
-
if (
|
|
623
|
-
|
|
624
|
-
if (parent.index != null) {
|
|
625
|
-
ids.push(`${parent.index}`);
|
|
626
|
-
}
|
|
286
|
+
if (this.containerState.defaultValue !== undefined) {
|
|
287
|
+
result.defaultValue = this.containerState.defaultValue;
|
|
627
288
|
}
|
|
628
|
-
parent = parent.parent;
|
|
629
|
-
}
|
|
630
|
-
//
|
|
631
|
-
this.fullPath.set(sections.reverse().join('.'));
|
|
632
|
-
this.parentPath.set(sections.slice(0, sections.length - 1).join('.'));
|
|
633
|
-
this._id = this.name || this.parent ? ids.reverse().join('_') : null;
|
|
634
|
-
if (this._id) {
|
|
635
|
-
this.layoutService.registerWidget(this._id, this);
|
|
636
289
|
}
|
|
290
|
+
return result;
|
|
637
291
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Base container builder mixin - Interface Segregation Principle
|
|
295
|
+
* Provides common container operations
|
|
296
|
+
*/
|
|
297
|
+
class BaseContainerMixin extends BaseContainerBuilder {
|
|
298
|
+
name(name) {
|
|
299
|
+
this.containerState.name = name;
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
path(path) {
|
|
303
|
+
this.containerState.path = path;
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
mode(mode) {
|
|
307
|
+
this.containerState.mode = mode;
|
|
308
|
+
this.inheritanceContext.mode = mode;
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
visible(condition) {
|
|
312
|
+
if (!this.containerState.options)
|
|
313
|
+
this.containerState.options = {};
|
|
314
|
+
this.containerState.options['visible'] = condition;
|
|
315
|
+
this.inheritanceContext.visible = condition;
|
|
316
|
+
return this;
|
|
317
|
+
}
|
|
318
|
+
disabled(condition) {
|
|
319
|
+
if (!this.containerState.options)
|
|
320
|
+
this.containerState.options = {};
|
|
321
|
+
this.containerState.options['disabled'] = condition;
|
|
322
|
+
this.inheritanceContext.disabled = condition;
|
|
323
|
+
return this;
|
|
324
|
+
}
|
|
325
|
+
readonly(condition) {
|
|
326
|
+
if (!this.containerState.options)
|
|
327
|
+
this.containerState.options = {};
|
|
328
|
+
this.containerState.options['readonly'] = condition;
|
|
329
|
+
this.inheritanceContext.readonly = condition;
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
direction(direction) {
|
|
333
|
+
if (!this.containerState.options)
|
|
334
|
+
this.containerState.options = {};
|
|
335
|
+
this.containerState.options['direction'] = direction;
|
|
336
|
+
this.inheritanceContext.direction = direction;
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
// Inheritance context methods
|
|
340
|
+
withInheritanceContext(context) {
|
|
341
|
+
this.inheritanceContext = mergeInheritanceContext(context);
|
|
342
|
+
return this;
|
|
343
|
+
}
|
|
344
|
+
getInheritanceContext() {
|
|
345
|
+
return { ...this.inheritanceContext };
|
|
642
346
|
}
|
|
643
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPValueWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
644
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPValueWidgetComponent }); }
|
|
645
347
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
const ds = new AXDataSource({
|
|
672
|
-
key: this.valueField(),
|
|
673
|
-
pageSize: 10,
|
|
674
|
-
load: async (e) => {
|
|
675
|
-
const raw = this.options()['dataSource'];
|
|
676
|
-
return {
|
|
677
|
-
items: raw,
|
|
678
|
-
total: raw.length,
|
|
679
|
-
};
|
|
680
|
-
},
|
|
681
|
-
byKey: (key) => {
|
|
682
|
-
const raw = this.options()['dataSource'];
|
|
683
|
-
const item = raw.filter((c) => c[this.valueField()] == key);
|
|
684
|
-
return Promise.resolve(item[0]);
|
|
685
|
-
},
|
|
686
|
-
});
|
|
687
|
-
this.dataSource.set(ds);
|
|
688
|
-
}
|
|
689
|
-
// resolve data source by name
|
|
690
|
-
else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
|
|
691
|
-
const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
|
|
692
|
-
const c = await this.dataService.get(id);
|
|
693
|
-
if (this.mode == 'designer' && c?.samples?.length) {
|
|
694
|
-
this.dataSource.set(convertArrayToDataSource(c.samples, {
|
|
695
|
-
key: this.valueField(),
|
|
696
|
-
pageSize: 500,
|
|
697
|
-
}));
|
|
698
|
-
}
|
|
699
|
-
else {
|
|
700
|
-
const ds = c?.source();
|
|
701
|
-
if (ds && ds instanceof Promise) {
|
|
702
|
-
const d = await ds;
|
|
703
|
-
this.dataSource.set(d);
|
|
704
|
-
}
|
|
705
|
-
else if (ds) {
|
|
706
|
-
this.dataSource.set(ds);
|
|
707
|
-
}
|
|
708
|
-
// empty datasource
|
|
709
|
-
else {
|
|
710
|
-
this.dataSource.set(convertArrayToDataSource([]));
|
|
711
|
-
}
|
|
348
|
+
/**
|
|
349
|
+
* Layout container mixin - Interface Segregation Principle
|
|
350
|
+
* Provides layout-specific operations
|
|
351
|
+
*/
|
|
352
|
+
class LayoutContainerMixin extends BaseContainerMixin {
|
|
353
|
+
layout(value) {
|
|
354
|
+
// Map layout intent to grid item sizing so containers like `form-field`
|
|
355
|
+
// can span multiple columns inside grid/fieldset layouts.
|
|
356
|
+
if (!this.containerState.options)
|
|
357
|
+
this.containerState.options = {};
|
|
358
|
+
if (typeof value === 'number') {
|
|
359
|
+
// Direct numeric shorthand → colSpan
|
|
360
|
+
this.containerState.options.colSpan = value;
|
|
361
|
+
}
|
|
362
|
+
else if (value) {
|
|
363
|
+
// Try to extract a reasonable colSpan from breakpoint positions
|
|
364
|
+
const positions = value.positions;
|
|
365
|
+
if (positions) {
|
|
366
|
+
const colSpan = positions?.lg?.colSpan ??
|
|
367
|
+
positions?.xl?.colSpan ??
|
|
368
|
+
positions?.xxl?.colSpan ??
|
|
369
|
+
positions?.md?.colSpan ??
|
|
370
|
+
positions?.sm?.colSpan;
|
|
371
|
+
if (colSpan != null) {
|
|
372
|
+
this.containerState.options.colSpan = colSpan;
|
|
712
373
|
}
|
|
713
374
|
}
|
|
714
|
-
// empty datasource
|
|
715
|
-
else {
|
|
716
|
-
this.dataSource.set(convertArrayToDataSource([]));
|
|
717
|
-
}
|
|
718
|
-
}, ...(ngDevMode ? [{ debugName: "rf" }] : []));
|
|
719
|
-
this.effect2 = effect(async () => {
|
|
720
|
-
const value = this.getValue();
|
|
721
|
-
const items = [];
|
|
722
|
-
if (Array.isArray(value)) {
|
|
723
|
-
items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
items.push(await this.extractItem(value));
|
|
727
|
-
}
|
|
728
|
-
this.selectedItems.set(items.filter((c) => c != null));
|
|
729
|
-
}, ...(ngDevMode ? [{ debugName: "effect2" }] : []));
|
|
730
|
-
}
|
|
731
|
-
async extractItem(item) {
|
|
732
|
-
if (isNil(item)) {
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
if (isObjectLike(item) && get(item, this.textField()) != null) {
|
|
736
|
-
return item;
|
|
737
|
-
}
|
|
738
|
-
const key = extractValue(item, this.valueField());
|
|
739
|
-
const ds = this.dataSource();
|
|
740
|
-
if (ds.config?.byKey) {
|
|
741
|
-
const found = await ds.config?.byKey(key);
|
|
742
|
-
if (found) {
|
|
743
|
-
return found;
|
|
744
|
-
}
|
|
745
375
|
}
|
|
746
|
-
return
|
|
747
|
-
? item
|
|
748
|
-
: {
|
|
749
|
-
[this.valueField()]: item,
|
|
750
|
-
[this.textField()]: item,
|
|
751
|
-
};
|
|
376
|
+
return this;
|
|
752
377
|
}
|
|
753
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPDataListWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
754
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPDataListWidgetComponent }); }
|
|
755
378
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
this.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Child container mixin - Interface Segregation Principle
|
|
381
|
+
* Provides child container management
|
|
382
|
+
*/
|
|
383
|
+
class ChildContainerMixin extends LayoutContainerMixin {
|
|
384
|
+
grid(delegate) {
|
|
385
|
+
const container = new GridContainerBuilder();
|
|
386
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
387
|
+
if (delegate) {
|
|
388
|
+
delegate(container);
|
|
389
|
+
}
|
|
390
|
+
this.ensureChildren();
|
|
391
|
+
this.containerState.children.push(container.build());
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
flex(delegate) {
|
|
395
|
+
const container = new FlexContainerBuilder();
|
|
396
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
397
|
+
if (delegate) {
|
|
398
|
+
delegate(container);
|
|
399
|
+
}
|
|
400
|
+
this.ensureChildren();
|
|
401
|
+
this.containerState.children.push(container.build());
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
panel(delegate) {
|
|
405
|
+
const container = new PanelContainerBuilder();
|
|
406
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
407
|
+
if (delegate) {
|
|
408
|
+
delegate(container);
|
|
409
|
+
}
|
|
410
|
+
this.ensureChildren();
|
|
411
|
+
this.containerState.children.push(container.build());
|
|
412
|
+
return this;
|
|
413
|
+
}
|
|
414
|
+
page(delegate) {
|
|
415
|
+
const container = new PageContainerBuilder();
|
|
416
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
417
|
+
if (delegate) {
|
|
418
|
+
delegate(container);
|
|
419
|
+
}
|
|
420
|
+
this.ensureChildren();
|
|
421
|
+
this.containerState.children.push(container.build());
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
tabset(delegate) {
|
|
425
|
+
const container = new TabsetContainerBuilder();
|
|
426
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
427
|
+
if (delegate) {
|
|
428
|
+
delegate(container);
|
|
429
|
+
}
|
|
430
|
+
this.ensureChildren();
|
|
431
|
+
this.containerState.children.push(container.build());
|
|
432
|
+
return this;
|
|
433
|
+
}
|
|
434
|
+
fieldset(delegate) {
|
|
435
|
+
const container = new FieldsetContainerBuilder();
|
|
436
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
437
|
+
if (delegate) {
|
|
438
|
+
delegate(container);
|
|
439
|
+
}
|
|
440
|
+
this.ensureChildren();
|
|
441
|
+
this.containerState.children.push(container.build());
|
|
442
|
+
return this;
|
|
443
|
+
}
|
|
444
|
+
dialog(delegate) {
|
|
445
|
+
const container = new DialogContainerBuilder(); // Will use inject() fallback
|
|
446
|
+
if (delegate) {
|
|
447
|
+
delegate(container);
|
|
448
|
+
}
|
|
449
|
+
return container;
|
|
450
|
+
}
|
|
451
|
+
formField(label, delegate) {
|
|
452
|
+
const field = new FormFieldBuilder(label);
|
|
453
|
+
field.withInheritanceContext(this.inheritanceContext);
|
|
454
|
+
if (delegate) {
|
|
455
|
+
delegate(field);
|
|
456
|
+
}
|
|
457
|
+
this.ensureChildren();
|
|
458
|
+
this.containerState.children.push(field.build());
|
|
459
|
+
return this;
|
|
773
460
|
}
|
|
774
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPColumnWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
775
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPColumnWidgetComponent }); }
|
|
776
461
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
class
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
this
|
|
785
|
-
const options = this.options();
|
|
786
|
-
const style = {};
|
|
787
|
-
const spacing = options?.['spacing'];
|
|
788
|
-
const border = options?.['border'];
|
|
789
|
-
const backgroundColor = options?.['backgroundColor'];
|
|
790
|
-
const direction = options?.['direction'];
|
|
791
|
-
const overflow = options?.['overflow'];
|
|
792
|
-
const overflowX = options?.['overflowX'];
|
|
793
|
-
const overflowY = options?.['overflowY'];
|
|
794
|
-
style['background-color'] = backgroundColor ?? '';
|
|
795
|
-
style['padding'] = spacing?.padding ?? '';
|
|
796
|
-
style['margin'] = spacing?.margin ?? '';
|
|
797
|
-
style['border-radius'] = border?.radius ?? '';
|
|
798
|
-
style['border-width'] = border?.width ?? '';
|
|
799
|
-
style['border-color'] = border?.color ?? '';
|
|
800
|
-
style['border-style'] = border?.style ?? '';
|
|
801
|
-
style['overflow'] = overflow ?? '';
|
|
802
|
-
style['overflow-x'] = overflowX ?? '';
|
|
803
|
-
style['overflow-y'] = overflowY ?? '';
|
|
804
|
-
style['direction'] = direction ?? '';
|
|
805
|
-
return style;
|
|
806
|
-
}, ...(ngDevMode ? [{ debugName: "hostBoxStyle" }] : []));
|
|
807
|
-
this.blockStyle = computed(() => {
|
|
808
|
-
const options = this.options();
|
|
809
|
-
const style = { ...this.hostBoxStyle() };
|
|
810
|
-
const width = options?.['width'];
|
|
811
|
-
const minWidth = options?.['minWidth'];
|
|
812
|
-
const maxWidth = options?.['maxWidth'];
|
|
813
|
-
const height = options?.['height'];
|
|
814
|
-
const minHeight = options?.['minHeight'];
|
|
815
|
-
const maxHeight = options?.['maxHeight'];
|
|
816
|
-
style['min-width'] = minWidth ?? '';
|
|
817
|
-
style['width'] = width ?? '';
|
|
818
|
-
style['max-width'] = maxWidth ?? '';
|
|
819
|
-
style['min-height'] = minHeight ?? '';
|
|
820
|
-
style['height'] = height ?? '';
|
|
821
|
-
style['max-height'] = maxHeight ?? '';
|
|
822
|
-
return style;
|
|
823
|
-
}, ...(ngDevMode ? [{ debugName: "blockStyle" }] : []));
|
|
824
|
-
this.inlineStyle = computed(() => {
|
|
825
|
-
return { ...this.hostBoxStyle() };
|
|
826
|
-
}, ...(ngDevMode ? [{ debugName: "inlineStyle" }] : []));
|
|
827
|
-
this.blockClass = computed(() => {
|
|
828
|
-
return {
|
|
829
|
-
'ax-block': true,
|
|
830
|
-
'ax-w-full': true,
|
|
831
|
-
// 'ax-widget-outline': true,
|
|
832
|
-
};
|
|
833
|
-
}, ...(ngDevMode ? [{ debugName: "blockClass" }] : []));
|
|
834
|
-
this.inlineClass = computed(() => {
|
|
835
|
-
return {
|
|
836
|
-
'ax-inline-block': true,
|
|
837
|
-
};
|
|
838
|
-
}, ...(ngDevMode ? [{ debugName: "inlineClass" }] : []));
|
|
462
|
+
/**
|
|
463
|
+
* Widget container mixin - Interface Segregation Principle
|
|
464
|
+
* Provides widget creation operations
|
|
465
|
+
*/
|
|
466
|
+
class WidgetContainerMixin extends ChildContainerMixin {
|
|
467
|
+
textBox(options) {
|
|
468
|
+
this.addWidget('text-editor', options);
|
|
469
|
+
return this;
|
|
839
470
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, decorators: [{
|
|
844
|
-
type: Injectable
|
|
845
|
-
}] });
|
|
846
|
-
|
|
847
|
-
class AXPBlockBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
|
|
848
|
-
constructor() {
|
|
849
|
-
super(...arguments);
|
|
850
|
-
this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
|
|
851
|
-
this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
|
|
471
|
+
largeTextBox(options) {
|
|
472
|
+
this.addWidget('large-text-editor', options);
|
|
473
|
+
return this;
|
|
852
474
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
}, ...(ngDevMode ? [{ debugName: "hostFlexStyle" }] : []));
|
|
903
|
-
this.hostFlexClass = computed(() => {
|
|
904
|
-
return {
|
|
905
|
-
...this.blockClass(),
|
|
906
|
-
'ax-flex': true,
|
|
907
|
-
'ax-h-full': true,
|
|
908
|
-
};
|
|
909
|
-
}, ...(ngDevMode ? [{ debugName: "hostFlexClass" }] : []));
|
|
910
|
-
this.hostClass = computed(() => {
|
|
911
|
-
return this.hostFlexClass();
|
|
912
|
-
}, ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
|
|
913
|
-
this.hostStyle = computed(() => {
|
|
914
|
-
return this.hostFlexStyle();
|
|
915
|
-
}, ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
|
|
916
|
-
}
|
|
917
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
918
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent }); }
|
|
919
|
-
}
|
|
920
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, decorators: [{
|
|
921
|
-
type: Injectable
|
|
922
|
-
}] });
|
|
923
|
-
|
|
924
|
-
class AXPInlineBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
|
|
925
|
-
constructor() {
|
|
926
|
-
super(...arguments);
|
|
927
|
-
this.hostClass = computed(() => this.inlineClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
|
|
928
|
-
this.hostStyle = computed(() => this.inlineStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
|
|
475
|
+
richText(options) {
|
|
476
|
+
this.addWidget('rich-text-editor', options);
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
479
|
+
passwordBox(options) {
|
|
480
|
+
this.addWidget('password-editor', options);
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
numberBox(options) {
|
|
484
|
+
this.addWidget('number-editor', options);
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
selectBox(options) {
|
|
488
|
+
this.addWidget('select-editor', options);
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
lookupBox(options) {
|
|
492
|
+
this.addWidget('lookup-editor', options);
|
|
493
|
+
return this;
|
|
494
|
+
}
|
|
495
|
+
selectionList(options) {
|
|
496
|
+
this.addWidget('selection-list-editor', options);
|
|
497
|
+
return this;
|
|
498
|
+
}
|
|
499
|
+
dateTimeBox(options) {
|
|
500
|
+
this.addWidget('date-time-editor', options);
|
|
501
|
+
return this;
|
|
502
|
+
}
|
|
503
|
+
toggleSwitch(options) {
|
|
504
|
+
this.addWidget('toggle-editor', options);
|
|
505
|
+
return this;
|
|
506
|
+
}
|
|
507
|
+
colorBox(options) {
|
|
508
|
+
this.addWidget('color-editor', options);
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
list(delegate) {
|
|
512
|
+
const container = new ListWidgetBuilder();
|
|
513
|
+
container.withInheritanceContext(this.inheritanceContext);
|
|
514
|
+
if (delegate) {
|
|
515
|
+
delegate(container);
|
|
516
|
+
}
|
|
517
|
+
this.ensureChildren();
|
|
518
|
+
this.containerState.children.push(container.build());
|
|
519
|
+
return this;
|
|
520
|
+
}
|
|
521
|
+
customWidget(type, options) {
|
|
522
|
+
this.addWidget(type, options);
|
|
523
|
+
return this;
|
|
929
524
|
}
|
|
930
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
931
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent }); }
|
|
932
|
-
}
|
|
933
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, decorators: [{
|
|
934
|
-
type: Injectable
|
|
935
|
-
}] });
|
|
936
|
-
|
|
937
|
-
class AXPFlexItemBaseLayoutWidgetComponent extends AXPInlineBaseLayoutWidgetComponent {
|
|
938
|
-
constructor() {
|
|
939
|
-
super(...arguments);
|
|
940
|
-
this.flexItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flexItem" }] : []));
|
|
941
|
-
this.hostFlexItemStyle = computed(() => {
|
|
942
|
-
const inlineStyle = this.blockStyle();
|
|
943
|
-
const style = { ...inlineStyle };
|
|
944
|
-
const fi = this.flexItem();
|
|
945
|
-
if (isNil(fi?.order)) {
|
|
946
|
-
style['order'] = '';
|
|
947
|
-
}
|
|
948
|
-
else {
|
|
949
|
-
style['order'] = fi.order;
|
|
950
|
-
}
|
|
951
|
-
if (isNil(fi?.grow)) {
|
|
952
|
-
style['flex-grow'] = '';
|
|
953
|
-
}
|
|
954
|
-
else {
|
|
955
|
-
style['flex-grow'] = fi.grow;
|
|
956
|
-
}
|
|
957
|
-
if (isNil(fi?.shrink)) {
|
|
958
|
-
style['flex-shrink'] = '';
|
|
959
|
-
}
|
|
960
|
-
else {
|
|
961
|
-
style['flex-shrink'] = fi.shrink;
|
|
962
|
-
}
|
|
963
|
-
if (isNil(fi?.basis)) {
|
|
964
|
-
style['flex-basis'] = '';
|
|
965
|
-
}
|
|
966
|
-
else {
|
|
967
|
-
style['flex-basis'] = fi.basis;
|
|
968
|
-
}
|
|
969
|
-
if (isNil(fi?.alignSelf)) {
|
|
970
|
-
style['align-self'] = '';
|
|
971
|
-
}
|
|
972
|
-
else {
|
|
973
|
-
style['align-self'] = fi.alignSelf;
|
|
974
|
-
}
|
|
975
|
-
return style;
|
|
976
|
-
}, ...(ngDevMode ? [{ debugName: "hostFlexItemStyle" }] : []));
|
|
977
|
-
this.hostFlexItemClass = computed(() => {
|
|
978
|
-
return {
|
|
979
|
-
...this.blockClass(),
|
|
980
|
-
};
|
|
981
|
-
}, ...(ngDevMode ? [{ debugName: "hostFlexItemClass" }] : []));
|
|
982
|
-
this.hostClass = computed(() => {
|
|
983
|
-
return this.hostFlexItemClass();
|
|
984
|
-
}, ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
|
|
985
|
-
this.hostStyle = computed(() => {
|
|
986
|
-
return this.hostFlexItemStyle();
|
|
987
|
-
}, ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
|
|
988
|
-
}
|
|
989
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
990
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent }); }
|
|
991
525
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
class
|
|
526
|
+
/**
|
|
527
|
+
* Flex Container Builder - Liskov Substitution Principle
|
|
528
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
529
|
+
*/
|
|
530
|
+
class FlexContainerBuilder extends WidgetContainerMixin {
|
|
997
531
|
constructor() {
|
|
998
|
-
super(
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent }); }
|
|
532
|
+
super('flex-layout');
|
|
533
|
+
}
|
|
534
|
+
setOptions(options) {
|
|
535
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
536
|
+
return this;
|
|
537
|
+
}
|
|
538
|
+
// Individual fluent methods for Flex
|
|
539
|
+
setDirection(direction) {
|
|
540
|
+
return this.setOptions({ flexDirection: direction });
|
|
541
|
+
}
|
|
542
|
+
setWrap(wrap) {
|
|
543
|
+
return this.setOptions({ flexWrap: wrap });
|
|
544
|
+
}
|
|
545
|
+
setJustifyContent(justify) {
|
|
546
|
+
return this.setOptions({ justifyContent: justify });
|
|
547
|
+
}
|
|
548
|
+
setAlignItems(align) {
|
|
549
|
+
return this.setOptions({ alignItems: align });
|
|
550
|
+
}
|
|
551
|
+
setGap(gap) {
|
|
552
|
+
return this.setOptions({ gap });
|
|
553
|
+
}
|
|
554
|
+
setBackgroundColor(color) {
|
|
555
|
+
return this.setOptions({ backgroundColor: color });
|
|
556
|
+
}
|
|
557
|
+
setPadding(padding) {
|
|
558
|
+
return this.setOptions({ spacing: { padding } });
|
|
559
|
+
}
|
|
560
|
+
setMargin(margin) {
|
|
561
|
+
return this.setOptions({ spacing: { margin } });
|
|
562
|
+
}
|
|
1030
563
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
class
|
|
564
|
+
/**
|
|
565
|
+
* Grid Container Builder - Liskov Substitution Principle
|
|
566
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
567
|
+
*/
|
|
568
|
+
class GridContainerBuilder extends WidgetContainerMixin {
|
|
1036
569
|
constructor() {
|
|
1037
|
-
super(
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
this.
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
|
|
570
|
+
super('grid-layout');
|
|
571
|
+
}
|
|
572
|
+
setOptions(options) {
|
|
573
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
574
|
+
return this;
|
|
575
|
+
}
|
|
576
|
+
// Individual fluent methods for Grid
|
|
577
|
+
setColumns(columns) {
|
|
578
|
+
return this.setOptions({ grid: { default: { columns } } });
|
|
579
|
+
}
|
|
580
|
+
setRows(rows) {
|
|
581
|
+
return this.setOptions({ grid: { default: { rows } } });
|
|
582
|
+
}
|
|
583
|
+
setGap(gap) {
|
|
584
|
+
return this.setOptions({ grid: { default: { gap } } });
|
|
585
|
+
}
|
|
586
|
+
setJustifyItems(justify) {
|
|
587
|
+
return this.setOptions({ grid: { default: { justifyItems: justify } } });
|
|
588
|
+
}
|
|
589
|
+
setAlignItems(align) {
|
|
590
|
+
return this.setOptions({ grid: { default: { alignItems: align } } });
|
|
591
|
+
}
|
|
592
|
+
setAutoFlow(flow) {
|
|
593
|
+
return this.setOptions({ grid: { default: { autoFlow: flow } } });
|
|
594
|
+
}
|
|
595
|
+
setBackgroundColor(color) {
|
|
596
|
+
return this.setOptions({ backgroundColor: color });
|
|
597
|
+
}
|
|
598
|
+
setPadding(padding) {
|
|
599
|
+
return this.setOptions({ spacing: { padding } });
|
|
600
|
+
}
|
|
601
|
+
setMargin(margin) {
|
|
602
|
+
return this.setOptions({ spacing: { margin } });
|
|
603
|
+
}
|
|
1070
604
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
class
|
|
605
|
+
/**
|
|
606
|
+
* Panel Container Builder - Liskov Substitution Principle
|
|
607
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
608
|
+
*/
|
|
609
|
+
class PanelContainerBuilder extends WidgetContainerMixin {
|
|
1076
610
|
constructor() {
|
|
1077
|
-
super(
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
this
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
611
|
+
super('panel-layout');
|
|
612
|
+
}
|
|
613
|
+
setOptions(options) {
|
|
614
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
615
|
+
return this;
|
|
616
|
+
}
|
|
617
|
+
// Individual fluent methods for Panel
|
|
618
|
+
setCaption(caption) {
|
|
619
|
+
return this.setOptions({ caption });
|
|
620
|
+
}
|
|
621
|
+
setIcon(icon) {
|
|
622
|
+
return this.setOptions({ icon });
|
|
623
|
+
}
|
|
624
|
+
setLook(look) {
|
|
625
|
+
return this.setOptions({ look });
|
|
626
|
+
}
|
|
627
|
+
setShowHeader(show) {
|
|
628
|
+
return this.setOptions({ showHeader: show });
|
|
629
|
+
}
|
|
630
|
+
setCollapsed(collapsed) {
|
|
631
|
+
return this.setOptions({ collapsed });
|
|
632
|
+
}
|
|
1099
633
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
class
|
|
634
|
+
/**
|
|
635
|
+
* Page Container Builder - Liskov Substitution Principle
|
|
636
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
637
|
+
*/
|
|
638
|
+
class PageContainerBuilder extends WidgetContainerMixin {
|
|
1105
639
|
constructor() {
|
|
1106
|
-
super(
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
640
|
+
super('page-layout');
|
|
641
|
+
}
|
|
642
|
+
setOptions(options) {
|
|
643
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
644
|
+
return this;
|
|
645
|
+
}
|
|
646
|
+
// Individual fluent methods for Page
|
|
647
|
+
setBackgroundColor(color) {
|
|
648
|
+
return this.setOptions({ backgroundColor: color });
|
|
649
|
+
}
|
|
650
|
+
setTheme(theme) {
|
|
651
|
+
return this.setOptions({ theme });
|
|
652
|
+
}
|
|
653
|
+
setHasHeader(hasHeader) {
|
|
654
|
+
return this.setOptions({ hasHeader });
|
|
655
|
+
}
|
|
656
|
+
setHasFooter(hasFooter) {
|
|
657
|
+
return this.setOptions({ hasFooter });
|
|
658
|
+
}
|
|
659
|
+
setDirection(direction) {
|
|
660
|
+
return this.setOptions({ direction });
|
|
661
|
+
}
|
|
1120
662
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
class
|
|
1126
|
-
/**
|
|
1127
|
-
*
|
|
1128
|
-
*/
|
|
663
|
+
/**
|
|
664
|
+
* Tabset Container Builder - Liskov Substitution Principle
|
|
665
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
666
|
+
*/
|
|
667
|
+
class TabsetContainerBuilder extends WidgetContainerMixin {
|
|
1129
668
|
constructor() {
|
|
1130
|
-
|
|
1131
|
-
AXPWidgetRegistryService.instance = this;
|
|
669
|
+
super('tabset-layout');
|
|
1132
670
|
}
|
|
1133
|
-
|
|
1134
|
-
this.
|
|
671
|
+
setOptions(options) {
|
|
672
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
673
|
+
return this;
|
|
1135
674
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
newWidget.name = widget.name;
|
|
1140
|
-
this.register(newWidget);
|
|
675
|
+
// Individual fluent methods for Tabset
|
|
676
|
+
setLook(look) {
|
|
677
|
+
return this.setOptions({ look });
|
|
1141
678
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
if (!widget) {
|
|
1145
|
-
throw new Error(`Widget with name "${name}" does not exist.`);
|
|
1146
|
-
}
|
|
1147
|
-
return widget;
|
|
679
|
+
setOrientation(orientation) {
|
|
680
|
+
return this.setOptions({ orientation });
|
|
1148
681
|
}
|
|
1149
|
-
|
|
1150
|
-
return
|
|
682
|
+
setActiveIndex(index) {
|
|
683
|
+
return this.setOptions({ activeIndex: index });
|
|
1151
684
|
}
|
|
1152
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1153
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRegistryService, providedIn: 'root' }); }
|
|
1154
685
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
super(...arguments);
|
|
1165
|
-
this.widgetRegistery = inject(AXPWidgetRegistryService);
|
|
1166
|
-
this.grid = inject(AXBaseDataTable);
|
|
1167
|
-
this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : []));
|
|
1168
|
-
this.loadingRow = signal(null, ...(ngDevMode ? [{ debugName: "loadingRow" }] : []));
|
|
1169
|
-
this.injector = inject(Injector);
|
|
1170
|
-
this.cdr = inject(ChangeDetectorRef);
|
|
1171
|
-
}
|
|
1172
|
-
get node() {
|
|
1173
|
-
return this._node;
|
|
1174
|
-
}
|
|
1175
|
-
set node(v) {
|
|
1176
|
-
this._node = v;
|
|
1177
|
-
}
|
|
1178
|
-
get renderFooterTemplate() {
|
|
1179
|
-
return this.footerTemplate ?? this._contentFooterTemplate;
|
|
1180
|
-
}
|
|
1181
|
-
get renderCellTemplate() {
|
|
1182
|
-
return this.cellTemplate ?? this._contentCellTemplate;
|
|
1183
|
-
}
|
|
1184
|
-
async handleExpandRow(row) {
|
|
1185
|
-
this.loadingRow.set(row);
|
|
1186
|
-
await this.grid.expandRow(row);
|
|
1187
|
-
this.loadingRow.set(null);
|
|
1188
|
-
// if (row.data?.__meta__?.expanded === undefined) {
|
|
1189
|
-
// this.width = `${parseInt(this.width as string) + 24}px`;
|
|
1190
|
-
// }
|
|
1191
|
-
}
|
|
1192
|
-
get renderHeaderTemplate() {
|
|
1193
|
-
return this.headerTemplate ?? this._contentHeaderTemplate;
|
|
1194
|
-
}
|
|
1195
|
-
get loadingEnabled() {
|
|
1196
|
-
return true;
|
|
1197
|
-
}
|
|
1198
|
-
get name() {
|
|
1199
|
-
return `col-${this.node.path}`;
|
|
1200
|
-
}
|
|
1201
|
-
async ngOnInit() {
|
|
1202
|
-
const widget = this.widgetRegistery.resolve(this.node.type);
|
|
1203
|
-
const mode = 'column';
|
|
1204
|
-
this.component = await widget?.components[mode]?.component();
|
|
1205
|
-
//
|
|
1206
|
-
const props = widget?.components[mode]?.properties
|
|
1207
|
-
?.filter((c) => c.schema.defaultValue)
|
|
1208
|
-
.map((c) => ({ [c.name]: c.schema.defaultValue }))
|
|
1209
|
-
.reduce((acc, curr) => {
|
|
1210
|
-
return { ...acc, ...curr };
|
|
1211
|
-
}, {});
|
|
1212
|
-
//
|
|
1213
|
-
this.mergedOptions.set(merge(props, this.node.options) || {});
|
|
1214
|
-
const tokenValue = {
|
|
1215
|
-
path: this.node.path,
|
|
1216
|
-
options: this.mergedOptions(),
|
|
1217
|
-
};
|
|
1218
|
-
this.widgetInjector = Injector.create({
|
|
1219
|
-
parent: this.injector,
|
|
1220
|
-
providers: [
|
|
1221
|
-
{
|
|
1222
|
-
provide: AXP_WIDGET_COLUMN_TOKEN,
|
|
1223
|
-
useValue: tokenValue,
|
|
1224
|
-
},
|
|
1225
|
-
],
|
|
1226
|
-
});
|
|
1227
|
-
this.width = this.customWidth ? this.customWidth : (this.mergedOptions().width ?? '200px');
|
|
1228
|
-
this.allowResizing = this.mergedOptions().allowResizing || true;
|
|
1229
|
-
this.cdr.detectChanges();
|
|
686
|
+
/**
|
|
687
|
+
* Form Field Builder - Liskov Substitution Principle
|
|
688
|
+
* Can only contain ONE widget with automatic path generation
|
|
689
|
+
*/
|
|
690
|
+
class FormFieldBuilder extends LayoutContainerMixin {
|
|
691
|
+
constructor(label) {
|
|
692
|
+
super('form-field');
|
|
693
|
+
this.hasWidget = false;
|
|
694
|
+
this.containerState.options = { label, showLabel: true };
|
|
1230
695
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
rowData: data,
|
|
1235
|
-
};
|
|
696
|
+
setOptions(options) {
|
|
697
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
698
|
+
return this;
|
|
1236
699
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
(click)="handleExpandRow(row)"
|
|
1248
|
-
class="ax-expand-handler"
|
|
1249
|
-
[class.ax-invisible]="row.data.hasChild === false"
|
|
1250
|
-
id="ax-expand-handler-container"
|
|
1251
|
-
[style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
|
|
1252
|
-
>
|
|
1253
|
-
@if (loadingRow() === row) {
|
|
1254
|
-
<i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
|
|
1255
|
-
} @else {
|
|
1256
|
-
@if (row.data?.__meta__?.expanded) {
|
|
1257
|
-
<i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
|
|
1258
|
-
} @else {
|
|
1259
|
-
<i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
</div>
|
|
700
|
+
setLabel(label) {
|
|
701
|
+
return this.setOptions({ label });
|
|
702
|
+
}
|
|
703
|
+
setShowLabel(showLabel) {
|
|
704
|
+
return this.setOptions({ showLabel });
|
|
705
|
+
}
|
|
706
|
+
// Single widget methods with automatic path generation
|
|
707
|
+
addSingleWidget(type, options) {
|
|
708
|
+
if (this.hasWidget) {
|
|
709
|
+
throw new Error('Form field can only contain one widget');
|
|
1263
710
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
711
|
+
const formFieldName = this.containerState.name;
|
|
712
|
+
const formFieldPath = this.containerState.path; // Get explicit path from form field
|
|
713
|
+
const formFieldLabel = this.containerState.options?.['label'];
|
|
714
|
+
const widgetName = options?.name;
|
|
715
|
+
// Generate widget path: explicit path -> widget name -> form field name -> label -> random
|
|
716
|
+
let widgetPath;
|
|
717
|
+
if (formFieldPath) {
|
|
718
|
+
widgetPath = formFieldPath; // Use explicit form field path first
|
|
1268
719
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
<ng-template #footer></ng-template>
|
|
1272
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1273
|
-
}
|
|
1274
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetColumnRendererComponent, decorators: [{
|
|
1275
|
-
type: Component,
|
|
1276
|
-
args: [{
|
|
1277
|
-
selector: 'axp-widget-column-renderer',
|
|
1278
|
-
template: `
|
|
1279
|
-
<ng-template #header>{{ caption | translate | async }}</ng-template>
|
|
1280
|
-
<ng-template #cell let-row>
|
|
1281
|
-
<div class="ax-flex ax-gap-2 ax-items-center">
|
|
1282
|
-
@if (expandHandler) {
|
|
1283
|
-
<div
|
|
1284
|
-
(click)="handleExpandRow(row)"
|
|
1285
|
-
class="ax-expand-handler"
|
|
1286
|
-
[class.ax-invisible]="row.data.hasChild === false"
|
|
1287
|
-
id="ax-expand-handler-container"
|
|
1288
|
-
[style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
|
|
1289
|
-
>
|
|
1290
|
-
@if (loadingRow() === row) {
|
|
1291
|
-
<i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
|
|
1292
|
-
} @else {
|
|
1293
|
-
@if (row.data?.__meta__?.expanded) {
|
|
1294
|
-
<i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
|
|
1295
|
-
} @else {
|
|
1296
|
-
<i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
</div>
|
|
720
|
+
else if (widgetName) {
|
|
721
|
+
widgetPath = widgetName;
|
|
1300
722
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
*ngComponentOutlet="component; injector: widgetInjector; inputs: getInputs(row.data)"
|
|
1304
|
-
></ng-container>
|
|
723
|
+
else if (formFieldName) {
|
|
724
|
+
widgetPath = formFieldName; // Use form field name as default path
|
|
1305
725
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
args: [{ required: true }]
|
|
1327
|
-
}], footerTemplate: [{
|
|
1328
|
-
type: Input
|
|
1329
|
-
}], _contentFooterTemplate: [{
|
|
1330
|
-
type: ViewChild,
|
|
1331
|
-
args: ['footer']
|
|
1332
|
-
}], expandHandler: [{
|
|
1333
|
-
type: Input
|
|
1334
|
-
}], cellTemplate: [{
|
|
1335
|
-
type: Input
|
|
1336
|
-
}], _contentCellTemplate: [{
|
|
1337
|
-
type: ViewChild,
|
|
1338
|
-
args: ['cell']
|
|
1339
|
-
}], headerTemplate: [{
|
|
1340
|
-
type: Input
|
|
1341
|
-
}], _contentHeaderTemplate: [{
|
|
1342
|
-
type: ViewChild,
|
|
1343
|
-
args: ['header']
|
|
1344
|
-
}] } });
|
|
1345
|
-
|
|
1346
|
-
class AXPWidgetContainerComponent {
|
|
1347
|
-
set context(value) {
|
|
1348
|
-
this.contextService.set(value);
|
|
726
|
+
else if (formFieldLabel) {
|
|
727
|
+
widgetPath = labelToPath(formFieldLabel);
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
widgetPath = generateRandomId();
|
|
731
|
+
}
|
|
732
|
+
const finalName = widgetName || formFieldName || widgetPath;
|
|
733
|
+
const child = new WidgetBuilder();
|
|
734
|
+
child.type(type);
|
|
735
|
+
child.name(finalName);
|
|
736
|
+
child.path(widgetPath);
|
|
737
|
+
// Remove name from options since it's now in state
|
|
738
|
+
const { name: _, ...cleanOptions } = (options || {});
|
|
739
|
+
child.withInheritanceContext(this.inheritanceContext);
|
|
740
|
+
child.options(cleanOptions);
|
|
741
|
+
// IMPORTANT: Store the widget builder, don't build it yet!
|
|
742
|
+
// This allows properties set after this method (like disabled, readonly) to be applied
|
|
743
|
+
this.childWidget = child;
|
|
744
|
+
this.hasWidget = true;
|
|
745
|
+
return this;
|
|
1349
746
|
}
|
|
1350
|
-
|
|
1351
|
-
this.
|
|
747
|
+
textBox(options) {
|
|
748
|
+
return this.addSingleWidget('text-editor', options);
|
|
1352
749
|
}
|
|
1353
|
-
|
|
1354
|
-
this.
|
|
1355
|
-
this.builderService = inject(AXPLayoutBuilderService);
|
|
1356
|
-
this.onContextChanged = new EventEmitter();
|
|
1357
|
-
this.status = computed(() => {
|
|
1358
|
-
return this.builderService.status();
|
|
1359
|
-
}, ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
1360
|
-
this.isBusy = computed(() => {
|
|
1361
|
-
return this.builderService.isBusy();
|
|
1362
|
-
}, ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
|
|
1363
|
-
effect(() => {
|
|
1364
|
-
if (this.contextService.isChanged()) {
|
|
1365
|
-
this.onContextChanged.emit(this.contextService.changeEvent());
|
|
1366
|
-
}
|
|
1367
|
-
});
|
|
750
|
+
largeTextBox(options) {
|
|
751
|
+
return this.addSingleWidget('large-text-editor', options);
|
|
1368
752
|
}
|
|
1369
|
-
|
|
1370
|
-
this.
|
|
753
|
+
richText(options) {
|
|
754
|
+
return this.addSingleWidget('rich-text-editor', options);
|
|
1371
755
|
}
|
|
1372
|
-
|
|
1373
|
-
return this.
|
|
756
|
+
passwordBox(options) {
|
|
757
|
+
return this.addSingleWidget('password-editor', options);
|
|
1374
758
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
}
|
|
1378
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetContainerComponent, decorators: [{
|
|
1379
|
-
type: Component,
|
|
1380
|
-
args: [{
|
|
1381
|
-
selector: 'axp-widgets-container',
|
|
1382
|
-
template: `<ng-content></ng-content>`,
|
|
1383
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1384
|
-
host: { style: 'display: contents;' },
|
|
1385
|
-
providers: [AXPLayoutBuilderService, AXPLayoutBuilderContextStore],
|
|
1386
|
-
standalone: false,
|
|
1387
|
-
}]
|
|
1388
|
-
}], ctorParameters: () => [], propDecorators: { onContextChanged: [{
|
|
1389
|
-
type: Output
|
|
1390
|
-
}], context: [{
|
|
1391
|
-
type: Input
|
|
1392
|
-
}], functions: [{
|
|
1393
|
-
type: Input
|
|
1394
|
-
}] } });
|
|
1395
|
-
|
|
1396
|
-
class AXPWidgetPlaceholderComponent {
|
|
1397
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1398
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: AXPWidgetPlaceholderComponent, isStandalone: true, selector: "axp-widget-placeholder", ngImport: i0, template: `<div>
|
|
1399
|
-
<ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
|
|
1400
|
-
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1401
|
-
}
|
|
1402
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetPlaceholderComponent, decorators: [{
|
|
1403
|
-
type: Component,
|
|
1404
|
-
args: [{
|
|
1405
|
-
selector: 'axp-widget-placeholder',
|
|
1406
|
-
template: `<div>
|
|
1407
|
-
<ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
|
|
1408
|
-
</div>`,
|
|
1409
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1410
|
-
imports: [AXSkeletonModule],
|
|
1411
|
-
standalone: true,
|
|
1412
|
-
}]
|
|
1413
|
-
}] });
|
|
1414
|
-
|
|
1415
|
-
class AXPWidgetRendererDirective {
|
|
1416
|
-
//#endregion
|
|
1417
|
-
//#endregion
|
|
1418
|
-
constructor() {
|
|
1419
|
-
this.parentNode = input(...(ngDevMode ? [undefined, { debugName: "parentNode" }] : []));
|
|
1420
|
-
this.index = input(...(ngDevMode ? [undefined, { debugName: "index" }] : []));
|
|
1421
|
-
this.mode = input.required(...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
1422
|
-
this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
|
|
1423
|
-
this._options = signal({}, ...(ngDevMode ? [{ debugName: "_options" }] : []));
|
|
1424
|
-
this.options = this._options.asReadonly();
|
|
1425
|
-
this.onOptionsChanged = output();
|
|
1426
|
-
this.onValueChanged = output();
|
|
1427
|
-
//#region ---- Properties ----
|
|
1428
|
-
this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : []));
|
|
1429
|
-
this.injector = inject(Injector);
|
|
1430
|
-
this.builderService = inject(AXPLayoutBuilderService);
|
|
1431
|
-
this.contextService = inject(AXPLayoutBuilderContextStore);
|
|
1432
|
-
this.widgetRegistery = inject(AXPWidgetRegistryService);
|
|
1433
|
-
this.unsubscriber = inject(AXUnsubscriber);
|
|
1434
|
-
this.translateService = inject(AXTranslationService);
|
|
1435
|
-
this.widgetService = inject(AXPWidgetRegistryService);
|
|
1436
|
-
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
1437
|
-
this.viewContainerRef = inject(ViewContainerRef);
|
|
1438
|
-
this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
1439
|
-
this.expressionEvaluators = new Map();
|
|
1440
|
-
this.renderTimeoutId = null;
|
|
1441
|
-
this.hasInitialRender = false;
|
|
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();
|
|
1457
|
-
effect(async () => {
|
|
1458
|
-
const changed = this.contextService.changeEvent();
|
|
1459
|
-
// Don't trigger re-render during initial setup
|
|
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
|
-
}
|
|
1465
|
-
return;
|
|
1466
|
-
}
|
|
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);
|
|
1471
|
-
}
|
|
1472
|
-
});
|
|
1473
|
-
this.builderService.onRefresh.pipe(this.unsubscriber.takeUntilDestroy).subscribe(async () => {
|
|
1474
|
-
await this.processBatchedUpdates();
|
|
1475
|
-
});
|
|
759
|
+
numberBox(options) {
|
|
760
|
+
return this.addSingleWidget('number-editor', options);
|
|
1476
761
|
}
|
|
1477
|
-
|
|
1478
|
-
|
|
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;
|
|
762
|
+
selectBox(options) {
|
|
763
|
+
return this.addSingleWidget('select-editor', options);
|
|
1485
764
|
}
|
|
1486
|
-
|
|
1487
|
-
this.
|
|
1488
|
-
value,
|
|
1489
|
-
timestamp: performance.now(),
|
|
1490
|
-
});
|
|
765
|
+
lookupBox(options) {
|
|
766
|
+
return this.addSingleWidget('lookup-editor', options);
|
|
1491
767
|
}
|
|
1492
|
-
|
|
1493
|
-
this.
|
|
768
|
+
selectionList(options) {
|
|
769
|
+
return this.addSingleWidget('selection-list-editor', options);
|
|
1494
770
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
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;
|
|
771
|
+
dateTimeBox(options) {
|
|
772
|
+
return this.addSingleWidget('date-time-editor', options);
|
|
1509
773
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
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);
|
|
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 });
|
|
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)`);
|
|
774
|
+
toggleSwitch(options) {
|
|
775
|
+
return this.addSingleWidget('toggle-editor', options);
|
|
1562
776
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
if (
|
|
1573
|
-
|
|
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
|
-
}
|
|
777
|
+
colorBox(options) {
|
|
778
|
+
return this.addSingleWidget('color-editor', options);
|
|
779
|
+
}
|
|
780
|
+
customWidget(type, options) {
|
|
781
|
+
return this.addSingleWidget(type, options);
|
|
782
|
+
}
|
|
783
|
+
// Override property setters to propagate changes to child widget
|
|
784
|
+
disabled(condition) {
|
|
785
|
+
super.disabled(condition);
|
|
786
|
+
if (this.childWidget) {
|
|
787
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1660
788
|
}
|
|
1661
|
-
return
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
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
|
-
}
|
|
789
|
+
return this;
|
|
790
|
+
}
|
|
791
|
+
readonly(condition) {
|
|
792
|
+
super.readonly(condition);
|
|
793
|
+
if (this.childWidget) {
|
|
794
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1675
795
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
|
|
1683
|
-
return true;
|
|
1684
|
-
}
|
|
796
|
+
return this;
|
|
797
|
+
}
|
|
798
|
+
visible(condition) {
|
|
799
|
+
super.visible(condition);
|
|
800
|
+
if (this.childWidget) {
|
|
801
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1685
802
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
return true;
|
|
1693
|
-
}
|
|
803
|
+
return this;
|
|
804
|
+
}
|
|
805
|
+
direction(direction) {
|
|
806
|
+
super.direction(direction);
|
|
807
|
+
if (this.childWidget) {
|
|
808
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1694
809
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
}
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
812
|
+
mode(mode) {
|
|
813
|
+
super.mode(mode);
|
|
814
|
+
if (this.childWidget) {
|
|
815
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1702
816
|
}
|
|
1703
|
-
return
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
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;
|
|
817
|
+
return this;
|
|
818
|
+
}
|
|
819
|
+
// Override withInheritanceContext to pass it to the child widget if it exists
|
|
820
|
+
withInheritanceContext(context) {
|
|
821
|
+
// Call parent implementation first
|
|
822
|
+
super.withInheritanceContext(context);
|
|
823
|
+
// If we have a child widget, update its inheritance context too
|
|
824
|
+
if (this.childWidget) {
|
|
825
|
+
this.childWidget.withInheritanceContext(this.inheritanceContext);
|
|
1720
826
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
827
|
+
return this;
|
|
828
|
+
}
|
|
829
|
+
// Override build() to build the child widget at the last moment
|
|
830
|
+
build() {
|
|
831
|
+
// Build the child widget and add it to children before building the form field
|
|
832
|
+
if (this.childWidget) {
|
|
833
|
+
this.ensureChildren();
|
|
834
|
+
this.containerState.children.push(this.childWidget.build());
|
|
1724
835
|
}
|
|
836
|
+
// Call parent build
|
|
837
|
+
return super.build();
|
|
1725
838
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Fieldset Container Builder - Liskov Substitution Principle
|
|
842
|
+
* Extends LayoutContainerMixin to inherit layout functionality
|
|
843
|
+
* Specialized for form fields only
|
|
844
|
+
*/
|
|
845
|
+
class FieldsetContainerBuilder extends LayoutContainerMixin {
|
|
846
|
+
constructor() {
|
|
847
|
+
super('fieldset-layout');
|
|
848
|
+
this.containerState.options = {};
|
|
849
|
+
}
|
|
850
|
+
setOptions(options) {
|
|
851
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
852
|
+
return this;
|
|
853
|
+
}
|
|
854
|
+
// Individual fluent methods for Fieldset
|
|
855
|
+
setTitle(title) {
|
|
856
|
+
return this.setOptions({ title });
|
|
857
|
+
}
|
|
858
|
+
setDescription(description) {
|
|
859
|
+
return this.setOptions({ description });
|
|
860
|
+
}
|
|
861
|
+
setIcon(icon) {
|
|
862
|
+
return this.setOptions({ icon });
|
|
863
|
+
}
|
|
864
|
+
setCollapsible(collapsible) {
|
|
865
|
+
return this.setOptions({ collapsible });
|
|
866
|
+
}
|
|
867
|
+
setIsOpen(isOpen) {
|
|
868
|
+
return this.setOptions({ isOpen });
|
|
869
|
+
}
|
|
870
|
+
setLook(look) {
|
|
871
|
+
return this.setOptions({ look });
|
|
872
|
+
}
|
|
873
|
+
setShowHeader(showHeader) {
|
|
874
|
+
return this.setOptions({ showHeader });
|
|
875
|
+
}
|
|
876
|
+
setCols(cols) {
|
|
877
|
+
return this.setOptions({ cols });
|
|
878
|
+
}
|
|
879
|
+
// Only form fields are allowed in fieldset
|
|
880
|
+
formField(label, delegate) {
|
|
881
|
+
const field = new FormFieldBuilder(label);
|
|
882
|
+
field.withInheritanceContext(this.inheritanceContext);
|
|
883
|
+
if (delegate) {
|
|
884
|
+
delegate(field);
|
|
1736
885
|
}
|
|
1737
|
-
|
|
886
|
+
this.ensureChildren();
|
|
887
|
+
this.containerState.children.push(field.build());
|
|
888
|
+
return this;
|
|
1738
889
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
this.
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* List Widget Builder - Liskov Substitution Principle
|
|
893
|
+
* Extends WidgetContainerMixin to inherit all common functionality
|
|
894
|
+
*/
|
|
895
|
+
class ListWidgetBuilder extends WidgetContainerMixin {
|
|
896
|
+
constructor() {
|
|
897
|
+
super('list');
|
|
898
|
+
}
|
|
899
|
+
setOptions(options) {
|
|
900
|
+
this.containerState.options = { ...this.containerState.options, ...options };
|
|
901
|
+
return this;
|
|
902
|
+
}
|
|
903
|
+
// Individual fluent methods for List Widget
|
|
904
|
+
setDataSource(dataSource) {
|
|
905
|
+
return this.setOptions({ dataSource });
|
|
906
|
+
}
|
|
907
|
+
setColumns(columns) {
|
|
908
|
+
return this.setOptions({ columns });
|
|
909
|
+
}
|
|
910
|
+
// Event handlers
|
|
911
|
+
setOnRowClick(handler) {
|
|
912
|
+
return this.setOptions({ onRowClick: handler });
|
|
913
|
+
}
|
|
914
|
+
setOnRowDoubleClick(handler) {
|
|
915
|
+
return this.setOptions({ onRowDoubleClick: handler });
|
|
916
|
+
}
|
|
917
|
+
setOnSelectionChange(handler) {
|
|
918
|
+
return this.setOptions({ onSelectionChange: handler });
|
|
919
|
+
}
|
|
920
|
+
setOnRowCommand(handler) {
|
|
921
|
+
return this.setOptions({ onRowCommand: handler });
|
|
1761
922
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
923
|
+
// Table features
|
|
924
|
+
setPaging(paging) {
|
|
925
|
+
return this.setOptions({ paging });
|
|
926
|
+
}
|
|
927
|
+
setShowHeader(show) {
|
|
928
|
+
return this.setOptions({ showHeader: show });
|
|
929
|
+
}
|
|
930
|
+
setShowFooter(show) {
|
|
931
|
+
return this.setOptions({ showFooter: show });
|
|
932
|
+
}
|
|
933
|
+
setFixHeader(fix) {
|
|
934
|
+
return this.setOptions({ fixHeader: fix });
|
|
935
|
+
}
|
|
936
|
+
setFixFooter(fix) {
|
|
937
|
+
return this.setOptions({ fixFooter: fix });
|
|
938
|
+
}
|
|
939
|
+
setFetchDataMode(mode) {
|
|
940
|
+
return this.setOptions({ fetchDataMode: mode });
|
|
941
|
+
}
|
|
942
|
+
setParentField(field) {
|
|
943
|
+
return this.setOptions({ parentField: field });
|
|
944
|
+
}
|
|
945
|
+
setMinHeight(height) {
|
|
946
|
+
return this.setOptions({ minHeight: height });
|
|
947
|
+
}
|
|
948
|
+
// Selection & Index
|
|
949
|
+
setShowIndex(show) {
|
|
950
|
+
return this.setOptions({ showIndex: show });
|
|
951
|
+
}
|
|
952
|
+
setAllowSelection(allow) {
|
|
953
|
+
return this.setOptions({ allowSelection: allow });
|
|
954
|
+
}
|
|
955
|
+
// Commands
|
|
956
|
+
setPrimaryCommands(commands) {
|
|
957
|
+
return this.setOptions({ primaryCommands: commands });
|
|
958
|
+
}
|
|
959
|
+
setSecondaryCommands(commands) {
|
|
960
|
+
return this.setOptions({ secondaryCommands: commands });
|
|
961
|
+
}
|
|
962
|
+
// Loading
|
|
963
|
+
setLoading(loading) {
|
|
964
|
+
return this.setOptions({ loading });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Dialog Container Builder - Specialized for dialog functionality
|
|
969
|
+
* Uses composition instead of inheritance for cleaner separation
|
|
970
|
+
*/
|
|
971
|
+
class DialogContainerBuilder {
|
|
972
|
+
constructor(popupService) {
|
|
973
|
+
this.dialogState = {
|
|
974
|
+
type: 'flex-layout', // This will be overridden when content layout exists
|
|
975
|
+
children: [],
|
|
976
|
+
mode: 'edit',
|
|
977
|
+
dialogOptions: {
|
|
978
|
+
title: '',
|
|
979
|
+
size: 'md',
|
|
980
|
+
closeButton: false,
|
|
981
|
+
},
|
|
982
|
+
actions: {
|
|
983
|
+
footer: {
|
|
984
|
+
prefix: [],
|
|
985
|
+
suffix: [],
|
|
986
|
+
},
|
|
987
|
+
},
|
|
988
|
+
};
|
|
989
|
+
if (popupService) {
|
|
990
|
+
this.popupService = popupService;
|
|
1765
991
|
}
|
|
1766
|
-
|
|
1767
|
-
|
|
992
|
+
else {
|
|
993
|
+
this.popupService = inject(AXPopupService);
|
|
1768
994
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
995
|
+
}
|
|
996
|
+
setOptions(options) {
|
|
997
|
+
this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
|
|
998
|
+
return this;
|
|
999
|
+
}
|
|
1000
|
+
// Individual fluent methods for Dialog
|
|
1001
|
+
setTitle(title) {
|
|
1002
|
+
return this.setOptions({ title });
|
|
1003
|
+
}
|
|
1004
|
+
setMessage(message) {
|
|
1005
|
+
return this.setOptions({ message });
|
|
1006
|
+
}
|
|
1007
|
+
setSize(size) {
|
|
1008
|
+
return this.setOptions({ size });
|
|
1009
|
+
}
|
|
1010
|
+
setCloseButton(closeButton) {
|
|
1011
|
+
return this.setOptions({ closeButton });
|
|
1012
|
+
}
|
|
1013
|
+
setContext(context) {
|
|
1014
|
+
return this.setOptions({ context });
|
|
1015
|
+
}
|
|
1016
|
+
content(delegate) {
|
|
1017
|
+
if (delegate) {
|
|
1018
|
+
// Create a flex container directly instead of through LayoutBuilder
|
|
1019
|
+
const flexContainer = new FlexContainerBuilder();
|
|
1020
|
+
flexContainer.setDirection('column');
|
|
1021
|
+
flexContainer.setGap('10px');
|
|
1022
|
+
delegate(flexContainer);
|
|
1023
|
+
this.contentLayout = flexContainer.build();
|
|
1771
1024
|
}
|
|
1025
|
+
return this;
|
|
1772
1026
|
}
|
|
1773
|
-
|
|
1774
|
-
if (
|
|
1775
|
-
|
|
1027
|
+
setActions(delegate) {
|
|
1028
|
+
if (delegate) {
|
|
1029
|
+
const actionBuilder = new ActionBuilder(this);
|
|
1030
|
+
delegate(actionBuilder);
|
|
1776
1031
|
}
|
|
1777
|
-
this
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
]?.filter((c) => c.schema.defaultValue != null);
|
|
1791
|
-
// Process default values (evaluate expressions if needed)
|
|
1792
|
-
const props = {};
|
|
1793
|
-
for (const property of propertiesToProcess) {
|
|
1794
|
-
const defaultValue = property.schema.defaultValue;
|
|
1795
|
-
if (typeof defaultValue === 'string' && this.expressionEvaluator.isExpression(defaultValue)) {
|
|
1796
|
-
// Evaluate expression for default value
|
|
1797
|
-
try {
|
|
1798
|
-
const evaluatedValue = await this.evaluateExpression(defaultValue);
|
|
1799
|
-
props[property.name] = evaluatedValue;
|
|
1800
|
-
}
|
|
1801
|
-
catch (error) {
|
|
1802
|
-
console.error(`Error evaluating default value expression for property ${property.name}:`, error);
|
|
1803
|
-
props[property.name] = cloneDeep(defaultValue); // Fallback to original value
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
else {
|
|
1807
|
-
// Use static default value
|
|
1808
|
-
props[property.name] = cloneDeep(defaultValue);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
//
|
|
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));
|
|
1816
|
-
this.preprocessAndInitialOptions(cloneDeep(this.node().options));
|
|
1817
|
-
await this.updateOptionsBasedOnContext();
|
|
1818
|
-
//
|
|
1819
|
-
this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
|
|
1820
|
-
// Evaluate default value
|
|
1821
|
-
let defaultValue = this.node().defaultValue;
|
|
1822
|
-
if (defaultValue && this.expressionEvaluator.isExpression(defaultValue)) {
|
|
1823
|
-
defaultValue = await this.evaluateExpression(defaultValue);
|
|
1824
|
-
}
|
|
1825
|
-
//
|
|
1826
|
-
const tokenValue = {
|
|
1827
|
-
node: this.node(),
|
|
1828
|
-
defaultValue: defaultValue,
|
|
1829
|
-
options: this.mergedOptions(),
|
|
1830
|
-
config: widget,
|
|
1032
|
+
return this;
|
|
1033
|
+
}
|
|
1034
|
+
// Build method to create dialog node
|
|
1035
|
+
build() {
|
|
1036
|
+
// If we have content layout, use it directly to avoid extra wrapper
|
|
1037
|
+
if (this.contentLayout) {
|
|
1038
|
+
return {
|
|
1039
|
+
...this.contentLayout,
|
|
1040
|
+
// Add dialog-specific properties
|
|
1041
|
+
options: {
|
|
1042
|
+
...this.contentLayout.options,
|
|
1043
|
+
...this.dialogState.dialogOptions,
|
|
1044
|
+
},
|
|
1831
1045
|
};
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1046
|
+
}
|
|
1047
|
+
// Fallback to dialog state structure if no content
|
|
1048
|
+
const result = {
|
|
1049
|
+
...this.dialogState,
|
|
1050
|
+
children: [],
|
|
1051
|
+
};
|
|
1052
|
+
// Add dialog-specific properties
|
|
1053
|
+
if (this.dialogState.dialogOptions) {
|
|
1054
|
+
result.options = { ...result.options, ...this.dialogState.dialogOptions };
|
|
1055
|
+
}
|
|
1056
|
+
return result;
|
|
1057
|
+
}
|
|
1058
|
+
// Dialog-specific methods
|
|
1059
|
+
async show() {
|
|
1060
|
+
const dialogNode = this.build();
|
|
1061
|
+
// Import the dialog renderer component dynamically
|
|
1062
|
+
const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
|
|
1063
|
+
// Create dialog configuration
|
|
1064
|
+
const dialogConfig = {
|
|
1065
|
+
title: this.dialogState.dialogOptions?.title || '',
|
|
1066
|
+
message: this.dialogState.dialogOptions?.message,
|
|
1067
|
+
context: this.dialogState.dialogOptions?.context || {},
|
|
1068
|
+
definition: dialogNode,
|
|
1069
|
+
actions: this.dialogState.actions,
|
|
1070
|
+
};
|
|
1071
|
+
// The Promise resolves when user clicks an action button
|
|
1072
|
+
return new Promise(async (resolve) => {
|
|
1073
|
+
this.popupService.open(AXPDialogRendererComponent, {
|
|
1074
|
+
title: dialogConfig.title,
|
|
1075
|
+
size: this.dialogState.dialogOptions?.size || 'md',
|
|
1076
|
+
closeButton: this.dialogState.dialogOptions?.closeButton || false,
|
|
1077
|
+
closeOnBackdropClick: false,
|
|
1078
|
+
draggable: false,
|
|
1079
|
+
data: {
|
|
1080
|
+
config: dialogConfig,
|
|
1081
|
+
callBack: (result) => {
|
|
1082
|
+
// Resolve with the dialog reference when user clicks an action
|
|
1083
|
+
resolve(result);
|
|
1838
1084
|
},
|
|
1839
|
-
|
|
1840
|
-
});
|
|
1841
|
-
//
|
|
1842
|
-
const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
|
|
1843
|
-
//
|
|
1844
|
-
const com = await widget?.components[this.mode()]?.component();
|
|
1845
|
-
if (!com) {
|
|
1846
|
-
console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
|
|
1850
|
-
this.instance = this.componentRef.instance;
|
|
1851
|
-
this.instance.setStatus(AXPWidgetStatus.Rendering);
|
|
1852
|
-
this.instance.parent = this.parentNode();
|
|
1853
|
-
this.instance.index = this.index();
|
|
1854
|
-
this.instance.mode = this.mode();
|
|
1855
|
-
this.instance.setStatus(AXPWidgetStatus.Rendered);
|
|
1856
|
-
this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
|
|
1857
|
-
this.onOptionsChanged.emit({ sender: this, widget: c.sender });
|
|
1858
|
-
});
|
|
1859
|
-
this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
|
|
1860
|
-
this.onValueChanged.emit({ sender: this, widget: c.sender });
|
|
1085
|
+
},
|
|
1861
1086
|
});
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region ---- Widget Builder Implementation ----
|
|
1092
|
+
/**
|
|
1093
|
+
* Widget Builder - Single Responsibility Principle
|
|
1094
|
+
* Handles individual widget configuration and building
|
|
1095
|
+
*/
|
|
1096
|
+
class WidgetBuilder {
|
|
1097
|
+
constructor(name) {
|
|
1098
|
+
this.widgetState = {
|
|
1099
|
+
type: 'widget',
|
|
1100
|
+
options: {},
|
|
1101
|
+
};
|
|
1102
|
+
this.inheritanceContext = {};
|
|
1103
|
+
if (name) {
|
|
1104
|
+
this.widgetState.name = name;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
type(type) {
|
|
1108
|
+
this.widgetState.type = type;
|
|
1109
|
+
return this;
|
|
1110
|
+
}
|
|
1111
|
+
name(name) {
|
|
1112
|
+
this.widgetState.name = name;
|
|
1113
|
+
if (!this.widgetState.path) {
|
|
1114
|
+
this.widgetState.path = name;
|
|
1115
|
+
}
|
|
1116
|
+
return this;
|
|
1117
|
+
}
|
|
1118
|
+
path(path) {
|
|
1119
|
+
this.widgetState.path = path;
|
|
1120
|
+
return this;
|
|
1121
|
+
}
|
|
1122
|
+
options(options) {
|
|
1123
|
+
// Merge options instead of replacing to preserve inherited properties
|
|
1124
|
+
this.widgetState.options = { ...this.widgetState.options, ...options };
|
|
1125
|
+
return this;
|
|
1126
|
+
}
|
|
1127
|
+
layout(value) {
|
|
1128
|
+
if (typeof value === 'number') {
|
|
1129
|
+
this.widgetState.layout = {
|
|
1130
|
+
positions: {
|
|
1131
|
+
sm: { colSpan: 12 },
|
|
1132
|
+
md: { colSpan: 12 },
|
|
1133
|
+
lg: { colSpan: value },
|
|
1134
|
+
xl: { colSpan: value },
|
|
1135
|
+
xxl: { colSpan: value },
|
|
1136
|
+
},
|
|
1137
|
+
};
|
|
1881
1138
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1139
|
+
else {
|
|
1140
|
+
this.widgetState.layout = value;
|
|
1884
1141
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1142
|
+
return this;
|
|
1143
|
+
}
|
|
1144
|
+
mode(mode) {
|
|
1145
|
+
this.widgetState.mode = mode;
|
|
1146
|
+
this.inheritanceContext.mode = mode;
|
|
1147
|
+
return this;
|
|
1148
|
+
}
|
|
1149
|
+
visible(condition) {
|
|
1150
|
+
if (!this.widgetState.options) {
|
|
1151
|
+
this.widgetState.options = {};
|
|
1887
1152
|
}
|
|
1153
|
+
this.widgetState.options['visible'] = condition;
|
|
1154
|
+
this.inheritanceContext.visible = condition;
|
|
1155
|
+
return this;
|
|
1888
1156
|
}
|
|
1889
|
-
|
|
1890
|
-
if (!this.
|
|
1891
|
-
|
|
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
|
|
1157
|
+
disabled(condition) {
|
|
1158
|
+
if (!this.widgetState.options) {
|
|
1159
|
+
this.widgetState.options = {};
|
|
1899
1160
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1161
|
+
this.widgetState.options['disabled'] = condition;
|
|
1162
|
+
this.inheritanceContext.disabled = condition;
|
|
1163
|
+
return this;
|
|
1164
|
+
}
|
|
1165
|
+
readonly(condition) {
|
|
1166
|
+
if (!this.widgetState.options) {
|
|
1167
|
+
this.widgetState.options = {};
|
|
1902
1168
|
}
|
|
1169
|
+
this.widgetState.options['readonly'] = condition;
|
|
1170
|
+
this.inheritanceContext.readonly = condition;
|
|
1171
|
+
return this;
|
|
1903
1172
|
}
|
|
1904
|
-
|
|
1905
|
-
if (!this.
|
|
1906
|
-
|
|
1173
|
+
direction(direction) {
|
|
1174
|
+
if (!this.widgetState.options) {
|
|
1175
|
+
this.widgetState.options = {};
|
|
1907
1176
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1177
|
+
this.widgetState.options['direction'] = direction;
|
|
1178
|
+
this.inheritanceContext.direction = direction;
|
|
1179
|
+
return this;
|
|
1910
1180
|
}
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1181
|
+
// Inheritance context methods
|
|
1182
|
+
withInheritanceContext(context) {
|
|
1183
|
+
this.inheritanceContext = mergeInheritanceContext(context);
|
|
1184
|
+
// Apply inherited properties to widget state
|
|
1185
|
+
const resolved = resolveInheritedProperties(context, this.inheritanceContext);
|
|
1186
|
+
// Always apply inherited properties (remove the conditions that check if already set)
|
|
1187
|
+
// This allows properties to be updated when inheritance context changes
|
|
1188
|
+
if (resolved.mode) {
|
|
1189
|
+
this.widgetState.mode = resolved.mode;
|
|
1918
1190
|
}
|
|
1919
|
-
|
|
1920
|
-
|
|
1191
|
+
if (!this.widgetState.options)
|
|
1192
|
+
this.widgetState.options = {};
|
|
1193
|
+
if (resolved.disabled !== undefined) {
|
|
1194
|
+
this.widgetState.options['disabled'] = resolved.disabled;
|
|
1195
|
+
}
|
|
1196
|
+
if (resolved.readonly !== undefined) {
|
|
1197
|
+
this.widgetState.options['readonly'] = resolved.readonly;
|
|
1198
|
+
}
|
|
1199
|
+
if (resolved.direction !== undefined) {
|
|
1200
|
+
this.widgetState.options['direction'] = resolved.direction;
|
|
1201
|
+
}
|
|
1202
|
+
if (resolved.visible !== undefined) {
|
|
1203
|
+
this.widgetState.options['visible'] = resolved.visible;
|
|
1204
|
+
}
|
|
1205
|
+
return this;
|
|
1921
1206
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
return;
|
|
1925
|
-
}
|
|
1926
|
-
Object.entries(obj).forEach(([key, value]) => {
|
|
1927
|
-
const currentPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
1928
|
-
// CRITICAL FIX: Skip trigger actions during options processing
|
|
1929
|
-
//
|
|
1930
|
-
// PROBLEM: Trigger actions were being evaluated immediately during widget setup/options processing,
|
|
1931
|
-
// causing them to execute before the actual trigger event occurred. This meant triggers would fire
|
|
1932
|
-
// during initialization instead of when the specified context path actually changed.
|
|
1933
|
-
//
|
|
1934
|
-
// ROOT CAUSE: The expression evaluator was processing trigger action expressions (like console.log
|
|
1935
|
-
// or widget.setValue calls) as part of the normal options preprocessing, treating them as dynamic
|
|
1936
|
-
// expressions that needed immediate evaluation.
|
|
1937
|
-
//
|
|
1938
|
-
// SOLUTION: Detect when we're processing trigger actions and store them as static values without
|
|
1939
|
-
// expression evaluation. This ensures trigger actions are only evaluated when the trigger's
|
|
1940
|
-
// subscription callback actually fires (when the event condition is met).
|
|
1941
|
-
//
|
|
1942
|
-
// RESULT: Triggers now only execute when their event filters pass (e.g., when the specified
|
|
1943
|
-
// context path changes), not during widget initialization.
|
|
1944
|
-
if (currentPath.includes('triggers') && (key === 'action' || currentPath.endsWith('.action'))) {
|
|
1945
|
-
// Apply static values directly without expression evaluation
|
|
1946
|
-
this.mergedOptions.update((currentOptions) => {
|
|
1947
|
-
return set(currentOptions, currentPath, value);
|
|
1948
|
-
});
|
|
1949
|
-
return;
|
|
1950
|
-
}
|
|
1951
|
-
if (typeof value === 'string' && this.expressionEvaluator.isExpression(value)) {
|
|
1952
|
-
// Cache dynamic expression for later evaluation
|
|
1953
|
-
this.expressionEvaluators.set(currentPath, () => this.evaluateExpression(value));
|
|
1954
|
-
}
|
|
1955
|
-
else if (typeof value === 'object' &&
|
|
1956
|
-
value !== null &&
|
|
1957
|
-
(value.constructor === Object || Array.isArray(value))) {
|
|
1958
|
-
// Recursively handle nested objects
|
|
1959
|
-
this.preprocessAndInitialOptions(value, currentPath);
|
|
1960
|
-
}
|
|
1961
|
-
else {
|
|
1962
|
-
// Apply static values directly
|
|
1963
|
-
this.mergedOptions.update((currentOptions) => {
|
|
1964
|
-
return set(currentOptions, currentPath, value);
|
|
1965
|
-
});
|
|
1966
|
-
}
|
|
1967
|
-
});
|
|
1207
|
+
getInheritanceContext() {
|
|
1208
|
+
return { ...this.inheritanceContext };
|
|
1968
1209
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
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
|
-
}
|
|
1210
|
+
build() {
|
|
1211
|
+
return {
|
|
1212
|
+
name: this.widgetState.name,
|
|
1213
|
+
type: this.widgetState.type,
|
|
1214
|
+
options: this.widgetState.options,
|
|
1215
|
+
mode: this.widgetState.mode,
|
|
1216
|
+
path: this.widgetState.path,
|
|
1217
|
+
defaultValue: this.widgetState.defaultValue,
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
//#region ---- Action Builder Implementation ----
|
|
1222
|
+
class ActionBuilder {
|
|
1223
|
+
constructor(dialogBuilder) {
|
|
1224
|
+
this.dialogBuilder = dialogBuilder;
|
|
1225
|
+
}
|
|
1226
|
+
cancel(text) {
|
|
1227
|
+
if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
|
|
1228
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix = [];
|
|
1229
|
+
}
|
|
1230
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix.push({
|
|
1231
|
+
title: text || '@general:actions.cancel.title',
|
|
1232
|
+
icon: 'fa-times',
|
|
1233
|
+
color: 'default',
|
|
1234
|
+
command: { name: 'cancel' },
|
|
2003
1235
|
});
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
if (changedUpdates.length > 0) {
|
|
2010
|
-
this.mergedOptions.update((o) => {
|
|
2011
|
-
const updatedOptions = { ...o };
|
|
2012
|
-
changedUpdates.forEach(({ path, newValue }) => {
|
|
2013
|
-
set(updatedOptions, path, newValue);
|
|
2014
|
-
});
|
|
2015
|
-
return updatedOptions;
|
|
2016
|
-
});
|
|
1236
|
+
return this;
|
|
1237
|
+
}
|
|
1238
|
+
submit(text) {
|
|
1239
|
+
if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
|
|
1240
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix = [];
|
|
2017
1241
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
1242
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix.push({
|
|
1243
|
+
title: text || '@general:actions.submit.title',
|
|
1244
|
+
icon: 'fa-check',
|
|
1245
|
+
color: 'primary',
|
|
1246
|
+
command: { name: 'submit', options: { validate: true } },
|
|
1247
|
+
});
|
|
1248
|
+
return this;
|
|
2022
1249
|
}
|
|
2023
|
-
|
|
2024
|
-
if (this.
|
|
2025
|
-
|
|
2026
|
-
this.instance.setValue(value);
|
|
1250
|
+
custom(action) {
|
|
1251
|
+
if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
|
|
1252
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix = [];
|
|
2027
1253
|
}
|
|
1254
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
|
|
1255
|
+
return this;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
class AXPLayoutConversionService {
|
|
1260
|
+
constructor() {
|
|
1261
|
+
//#region ---- Caching ----
|
|
1262
|
+
this.widgetTreeCache = new Map();
|
|
1263
|
+
this.formDefinitionCache = new Map();
|
|
1264
|
+
}
|
|
1265
|
+
//#endregion
|
|
1266
|
+
//#region ---- Public Methods ----
|
|
1267
|
+
/**
|
|
1268
|
+
* Convert AXPDynamicFormDefinition to AXPWidgetNode tree structure
|
|
1269
|
+
* Groups become Fieldset Layouts with Form Field widgets as children
|
|
1270
|
+
* Fields become Form Field widgets with Editor widgets as children
|
|
1271
|
+
*/
|
|
1272
|
+
convertFormDefinition(formDefinition) {
|
|
1273
|
+
// Create cache key based on form definition content
|
|
1274
|
+
const cacheKey = this.createFormDefinitionCacheKey(formDefinition);
|
|
1275
|
+
// Check cache first
|
|
1276
|
+
if (this.widgetTreeCache.has(cacheKey)) {
|
|
1277
|
+
return this.widgetTreeCache.get(cacheKey);
|
|
1278
|
+
}
|
|
1279
|
+
// Generate widget tree
|
|
1280
|
+
const widgetTree = {
|
|
1281
|
+
type: 'grid-layout',
|
|
1282
|
+
name: 'dynamic-form-container',
|
|
1283
|
+
options: {
|
|
1284
|
+
title: 'Dynamic Form',
|
|
1285
|
+
grid: {
|
|
1286
|
+
default: {
|
|
1287
|
+
columns: 1,
|
|
1288
|
+
gap: '1rem',
|
|
1289
|
+
},
|
|
1290
|
+
},
|
|
1291
|
+
},
|
|
1292
|
+
children: formDefinition.groups.map((group) => this.createGroupAsFieldsetWidget(group)),
|
|
1293
|
+
};
|
|
1294
|
+
// Cache the result
|
|
1295
|
+
this.widgetTreeCache.set(cacheKey, widgetTree);
|
|
1296
|
+
return widgetTree;
|
|
2028
1297
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
1298
|
+
/**
|
|
1299
|
+
* Convert AXPWidgetNode tree back to AXPDynamicFormDefinition
|
|
1300
|
+
* Parses Fieldset Layouts back to Groups
|
|
1301
|
+
* Parses Form Field widgets back to Fields
|
|
1302
|
+
*/
|
|
1303
|
+
convertWidgetTreeToFormDefinition(widgetTree) {
|
|
1304
|
+
// Create cache key based on widget tree content
|
|
1305
|
+
const cacheKey = this.createWidgetTreeCacheKey(widgetTree);
|
|
1306
|
+
// Check cache first
|
|
1307
|
+
if (this.formDefinitionCache.has(cacheKey)) {
|
|
1308
|
+
return this.formDefinitionCache.get(cacheKey);
|
|
1309
|
+
}
|
|
1310
|
+
// Parse widget tree
|
|
1311
|
+
const groups = [];
|
|
1312
|
+
if (widgetTree.children) {
|
|
1313
|
+
widgetTree.children.forEach((child) => {
|
|
1314
|
+
if (child.type === 'fieldset-layout') {
|
|
1315
|
+
const group = this.extractGroupFromFieldset(child);
|
|
1316
|
+
groups.push(group);
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
2033
1319
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
1320
|
+
const formDefinition = { groups };
|
|
1321
|
+
// Cache the result
|
|
1322
|
+
this.formDefinitionCache.set(cacheKey, formDefinition);
|
|
1323
|
+
return formDefinition;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Validate that a widget tree represents a valid dynamic form structure
|
|
1327
|
+
*/
|
|
1328
|
+
validateFormWidgetTree(widgetTree) {
|
|
1329
|
+
if (!widgetTree || widgetTree.type !== 'grid-layout') {
|
|
2036
1330
|
return false;
|
|
2037
1331
|
}
|
|
1332
|
+
if (!widgetTree.children || widgetTree.children.length === 0) {
|
|
1333
|
+
return true; // Empty form is valid
|
|
1334
|
+
}
|
|
1335
|
+
// Check that all children are fieldset-layout widgets
|
|
1336
|
+
return widgetTree.children.every((child) => child.type === 'fieldset-layout' &&
|
|
1337
|
+
child.children &&
|
|
1338
|
+
child.children.every((formField) => formField.type === 'form-field' && formField.children && formField.children.length > 0));
|
|
2038
1339
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
vars: this.getVariablesScope(),
|
|
2046
|
-
};
|
|
1340
|
+
/**
|
|
1341
|
+
* Clear all caches
|
|
1342
|
+
*/
|
|
1343
|
+
clearCaches() {
|
|
1344
|
+
this.widgetTreeCache.clear();
|
|
1345
|
+
this.formDefinitionCache.clear();
|
|
2047
1346
|
}
|
|
2048
|
-
|
|
1347
|
+
/**
|
|
1348
|
+
* Get cache statistics
|
|
1349
|
+
*/
|
|
1350
|
+
getCacheStats() {
|
|
2049
1351
|
return {
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
const fullPath = path.startsWith('>') ? `${this.instance?.parentPath()}.${path.substring(1)}` : path;
|
|
2053
|
-
const value = this.contextService.getValue(fullPath);
|
|
2054
|
-
return value;
|
|
2055
|
-
},
|
|
2056
|
-
set: (path, value) => {
|
|
2057
|
-
this.contextService.update(path, value);
|
|
2058
|
-
},
|
|
2059
|
-
data: () => {
|
|
2060
|
-
return this.contextService.data();
|
|
2061
|
-
},
|
|
2062
|
-
isDirty: () => {
|
|
2063
|
-
return this.contextService.isDirty();
|
|
2064
|
-
},
|
|
1352
|
+
widgetTreeCacheSize: this.widgetTreeCache.size,
|
|
1353
|
+
formDefinitionCacheSize: this.formDefinitionCache.size,
|
|
2065
1354
|
};
|
|
2066
1355
|
}
|
|
2067
|
-
|
|
1356
|
+
//#endregion
|
|
1357
|
+
//#region ---- Private Methods ----
|
|
1358
|
+
/**
|
|
1359
|
+
* Convert a single group to Fieldset widget structure
|
|
1360
|
+
*/
|
|
1361
|
+
createGroupAsFieldsetWidget(group) {
|
|
1362
|
+
// Determine columns count from layout or default to 1
|
|
1363
|
+
const columnsCount = 1;
|
|
2068
1364
|
return {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
// Ensure c.path exists
|
|
2076
|
-
if (!c.path) {
|
|
2077
|
-
return false;
|
|
2078
|
-
}
|
|
2079
|
-
// Pattern: "prefix*" - matches paths that start with prefix
|
|
2080
|
-
if (path.endsWith('*')) {
|
|
2081
|
-
const prefix = path.substring(0, path.length - 1);
|
|
2082
|
-
return c.path.startsWith(prefix);
|
|
2083
|
-
}
|
|
2084
|
-
// Pattern: "*suffix" - matches paths that end with suffix
|
|
2085
|
-
else if (path.startsWith('*')) {
|
|
2086
|
-
const suffix = path.substring(1);
|
|
2087
|
-
return c.path.endsWith(suffix);
|
|
2088
|
-
}
|
|
2089
|
-
// Exact match
|
|
2090
|
-
else {
|
|
2091
|
-
return c.path === path;
|
|
2092
|
-
}
|
|
2093
|
-
}));
|
|
1365
|
+
type: 'fieldset-layout',
|
|
1366
|
+
name: group.name,
|
|
1367
|
+
options: {
|
|
1368
|
+
title: group.title,
|
|
1369
|
+
description: group.description,
|
|
1370
|
+
cols: columnsCount,
|
|
2094
1371
|
},
|
|
2095
|
-
|
|
1372
|
+
children: this.createFieldWidgets(group.parameters, columnsCount),
|
|
2096
1373
|
};
|
|
2097
1374
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
setValue: (value) => {
|
|
2104
|
-
this.instance.setValue(value);
|
|
2105
|
-
},
|
|
2106
|
-
clear: () => {
|
|
2107
|
-
this.instance.setValue(undefined);
|
|
2108
|
-
},
|
|
2109
|
-
refresh: () => {
|
|
2110
|
-
const refresh = this.instance?.['refresh'];
|
|
2111
|
-
if (refresh && typeof refresh === 'function') {
|
|
2112
|
-
refresh.bind(this.instance)();
|
|
2113
|
-
}
|
|
2114
|
-
},
|
|
2115
|
-
output: (name) => {
|
|
2116
|
-
this.instance.output(name);
|
|
2117
|
-
},
|
|
2118
|
-
find: (id) => {
|
|
2119
|
-
return this.builderService.getWidget(id);
|
|
2120
|
-
},
|
|
2121
|
-
};
|
|
1375
|
+
/**
|
|
1376
|
+
* Convert fields to Form Field widgets
|
|
1377
|
+
*/
|
|
1378
|
+
createFieldWidgets(fields, columnsCount) {
|
|
1379
|
+
return fields.map((field) => this.createFormFieldWidget(field));
|
|
2122
1380
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
1381
|
+
/**
|
|
1382
|
+
* Convert a single field to Form Field widget with editor as child
|
|
1383
|
+
*/
|
|
1384
|
+
createFormFieldWidget(field) {
|
|
1385
|
+
return {
|
|
1386
|
+
type: 'form-field',
|
|
1387
|
+
name: field.path,
|
|
1388
|
+
options: {
|
|
1389
|
+
label: field.title,
|
|
1390
|
+
description: field.description,
|
|
1391
|
+
showLabel: true,
|
|
2133
1392
|
},
|
|
1393
|
+
children: [field.widget], // The editor widget becomes a child of form-field
|
|
2134
1394
|
};
|
|
2135
|
-
// Add custom functions from builder service
|
|
2136
|
-
Object.entries(this.builderService.functions).forEach(([key, fn]) => {
|
|
2137
|
-
scope[key] = (...args) => {
|
|
2138
|
-
return fn(...args);
|
|
2139
|
-
};
|
|
2140
|
-
});
|
|
2141
|
-
return scope;
|
|
2142
1395
|
}
|
|
2143
|
-
|
|
1396
|
+
/**
|
|
1397
|
+
* Extract group information from Fieldset Layout widget
|
|
1398
|
+
*/
|
|
1399
|
+
extractGroupFromFieldset(fieldsetNode) {
|
|
1400
|
+
const columnsCount = fieldsetNode.options?.['cols'] || 1;
|
|
1401
|
+
// Extract fields directly from fieldset children
|
|
1402
|
+
const fields = [];
|
|
1403
|
+
if (fieldsetNode.children) {
|
|
1404
|
+
fieldsetNode.children.forEach((formField) => {
|
|
1405
|
+
if (formField.type === 'form-field' && formField.children && formField.children.length > 0) {
|
|
1406
|
+
const field = this.extractFieldFromFormWidget(formField);
|
|
1407
|
+
if (field) {
|
|
1408
|
+
fields.push(field);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
2144
1413
|
return {
|
|
2145
|
-
|
|
1414
|
+
name: fieldsetNode.name || `group-${Date.now()}`,
|
|
1415
|
+
title: fieldsetNode.options?.['title'],
|
|
1416
|
+
description: fieldsetNode.options?.['description'],
|
|
1417
|
+
parameters: fields,
|
|
2146
1418
|
};
|
|
2147
1419
|
}
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
if (!
|
|
2153
|
-
return;
|
|
2154
|
-
}
|
|
2155
|
-
for (const trigger of triggers) {
|
|
2156
|
-
try {
|
|
2157
|
-
const event = await this.evaluateTrigger(trigger.event);
|
|
2158
|
-
if (event) {
|
|
2159
|
-
event.pipe(this.unsubscriber.takeUntilDestroy).subscribe(() => {
|
|
2160
|
-
const exec = async (action) => {
|
|
2161
|
-
if (!isEmpty(action) && isString(action)) {
|
|
2162
|
-
await this.evaluateAction(action);
|
|
2163
|
-
}
|
|
2164
|
-
};
|
|
2165
|
-
const actions = Array.isArray(trigger.action) ? trigger.action : [trigger.action];
|
|
2166
|
-
actions.forEach(async (a) => await exec(a));
|
|
2167
|
-
});
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
catch (error) {
|
|
2171
|
-
console.error('Error assigning trigger:', error);
|
|
2172
|
-
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Extract field information from Form Field widget
|
|
1422
|
+
*/
|
|
1423
|
+
extractFieldFromFormWidget(formFieldNode) {
|
|
1424
|
+
if (!formFieldNode.children || formFieldNode.children.length === 0) {
|
|
1425
|
+
return null;
|
|
2173
1426
|
}
|
|
1427
|
+
const editorWidget = formFieldNode.children[0];
|
|
1428
|
+
return {
|
|
1429
|
+
path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
|
|
1430
|
+
title: formFieldNode.options?.['label'],
|
|
1431
|
+
description: formFieldNode.options?.['description'],
|
|
1432
|
+
widget: editorWidget,
|
|
1433
|
+
mode: formFieldNode.mode,
|
|
1434
|
+
};
|
|
2174
1435
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
1436
|
+
/**
|
|
1437
|
+
* Create cache key for form definition
|
|
1438
|
+
*/
|
|
1439
|
+
createFormDefinitionCacheKey(formDefinition) {
|
|
1440
|
+
// Create a hash-like key instead of full JSON string
|
|
1441
|
+
const keyParts = [];
|
|
1442
|
+
keyParts.push(`groups:${formDefinition.groups.length}`);
|
|
1443
|
+
formDefinition.groups.forEach((group, groupIndex) => {
|
|
1444
|
+
keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
|
|
1445
|
+
group.parameters.forEach((param, paramIndex) => {
|
|
1446
|
+
keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
|
|
1447
|
+
});
|
|
1448
|
+
});
|
|
1449
|
+
if (formDefinition.mode) {
|
|
1450
|
+
keyParts.push(`mode:${formDefinition.mode}`);
|
|
2186
1451
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
1452
|
+
// Join with delimiter and create a shorter hash
|
|
1453
|
+
const keyString = keyParts.join('|');
|
|
1454
|
+
// If still too long, create a simple hash
|
|
1455
|
+
if (keyString.length > 100) {
|
|
1456
|
+
return this.createSimpleHash(keyString);
|
|
2190
1457
|
}
|
|
1458
|
+
return keyString;
|
|
2191
1459
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
1460
|
+
/**
|
|
1461
|
+
* Create cache key for widget tree
|
|
1462
|
+
*/
|
|
1463
|
+
createWidgetTreeCacheKey(widgetTree) {
|
|
1464
|
+
// Create a hash-like key instead of full JSON string
|
|
1465
|
+
const keyParts = [];
|
|
1466
|
+
keyParts.push(`type:${widgetTree.type}`);
|
|
1467
|
+
if (widgetTree.name) {
|
|
1468
|
+
keyParts.push(`name:${widgetTree.name}`);
|
|
1469
|
+
}
|
|
1470
|
+
if (widgetTree.children) {
|
|
1471
|
+
keyParts.push(`children:${widgetTree.children.length}`);
|
|
1472
|
+
widgetTree.children.forEach((child, index) => {
|
|
1473
|
+
keyParts.push(`c${index}:${child.type}`);
|
|
1474
|
+
if (child.children) {
|
|
1475
|
+
keyParts.push(`cc${index}:${child.children.length}`);
|
|
1476
|
+
child.children.forEach((grandChild, gIndex) => {
|
|
1477
|
+
keyParts.push(`gc${index}.${gIndex}:${grandChild.type}`);
|
|
1478
|
+
if (grandChild.children) {
|
|
1479
|
+
keyParts.push(`gcc${index}.${gIndex}:${grandChild.children.length}`);
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
2196
1484
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
1485
|
+
// Join with delimiter and create a shorter hash
|
|
1486
|
+
const keyString = keyParts.join('|');
|
|
1487
|
+
// If still too long, create a simple hash
|
|
1488
|
+
if (keyString.length > 100) {
|
|
1489
|
+
return this.createSimpleHash(keyString);
|
|
2199
1490
|
}
|
|
1491
|
+
return keyString;
|
|
2200
1492
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
1493
|
+
/**
|
|
1494
|
+
* Create a simple hash from a string
|
|
1495
|
+
*/
|
|
1496
|
+
createSimpleHash(str) {
|
|
1497
|
+
let hash = 0;
|
|
1498
|
+
if (str.length === 0)
|
|
1499
|
+
return hash.toString();
|
|
1500
|
+
for (let i = 0; i < str.length; i++) {
|
|
1501
|
+
const char = str.charCodeAt(i);
|
|
1502
|
+
hash = (hash << 5) - hash + char;
|
|
1503
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
1504
|
+
}
|
|
1505
|
+
return Math.abs(hash).toString(36); // Convert to base36 for shorter string
|
|
1506
|
+
}
|
|
1507
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1508
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
|
|
2207
1509
|
}
|
|
2208
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
2209
|
-
type:
|
|
1510
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
|
|
1511
|
+
type: Injectable,
|
|
2210
1512
|
args: [{
|
|
2211
|
-
|
|
2212
|
-
exportAs: 'widgetRenderer',
|
|
2213
|
-
providers: [
|
|
2214
|
-
{
|
|
2215
|
-
provide: AXUnsubscriber,
|
|
2216
|
-
},
|
|
2217
|
-
],
|
|
2218
|
-
standalone: false,
|
|
1513
|
+
providedIn: 'root',
|
|
2219
1514
|
}]
|
|
2220
|
-
}]
|
|
1515
|
+
}] });
|
|
2221
1516
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
1517
|
+
class AXPLayoutRendererComponent {
|
|
1518
|
+
constructor() {
|
|
1519
|
+
this.evaluatorService = inject(AXPExpressionEvaluatorService);
|
|
1520
|
+
this.conversionService = inject(AXPLayoutConversionService);
|
|
1521
|
+
/**
|
|
1522
|
+
* Tracks the latest scheduled evaluation to ensure last-write-wins for async evaluate
|
|
1523
|
+
*/
|
|
1524
|
+
this.evaluationRunId = 0;
|
|
1525
|
+
/**
|
|
1526
|
+
* RxJS subjects for context management
|
|
1527
|
+
*/
|
|
1528
|
+
this.contextUpdateSubject = new Subject();
|
|
1529
|
+
this.contextChangeSubject = new Subject();
|
|
1530
|
+
/**
|
|
1531
|
+
* Cache for expression evaluation results
|
|
1532
|
+
*/
|
|
1533
|
+
this.expressionCache = new Map();
|
|
1534
|
+
/**
|
|
1535
|
+
* Cache for widget tree comparisons
|
|
1536
|
+
*/
|
|
1537
|
+
this.widgetTreeCache = new Map();
|
|
1538
|
+
/**
|
|
1539
|
+
* Last layout hash for change detection
|
|
1540
|
+
*/
|
|
1541
|
+
this.lastLayoutHash = '';
|
|
1542
|
+
//#region ---- Inputs ----
|
|
1543
|
+
/**
|
|
1544
|
+
* Form definition containing groups and fields OR widget tree
|
|
1545
|
+
*/
|
|
1546
|
+
this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
|
|
1547
|
+
/**
|
|
1548
|
+
* Form context/model data
|
|
1549
|
+
*/
|
|
1550
|
+
this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
|
|
1551
|
+
/**
|
|
1552
|
+
* Form appearance and density styling (normal, compact, spacious)
|
|
1553
|
+
*/
|
|
1554
|
+
this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
1555
|
+
/**
|
|
1556
|
+
* Default form mode. Can be overridden by section/group and field.
|
|
1557
|
+
*/
|
|
1558
|
+
this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
1559
|
+
//#endregion
|
|
1560
|
+
//#region ---- Widget Tree Conversion ----
|
|
1561
|
+
this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
|
|
1562
|
+
/**
|
|
1563
|
+
* Convert and evaluate data when inputs change (optimized with RxJS)
|
|
1564
|
+
*/
|
|
1565
|
+
this.conversionEffect = effect(() => {
|
|
1566
|
+
const inputData = this.layout();
|
|
1567
|
+
const ctx = this.internalContext();
|
|
1568
|
+
const look = this.look();
|
|
1569
|
+
const runId = ++this.evaluationRunId;
|
|
1570
|
+
// Generate layout hash for change detection
|
|
1571
|
+
const layoutHash = this.generateLayoutHash(inputData, look);
|
|
1572
|
+
// Skip if layout hasn't changed
|
|
1573
|
+
if (layoutHash === this.lastLayoutHash && this.widgetTree()) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
this.lastLayoutHash = layoutHash;
|
|
1577
|
+
(async () => {
|
|
1578
|
+
// First evaluate expressions if needed
|
|
1579
|
+
const evaluated = await this.expressionEvaluator(inputData, ctx);
|
|
1580
|
+
// Ignore stale results
|
|
1581
|
+
if (runId !== this.evaluationRunId) {
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
// Convert to widget tree (this will also apply the layout look)
|
|
1585
|
+
const tree = evaluated;
|
|
1586
|
+
// Update widget tree
|
|
1587
|
+
const prev = this.widgetTree();
|
|
1588
|
+
if (!isEqual(prev, tree)) {
|
|
1589
|
+
tree.mode = this.mode();
|
|
1590
|
+
this.widgetTree.set(tree);
|
|
1591
|
+
}
|
|
1592
|
+
})();
|
|
1593
|
+
}, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region ---- Outputs ----
|
|
1596
|
+
/**
|
|
1597
|
+
* Emitted when context change is initiated
|
|
1598
|
+
*/
|
|
1599
|
+
this.contextInitiated = output();
|
|
1600
|
+
/**
|
|
1601
|
+
* Emitted when form becomes valid/invalid
|
|
1602
|
+
*/
|
|
1603
|
+
this.validityChange = output();
|
|
1604
|
+
//#endregion
|
|
1605
|
+
//#region ---- Properties ----
|
|
1606
|
+
this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
|
|
1607
|
+
this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
|
|
1608
|
+
/**
|
|
1609
|
+
* Internal context signal for reactivity
|
|
1610
|
+
*/
|
|
1611
|
+
this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
|
|
1612
|
+
/**
|
|
1613
|
+
* Initial context for reset functionality
|
|
1614
|
+
*/
|
|
1615
|
+
this.initialContext = {};
|
|
1616
|
+
//#endregion
|
|
1617
|
+
//#region ---- Effects ----
|
|
1618
|
+
/**
|
|
1619
|
+
* Effect to sync context changes from external to internal (optimized with RxJS)
|
|
1620
|
+
*/
|
|
1621
|
+
this.#contextSyncEffect = effect(() => {
|
|
1622
|
+
const ctx = this.context() ?? {};
|
|
1623
|
+
this.contextUpdateSubject.next(ctx);
|
|
1624
|
+
}, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
|
|
1625
|
+
/**
|
|
1626
|
+
* Effect to handle widget tree status changes
|
|
1627
|
+
*/
|
|
1628
|
+
this.#widgetStatusEffect = effect(() => {
|
|
1629
|
+
const widgetTree = this.widgetTree();
|
|
1630
|
+
if (widgetTree) {
|
|
1631
|
+
this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
|
|
1632
|
+
}
|
|
1633
|
+
}, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
|
|
1634
|
+
}
|
|
1635
|
+
async expressionEvaluator(expression, context) {
|
|
1636
|
+
// Check if it's a form definition that needs conversion
|
|
1637
|
+
if (this.isFormDefinition(expression)) {
|
|
1638
|
+
return this.conversionService.convertFormDefinition(expression);
|
|
1639
|
+
}
|
|
1640
|
+
// Generate cache key using a more efficient method
|
|
1641
|
+
const cacheKey = this.generateCacheKey(expression, context);
|
|
1642
|
+
// Check cache first
|
|
1643
|
+
if (this.expressionCache.has(cacheKey)) {
|
|
1644
|
+
return this.expressionCache.get(cacheKey);
|
|
1645
|
+
}
|
|
1646
|
+
const scope = {
|
|
1647
|
+
context: {
|
|
1648
|
+
eval: (path) => get(context, path),
|
|
1649
|
+
},
|
|
2238
1650
|
};
|
|
1651
|
+
const result = await this.evaluatorService.evaluate(expression, scope);
|
|
1652
|
+
// Cache result with LRU-like behavior
|
|
1653
|
+
if (this.expressionCache.size > 50) {
|
|
1654
|
+
// Clear half the cache when it gets too large
|
|
1655
|
+
const keysToDelete = Array.from(this.expressionCache.keys()).slice(0, 25);
|
|
1656
|
+
keysToDelete.forEach((key) => this.expressionCache.delete(key));
|
|
1657
|
+
}
|
|
1658
|
+
this.expressionCache.set(cacheKey, result);
|
|
1659
|
+
return result;
|
|
2239
1660
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
deps: [AXPWidgetRegistryService],
|
|
2251
|
-
multi: true,
|
|
2252
|
-
},
|
|
2253
|
-
],
|
|
2254
|
-
};
|
|
1661
|
+
//#endregion
|
|
1662
|
+
//#region ---- Lifecycle Methods ----
|
|
1663
|
+
ngOnInit() {
|
|
1664
|
+
// Initialize internal context with input context
|
|
1665
|
+
const ctx = this.context() ?? {};
|
|
1666
|
+
this.internalContext.set(ctx);
|
|
1667
|
+
// Store initial context for reset functionality
|
|
1668
|
+
this.initialContext = cloneDeep(ctx);
|
|
1669
|
+
// Setup RxJS streams for context management
|
|
1670
|
+
this.setupContextStreams();
|
|
2255
1671
|
}
|
|
1672
|
+
//#endregion
|
|
1673
|
+
//#region ---- Effects ----
|
|
2256
1674
|
/**
|
|
2257
|
-
*
|
|
1675
|
+
* Effect to sync context changes from external to internal (optimized with RxJS)
|
|
2258
1676
|
*/
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
exports: [...COMPONENTS],
|
|
2273
|
-
declarations: [...COMPONENTS],
|
|
2274
|
-
}]
|
|
2275
|
-
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2276
|
-
type: Optional
|
|
2277
|
-
}, {
|
|
2278
|
-
type: Inject,
|
|
2279
|
-
args: ['AXPLayoutBuilderModuleFactory']
|
|
2280
|
-
}] }] });
|
|
2281
|
-
|
|
2282
|
-
class AXPPropertyEditorHelper {
|
|
2283
|
-
static expandShorthand(values) {
|
|
2284
|
-
switch (values.length) {
|
|
2285
|
-
case 1:
|
|
2286
|
-
return [values[0], values[0], values[0], values[0]];
|
|
2287
|
-
case 2:
|
|
2288
|
-
return [values[0], values[1], values[0], values[1]];
|
|
2289
|
-
case 3:
|
|
2290
|
-
return [values[0], values[1], values[2], values[1]];
|
|
2291
|
-
case 4:
|
|
2292
|
-
return values;
|
|
2293
|
-
default:
|
|
2294
|
-
throw new Error(`Invalid shorthand value count. Input: ${values}`);
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
static condenseShorthand(values) {
|
|
2298
|
-
if (values.length !== 4) {
|
|
2299
|
-
throw new Error('Expected 4 values for condensation.');
|
|
2300
|
-
}
|
|
2301
|
-
if (values[0] === values[1] && values[1] === values[2] && values[2] === values[3]) {
|
|
2302
|
-
return `${values[0]}`;
|
|
2303
|
-
}
|
|
2304
|
-
else if (values[0] === values[2] && values[1] === values[3]) {
|
|
2305
|
-
return `${values[0]} ${values[1]}`;
|
|
2306
|
-
}
|
|
2307
|
-
else if (values[1] === values[3]) {
|
|
2308
|
-
return `${values[0]} ${values[1]} ${values[2]}`;
|
|
1677
|
+
#contextSyncEffect;
|
|
1678
|
+
/**
|
|
1679
|
+
* Effect to handle widget tree status changes
|
|
1680
|
+
*/
|
|
1681
|
+
#widgetStatusEffect;
|
|
1682
|
+
//#endregion
|
|
1683
|
+
//#region ---- Event Handlers ----
|
|
1684
|
+
/**
|
|
1685
|
+
* Handle context change events from widget container (optimized with RxJS)
|
|
1686
|
+
*/
|
|
1687
|
+
handleContextChanged(event) {
|
|
1688
|
+
if (event.state === 'initiated') {
|
|
1689
|
+
this.contextInitiated.emit(event.data);
|
|
2309
1690
|
}
|
|
2310
1691
|
else {
|
|
2311
|
-
|
|
1692
|
+
this.contextChangeSubject.next(event.data ?? {});
|
|
2312
1693
|
}
|
|
2313
1694
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
1695
|
+
//#endregion
|
|
1696
|
+
//#region ---- Public Methods ----
|
|
1697
|
+
/**
|
|
1698
|
+
* Get the form component instance
|
|
1699
|
+
*/
|
|
1700
|
+
getForm() {
|
|
1701
|
+
return this.form();
|
|
2317
1702
|
}
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
bottom: AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
|
|
2324
|
-
left: AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
|
|
2325
|
-
};
|
|
1703
|
+
/**
|
|
1704
|
+
* Get the widget container component instance
|
|
1705
|
+
*/
|
|
1706
|
+
getContainer() {
|
|
1707
|
+
return this.container();
|
|
2326
1708
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
'bottom-left': AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
|
|
2333
|
-
'bottom-right': AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
|
|
2334
|
-
};
|
|
1709
|
+
/**
|
|
1710
|
+
* Get current form context
|
|
1711
|
+
*/
|
|
1712
|
+
getContext() {
|
|
1713
|
+
return this.internalContext();
|
|
2335
1714
|
}
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
1715
|
+
/**
|
|
1716
|
+
* Update form context programmatically
|
|
1717
|
+
*/
|
|
1718
|
+
updateContext(context) {
|
|
1719
|
+
this.internalContext.set(context);
|
|
2341
1720
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
style: this.parseSides(input.style),
|
|
2348
|
-
};
|
|
1721
|
+
/**
|
|
1722
|
+
* Get the current widget tree
|
|
1723
|
+
*/
|
|
1724
|
+
getWidgetTree() {
|
|
1725
|
+
return this.widgetTree();
|
|
2349
1726
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
1727
|
+
/**
|
|
1728
|
+
* Validate the form
|
|
1729
|
+
*/
|
|
1730
|
+
async validate() {
|
|
1731
|
+
const form = this.form();
|
|
1732
|
+
if (form) {
|
|
1733
|
+
const isValid = await form.validate();
|
|
1734
|
+
this.validityChange.emit(isValid.result);
|
|
1735
|
+
return isValid;
|
|
1736
|
+
}
|
|
2352
1737
|
return {
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
format(input.margin.bottom, units.margin.bottom),
|
|
2357
|
-
format(input.margin.left, units.margin.left),
|
|
2358
|
-
]),
|
|
2359
|
-
padding: AXPPropertyEditorHelper.condenseShorthand([
|
|
2360
|
-
format(input.padding.top, units.padding.top),
|
|
2361
|
-
format(input.padding.right, units.padding.right),
|
|
2362
|
-
format(input.padding.bottom, units.padding.bottom),
|
|
2363
|
-
format(input.padding.left, units.padding.left),
|
|
2364
|
-
]),
|
|
1738
|
+
result: false,
|
|
1739
|
+
messages: [],
|
|
1740
|
+
rules: [],
|
|
2365
1741
|
};
|
|
2366
1742
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
]),
|
|
2376
|
-
radius: AXPPropertyEditorHelper.condenseShorthand([
|
|
2377
|
-
format(input.radius['top-left'], units.radius['top-left']),
|
|
2378
|
-
format(input.radius['top-right'], units.radius['top-right']),
|
|
2379
|
-
format(input.radius['bottom-right'], units.radius['bottom-right']),
|
|
2380
|
-
format(input.radius['bottom-left'], units.radius['bottom-left']),
|
|
2381
|
-
]),
|
|
2382
|
-
color: AXPPropertyEditorHelper.condenseShorthand([
|
|
2383
|
-
`${input.color.top}${units.color.top}`,
|
|
2384
|
-
`${input.color.right}${units.color.right}`,
|
|
2385
|
-
`${input.color.bottom}${units.color.bottom}`,
|
|
2386
|
-
`${input.color.left}${units.color.left}`,
|
|
2387
|
-
]),
|
|
2388
|
-
style: AXPPropertyEditorHelper.condenseShorthand([
|
|
2389
|
-
`${input.style.top}${units.style.top}`,
|
|
2390
|
-
`${input.style.right}${units.style.right}`,
|
|
2391
|
-
`${input.style.bottom}${units.style.bottom}`,
|
|
2392
|
-
`${input.style.left}${units.style.left}`,
|
|
2393
|
-
]),
|
|
2394
|
-
};
|
|
1743
|
+
/**
|
|
1744
|
+
* Clear the form context
|
|
1745
|
+
*/
|
|
1746
|
+
clear() {
|
|
1747
|
+
// Clear internal context
|
|
1748
|
+
this.internalContext.set({});
|
|
1749
|
+
// Update the model signal
|
|
1750
|
+
this.context.set({});
|
|
2395
1751
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
}
|
|
2406
|
-
static getValueFromUnit(value, unit) {
|
|
2407
|
-
return unit ? `${value}${unit}` : `${value}`;
|
|
2408
|
-
}
|
|
2409
|
-
static parseGap(gap) {
|
|
2410
|
-
const parts = gap.split(/\s+/);
|
|
2411
|
-
const match = parts[0].match(/^(\d+\.?\d*)([a-z%]+)$/);
|
|
2412
|
-
if (!match) {
|
|
2413
|
-
throw new Error('Invalid gap format');
|
|
2414
|
-
}
|
|
2415
|
-
const [, xValue, unit] = match;
|
|
2416
|
-
let yValue = parseFloat(xValue);
|
|
2417
|
-
if (parts.length === 2) {
|
|
2418
|
-
const secondMatch = parts[1].match(/^(\d+\.?\d*)[a-z%]+$/);
|
|
2419
|
-
if (!secondMatch) {
|
|
2420
|
-
throw new Error('Invalid gap format');
|
|
2421
|
-
}
|
|
2422
|
-
yValue = parseFloat(secondMatch[1]);
|
|
2423
|
-
}
|
|
2424
|
-
return {
|
|
2425
|
-
values: {
|
|
2426
|
-
x: parseFloat(xValue),
|
|
2427
|
-
y: yValue,
|
|
2428
|
-
},
|
|
2429
|
-
unit,
|
|
2430
|
-
};
|
|
1752
|
+
/**
|
|
1753
|
+
* Reset the form to its initial state
|
|
1754
|
+
*/
|
|
1755
|
+
reset() {
|
|
1756
|
+
// Reset to initial context
|
|
1757
|
+
const resetContext = cloneDeep(this.initialContext);
|
|
1758
|
+
this.internalContext.set(resetContext);
|
|
1759
|
+
// Update the model signal
|
|
1760
|
+
this.context.set(resetContext);
|
|
2431
1761
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
1762
|
+
//#endregion
|
|
1763
|
+
//#region ---- RxJS Stream Setup ----
|
|
1764
|
+
/**
|
|
1765
|
+
* Setup RxJS streams for context management
|
|
1766
|
+
*/
|
|
1767
|
+
setupContextStreams() {
|
|
1768
|
+
// Debounced context updates from external source
|
|
1769
|
+
this.contextUpdateSubject
|
|
1770
|
+
.pipe(debounceTime(16), // ~60fps
|
|
1771
|
+
distinctUntilChanged((prev, curr) => isEqual(prev, curr)), startWith(this.context() ?? {}))
|
|
1772
|
+
.subscribe((ctx) => {
|
|
1773
|
+
this.internalContext.set(ctx);
|
|
1774
|
+
});
|
|
1775
|
+
// Debounced context changes from widgets
|
|
1776
|
+
this.contextChangeSubject
|
|
1777
|
+
.pipe(debounceTime(16), // ~60fps
|
|
1778
|
+
distinctUntilChanged((prev, curr) => isEqual(prev, curr)))
|
|
1779
|
+
.subscribe((ctx) => {
|
|
1780
|
+
this.internalContext.set(ctx);
|
|
1781
|
+
// Update the model signal directly - it will emit change events automatically
|
|
1782
|
+
this.context.set(this.internalContext());
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
//#endregion
|
|
1786
|
+
//#region ---- Type Guards ----
|
|
1787
|
+
/**
|
|
1788
|
+
* Type guard to check if the input is a form definition
|
|
1789
|
+
*/
|
|
1790
|
+
isFormDefinition(data) {
|
|
1791
|
+
return data && typeof data === 'object' && 'groups' in data && Array.isArray(data.groups);
|
|
1792
|
+
}
|
|
1793
|
+
//#endregion
|
|
1794
|
+
//#region ---- Utility Methods ----
|
|
1795
|
+
/**
|
|
1796
|
+
* Generate layout hash for change detection (short hash)
|
|
1797
|
+
*/
|
|
1798
|
+
generateLayoutHash(layout, look) {
|
|
1799
|
+
if (!layout)
|
|
1800
|
+
return '';
|
|
1801
|
+
// Generate short hash for large layout strings
|
|
1802
|
+
const layoutStr = typeof layout === 'string' ? layout : JSON.stringify(layout);
|
|
1803
|
+
const layoutHash = this.simpleHash(layoutStr);
|
|
1804
|
+
return `${layoutHash}|${look}`;
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Generate cache key for expression evaluation (short hash)
|
|
1808
|
+
*/
|
|
1809
|
+
generateCacheKey(expression, context) {
|
|
1810
|
+
// Use short hash for better performance
|
|
1811
|
+
const exprStr = typeof expression === 'string' ? expression : JSON.stringify(expression);
|
|
1812
|
+
const exprHash = this.simpleHash(exprStr);
|
|
1813
|
+
const ctxHash = this.generateContextHash(context);
|
|
1814
|
+
return `${exprHash}|${ctxHash}`;
|
|
2438
1815
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
1816
|
+
/**
|
|
1817
|
+
* Generate a simple hash for context change detection
|
|
1818
|
+
*/
|
|
1819
|
+
generateContextHash(context) {
|
|
1820
|
+
if (!context || typeof context !== 'object') {
|
|
1821
|
+
return String(context);
|
|
2442
1822
|
}
|
|
2443
|
-
|
|
1823
|
+
// Generate short hash for context
|
|
1824
|
+
const keys = Object.keys(context).sort();
|
|
1825
|
+
const contextStr = keys.map((key) => `${key}:${context[key]}`).join('|');
|
|
1826
|
+
return this.simpleHash(contextStr);
|
|
2444
1827
|
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Simple hash function for generating short keys
|
|
1830
|
+
*/
|
|
1831
|
+
simpleHash(str) {
|
|
1832
|
+
let hash = 0;
|
|
1833
|
+
if (str.length === 0)
|
|
1834
|
+
return hash.toString();
|
|
1835
|
+
for (let i = 0; i < str.length; i++) {
|
|
1836
|
+
const char = str.charCodeAt(i);
|
|
1837
|
+
hash = (hash << 5) - hash + char;
|
|
1838
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
1839
|
+
}
|
|
1840
|
+
// Convert to positive hex string
|
|
1841
|
+
return Math.abs(hash).toString(16);
|
|
1842
|
+
}
|
|
1843
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1844
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: AXPLayoutRendererComponent, isStandalone: true, selector: "axp-layout-renderer", inputs: { layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: true, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { context: "contextChange", contextInitiated: "contextInitiated", validityChange: "validityChange" }, viewQueries: [{ propertyName: "form", first: true, predicate: AXFormComponent, descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1845
|
+
<ax-form>
|
|
1846
|
+
<axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
|
|
1847
|
+
@if (widgetTree()) {
|
|
1848
|
+
<ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
|
|
1849
|
+
}
|
|
1850
|
+
</axp-widgets-container>
|
|
1851
|
+
</ax-form>
|
|
1852
|
+
`, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i2.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2445
1853
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
1854
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
|
|
1855
|
+
type: Component,
|
|
1856
|
+
args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1857
|
+
<ax-form>
|
|
1858
|
+
<axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
|
|
1859
|
+
@if (widgetTree()) {
|
|
1860
|
+
<ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
|
|
1861
|
+
}
|
|
1862
|
+
</axp-widgets-container>
|
|
1863
|
+
</ax-form>
|
|
1864
|
+
`, styles: [":host{display:block;width:100%}\n"] }]
|
|
1865
|
+
}] });
|
|
1866
|
+
|
|
1867
|
+
class LayoutBuilderModule {
|
|
1868
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1869
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
|
|
1870
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
|
|
1871
|
+
}
|
|
1872
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, decorators: [{
|
|
1873
|
+
type: NgModule,
|
|
1874
|
+
args: [{
|
|
1875
|
+
imports: [CommonModule, AXPLayoutRendererComponent],
|
|
1876
|
+
providers: [AXPLayoutBuilderService],
|
|
1877
|
+
exports: [AXPLayoutRendererComponent],
|
|
1878
|
+
}]
|
|
1879
|
+
}] });
|
|
1880
|
+
|
|
1881
|
+
//#endregion
|
|
1882
|
+
|
|
1883
|
+
class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
1884
|
+
constructor() {
|
|
1885
|
+
super(...arguments);
|
|
1886
|
+
this.result = new EventEmitter();
|
|
1887
|
+
this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
|
|
1888
|
+
// This will be set by the popup service automatically - same as dynamic-dialog
|
|
1889
|
+
this.callBack = () => { };
|
|
1890
|
+
this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
|
|
1891
|
+
}
|
|
1892
|
+
ngOnInit() {
|
|
1893
|
+
// Initialize context with provided context
|
|
1894
|
+
this.context.set(this.config?.context || {});
|
|
2453
1895
|
}
|
|
2454
|
-
|
|
1896
|
+
handleContextChanged(event) {
|
|
1897
|
+
this.context.set(event);
|
|
1898
|
+
}
|
|
1899
|
+
handleContextInitiated(event) {
|
|
1900
|
+
this.context.set(event);
|
|
1901
|
+
}
|
|
1902
|
+
visibleFooterPrefixActions() {
|
|
1903
|
+
return this.config?.actions?.footer?.prefix || [];
|
|
1904
|
+
}
|
|
1905
|
+
visibleFooterSuffixActions() {
|
|
1906
|
+
return (this.config?.actions?.footer?.suffix || [
|
|
1907
|
+
{ title: 'Cancel', color: 'secondary', command: { name: 'cancel' } },
|
|
1908
|
+
{ title: 'Confirm', color: 'primary', command: { name: 'confirm' } },
|
|
1909
|
+
]);
|
|
1910
|
+
}
|
|
1911
|
+
isFormLoading() {
|
|
1912
|
+
return this.isDialogLoading();
|
|
1913
|
+
}
|
|
1914
|
+
isSubmitting() {
|
|
1915
|
+
return this.isDialogLoading();
|
|
1916
|
+
}
|
|
1917
|
+
async executeAction(action) {
|
|
1918
|
+
const actionName = action.command?.name || action.title?.toLowerCase();
|
|
1919
|
+
// Store the action and context - same pattern as dynamic-dialog
|
|
1920
|
+
const result = {
|
|
1921
|
+
context: this.context(),
|
|
1922
|
+
action: actionName,
|
|
1923
|
+
};
|
|
1924
|
+
// Store result in component property
|
|
1925
|
+
this.dialogResult = result;
|
|
1926
|
+
// Store in popup data for DialogRef access
|
|
1927
|
+
if (this.data) {
|
|
1928
|
+
this.data.context = result.context;
|
|
1929
|
+
this.data.action = result.action;
|
|
1930
|
+
}
|
|
1931
|
+
// Call the callback with DialogRef - same pattern as dynamic-dialog
|
|
1932
|
+
this.callBack({
|
|
1933
|
+
close: (result) => {
|
|
1934
|
+
this.close(result);
|
|
1935
|
+
},
|
|
1936
|
+
context: () => {
|
|
1937
|
+
return this.context();
|
|
1938
|
+
},
|
|
1939
|
+
action: () => {
|
|
1940
|
+
return result.action;
|
|
1941
|
+
},
|
|
1942
|
+
setLoading: (loading) => {
|
|
1943
|
+
this.isDialogLoading.set(loading);
|
|
1944
|
+
},
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
close(result) {
|
|
1948
|
+
if (result) {
|
|
1949
|
+
this.result.emit(result);
|
|
1950
|
+
}
|
|
1951
|
+
super.close(result);
|
|
1952
|
+
}
|
|
1953
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1954
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", inputs: { config: "config" }, outputs: { result: "result" }, usesInheritance: true, ngImport: i0, template: `
|
|
1955
|
+
<div class="ax-p-4">
|
|
1956
|
+
<axp-layout-renderer
|
|
1957
|
+
[layout]="config.definition"
|
|
1958
|
+
[context]="context()"
|
|
1959
|
+
(contextChange)="handleContextChanged($event)"
|
|
1960
|
+
(contextInitiated)="handleContextInitiated($event)"
|
|
1961
|
+
>
|
|
1962
|
+
</axp-layout-renderer>
|
|
1963
|
+
</div>
|
|
1964
|
+
|
|
1965
|
+
<ax-footer>
|
|
1966
|
+
<ax-prefix>
|
|
1967
|
+
<ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
|
|
1968
|
+
</ax-prefix>
|
|
1969
|
+
<ax-suffix>
|
|
1970
|
+
<ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
|
|
1971
|
+
</ax-suffix>
|
|
1972
|
+
</ax-footer>
|
|
1973
|
+
|
|
1974
|
+
<!-- Footer Prefix Actions -->
|
|
1975
|
+
<ng-template #footerPrefixActions>
|
|
1976
|
+
@for (action of visibleFooterPrefixActions(); track $index) {
|
|
1977
|
+
<ax-button
|
|
1978
|
+
[disabled]="action.disabled || isFormLoading()"
|
|
1979
|
+
[text]="(action.title | translate | async)!"
|
|
1980
|
+
[look]="'outline'"
|
|
1981
|
+
[color]="action.color"
|
|
1982
|
+
(onClick)="executeAction(action)"
|
|
1983
|
+
>
|
|
1984
|
+
@if (isFormLoading() && action.command?.name != 'cancel') {
|
|
1985
|
+
<ax-loading></ax-loading>
|
|
1986
|
+
}
|
|
1987
|
+
<ax-prefix>
|
|
1988
|
+
<i class="{{ action.icon }}"></i>
|
|
1989
|
+
</ax-prefix>
|
|
1990
|
+
</ax-button>
|
|
1991
|
+
}
|
|
1992
|
+
</ng-template>
|
|
1993
|
+
|
|
1994
|
+
<!-- Footer Suffix Actions -->
|
|
1995
|
+
<ng-template #footerSuffixActions>
|
|
1996
|
+
@for (action of visibleFooterSuffixActions(); track $index) {
|
|
1997
|
+
<ax-button
|
|
1998
|
+
[disabled]="action.disabled || isSubmitting()"
|
|
1999
|
+
[text]="(action.title | translate | async)!"
|
|
2000
|
+
[look]="'solid'"
|
|
2001
|
+
[color]="action.color"
|
|
2002
|
+
(onClick)="executeAction(action)"
|
|
2003
|
+
>
|
|
2004
|
+
@if (action.icon) {
|
|
2005
|
+
<ax-prefix>
|
|
2006
|
+
<ax-icon icon="{{ action.icon }}"></ax-icon>
|
|
2007
|
+
</ax-prefix>
|
|
2008
|
+
}
|
|
2009
|
+
</ax-button>
|
|
2010
|
+
}
|
|
2011
|
+
</ng-template>
|
|
2012
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXPLayoutRendererComponent, selector: "axp-layout-renderer", inputs: ["layout", "context", "look", "mode"], outputs: ["contextChange", "contextInitiated", "validityChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }] }); }
|
|
2455
2013
|
}
|
|
2014
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
|
|
2015
|
+
type: Component,
|
|
2016
|
+
args: [{
|
|
2017
|
+
selector: 'axp-dialog-renderer',
|
|
2018
|
+
standalone: true,
|
|
2019
|
+
imports: [
|
|
2020
|
+
CommonModule,
|
|
2021
|
+
AXPLayoutRendererComponent,
|
|
2022
|
+
AXButtonModule,
|
|
2023
|
+
AXDecoratorModule,
|
|
2024
|
+
AXLoadingModule,
|
|
2025
|
+
AXTranslationModule,
|
|
2026
|
+
],
|
|
2027
|
+
template: `
|
|
2028
|
+
<div class="ax-p-4">
|
|
2029
|
+
<axp-layout-renderer
|
|
2030
|
+
[layout]="config.definition"
|
|
2031
|
+
[context]="context()"
|
|
2032
|
+
(contextChange)="handleContextChanged($event)"
|
|
2033
|
+
(contextInitiated)="handleContextInitiated($event)"
|
|
2034
|
+
>
|
|
2035
|
+
</axp-layout-renderer>
|
|
2036
|
+
</div>
|
|
2037
|
+
|
|
2038
|
+
<ax-footer>
|
|
2039
|
+
<ax-prefix>
|
|
2040
|
+
<ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
|
|
2041
|
+
</ax-prefix>
|
|
2042
|
+
<ax-suffix>
|
|
2043
|
+
<ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
|
|
2044
|
+
</ax-suffix>
|
|
2045
|
+
</ax-footer>
|
|
2456
2046
|
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
]
|
|
2047
|
+
<!-- Footer Prefix Actions -->
|
|
2048
|
+
<ng-template #footerPrefixActions>
|
|
2049
|
+
@for (action of visibleFooterPrefixActions(); track $index) {
|
|
2050
|
+
<ax-button
|
|
2051
|
+
[disabled]="action.disabled || isFormLoading()"
|
|
2052
|
+
[text]="(action.title | translate | async)!"
|
|
2053
|
+
[look]="'outline'"
|
|
2054
|
+
[color]="action.color"
|
|
2055
|
+
(onClick)="executeAction(action)"
|
|
2056
|
+
>
|
|
2057
|
+
@if (isFormLoading() && action.command?.name != 'cancel') {
|
|
2058
|
+
<ax-loading></ax-loading>
|
|
2059
|
+
}
|
|
2060
|
+
<ax-prefix>
|
|
2061
|
+
<i class="{{ action.icon }}"></i>
|
|
2062
|
+
</ax-prefix>
|
|
2063
|
+
</ax-button>
|
|
2064
|
+
}
|
|
2065
|
+
</ng-template>
|
|
2066
|
+
|
|
2067
|
+
<!-- Footer Suffix Actions -->
|
|
2068
|
+
<ng-template #footerSuffixActions>
|
|
2069
|
+
@for (action of visibleFooterSuffixActions(); track $index) {
|
|
2070
|
+
<ax-button
|
|
2071
|
+
[disabled]="action.disabled || isSubmitting()"
|
|
2072
|
+
[text]="(action.title | translate | async)!"
|
|
2073
|
+
[look]="'solid'"
|
|
2074
|
+
[color]="action.color"
|
|
2075
|
+
(onClick)="executeAction(action)"
|
|
2076
|
+
>
|
|
2077
|
+
@if (action.icon) {
|
|
2078
|
+
<ax-prefix>
|
|
2079
|
+
<ax-icon icon="{{ action.icon }}"></ax-icon>
|
|
2080
|
+
</ax-prefix>
|
|
2081
|
+
}
|
|
2082
|
+
</ax-button>
|
|
2083
|
+
}
|
|
2084
|
+
</ng-template>
|
|
2085
|
+
`,
|
|
2086
|
+
}]
|
|
2087
|
+
}], propDecorators: { config: [{
|
|
2088
|
+
type: Input
|
|
2089
|
+
}], result: [{
|
|
2090
|
+
type: Output
|
|
2091
|
+
}] } });
|
|
2483
2092
|
|
|
2484
|
-
var
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
AXPWidgetGroupEnum["FormTemplate"] = "form-template";
|
|
2489
|
-
AXPWidgetGroupEnum["PropertyEditor"] = "property-editor";
|
|
2490
|
-
AXPWidgetGroupEnum["MetaData"] = "meta-data";
|
|
2491
|
-
AXPWidgetGroupEnum["SettingWidget"] = "setting-widget";
|
|
2492
|
-
AXPWidgetGroupEnum["EntityWidget"] = "entity-widget";
|
|
2493
|
-
})(AXPWidgetGroupEnum || (AXPWidgetGroupEnum = {}));
|
|
2093
|
+
var dialogRenderer_component = /*#__PURE__*/Object.freeze({
|
|
2094
|
+
__proto__: null,
|
|
2095
|
+
AXPDialogRendererComponent: AXPDialogRendererComponent
|
|
2096
|
+
});
|
|
2494
2097
|
|
|
2495
2098
|
/**
|
|
2496
2099
|
* Generated bundle index. Do not edit.
|
|
2497
2100
|
*/
|
|
2498
2101
|
|
|
2499
|
-
export {
|
|
2102
|
+
export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
|
|
2500
2103
|
//# sourceMappingURL=acorex-platform-layout-builder.mjs.map
|