@masterteam/form-builder 0.0.7 → 0.0.8
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.
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, computed, input, signal, viewChild, Component, effect, output, DestroyRef } from '@angular/core';
|
|
3
|
-
import * as i5 from '@angular/common';
|
|
2
|
+
import { inject, Injectable, computed, input, signal, viewChild, Component, effect, output, DestroyRef, viewChildren } from '@angular/core';
|
|
4
3
|
import { CommonModule } from '@angular/common';
|
|
5
4
|
import * as i1 from '@angular/forms';
|
|
6
5
|
import { FormControl, ReactiveFormsModule, FormGroup, FormsModule } from '@angular/forms';
|
|
@@ -8,13 +7,16 @@ import * as i2 from 'primeng/tabs';
|
|
|
8
7
|
import { TabsModule } from 'primeng/tabs';
|
|
9
8
|
import * as i3 from 'primeng/skeleton';
|
|
10
9
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
10
|
+
import * as i4 from 'primeng/popover';
|
|
11
|
+
import { Popover, PopoverModule } from 'primeng/popover';
|
|
11
12
|
import { Button } from '@masterteam/components/button';
|
|
12
13
|
import { Card } from '@masterteam/components/card';
|
|
13
14
|
import { TextField } from '@masterteam/components/text-field';
|
|
15
|
+
import { SelectField } from '@masterteam/components/select-field';
|
|
14
16
|
import { ModalService } from '@masterteam/components/modal';
|
|
15
17
|
import { ConfirmationService } from '@masterteam/components/confirmation';
|
|
16
18
|
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
|
|
17
|
-
import * as
|
|
19
|
+
import * as i5 from '@angular/cdk/drag-drop';
|
|
18
20
|
import { CdkDrag, CdkDropList, CdkDragPlaceholder, DragDropModule } from '@angular/cdk/drag-drop';
|
|
19
21
|
import { DynamicField } from '@masterteam/forms/dynamic-field';
|
|
20
22
|
import { Icon } from '@masterteam/icons';
|
|
@@ -30,6 +32,7 @@ import { Tabs } from '@masterteam/components/tabs';
|
|
|
30
32
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
31
33
|
import { Table } from '@masterteam/components/table';
|
|
32
34
|
import { RadioCards } from '@masterteam/components/radio-cards';
|
|
35
|
+
import { of, map, catchError } from 'rxjs';
|
|
33
36
|
|
|
34
37
|
// ============================================================================
|
|
35
38
|
// Module Configuration Actions
|
|
@@ -257,6 +260,15 @@ let FormBuilderState = class FormBuilderState extends CrudStateBase {
|
|
|
257
260
|
static getModuleId(state) {
|
|
258
261
|
return state?.moduleId ?? null;
|
|
259
262
|
}
|
|
263
|
+
static getParentModuleType(state) {
|
|
264
|
+
return state?.parentModuleType ?? null;
|
|
265
|
+
}
|
|
266
|
+
static getParentModuleId(state) {
|
|
267
|
+
return state?.parentModuleId ?? null;
|
|
268
|
+
}
|
|
269
|
+
static getParentPath(state) {
|
|
270
|
+
return state?.parentPath ?? '';
|
|
271
|
+
}
|
|
260
272
|
static getProperties(state) {
|
|
261
273
|
return state?.properties ?? [];
|
|
262
274
|
}
|
|
@@ -394,7 +406,7 @@ let FormBuilderState = class FormBuilderState extends CrudStateBase {
|
|
|
394
406
|
const tempField = {
|
|
395
407
|
id: tempId,
|
|
396
408
|
sectionId: action.sectionId,
|
|
397
|
-
|
|
409
|
+
propertyKey: action.payload.propertyKey,
|
|
398
410
|
width: action.payload.width,
|
|
399
411
|
order: action.payload.order ?? 0,
|
|
400
412
|
hiddenInCreation: action.payload.hiddenInCreation,
|
|
@@ -862,6 +874,15 @@ __decorate([
|
|
|
862
874
|
__decorate([
|
|
863
875
|
Selector()
|
|
864
876
|
], FormBuilderState, "getModuleId", null);
|
|
877
|
+
__decorate([
|
|
878
|
+
Selector()
|
|
879
|
+
], FormBuilderState, "getParentModuleType", null);
|
|
880
|
+
__decorate([
|
|
881
|
+
Selector()
|
|
882
|
+
], FormBuilderState, "getParentModuleId", null);
|
|
883
|
+
__decorate([
|
|
884
|
+
Selector()
|
|
885
|
+
], FormBuilderState, "getParentPath", null);
|
|
865
886
|
__decorate([
|
|
866
887
|
Selector()
|
|
867
888
|
], FormBuilderState, "getProperties", null);
|
|
@@ -890,6 +911,9 @@ class FormBuilderFacade {
|
|
|
890
911
|
validations = select(FormBuilderState.getValidations);
|
|
891
912
|
moduleType = select(FormBuilderState.getModuleType);
|
|
892
913
|
moduleId = select(FormBuilderState.getModuleId);
|
|
914
|
+
parentModuleType = select(FormBuilderState.getParentModuleType);
|
|
915
|
+
parentModuleId = select(FormBuilderState.getParentModuleId);
|
|
916
|
+
parentPath = select(FormBuilderState.getParentPath);
|
|
893
917
|
// ============================================================================
|
|
894
918
|
// Loading Signals
|
|
895
919
|
// ============================================================================
|
|
@@ -1325,7 +1349,7 @@ class FBFieldConditions {
|
|
|
1325
1349
|
this.ref.close({ saved: false });
|
|
1326
1350
|
}
|
|
1327
1351
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBFieldConditions, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1328
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.3", type: FBFieldConditions, isStandalone: true, selector: "mt-fb-field-conditions", inputs: { initialFormula: { classPropertyName: "initialFormula", publicName: "initialFormula", isSignal: true, isRequired: false, transformFunction: null }, availableFields: { classPropertyName: "availableFields", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-4 h-full overflow-y-auto pb-10 flex flex-col gap-3\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyKeys()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n />\r\n <!-- Formula Editor -->\r\n <mt-formula-editor\r\n #editor\r\n [placeholder]=\"t('build-condition-formula')\"\r\n [formControl]=\"formulaControl\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n />\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
1352
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.3", type: FBFieldConditions, isStandalone: true, selector: "mt-fb-field-conditions", inputs: { initialFormula: { classPropertyName: "initialFormula", publicName: "initialFormula", isSignal: true, isRequired: false, transformFunction: null }, availableFields: { classPropertyName: "availableFields", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-4 h-full overflow-y-auto pb-10 flex flex-col gap-3\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyKeys()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n />\r\n <!-- Formula Editor -->\r\n <mt-formula-editor\r\n #editor\r\n [placeholder]=\"t('build-condition-formula')\"\r\n [formControl]=\"formulaControl\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n />\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
1329
1353
|
}
|
|
1330
1354
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBFieldConditions, decorators: [{
|
|
1331
1355
|
type: Component,
|
|
@@ -1368,7 +1392,7 @@ class FBFieldForm {
|
|
|
1368
1392
|
if (currentField && field.id === currentField.id)
|
|
1369
1393
|
continue;
|
|
1370
1394
|
fields.push({
|
|
1371
|
-
key: field.
|
|
1395
|
+
key: field.propertyKey,
|
|
1372
1396
|
name: field.name,
|
|
1373
1397
|
type: field.type,
|
|
1374
1398
|
});
|
|
@@ -1853,7 +1877,7 @@ class FBPreviewForm {
|
|
|
1853
1877
|
fields: visibleFields.map((field, fieldIndex) => {
|
|
1854
1878
|
const colSpan = this.getColSpan(field.width);
|
|
1855
1879
|
return {
|
|
1856
|
-
key: `field_${field.
|
|
1880
|
+
key: `field_${field.propertyKey}`,
|
|
1857
1881
|
label: field.name,
|
|
1858
1882
|
type: this.mapFieldType(field.type),
|
|
1859
1883
|
colSpan,
|
|
@@ -2337,7 +2361,7 @@ class FBValidationRuleForm {
|
|
|
2337
2361
|
this.ref.close(false);
|
|
2338
2362
|
}
|
|
2339
2363
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBValidationRuleForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2340
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.3", type: FBValidationRuleForm, isStandalone: true, selector: "mt-fb-validation-rule-form", inputs: { initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, availableFields: { classPropertyName: "availableFields", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-4 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyKeys()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n />\r\n <mt-formula-editor\r\n #editor\r\n [placeholder]=\"t('validation-formula-placeholder')\"\r\n [formControl]=\"formulaControl\"\r\n />\r\n <div class=\"grid grid-cols-2 gap-4\">\r\n <mt-text-field\r\n [label]=\"t('message-en')\"\r\n [placeholder]=\"t('message-en')\"\r\n [formControl]=\"messageEnControl\"\r\n />\r\n <mt-text-field\r\n [label]=\"t('message-ar')\"\r\n [placeholder]=\"t('message-ar')\"\r\n [formControl]=\"messageArControl\"\r\n />\r\n </div>\r\n <div class=\"grid gap-2\">\r\n <label class=\"text-sm font-medium text-gray-700 dark:text-gray-100\">\r\n {{ t(\"severity\") }}\r\n </label>\r\n <mt-radio-cards\r\n [options]=\"severityOptions()\"\r\n [activeId]=\"severityControl.value\"\r\n (selectionChange)=\"onSeverityChange($event)\"\r\n ></mt-radio-cards>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n />\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: RadioCards, selector: "mt-radio-cards", inputs: ["circle", "color", "size", "columns", "options", "activeId", "itemTemplate"], outputs: ["optionsChange", "activeIdChange", "selectionChange"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
2364
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.3", type: FBValidationRuleForm, isStandalone: true, selector: "mt-fb-validation-rule-form", inputs: { initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, availableFields: { classPropertyName: "availableFields", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-4 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyKeys()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n />\r\n <mt-formula-editor\r\n #editor\r\n [placeholder]=\"t('validation-formula-placeholder')\"\r\n [formControl]=\"formulaControl\"\r\n />\r\n <div class=\"grid grid-cols-2 gap-4\">\r\n <mt-text-field\r\n [label]=\"t('message-en')\"\r\n [placeholder]=\"t('message-en')\"\r\n [formControl]=\"messageEnControl\"\r\n />\r\n <mt-text-field\r\n [label]=\"t('message-ar')\"\r\n [placeholder]=\"t('message-ar')\"\r\n [formControl]=\"messageArControl\"\r\n />\r\n </div>\r\n <div class=\"grid gap-2\">\r\n <label class=\"text-sm font-medium text-gray-700 dark:text-gray-100\">\r\n {{ t(\"severity\") }}\r\n </label>\r\n <mt-radio-cards\r\n [options]=\"severityOptions()\"\r\n [activeId]=\"severityControl.value\"\r\n (selectionChange)=\"onSeverityChange($event)\"\r\n ></mt-radio-cards>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n />\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: RadioCards, selector: "mt-radio-cards", inputs: ["circle", "color", "size", "columns", "options", "activeId", "itemTemplate"], outputs: ["optionsChange", "activeIdChange", "selectionChange"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
2341
2365
|
}
|
|
2342
2366
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBValidationRuleForm, decorators: [{
|
|
2343
2367
|
type: Component,
|
|
@@ -2364,7 +2388,7 @@ class FBValidationRules {
|
|
|
2364
2388
|
propertiesMap = computed(() => {
|
|
2365
2389
|
const map = new Map();
|
|
2366
2390
|
for (const prop of this.properties()) {
|
|
2367
|
-
map.set(prop.
|
|
2391
|
+
map.set(prop.key, prop);
|
|
2368
2392
|
}
|
|
2369
2393
|
return map;
|
|
2370
2394
|
}, ...(ngDevMode ? [{ debugName: "propertiesMap" }] : []));
|
|
@@ -2375,15 +2399,10 @@ class FBValidationRules {
|
|
|
2375
2399
|
const fields = [];
|
|
2376
2400
|
for (const section of sections) {
|
|
2377
2401
|
for (const field of section.fields) {
|
|
2378
|
-
const prop = propsMap.get(field.
|
|
2379
|
-
const
|
|
2380
|
-
const name = typeof propName === 'string'
|
|
2381
|
-
? propName
|
|
2382
|
-
: (propName?.[lang] ??
|
|
2383
|
-
propName?.['en'] ??
|
|
2384
|
-
`Property ${field.propertyId}`);
|
|
2402
|
+
const prop = field.property ?? propsMap.get(field.propertyKey);
|
|
2403
|
+
const name = this.resolvePropertyName(prop, field.propertyKey, lang);
|
|
2385
2404
|
fields.push({
|
|
2386
|
-
key:
|
|
2405
|
+
key: field.propertyKey,
|
|
2387
2406
|
name,
|
|
2388
2407
|
type: prop?.viewType || 'text',
|
|
2389
2408
|
});
|
|
@@ -2438,6 +2457,24 @@ class FBValidationRules {
|
|
|
2438
2457
|
this.activeLang.set(lang ?? 'en');
|
|
2439
2458
|
});
|
|
2440
2459
|
}
|
|
2460
|
+
resolvePropertyName(property, fallbackKey, lang) {
|
|
2461
|
+
if (!property) {
|
|
2462
|
+
return `Property ${fallbackKey}`;
|
|
2463
|
+
}
|
|
2464
|
+
const name = property?.name;
|
|
2465
|
+
if (typeof name === 'string') {
|
|
2466
|
+
return name;
|
|
2467
|
+
}
|
|
2468
|
+
const display = name?.['display'];
|
|
2469
|
+
if (typeof display === 'string' && display.trim().length > 0) {
|
|
2470
|
+
return display;
|
|
2471
|
+
}
|
|
2472
|
+
const localized = name?.[lang] ?? name?.['en'] ?? name?.['ar'];
|
|
2473
|
+
if (typeof localized === 'string' && localized.trim().length > 0) {
|
|
2474
|
+
return localized;
|
|
2475
|
+
}
|
|
2476
|
+
return property?.key ?? `Property ${fallbackKey}`;
|
|
2477
|
+
}
|
|
2441
2478
|
openCreate() {
|
|
2442
2479
|
this.modalService.openModal(FBValidationRuleForm, 'drawer', {
|
|
2443
2480
|
header: this.transloco.translate('formBuilder.add-validation-rule'),
|
|
@@ -2483,29 +2520,199 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
2483
2520
|
args: [{ selector: 'mt-fb-validation-rules', standalone: true, imports: [TranslocoDirective, Table, Button], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-auto']\">\r\n <div class=\"flex justify-end pb-4\">\r\n <mt-button\r\n [label]=\"t('add-validation-rule')\"\r\n severity=\"primary\"\r\n variant=\"outlined\"\r\n (onClick)=\"openCreate()\"\r\n ></mt-button>\r\n </div>\r\n <mt-table\r\n [data]=\"tableRows()\"\r\n [columns]=\"tableColumns()\"\r\n [rowActions]=\"rowActions()\"\r\n (cellChange)=\"onCellChange($event)\"\r\n ></mt-table>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
2484
2521
|
}], ctorParameters: () => [] });
|
|
2485
2522
|
|
|
2523
|
+
class FormBuilderContextService {
|
|
2524
|
+
http = inject(HttpClient);
|
|
2525
|
+
baseUrl = 'context';
|
|
2526
|
+
/**
|
|
2527
|
+
* Resolves context data including navigation paths and properties.
|
|
2528
|
+
* No caching - always fetches fresh data.
|
|
2529
|
+
*/
|
|
2530
|
+
resolveContext(contextKey, options = {}) {
|
|
2531
|
+
if (!contextKey)
|
|
2532
|
+
return of(null);
|
|
2533
|
+
return this.http
|
|
2534
|
+
.post(`${this.baseUrl}/resolve`, {
|
|
2535
|
+
contextKey,
|
|
2536
|
+
displayTags: options.displayTags,
|
|
2537
|
+
propertyFlags: options.propertyFlags,
|
|
2538
|
+
matchAllTags: options.matchAllTags,
|
|
2539
|
+
includeVirtual: options.includeVirtual,
|
|
2540
|
+
includeNavigationPaths: options.includeNavigationPaths,
|
|
2541
|
+
includeConnectionMetadata: options.includeConnectionMetadata,
|
|
2542
|
+
includeRelatedContextValues: options.includeRelatedContextValues,
|
|
2543
|
+
relatedContextsToInclude: options.relatedContextsToInclude,
|
|
2544
|
+
})
|
|
2545
|
+
.pipe(map((response) => this.normalizeResponse(response)), catchError((error) => {
|
|
2546
|
+
console.warn('Failed to resolve context:', error);
|
|
2547
|
+
return of(null);
|
|
2548
|
+
}));
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Gets properties for a context.
|
|
2552
|
+
* No caching - always fetches fresh data.
|
|
2553
|
+
*/
|
|
2554
|
+
getProperties(contextKey, options = {}) {
|
|
2555
|
+
if (!contextKey)
|
|
2556
|
+
return of([]);
|
|
2557
|
+
return this.resolveContext(contextKey, options).pipe(map((resolved) => resolved?.properties ?? []));
|
|
2558
|
+
}
|
|
2559
|
+
normalizeResponse(response) {
|
|
2560
|
+
const payload = response?.data ?? response;
|
|
2561
|
+
if (!payload)
|
|
2562
|
+
return null;
|
|
2563
|
+
const properties = this.normalizeProperties(payload.properties ?? []);
|
|
2564
|
+
return {
|
|
2565
|
+
...payload,
|
|
2566
|
+
properties,
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
normalizeProperties(raw) {
|
|
2570
|
+
return (raw ?? [])
|
|
2571
|
+
.map((prop) => {
|
|
2572
|
+
const key = prop?.key ?? prop?.propertyKey;
|
|
2573
|
+
if (!key)
|
|
2574
|
+
return null;
|
|
2575
|
+
const name = prop?.name ?? prop?.displayName ?? key;
|
|
2576
|
+
return {
|
|
2577
|
+
...prop,
|
|
2578
|
+
key,
|
|
2579
|
+
name,
|
|
2580
|
+
};
|
|
2581
|
+
})
|
|
2582
|
+
.filter((prop) => Boolean(prop));
|
|
2583
|
+
}
|
|
2584
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2585
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, providedIn: 'root' });
|
|
2586
|
+
}
|
|
2587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, decorators: [{
|
|
2588
|
+
type: Injectable,
|
|
2589
|
+
args: [{ providedIn: 'root' }]
|
|
2590
|
+
}] });
|
|
2591
|
+
|
|
2592
|
+
const SCOPE_ORDER = [
|
|
2593
|
+
'Current',
|
|
2594
|
+
'Host',
|
|
2595
|
+
'Parent',
|
|
2596
|
+
'Ancestors',
|
|
2597
|
+
'Children',
|
|
2598
|
+
'Descendants',
|
|
2599
|
+
'Related',
|
|
2600
|
+
'Siblings',
|
|
2601
|
+
];
|
|
2602
|
+
const PATH_TOKENS = [
|
|
2603
|
+
'Parent',
|
|
2604
|
+
'Ancestors',
|
|
2605
|
+
'Children',
|
|
2606
|
+
'Descendants',
|
|
2607
|
+
'Related',
|
|
2608
|
+
'Siblings',
|
|
2609
|
+
'Host',
|
|
2610
|
+
];
|
|
2611
|
+
const SCOPE_LABELS = {
|
|
2612
|
+
Current: 'Current',
|
|
2613
|
+
Host: 'Host',
|
|
2614
|
+
Parent: 'Parent',
|
|
2615
|
+
Ancestors: 'Ancestors',
|
|
2616
|
+
Children: 'Children',
|
|
2617
|
+
Descendants: 'Descendants',
|
|
2618
|
+
Related: 'Related',
|
|
2619
|
+
Siblings: 'Siblings',
|
|
2620
|
+
};
|
|
2621
|
+
// ============================================================================
|
|
2622
|
+
// Component
|
|
2623
|
+
// ============================================================================
|
|
2486
2624
|
class FormBuilder {
|
|
2487
2625
|
modalService = inject(ModalService);
|
|
2488
2626
|
confirmationService = inject(ConfirmationService);
|
|
2489
2627
|
translocoService = inject(TranslocoService);
|
|
2490
2628
|
facade = inject(FormBuilderFacade);
|
|
2629
|
+
contextService = inject(FormBuilderContextService);
|
|
2630
|
+
popovers = viewChildren(Popover, ...(ngDevMode ? [{ debugName: "popovers" }] : []));
|
|
2491
2631
|
dialogRef;
|
|
2492
|
-
//
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
// State from facade
|
|
2632
|
+
// ============================================================================
|
|
2633
|
+
// Facade State
|
|
2634
|
+
// ============================================================================
|
|
2496
2635
|
sections = this.facade.sections;
|
|
2497
2636
|
properties = this.facade.properties;
|
|
2498
2637
|
isLoading = this.facade.isLoadingFormConfiguration;
|
|
2499
2638
|
error = this.facade.formConfigurationError;
|
|
2500
|
-
|
|
2639
|
+
moduleType = this.facade.moduleType;
|
|
2640
|
+
moduleId = this.facade.moduleId;
|
|
2641
|
+
parentPath = this.facade.parentPath;
|
|
2642
|
+
// ============================================================================
|
|
2643
|
+
// UI State (Simple Signals)
|
|
2644
|
+
// ============================================================================
|
|
2645
|
+
activeScope = signal('Current', ...(ngDevMode ? [{ debugName: "activeScope" }] : []));
|
|
2646
|
+
searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
|
|
2647
|
+
isContextLoading = signal(false, ...(ngDevMode ? [{ debugName: "isContextLoading" }] : []));
|
|
2648
|
+
// Initial context data (from first load)
|
|
2649
|
+
initialContext = signal(null, ...(ngDevMode ? [{ debugName: "initialContext" }] : []));
|
|
2650
|
+
currentProperties = signal([], ...(ngDevMode ? [{ debugName: "currentProperties" }] : []));
|
|
2651
|
+
// Non-current scope state (reset on tab change)
|
|
2652
|
+
scopeLoading = signal(false, ...(ngDevMode ? [{ debugName: "scopeLoading" }] : []));
|
|
2653
|
+
scopeProperties = signal([], ...(ngDevMode ? [{ debugName: "scopeProperties" }] : []));
|
|
2654
|
+
scopePath = signal([], ...(ngDevMode ? [{ debugName: "scopePath" }] : []));
|
|
2655
|
+
scopeSelectedKey = signal('', ...(ngDevMode ? [{ debugName: "scopeSelectedKey" }] : []));
|
|
2656
|
+
scopeNavigation = signal(null, ...(ngDevMode ? [{ debugName: "scopeNavigation" }] : []));
|
|
2657
|
+
// Request tracking
|
|
2658
|
+
initialRequestId = 0;
|
|
2659
|
+
scopeRequestId = 0;
|
|
2660
|
+
lastContextKey = '';
|
|
2661
|
+
// ============================================================================
|
|
2662
|
+
// Computed Values
|
|
2663
|
+
// ============================================================================
|
|
2664
|
+
contextKey = computed(() => {
|
|
2665
|
+
const type = this.moduleType();
|
|
2666
|
+
const id = this.moduleId();
|
|
2667
|
+
const parent = this.parentPath();
|
|
2668
|
+
if (!type || id === null || id === undefined)
|
|
2669
|
+
return '';
|
|
2670
|
+
const segments = [];
|
|
2671
|
+
const raw = parent.split('/').filter(Boolean);
|
|
2672
|
+
for (let i = 0; i < raw.length; i += 2) {
|
|
2673
|
+
if (raw[i] && raw[i + 1] !== undefined) {
|
|
2674
|
+
segments.push(`${raw[i]}:${raw[i + 1]}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
segments.push(`${type}:${id}`);
|
|
2678
|
+
return segments.join('/');
|
|
2679
|
+
}, ...(ngDevMode ? [{ debugName: "contextKey" }] : []));
|
|
2680
|
+
usedPropertyKeys = computed(() => {
|
|
2681
|
+
return new Set(this.sections().flatMap((s) => s.fields.map((f) => f.propertyKey)));
|
|
2682
|
+
}, ...(ngDevMode ? [{ debugName: "usedPropertyKeys" }] : []));
|
|
2501
2683
|
propertiesMap = computed(() => {
|
|
2502
2684
|
const map = new Map();
|
|
2503
2685
|
for (const prop of this.properties()) {
|
|
2504
|
-
map.set(prop.
|
|
2686
|
+
map.set(prop.key, prop);
|
|
2505
2687
|
}
|
|
2506
2688
|
return map;
|
|
2507
2689
|
}, ...(ngDevMode ? [{ debugName: "propertiesMap" }] : []));
|
|
2508
|
-
|
|
2690
|
+
/** Navigation options from initial context */
|
|
2691
|
+
baseScopeContexts = computed(() => {
|
|
2692
|
+
const ctx = this.initialContext();
|
|
2693
|
+
const nav = ctx?.navigationPaths;
|
|
2694
|
+
const currentKey = ctx?.currentState?.contextPath ?? this.contextKey();
|
|
2695
|
+
return {
|
|
2696
|
+
Current: currentKey
|
|
2697
|
+
? [this.createContextOption(currentKey, ctx?.currentState)]
|
|
2698
|
+
: [],
|
|
2699
|
+
Host: nav?.hostContext
|
|
2700
|
+
? [this.createContextOption(nav.hostContext)]
|
|
2701
|
+
: [],
|
|
2702
|
+
Parent: this.mergeContextOptions(nav?.parent),
|
|
2703
|
+
Ancestors: this.mergeContextOptions(nav?.ancestors),
|
|
2704
|
+
Children: this.mergeContextOptions(nav?.children),
|
|
2705
|
+
Descendants: this.mergeContextOptions(nav?.descendants),
|
|
2706
|
+
Related: this.mergeContextOptions(nav?.related),
|
|
2707
|
+
Siblings: this.mergeContextOptions(nav?.siblingContexts),
|
|
2708
|
+
};
|
|
2709
|
+
}, ...(ngDevMode ? [{ debugName: "baseScopeContexts" }] : []));
|
|
2710
|
+
/** Available scope tabs */
|
|
2711
|
+
scopeOptions = computed(() => {
|
|
2712
|
+
const contexts = this.baseScopeContexts();
|
|
2713
|
+
return SCOPE_ORDER.filter((s) => (contexts[s] ?? []).length > 0).map((s) => ({ key: s, label: SCOPE_LABELS[s] }));
|
|
2714
|
+
}, ...(ngDevMode ? [{ debugName: "scopeOptions" }] : []));
|
|
2715
|
+
/** Enriched sections for display */
|
|
2509
2716
|
enrichedSections = computed(() => {
|
|
2510
2717
|
const sections = this.sections();
|
|
2511
2718
|
const propsMap = this.propertiesMap();
|
|
@@ -2513,119 +2720,427 @@ class FormBuilder {
|
|
|
2513
2720
|
return sections.map((section) => ({
|
|
2514
2721
|
...section,
|
|
2515
2722
|
fields: section.fields.map((field) => {
|
|
2516
|
-
const prop = propsMap.get(field.
|
|
2517
|
-
const propName = prop?.name;
|
|
2518
|
-
const name = typeof propName === 'string'
|
|
2519
|
-
? propName
|
|
2520
|
-
: (propName?.[lang] ??
|
|
2521
|
-
propName?.['en'] ??
|
|
2522
|
-
`Property ${field.propertyId}`);
|
|
2723
|
+
const prop = field.property ?? propsMap.get(field.propertyKey);
|
|
2523
2724
|
return {
|
|
2524
2725
|
...field,
|
|
2525
|
-
name,
|
|
2726
|
+
name: this.resolvePropertyName(prop, field.propertyKey, lang),
|
|
2526
2727
|
type: prop?.viewType || 'text',
|
|
2527
2728
|
data: prop,
|
|
2528
2729
|
};
|
|
2529
2730
|
}),
|
|
2530
2731
|
}));
|
|
2531
2732
|
}, ...(ngDevMode ? [{ debugName: "enrichedSections" }] : []));
|
|
2532
|
-
//
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
const usedPropertyIds = new Set(this.sections().flatMap((s) => s.fields.map((f) => f.propertyId)));
|
|
2536
|
-
const availableProps = this.properties().filter((p) => !usedPropertyIds.has(p.id));
|
|
2537
|
-
const systemProps = availableProps.filter((p) => p['isSystem']);
|
|
2538
|
-
const customProps = availableProps.filter((p) => !p['isSystem']);
|
|
2539
|
-
if (systemProps.length > 0) {
|
|
2540
|
-
tabs.push({ id: 'system', title: 'System', properties: systemProps });
|
|
2541
|
-
}
|
|
2542
|
-
if (customProps.length > 0) {
|
|
2543
|
-
tabs.push({ id: 'custom', title: 'Custom', properties: customProps });
|
|
2544
|
-
}
|
|
2545
|
-
return tabs;
|
|
2546
|
-
}, ...(ngDevMode ? [{ debugName: "availableTabs" }] : []));
|
|
2547
|
-
// Filtered properties based on search query
|
|
2548
|
-
filteredPropertiesByTab = computed(() => {
|
|
2549
|
-
const query = this.searchQuery().toLowerCase().trim();
|
|
2550
|
-
const tabs = this.availableTabs();
|
|
2551
|
-
if (!query) {
|
|
2552
|
-
return tabs;
|
|
2553
|
-
}
|
|
2554
|
-
return tabs.map((tab) => ({
|
|
2555
|
-
...tab,
|
|
2556
|
-
properties: tab.properties.filter((prop) => {
|
|
2557
|
-
const name = prop.name;
|
|
2558
|
-
return name.toLowerCase().includes(query);
|
|
2559
|
-
}),
|
|
2560
|
-
}));
|
|
2561
|
-
}, ...(ngDevMode ? [{ debugName: "filteredPropertiesByTab" }] : []));
|
|
2733
|
+
// ============================================================================
|
|
2734
|
+
// Lifecycle
|
|
2735
|
+
// ============================================================================
|
|
2562
2736
|
constructor() {
|
|
2563
|
-
//
|
|
2737
|
+
// Effect: watch for context key changes and load initial data
|
|
2564
2738
|
effect(() => {
|
|
2565
|
-
const
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
this.
|
|
2739
|
+
const key = this.contextKey();
|
|
2740
|
+
if (key && key !== this.lastContextKey) {
|
|
2741
|
+
this.lastContextKey = key;
|
|
2742
|
+
this.loadInitialContext(key);
|
|
2743
|
+
}
|
|
2744
|
+
}, { allowSignalWrites: true });
|
|
2745
|
+
}
|
|
2746
|
+
ngOnInit() {
|
|
2747
|
+
// Trigger initial load if context key is already available
|
|
2748
|
+
const key = this.contextKey();
|
|
2749
|
+
if (key && key !== this.lastContextKey) {
|
|
2750
|
+
this.lastContextKey = key;
|
|
2751
|
+
this.loadInitialContext(key);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
// ============================================================================
|
|
2755
|
+
// Initial Context Loading
|
|
2756
|
+
// ============================================================================
|
|
2757
|
+
loadInitialContext(contextKey) {
|
|
2758
|
+
const requestId = ++this.initialRequestId;
|
|
2759
|
+
this.isContextLoading.set(true);
|
|
2760
|
+
this.initialContext.set(null);
|
|
2761
|
+
this.currentProperties.set([]);
|
|
2762
|
+
this.resetScopeState();
|
|
2763
|
+
this.activeScope.set('Current');
|
|
2764
|
+
this.contextService
|
|
2765
|
+
.resolveContext(contextKey, {
|
|
2766
|
+
propertyFlags: 'None',
|
|
2767
|
+
includeVirtual: true,
|
|
2768
|
+
includeNavigationPaths: true,
|
|
2769
|
+
includeConnectionMetadata: true,
|
|
2770
|
+
matchAllTags: true,
|
|
2771
|
+
includeRelatedContextValues: true,
|
|
2772
|
+
})
|
|
2773
|
+
.subscribe((response) => {
|
|
2774
|
+
if (requestId !== this.initialRequestId)
|
|
2775
|
+
return;
|
|
2776
|
+
this.isContextLoading.set(false);
|
|
2777
|
+
this.initialContext.set(response);
|
|
2778
|
+
this.currentProperties.set(response?.properties ?? []);
|
|
2779
|
+
this.facade.setProperties(response?.properties ?? []);
|
|
2780
|
+
});
|
|
2781
|
+
}
|
|
2782
|
+
// ============================================================================
|
|
2783
|
+
// Tab Change Handler (Called from template)
|
|
2784
|
+
// ============================================================================
|
|
2785
|
+
onScopeChange(scope) {
|
|
2786
|
+
if (scope === this.activeScope())
|
|
2787
|
+
return;
|
|
2788
|
+
this.activeScope.set(scope);
|
|
2789
|
+
this.resetScopeState();
|
|
2790
|
+
if (scope === 'Current') {
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
// Get first context option for this scope
|
|
2794
|
+
const options = this.baseScopeContexts()[scope] ?? [];
|
|
2795
|
+
if (options.length === 0)
|
|
2796
|
+
return;
|
|
2797
|
+
const firstOption = options[0];
|
|
2798
|
+
this.scopePath.set([
|
|
2799
|
+
{
|
|
2800
|
+
token: scope,
|
|
2801
|
+
value: firstOption.contextKey,
|
|
2802
|
+
label: firstOption.label,
|
|
2803
|
+
},
|
|
2804
|
+
]);
|
|
2805
|
+
this.loadScopeContext(firstOption.contextKey);
|
|
2806
|
+
}
|
|
2807
|
+
// ============================================================================
|
|
2808
|
+
// Scope Context Loading
|
|
2809
|
+
// ============================================================================
|
|
2810
|
+
loadScopeContext(contextKey) {
|
|
2811
|
+
const requestId = ++this.scopeRequestId;
|
|
2812
|
+
this.scopeLoading.set(true);
|
|
2813
|
+
this.contextService
|
|
2814
|
+
.resolveContext(contextKey, {
|
|
2815
|
+
propertyFlags: 'None',
|
|
2816
|
+
includeVirtual: true,
|
|
2817
|
+
includeNavigationPaths: true,
|
|
2818
|
+
includeConnectionMetadata: true,
|
|
2819
|
+
matchAllTags: true,
|
|
2820
|
+
includeRelatedContextValues: true,
|
|
2821
|
+
})
|
|
2822
|
+
.subscribe((response) => {
|
|
2823
|
+
if (requestId !== this.scopeRequestId)
|
|
2824
|
+
return;
|
|
2825
|
+
this.scopeLoading.set(false);
|
|
2826
|
+
this.scopeNavigation.set(response);
|
|
2827
|
+
this.scopeProperties.set(response?.properties ?? []);
|
|
2828
|
+
// Auto-select first available property
|
|
2829
|
+
const used = this.usedPropertyKeys();
|
|
2830
|
+
const available = (response?.properties ?? []).filter((p) => !used.has(p.key));
|
|
2831
|
+
if (available.length > 0) {
|
|
2832
|
+
this.scopeSelectedKey.set(available[0].key);
|
|
2569
2833
|
}
|
|
2570
2834
|
});
|
|
2571
2835
|
}
|
|
2836
|
+
resetScopeState() {
|
|
2837
|
+
this.scopeLoading.set(false);
|
|
2838
|
+
this.scopeProperties.set([]);
|
|
2839
|
+
this.scopePath.set([]);
|
|
2840
|
+
this.scopeSelectedKey.set('');
|
|
2841
|
+
this.scopeNavigation.set(null);
|
|
2842
|
+
this.hideAllPopovers();
|
|
2843
|
+
}
|
|
2844
|
+
// ============================================================================
|
|
2845
|
+
// Path Segment Methods (accept scope parameter for template compatibility)
|
|
2846
|
+
// ============================================================================
|
|
2847
|
+
getScopeBaseContexts(scope) {
|
|
2848
|
+
return this.baseScopeContexts()[scope] ?? [];
|
|
2849
|
+
}
|
|
2850
|
+
/** Template calls this with scope.key */
|
|
2851
|
+
getScopePath(_scope) {
|
|
2852
|
+
return this.scopePath();
|
|
2853
|
+
}
|
|
2854
|
+
getTokenLabel(token) {
|
|
2855
|
+
return SCOPE_LABELS[token] ?? token;
|
|
2856
|
+
}
|
|
2857
|
+
getScopeSegmentLabel(_scope, index) {
|
|
2858
|
+
const path = this.scopePath();
|
|
2859
|
+
const segment = path[index];
|
|
2860
|
+
if (!segment?.value)
|
|
2861
|
+
return `Select ${this.getTokenLabel(segment?.token)}`;
|
|
2862
|
+
// Use stored label - don't look up from options (which change on resolve)
|
|
2863
|
+
return segment.label || this.formatContextKey(segment.value);
|
|
2864
|
+
}
|
|
2865
|
+
getScopeSegmentOptions(_scope, index) {
|
|
2866
|
+
const path = this.scopePath();
|
|
2867
|
+
const segment = path[index];
|
|
2868
|
+
if (!segment)
|
|
2869
|
+
return [];
|
|
2870
|
+
if (index === 0) {
|
|
2871
|
+
return this.getScopeBaseContexts(this.activeScope());
|
|
2872
|
+
}
|
|
2873
|
+
const nav = this.scopeNavigation();
|
|
2874
|
+
return this.getNavigationOptionsByToken(nav, segment.token);
|
|
2875
|
+
}
|
|
2876
|
+
setScopeSegmentValue(_scope, index, contextKey, label) {
|
|
2877
|
+
const path = this.scopePath();
|
|
2878
|
+
const newPath = path.slice(0, index + 1);
|
|
2879
|
+
if (newPath[index]) {
|
|
2880
|
+
// Store label at selection time so it doesn't change on resolve
|
|
2881
|
+
const segmentLabel = label || this.findOptionLabel(_scope, index, contextKey);
|
|
2882
|
+
newPath[index] = {
|
|
2883
|
+
...newPath[index],
|
|
2884
|
+
value: contextKey,
|
|
2885
|
+
label: segmentLabel,
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
this.scopePath.set(newPath);
|
|
2889
|
+
this.loadScopeContext(contextKey);
|
|
2890
|
+
}
|
|
2891
|
+
findOptionLabel(_scope, index, contextKey) {
|
|
2892
|
+
const options = this.getScopeSegmentOptions(_scope, index);
|
|
2893
|
+
const option = options.find((o) => o.contextKey === contextKey);
|
|
2894
|
+
return option?.label ?? this.formatContextKey(contextKey);
|
|
2895
|
+
}
|
|
2896
|
+
/** Template handler: hide popover first, then set value (like formula-builder) */
|
|
2897
|
+
onSetScopeSegmentValue(scope, index, contextKey, popover) {
|
|
2898
|
+
popover.hide();
|
|
2899
|
+
this.setScopeSegmentValue(scope, index, contextKey);
|
|
2900
|
+
}
|
|
2901
|
+
addScopeSegment(_scope, token) {
|
|
2902
|
+
const path = this.scopePath();
|
|
2903
|
+
this.scopePath.set([...path, { token, value: '', label: '' }]);
|
|
2904
|
+
}
|
|
2905
|
+
/** Template handler: hide popover first, then add segment (like formula-builder) */
|
|
2906
|
+
onAddScopeSegment(scope, token, popover) {
|
|
2907
|
+
popover.hide();
|
|
2908
|
+
this.addScopeSegment(scope, token);
|
|
2909
|
+
}
|
|
2910
|
+
getScopeNextTokens(_scope) {
|
|
2911
|
+
const path = this.scopePath();
|
|
2912
|
+
const last = path[path.length - 1];
|
|
2913
|
+
if (!last?.value)
|
|
2914
|
+
return [];
|
|
2915
|
+
const nav = this.scopeNavigation();
|
|
2916
|
+
if (!nav)
|
|
2917
|
+
return [];
|
|
2918
|
+
return PATH_TOKENS.filter((t) => this.getNavigationOptionsByToken(nav, t).length > 0);
|
|
2919
|
+
}
|
|
2920
|
+
canAddNextScopeSegment(_scope) {
|
|
2921
|
+
const path = this.scopePath();
|
|
2922
|
+
if (path.length === 0)
|
|
2923
|
+
return false;
|
|
2924
|
+
if (!path.every((s) => Boolean(s.value)))
|
|
2925
|
+
return false;
|
|
2926
|
+
return this.getScopeNextTokens().length > 0;
|
|
2927
|
+
}
|
|
2928
|
+
canRemoveScopePath(_scope) {
|
|
2929
|
+
return this.scopePath().length > 1;
|
|
2930
|
+
}
|
|
2931
|
+
removeScopePath(_scope) {
|
|
2932
|
+
const path = this.scopePath();
|
|
2933
|
+
if (path.length <= 1)
|
|
2934
|
+
return;
|
|
2935
|
+
const newPath = path.slice(0, -1);
|
|
2936
|
+
this.scopePath.set(newPath);
|
|
2937
|
+
const last = newPath[newPath.length - 1];
|
|
2938
|
+
if (last?.value) {
|
|
2939
|
+
this.loadScopeContext(last.value);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
/** Template-compatible version (returns properties array for the scope) */
|
|
2943
|
+
getScopeProperties(_scope) {
|
|
2944
|
+
const scope = _scope ?? this.activeScope();
|
|
2945
|
+
return scope === 'Current'
|
|
2946
|
+
? this.currentProperties()
|
|
2947
|
+
: this.scopeProperties();
|
|
2948
|
+
}
|
|
2949
|
+
// ============================================================================
|
|
2950
|
+
// Property Methods (accept scope parameter for template compatibility)
|
|
2951
|
+
// ============================================================================
|
|
2952
|
+
getScopePropertyOptions(_scope) {
|
|
2953
|
+
const scope = _scope ?? this.activeScope();
|
|
2954
|
+
const props = scope === 'Current' ? this.currentProperties() : this.scopeProperties();
|
|
2955
|
+
const used = this.usedPropertyKeys();
|
|
2956
|
+
return props
|
|
2957
|
+
.filter((p) => !used.has(p.key))
|
|
2958
|
+
.map((p) => ({ key: p.key, name: this.getPropertyLabel(p) }));
|
|
2959
|
+
}
|
|
2960
|
+
getScopeSelectedPropertyKey(_scope) {
|
|
2961
|
+
return this.scopeSelectedKey();
|
|
2962
|
+
}
|
|
2963
|
+
setScopeSelectedProperty(_scope, key) {
|
|
2964
|
+
this.scopeSelectedKey.set(key);
|
|
2965
|
+
}
|
|
2966
|
+
getScopeSelectedProperty(_scope) {
|
|
2967
|
+
const key = this.scopeSelectedKey();
|
|
2968
|
+
if (!key)
|
|
2969
|
+
return null;
|
|
2970
|
+
return this.scopeProperties().find((p) => p.key === key) ?? null;
|
|
2971
|
+
}
|
|
2972
|
+
getFilteredProperties(_scope) {
|
|
2973
|
+
const scope = _scope ?? this.activeScope();
|
|
2974
|
+
const used = this.usedPropertyKeys();
|
|
2975
|
+
const query = this.searchQuery().toLowerCase().trim();
|
|
2976
|
+
if (scope !== 'Current') {
|
|
2977
|
+
const selected = this.getScopeSelectedProperty();
|
|
2978
|
+
if (!selected || used.has(selected.key))
|
|
2979
|
+
return [];
|
|
2980
|
+
return [selected];
|
|
2981
|
+
}
|
|
2982
|
+
let available = this.currentProperties().filter((p) => !used.has(p.key));
|
|
2983
|
+
if (query) {
|
|
2984
|
+
available = available.filter((p) => this.getPropertyLabel(p).toLowerCase().includes(query));
|
|
2985
|
+
}
|
|
2986
|
+
return available;
|
|
2987
|
+
}
|
|
2988
|
+
isScopePropertiesLoading(_scope) {
|
|
2989
|
+
const scope = _scope ?? this.activeScope();
|
|
2990
|
+
return scope !== 'Current' && this.scopeLoading();
|
|
2991
|
+
}
|
|
2992
|
+
getPropertyLabel(property) {
|
|
2993
|
+
const lang = document.documentElement.lang;
|
|
2994
|
+
return this.resolvePropertyName(property, property.key, lang);
|
|
2995
|
+
}
|
|
2996
|
+
// ============================================================================
|
|
2997
|
+
// Helper Methods
|
|
2998
|
+
// ============================================================================
|
|
2999
|
+
hideAllPopovers() {
|
|
3000
|
+
for (const popover of this.popovers()) {
|
|
3001
|
+
popover.hide();
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
getNavigationOptionsByToken(response, token) {
|
|
3005
|
+
const nav = response?.navigationPaths;
|
|
3006
|
+
if (!nav)
|
|
3007
|
+
return [];
|
|
3008
|
+
switch (token) {
|
|
3009
|
+
case 'Parent':
|
|
3010
|
+
return this.mergeContextOptions(nav.parent);
|
|
3011
|
+
case 'Ancestors':
|
|
3012
|
+
return this.mergeContextOptions(nav.ancestors);
|
|
3013
|
+
case 'Children':
|
|
3014
|
+
return this.mergeContextOptions(nav.children);
|
|
3015
|
+
case 'Descendants':
|
|
3016
|
+
return this.mergeContextOptions(nav.descendants);
|
|
3017
|
+
case 'Related':
|
|
3018
|
+
return this.mergeContextOptions(nav.related);
|
|
3019
|
+
case 'Siblings':
|
|
3020
|
+
return this.mergeContextOptions(nav.siblingContexts);
|
|
3021
|
+
case 'Host':
|
|
3022
|
+
return nav.hostContext
|
|
3023
|
+
? [this.createContextOption(nav.hostContext)]
|
|
3024
|
+
: [];
|
|
3025
|
+
default:
|
|
3026
|
+
return [];
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
resolvePropertyName(property, fallbackKey, lang) {
|
|
3030
|
+
if (!property)
|
|
3031
|
+
return `Property ${fallbackKey}`;
|
|
3032
|
+
const name = property.name;
|
|
3033
|
+
if (typeof name === 'string')
|
|
3034
|
+
return name;
|
|
3035
|
+
const display = name?.['display'];
|
|
3036
|
+
if (typeof display === 'string' && display.trim())
|
|
3037
|
+
return display;
|
|
3038
|
+
const localized = name?.[lang] ?? name?.['en'] ?? name?.['ar'];
|
|
3039
|
+
if (typeof localized === 'string' && localized.trim())
|
|
3040
|
+
return localized;
|
|
3041
|
+
return property.key ?? `Property ${fallbackKey}`;
|
|
3042
|
+
}
|
|
3043
|
+
createContextOption(pathOrKey, currentState) {
|
|
3044
|
+
if (typeof pathOrKey === 'string') {
|
|
3045
|
+
return {
|
|
3046
|
+
contextKey: pathOrKey,
|
|
3047
|
+
label: this.formatContextLabel({
|
|
3048
|
+
contextKey: pathOrKey,
|
|
3049
|
+
contextType: currentState?.contextType,
|
|
3050
|
+
contextId: currentState?.contextId,
|
|
3051
|
+
}),
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
return {
|
|
3055
|
+
contextKey: pathOrKey.contextKey,
|
|
3056
|
+
label: this.formatContextLabel(pathOrKey),
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
formatContextLabel(path) {
|
|
3060
|
+
// Just return the name - no path formatting needed
|
|
3061
|
+
if (path.name)
|
|
3062
|
+
return path.name;
|
|
3063
|
+
if (path.key)
|
|
3064
|
+
return path.key;
|
|
3065
|
+
if (path.contextType && path.contextId !== undefined)
|
|
3066
|
+
return `${path.contextType} ${path.contextId}`;
|
|
3067
|
+
return path.contextKey || 'Context';
|
|
3068
|
+
}
|
|
3069
|
+
formatContextKey(contextKey) {
|
|
3070
|
+
return contextKey
|
|
3071
|
+
.split('/')
|
|
3072
|
+
.filter(Boolean)
|
|
3073
|
+
.map((seg) => {
|
|
3074
|
+
const [type, id] = seg.split(':');
|
|
3075
|
+
return id !== undefined ? `${type} ${id}` : type;
|
|
3076
|
+
})
|
|
3077
|
+
.join(' / ');
|
|
3078
|
+
}
|
|
3079
|
+
mergeContextOptions(items) {
|
|
3080
|
+
if (!items?.length)
|
|
3081
|
+
return [];
|
|
3082
|
+
const seen = new Set();
|
|
3083
|
+
return items
|
|
3084
|
+
.slice()
|
|
3085
|
+
.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0))
|
|
3086
|
+
.filter((item) => {
|
|
3087
|
+
if (!item?.contextKey || seen.has(item.contextKey))
|
|
3088
|
+
return false;
|
|
3089
|
+
seen.add(item.contextKey);
|
|
3090
|
+
return true;
|
|
3091
|
+
})
|
|
3092
|
+
.map((item) => this.createContextOption(item));
|
|
3093
|
+
}
|
|
3094
|
+
// ============================================================================
|
|
3095
|
+
// Drag & Drop
|
|
3096
|
+
// ============================================================================
|
|
2572
3097
|
drop(event) {
|
|
2573
3098
|
const targetSectionId = event.container.id;
|
|
2574
3099
|
if (event.previousContainer === event.container) {
|
|
2575
|
-
// Reordering within the same section
|
|
2576
3100
|
const fields = [...event.container.data];
|
|
2577
3101
|
const [movedField] = fields.splice(event.previousIndex, 1);
|
|
2578
3102
|
fields.splice(event.currentIndex, 0, movedField);
|
|
2579
|
-
|
|
2580
|
-
const reorderPayload = fields.map((field, index) => ({
|
|
2581
|
-
id: field.id,
|
|
2582
|
-
order: index,
|
|
2583
|
-
}));
|
|
2584
|
-
this.facade.reorderFields(targetSectionId, reorderPayload);
|
|
3103
|
+
this.facade.reorderFields(targetSectionId, fields.map((f, i) => ({ id: f.id, order: i })));
|
|
2585
3104
|
}
|
|
2586
3105
|
else if (event.previousContainer.id.startsWith('toolbox-')) {
|
|
2587
|
-
|
|
2588
|
-
const propertyItem = event.item.data;
|
|
3106
|
+
const prop = event.item.data;
|
|
2589
3107
|
this.facade.addField(targetSectionId, {
|
|
2590
|
-
|
|
3108
|
+
propertyKey: prop.key,
|
|
2591
3109
|
width: '100',
|
|
2592
3110
|
order: event.currentIndex,
|
|
2593
3111
|
hiddenInCreation: false,
|
|
2594
3112
|
});
|
|
2595
3113
|
}
|
|
2596
3114
|
else {
|
|
2597
|
-
// Moving between sections
|
|
2598
|
-
const sourceSectionId = event.previousContainer.id;
|
|
2599
3115
|
const field = event.item.data;
|
|
2600
|
-
this.facade.moveField(
|
|
3116
|
+
this.facade.moveField(event.previousContainer.id, field.id, {
|
|
2601
3117
|
targetSectionId,
|
|
2602
3118
|
order: event.currentIndex,
|
|
2603
3119
|
});
|
|
2604
3120
|
}
|
|
2605
3121
|
}
|
|
3122
|
+
// ============================================================================
|
|
3123
|
+
// Modal Actions
|
|
3124
|
+
// ============================================================================
|
|
2606
3125
|
addSection() {
|
|
2607
3126
|
this.dialogRef = this.modalService.openModal(FBSectionForm, 'drawer', {
|
|
2608
3127
|
header: this.translocoService.translate('formBuilder.add-section'),
|
|
2609
3128
|
height: '20vw',
|
|
2610
|
-
styleClass: '!w-[27%] !absolute !shadow-none
|
|
3129
|
+
styleClass: '!w-[27%] !absolute !shadow-none',
|
|
2611
3130
|
position: 'end',
|
|
2612
3131
|
appendTo: '#page-content',
|
|
2613
3132
|
dismissible: true,
|
|
2614
|
-
inputValues: {
|
|
2615
|
-
sectionsCount: this.sections().length,
|
|
2616
|
-
},
|
|
3133
|
+
inputValues: { sectionsCount: this.sections().length },
|
|
2617
3134
|
});
|
|
2618
3135
|
}
|
|
2619
3136
|
openPreview() {
|
|
2620
3137
|
this.dialogRef = this.modalService.openModal(FBPreviewForm, 'drawer', {
|
|
2621
3138
|
header: this.translocoService.translate('formBuilder.preview'),
|
|
2622
|
-
styleClass: '!w-[79%] !absolute !shadow-none
|
|
3139
|
+
styleClass: '!w-[79%] !absolute !shadow-none',
|
|
2623
3140
|
position: 'end',
|
|
2624
3141
|
appendTo: 'page-content',
|
|
2625
3142
|
dismissible: true,
|
|
2626
|
-
inputValues: {
|
|
2627
|
-
sections: this.enrichedSections(),
|
|
2628
|
-
},
|
|
3143
|
+
inputValues: { sections: this.enrichedSections() },
|
|
2629
3144
|
});
|
|
2630
3145
|
}
|
|
2631
3146
|
openValidationRules() {
|
|
@@ -2641,14 +3156,12 @@ class FormBuilder {
|
|
|
2641
3156
|
this.confirmationService.confirm({
|
|
2642
3157
|
type: 'dialog',
|
|
2643
3158
|
acceptButtonStyleClass: 'p-button-danger',
|
|
2644
|
-
accept: () =>
|
|
2645
|
-
this.facade.resetFormConfiguration();
|
|
2646
|
-
},
|
|
3159
|
+
accept: () => this.facade.resetFormConfiguration(),
|
|
2647
3160
|
});
|
|
2648
3161
|
}
|
|
2649
3162
|
noReturnPredicate = () => false;
|
|
2650
3163
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2651
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormBuilder, isStandalone: true, selector: "mt-form-builder", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex gap-7 h-full w-full overflow-hidden\" cdkDropListGroup>\r\n <!-- Properties Sidebar -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5\">\r\n {{ t(\"form-elements\") }}\r\n </h3>\r\n\r\n @if (properties().length === 0) {\r\n @if (isLoading()) {\r\n <!-- Properties Loading Skeleton -->\r\n <div class=\"flex gap-4 py-3 px-5 mt-4\">\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"py-4 px-5 space-y-5\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else {\r\n <!-- No Properties State -->\r\n <div class=\"flex-1 flex items-center justify-center p-4\">\r\n <p class=\"text-sm font-semibold text-gray-500\">\r\n {{ t(\"no-data-found\") }}\r\n </p>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Tabs using PrimeNG -->\r\n <p-tabs\r\n [(value)]=\"activeTab\"\r\n styleClass=\"structure-tabs\"\r\n class=\"flex flex-1 flex-col min-h-0\"\r\n >\r\n <p-tablist class=\"shrink-0\">\r\n @for (tab of availableTabs(); track tab.id) {\r\n <p-tab [value]=\"tab.id\">{{ tab.title | titlecase }}</p-tab>\r\n }\r\n </p-tablist>\r\n <p-tabpanels\r\n class=\"!bg-transparent !p-0 !pb-3 flex-1 overflow-hidden\"\r\n >\r\n @for (tab of filteredPropertiesByTab(); track tab.id) {\r\n <p-tabpanel [value]=\"tab.id\" class=\"h-full flex flex-col\">\r\n <!-- Node List -->\r\n <div\r\n class=\"space-y-4 px-4 pb-4 [&_.cdk-drag-placeholder]:hidden flex-1 overflow-y-auto\"\r\n [id]=\"'toolbox-' + tab.id\"\r\n cdkDropList\r\n cdkDropListSortingDisabled\r\n [cdkDropListData]=\"tab.properties\"\r\n [cdkDropListEnterPredicate]=\"noReturnPredicate\"\r\n >\r\n <!-- Search Field (Sticky) -->\r\n <div class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1\">\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n @for (node of tab.properties; track $index) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"node\"\r\n class=\"group cursor-move select-none flex items-center gap-3 py-3 px-3 rounded-lg border border-dashed border-surface-300 hover:bg-emphasis dark:border-surface-500 transition-colors\"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n class=\"col-span-12 min-h-27 w-full rounded-2xl bg-black/10 z-1\"\r\n ></div>\r\n <span class=\"flex-1 text-base font-medium\">{{\r\n node.name\r\n }}</span>\r\n\r\n <mt-icon\r\n class=\"text-lg\"\r\n icon=\"general.menu-05\"\r\n ></mt-icon>\r\n </div>\r\n }\r\n\r\n @if (tab.properties.length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">\r\n @if (searchQuery()) {\r\n {{ t(\"no-data-found\") }}\r\n } @else {\r\n All {{ tab.title }} items are in use\r\n }\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n </p-tabpanel>\r\n }\r\n </p-tabpanels>\r\n </p-tabs>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div class=\"flex flex-col w-2/3 gap-4 h-full\">\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 flex items-center gap-3\">\r\n <mt-button\r\n icon=\"layout.layout-top\"\r\n [label]=\"t('add-section')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"addSection()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"general.eye\"\r\n [label]=\"t('preview')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openPreview()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('validation-rules')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openValidationRules()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"finance.credit-card-plus\"\r\n [label]=\"t('reset')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"resetFormConfiguration()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n @if (isLoading()) {\r\n <!-- Form Loading Skeleton -->\r\n @for (i of [1, 2]; track i) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 space-y-4\">\r\n <!-- Section header skeleton -->\r\n <div class=\"flex items-center justify-between\">\r\n <p-skeleton width=\"10rem\" height=\"1.5rem\" />\r\n <div class=\"flex gap-2\">\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n </div>\r\n </div>\r\n <!-- Fields skeleton -->\r\n <div class=\"grid grid-cols-12 gap-4\">\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-12\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n } @else {\r\n @for (section of enrichedSections(); track section.id) {\r\n <mt-fb-section\r\n [section]=\"section\"\r\n [sectionsCount]=\"enrichedSections().length\"\r\n [allSections]=\"enrichedSections()\"\r\n (onFieldDrop)=\"drop($event)\"\r\n >\r\n </mt-fb-section>\r\n } @empty {\r\n <mt-card>\r\n <div class=\"h-27 p-4\">\r\n <div\r\n class=\"flex justify-center items-center gap-4 h-full border-1 border-primary rounded-xl bg-primary-50 text-primary\"\r\n >\r\n <span>{{ t(\"no-section\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [".cdk-drag{cursor:grab}.cdk-drag:active,.cdk-drag-preview{cursor:grabbing}.cdk-drag-placeholder{opacity:.5}.cdk-drop-list-dragging .cdk-drag{cursor:grabbing}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i2.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i2.TabPanel, selector: "p-tabpanel", inputs: ["lazy", "value"], outputs: ["valueChange"] }, { kind: "component", type: i2.TabList, selector: "p-tablist" }, { kind: "component", type: i2.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i3.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: FBSection, selector: "mt-fb-section", inputs: ["section", "sectionsCount", "allSections"], outputs: ["onFieldDrop"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i4.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "pipe", type: i5.TitleCasePipe, name: "titlecase" }] });
|
|
3164
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormBuilder, isStandalone: true, selector: "mt-form-builder", viewQueries: [{ propertyName: "popovers", predicate: Popover, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex gap-7 h-full w-full overflow-hidden\" cdkDropListGroup>\r\n <!-- Properties Sidebar -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5\">\r\n {{ t(\"form-elements\") }}\r\n </h3>\r\n\r\n @if (isContextLoading()) {\r\n <!-- Properties Loading Skeleton -->\r\n <div class=\"flex gap-4 py-3 px-5 mt-4\">\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"py-4 px-5 space-y-5\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else if (scopeOptions().length === 0) {\r\n <!-- No Properties State -->\r\n <div class=\"flex-1 flex items-center justify-center p-4\">\r\n <p class=\"text-sm font-semibold text-gray-500\">\r\n {{ t(\"no-data-found\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Scope Tabs using PrimeNG -->\r\n <p-tabs\r\n [value]=\"activeScope()\"\r\n (valueChange)=\"onScopeChange($event)\"\r\n styleClass=\"structure-tabs\"\r\n class=\"flex flex-1 flex-col min-h-0\"\r\n >\r\n <p-tablist class=\"shrink-0\">\r\n @for (scope of scopeOptions(); track scope.key) {\r\n <p-tab [value]=\"scope.key\">{{ scope.label }}</p-tab>\r\n }\r\n </p-tablist>\r\n <p-tabpanels\r\n class=\"!bg-transparent !p-0 !pb-3 flex-1 overflow-hidden\"\r\n >\r\n @for (scope of scopeOptions(); track scope.key) {\r\n <p-tabpanel [value]=\"scope.key\" class=\"h-full flex flex-col\">\r\n @if (scope.key !== \"Current\") {\r\n <div class=\"px-4 pt-3 pb-2\">\r\n <div\r\n class=\"flex flex-col gap-3 rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/50\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >\r\n Path\r\n </span>\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <span class=\"text-xs font-semibold text-slate-600\">\r\n {{ scope.label }}\r\n </span>\r\n </div>\r\n @if (getScopeBaseContexts(scope.key).length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">\r\n No context available\r\n </span>\r\n } @else {\r\n <div class=\"flex flex-col gap-1.5\">\r\n @for (\r\n segment of getScopePath(scope.key);\r\n track $index;\r\n let segmentIndex = $index;\r\n let isLast = $last\r\n ) {\r\n <div\r\n class=\"flex items-center gap-1 py-0.5 px-2 border border-gray-200 rounded bg-white dark:bg-slate-800 dark:border-slate-600\"\r\n >\r\n <div\r\n class=\"flex-1 min-w-0 text-xs font-medium text-slate-600 dark:text-slate-300 truncate cursor-pointer\"\r\n (click)=\"segmentPopover.toggle($event)\"\r\n >\r\n {{\r\n getScopeSegmentLabel(\r\n scope.key,\r\n segmentIndex\r\n )\r\n }}\r\n </div>\r\n <mt-button\r\n type=\"button\"\r\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"!p-0.5 !min-w-0\"\r\n (onClick)=\"segmentPopover.toggle($event)\"\r\n ></mt-button>\r\n @if (isLast && canRemoveScopePath(scope.key)) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.trash-01\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"!p-0.5 !min-w-0\"\r\n (onClick)=\"removeScopePath(scope.key)\"\r\n ></mt-button>\r\n }\r\n </div>\r\n <p-popover\r\n #segmentPopover\r\n [style]=\"{ width: 'max-content' }\"\r\n [dismissable]=\"true\"\r\n >\r\n <div class=\"p-2\">\r\n <div\r\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\r\n >\r\n Select {{ getTokenLabel(segment.token) }}\r\n </div>\r\n @if (\r\n getScopeSegmentOptions(\r\n scope.key,\r\n segmentIndex\r\n ).length === 0\r\n ) {\r\n <div\r\n class=\"text-xs font-medium text-slate-400\"\r\n >\r\n No options\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of getScopeSegmentOptions(\r\n scope.key,\r\n segmentIndex\r\n );\r\n track option.contextKey\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.label\"\r\n [icon]=\"\r\n option.contextKey === segment.value\r\n ? 'general.check'\r\n : 'general.minus'\r\n \"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700\"\r\n (onClick)=\"\r\n onSetScopeSegmentValue(\r\n scope.key,\r\n segmentIndex,\r\n option.contextKey,\r\n segmentPopover\r\n )\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n @if (canAddNextScopeSegment(scope.key)) {\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n styleClass=\"flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90\"\r\n (onClick)=\"nextContextPopover.toggle($event)\"\r\n ></mt-button>\r\n <span class=\"text-xs text-slate-400\">\r\n Add segment\r\n </span>\r\n </div>\r\n }\r\n <!-- Popover OUTSIDE the @if block like formula-builder -->\r\n <p-popover\r\n #nextContextPopover\r\n [style]=\"{ width: 'max-content' }\"\r\n [dismissable]=\"true\"\r\n appendTo=\"body\"\r\n >\r\n <div class=\"p-2\">\r\n <div\r\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\r\n >\r\n Add Segment\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n token of getScopeNextTokens(scope.key);\r\n track token\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"getTokenLabel(token)\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700\"\r\n (onClick)=\"\r\n onAddScopeSegment(\r\n scope.key,\r\n token,\r\n nextContextPopover\r\n )\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </div>\r\n </p-popover>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Node List -->\r\n <div\r\n class=\"space-y-4 px-4 pb-4 [&_.cdk-drag-placeholder]:hidden flex-1 overflow-y-auto\"\r\n [id]=\"'toolbox-' + scope.key\"\r\n cdkDropList\r\n cdkDropListSortingDisabled\r\n [cdkDropListData]=\"getFilteredProperties(scope.key)\"\r\n [cdkDropListEnterPredicate]=\"noReturnPredicate\"\r\n >\r\n @if (scope.key === \"Current\") {\r\n <!-- Search Field (Sticky) -->\r\n <div\r\n class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1\"\r\n >\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"sticky top-0 bg-surface-0 py-3\">\r\n @if (isScopePropertiesLoading(scope.key)) {\r\n <p-skeleton height=\"2.5rem\" styleClass=\"rounded-lg\" />\r\n } @else {\r\n <mt-select-field\r\n class=\"w-full\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n placeholder=\"Select...\"\r\n [ngModel]=\"getScopeSelectedPropertyKey(scope.key)\"\r\n (ngModelChange)=\"\r\n setScopeSelectedProperty(scope.key, $event)\r\n \"\r\n [options]=\"getScopePropertyOptions(scope.key)\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n @if (isScopePropertiesLoading(scope.key)) {\r\n <div class=\"space-y-4\">\r\n @for (i of [1, 2, 3, 4]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else {\r\n @for (\r\n node of getFilteredProperties(scope.key);\r\n track node.key\r\n ) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"node\"\r\n class=\"group cursor-move select-none flex items-center gap-3 py-3 px-3 rounded-lg border border-dashed border-surface-300 hover:bg-emphasis dark:border-surface-500 transition-colors\"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n class=\"col-span-12 min-h-27 w-full rounded-2xl bg-black/10 z-1\"\r\n ></div>\r\n <span\r\n class=\"flex-1 text-start text-base font-medium\"\r\n >{{ getPropertyLabel(node) }}</span\r\n >\r\n\r\n <mt-icon\r\n class=\"text-lg\"\r\n icon=\"general.menu-05\"\r\n ></mt-icon>\r\n </div>\r\n }\r\n\r\n @if (getFilteredProperties(scope.key).length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">\r\n @if (scope.key === \"Current\" && searchQuery()) {\r\n {{ t(\"no-data-found\") }}\r\n } @else if (scope.key !== \"Current\") {\r\n Select a property\r\n } @else if (\r\n getScopeProperties(scope.key).length === 0\r\n ) {\r\n No properties available\r\n } @else {\r\n All items are in use\r\n }\r\n </p>\r\n </div>\r\n }\r\n }\r\n </div>\r\n </p-tabpanel>\r\n }\r\n </p-tabpanels>\r\n </p-tabs>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div class=\"flex flex-col w-2/3 gap-4 h-full\">\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 flex items-center gap-3\">\r\n <mt-button\r\n icon=\"layout.layout-top\"\r\n [label]=\"t('add-section')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"addSection()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"general.eye\"\r\n [label]=\"t('preview')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openPreview()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('validation-rules')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openValidationRules()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"finance.credit-card-plus\"\r\n [label]=\"t('reset')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"resetFormConfiguration()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n @if (isLoading()) {\r\n <!-- Form Loading Skeleton -->\r\n @for (i of [1, 2]; track i) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 space-y-4\">\r\n <!-- Section header skeleton -->\r\n <div class=\"flex items-center justify-between\">\r\n <p-skeleton width=\"10rem\" height=\"1.5rem\" />\r\n <div class=\"flex gap-2\">\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n </div>\r\n </div>\r\n <!-- Fields skeleton -->\r\n <div class=\"grid grid-cols-12 gap-4\">\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-12\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n } @else {\r\n @for (section of enrichedSections(); track section.id) {\r\n <mt-fb-section\r\n [section]=\"section\"\r\n [sectionsCount]=\"enrichedSections().length\"\r\n [allSections]=\"enrichedSections()\"\r\n (onFieldDrop)=\"drop($event)\"\r\n >\r\n </mt-fb-section>\r\n } @empty {\r\n <mt-card>\r\n <div class=\"h-27 p-4\">\r\n <div\r\n class=\"flex justify-center items-center gap-4 h-full border-1 border-primary rounded-xl bg-primary-50 text-primary\"\r\n >\r\n <span>{{ t(\"no-section\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [".cdk-drag{cursor:grab}.cdk-drag:active,.cdk-drag-preview{cursor:grabbing}.cdk-drag-placeholder{opacity:.5}.cdk-drop-list-dragging .cdk-drag{cursor:grabbing}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i2.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i2.TabPanel, selector: "p-tabpanel", inputs: ["lazy", "value"], outputs: ["valueChange"] }, { kind: "component", type: i2.TabList, selector: "p-tablist" }, { kind: "component", type: i2.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i3.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i4.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading"], outputs: ["onChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: FBSection, selector: "mt-fb-section", inputs: ["section", "sectionsCount", "allSections"], outputs: ["onFieldDrop"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }] });
|
|
2652
3165
|
}
|
|
2653
3166
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilder, decorators: [{
|
|
2654
3167
|
type: Component,
|
|
@@ -2657,15 +3170,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
2657
3170
|
FormsModule,
|
|
2658
3171
|
TabsModule,
|
|
2659
3172
|
SkeletonModule,
|
|
3173
|
+
PopoverModule,
|
|
2660
3174
|
Button,
|
|
2661
3175
|
Card,
|
|
2662
3176
|
TextField,
|
|
3177
|
+
SelectField,
|
|
2663
3178
|
TranslocoDirective,
|
|
2664
3179
|
FBSection,
|
|
2665
3180
|
DragDropModule,
|
|
2666
3181
|
Icon,
|
|
2667
|
-
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex gap-7 h-full w-full overflow-hidden\" cdkDropListGroup>\r\n <!-- Properties Sidebar -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5\">\r\n {{ t(\"form-elements\") }}\r\n </h3>\r\n\r\n @if (properties().length === 0) {\r\n @if (isLoading()) {\r\n <!-- Properties Loading Skeleton -->\r\n <div class=\"flex gap-4 py-3 px-5 mt-4\">\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"py-4 px-5 space-y-5\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else {\r\n <!-- No Properties State -->\r\n <div class=\"flex-1 flex items-center justify-center p-4\">\r\n <p class=\"text-sm font-semibold text-gray-500\">\r\n {{ t(\"no-data-found\") }}\r\n </p>\r\n </div>\r\n }\r\n } @else {\r\n <!-- Tabs using PrimeNG -->\r\n <p-tabs\r\n [(value)]=\"activeTab\"\r\n styleClass=\"structure-tabs\"\r\n class=\"flex flex-1 flex-col min-h-0\"\r\n >\r\n <p-tablist class=\"shrink-0\">\r\n @for (tab of availableTabs(); track tab.id) {\r\n <p-tab [value]=\"tab.id\">{{ tab.title | titlecase }}</p-tab>\r\n }\r\n </p-tablist>\r\n <p-tabpanels\r\n class=\"!bg-transparent !p-0 !pb-3 flex-1 overflow-hidden\"\r\n >\r\n @for (tab of filteredPropertiesByTab(); track tab.id) {\r\n <p-tabpanel [value]=\"tab.id\" class=\"h-full flex flex-col\">\r\n <!-- Node List -->\r\n <div\r\n class=\"space-y-4 px-4 pb-4 [&_.cdk-drag-placeholder]:hidden flex-1 overflow-y-auto\"\r\n [id]=\"'toolbox-' + tab.id\"\r\n cdkDropList\r\n cdkDropListSortingDisabled\r\n [cdkDropListData]=\"tab.properties\"\r\n [cdkDropListEnterPredicate]=\"noReturnPredicate\"\r\n >\r\n <!-- Search Field (Sticky) -->\r\n <div class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1\">\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n @for (node of tab.properties; track $index) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"node\"\r\n class=\"group cursor-move select-none flex items-center gap-3 py-3 px-3 rounded-lg border border-dashed border-surface-300 hover:bg-emphasis dark:border-surface-500 transition-colors\"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n class=\"col-span-12 min-h-27 w-full rounded-2xl bg-black/10 z-1\"\r\n ></div>\r\n <span class=\"flex-1 text-base font-medium\">{{\r\n node.name\r\n }}</span>\r\n\r\n <mt-icon\r\n class=\"text-lg\"\r\n icon=\"general.menu-05\"\r\n ></mt-icon>\r\n </div>\r\n }\r\n\r\n @if (tab.properties.length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">\r\n @if (searchQuery()) {\r\n {{ t(\"no-data-found\") }}\r\n } @else {\r\n All {{ tab.title }} items are in use\r\n }\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n </p-tabpanel>\r\n }\r\n </p-tabpanels>\r\n </p-tabs>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div class=\"flex flex-col w-2/3 gap-4 h-full\">\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 flex items-center gap-3\">\r\n <mt-button\r\n icon=\"layout.layout-top\"\r\n [label]=\"t('add-section')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"addSection()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"general.eye\"\r\n [label]=\"t('preview')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openPreview()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('validation-rules')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openValidationRules()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"finance.credit-card-plus\"\r\n [label]=\"t('reset')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"resetFormConfiguration()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n @if (isLoading()) {\r\n <!-- Form Loading Skeleton -->\r\n @for (i of [1, 2]; track i) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 space-y-4\">\r\n <!-- Section header skeleton -->\r\n <div class=\"flex items-center justify-between\">\r\n <p-skeleton width=\"10rem\" height=\"1.5rem\" />\r\n <div class=\"flex gap-2\">\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n </div>\r\n </div>\r\n <!-- Fields skeleton -->\r\n <div class=\"grid grid-cols-12 gap-4\">\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-12\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n } @else {\r\n @for (section of enrichedSections(); track section.id) {\r\n <mt-fb-section\r\n [section]=\"section\"\r\n [sectionsCount]=\"enrichedSections().length\"\r\n [allSections]=\"enrichedSections()\"\r\n (onFieldDrop)=\"drop($event)\"\r\n >\r\n </mt-fb-section>\r\n } @empty {\r\n <mt-card>\r\n <div class=\"h-27 p-4\">\r\n <div\r\n class=\"flex justify-center items-center gap-4 h-full border-1 border-primary rounded-xl bg-primary-50 text-primary\"\r\n >\r\n <span>{{ t(\"no-section\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [".cdk-drag{cursor:grab}.cdk-drag:active,.cdk-drag-preview{cursor:grabbing}.cdk-drag-placeholder{opacity:.5}.cdk-drop-list-dragging .cdk-drag{cursor:grabbing}\n"] }]
|
|
2668
|
-
}], ctorParameters: () => [] });
|
|
3182
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex gap-7 h-full w-full overflow-hidden\" cdkDropListGroup>\r\n <!-- Properties Sidebar -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5\">\r\n {{ t(\"form-elements\") }}\r\n </h3>\r\n\r\n @if (isContextLoading()) {\r\n <!-- Properties Loading Skeleton -->\r\n <div class=\"flex gap-4 py-3 px-5 mt-4\">\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"py-4 px-5 space-y-5\">\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else if (scopeOptions().length === 0) {\r\n <!-- No Properties State -->\r\n <div class=\"flex-1 flex items-center justify-center p-4\">\r\n <p class=\"text-sm font-semibold text-gray-500\">\r\n {{ t(\"no-data-found\") }}\r\n </p>\r\n </div>\r\n } @else {\r\n <!-- Scope Tabs using PrimeNG -->\r\n <p-tabs\r\n [value]=\"activeScope()\"\r\n (valueChange)=\"onScopeChange($event)\"\r\n styleClass=\"structure-tabs\"\r\n class=\"flex flex-1 flex-col min-h-0\"\r\n >\r\n <p-tablist class=\"shrink-0\">\r\n @for (scope of scopeOptions(); track scope.key) {\r\n <p-tab [value]=\"scope.key\">{{ scope.label }}</p-tab>\r\n }\r\n </p-tablist>\r\n <p-tabpanels\r\n class=\"!bg-transparent !p-0 !pb-3 flex-1 overflow-hidden\"\r\n >\r\n @for (scope of scopeOptions(); track scope.key) {\r\n <p-tabpanel [value]=\"scope.key\" class=\"h-full flex flex-col\">\r\n @if (scope.key !== \"Current\") {\r\n <div class=\"px-4 pt-3 pb-2\">\r\n <div\r\n class=\"flex flex-col gap-3 rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/50\"\r\n >\r\n <div class=\"flex items-center gap-2\">\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >\r\n Path\r\n </span>\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <span class=\"text-xs font-semibold text-slate-600\">\r\n {{ scope.label }}\r\n </span>\r\n </div>\r\n @if (getScopeBaseContexts(scope.key).length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">\r\n No context available\r\n </span>\r\n } @else {\r\n <div class=\"flex flex-col gap-1.5\">\r\n @for (\r\n segment of getScopePath(scope.key);\r\n track $index;\r\n let segmentIndex = $index;\r\n let isLast = $last\r\n ) {\r\n <div\r\n class=\"flex items-center gap-1 py-0.5 px-2 border border-gray-200 rounded bg-white dark:bg-slate-800 dark:border-slate-600\"\r\n >\r\n <div\r\n class=\"flex-1 min-w-0 text-xs font-medium text-slate-600 dark:text-slate-300 truncate cursor-pointer\"\r\n (click)=\"segmentPopover.toggle($event)\"\r\n >\r\n {{\r\n getScopeSegmentLabel(\r\n scope.key,\r\n segmentIndex\r\n )\r\n }}\r\n </div>\r\n <mt-button\r\n type=\"button\"\r\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"!p-0.5 !min-w-0\"\r\n (onClick)=\"segmentPopover.toggle($event)\"\r\n ></mt-button>\r\n @if (isLast && canRemoveScopePath(scope.key)) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.trash-01\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"!p-0.5 !min-w-0\"\r\n (onClick)=\"removeScopePath(scope.key)\"\r\n ></mt-button>\r\n }\r\n </div>\r\n <p-popover\r\n #segmentPopover\r\n [style]=\"{ width: 'max-content' }\"\r\n [dismissable]=\"true\"\r\n >\r\n <div class=\"p-2\">\r\n <div\r\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\r\n >\r\n Select {{ getTokenLabel(segment.token) }}\r\n </div>\r\n @if (\r\n getScopeSegmentOptions(\r\n scope.key,\r\n segmentIndex\r\n ).length === 0\r\n ) {\r\n <div\r\n class=\"text-xs font-medium text-slate-400\"\r\n >\r\n No options\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of getScopeSegmentOptions(\r\n scope.key,\r\n segmentIndex\r\n );\r\n track option.contextKey\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.label\"\r\n [icon]=\"\r\n option.contextKey === segment.value\r\n ? 'general.check'\r\n : 'general.minus'\r\n \"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700\"\r\n (onClick)=\"\r\n onSetScopeSegmentValue(\r\n scope.key,\r\n segmentIndex,\r\n option.contextKey,\r\n segmentPopover\r\n )\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </p-popover>\r\n }\r\n @if (canAddNextScopeSegment(scope.key)) {\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n styleClass=\"flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90\"\r\n (onClick)=\"nextContextPopover.toggle($event)\"\r\n ></mt-button>\r\n <span class=\"text-xs text-slate-400\">\r\n Add segment\r\n </span>\r\n </div>\r\n }\r\n <!-- Popover OUTSIDE the @if block like formula-builder -->\r\n <p-popover\r\n #nextContextPopover\r\n [style]=\"{ width: 'max-content' }\"\r\n [dismissable]=\"true\"\r\n appendTo=\"body\"\r\n >\r\n <div class=\"p-2\">\r\n <div\r\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\r\n >\r\n Add Segment\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n token of getScopeNextTokens(scope.key);\r\n track token\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"getTokenLabel(token)\"\r\n size=\"small\"\r\n severity=\"secondary\"\r\n styleClass=\"w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700\"\r\n (onClick)=\"\r\n onAddScopeSegment(\r\n scope.key,\r\n token,\r\n nextContextPopover\r\n )\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </div>\r\n </p-popover>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Node List -->\r\n <div\r\n class=\"space-y-4 px-4 pb-4 [&_.cdk-drag-placeholder]:hidden flex-1 overflow-y-auto\"\r\n [id]=\"'toolbox-' + scope.key\"\r\n cdkDropList\r\n cdkDropListSortingDisabled\r\n [cdkDropListData]=\"getFilteredProperties(scope.key)\"\r\n [cdkDropListEnterPredicate]=\"noReturnPredicate\"\r\n >\r\n @if (scope.key === \"Current\") {\r\n <!-- Search Field (Sticky) -->\r\n <div\r\n class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1\"\r\n >\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"sticky top-0 bg-surface-0 py-3\">\r\n @if (isScopePropertiesLoading(scope.key)) {\r\n <p-skeleton height=\"2.5rem\" styleClass=\"rounded-lg\" />\r\n } @else {\r\n <mt-select-field\r\n class=\"w-full\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n placeholder=\"Select...\"\r\n [ngModel]=\"getScopeSelectedPropertyKey(scope.key)\"\r\n (ngModelChange)=\"\r\n setScopeSelectedProperty(scope.key, $event)\r\n \"\r\n [options]=\"getScopePropertyOptions(scope.key)\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n }\r\n\r\n @if (isScopePropertiesLoading(scope.key)) {\r\n <div class=\"space-y-4\">\r\n @for (i of [1, 2, 3, 4]; track i) {\r\n <p-skeleton height=\"3rem\" styleClass=\"rounded-lg\" />\r\n }\r\n </div>\r\n } @else {\r\n @for (\r\n node of getFilteredProperties(scope.key);\r\n track node.key\r\n ) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"node\"\r\n class=\"group cursor-move select-none flex items-center gap-3 py-3 px-3 rounded-lg border border-dashed border-surface-300 hover:bg-emphasis dark:border-surface-500 transition-colors\"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n class=\"col-span-12 min-h-27 w-full rounded-2xl bg-black/10 z-1\"\r\n ></div>\r\n <span\r\n class=\"flex-1 text-start text-base font-medium\"\r\n >{{ getPropertyLabel(node) }}</span\r\n >\r\n\r\n <mt-icon\r\n class=\"text-lg\"\r\n icon=\"general.menu-05\"\r\n ></mt-icon>\r\n </div>\r\n }\r\n\r\n @if (getFilteredProperties(scope.key).length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">\r\n @if (scope.key === \"Current\" && searchQuery()) {\r\n {{ t(\"no-data-found\") }}\r\n } @else if (scope.key !== \"Current\") {\r\n Select a property\r\n } @else if (\r\n getScopeProperties(scope.key).length === 0\r\n ) {\r\n No properties available\r\n } @else {\r\n All items are in use\r\n }\r\n </p>\r\n </div>\r\n }\r\n }\r\n </div>\r\n </p-tabpanel>\r\n }\r\n </p-tabpanels>\r\n </p-tabs>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div class=\"flex flex-col w-2/3 gap-4 h-full\">\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 flex items-center gap-3\">\r\n <mt-button\r\n icon=\"layout.layout-top\"\r\n [label]=\"t('add-section')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"addSection()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"general.eye\"\r\n [label]=\"t('preview')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openPreview()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('validation-rules')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"openValidationRules()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n <mt-button\r\n icon=\"finance.credit-card-plus\"\r\n [label]=\"t('reset')\"\r\n variant=\"outlined\"\r\n severity=\"primary\"\r\n (onClick)=\"resetFormConfiguration()\"\r\n [disabled]=\"isLoading()\"\r\n ></mt-button>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n @if (isLoading()) {\r\n <!-- Form Loading Skeleton -->\r\n @for (i of [1, 2]; track i) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div class=\"p-4 space-y-4\">\r\n <!-- Section header skeleton -->\r\n <div class=\"flex items-center justify-between\">\r\n <p-skeleton width=\"10rem\" height=\"1.5rem\" />\r\n <div class=\"flex gap-2\">\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n <p-skeleton width=\"2rem\" height=\"2rem\" shape=\"circle\" />\r\n </div>\r\n </div>\r\n <!-- Fields skeleton -->\r\n <div class=\"grid grid-cols-12 gap-4\">\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-6\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n <div class=\"col-span-12\">\r\n <p-skeleton height=\"4rem\" styleClass=\"rounded-lg\" />\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n } @else {\r\n @for (section of enrichedSections(); track section.id) {\r\n <mt-fb-section\r\n [section]=\"section\"\r\n [sectionsCount]=\"enrichedSections().length\"\r\n [allSections]=\"enrichedSections()\"\r\n (onFieldDrop)=\"drop($event)\"\r\n >\r\n </mt-fb-section>\r\n } @empty {\r\n <mt-card>\r\n <div class=\"h-27 p-4\">\r\n <div\r\n class=\"flex justify-center items-center gap-4 h-full border-1 border-primary rounded-xl bg-primary-50 text-primary\"\r\n >\r\n <span>{{ t(\"no-section\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [".cdk-drag{cursor:grab}.cdk-drag:active,.cdk-drag-preview{cursor:grabbing}.cdk-drag-placeholder{opacity:.5}.cdk-drop-list-dragging .cdk-drag{cursor:grabbing}\n"] }]
|
|
3183
|
+
}], ctorParameters: () => [], propDecorators: { popovers: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => Popover), { isSignal: true }] }] } });
|
|
2669
3184
|
|
|
2670
3185
|
/*
|
|
2671
3186
|
* Public API Surface of form-builder
|