@masterteam/form-builder 0.0.50 → 0.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/assets/i18n/ar.json
CHANGED
package/assets/i18n/en.json
CHANGED
|
@@ -84,6 +84,8 @@
|
|
|
84
84
|
"toolbar-fields": "Fields",
|
|
85
85
|
"toolbar-operators": "Operators",
|
|
86
86
|
"toolbar-no-fields": "No fields available",
|
|
87
|
+
"toolbar-values": "Values",
|
|
88
|
+
"toolbar-no-values": "No values available",
|
|
87
89
|
"build-condition-formula": "Build condition formula..."
|
|
88
90
|
}
|
|
89
91
|
}
|
|
@@ -29,10 +29,10 @@ import { DynamicForm } from '@masterteam/forms/dynamic-form';
|
|
|
29
29
|
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
30
30
|
import { ModalRef } from '@masterteam/components/dialog';
|
|
31
31
|
import { CONDITION_FUNCTION_CATEGORIES, CONDITION_OPERATORS, FormulaToolbar, FormulaEditor, serializeTokens, tokenizeFormulaTemplate, serializeTokensForDisplay } from '@masterteam/components/formula';
|
|
32
|
+
import { of, map, catchError, tap, startWith } from 'rxjs';
|
|
32
33
|
import { Tooltip } from '@masterteam/components/tooltip';
|
|
33
34
|
import { ClientForm } from '@masterteam/forms/client-form';
|
|
34
35
|
import { Table } from '@masterteam/components/table';
|
|
35
|
-
import { startWith, of, map, catchError } from 'rxjs';
|
|
36
36
|
import { RadioCardsField } from '@masterteam/components/radio-cards-field';
|
|
37
37
|
import { FormulaBuilder } from '@masterteam/formula-builder';
|
|
38
38
|
|
|
@@ -1227,6 +1227,146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1227
1227
|
args: [{ providedIn: 'root' }]
|
|
1228
1228
|
}] });
|
|
1229
1229
|
|
|
1230
|
+
/** Lower-case a view type and strip the separators the backend may send. */
|
|
1231
|
+
function normalizeViewType(viewType) {
|
|
1232
|
+
return (viewType ?? '').toLowerCase().replace(/[ _-]/g, '');
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* View types whose values the backend can serve. Matched after lower-casing
|
|
1236
|
+
* and stripping spaces/underscores/dashes, and kept in step with the backend
|
|
1237
|
+
* gate (`PropertyItemsLookupSourceResolver.IsLookupBacked` plus `Status` and
|
|
1238
|
+
* `PhaseGate`) — every other view type is rejected with a validation error.
|
|
1239
|
+
*/
|
|
1240
|
+
const VALUE_BACKED_VIEW_TYPES = new Set([
|
|
1241
|
+
'lookup',
|
|
1242
|
+
'lookupmultiselect',
|
|
1243
|
+
'lookupmatrix',
|
|
1244
|
+
'status',
|
|
1245
|
+
'phasegate',
|
|
1246
|
+
]);
|
|
1247
|
+
/**
|
|
1248
|
+
* View types whose item keys are ids to compare numerically. Only phase values
|
|
1249
|
+
* qualify: lookup items legitimately carry numeric-looking string keys (the
|
|
1250
|
+
* backend falls back to the item id when a lookup item has no key) and compare
|
|
1251
|
+
* as strings, so this must stay keyed on the view type, never on whether a key
|
|
1252
|
+
* happens to parse as a number.
|
|
1253
|
+
*/
|
|
1254
|
+
const NUMERIC_VALUE_VIEW_TYPES = new Set(['phasegate']);
|
|
1255
|
+
/** Whether a property's values can be picked from a source instead of typed. */
|
|
1256
|
+
function isValueBackedViewType(viewType) {
|
|
1257
|
+
if (!viewType)
|
|
1258
|
+
return false;
|
|
1259
|
+
return VALUE_BACKED_VIEW_TYPES.has(normalizeViewType(viewType));
|
|
1260
|
+
}
|
|
1261
|
+
class FormBuilderContextService {
|
|
1262
|
+
http = inject(HttpClient);
|
|
1263
|
+
baseUrl = 'context';
|
|
1264
|
+
propertyItemsCache = new Map();
|
|
1265
|
+
/**
|
|
1266
|
+
* Resolves context data including navigation paths and properties.
|
|
1267
|
+
* No caching - always fetches fresh data.
|
|
1268
|
+
*/
|
|
1269
|
+
resolveContext(contextKey, options = {}) {
|
|
1270
|
+
if (!contextKey)
|
|
1271
|
+
return of(null);
|
|
1272
|
+
return this.http
|
|
1273
|
+
.post(`${this.baseUrl}/resolve`, {
|
|
1274
|
+
contextKey,
|
|
1275
|
+
displayTags: options.displayTags,
|
|
1276
|
+
propertyFlags: options.propertyFlags,
|
|
1277
|
+
matchAllTags: options.matchAllTags,
|
|
1278
|
+
includeVirtual: options.includeVirtual,
|
|
1279
|
+
includeNavigationPaths: options.includeNavigationPaths,
|
|
1280
|
+
includeConnectionMetadata: options.includeConnectionMetadata,
|
|
1281
|
+
includeRelatedContextValues: options.includeRelatedContextValues,
|
|
1282
|
+
relatedContextsToInclude: options.relatedContextsToInclude,
|
|
1283
|
+
// Consumer-supplied fields win on collision with any field above.
|
|
1284
|
+
...options.extraParams,
|
|
1285
|
+
})
|
|
1286
|
+
.pipe(map((response) => this.normalizeResponse(response)), catchError((error) => {
|
|
1287
|
+
console.warn('Failed to resolve context:', error);
|
|
1288
|
+
return of(null);
|
|
1289
|
+
}));
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Gets properties for a context.
|
|
1293
|
+
* No caching - always fetches fresh data.
|
|
1294
|
+
*/
|
|
1295
|
+
getProperties(contextKey, options = {}) {
|
|
1296
|
+
if (!contextKey)
|
|
1297
|
+
return of([]);
|
|
1298
|
+
return this.resolveContext(contextKey, options).pipe(map((resolved) => resolved?.properties ?? []));
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Selectable values of a lookup / lookup-matrix / status / phase property,
|
|
1302
|
+
* used by the condition builder so a compared value is picked rather than
|
|
1303
|
+
* typed.
|
|
1304
|
+
*
|
|
1305
|
+
* The backend answers 400 for any other view type, which we swallow into an
|
|
1306
|
+
* empty list — callers already gate on `isValueBackedViewType`, so empty
|
|
1307
|
+
* simply means "nothing to pick".
|
|
1308
|
+
*
|
|
1309
|
+
* Deliberately sent **without** the backend's optional `contextKey`. A form's
|
|
1310
|
+
* fields can come from several scopes at once (the builder's Current / Host /
|
|
1311
|
+
* Parent tabs), so a module form legitimately carries level-scoped fields —
|
|
1312
|
+
* and scoping the request to the form's own context 404s every one of them,
|
|
1313
|
+
* because the property is not in that catalog. Resolution by full property
|
|
1314
|
+
* key is unambiguous for persisted properties, which is what fields carry.
|
|
1315
|
+
*/
|
|
1316
|
+
getPropertyItems(propertyKey) {
|
|
1317
|
+
if (!propertyKey)
|
|
1318
|
+
return of([]);
|
|
1319
|
+
const cached = this.propertyItemsCache.get(propertyKey);
|
|
1320
|
+
if (cached)
|
|
1321
|
+
return of(cached);
|
|
1322
|
+
return this.http
|
|
1323
|
+
.get(`formulas/builder/property-items/${encodeURIComponent(propertyKey)}`)
|
|
1324
|
+
.pipe(map((response) => {
|
|
1325
|
+
const payload = response && 'data' in response ? response.data : response;
|
|
1326
|
+
// The response's own view type decides how a picked value has to
|
|
1327
|
+
// serialize — the item keys look alike, so this is the only signal.
|
|
1328
|
+
const valueType = NUMERIC_VALUE_VIEW_TYPES.has(normalizeViewType(payload?.viewType))
|
|
1329
|
+
? 'number'
|
|
1330
|
+
: 'string';
|
|
1331
|
+
return (payload?.items ?? []).map((item) => ({ ...item, valueType }));
|
|
1332
|
+
}), tap((items) => this.propertyItemsCache.set(propertyKey, items)), catchError((error) => {
|
|
1333
|
+
console.warn('Failed to load property items:', error);
|
|
1334
|
+
return of([]);
|
|
1335
|
+
}));
|
|
1336
|
+
}
|
|
1337
|
+
normalizeResponse(response) {
|
|
1338
|
+
const payload = response?.data ?? response;
|
|
1339
|
+
if (!payload)
|
|
1340
|
+
return null;
|
|
1341
|
+
const properties = this.normalizeProperties(payload.properties ?? []);
|
|
1342
|
+
return {
|
|
1343
|
+
...payload,
|
|
1344
|
+
properties,
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
normalizeProperties(raw) {
|
|
1348
|
+
return (raw ?? [])
|
|
1349
|
+
.map((prop) => {
|
|
1350
|
+
const key = prop?.key ?? prop?.propertyKey;
|
|
1351
|
+
if (!key)
|
|
1352
|
+
return null;
|
|
1353
|
+
const name = prop?.name ?? prop?.displayName ?? key;
|
|
1354
|
+
return {
|
|
1355
|
+
...prop,
|
|
1356
|
+
key,
|
|
1357
|
+
name,
|
|
1358
|
+
};
|
|
1359
|
+
})
|
|
1360
|
+
.filter((prop) => Boolean(prop));
|
|
1361
|
+
}
|
|
1362
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1363
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, providedIn: 'root' });
|
|
1364
|
+
}
|
|
1365
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, decorators: [{
|
|
1366
|
+
type: Injectable,
|
|
1367
|
+
args: [{ providedIn: 'root' }]
|
|
1368
|
+
}] });
|
|
1369
|
+
|
|
1230
1370
|
/**
|
|
1231
1371
|
* Field Conditions Dialog
|
|
1232
1372
|
*
|
|
@@ -1235,6 +1375,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1235
1375
|
*/
|
|
1236
1376
|
class FBFieldConditions {
|
|
1237
1377
|
modalService = inject(ModalService);
|
|
1378
|
+
contextService = inject(FormBuilderContextService);
|
|
1238
1379
|
ref = inject(ModalRef);
|
|
1239
1380
|
transloco = inject(TranslocoService);
|
|
1240
1381
|
destroyRef = inject(DestroyRef);
|
|
@@ -1253,11 +1394,16 @@ class FBFieldConditions {
|
|
|
1253
1394
|
// Condition-specific functions and operators
|
|
1254
1395
|
functionCategories = CONDITION_FUNCTION_CATEGORIES;
|
|
1255
1396
|
operators = CONDITION_OPERATORS;
|
|
1397
|
+
/** Values already loaded, keyed by property key. */
|
|
1398
|
+
propertyValues = signal({}, ...(ngDevMode ? [{ debugName: "propertyValues" }] : /* istanbul ignore next */ []));
|
|
1399
|
+
/** Property key whose values are being fetched right now. */
|
|
1400
|
+
loadingValuesFor = signal(null, ...(ngDevMode ? [{ debugName: "loadingValuesFor" }] : /* istanbul ignore next */ []));
|
|
1256
1401
|
/** Property display items for toolbar; keys remain the persisted formula value. */
|
|
1257
1402
|
propertyItems = computed(() => this.availableFields().map((field) => ({
|
|
1258
1403
|
key: field.key,
|
|
1259
1404
|
name: field.name,
|
|
1260
1405
|
description: field.type ? `${field.name} (${field.type})` : field.name,
|
|
1406
|
+
hasValues: isValueBackedViewType(field.type),
|
|
1261
1407
|
})), ...(ngDevMode ? [{ debugName: "propertyItems" }] : /* istanbul ignore next */ []));
|
|
1262
1408
|
/** Toolbar labels */
|
|
1263
1409
|
toolbarLabels = computed(() => {
|
|
@@ -1267,6 +1413,8 @@ class FBFieldConditions {
|
|
|
1267
1413
|
properties: this.transloco.translate('formBuilder.toolbar-fields'),
|
|
1268
1414
|
operators: this.transloco.translate('formBuilder.toolbar-operators'),
|
|
1269
1415
|
noPropertiesAvailable: this.transloco.translate('formBuilder.toolbar-no-fields'),
|
|
1416
|
+
values: this.transloco.translate('formBuilder.toolbar-values'),
|
|
1417
|
+
noValuesAvailable: this.transloco.translate('formBuilder.toolbar-no-values'),
|
|
1270
1418
|
};
|
|
1271
1419
|
}, ...(ngDevMode ? [{ debugName: "toolbarLabels" }] : /* istanbul ignore next */ []));
|
|
1272
1420
|
constructor() {
|
|
@@ -1289,6 +1437,28 @@ class FBFieldConditions {
|
|
|
1289
1437
|
}
|
|
1290
1438
|
}
|
|
1291
1439
|
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Load the selectable values of a lookup / status field the first time its
|
|
1442
|
+
* value list is opened, so the compared value is picked from the real source
|
|
1443
|
+
* instead of typed as free text.
|
|
1444
|
+
*/
|
|
1445
|
+
onPropertyValuesRequest(propertyKey) {
|
|
1446
|
+
if (this.propertyValues()[propertyKey] !== undefined)
|
|
1447
|
+
return;
|
|
1448
|
+
this.loadingValuesFor.set(propertyKey);
|
|
1449
|
+
this.contextService
|
|
1450
|
+
.getPropertyItems(propertyKey)
|
|
1451
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1452
|
+
.subscribe((items) => {
|
|
1453
|
+
this.propertyValues.update((current) => ({
|
|
1454
|
+
...current,
|
|
1455
|
+
[propertyKey]: items,
|
|
1456
|
+
}));
|
|
1457
|
+
if (this.loadingValuesFor() === propertyKey) {
|
|
1458
|
+
this.loadingValuesFor.set(null);
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1292
1462
|
/** Handle block insert from toolbar */
|
|
1293
1463
|
onBlockInsert(block) {
|
|
1294
1464
|
const editor = this.editorRef();
|
|
@@ -1326,7 +1496,7 @@ class FBFieldConditions {
|
|
|
1326
1496
|
return raw.trim();
|
|
1327
1497
|
}
|
|
1328
1498
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FBFieldConditions, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1329
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.8", 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\r\n [class]=\"[\r\n modalService.contentClass,\r\n 'min-w-0',\r\n 'p-4',\r\n 'overflow-y-hidden!',\r\n 'max-[640px]:p-3',\r\n ]\"\r\n >\r\n <div class=\"mt-4 flex h-full min-h-0 flex-col gap-3 overflow-y-auto pb-10\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyItems()\"\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\r\n [class]=\"[\r\n modalService.footerClass,\r\n '!h-auto',\r\n 'min-h-0',\r\n 'flex-col',\r\n 'gap-2',\r\n 'sm:flex-row',\r\n 'sm:items-center',\r\n 'sm:justify-end',\r\n ]\"\r\n >\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n variant=\"outlined\"\r\n [disabled]=\"submitting()\"\r\n styleClass=\"w-full sm:w-auto\"\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 styleClass=\"w-full sm:w-auto\"\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", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "visibleTabs", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
1499
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.8", 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\r\n [class]=\"[\r\n modalService.contentClass,\r\n 'min-w-0',\r\n 'p-4',\r\n 'overflow-y-hidden!',\r\n 'max-[640px]:p-3',\r\n ]\"\r\n >\r\n <div class=\"mt-4 flex h-full min-h-0 flex-col gap-3 overflow-y-auto pb-10\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyItems()\"\r\n [propertyValues]=\"propertyValues()\"\r\n [loadingValuesFor]=\"loadingValuesFor()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n (onPropertyValuesRequest)=\"onPropertyValuesRequest($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\r\n [class]=\"[\r\n modalService.footerClass,\r\n '!h-auto',\r\n 'min-h-0',\r\n 'flex-col',\r\n 'gap-2',\r\n 'sm:flex-row',\r\n 'sm:items-center',\r\n 'sm:justify-end',\r\n ]\"\r\n >\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n variant=\"outlined\"\r\n [disabled]=\"submitting()\"\r\n styleClass=\"w-full sm:w-auto\"\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 styleClass=\"w-full sm:w-auto\"\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", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "propertyValues", "loadingValuesFor", "functionCategories", "operators", "initialTab", "visibleTabs", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange", "onPropertyValuesRequest"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }] });
|
|
1330
1500
|
}
|
|
1331
1501
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FBFieldConditions, decorators: [{
|
|
1332
1502
|
type: Component,
|
|
@@ -1336,7 +1506,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1336
1506
|
Button,
|
|
1337
1507
|
FormulaToolbar,
|
|
1338
1508
|
FormulaEditor,
|
|
1339
|
-
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div\r\n [class]=\"[\r\n modalService.contentClass,\r\n 'min-w-0',\r\n 'p-4',\r\n 'overflow-y-hidden!',\r\n 'max-[640px]:p-3',\r\n ]\"\r\n >\r\n <div class=\"mt-4 flex h-full min-h-0 flex-col gap-3 overflow-y-auto pb-10\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyItems()\"\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\r\n [class]=\"[\r\n modalService.footerClass,\r\n '!h-auto',\r\n 'min-h-0',\r\n 'flex-col',\r\n 'gap-2',\r\n 'sm:flex-row',\r\n 'sm:items-center',\r\n 'sm:justify-end',\r\n ]\"\r\n >\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n variant=\"outlined\"\r\n [disabled]=\"submitting()\"\r\n styleClass=\"w-full sm:w-auto\"\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 styleClass=\"w-full sm:w-auto\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1509
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div\r\n [class]=\"[\r\n modalService.contentClass,\r\n 'min-w-0',\r\n 'p-4',\r\n 'overflow-y-hidden!',\r\n 'max-[640px]:p-3',\r\n ]\"\r\n >\r\n <div class=\"mt-4 flex h-full min-h-0 flex-col gap-3 overflow-y-auto pb-10\">\r\n <!-- Formula Toolbar -->\r\n <mt-formula-toolbar\r\n [knownProperties]=\"propertyItems()\"\r\n [propertyValues]=\"propertyValues()\"\r\n [loadingValuesFor]=\"loadingValuesFor()\"\r\n [functionCategories]=\"functionCategories\"\r\n [operators]=\"operators\"\r\n [labels]=\"toolbarLabels()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n (onPropertyValuesRequest)=\"onPropertyValuesRequest($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\r\n [class]=\"[\r\n modalService.footerClass,\r\n '!h-auto',\r\n 'min-h-0',\r\n 'flex-col',\r\n 'gap-2',\r\n 'sm:flex-row',\r\n 'sm:items-center',\r\n 'sm:justify-end',\r\n ]\"\r\n >\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n variant=\"outlined\"\r\n [disabled]=\"submitting()\"\r\n styleClass=\"w-full sm:w-auto\"\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 styleClass=\"w-full sm:w-auto\"\r\n (onClick)=\"onSave()\"\r\n />\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1340
1510
|
}], ctorParameters: () => [], propDecorators: { initialFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFormula", required: false }] }], availableFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableFields", required: false }] }], editorRef: [{ type: i0.ViewChild, args: ['editor', { isSignal: true }] }] } });
|
|
1341
1511
|
|
|
1342
1512
|
class FBFieldForm {
|
|
@@ -2453,77 +2623,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2453
2623
|
args: [{ selector: 'mt-fb-validation-rules', standalone: true, imports: [TranslocoDirective, Table, Button], template: "<ng-container *transloco=\"let t; prefix: 'formBuilder'\">\r\n <div\r\n [class]=\"[\r\n modalService.contentClass,\r\n 'min-w-0',\r\n 'p-4',\r\n 'overflow-y-auto',\r\n 'max-[640px]:p-3',\r\n ]\"\r\n >\r\n <div class=\"flex justify-end pb-4 max-[640px]:justify-stretch\">\r\n <mt-button\r\n [label]=\"t('add-validation-rule')\"\r\n severity=\"primary\"\r\n variant=\"outlined\"\r\n styleClass=\"w-full sm:w-auto\"\r\n (onClick)=\"openCreate()\"\r\n ></mt-button>\r\n </div>\r\n <mt-table\r\n noCard\r\n [data]=\"tableRows()\"\r\n [columns]=\"tableColumns()\"\r\n [rowActions]=\"rowActions()\"\r\n storageKey=\"fb-validation-rules-table\"\r\n (cellChange)=\"onCellChange($event)\"\r\n ></mt-table>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
2454
2624
|
}], ctorParameters: () => [] });
|
|
2455
2625
|
|
|
2456
|
-
class FormBuilderContextService {
|
|
2457
|
-
http = inject(HttpClient);
|
|
2458
|
-
baseUrl = 'context';
|
|
2459
|
-
/**
|
|
2460
|
-
* Resolves context data including navigation paths and properties.
|
|
2461
|
-
* No caching - always fetches fresh data.
|
|
2462
|
-
*/
|
|
2463
|
-
resolveContext(contextKey, options = {}) {
|
|
2464
|
-
if (!contextKey)
|
|
2465
|
-
return of(null);
|
|
2466
|
-
return this.http
|
|
2467
|
-
.post(`${this.baseUrl}/resolve`, {
|
|
2468
|
-
contextKey,
|
|
2469
|
-
displayTags: options.displayTags,
|
|
2470
|
-
propertyFlags: options.propertyFlags,
|
|
2471
|
-
matchAllTags: options.matchAllTags,
|
|
2472
|
-
includeVirtual: options.includeVirtual,
|
|
2473
|
-
includeNavigationPaths: options.includeNavigationPaths,
|
|
2474
|
-
includeConnectionMetadata: options.includeConnectionMetadata,
|
|
2475
|
-
includeRelatedContextValues: options.includeRelatedContextValues,
|
|
2476
|
-
relatedContextsToInclude: options.relatedContextsToInclude,
|
|
2477
|
-
// Consumer-supplied fields win on collision with any field above.
|
|
2478
|
-
...options.extraParams,
|
|
2479
|
-
})
|
|
2480
|
-
.pipe(map((response) => this.normalizeResponse(response)), catchError((error) => {
|
|
2481
|
-
console.warn('Failed to resolve context:', error);
|
|
2482
|
-
return of(null);
|
|
2483
|
-
}));
|
|
2484
|
-
}
|
|
2485
|
-
/**
|
|
2486
|
-
* Gets properties for a context.
|
|
2487
|
-
* No caching - always fetches fresh data.
|
|
2488
|
-
*/
|
|
2489
|
-
getProperties(contextKey, options = {}) {
|
|
2490
|
-
if (!contextKey)
|
|
2491
|
-
return of([]);
|
|
2492
|
-
return this.resolveContext(contextKey, options).pipe(map((resolved) => resolved?.properties ?? []));
|
|
2493
|
-
}
|
|
2494
|
-
normalizeResponse(response) {
|
|
2495
|
-
const payload = response?.data ?? response;
|
|
2496
|
-
if (!payload)
|
|
2497
|
-
return null;
|
|
2498
|
-
const properties = this.normalizeProperties(payload.properties ?? []);
|
|
2499
|
-
return {
|
|
2500
|
-
...payload,
|
|
2501
|
-
properties,
|
|
2502
|
-
};
|
|
2503
|
-
}
|
|
2504
|
-
normalizeProperties(raw) {
|
|
2505
|
-
return (raw ?? [])
|
|
2506
|
-
.map((prop) => {
|
|
2507
|
-
const key = prop?.key ?? prop?.propertyKey;
|
|
2508
|
-
if (!key)
|
|
2509
|
-
return null;
|
|
2510
|
-
const name = prop?.name ?? prop?.displayName ?? key;
|
|
2511
|
-
return {
|
|
2512
|
-
...prop,
|
|
2513
|
-
key,
|
|
2514
|
-
name,
|
|
2515
|
-
};
|
|
2516
|
-
})
|
|
2517
|
-
.filter((prop) => Boolean(prop));
|
|
2518
|
-
}
|
|
2519
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2520
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, providedIn: 'root' });
|
|
2521
|
-
}
|
|
2522
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormBuilderContextService, decorators: [{
|
|
2523
|
-
type: Injectable,
|
|
2524
|
-
args: [{ providedIn: 'root' }]
|
|
2525
|
-
}] });
|
|
2526
|
-
|
|
2527
2626
|
const SECTIONS_DROP_LIST_ID = 'sections-list';
|
|
2528
2627
|
const SCOPE_ORDER = [
|
|
2529
2628
|
'Current',
|