@masterteam/form-builder 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,20 +1,23 @@
|
|
|
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
|
|
2
|
+
import { inject, Injectable, computed, input, signal, viewChild, Component, effect, output, DestroyRef, viewChildren } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormControl, ReactiveFormsModule, FormGroup, FormsModule } from '@angular/forms';
|
|
7
|
-
import * as
|
|
7
|
+
import * as i3 from 'primeng/tabs';
|
|
8
8
|
import { TabsModule } from 'primeng/tabs';
|
|
9
|
-
import * as
|
|
9
|
+
import * as i4 from 'primeng/skeleton';
|
|
10
10
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
11
|
+
import * as i5 from 'primeng/popover';
|
|
12
|
+
import { Popover, PopoverModule } from 'primeng/popover';
|
|
11
13
|
import { Button } from '@masterteam/components/button';
|
|
12
14
|
import { Card } from '@masterteam/components/card';
|
|
13
15
|
import { TextField } from '@masterteam/components/text-field';
|
|
16
|
+
import { SelectField } from '@masterteam/components/select-field';
|
|
14
17
|
import { ModalService } from '@masterteam/components/modal';
|
|
15
18
|
import { ConfirmationService } from '@masterteam/components/confirmation';
|
|
16
19
|
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
|
|
17
|
-
import * as
|
|
20
|
+
import * as i6 from '@angular/cdk/drag-drop';
|
|
18
21
|
import { CdkDrag, CdkDropList, CdkDragPlaceholder, DragDropModule } from '@angular/cdk/drag-drop';
|
|
19
22
|
import { DynamicField } from '@masterteam/forms/dynamic-field';
|
|
20
23
|
import { Icon } from '@masterteam/icons';
|
|
@@ -30,6 +33,7 @@ import { Tabs } from '@masterteam/components/tabs';
|
|
|
30
33
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
31
34
|
import { Table } from '@masterteam/components/table';
|
|
32
35
|
import { RadioCards } from '@masterteam/components/radio-cards';
|
|
36
|
+
import { of, map, catchError } from 'rxjs';
|
|
33
37
|
|
|
34
38
|
// ============================================================================
|
|
35
39
|
// Module Configuration Actions
|
|
@@ -257,6 +261,15 @@ let FormBuilderState = class FormBuilderState extends CrudStateBase {
|
|
|
257
261
|
static getModuleId(state) {
|
|
258
262
|
return state?.moduleId ?? null;
|
|
259
263
|
}
|
|
264
|
+
static getParentModuleType(state) {
|
|
265
|
+
return state?.parentModuleType ?? null;
|
|
266
|
+
}
|
|
267
|
+
static getParentModuleId(state) {
|
|
268
|
+
return state?.parentModuleId ?? null;
|
|
269
|
+
}
|
|
270
|
+
static getParentPath(state) {
|
|
271
|
+
return state?.parentPath ?? '';
|
|
272
|
+
}
|
|
260
273
|
static getProperties(state) {
|
|
261
274
|
return state?.properties ?? [];
|
|
262
275
|
}
|
|
@@ -394,7 +407,7 @@ let FormBuilderState = class FormBuilderState extends CrudStateBase {
|
|
|
394
407
|
const tempField = {
|
|
395
408
|
id: tempId,
|
|
396
409
|
sectionId: action.sectionId,
|
|
397
|
-
|
|
410
|
+
propertyKey: action.payload.propertyKey,
|
|
398
411
|
width: action.payload.width,
|
|
399
412
|
order: action.payload.order ?? 0,
|
|
400
413
|
hiddenInCreation: action.payload.hiddenInCreation,
|
|
@@ -862,6 +875,15 @@ __decorate([
|
|
|
862
875
|
__decorate([
|
|
863
876
|
Selector()
|
|
864
877
|
], FormBuilderState, "getModuleId", null);
|
|
878
|
+
__decorate([
|
|
879
|
+
Selector()
|
|
880
|
+
], FormBuilderState, "getParentModuleType", null);
|
|
881
|
+
__decorate([
|
|
882
|
+
Selector()
|
|
883
|
+
], FormBuilderState, "getParentModuleId", null);
|
|
884
|
+
__decorate([
|
|
885
|
+
Selector()
|
|
886
|
+
], FormBuilderState, "getParentPath", null);
|
|
865
887
|
__decorate([
|
|
866
888
|
Selector()
|
|
867
889
|
], FormBuilderState, "getProperties", null);
|
|
@@ -890,6 +912,9 @@ class FormBuilderFacade {
|
|
|
890
912
|
validations = select(FormBuilderState.getValidations);
|
|
891
913
|
moduleType = select(FormBuilderState.getModuleType);
|
|
892
914
|
moduleId = select(FormBuilderState.getModuleId);
|
|
915
|
+
parentModuleType = select(FormBuilderState.getParentModuleType);
|
|
916
|
+
parentModuleId = select(FormBuilderState.getParentModuleId);
|
|
917
|
+
parentPath = select(FormBuilderState.getParentPath);
|
|
893
918
|
// ============================================================================
|
|
894
919
|
// Loading Signals
|
|
895
920
|
// ============================================================================
|
|
@@ -1325,7 +1350,7 @@ class FBFieldConditions {
|
|
|
1325
1350
|
this.ref.close({ saved: false });
|
|
1326
1351
|
}
|
|
1327
1352
|
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"] }] });
|
|
1353
|
+
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
1354
|
}
|
|
1330
1355
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBFieldConditions, decorators: [{
|
|
1331
1356
|
type: Component,
|
|
@@ -1349,6 +1374,8 @@ class FBFieldForm {
|
|
|
1349
1374
|
initialData = input(null, ...(ngDevMode ? [{ debugName: "initialData" }] : []));
|
|
1350
1375
|
/** All sections with enriched fields (to get available fields for conditions) */
|
|
1351
1376
|
allSections = input([], ...(ngDevMode ? [{ debugName: "allSections" }] : []));
|
|
1377
|
+
mode = input('builder', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
1378
|
+
isManageProperties = computed(() => this.mode() === 'manageProperties', ...(ngDevMode ? [{ debugName: "isManageProperties" }] : []));
|
|
1352
1379
|
// UI State
|
|
1353
1380
|
submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : []));
|
|
1354
1381
|
deleting = signal(false, ...(ngDevMode ? [{ debugName: "deleting" }] : []));
|
|
@@ -1368,7 +1395,7 @@ class FBFieldForm {
|
|
|
1368
1395
|
if (currentField && field.id === currentField.id)
|
|
1369
1396
|
continue;
|
|
1370
1397
|
fields.push({
|
|
1371
|
-
key: field.
|
|
1398
|
+
key: field.propertyKey,
|
|
1372
1399
|
name: field.name,
|
|
1373
1400
|
type: field.type,
|
|
1374
1401
|
});
|
|
@@ -1379,6 +1406,8 @@ class FBFieldForm {
|
|
|
1379
1406
|
// Form
|
|
1380
1407
|
formControl = new FormControl();
|
|
1381
1408
|
showHideControl = new FormControl(false);
|
|
1409
|
+
isReadControl = new FormControl(true);
|
|
1410
|
+
isWriteControl = new FormControl(true);
|
|
1382
1411
|
formConfig = {
|
|
1383
1412
|
sections: [
|
|
1384
1413
|
{
|
|
@@ -1427,6 +1456,34 @@ class FBFieldForm {
|
|
|
1427
1456
|
},
|
|
1428
1457
|
],
|
|
1429
1458
|
};
|
|
1459
|
+
/** Simplified form config for manageProperties mode: only size */
|
|
1460
|
+
managePropertiesFormConfig = {
|
|
1461
|
+
sections: [
|
|
1462
|
+
{
|
|
1463
|
+
key: 'section-form',
|
|
1464
|
+
type: 'none',
|
|
1465
|
+
columns: 12,
|
|
1466
|
+
order: 1,
|
|
1467
|
+
fields: [
|
|
1468
|
+
new RadioCardsFieldConfig({
|
|
1469
|
+
key: 'size',
|
|
1470
|
+
label: this.transloco.translate('formBuilder.size'),
|
|
1471
|
+
placeholder: this.transloco.translate('formBuilder.size'),
|
|
1472
|
+
options: [
|
|
1473
|
+
{ id: 's', name: 'S' },
|
|
1474
|
+
{ id: 'm', name: 'M' },
|
|
1475
|
+
{ id: 'l', name: 'L' },
|
|
1476
|
+
],
|
|
1477
|
+
order: 1,
|
|
1478
|
+
size: 'small',
|
|
1479
|
+
}),
|
|
1480
|
+
],
|
|
1481
|
+
},
|
|
1482
|
+
],
|
|
1483
|
+
};
|
|
1484
|
+
activeFormConfig = computed(() => this.isManageProperties()
|
|
1485
|
+
? this.managePropertiesFormConfig
|
|
1486
|
+
: this.formConfig, ...(ngDevMode ? [{ debugName: "activeFormConfig" }] : []));
|
|
1430
1487
|
constructor() {
|
|
1431
1488
|
effect(() => {
|
|
1432
1489
|
const data = this.initialData();
|
|
@@ -1445,6 +1502,21 @@ class FBFieldForm {
|
|
|
1445
1502
|
// Set show/hide toggle based on existing value
|
|
1446
1503
|
this.showHideControl.patchValue(data.showConditionalDisplayFormula ?? false);
|
|
1447
1504
|
this.conditionalDisplayFormula.set(data.conditionalDisplayFormula ?? '');
|
|
1505
|
+
// isRead/isWrite controls
|
|
1506
|
+
this.isReadControl.patchValue(data.isRead !== false);
|
|
1507
|
+
this.isWriteControl.patchValue(data.isWrite !== false);
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
// Cascade: isWrite=true forces isRead=true
|
|
1511
|
+
this.isWriteControl.valueChanges.subscribe((isWrite) => {
|
|
1512
|
+
if (isWrite && !this.isReadControl.value) {
|
|
1513
|
+
this.isReadControl.patchValue(true, { emitEvent: false });
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
// Cascade: isRead=false forces isWrite=false
|
|
1517
|
+
this.isReadControl.valueChanges.subscribe((isRead) => {
|
|
1518
|
+
if (!isRead && this.isWriteControl.value) {
|
|
1519
|
+
this.isWriteControl.patchValue(false, { emitEvent: false });
|
|
1448
1520
|
}
|
|
1449
1521
|
});
|
|
1450
1522
|
}
|
|
@@ -1463,14 +1535,21 @@ class FBFieldForm {
|
|
|
1463
1535
|
};
|
|
1464
1536
|
const payload = {
|
|
1465
1537
|
width: widthMap[formValue.size] ?? '100',
|
|
1466
|
-
hiddenInCreation: formValue.hiddenInCreation ?? false,
|
|
1467
|
-
hiddenInEditForm: formValue.hiddenInEditForm ?? false,
|
|
1468
|
-
isRequired: formValue.isRequired ?? false,
|
|
1469
|
-
showConditionalDisplayFormula: this.showHideControl.value ?? false,
|
|
1470
|
-
conditionalDisplayFormula: this.showHideControl.value
|
|
1471
|
-
? this.conditionalDisplayFormula()
|
|
1472
|
-
: null,
|
|
1473
1538
|
};
|
|
1539
|
+
if (this.isManageProperties()) {
|
|
1540
|
+
payload['isRead'] = this.isReadControl.value ?? true;
|
|
1541
|
+
payload['isWrite'] = this.isWriteControl.value ?? true;
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
payload['hiddenInCreation'] = formValue.hiddenInCreation ?? false;
|
|
1545
|
+
payload['hiddenInEditForm'] = formValue.hiddenInEditForm ?? false;
|
|
1546
|
+
payload['isRequired'] = formValue.isRequired ?? false;
|
|
1547
|
+
payload['showConditionalDisplayFormula'] =
|
|
1548
|
+
this.showHideControl.value ?? false;
|
|
1549
|
+
payload['conditionalDisplayFormula'] = this.showHideControl.value
|
|
1550
|
+
? this.conditionalDisplayFormula()
|
|
1551
|
+
: null;
|
|
1552
|
+
}
|
|
1474
1553
|
this.submitting.set(true);
|
|
1475
1554
|
this.facade.updateField(sectionId, field.id, payload).subscribe({
|
|
1476
1555
|
next: () => this.ref.close(true),
|
|
@@ -1517,7 +1596,7 @@ class FBFieldForm {
|
|
|
1517
1596
|
});
|
|
1518
1597
|
}
|
|
1519
1598
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBFieldForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1520
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FBFieldForm, isStandalone: true, selector: "mt-fb-field-form", inputs: { sectionId: { classPropertyName: "sectionId", publicName: "sectionId", isSignal: true, isRequired: false, transformFunction: null }, initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, allSections: { classPropertyName: "allSections", publicName: "allSections", isSignal: true, isRequired: false, transformFunction: null } }, 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-2 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-dynamic-form
|
|
1599
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FBFieldForm, isStandalone: true, selector: "mt-fb-field-form", inputs: { sectionId: { classPropertyName: "sectionId", publicName: "sectionId", isSignal: true, isRequired: false, transformFunction: null }, initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, allSections: { classPropertyName: "allSections", publicName: "allSections", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, 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-2 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-dynamic-form\r\n [formConfig]=\"activeFormConfig()\"\r\n [formControl]=\"formControl\"\r\n >\r\n </mt-dynamic-form>\r\n\r\n @if (isManageProperties()) {\r\n <!-- isRead Toggle -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('is-read')\"\r\n [descriptionCard]=\"t('is-read-description')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"isReadControl\"\r\n ></mt-toggle-field>\r\n\r\n <!-- isWrite Toggle -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('is-write')\"\r\n [descriptionCard]=\"t('is-write-description')\"\r\n icon=\"general.edit-02\"\r\n [formControl]=\"isWriteControl\"\r\n ></mt-toggle-field>\r\n } @else {\r\n <!-- Show/Hide Toggle with Set Conditions -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-description')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n class=\"mt-3\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('set-conditions')\"\r\n size=\"small\"\r\n (onClick)=\"onSetConditions()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n @if (initialData() && !isManageProperties()) {\r\n <mt-button\r\n [tooltip]=\"t('delete')\"\r\n severity=\"danger\"\r\n [variant]=\"'outlined'\"\r\n icon=\"general.trash-01\"\r\n [loading]=\"deleting()\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onDelete($event)\"\r\n class=\"me-auto\"\r\n ></mt-button>\r\n }\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting() || deleting()\"\r\n (onClick)=\"onCancel()\"\r\n >\r\n </mt-button>\r\n <mt-button\r\n [disabled]=\"!formControl.valid || deleting()\"\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n >\r\n </mt-button>\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: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig"] }, { 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: ToggleField, selector: "mt-toggle-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }] });
|
|
1521
1600
|
}
|
|
1522
1601
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBFieldForm, decorators: [{
|
|
1523
1602
|
type: Component,
|
|
@@ -1527,8 +1606,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
1527
1606
|
DynamicForm,
|
|
1528
1607
|
Button,
|
|
1529
1608
|
ToggleField,
|
|
1530
|
-
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-2 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-dynamic-form
|
|
1531
|
-
}], ctorParameters: () => [], propDecorators: { sectionId: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionId", required: false }] }], initialData: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialData", required: false }] }], allSections: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSections", required: false }] }] } });
|
|
1609
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-2 h-full overflow-y-auto pb-10 flex flex-col gap-4\">\r\n <mt-dynamic-form\r\n [formConfig]=\"activeFormConfig()\"\r\n [formControl]=\"formControl\"\r\n >\r\n </mt-dynamic-form>\r\n\r\n @if (isManageProperties()) {\r\n <!-- isRead Toggle -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('is-read')\"\r\n [descriptionCard]=\"t('is-read-description')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"isReadControl\"\r\n ></mt-toggle-field>\r\n\r\n <!-- isWrite Toggle -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('is-write')\"\r\n [descriptionCard]=\"t('is-write-description')\"\r\n icon=\"general.edit-02\"\r\n [formControl]=\"isWriteControl\"\r\n ></mt-toggle-field>\r\n } @else {\r\n <!-- Show/Hide Toggle with Set Conditions -->\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-description')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n class=\"mt-3\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('set-conditions')\"\r\n size=\"small\"\r\n (onClick)=\"onSetConditions()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n @if (initialData() && !isManageProperties()) {\r\n <mt-button\r\n [tooltip]=\"t('delete')\"\r\n severity=\"danger\"\r\n [variant]=\"'outlined'\"\r\n icon=\"general.trash-01\"\r\n [loading]=\"deleting()\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onDelete($event)\"\r\n class=\"me-auto\"\r\n ></mt-button>\r\n }\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting() || deleting()\"\r\n (onClick)=\"onCancel()\"\r\n >\r\n </mt-button>\r\n <mt-button\r\n [disabled]=\"!formControl.valid || deleting()\"\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n >\r\n </mt-button>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1610
|
+
}], ctorParameters: () => [], propDecorators: { sectionId: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionId", required: false }] }], initialData: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialData", required: false }] }], allSections: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSections", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }] } });
|
|
1532
1611
|
|
|
1533
1612
|
class FBSectionForm {
|
|
1534
1613
|
transloco = inject(TranslocoService);
|
|
@@ -1650,6 +1729,8 @@ class FBSection {
|
|
|
1650
1729
|
sectionsCount = input(0, ...(ngDevMode ? [{ debugName: "sectionsCount" }] : []));
|
|
1651
1730
|
/** All sections - used to get available fields for condition formulas */
|
|
1652
1731
|
allSections = input([], ...(ngDevMode ? [{ debugName: "allSections" }] : []));
|
|
1732
|
+
/** Operating mode: 'builder' (full editor) or 'manageProperties' (read/write only). */
|
|
1733
|
+
mode = input('builder', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
1653
1734
|
// Outputs - only keep drag/drop since it needs coordination with parent drop list group
|
|
1654
1735
|
onFieldDrop = output();
|
|
1655
1736
|
// Computed
|
|
@@ -1659,6 +1740,16 @@ class FBSection {
|
|
|
1659
1740
|
return section.name[lang] ?? section.name['en'];
|
|
1660
1741
|
}, ...(ngDevMode ? [{ debugName: "sectionName" }] : []));
|
|
1661
1742
|
fields = computed(() => this.section().fields, ...(ngDevMode ? [{ debugName: "fields" }] : []));
|
|
1743
|
+
/** In manageProperties mode, isRead=false fields go to the end */
|
|
1744
|
+
displayFields = computed(() => {
|
|
1745
|
+
const raw = this.fields();
|
|
1746
|
+
if (this.mode() !== 'manageProperties')
|
|
1747
|
+
return raw;
|
|
1748
|
+
const readable = raw.filter((f) => f.isRead !== false);
|
|
1749
|
+
const unreadable = raw.filter((f) => f.isRead === false);
|
|
1750
|
+
return [...readable, ...unreadable];
|
|
1751
|
+
}, ...(ngDevMode ? [{ debugName: "displayFields" }] : []));
|
|
1752
|
+
isManageProperties = computed(() => this.mode() === 'manageProperties', ...(ngDevMode ? [{ debugName: "isManageProperties" }] : []));
|
|
1662
1753
|
// UI State
|
|
1663
1754
|
expanded = signal(true, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
1664
1755
|
// Form groups cache for dynamic fields
|
|
@@ -1741,6 +1832,7 @@ class FBSection {
|
|
|
1741
1832
|
initialData: field,
|
|
1742
1833
|
sectionId: this.section().id,
|
|
1743
1834
|
allSections: this.allSections(),
|
|
1835
|
+
mode: this.mode(),
|
|
1744
1836
|
},
|
|
1745
1837
|
});
|
|
1746
1838
|
}
|
|
@@ -1782,7 +1874,7 @@ class FBSection {
|
|
|
1782
1874
|
return typeMap[field.type] ?? 'text';
|
|
1783
1875
|
}
|
|
1784
1876
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1785
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FBSection, isStandalone: true, selector: "mt-fb-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, sectionsCount: { classPropertyName: "sectionsCount", publicName: "sectionsCount", isSignal: true, isRequired: false, transformFunction: null }, allSections: { classPropertyName: "allSections", publicName: "allSections", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onFieldDrop: "onFieldDrop" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex flex-col\">\r\n <div\r\n class=\"flex justify-between items-center bg-primary py-3 px-3 text-primary-contrast rounded-xl\"\r\n >\r\n <mt-icon\r\n class=\"text-2xl cursor-pointer transition-[rotate]\"\r\n [class.rotate-180]=\"expanded()\"\r\n icon=\"arrow.chevron-down\"\r\n (click)=\"toggleExpanded()\"\r\n ></mt-icon>\r\n\r\n <div class=\"flex gap-3 items-center\">\r\n <span class=\"font-semibold\">\r\n {{ sectionName() }}\r\n </span>\r\n <mt-icon\r\n class=\"text-lg cursor-pointer\"\r\n icon=\"general.edit-02\"\r\n (click)=\"editSection($event)\"\r\n [mtTooltip]=\"t('edit-section')\"\r\n ></mt-icon>\r\n </div>\r\n <span></span>\r\n </div>\r\n @if (expanded()) {\r\n <div\r\n cdkDropList\r\n [id]=\"section().id\"\r\n cdkDropListOrientation=\"mixed\"\r\n [cdkDropListData]=\"fields()\"\r\n (cdkDropListDropped)=\"onDrop($event)\"\r\n class=\"grid grid-cols-12 gap-4 relative py-4\"\r\n [class.min-h-27]=\"fields().length === 0\"\r\n >\r\n @for (field of fields(); track field.id) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"field\"\r\n [cdkDragDisabled]=\"field._pending || field._deleting\"\r\n [class]=\"\r\n getFieldColSpan(field) + ' cursor-grab active:cursor-grabbing'\r\n \"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n [class]=\"\r\n 'h-full min-h-27 bg-black/10 rounded-2xl ' +\r\n getFieldColSpan(field)\r\n \"\r\n ></div>\r\n @if (field._pending) {\r\n <!-- Skeleton for pending field -->\r\n <mt-card class=\"h-full animate-pulse\">\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1 space-y-3\">\r\n <div class=\"h-4 bg-surface-200 rounded w-1/3\"></div>\r\n <div class=\"h-10 bg-surface-200 rounded\"></div>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n </div>\r\n </div>\r\n </mt-card>\r\n } @else {\r\n <mt-card\r\n class=\"h-full relative\"\r\n [class.opacity-50]=\"field._deleting\"\r\n >\r\n @if (field._deleting) {\r\n <div\r\n class=\"absolute inset-0 flex items-center justify-center bg-white/50 rounded-xl z-10\"\r\n >\r\n <div\r\n class=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\"\r\n ></div>\r\n </div>\r\n }\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1\">\r\n <form [formGroup]=\"getFormGroup(field)\">\r\n <div class=\"flex items-center gap-4 mb-2\">\r\n <div class=\"font-semibold\">\r\n {{ field.name }}\r\n </div>\r\n <!-- <div class=\"flex gap-2\">\r\n <mt-icon\r\n class=\"text-lg cursor-pointer text-primary\"\r\n icon=\"general.edit-02\"\r\n (click)=\"editField(field)\"\r\n [mtTooltip]=\"t('edit-field')\"\r\n ></mt-icon>\r\n <mt-icon\r\n class=\"text-lg cursor-pointer text-red-700\"\r\n icon=\"general.trash-01\"\r\n (click)=\"removeField($event, field)\"\r\n [mtTooltip]=\"t('remove-field')\"\r\n ></mt-icon>\r\n </div> -->\r\n </div>\r\n\r\n <mt-dynamic-field\r\n [fieldName]=\"field.name\"\r\n [fieldConfig]=\"{\r\n readonly: true,\r\n type: getFieldType(field),\r\n }\"\r\n />\r\n </form>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.settings-01\"\r\n [tooltip]=\"t('edit-field')\"\r\n [variant]=\"'outlined'\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"editField(field)\"\r\n ></mt-button>\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.trash-01\"\r\n severity=\"danger\"\r\n [variant]=\"'outlined'\"\r\n [tooltip]=\"t('remove-field')\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"removeField($event, field)\"\r\n ></mt-button>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n } @empty {\r\n <mt-card class=\"absolute inset-0 top-4 h-full\" paddingless>\r\n <div class=\"size-full 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 <mt-icon icon=\"editor.move\" class=\"text-3xl\" />\r\n <span>{{ t(\"drag-from-the-side\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ 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: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: 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: 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: CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: DynamicField, selector: "mt-dynamic-field", inputs: ["fieldConfig", "fieldName"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }] });
|
|
1877
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FBSection, isStandalone: true, selector: "mt-fb-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, sectionsCount: { classPropertyName: "sectionsCount", publicName: "sectionsCount", isSignal: true, isRequired: false, transformFunction: null }, allSections: { classPropertyName: "allSections", publicName: "allSections", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onFieldDrop: "onFieldDrop" }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex flex-col\">\r\n <div\r\n class=\"flex justify-between items-center bg-primary py-3 px-3 text-primary-contrast rounded-xl\"\r\n >\r\n <mt-icon\r\n class=\"text-2xl cursor-pointer transition-[rotate]\"\r\n [class.rotate-180]=\"expanded()\"\r\n icon=\"arrow.chevron-down\"\r\n (click)=\"toggleExpanded()\"\r\n ></mt-icon>\r\n\r\n <div class=\"flex gap-3 items-center\">\r\n <span class=\"font-semibold\">\r\n {{ sectionName() }}\r\n </span>\r\n @if (!isManageProperties()) {\r\n <mt-icon\r\n class=\"text-lg cursor-pointer\"\r\n icon=\"general.edit-02\"\r\n (click)=\"editSection($event)\"\r\n [mtTooltip]=\"t('edit-section')\"\r\n ></mt-icon>\r\n }\r\n </div>\r\n <span></span>\r\n </div>\r\n @if (expanded()) {\r\n <div\r\n cdkDropList\r\n [id]=\"section().id\"\r\n cdkDropListOrientation=\"mixed\"\r\n [cdkDropListData]=\"displayFields()\"\r\n [cdkDropListDisabled]=\"isManageProperties()\"\r\n (cdkDropListDropped)=\"onDrop($event)\"\r\n class=\"grid grid-cols-12 gap-4 relative py-4\"\r\n [class.min-h-27]=\"displayFields().length === 0\"\r\n >\r\n @for (field of displayFields(); track field.id) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"field\"\r\n [cdkDragDisabled]=\"\r\n isManageProperties() || field._pending || field._deleting\r\n \"\r\n [class]=\"\r\n getFieldColSpan(field) +\r\n (isManageProperties()\r\n ? ''\r\n : ' cursor-grab active:cursor-grabbing')\r\n \"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n [class]=\"\r\n 'h-full min-h-27 bg-black/10 rounded-2xl ' +\r\n getFieldColSpan(field)\r\n \"\r\n ></div>\r\n @if (field._pending) {\r\n <!-- Skeleton for pending field -->\r\n <mt-card class=\"h-full animate-pulse\">\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1 space-y-3\">\r\n <div class=\"h-4 bg-surface-200 rounded w-1/3\"></div>\r\n <div class=\"h-10 bg-surface-200 rounded\"></div>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n </div>\r\n </div>\r\n </mt-card>\r\n } @else {\r\n <mt-card\r\n class=\"h-full relative transition-all duration-200\"\r\n [class.opacity-50]=\"field._deleting\"\r\n [class.opacity-40]=\"\r\n isManageProperties() && field.isRead === false\r\n \"\r\n [class.blur-[1px]]=\"\r\n isManageProperties() && field.isRead === false\r\n \"\r\n >\r\n @if (field._deleting) {\r\n <div\r\n class=\"absolute inset-0 flex items-center justify-center bg-white/50 rounded-xl z-10\"\r\n >\r\n <div\r\n class=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\"\r\n ></div>\r\n </div>\r\n }\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1\">\r\n <form [formGroup]=\"getFormGroup(field)\">\r\n <div class=\"flex items-center gap-4 mb-2\">\r\n <div class=\"font-semibold\">\r\n {{ field.name }}\r\n </div>\r\n @if (isManageProperties()) {\r\n <div class=\"flex items-center gap-1.5 ms-auto\">\r\n @if (field.isRead === false) {\r\n <span\r\n class=\"text-xs font-medium text-red-500 bg-red-50 px-1.5 py-0.5 rounded\"\r\n >\r\n {{ t(\"hidden\") }}\r\n </span>\r\n } @else if (field.isWrite === false) {\r\n <span\r\n class=\"text-xs font-medium text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded\"\r\n >\r\n {{ t(\"read-only\") }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <mt-dynamic-field\r\n [fieldName]=\"field.name\"\r\n [fieldConfig]=\"{\r\n readonly: true,\r\n type: getFieldType(field),\r\n }\"\r\n />\r\n </form>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.settings-01\"\r\n [tooltip]=\"t('edit-field')\"\r\n [variant]=\"'outlined'\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"editField(field)\"\r\n ></mt-button>\r\n @if (!isManageProperties()) {\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.trash-01\"\r\n severity=\"danger\"\r\n [variant]=\"'outlined'\"\r\n [tooltip]=\"t('remove-field')\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"removeField($event, field)\"\r\n ></mt-button>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n } @empty {\r\n <mt-card class=\"absolute inset-0 top-4 h-full\" paddingless>\r\n <div class=\"size-full 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 <mt-icon icon=\"editor.move\" class=\"text-3xl\" />\r\n <span>{{ t(\"drag-from-the-side\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ 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: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "directive", type: 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: 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: CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: DynamicField, selector: "mt-dynamic-field", inputs: ["fieldConfig", "fieldName"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }] });
|
|
1786
1878
|
}
|
|
1787
1879
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBSection, decorators: [{
|
|
1788
1880
|
type: Component,
|
|
@@ -1797,8 +1889,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
1797
1889
|
DynamicField,
|
|
1798
1890
|
ReactiveFormsModule,
|
|
1799
1891
|
Tooltip,
|
|
1800
|
-
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex flex-col\">\r\n <div\r\n class=\"flex justify-between items-center bg-primary py-3 px-3 text-primary-contrast rounded-xl\"\r\n >\r\n <mt-icon\r\n class=\"text-2xl cursor-pointer transition-[rotate]\"\r\n [class.rotate-180]=\"expanded()\"\r\n icon=\"arrow.chevron-down\"\r\n (click)=\"toggleExpanded()\"\r\n ></mt-icon>\r\n\r\n <div class=\"flex gap-3 items-center\">\r\n <span class=\"font-semibold\">\r\n {{ sectionName() }}\r\n </span>\r\n <mt-icon\r\n
|
|
1801
|
-
}], ctorParameters: () => [], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], sectionsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionsCount", required: false }] }], allSections: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSections", required: false }] }], onFieldDrop: [{ type: i0.Output, args: ["onFieldDrop"] }] } });
|
|
1892
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div class=\"flex flex-col\">\r\n <div\r\n class=\"flex justify-between items-center bg-primary py-3 px-3 text-primary-contrast rounded-xl\"\r\n >\r\n <mt-icon\r\n class=\"text-2xl cursor-pointer transition-[rotate]\"\r\n [class.rotate-180]=\"expanded()\"\r\n icon=\"arrow.chevron-down\"\r\n (click)=\"toggleExpanded()\"\r\n ></mt-icon>\r\n\r\n <div class=\"flex gap-3 items-center\">\r\n <span class=\"font-semibold\">\r\n {{ sectionName() }}\r\n </span>\r\n @if (!isManageProperties()) {\r\n <mt-icon\r\n class=\"text-lg cursor-pointer\"\r\n icon=\"general.edit-02\"\r\n (click)=\"editSection($event)\"\r\n [mtTooltip]=\"t('edit-section')\"\r\n ></mt-icon>\r\n }\r\n </div>\r\n <span></span>\r\n </div>\r\n @if (expanded()) {\r\n <div\r\n cdkDropList\r\n [id]=\"section().id\"\r\n cdkDropListOrientation=\"mixed\"\r\n [cdkDropListData]=\"displayFields()\"\r\n [cdkDropListDisabled]=\"isManageProperties()\"\r\n (cdkDropListDropped)=\"onDrop($event)\"\r\n class=\"grid grid-cols-12 gap-4 relative py-4\"\r\n [class.min-h-27]=\"displayFields().length === 0\"\r\n >\r\n @for (field of displayFields(); track field.id) {\r\n <div\r\n cdkDrag\r\n [cdkDragData]=\"field\"\r\n [cdkDragDisabled]=\"\r\n isManageProperties() || field._pending || field._deleting\r\n \"\r\n [class]=\"\r\n getFieldColSpan(field) +\r\n (isManageProperties()\r\n ? ''\r\n : ' cursor-grab active:cursor-grabbing')\r\n \"\r\n >\r\n <div\r\n *cdkDragPlaceholder\r\n [class]=\"\r\n 'h-full min-h-27 bg-black/10 rounded-2xl ' +\r\n getFieldColSpan(field)\r\n \"\r\n ></div>\r\n @if (field._pending) {\r\n <!-- Skeleton for pending field -->\r\n <mt-card class=\"h-full animate-pulse\">\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1 space-y-3\">\r\n <div class=\"h-4 bg-surface-200 rounded w-1/3\"></div>\r\n <div class=\"h-10 bg-surface-200 rounded\"></div>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n <div class=\"h-8 w-8 bg-surface-200 rounded\"></div>\r\n </div>\r\n </div>\r\n </mt-card>\r\n } @else {\r\n <mt-card\r\n class=\"h-full relative transition-all duration-200\"\r\n [class.opacity-50]=\"field._deleting\"\r\n [class.opacity-40]=\"\r\n isManageProperties() && field.isRead === false\r\n \"\r\n [class.blur-[1px]]=\"\r\n isManageProperties() && field.isRead === false\r\n \"\r\n >\r\n @if (field._deleting) {\r\n <div\r\n class=\"absolute inset-0 flex items-center justify-center bg-white/50 rounded-xl z-10\"\r\n >\r\n <div\r\n class=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\"\r\n ></div>\r\n </div>\r\n }\r\n <div class=\"flex gap-2\">\r\n <div class=\"flex-1\">\r\n <form [formGroup]=\"getFormGroup(field)\">\r\n <div class=\"flex items-center gap-4 mb-2\">\r\n <div class=\"font-semibold\">\r\n {{ field.name }}\r\n </div>\r\n @if (isManageProperties()) {\r\n <div class=\"flex items-center gap-1.5 ms-auto\">\r\n @if (field.isRead === false) {\r\n <span\r\n class=\"text-xs font-medium text-red-500 bg-red-50 px-1.5 py-0.5 rounded\"\r\n >\r\n {{ t(\"hidden\") }}\r\n </span>\r\n } @else if (field.isWrite === false) {\r\n <span\r\n class=\"text-xs font-medium text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded\"\r\n >\r\n {{ t(\"read-only\") }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <mt-dynamic-field\r\n [fieldName]=\"field.name\"\r\n [fieldConfig]=\"{\r\n readonly: true,\r\n type: getFieldType(field),\r\n }\"\r\n />\r\n </form>\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.settings-01\"\r\n [tooltip]=\"t('edit-field')\"\r\n [variant]=\"'outlined'\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"editField(field)\"\r\n ></mt-button>\r\n @if (!isManageProperties()) {\r\n <mt-button\r\n size=\"small\"\r\n icon=\"general.trash-01\"\r\n severity=\"danger\"\r\n [variant]=\"'outlined'\"\r\n [tooltip]=\"t('remove-field')\"\r\n [disabled]=\"field._deleting\"\r\n (onClick)=\"removeField($event, field)\"\r\n ></mt-button>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n } @empty {\r\n <mt-card class=\"absolute inset-0 top-4 h-full\" paddingless>\r\n <div class=\"size-full 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 <mt-icon icon=\"editor.move\" class=\"text-3xl\" />\r\n <span>{{ t(\"drag-from-the-side\") }}</span>\r\n </div>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1893
|
+
}], ctorParameters: () => [], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], sectionsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionsCount", required: false }] }], allSections: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSections", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], onFieldDrop: [{ type: i0.Output, args: ["onFieldDrop"] }] } });
|
|
1802
1894
|
|
|
1803
1895
|
class FBPreviewForm {
|
|
1804
1896
|
modalService = inject(ModalService);
|
|
@@ -1853,7 +1945,7 @@ class FBPreviewForm {
|
|
|
1853
1945
|
fields: visibleFields.map((field, fieldIndex) => {
|
|
1854
1946
|
const colSpan = this.getColSpan(field.width);
|
|
1855
1947
|
return {
|
|
1856
|
-
key: `field_${field.
|
|
1948
|
+
key: `field_${field.propertyKey}`,
|
|
1857
1949
|
label: field.name,
|
|
1858
1950
|
type: this.mapFieldType(field.type),
|
|
1859
1951
|
colSpan,
|
|
@@ -2337,7 +2429,7 @@ class FBValidationRuleForm {
|
|
|
2337
2429
|
this.ref.close(false);
|
|
2338
2430
|
}
|
|
2339
2431
|
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"] }] });
|
|
2432
|
+
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
2433
|
}
|
|
2342
2434
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FBValidationRuleForm, decorators: [{
|
|
2343
2435
|
type: Component,
|
|
@@ -2364,7 +2456,7 @@ class FBValidationRules {
|
|
|
2364
2456
|
propertiesMap = computed(() => {
|
|
2365
2457
|
const map = new Map();
|
|
2366
2458
|
for (const prop of this.properties()) {
|
|
2367
|
-
map.set(prop.
|
|
2459
|
+
map.set(prop.key, prop);
|
|
2368
2460
|
}
|
|
2369
2461
|
return map;
|
|
2370
2462
|
}, ...(ngDevMode ? [{ debugName: "propertiesMap" }] : []));
|
|
@@ -2375,15 +2467,10 @@ class FBValidationRules {
|
|
|
2375
2467
|
const fields = [];
|
|
2376
2468
|
for (const section of sections) {
|
|
2377
2469
|
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}`);
|
|
2470
|
+
const prop = field.property ?? propsMap.get(field.propertyKey);
|
|
2471
|
+
const name = this.resolvePropertyName(prop, field.propertyKey, lang);
|
|
2385
2472
|
fields.push({
|
|
2386
|
-
key:
|
|
2473
|
+
key: field.propertyKey,
|
|
2387
2474
|
name,
|
|
2388
2475
|
type: prop?.viewType || 'text',
|
|
2389
2476
|
});
|
|
@@ -2438,6 +2525,24 @@ class FBValidationRules {
|
|
|
2438
2525
|
this.activeLang.set(lang ?? 'en');
|
|
2439
2526
|
});
|
|
2440
2527
|
}
|
|
2528
|
+
resolvePropertyName(property, fallbackKey, lang) {
|
|
2529
|
+
if (!property) {
|
|
2530
|
+
return `Property ${fallbackKey}`;
|
|
2531
|
+
}
|
|
2532
|
+
const name = property?.name;
|
|
2533
|
+
if (typeof name === 'string') {
|
|
2534
|
+
return name;
|
|
2535
|
+
}
|
|
2536
|
+
const display = name?.['display'];
|
|
2537
|
+
if (typeof display === 'string' && display.trim().length > 0) {
|
|
2538
|
+
return display;
|
|
2539
|
+
}
|
|
2540
|
+
const localized = name?.[lang] ?? name?.['en'] ?? name?.['ar'];
|
|
2541
|
+
if (typeof localized === 'string' && localized.trim().length > 0) {
|
|
2542
|
+
return localized;
|
|
2543
|
+
}
|
|
2544
|
+
return property?.key ?? `Property ${fallbackKey}`;
|
|
2545
|
+
}
|
|
2441
2546
|
openCreate() {
|
|
2442
2547
|
this.modalService.openModal(FBValidationRuleForm, 'drawer', {
|
|
2443
2548
|
header: this.transloco.translate('formBuilder.add-validation-rule'),
|
|
@@ -2483,29 +2588,203 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
2483
2588
|
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
2589
|
}], ctorParameters: () => [] });
|
|
2485
2590
|
|
|
2591
|
+
class FormBuilderContextService {
|
|
2592
|
+
http = inject(HttpClient);
|
|
2593
|
+
baseUrl = 'context';
|
|
2594
|
+
/**
|
|
2595
|
+
* Resolves context data including navigation paths and properties.
|
|
2596
|
+
* No caching - always fetches fresh data.
|
|
2597
|
+
*/
|
|
2598
|
+
resolveContext(contextKey, options = {}) {
|
|
2599
|
+
if (!contextKey)
|
|
2600
|
+
return of(null);
|
|
2601
|
+
return this.http
|
|
2602
|
+
.post(`${this.baseUrl}/resolve`, {
|
|
2603
|
+
contextKey,
|
|
2604
|
+
displayTags: options.displayTags,
|
|
2605
|
+
propertyFlags: options.propertyFlags,
|
|
2606
|
+
matchAllTags: options.matchAllTags,
|
|
2607
|
+
includeVirtual: options.includeVirtual,
|
|
2608
|
+
includeNavigationPaths: options.includeNavigationPaths,
|
|
2609
|
+
includeConnectionMetadata: options.includeConnectionMetadata,
|
|
2610
|
+
includeRelatedContextValues: options.includeRelatedContextValues,
|
|
2611
|
+
relatedContextsToInclude: options.relatedContextsToInclude,
|
|
2612
|
+
})
|
|
2613
|
+
.pipe(map((response) => this.normalizeResponse(response)), catchError((error) => {
|
|
2614
|
+
console.warn('Failed to resolve context:', error);
|
|
2615
|
+
return of(null);
|
|
2616
|
+
}));
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* Gets properties for a context.
|
|
2620
|
+
* No caching - always fetches fresh data.
|
|
2621
|
+
*/
|
|
2622
|
+
getProperties(contextKey, options = {}) {
|
|
2623
|
+
if (!contextKey)
|
|
2624
|
+
return of([]);
|
|
2625
|
+
return this.resolveContext(contextKey, options).pipe(map((resolved) => resolved?.properties ?? []));
|
|
2626
|
+
}
|
|
2627
|
+
normalizeResponse(response) {
|
|
2628
|
+
const payload = response?.data ?? response;
|
|
2629
|
+
if (!payload)
|
|
2630
|
+
return null;
|
|
2631
|
+
const properties = this.normalizeProperties(payload.properties ?? []);
|
|
2632
|
+
return {
|
|
2633
|
+
...payload,
|
|
2634
|
+
properties,
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
normalizeProperties(raw) {
|
|
2638
|
+
return (raw ?? [])
|
|
2639
|
+
.map((prop) => {
|
|
2640
|
+
const key = prop?.key ?? prop?.propertyKey;
|
|
2641
|
+
if (!key)
|
|
2642
|
+
return null;
|
|
2643
|
+
const name = prop?.name ?? prop?.displayName ?? key;
|
|
2644
|
+
return {
|
|
2645
|
+
...prop,
|
|
2646
|
+
key,
|
|
2647
|
+
name,
|
|
2648
|
+
};
|
|
2649
|
+
})
|
|
2650
|
+
.filter((prop) => Boolean(prop));
|
|
2651
|
+
}
|
|
2652
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2653
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, providedIn: 'root' });
|
|
2654
|
+
}
|
|
2655
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilderContextService, decorators: [{
|
|
2656
|
+
type: Injectable,
|
|
2657
|
+
args: [{ providedIn: 'root' }]
|
|
2658
|
+
}] });
|
|
2659
|
+
|
|
2660
|
+
const SCOPE_ORDER = [
|
|
2661
|
+
'Current',
|
|
2662
|
+
'Host',
|
|
2663
|
+
'Parent',
|
|
2664
|
+
'Ancestors',
|
|
2665
|
+
'Children',
|
|
2666
|
+
'Descendants',
|
|
2667
|
+
'Related',
|
|
2668
|
+
'Siblings',
|
|
2669
|
+
];
|
|
2670
|
+
const PATH_TOKENS = [
|
|
2671
|
+
'Parent',
|
|
2672
|
+
'Ancestors',
|
|
2673
|
+
'Children',
|
|
2674
|
+
'Descendants',
|
|
2675
|
+
'Related',
|
|
2676
|
+
'Siblings',
|
|
2677
|
+
'Host',
|
|
2678
|
+
];
|
|
2679
|
+
const SCOPE_LABELS = {
|
|
2680
|
+
Current: 'Current',
|
|
2681
|
+
Host: 'Host',
|
|
2682
|
+
Parent: 'Parent',
|
|
2683
|
+
Ancestors: 'Ancestors',
|
|
2684
|
+
Children: 'Children',
|
|
2685
|
+
Descendants: 'Descendants',
|
|
2686
|
+
Related: 'Related',
|
|
2687
|
+
Siblings: 'Siblings',
|
|
2688
|
+
};
|
|
2689
|
+
// ============================================================================
|
|
2690
|
+
// Component
|
|
2691
|
+
// ============================================================================
|
|
2486
2692
|
class FormBuilder {
|
|
2487
2693
|
modalService = inject(ModalService);
|
|
2488
2694
|
confirmationService = inject(ConfirmationService);
|
|
2489
2695
|
translocoService = inject(TranslocoService);
|
|
2490
2696
|
facade = inject(FormBuilderFacade);
|
|
2697
|
+
contextService = inject(FormBuilderContextService);
|
|
2698
|
+
popovers = viewChildren(Popover, ...(ngDevMode ? [{ debugName: "popovers" }] : []));
|
|
2699
|
+
/** Extra CSS class(es) applied to the main canvas wrapper div. */
|
|
2700
|
+
canvasStyleClass = input('', ...(ngDevMode ? [{ debugName: "canvasStyleClass" }] : []));
|
|
2701
|
+
/** Operating mode: 'builder' (full editor) or 'manageProperties' (read/write permissions only). */
|
|
2702
|
+
mode = input('builder', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
2491
2703
|
dialogRef;
|
|
2492
|
-
//
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
// State from facade
|
|
2704
|
+
// ============================================================================
|
|
2705
|
+
// Facade State
|
|
2706
|
+
// ============================================================================
|
|
2496
2707
|
sections = this.facade.sections;
|
|
2497
2708
|
properties = this.facade.properties;
|
|
2498
2709
|
isLoading = this.facade.isLoadingFormConfiguration;
|
|
2499
2710
|
error = this.facade.formConfigurationError;
|
|
2500
|
-
|
|
2711
|
+
moduleType = this.facade.moduleType;
|
|
2712
|
+
moduleId = this.facade.moduleId;
|
|
2713
|
+
parentPath = this.facade.parentPath;
|
|
2714
|
+
// ============================================================================
|
|
2715
|
+
// UI State (Simple Signals)
|
|
2716
|
+
// ============================================================================
|
|
2717
|
+
activeScope = signal('Current', ...(ngDevMode ? [{ debugName: "activeScope" }] : []));
|
|
2718
|
+
searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
|
|
2719
|
+
isContextLoading = signal(false, ...(ngDevMode ? [{ debugName: "isContextLoading" }] : []));
|
|
2720
|
+
// Initial context data (from first load)
|
|
2721
|
+
initialContext = signal(null, ...(ngDevMode ? [{ debugName: "initialContext" }] : []));
|
|
2722
|
+
currentProperties = signal([], ...(ngDevMode ? [{ debugName: "currentProperties" }] : []));
|
|
2723
|
+
// Non-current scope state (reset on tab change)
|
|
2724
|
+
scopeLoading = signal(false, ...(ngDevMode ? [{ debugName: "scopeLoading" }] : []));
|
|
2725
|
+
scopeProperties = signal([], ...(ngDevMode ? [{ debugName: "scopeProperties" }] : []));
|
|
2726
|
+
scopePath = signal([], ...(ngDevMode ? [{ debugName: "scopePath" }] : []));
|
|
2727
|
+
scopeSelectedKey = signal('', ...(ngDevMode ? [{ debugName: "scopeSelectedKey" }] : []));
|
|
2728
|
+
scopeNavigation = signal(null, ...(ngDevMode ? [{ debugName: "scopeNavigation" }] : []));
|
|
2729
|
+
// Request tracking
|
|
2730
|
+
initialRequestId = 0;
|
|
2731
|
+
scopeRequestId = 0;
|
|
2732
|
+
lastContextKey = '';
|
|
2733
|
+
// ============================================================================
|
|
2734
|
+
// Computed Values
|
|
2735
|
+
// ============================================================================
|
|
2736
|
+
contextKey = computed(() => {
|
|
2737
|
+
const type = this.moduleType();
|
|
2738
|
+
const id = this.moduleId();
|
|
2739
|
+
const parent = this.parentPath();
|
|
2740
|
+
if (!type || id === null || id === undefined)
|
|
2741
|
+
return '';
|
|
2742
|
+
const segments = [];
|
|
2743
|
+
const raw = parent.split('/').filter(Boolean);
|
|
2744
|
+
for (let i = 0; i < raw.length; i += 2) {
|
|
2745
|
+
if (raw[i] && raw[i + 1] !== undefined) {
|
|
2746
|
+
segments.push(`${raw[i]}:${raw[i + 1]}`);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
segments.push(`${type}:${id}`);
|
|
2750
|
+
return segments.join('/');
|
|
2751
|
+
}, ...(ngDevMode ? [{ debugName: "contextKey" }] : []));
|
|
2752
|
+
usedPropertyKeys = computed(() => {
|
|
2753
|
+
return new Set(this.sections().flatMap((s) => s.fields.map((f) => f.propertyKey)));
|
|
2754
|
+
}, ...(ngDevMode ? [{ debugName: "usedPropertyKeys" }] : []));
|
|
2501
2755
|
propertiesMap = computed(() => {
|
|
2502
2756
|
const map = new Map();
|
|
2503
2757
|
for (const prop of this.properties()) {
|
|
2504
|
-
map.set(prop.
|
|
2758
|
+
map.set(prop.key, prop);
|
|
2505
2759
|
}
|
|
2506
2760
|
return map;
|
|
2507
2761
|
}, ...(ngDevMode ? [{ debugName: "propertiesMap" }] : []));
|
|
2508
|
-
|
|
2762
|
+
/** Navigation options from initial context */
|
|
2763
|
+
baseScopeContexts = computed(() => {
|
|
2764
|
+
const ctx = this.initialContext();
|
|
2765
|
+
const nav = ctx?.navigationPaths;
|
|
2766
|
+
const currentKey = ctx?.currentState?.contextPath ?? this.contextKey();
|
|
2767
|
+
return {
|
|
2768
|
+
Current: currentKey
|
|
2769
|
+
? [this.createContextOption(currentKey, ctx?.currentState)]
|
|
2770
|
+
: [],
|
|
2771
|
+
Host: nav?.hostContext
|
|
2772
|
+
? [this.createContextOption(nav.hostContext)]
|
|
2773
|
+
: [],
|
|
2774
|
+
Parent: this.mergeContextOptions(nav?.parent),
|
|
2775
|
+
Ancestors: this.mergeContextOptions(nav?.ancestors),
|
|
2776
|
+
Children: this.mergeContextOptions(nav?.children),
|
|
2777
|
+
Descendants: this.mergeContextOptions(nav?.descendants),
|
|
2778
|
+
Related: this.mergeContextOptions(nav?.related),
|
|
2779
|
+
Siblings: this.mergeContextOptions(nav?.siblingContexts),
|
|
2780
|
+
};
|
|
2781
|
+
}, ...(ngDevMode ? [{ debugName: "baseScopeContexts" }] : []));
|
|
2782
|
+
/** Available scope tabs */
|
|
2783
|
+
scopeOptions = computed(() => {
|
|
2784
|
+
const contexts = this.baseScopeContexts();
|
|
2785
|
+
return SCOPE_ORDER.filter((s) => (contexts[s] ?? []).length > 0).map((s) => ({ key: s, label: SCOPE_LABELS[s] }));
|
|
2786
|
+
}, ...(ngDevMode ? [{ debugName: "scopeOptions" }] : []));
|
|
2787
|
+
/** Enriched sections for display */
|
|
2509
2788
|
enrichedSections = computed(() => {
|
|
2510
2789
|
const sections = this.sections();
|
|
2511
2790
|
const propsMap = this.propertiesMap();
|
|
@@ -2513,119 +2792,436 @@ class FormBuilder {
|
|
|
2513
2792
|
return sections.map((section) => ({
|
|
2514
2793
|
...section,
|
|
2515
2794
|
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}`);
|
|
2795
|
+
const prop = field.property ?? propsMap.get(field.propertyKey);
|
|
2523
2796
|
return {
|
|
2524
2797
|
...field,
|
|
2525
|
-
name,
|
|
2798
|
+
name: this.resolvePropertyName(prop, field.propertyKey, lang),
|
|
2526
2799
|
type: prop?.viewType || 'text',
|
|
2527
2800
|
data: prop,
|
|
2528
2801
|
};
|
|
2529
2802
|
}),
|
|
2530
2803
|
}));
|
|
2531
2804
|
}, ...(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" }] : []));
|
|
2805
|
+
// ============================================================================
|
|
2806
|
+
// Lifecycle
|
|
2807
|
+
// ============================================================================
|
|
2562
2808
|
constructor() {
|
|
2563
|
-
//
|
|
2809
|
+
// Effect: watch for context key changes and load initial data
|
|
2564
2810
|
effect(() => {
|
|
2565
|
-
const
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2811
|
+
const key = this.contextKey();
|
|
2812
|
+
if (key && key !== this.lastContextKey) {
|
|
2813
|
+
this.lastContextKey = key;
|
|
2814
|
+
// In manageProperties mode, skip context resolve — just load form config
|
|
2815
|
+
if (this.mode() === 'manageProperties') {
|
|
2816
|
+
this.facade.getFormConfiguration();
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
this.loadInitialContext(key);
|
|
2820
|
+
}
|
|
2821
|
+
}, { allowSignalWrites: true });
|
|
2822
|
+
}
|
|
2823
|
+
ngOnInit() {
|
|
2824
|
+
// Trigger initial load if context key is already available
|
|
2825
|
+
const key = this.contextKey();
|
|
2826
|
+
if (key && key !== this.lastContextKey) {
|
|
2827
|
+
this.lastContextKey = key;
|
|
2828
|
+
if (this.mode() === 'manageProperties') {
|
|
2829
|
+
this.facade.getFormConfiguration();
|
|
2830
|
+
return;
|
|
2569
2831
|
}
|
|
2832
|
+
this.loadInitialContext(key);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
// ============================================================================
|
|
2836
|
+
// Initial Context Loading
|
|
2837
|
+
// ============================================================================
|
|
2838
|
+
loadInitialContext(contextKey) {
|
|
2839
|
+
const requestId = ++this.initialRequestId;
|
|
2840
|
+
this.isContextLoading.set(true);
|
|
2841
|
+
this.initialContext.set(null);
|
|
2842
|
+
this.currentProperties.set([]);
|
|
2843
|
+
this.resetScopeState();
|
|
2844
|
+
this.activeScope.set('Current');
|
|
2845
|
+
this.contextService
|
|
2846
|
+
.resolveContext(contextKey, {
|
|
2847
|
+
propertyFlags: 'None',
|
|
2848
|
+
includeVirtual: true,
|
|
2849
|
+
includeNavigationPaths: true,
|
|
2850
|
+
includeConnectionMetadata: true,
|
|
2851
|
+
matchAllTags: true,
|
|
2852
|
+
includeRelatedContextValues: true,
|
|
2853
|
+
})
|
|
2854
|
+
.subscribe((response) => {
|
|
2855
|
+
if (requestId !== this.initialRequestId)
|
|
2856
|
+
return;
|
|
2857
|
+
this.isContextLoading.set(false);
|
|
2858
|
+
this.initialContext.set(response);
|
|
2859
|
+
this.currentProperties.set(response?.properties ?? []);
|
|
2860
|
+
this.facade.setProperties(response?.properties ?? []);
|
|
2570
2861
|
});
|
|
2571
2862
|
}
|
|
2863
|
+
// ============================================================================
|
|
2864
|
+
// Tab Change Handler (Called from template)
|
|
2865
|
+
// ============================================================================
|
|
2866
|
+
onScopeChange(scope) {
|
|
2867
|
+
if (scope === this.activeScope())
|
|
2868
|
+
return;
|
|
2869
|
+
this.activeScope.set(scope);
|
|
2870
|
+
this.resetScopeState();
|
|
2871
|
+
if (scope === 'Current') {
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
// Get first context option for this scope
|
|
2875
|
+
const options = this.baseScopeContexts()[scope] ?? [];
|
|
2876
|
+
if (options.length === 0)
|
|
2877
|
+
return;
|
|
2878
|
+
const firstOption = options[0];
|
|
2879
|
+
this.scopePath.set([
|
|
2880
|
+
{
|
|
2881
|
+
token: scope,
|
|
2882
|
+
value: firstOption.contextKey,
|
|
2883
|
+
label: firstOption.label,
|
|
2884
|
+
},
|
|
2885
|
+
]);
|
|
2886
|
+
this.loadScopeContext(firstOption.contextKey);
|
|
2887
|
+
}
|
|
2888
|
+
// ============================================================================
|
|
2889
|
+
// Scope Context Loading
|
|
2890
|
+
// ============================================================================
|
|
2891
|
+
loadScopeContext(contextKey) {
|
|
2892
|
+
const requestId = ++this.scopeRequestId;
|
|
2893
|
+
this.scopeLoading.set(true);
|
|
2894
|
+
this.contextService
|
|
2895
|
+
.resolveContext(contextKey, {
|
|
2896
|
+
propertyFlags: 'None',
|
|
2897
|
+
includeVirtual: true,
|
|
2898
|
+
includeNavigationPaths: true,
|
|
2899
|
+
includeConnectionMetadata: true,
|
|
2900
|
+
matchAllTags: true,
|
|
2901
|
+
includeRelatedContextValues: true,
|
|
2902
|
+
})
|
|
2903
|
+
.subscribe((response) => {
|
|
2904
|
+
if (requestId !== this.scopeRequestId)
|
|
2905
|
+
return;
|
|
2906
|
+
this.scopeLoading.set(false);
|
|
2907
|
+
this.scopeNavigation.set(response);
|
|
2908
|
+
this.scopeProperties.set(response?.properties ?? []);
|
|
2909
|
+
// Auto-select first available property
|
|
2910
|
+
const used = this.usedPropertyKeys();
|
|
2911
|
+
const available = (response?.properties ?? []).filter((p) => !used.has(p.key));
|
|
2912
|
+
if (available.length > 0) {
|
|
2913
|
+
this.scopeSelectedKey.set(available[0].key);
|
|
2914
|
+
}
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
resetScopeState() {
|
|
2918
|
+
this.scopeLoading.set(false);
|
|
2919
|
+
this.scopeProperties.set([]);
|
|
2920
|
+
this.scopePath.set([]);
|
|
2921
|
+
this.scopeSelectedKey.set('');
|
|
2922
|
+
this.scopeNavigation.set(null);
|
|
2923
|
+
this.hideAllPopovers();
|
|
2924
|
+
}
|
|
2925
|
+
// ============================================================================
|
|
2926
|
+
// Path Segment Methods (accept scope parameter for template compatibility)
|
|
2927
|
+
// ============================================================================
|
|
2928
|
+
getScopeBaseContexts(scope) {
|
|
2929
|
+
return this.baseScopeContexts()[scope] ?? [];
|
|
2930
|
+
}
|
|
2931
|
+
/** Template calls this with scope.key */
|
|
2932
|
+
getScopePath(_scope) {
|
|
2933
|
+
return this.scopePath();
|
|
2934
|
+
}
|
|
2935
|
+
getTokenLabel(token) {
|
|
2936
|
+
return SCOPE_LABELS[token] ?? token;
|
|
2937
|
+
}
|
|
2938
|
+
getScopeSegmentLabel(_scope, index) {
|
|
2939
|
+
const path = this.scopePath();
|
|
2940
|
+
const segment = path[index];
|
|
2941
|
+
if (!segment?.value)
|
|
2942
|
+
return `Select ${this.getTokenLabel(segment?.token)}`;
|
|
2943
|
+
// Use stored label - don't look up from options (which change on resolve)
|
|
2944
|
+
return segment.label || this.formatContextKey(segment.value);
|
|
2945
|
+
}
|
|
2946
|
+
getScopeSegmentOptions(_scope, index) {
|
|
2947
|
+
const path = this.scopePath();
|
|
2948
|
+
const segment = path[index];
|
|
2949
|
+
if (!segment)
|
|
2950
|
+
return [];
|
|
2951
|
+
if (index === 0) {
|
|
2952
|
+
return this.getScopeBaseContexts(this.activeScope());
|
|
2953
|
+
}
|
|
2954
|
+
const nav = this.scopeNavigation();
|
|
2955
|
+
return this.getNavigationOptionsByToken(nav, segment.token);
|
|
2956
|
+
}
|
|
2957
|
+
setScopeSegmentValue(_scope, index, contextKey, label) {
|
|
2958
|
+
const path = this.scopePath();
|
|
2959
|
+
const newPath = path.slice(0, index + 1);
|
|
2960
|
+
if (newPath[index]) {
|
|
2961
|
+
// Store label at selection time so it doesn't change on resolve
|
|
2962
|
+
const segmentLabel = label || this.findOptionLabel(_scope, index, contextKey);
|
|
2963
|
+
newPath[index] = {
|
|
2964
|
+
...newPath[index],
|
|
2965
|
+
value: contextKey,
|
|
2966
|
+
label: segmentLabel,
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
this.scopePath.set(newPath);
|
|
2970
|
+
this.loadScopeContext(contextKey);
|
|
2971
|
+
}
|
|
2972
|
+
findOptionLabel(_scope, index, contextKey) {
|
|
2973
|
+
const options = this.getScopeSegmentOptions(_scope, index);
|
|
2974
|
+
const option = options.find((o) => o.contextKey === contextKey);
|
|
2975
|
+
return option?.label ?? this.formatContextKey(contextKey);
|
|
2976
|
+
}
|
|
2977
|
+
/** Template handler: hide popover first, then set value (like formula-builder) */
|
|
2978
|
+
onSetScopeSegmentValue(scope, index, contextKey, popover) {
|
|
2979
|
+
popover.hide();
|
|
2980
|
+
this.setScopeSegmentValue(scope, index, contextKey);
|
|
2981
|
+
}
|
|
2982
|
+
addScopeSegment(_scope, token) {
|
|
2983
|
+
const path = this.scopePath();
|
|
2984
|
+
this.scopePath.set([...path, { token, value: '', label: '' }]);
|
|
2985
|
+
}
|
|
2986
|
+
/** Template handler: hide popover first, then add segment (like formula-builder) */
|
|
2987
|
+
onAddScopeSegment(scope, token, popover) {
|
|
2988
|
+
popover.hide();
|
|
2989
|
+
this.addScopeSegment(scope, token);
|
|
2990
|
+
}
|
|
2991
|
+
getScopeNextTokens(_scope) {
|
|
2992
|
+
const path = this.scopePath();
|
|
2993
|
+
const last = path[path.length - 1];
|
|
2994
|
+
if (!last?.value)
|
|
2995
|
+
return [];
|
|
2996
|
+
const nav = this.scopeNavigation();
|
|
2997
|
+
if (!nav)
|
|
2998
|
+
return [];
|
|
2999
|
+
return PATH_TOKENS.filter((t) => this.getNavigationOptionsByToken(nav, t).length > 0);
|
|
3000
|
+
}
|
|
3001
|
+
canAddNextScopeSegment(_scope) {
|
|
3002
|
+
const path = this.scopePath();
|
|
3003
|
+
if (path.length === 0)
|
|
3004
|
+
return false;
|
|
3005
|
+
if (!path.every((s) => Boolean(s.value)))
|
|
3006
|
+
return false;
|
|
3007
|
+
return this.getScopeNextTokens().length > 0;
|
|
3008
|
+
}
|
|
3009
|
+
canRemoveScopePath(_scope) {
|
|
3010
|
+
return this.scopePath().length > 1;
|
|
3011
|
+
}
|
|
3012
|
+
removeScopePath(_scope) {
|
|
3013
|
+
const path = this.scopePath();
|
|
3014
|
+
if (path.length <= 1)
|
|
3015
|
+
return;
|
|
3016
|
+
const newPath = path.slice(0, -1);
|
|
3017
|
+
this.scopePath.set(newPath);
|
|
3018
|
+
const last = newPath[newPath.length - 1];
|
|
3019
|
+
if (last?.value) {
|
|
3020
|
+
this.loadScopeContext(last.value);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
/** Template-compatible version (returns properties array for the scope) */
|
|
3024
|
+
getScopeProperties(_scope) {
|
|
3025
|
+
const scope = _scope ?? this.activeScope();
|
|
3026
|
+
return scope === 'Current'
|
|
3027
|
+
? this.currentProperties()
|
|
3028
|
+
: this.scopeProperties();
|
|
3029
|
+
}
|
|
3030
|
+
// ============================================================================
|
|
3031
|
+
// Property Methods (accept scope parameter for template compatibility)
|
|
3032
|
+
// ============================================================================
|
|
3033
|
+
getScopePropertyOptions(_scope) {
|
|
3034
|
+
const scope = _scope ?? this.activeScope();
|
|
3035
|
+
const props = scope === 'Current' ? this.currentProperties() : this.scopeProperties();
|
|
3036
|
+
const used = this.usedPropertyKeys();
|
|
3037
|
+
return props
|
|
3038
|
+
.filter((p) => !used.has(p.key))
|
|
3039
|
+
.map((p) => ({ key: p.key, name: this.getPropertyLabel(p) }));
|
|
3040
|
+
}
|
|
3041
|
+
getScopeSelectedPropertyKey(_scope) {
|
|
3042
|
+
return this.scopeSelectedKey();
|
|
3043
|
+
}
|
|
3044
|
+
setScopeSelectedProperty(_scope, key) {
|
|
3045
|
+
this.scopeSelectedKey.set(key);
|
|
3046
|
+
}
|
|
3047
|
+
getScopeSelectedProperty(_scope) {
|
|
3048
|
+
const key = this.scopeSelectedKey();
|
|
3049
|
+
if (!key)
|
|
3050
|
+
return null;
|
|
3051
|
+
return this.scopeProperties().find((p) => p.key === key) ?? null;
|
|
3052
|
+
}
|
|
3053
|
+
getFilteredProperties(_scope) {
|
|
3054
|
+
const scope = _scope ?? this.activeScope();
|
|
3055
|
+
const used = this.usedPropertyKeys();
|
|
3056
|
+
const query = this.searchQuery().toLowerCase().trim();
|
|
3057
|
+
if (scope !== 'Current') {
|
|
3058
|
+
const selected = this.getScopeSelectedProperty();
|
|
3059
|
+
if (!selected || used.has(selected.key))
|
|
3060
|
+
return [];
|
|
3061
|
+
return [selected];
|
|
3062
|
+
}
|
|
3063
|
+
let available = this.currentProperties().filter((p) => !used.has(p.key));
|
|
3064
|
+
if (query) {
|
|
3065
|
+
available = available.filter((p) => this.getPropertyLabel(p).toLowerCase().includes(query));
|
|
3066
|
+
}
|
|
3067
|
+
return available;
|
|
3068
|
+
}
|
|
3069
|
+
isScopePropertiesLoading(_scope) {
|
|
3070
|
+
const scope = _scope ?? this.activeScope();
|
|
3071
|
+
return scope !== 'Current' && this.scopeLoading();
|
|
3072
|
+
}
|
|
3073
|
+
getPropertyLabel(property) {
|
|
3074
|
+
const lang = document.documentElement.lang;
|
|
3075
|
+
return this.resolvePropertyName(property, property.key, lang);
|
|
3076
|
+
}
|
|
3077
|
+
// ============================================================================
|
|
3078
|
+
// Helper Methods
|
|
3079
|
+
// ============================================================================
|
|
3080
|
+
hideAllPopovers() {
|
|
3081
|
+
for (const popover of this.popovers()) {
|
|
3082
|
+
popover.hide();
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
getNavigationOptionsByToken(response, token) {
|
|
3086
|
+
const nav = response?.navigationPaths;
|
|
3087
|
+
if (!nav)
|
|
3088
|
+
return [];
|
|
3089
|
+
switch (token) {
|
|
3090
|
+
case 'Parent':
|
|
3091
|
+
return this.mergeContextOptions(nav.parent);
|
|
3092
|
+
case 'Ancestors':
|
|
3093
|
+
return this.mergeContextOptions(nav.ancestors);
|
|
3094
|
+
case 'Children':
|
|
3095
|
+
return this.mergeContextOptions(nav.children);
|
|
3096
|
+
case 'Descendants':
|
|
3097
|
+
return this.mergeContextOptions(nav.descendants);
|
|
3098
|
+
case 'Related':
|
|
3099
|
+
return this.mergeContextOptions(nav.related);
|
|
3100
|
+
case 'Siblings':
|
|
3101
|
+
return this.mergeContextOptions(nav.siblingContexts);
|
|
3102
|
+
case 'Host':
|
|
3103
|
+
return nav.hostContext
|
|
3104
|
+
? [this.createContextOption(nav.hostContext)]
|
|
3105
|
+
: [];
|
|
3106
|
+
default:
|
|
3107
|
+
return [];
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
resolvePropertyName(property, fallbackKey, lang) {
|
|
3111
|
+
if (!property)
|
|
3112
|
+
return `Property ${fallbackKey}`;
|
|
3113
|
+
const name = property.name;
|
|
3114
|
+
if (typeof name === 'string')
|
|
3115
|
+
return name;
|
|
3116
|
+
const display = name?.['display'];
|
|
3117
|
+
if (typeof display === 'string' && display.trim())
|
|
3118
|
+
return display;
|
|
3119
|
+
const localized = name?.[lang] ?? name?.['en'] ?? name?.['ar'];
|
|
3120
|
+
if (typeof localized === 'string' && localized.trim())
|
|
3121
|
+
return localized;
|
|
3122
|
+
return property.key ?? `Property ${fallbackKey}`;
|
|
3123
|
+
}
|
|
3124
|
+
createContextOption(pathOrKey, currentState) {
|
|
3125
|
+
if (typeof pathOrKey === 'string') {
|
|
3126
|
+
return {
|
|
3127
|
+
contextKey: pathOrKey,
|
|
3128
|
+
label: this.formatContextLabel({
|
|
3129
|
+
contextKey: pathOrKey,
|
|
3130
|
+
contextType: currentState?.contextType,
|
|
3131
|
+
contextId: currentState?.contextId,
|
|
3132
|
+
}),
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
return {
|
|
3136
|
+
contextKey: pathOrKey.contextKey,
|
|
3137
|
+
label: this.formatContextLabel(pathOrKey),
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
formatContextLabel(path) {
|
|
3141
|
+
// Just return the name - no path formatting needed
|
|
3142
|
+
if (path.name)
|
|
3143
|
+
return path.name;
|
|
3144
|
+
if (path.key)
|
|
3145
|
+
return path.key;
|
|
3146
|
+
if (path.contextType && path.contextId !== undefined)
|
|
3147
|
+
return `${path.contextType} ${path.contextId}`;
|
|
3148
|
+
return path.contextKey || 'Context';
|
|
3149
|
+
}
|
|
3150
|
+
formatContextKey(contextKey) {
|
|
3151
|
+
return contextKey
|
|
3152
|
+
.split('/')
|
|
3153
|
+
.filter(Boolean)
|
|
3154
|
+
.map((seg) => {
|
|
3155
|
+
const [type, id] = seg.split(':');
|
|
3156
|
+
return id !== undefined ? `${type} ${id}` : type;
|
|
3157
|
+
})
|
|
3158
|
+
.join(' / ');
|
|
3159
|
+
}
|
|
3160
|
+
mergeContextOptions(items) {
|
|
3161
|
+
if (!items?.length)
|
|
3162
|
+
return [];
|
|
3163
|
+
const seen = new Set();
|
|
3164
|
+
return items
|
|
3165
|
+
.slice()
|
|
3166
|
+
.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0))
|
|
3167
|
+
.filter((item) => {
|
|
3168
|
+
if (!item?.contextKey || seen.has(item.contextKey))
|
|
3169
|
+
return false;
|
|
3170
|
+
seen.add(item.contextKey);
|
|
3171
|
+
return true;
|
|
3172
|
+
})
|
|
3173
|
+
.map((item) => this.createContextOption(item));
|
|
3174
|
+
}
|
|
3175
|
+
// ============================================================================
|
|
3176
|
+
// Drag & Drop
|
|
3177
|
+
// ============================================================================
|
|
2572
3178
|
drop(event) {
|
|
2573
3179
|
const targetSectionId = event.container.id;
|
|
2574
3180
|
if (event.previousContainer === event.container) {
|
|
2575
|
-
// Reordering within the same section
|
|
2576
3181
|
const fields = [...event.container.data];
|
|
2577
3182
|
const [movedField] = fields.splice(event.previousIndex, 1);
|
|
2578
3183
|
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);
|
|
3184
|
+
this.facade.reorderFields(targetSectionId, fields.map((f, i) => ({ id: f.id, order: i })));
|
|
2585
3185
|
}
|
|
2586
3186
|
else if (event.previousContainer.id.startsWith('toolbox-')) {
|
|
2587
|
-
|
|
2588
|
-
const propertyItem = event.item.data;
|
|
3187
|
+
const prop = event.item.data;
|
|
2589
3188
|
this.facade.addField(targetSectionId, {
|
|
2590
|
-
|
|
3189
|
+
propertyKey: prop.key,
|
|
2591
3190
|
width: '100',
|
|
2592
3191
|
order: event.currentIndex,
|
|
2593
3192
|
hiddenInCreation: false,
|
|
2594
3193
|
});
|
|
2595
3194
|
}
|
|
2596
3195
|
else {
|
|
2597
|
-
// Moving between sections
|
|
2598
|
-
const sourceSectionId = event.previousContainer.id;
|
|
2599
3196
|
const field = event.item.data;
|
|
2600
|
-
this.facade.moveField(
|
|
3197
|
+
this.facade.moveField(event.previousContainer.id, field.id, {
|
|
2601
3198
|
targetSectionId,
|
|
2602
3199
|
order: event.currentIndex,
|
|
2603
3200
|
});
|
|
2604
3201
|
}
|
|
2605
3202
|
}
|
|
3203
|
+
// ============================================================================
|
|
3204
|
+
// Modal Actions
|
|
3205
|
+
// ============================================================================
|
|
2606
3206
|
addSection() {
|
|
2607
3207
|
this.dialogRef = this.modalService.openModal(FBSectionForm, 'drawer', {
|
|
2608
3208
|
header: this.translocoService.translate('formBuilder.add-section'),
|
|
2609
3209
|
height: '20vw',
|
|
2610
|
-
styleClass: '!w-[27%] !absolute !shadow-none
|
|
3210
|
+
styleClass: '!w-[27%] !absolute !shadow-none',
|
|
2611
3211
|
position: 'end',
|
|
2612
3212
|
appendTo: '#page-content',
|
|
2613
3213
|
dismissible: true,
|
|
2614
|
-
inputValues: {
|
|
2615
|
-
sectionsCount: this.sections().length,
|
|
2616
|
-
},
|
|
3214
|
+
inputValues: { sectionsCount: this.sections().length },
|
|
2617
3215
|
});
|
|
2618
3216
|
}
|
|
2619
3217
|
openPreview() {
|
|
2620
3218
|
this.dialogRef = this.modalService.openModal(FBPreviewForm, 'drawer', {
|
|
2621
3219
|
header: this.translocoService.translate('formBuilder.preview'),
|
|
2622
|
-
styleClass: '!w-[79%] !absolute !shadow-none
|
|
3220
|
+
styleClass: '!w-[79%] !absolute !shadow-none',
|
|
2623
3221
|
position: 'end',
|
|
2624
3222
|
appendTo: 'page-content',
|
|
2625
3223
|
dismissible: true,
|
|
2626
|
-
inputValues: {
|
|
2627
|
-
sections: this.enrichedSections(),
|
|
2628
|
-
},
|
|
3224
|
+
inputValues: { sections: this.enrichedSections() },
|
|
2629
3225
|
});
|
|
2630
3226
|
}
|
|
2631
3227
|
openValidationRules() {
|
|
@@ -2641,14 +3237,12 @@ class FormBuilder {
|
|
|
2641
3237
|
this.confirmationService.confirm({
|
|
2642
3238
|
type: 'dialog',
|
|
2643
3239
|
acceptButtonStyleClass: 'p-button-danger',
|
|
2644
|
-
accept: () =>
|
|
2645
|
-
this.facade.resetFormConfiguration();
|
|
2646
|
-
},
|
|
3240
|
+
accept: () => this.facade.resetFormConfiguration(),
|
|
2647
3241
|
});
|
|
2648
3242
|
}
|
|
2649
3243
|
noReturnPredicate = () => false;
|
|
2650
3244
|
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" }] });
|
|
3245
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: FormBuilder, isStandalone: true, selector: "mt-form-builder", inputs: { canvasStyleClass: { classPropertyName: "canvasStyleClass", publicName: "canvasStyleClass", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, 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 (hidden in manageProperties mode) -->\r\n @if (mode() !== \"manageProperties\") {\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 (\r\n isLast && canRemoveScopePath(scope.key)\r\n ) {\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 ===\r\n 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)=\"\r\n nextContextPopover.toggle($event)\r\n \"\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\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-lg\"\r\n />\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\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div\r\n class=\"flex flex-col w-2/3 gap-4 h-full\"\r\n [ngClass]=\"canvasStyleClass()\"\r\n >\r\n @if (mode() !== \"manageProperties\") {\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\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 [mode]=\"mode()\"\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: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: i3.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i3.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i3.TabPanel, selector: "p-tabpanel", inputs: ["lazy", "value"], outputs: ["valueChange"] }, { kind: "component", type: i3.TabList, selector: "p-tablist" }, { kind: "component", type: i3.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i5.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", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], 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", "mode"], outputs: ["onFieldDrop"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i6.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: i6.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i6.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: i6.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }] });
|
|
2652
3246
|
}
|
|
2653
3247
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: FormBuilder, decorators: [{
|
|
2654
3248
|
type: Component,
|
|
@@ -2657,15 +3251,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
2657
3251
|
FormsModule,
|
|
2658
3252
|
TabsModule,
|
|
2659
3253
|
SkeletonModule,
|
|
3254
|
+
PopoverModule,
|
|
2660
3255
|
Button,
|
|
2661
3256
|
Card,
|
|
2662
3257
|
TextField,
|
|
3258
|
+
SelectField,
|
|
2663
3259
|
TranslocoDirective,
|
|
2664
3260
|
FBSection,
|
|
2665
3261
|
DragDropModule,
|
|
2666
3262
|
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: () => [] });
|
|
3263
|
+
], 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 (hidden in manageProperties mode) -->\r\n @if (mode() !== \"manageProperties\") {\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 (\r\n isLast && canRemoveScopePath(scope.key)\r\n ) {\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 ===\r\n 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)=\"\r\n nextContextPopover.toggle($event)\r\n \"\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\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-lg\"\r\n />\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\r\n <!-- Main Canvas Area -->\r\n <div class=\"flex flex-1 gap-4 h-full overflow-y-auto\">\r\n <div\r\n class=\"flex flex-col w-2/3 gap-4 h-full\"\r\n [ngClass]=\"canvasStyleClass()\"\r\n >\r\n @if (mode() !== \"manageProperties\") {\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\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 [mode]=\"mode()\"\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"] }]
|
|
3264
|
+
}], ctorParameters: () => [], propDecorators: { popovers: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => Popover), { isSignal: true }] }], canvasStyleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasStyleClass", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }] } });
|
|
2669
3265
|
|
|
2670
3266
|
/*
|
|
2671
3267
|
* Public API Surface of form-builder
|