@masterteam/formula-builder 0.0.8 → 0.0.10
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.
|
@@ -42,7 +42,7 @@ const ERROR_VALIDATION = {
|
|
|
42
42
|
};
|
|
43
43
|
class FormulaValidatorService {
|
|
44
44
|
http = inject(HttpClient);
|
|
45
|
-
apiUrl = '
|
|
45
|
+
apiUrl = 'formulas';
|
|
46
46
|
getBaseUrl(mode) {
|
|
47
47
|
return mode === 'current-only'
|
|
48
48
|
? `${this.apiUrl}/current-only`
|
|
@@ -51,9 +51,9 @@ class FormulaValidatorService {
|
|
|
51
51
|
/** Debounce time in ms */
|
|
52
52
|
DEBOUNCE_MS = 300;
|
|
53
53
|
/** Current validation result */
|
|
54
|
-
_validation = signal(EMPTY_VALIDATION, ...(ngDevMode ? [{ debugName: "_validation" }] : []));
|
|
54
|
+
_validation = signal(EMPTY_VALIDATION, ...(ngDevMode ? [{ debugName: "_validation" }] : /* istanbul ignore next */ []));
|
|
55
55
|
/** Loading state */
|
|
56
|
-
_isValidating = signal(false, ...(ngDevMode ? [{ debugName: "_isValidating" }] : []));
|
|
56
|
+
_isValidating = signal(false, ...(ngDevMode ? [{ debugName: "_isValidating" }] : /* istanbul ignore next */ []));
|
|
57
57
|
/** Validation subject for debouncing */
|
|
58
58
|
validateSubject = new Subject();
|
|
59
59
|
unwrapResponse(response) {
|
|
@@ -169,17 +169,17 @@ class FormulaValidatorService {
|
|
|
169
169
|
getComplexity() {
|
|
170
170
|
return this._validation().complexity;
|
|
171
171
|
}
|
|
172
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
173
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
172
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaValidatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
173
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaValidatorService, providedIn: 'root' });
|
|
174
174
|
}
|
|
175
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
175
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaValidatorService, decorators: [{
|
|
176
176
|
type: Injectable,
|
|
177
177
|
args: [{ providedIn: 'root' }]
|
|
178
178
|
}], ctorParameters: () => [] });
|
|
179
179
|
|
|
180
180
|
class FormulaContextService {
|
|
181
181
|
http = inject(HttpClient);
|
|
182
|
-
apiUrl = '
|
|
182
|
+
apiUrl = 'formulas';
|
|
183
183
|
getBaseUrl(mode) {
|
|
184
184
|
return mode === 'current-only'
|
|
185
185
|
? `${this.apiUrl}/current-only`
|
|
@@ -227,9 +227,7 @@ class FormulaContextService {
|
|
|
227
227
|
}));
|
|
228
228
|
}
|
|
229
229
|
getScopeProperties(schemaId, component, tablePath, moduleId, mode = 'default') {
|
|
230
|
-
if (!schemaId || !
|
|
231
|
-
return of([]);
|
|
232
|
-
if (mode === 'current-only')
|
|
230
|
+
if (!schemaId || !tablePath)
|
|
233
231
|
return of([]);
|
|
234
232
|
const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}`;
|
|
235
233
|
const cached = this.propertiesCache.get(cacheKey);
|
|
@@ -238,9 +236,11 @@ class FormulaContextService {
|
|
|
238
236
|
}
|
|
239
237
|
const params = new URLSearchParams({
|
|
240
238
|
schemaId: schemaId.toString(),
|
|
241
|
-
component,
|
|
242
239
|
tablePath,
|
|
243
240
|
});
|
|
241
|
+
if (component) {
|
|
242
|
+
params.set('component', component);
|
|
243
|
+
}
|
|
244
244
|
if (moduleId !== undefined) {
|
|
245
245
|
params.set('moduleId', moduleId.toString());
|
|
246
246
|
}
|
|
@@ -256,8 +256,6 @@ class FormulaContextService {
|
|
|
256
256
|
getNextTokens(schemaId, component, tablePath, moduleId, mode = 'default') {
|
|
257
257
|
if (!schemaId || !component || !tablePath)
|
|
258
258
|
return of(null);
|
|
259
|
-
if (mode === 'current-only')
|
|
260
|
-
return of(null);
|
|
261
259
|
const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}`;
|
|
262
260
|
const cached = this.nextTokensCache.get(cacheKey);
|
|
263
261
|
if (cached) {
|
|
@@ -282,17 +280,17 @@ class FormulaContextService {
|
|
|
282
280
|
return of(null);
|
|
283
281
|
}));
|
|
284
282
|
}
|
|
285
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
286
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
283
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
284
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaContextService, providedIn: 'root' });
|
|
287
285
|
}
|
|
288
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaContextService, decorators: [{
|
|
289
287
|
type: Injectable,
|
|
290
288
|
args: [{ providedIn: 'root' }]
|
|
291
289
|
}] });
|
|
292
290
|
|
|
293
291
|
class FormulaAutocompleteService {
|
|
294
292
|
http = inject(HttpClient);
|
|
295
|
-
apiUrl = '
|
|
293
|
+
apiUrl = 'formulas';
|
|
296
294
|
getBaseUrl(mode) {
|
|
297
295
|
return mode === 'current-only'
|
|
298
296
|
? `${this.apiUrl}/current-only`
|
|
@@ -323,10 +321,10 @@ class FormulaAutocompleteService {
|
|
|
323
321
|
return of(null);
|
|
324
322
|
}));
|
|
325
323
|
}
|
|
326
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
327
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
324
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaAutocompleteService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
325
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaAutocompleteService, providedIn: 'root' });
|
|
328
326
|
}
|
|
329
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
327
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaAutocompleteService, decorators: [{
|
|
330
328
|
type: Injectable,
|
|
331
329
|
args: [{ providedIn: 'root' }]
|
|
332
330
|
}] });
|
|
@@ -425,13 +423,14 @@ const DEFAULT_TOOLBAR_TABS = [
|
|
|
425
423
|
'properties',
|
|
426
424
|
'operators',
|
|
427
425
|
];
|
|
428
|
-
const
|
|
426
|
+
const PROPERTY_SCOPE_PRIORITY = [
|
|
429
427
|
'Current',
|
|
430
428
|
'Host',
|
|
431
429
|
'Parent',
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
430
|
+
'Children',
|
|
431
|
+
'Descendants',
|
|
432
|
+
'Modules',
|
|
433
|
+
'Schedule',
|
|
435
434
|
];
|
|
436
435
|
const SCOPE_ICON_MAP = {
|
|
437
436
|
Current: 'map.marker-pin-01',
|
|
@@ -441,37 +440,38 @@ const SCOPE_ICON_MAP = {
|
|
|
441
440
|
Children: 'user.users-01',
|
|
442
441
|
Descendants: 'custom.hierarchy-structure',
|
|
443
442
|
Modules: 'file.folder-closed',
|
|
443
|
+
Schedule: 'general.placeholder',
|
|
444
444
|
};
|
|
445
445
|
class FormulaBuilder {
|
|
446
446
|
validatorService = inject(FormulaValidatorService);
|
|
447
447
|
contextService = inject(FormulaContextService);
|
|
448
448
|
autocompleteService = inject(FormulaAutocompleteService);
|
|
449
449
|
/** Reference to the formula editor */
|
|
450
|
-
editor = viewChild('formulaEditor', ...(ngDevMode ? [{ debugName: "editor" }] : []));
|
|
451
|
-
codeEditor = viewChild('formulaEditorCode', ...(ngDevMode ? [{ debugName: "codeEditor" }] : []));
|
|
450
|
+
editor = viewChild('formulaEditor', ...(ngDevMode ? [{ debugName: "editor" }] : /* istanbul ignore next */ []));
|
|
451
|
+
codeEditor = viewChild('formulaEditorCode', ...(ngDevMode ? [{ debugName: "codeEditor" }] : /* istanbul ignore next */ []));
|
|
452
452
|
// ===== INPUTS =====
|
|
453
453
|
/** Properties per table path (e.g., "Current", "Children.Project") */
|
|
454
|
-
propertiesByPath = input({}, ...(ngDevMode ? [{ debugName: "propertiesByPath" }] : []));
|
|
454
|
+
propertiesByPath = input({}, ...(ngDevMode ? [{ debugName: "propertiesByPath" }] : /* istanbul ignore next */ []));
|
|
455
455
|
/** Level schema ID for validation context */
|
|
456
|
-
levelSchemaId = input(...(ngDevMode ? [undefined, { debugName: "levelSchemaId" }] : []));
|
|
456
|
+
levelSchemaId = input(...(ngDevMode ? [undefined, { debugName: "levelSchemaId" }] : /* istanbul ignore next */ []));
|
|
457
457
|
/** Optional module ID when the formula is scoped inside a level-linked module */
|
|
458
|
-
moduleId = input(...(ngDevMode ? [undefined, { debugName: "moduleId" }] : []));
|
|
458
|
+
moduleId = input(...(ngDevMode ? [undefined, { debugName: "moduleId" }] : /* istanbul ignore next */ []));
|
|
459
459
|
/** Optional current context entity key, e.g. LevelData or Risk */
|
|
460
|
-
contextEntityTypeKey = input(...(ngDevMode ? [undefined, { debugName: "contextEntityTypeKey" }] : []));
|
|
460
|
+
contextEntityTypeKey = input(...(ngDevMode ? [undefined, { debugName: "contextEntityTypeKey" }] : /* istanbul ignore next */ []));
|
|
461
461
|
/** Template ID for validation context */
|
|
462
|
-
templateId = input(...(ngDevMode ? [undefined, { debugName: "templateId" }] : []));
|
|
462
|
+
templateId = input(...(ngDevMode ? [undefined, { debugName: "templateId" }] : /* istanbul ignore next */ []));
|
|
463
463
|
/** Placeholder text */
|
|
464
|
-
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
464
|
+
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
465
465
|
/** Hide the top toolbar */
|
|
466
|
-
hideToolbar = input(false, ...(ngDevMode ? [{ debugName: "hideToolbar" }] : []));
|
|
466
|
+
hideToolbar = input(false, ...(ngDevMode ? [{ debugName: "hideToolbar" }] : /* istanbul ignore next */ []));
|
|
467
467
|
/** Hide the status bar */
|
|
468
|
-
hideStatusBar = input(false, ...(ngDevMode ? [{ debugName: "hideStatusBar" }] : []));
|
|
468
|
+
hideStatusBar = input(false, ...(ngDevMode ? [{ debugName: "hideStatusBar" }] : /* istanbul ignore next */ []));
|
|
469
469
|
/** Visible toolbar tabs */
|
|
470
|
-
toolbarTabs = input(DEFAULT_TOOLBAR_TABS, ...(ngDevMode ? [{ debugName: "toolbarTabs" }] : []));
|
|
470
|
+
toolbarTabs = input(DEFAULT_TOOLBAR_TABS, ...(ngDevMode ? [{ debugName: "toolbarTabs" }] : /* istanbul ignore next */ []));
|
|
471
471
|
/** Force code editor mode and hide the mode toggle */
|
|
472
|
-
codeOnly = input(false, ...(ngDevMode ? [{ debugName: "codeOnly" }] : []));
|
|
472
|
+
codeOnly = input(false, ...(ngDevMode ? [{ debugName: "codeOnly" }] : /* istanbul ignore next */ []));
|
|
473
473
|
/** Use Process Builder current-only Formula Engine endpoints */
|
|
474
|
-
isProcessBuilder = input(false, ...(ngDevMode ? [{ debugName: "isProcessBuilder" }] : []));
|
|
474
|
+
isProcessBuilder = input(false, ...(ngDevMode ? [{ debugName: "isProcessBuilder" }] : /* istanbul ignore next */ []));
|
|
475
475
|
// ===== OUTPUTS =====
|
|
476
476
|
/** Emits validation result on change */
|
|
477
477
|
validationChange = output();
|
|
@@ -479,13 +479,13 @@ class FormulaBuilder {
|
|
|
479
479
|
tokensChange = output();
|
|
480
480
|
// ===== STATE =====
|
|
481
481
|
/** Editor focus state */
|
|
482
|
-
hasFocus = signal(false, ...(ngDevMode ? [{ debugName: "hasFocus" }] : []));
|
|
482
|
+
hasFocus = signal(false, ...(ngDevMode ? [{ debugName: "hasFocus" }] : /* istanbul ignore next */ []));
|
|
483
483
|
/** Form control value */
|
|
484
|
-
expressionValue = signal('', ...(ngDevMode ? [{ debugName: "expressionValue" }] : []));
|
|
485
|
-
builderValue = signal([], ...(ngDevMode ? [{ debugName: "builderValue" }] : []));
|
|
484
|
+
expressionValue = signal('', ...(ngDevMode ? [{ debugName: "expressionValue" }] : /* istanbul ignore next */ []));
|
|
485
|
+
builderValue = signal([], ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
|
|
486
486
|
/** Tokens for the editor */
|
|
487
|
-
tokens = signal([], ...(ngDevMode ? [{ debugName: "tokens" }] : []));
|
|
488
|
-
expression = computed(() => this.expressionValue(), ...(ngDevMode ? [{ debugName: "expression" }] : []));
|
|
487
|
+
tokens = signal([], ...(ngDevMode ? [{ debugName: "tokens" }] : /* istanbul ignore next */ []));
|
|
488
|
+
expression = computed(() => this.expressionValue(), ...(ngDevMode ? [{ debugName: "expression" }] : /* istanbul ignore next */ []));
|
|
489
489
|
resolvedContextEntityTypeKey = computed(() => {
|
|
490
490
|
const value = this.contextEntityTypeKey();
|
|
491
491
|
if (typeof value !== 'string') {
|
|
@@ -493,7 +493,7 @@ class FormulaBuilder {
|
|
|
493
493
|
}
|
|
494
494
|
const normalized = value.trim();
|
|
495
495
|
return normalized.length > 0 ? normalized : undefined;
|
|
496
|
-
}, ...(ngDevMode ? [{ debugName: "resolvedContextEntityTypeKey" }] : []));
|
|
496
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedContextEntityTypeKey" }] : /* istanbul ignore next */ []));
|
|
497
497
|
isModuleContext = computed(() => {
|
|
498
498
|
const contextType = this.resolvedContextEntityTypeKey() ??
|
|
499
499
|
this.builderContext()?.contextEntityTypeKey;
|
|
@@ -502,7 +502,7 @@ class FormulaBuilder {
|
|
|
502
502
|
}
|
|
503
503
|
const normalized = contextType.trim().toLowerCase();
|
|
504
504
|
return normalized.length > 0 && normalized !== 'leveldata';
|
|
505
|
-
}, ...(ngDevMode ? [{ debugName: "isModuleContext" }] : []));
|
|
505
|
+
}, ...(ngDevMode ? [{ debugName: "isModuleContext" }] : /* istanbul ignore next */ []));
|
|
506
506
|
autocompleteProvider = (request) => {
|
|
507
507
|
return this.autocompleteService.getSuggestions({
|
|
508
508
|
...request,
|
|
@@ -515,37 +515,37 @@ class FormulaBuilder {
|
|
|
515
515
|
/** Track last synced builder JSON to avoid loops */
|
|
516
516
|
lastBuilderJson = '';
|
|
517
517
|
/** Editor mode */
|
|
518
|
-
editorMode = signal('builder', ...(ngDevMode ? [{ debugName: "editorMode" }] : []));
|
|
519
|
-
isCodeMode = computed(() => this.editorMode() === 'code', ...(ngDevMode ? [{ debugName: "isCodeMode" }] : []));
|
|
520
|
-
isCodeModeLocked = signal(false, ...(ngDevMode ? [{ debugName: "isCodeModeLocked" }] : []));
|
|
521
|
-
showModeToggle = computed(() => !this.codeOnly(), ...(ngDevMode ? [{ debugName: "showModeToggle" }] : []));
|
|
522
|
-
formulaApiMode = computed(() => this.isProcessBuilder() ? 'current-only' : 'default', ...(ngDevMode ? [{ debugName: "formulaApiMode" }] : []));
|
|
518
|
+
editorMode = signal('builder', ...(ngDevMode ? [{ debugName: "editorMode" }] : /* istanbul ignore next */ []));
|
|
519
|
+
isCodeMode = computed(() => this.editorMode() === 'code', ...(ngDevMode ? [{ debugName: "isCodeMode" }] : /* istanbul ignore next */ []));
|
|
520
|
+
isCodeModeLocked = signal(false, ...(ngDevMode ? [{ debugName: "isCodeModeLocked" }] : /* istanbul ignore next */ []));
|
|
521
|
+
showModeToggle = computed(() => !this.codeOnly(), ...(ngDevMode ? [{ debugName: "showModeToggle" }] : /* istanbul ignore next */ []));
|
|
522
|
+
formulaApiMode = computed(() => this.isProcessBuilder() ? 'current-only' : 'default', ...(ngDevMode ? [{ debugName: "formulaApiMode" }] : /* istanbul ignore next */ []));
|
|
523
523
|
resolvedToolbarTabs = computed(() => {
|
|
524
524
|
const tabs = this.toolbarTabs();
|
|
525
525
|
return tabs.length > 0 ? tabs : DEFAULT_TOOLBAR_TABS;
|
|
526
|
-
}, ...(ngDevMode ? [{ debugName: "resolvedToolbarTabs" }] : []));
|
|
526
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedToolbarTabs" }] : /* istanbul ignore next */ []));
|
|
527
527
|
codeModeSnapshotTokens = [];
|
|
528
528
|
codeModeSnapshotExpression = '';
|
|
529
529
|
/** ControlValueAccessor callbacks */
|
|
530
530
|
onChange = () => { };
|
|
531
531
|
onTouched = () => { };
|
|
532
532
|
/** Disabled state */
|
|
533
|
-
isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
533
|
+
isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
534
534
|
/** Validation result from service */
|
|
535
535
|
validation = this.validatorService.validation;
|
|
536
536
|
/** Is validation in progress */
|
|
537
537
|
isValidating = this.validatorService.isValidating;
|
|
538
538
|
/** Builder context from API */
|
|
539
|
-
builderContext = signal(null, ...(ngDevMode ? [{ debugName: "builderContext" }] : []));
|
|
539
|
+
builderContext = signal(null, ...(ngDevMode ? [{ debugName: "builderContext" }] : /* istanbul ignore next */ []));
|
|
540
540
|
/** Function categories from context */
|
|
541
|
-
functionCategories = computed(() => this.builderContext()?.functions?.categories ?? [], ...(ngDevMode ? [{ debugName: "functionCategories" }] : []));
|
|
541
|
+
functionCategories = computed(() => this.builderContext()?.functions?.categories ?? [], ...(ngDevMode ? [{ debugName: "functionCategories" }] : /* istanbul ignore next */ []));
|
|
542
542
|
/** Is builder context loading */
|
|
543
|
-
isContextLoading = signal(false, ...(ngDevMode ? [{ debugName: "isContextLoading" }] : []));
|
|
543
|
+
isContextLoading = signal(false, ...(ngDevMode ? [{ debugName: "isContextLoading" }] : /* istanbul ignore next */ []));
|
|
544
544
|
contextRequestId = 0;
|
|
545
545
|
/** Properties loaded from API */
|
|
546
|
-
propertiesByPathApi = signal({}, ...(ngDevMode ? [{ debugName: "propertiesByPathApi" }] : []));
|
|
546
|
+
propertiesByPathApi = signal({}, ...(ngDevMode ? [{ debugName: "propertiesByPathApi" }] : /* istanbul ignore next */ []));
|
|
547
547
|
/** Current property list loading path */
|
|
548
|
-
propertyLoadingPath = signal(null, ...(ngDevMode ? [{ debugName: "propertyLoadingPath" }] : []));
|
|
548
|
+
propertyLoadingPath = signal(null, ...(ngDevMode ? [{ debugName: "propertyLoadingPath" }] : /* istanbul ignore next */ []));
|
|
549
549
|
constructor() {
|
|
550
550
|
// Validate when formula changes
|
|
551
551
|
effect(() => {
|
|
@@ -590,16 +590,9 @@ class FormulaBuilder {
|
|
|
590
590
|
return;
|
|
591
591
|
}
|
|
592
592
|
if (context.currentProperties?.length) {
|
|
593
|
-
const scalarOperators = context.schemaDescriptor?.operators?.filter((operator) => operator.cardinality === 'scalar' &&
|
|
594
|
-
!operator.requiresParameter &&
|
|
595
|
-
!operator.parameterKind) ?? [];
|
|
596
|
-
const scopedProperties = scalarOperators.reduce((acc, operator) => ({
|
|
597
|
-
...acc,
|
|
598
|
-
[operator.token]: context.currentProperties,
|
|
599
|
-
}), {});
|
|
600
593
|
this.propertiesByPathApi.update((current) => ({
|
|
601
594
|
...current,
|
|
602
|
-
|
|
595
|
+
Current: context.currentProperties,
|
|
603
596
|
}));
|
|
604
597
|
}
|
|
605
598
|
});
|
|
@@ -618,15 +611,11 @@ class FormulaBuilder {
|
|
|
618
611
|
effect(() => {
|
|
619
612
|
const schemaId = this.levelSchemaId();
|
|
620
613
|
const moduleId = this.moduleId();
|
|
621
|
-
const
|
|
614
|
+
const apiMode = this.formulaApiMode();
|
|
622
615
|
const tablePath = this.resolvedTablePath();
|
|
623
616
|
const component = this.propertiesComponent();
|
|
624
617
|
const context = this.builderContext();
|
|
625
|
-
if (
|
|
626
|
-
!schemaId ||
|
|
627
|
-
!tablePath ||
|
|
628
|
-
!component ||
|
|
629
|
-
!context) {
|
|
618
|
+
if (!schemaId || !tablePath || !context) {
|
|
630
619
|
this.propertyLoadingPath.set(null);
|
|
631
620
|
return;
|
|
632
621
|
}
|
|
@@ -637,7 +626,7 @@ class FormulaBuilder {
|
|
|
637
626
|
}
|
|
638
627
|
this.propertyLoadingPath.set(tablePath);
|
|
639
628
|
this.contextService
|
|
640
|
-
.getScopeProperties(schemaId, component, tablePath, moduleId)
|
|
629
|
+
.getScopeProperties(schemaId, component, tablePath, moduleId, apiMode)
|
|
641
630
|
.subscribe((props) => {
|
|
642
631
|
this.propertiesByPathApi.update((current) => ({
|
|
643
632
|
...current,
|
|
@@ -922,38 +911,53 @@ class FormulaBuilder {
|
|
|
922
911
|
this.codeModeSnapshotExpression = '';
|
|
923
912
|
}
|
|
924
913
|
// ===== PROPERTY COMPOSER =====
|
|
925
|
-
propertyScope = signal('', ...(ngDevMode ? [{ debugName: "propertyScope" }] : []));
|
|
926
|
-
propertyPathSegments = signal([], ...(ngDevMode ? [{ debugName: "propertyPathSegments" }] : []));
|
|
927
|
-
propertyFieldKey = signal('', ...(ngDevMode ? [{ debugName: "propertyFieldKey" }] : []));
|
|
928
|
-
nextTokenRulesByPath = signal({}, ...(ngDevMode ? [{ debugName: "nextTokenRulesByPath" }] : []));
|
|
914
|
+
propertyScope = signal('', ...(ngDevMode ? [{ debugName: "propertyScope" }] : /* istanbul ignore next */ []));
|
|
915
|
+
propertyPathSegments = signal([], ...(ngDevMode ? [{ debugName: "propertyPathSegments" }] : /* istanbul ignore next */ []));
|
|
916
|
+
propertyFieldKey = signal('', ...(ngDevMode ? [{ debugName: "propertyFieldKey" }] : /* istanbul ignore next */ []));
|
|
917
|
+
nextTokenRulesByPath = signal({}, ...(ngDevMode ? [{ debugName: "nextTokenRulesByPath" }] : /* istanbul ignore next */ []));
|
|
929
918
|
contextOperators = computed(() => {
|
|
930
919
|
return this.builderContext()?.schemaDescriptor?.operators ?? [];
|
|
931
|
-
}, ...(ngDevMode ? [{ debugName: "contextOperators" }] : []));
|
|
932
|
-
baseOperator = computed(() => this.contextOperators().find((op) => op.token === this.propertyScope()), ...(ngDevMode ? [{ debugName: "baseOperator" }] : []));
|
|
920
|
+
}, ...(ngDevMode ? [{ debugName: "contextOperators" }] : /* istanbul ignore next */ []));
|
|
921
|
+
baseOperator = computed(() => this.contextOperators().find((op) => op.token === this.propertyScope()), ...(ngDevMode ? [{ debugName: "baseOperator" }] : /* istanbul ignore next */ []));
|
|
933
922
|
baseRule = computed(() => {
|
|
934
923
|
const scope = this.propertyScope();
|
|
935
924
|
return this.builderContext()?.schemaDescriptor?.nextTokenRules?.[scope];
|
|
936
|
-
}, ...(ngDevMode ? [{ debugName: "baseRule" }] : []));
|
|
925
|
+
}, ...(ngDevMode ? [{ debugName: "baseRule" }] : /* istanbul ignore next */ []));
|
|
937
926
|
availableScopes = computed(() => {
|
|
938
927
|
const operators = this.contextOperators();
|
|
939
928
|
if (operators.length === 0)
|
|
940
929
|
return [];
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
930
|
+
const baseRules = this.builderContext()?.schemaDescriptor?.nextTokenRules ?? {};
|
|
931
|
+
const priority = new Map(PROPERTY_SCOPE_PRIORITY.map((token, index) => [token, index]));
|
|
932
|
+
const scopedOperators = operators.filter((operator) => operator.token.trim().length > 0);
|
|
933
|
+
return [...scopedOperators]
|
|
934
|
+
.filter((operator) => {
|
|
935
|
+
const parameterKind = baseRules[operator.token]?.parameterKind ?? operator.parameterKind;
|
|
936
|
+
const requiresParameter = operator.requiresParameter ?? Boolean(parameterKind);
|
|
937
|
+
return ((!parameterKind && !requiresParameter) ||
|
|
938
|
+
getAllowedValues(baseRules[operator.token]).length > 0);
|
|
939
|
+
})
|
|
940
|
+
.sort((a, b) => {
|
|
941
|
+
const aPriority = priority.get(a.token) ?? Number.MAX_SAFE_INTEGER;
|
|
942
|
+
const bPriority = priority.get(b.token) ?? Number.MAX_SAFE_INTEGER;
|
|
943
|
+
if (aPriority !== bPriority) {
|
|
944
|
+
return aPriority - bPriority;
|
|
945
|
+
}
|
|
946
|
+
return a.token.localeCompare(b.token);
|
|
947
|
+
})
|
|
944
948
|
.map((operator) => ({
|
|
945
949
|
key: operator.token,
|
|
946
950
|
label: `@${operator.token}`,
|
|
947
951
|
requiresParam: operator.requiresParameter ?? false,
|
|
948
952
|
optional: !operator.requiresParameter,
|
|
949
953
|
}));
|
|
950
|
-
}, ...(ngDevMode ? [{ debugName: "availableScopes" }] : []));
|
|
954
|
+
}, ...(ngDevMode ? [{ debugName: "availableScopes" }] : /* istanbul ignore next */ []));
|
|
951
955
|
/** Radio card options for scope selection */
|
|
952
956
|
scopeOptions = computed(() => this.availableScopes().map((scope) => ({
|
|
953
957
|
id: scope.key,
|
|
954
958
|
name: this.getOperatorDisplayName(scope.key),
|
|
955
959
|
icon: SCOPE_ICON_MAP[scope.key] ?? DEFAULT_SCOPE_ICON,
|
|
956
|
-
})), ...(ngDevMode ? [{ debugName: "scopeOptions" }] : []));
|
|
960
|
+
})), ...(ngDevMode ? [{ debugName: "scopeOptions" }] : /* istanbul ignore next */ []));
|
|
957
961
|
/** Handle scope selection from radio cards */
|
|
958
962
|
onScopeChange(item) {
|
|
959
963
|
this.setPropertyScope(String(item.id));
|
|
@@ -967,20 +971,20 @@ class FormulaBuilder {
|
|
|
967
971
|
baseRule: this.baseRule(),
|
|
968
972
|
rulesByPath: this.nextTokenRulesByPath(),
|
|
969
973
|
});
|
|
970
|
-
}, ...(ngDevMode ? [{ debugName: "pathSegments" }] : []));
|
|
974
|
+
}, ...(ngDevMode ? [{ debugName: "pathSegments" }] : /* istanbul ignore next */ []));
|
|
971
975
|
isDirectAccess = computed(() => {
|
|
972
976
|
const operator = this.baseOperator();
|
|
973
977
|
const needsParameter = Boolean(operator?.parameterKind) || Boolean(operator?.requiresParameter);
|
|
974
978
|
return !needsParameter && this.pathSegments().length === 0;
|
|
975
|
-
}, ...(ngDevMode ? [{ debugName: "isDirectAccess" }] : []));
|
|
976
|
-
propertyTablePath = computed(() => buildTablePath(this.propertyScope(), this.pathSegments(), this.baseOperator(), false), ...(ngDevMode ? [{ debugName: "propertyTablePath" }] : []));
|
|
977
|
-
resolvedTablePath = computed(() => buildTablePath(this.propertyScope(), this.pathSegments(), this.baseOperator(), true), ...(ngDevMode ? [{ debugName: "resolvedTablePath" }] : []));
|
|
979
|
+
}, ...(ngDevMode ? [{ debugName: "isDirectAccess" }] : /* istanbul ignore next */ []));
|
|
980
|
+
propertyTablePath = computed(() => buildTablePath(this.propertyScope(), this.pathSegments(), this.baseOperator(), false), ...(ngDevMode ? [{ debugName: "propertyTablePath" }] : /* istanbul ignore next */ []));
|
|
981
|
+
resolvedTablePath = computed(() => buildTablePath(this.propertyScope(), this.pathSegments(), this.baseOperator(), true), ...(ngDevMode ? [{ debugName: "resolvedTablePath" }] : /* istanbul ignore next */ []));
|
|
978
982
|
currentNextTokenRules = computed(() => {
|
|
979
983
|
const path = this.resolvedTablePath();
|
|
980
984
|
if (!path)
|
|
981
985
|
return {};
|
|
982
986
|
return this.nextTokenRulesByPath()[path] ?? {};
|
|
983
|
-
}, ...(ngDevMode ? [{ debugName: "currentNextTokenRules" }] : []));
|
|
987
|
+
}, ...(ngDevMode ? [{ debugName: "currentNextTokenRules" }] : /* istanbul ignore next */ []));
|
|
984
988
|
nextOperatorOptions = computed(() => {
|
|
985
989
|
const rules = this.currentNextTokenRules();
|
|
986
990
|
const operators = this.contextOperators();
|
|
@@ -999,7 +1003,7 @@ class FormulaBuilder {
|
|
|
999
1003
|
};
|
|
1000
1004
|
})
|
|
1001
1005
|
.filter((option) => option.allowedValues.length > 0 || !option.requiresParameter);
|
|
1002
|
-
}, ...(ngDevMode ? [{ debugName: "nextOperatorOptions" }] : []));
|
|
1006
|
+
}, ...(ngDevMode ? [{ debugName: "nextOperatorOptions" }] : /* istanbul ignore next */ []));
|
|
1003
1007
|
canAddNextSegment = computed(() => {
|
|
1004
1008
|
if (this.nextOperatorOptions().length === 0)
|
|
1005
1009
|
return false;
|
|
@@ -1008,14 +1012,10 @@ class FormulaBuilder {
|
|
|
1008
1012
|
return false;
|
|
1009
1013
|
}
|
|
1010
1014
|
return segments.every((segment) => !segment.requiresParameter || Boolean(segment.value));
|
|
1011
|
-
}, ...(ngDevMode ? [{ debugName: "canAddNextSegment" }] : []));
|
|
1015
|
+
}, ...(ngDevMode ? [{ debugName: "canAddNextSegment" }] : /* istanbul ignore next */ []));
|
|
1012
1016
|
propertiesComponent = computed(() => {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
const contextKey = this.builderContext()?.contextEntityTypeKey;
|
|
1017
|
-
return contextKey;
|
|
1018
|
-
}, ...(ngDevMode ? [{ debugName: "propertiesComponent" }] : []));
|
|
1017
|
+
return this.builderContext()?.contextEntityTypeKey;
|
|
1018
|
+
}, ...(ngDevMode ? [{ debugName: "propertiesComponent" }] : /* istanbul ignore next */ []));
|
|
1019
1019
|
mergedPropertiesByPath = computed(() => {
|
|
1020
1020
|
const merged = {
|
|
1021
1021
|
...this.propertiesByPath(),
|
|
@@ -1027,11 +1027,11 @@ class FormulaBuilder {
|
|
|
1027
1027
|
}
|
|
1028
1028
|
});
|
|
1029
1029
|
return merged;
|
|
1030
|
-
}, ...(ngDevMode ? [{ debugName: "mergedPropertiesByPath" }] : []));
|
|
1030
|
+
}, ...(ngDevMode ? [{ debugName: "mergedPropertiesByPath" }] : /* istanbul ignore next */ []));
|
|
1031
1031
|
isPropertyLoading = computed(() => {
|
|
1032
1032
|
const path = this.resolvedTablePath();
|
|
1033
1033
|
return Boolean(path) && this.propertyLoadingPath() === path;
|
|
1034
|
-
}, ...(ngDevMode ? [{ debugName: "isPropertyLoading" }] : []));
|
|
1034
|
+
}, ...(ngDevMode ? [{ debugName: "isPropertyLoading" }] : /* istanbul ignore next */ []));
|
|
1035
1035
|
propertyOptions = computed(() => {
|
|
1036
1036
|
const path = this.resolvedTablePath();
|
|
1037
1037
|
if (!path)
|
|
@@ -1042,16 +1042,16 @@ class FormulaBuilder {
|
|
|
1042
1042
|
key: prop.key,
|
|
1043
1043
|
name: prop.name ?? prop.key,
|
|
1044
1044
|
}));
|
|
1045
|
-
}, ...(ngDevMode ? [{ debugName: "propertyOptions" }] : []));
|
|
1045
|
+
}, ...(ngDevMode ? [{ debugName: "propertyOptions" }] : /* istanbul ignore next */ []));
|
|
1046
1046
|
selectedProperty = computed(() => {
|
|
1047
1047
|
const key = this.propertyFieldKey();
|
|
1048
1048
|
return this.propertyOptions().find((prop) => prop.key === key) ?? null;
|
|
1049
|
-
}, ...(ngDevMode ? [{ debugName: "selectedProperty" }] : []));
|
|
1049
|
+
}, ...(ngDevMode ? [{ debugName: "selectedProperty" }] : /* istanbul ignore next */ []));
|
|
1050
1050
|
canInsertProperty = computed(() => {
|
|
1051
1051
|
if (!this.selectedProperty())
|
|
1052
1052
|
return false;
|
|
1053
1053
|
return Boolean(this.resolvedTablePath());
|
|
1054
|
-
}, ...(ngDevMode ? [{ debugName: "canInsertProperty" }] : []));
|
|
1054
|
+
}, ...(ngDevMode ? [{ debugName: "canInsertProperty" }] : /* istanbul ignore next */ []));
|
|
1055
1055
|
setPropertyScope(scope) {
|
|
1056
1056
|
if (this.propertyScope() === scope) {
|
|
1057
1057
|
return;
|
|
@@ -1111,16 +1111,16 @@ class FormulaBuilder {
|
|
|
1111
1111
|
}
|
|
1112
1112
|
return token;
|
|
1113
1113
|
}
|
|
1114
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
1115
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
1114
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1115
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: FormulaBuilder, isStandalone: true, selector: "mt-formula-builder", inputs: { propertiesByPath: { classPropertyName: "propertiesByPath", publicName: "propertiesByPath", isSignal: true, isRequired: false, transformFunction: null }, levelSchemaId: { classPropertyName: "levelSchemaId", publicName: "levelSchemaId", isSignal: true, isRequired: false, transformFunction: null }, moduleId: { classPropertyName: "moduleId", publicName: "moduleId", isSignal: true, isRequired: false, transformFunction: null }, contextEntityTypeKey: { classPropertyName: "contextEntityTypeKey", publicName: "contextEntityTypeKey", isSignal: true, isRequired: false, transformFunction: null }, templateId: { classPropertyName: "templateId", publicName: "templateId", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hideToolbar: { classPropertyName: "hideToolbar", publicName: "hideToolbar", isSignal: true, isRequired: false, transformFunction: null }, hideStatusBar: { classPropertyName: "hideStatusBar", publicName: "hideStatusBar", isSignal: true, isRequired: false, transformFunction: null }, toolbarTabs: { classPropertyName: "toolbarTabs", publicName: "toolbarTabs", isSignal: true, isRequired: false, transformFunction: null }, codeOnly: { classPropertyName: "codeOnly", publicName: "codeOnly", isSignal: true, isRequired: false, transformFunction: null }, isProcessBuilder: { classPropertyName: "isProcessBuilder", publicName: "isProcessBuilder", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validationChange: "validationChange", tokensChange: "tokensChange" }, host: { classAttribute: "block" }, providers: [
|
|
1116
1116
|
{
|
|
1117
1117
|
provide: NG_VALUE_ACCESSOR,
|
|
1118
1118
|
useExisting: forwardRef(() => FormulaBuilder),
|
|
1119
1119
|
multi: true,
|
|
1120
1120
|
},
|
|
1121
|
-
], viewQueries: [{ propertyName: "editor", first: true, predicate: ["formulaEditor"], descendants: true, isSignal: true }, { propertyName: "codeEditor", first: true, predicate: ["formulaEditorCode"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-card\r\n headless\r\n [class.ring-2]=\"hasFocus()\"\r\n [class.ring-primary]=\"hasFocus()\"\r\n [paddingless]=\"true\"\r\n>\r\n <div\r\n *transloco=\"let t; prefix: 'formulaBuilder'\"\r\n class=\"flex flex-col overflow-hidden\"\r\n >\r\n <!-- Toolbar - Pass data via inputs (pure component) -->\r\n @if (!hideToolbar()) {\r\n <mt-formula-toolbar\r\n [functionCategories]=\"functionCategories()\"\r\n [visibleTabs]=\"resolvedToolbarTabs()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n >\r\n <ng-template #properties let-insertBlock=\"insertBlock\">\r\n @if (isContextLoading()) {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <p-skeleton\r\n width=\"18rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <p-skeleton\r\n width=\"7rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div class=\"flex items-center gap-4\">\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <!-- Scope Selection - Full width, centered -->\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <mt-radio-cards\r\n [options]=\"scopeOptions()\"\r\n [activeId]=\"propertyScope()\"\r\n (selectionChange)=\"onScopeChange($event)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Path + Field + Preview Row -->\r\n <div class=\"flex items-center gap-4\">\r\n <!-- Path Container - Flexible -->\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 >{{ t(\"properties.path\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (isDirectAccess()) {\r\n <span\r\n class=\"rounded-md bg-emerald-100 px-2.5 py-1 text-xs font-medium text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\r\n >\r\n \u2713 {{ t(\"properties.direct-access\") }}\r\n </span>\r\n } @else if (pathSegments().length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">{{\r\n t(\"properties.no-path-configured\")\r\n }}</span>\r\n } @else {\r\n <div\r\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\r\n >\r\n @for (\r\n segment of pathSegments();\r\n track $index;\r\n let segmentIndex = $index\r\n ) {\r\n @if (segmentIndex > 0) {\r\n <span\r\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\r\n >\u203A</span\r\n >\r\n }\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"\n segment.value ||\n t('properties.select') +\n ' ' +\n getOperatorDisplayName(segment.operatorToken)\n \"\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [severity]=\"\r\n segment.value ? 'primary' : 'secondary'\r\n \"\r\n [iconPos]=\"'end'\"\r\n (onClick)=\"pathPopover.toggle($event)\"\r\n ></mt-button>\r\n\r\n <p-popover\r\n #pathPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.select\") }}\n {{ getOperatorDisplayName(segment.operatorToken) }}\n </div>\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of segmentOptions(segmentIndex);\r\n track option.key\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.name\"\r\n [icon]=\"\r\n segment.value === option.key\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]=\"\r\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\r\n (segment.value === option.key\r\n ? 'bg-primary text-white'\r\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\r\n \"\r\n (onClick)=\"\r\n setPathSegmentValue(\r\n segmentIndex,\r\n option.key\r\n );\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n @if (segment.optional) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.clear')\"\r\n [outlined]=\"true\"\r\n icon=\"general.x-close\"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"mt-2 w-full rounded-md border border-slate-200 py-1 text-xs text-slate-400 hover:bg-slate-50 dark:border-slate-700\"\r\n (onClick)=\"\r\n setPathSegmentValue(segmentIndex, null);\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </p-popover>\r\n\r\n @if (\r\n segment.canRemove &&\r\n segmentIndex === pathSegments().length - 1\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.x-close\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\r\n (onClick)=\"removePathSegment(segmentIndex)\"\r\n ></mt-button>\r\n }\r\n }\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n [disabled]=\"!canAddNextSegment()\"\r\n [class.hidden]=\"!canAddNextSegment()\"\r\n styleClass=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90 disabled:opacity-50\"\r\n (onClick)=\"nextOperatorPopover.toggle($event)\"\r\n ></mt-button>\r\n </div>\r\n }\r\n <p-popover\r\n #nextOperatorPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.add-segment\") }}\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of nextOperatorOptions();\r\n track option.token\r\n ) {\r\n <mt-button\n type=\"button\"\n [label]=\"getOperatorDisplayName(option.token)\"\n icon=\"general.plus\"\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 onAddSegmentSelection(\r\n option.token,\r\n nextOperatorPopover\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 </div>\r\n\r\n <!-- Field Selection - Under Path -->\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >{{ t(\"properties.field\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n @if (isPropertyLoading()) {\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n } @else {\r\n <mt-select-field\r\n class=\"flex-1\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('properties.select')\"\r\n [(ngModel)]=\"propertyFieldKey\"\r\n [options]=\"propertyOptions()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n <div\r\n class=\"w-full truncate rounded-lg bg-amber-100 px-3 py-2 text-sm font-semibold text-amber-700 shadow-sm hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:hover:bg-amber-900/50\"\r\n [mtTooltip]=\"\r\n '@' +\r\n propertyTablePath() +\r\n '::' +\r\n (selectedProperty()?.key ?? '...')\r\n \"\r\n tooltipPosition=\"top\"\r\n >\r\n @{{ propertyTablePath() }}::{{\r\n selectedProperty()?.key ?? \"...\"\r\n }}\r\n </div>\r\n <div class=\"flex justify-end\">\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert')\"\r\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [disabled]=\"!canInsertProperty()\"\r\n (onClick)=\"insertSelectedProperty(insertBlock)\"\r\n ></mt-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-formula-toolbar>\r\n }\r\n\r\n <!-- Editor Area -->\r\n <div class=\"p-3\">\r\n <div\r\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\r\n >\r\n @if (showModeToggle()) {\r\n <div\r\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\r\n >\r\n <mt-toggle-field\r\n [label]=\"t('codeMode')\"\r\n labelPosition=\"end\"\r\n size=\"small\"\r\n [ngModel]=\"isCodeMode()\"\r\n [readonly]=\"isCodeModeLocked()\"\r\n [pInputs]=\"{ disabled: isCodeModeLocked() }\"\r\n (ngModelChange)=\"onModeToggle($event)\"\r\n />\r\n </div>\r\n }\r\n <div class=\"p-0\">\r\n @if (isCodeMode()) {\r\n <mt-formula-editor-code\r\n #formulaEditorCode\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialFormula]=\"expression()\"\r\n [borderless]=\"true\"\r\n [autocompleteProvider]=\"autocompleteProvider\"\r\n (formulaChange)=\"onCodeFormulaChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n } @else {\r\n <mt-formula-editor\r\n #formulaEditor\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialTokens]=\"tokens()\"\r\n [borderless]=\"true\"\r\n (formulaChange)=\"onFormulaChange($event)\"\r\n (tokensChange)=\"onTokensChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Status Bar - Pass data via inputs (pure component) -->\r\n @if (!hideStatusBar()) {\r\n <mt-formula-status-bar [validation]=\"validation()\" />\r\n }\r\n </div>\r\n</mt-card>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { 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: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: 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: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: RadioCards, selector: "mt-radio-cards", inputs: ["circle", "color", "size", "columns", "options", "activeId", "itemTemplate"], outputs: ["optionsChange", "activeIdChange", "selectionChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i2.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "visibleTabs", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaStatusBar, selector: "mt-formula-status-bar", inputs: ["validation", "labels"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }, { kind: "component", type: FormulaEditorCode, selector: "mt-formula-editor-code", inputs: ["placeholder", "initialFormula", "disabled", "language", "theme", "borderless", "autocompleteProvider", "autocompleteDebounceMs"], outputs: ["formulaChange", "onBlur", "onFocus"] }] });
|
|
1121
|
+
], viewQueries: [{ propertyName: "editor", first: true, predicate: ["formulaEditor"], descendants: true, isSignal: true }, { propertyName: "codeEditor", first: true, predicate: ["formulaEditorCode"], descendants: true, isSignal: true }], ngImport: i0, template: "<mt-card\r\n headless\r\n [class.ring-2]=\"hasFocus()\"\r\n [class.ring-primary]=\"hasFocus()\"\r\n [paddingless]=\"true\"\r\n>\r\n <div\r\n *transloco=\"let t; prefix: 'formulaBuilder'\"\r\n class=\"flex flex-col overflow-hidden\"\r\n >\r\n <!-- Toolbar - Pass data via inputs (pure component) -->\r\n @if (!hideToolbar()) {\r\n <mt-formula-toolbar\r\n [functionCategories]=\"functionCategories()\"\r\n [visibleTabs]=\"resolvedToolbarTabs()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n >\r\n <ng-template #properties let-insertBlock=\"insertBlock\">\r\n @if (isContextLoading()) {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <p-skeleton\r\n width=\"18rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <p-skeleton\r\n width=\"7rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div class=\"flex items-center gap-4\">\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <!-- Scope Selection - Full width, centered -->\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <mt-radio-cards\r\n [options]=\"scopeOptions()\"\r\n [activeId]=\"propertyScope()\"\r\n (selectionChange)=\"onScopeChange($event)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Path + Field + Preview Row -->\r\n <div class=\"flex items-center gap-4\">\r\n <!-- Path Container - Flexible -->\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 >{{ t(\"properties.path\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (isDirectAccess()) {\r\n <span\r\n class=\"rounded-md bg-emerald-100 px-2.5 py-1 text-xs font-medium text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\r\n >\r\n \u2713 {{ t(\"properties.direct-access\") }}\r\n </span>\r\n } @else if (pathSegments().length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">{{\r\n t(\"properties.no-path-configured\")\r\n }}</span>\r\n } @else {\r\n <div\r\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\r\n >\r\n @for (\r\n segment of pathSegments();\r\n track $index;\r\n let segmentIndex = $index\r\n ) {\r\n @if (segmentIndex > 0) {\r\n <span\r\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\r\n >\u203A</span\r\n >\r\n }\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"\r\n segment.value ||\r\n t('properties.select') +\r\n ' ' +\r\n getOperatorDisplayName(segment.operatorToken)\r\n \"\r\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [severity]=\"\r\n segment.value ? 'primary' : 'secondary'\r\n \"\r\n [iconPos]=\"'end'\"\r\n (onClick)=\"pathPopover.toggle($event)\"\r\n ></mt-button>\r\n\r\n <p-popover\r\n #pathPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.select\") }}\r\n {{\r\n getOperatorDisplayName(\r\n segment.operatorToken\r\n )\r\n }}\r\n </div>\r\n <div\r\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\r\n >\r\n @for (\r\n option of segmentOptions(segmentIndex);\r\n track option.key\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.name\"\r\n [icon]=\"\r\n segment.value === option.key\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]=\"\r\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\r\n (segment.value === option.key\r\n ? 'bg-primary text-white'\r\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\r\n \"\r\n (onClick)=\"\r\n setPathSegmentValue(\r\n segmentIndex,\r\n option.key\r\n );\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n @if (segment.optional) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.clear')\"\r\n [outlined]=\"true\"\r\n icon=\"general.x-close\"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"mt-2 w-full rounded-md border border-slate-200 py-1 text-xs text-slate-400 hover:bg-slate-50 dark:border-slate-700\"\r\n (onClick)=\"\r\n setPathSegmentValue(segmentIndex, null);\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </p-popover>\r\n\r\n @if (\r\n segment.canRemove &&\r\n segmentIndex === pathSegments().length - 1\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.x-close\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\r\n (onClick)=\"removePathSegment(segmentIndex)\"\r\n ></mt-button>\r\n }\r\n }\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n [disabled]=\"!canAddNextSegment()\"\r\n [class.hidden]=\"!canAddNextSegment()\"\r\n styleClass=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90 disabled:opacity-50\"\r\n (onClick)=\"nextOperatorPopover.toggle($event)\"\r\n ></mt-button>\r\n </div>\r\n }\r\n <p-popover\r\n #nextOperatorPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.add-segment\") }}\r\n </div>\r\n <div\r\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\r\n >\r\n @for (\r\n option of nextOperatorOptions();\r\n track option.token\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"getOperatorDisplayName(option.token)\"\r\n icon=\"general.plus\"\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 onAddSegmentSelection(\r\n option.token,\r\n nextOperatorPopover\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 </div>\r\n\r\n <!-- Field Selection - Under Path -->\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >{{ t(\"properties.field\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n @if (isPropertyLoading()) {\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n } @else {\r\n <mt-select-field\r\n class=\"flex-1\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('properties.select')\"\r\n [(ngModel)]=\"propertyFieldKey\"\r\n [options]=\"propertyOptions()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n <div\r\n class=\"w-full truncate rounded-lg bg-amber-100 px-3 py-2 text-sm font-semibold text-amber-700 shadow-sm hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:hover:bg-amber-900/50\"\r\n [mtTooltip]=\"\r\n '@' +\r\n propertyTablePath() +\r\n '::' +\r\n (selectedProperty()?.key ?? '...')\r\n \"\r\n tooltipPosition=\"top\"\r\n >\r\n @{{ propertyTablePath() }}::{{\r\n selectedProperty()?.key ?? \"...\"\r\n }}\r\n </div>\r\n <div class=\"flex justify-end\">\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert')\"\r\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [disabled]=\"!canInsertProperty()\"\r\n (onClick)=\"insertSelectedProperty(insertBlock)\"\r\n ></mt-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-formula-toolbar>\r\n }\r\n\r\n <!-- Editor Area -->\r\n <div class=\"p-3\">\r\n <div\r\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\r\n >\r\n @if (showModeToggle()) {\r\n <div\r\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\r\n >\r\n <mt-toggle-field\r\n [label]=\"t('codeMode')\"\r\n labelPosition=\"end\"\r\n size=\"small\"\r\n [ngModel]=\"isCodeMode()\"\r\n [readonly]=\"isCodeModeLocked()\"\r\n [pInputs]=\"{ disabled: isCodeModeLocked() }\"\r\n (ngModelChange)=\"onModeToggle($event)\"\r\n />\r\n </div>\r\n }\r\n <div class=\"p-0\">\r\n @if (isCodeMode()) {\r\n <mt-formula-editor-code\r\n #formulaEditorCode\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialFormula]=\"expression()\"\r\n [borderless]=\"true\"\r\n [autocompleteProvider]=\"autocompleteProvider\"\r\n (formulaChange)=\"onCodeFormulaChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n } @else {\r\n <mt-formula-editor\r\n #formulaEditor\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialTokens]=\"tokens()\"\r\n [borderless]=\"true\"\r\n (formulaChange)=\"onFormulaChange($event)\"\r\n (tokensChange)=\"onTokensChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Status Bar - Pass data via inputs (pure component) -->\r\n @if (!hideStatusBar()) {\r\n <mt-formula-status-bar [validation]=\"validation()\" />\r\n }\r\n </div>\r\n</mt-card>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { 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: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { 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: 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: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: RadioCards, selector: "mt-radio-cards", inputs: ["circle", "color", "size", "columns", "options", "activeId", "itemTemplate"], outputs: ["optionsChange", "activeIdChange", "selectionChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i2.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: FormulaToolbar, selector: "mt-formula-toolbar", inputs: ["knownProperties", "propertiesTemplate", "functionCategories", "operators", "initialTab", "visibleTabs", "searchPlaceholder", "labels"], outputs: ["onBlockInsert", "onTabChange"] }, { kind: "component", type: FormulaStatusBar, selector: "mt-formula-status-bar", inputs: ["validation", "labels"] }, { kind: "component", type: FormulaEditor, selector: "mt-formula-editor", inputs: ["placeholder", "initialTokens", "disabled", "borderless"], outputs: ["formulaChange", "tokensChange", "onBlur", "onFocus"] }, { kind: "component", type: FormulaEditorCode, selector: "mt-formula-editor-code", inputs: ["placeholder", "initialFormula", "disabled", "language", "theme", "borderless", "autocompleteProvider", "autocompleteDebounceMs"], outputs: ["formulaChange", "onBlur", "onFocus"] }] });
|
|
1122
1122
|
}
|
|
1123
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaBuilder, decorators: [{
|
|
1124
1124
|
type: Component,
|
|
1125
1125
|
args: [{ selector: 'mt-formula-builder', standalone: true, imports: [
|
|
1126
1126
|
CommonModule,
|
|
@@ -1146,7 +1146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
1146
1146
|
useExisting: forwardRef(() => FormulaBuilder),
|
|
1147
1147
|
multi: true,
|
|
1148
1148
|
},
|
|
1149
|
-
], template: "<mt-card\r\n headless\r\n [class.ring-2]=\"hasFocus()\"\r\n [class.ring-primary]=\"hasFocus()\"\r\n [paddingless]=\"true\"\r\n>\r\n <div\r\n *transloco=\"let t; prefix: 'formulaBuilder'\"\r\n class=\"flex flex-col overflow-hidden\"\r\n >\r\n <!-- Toolbar - Pass data via inputs (pure component) -->\r\n @if (!hideToolbar()) {\r\n <mt-formula-toolbar\r\n [functionCategories]=\"functionCategories()\"\r\n [visibleTabs]=\"resolvedToolbarTabs()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n >\r\n <ng-template #properties let-insertBlock=\"insertBlock\">\r\n @if (isContextLoading()) {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <p-skeleton\r\n width=\"18rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <p-skeleton\r\n width=\"7rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div class=\"flex items-center gap-4\">\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <!-- Scope Selection - Full width, centered -->\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <mt-radio-cards\r\n [options]=\"scopeOptions()\"\r\n [activeId]=\"propertyScope()\"\r\n (selectionChange)=\"onScopeChange($event)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Path + Field + Preview Row -->\r\n <div class=\"flex items-center gap-4\">\r\n <!-- Path Container - Flexible -->\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 >{{ t(\"properties.path\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (isDirectAccess()) {\r\n <span\r\n class=\"rounded-md bg-emerald-100 px-2.5 py-1 text-xs font-medium text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\r\n >\r\n \u2713 {{ t(\"properties.direct-access\") }}\r\n </span>\r\n } @else if (pathSegments().length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">{{\r\n t(\"properties.no-path-configured\")\r\n }}</span>\r\n } @else {\r\n <div\r\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\r\n >\r\n @for (\r\n segment of pathSegments();\r\n track $index;\r\n let segmentIndex = $index\r\n ) {\r\n @if (segmentIndex > 0) {\r\n <span\r\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\r\n >\u203A</span\r\n >\r\n }\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"\n segment.value ||\n t('properties.select') +\n ' ' +\n getOperatorDisplayName(segment.operatorToken)\n \"\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [severity]=\"\r\n segment.value ? 'primary' : 'secondary'\r\n \"\r\n [iconPos]=\"'end'\"\r\n (onClick)=\"pathPopover.toggle($event)\"\r\n ></mt-button>\r\n\r\n <p-popover\r\n #pathPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.select\") }}\n {{ getOperatorDisplayName(segment.operatorToken) }}\n </div>\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of segmentOptions(segmentIndex);\r\n track option.key\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.name\"\r\n [icon]=\"\r\n segment.value === option.key\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]=\"\r\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\r\n (segment.value === option.key\r\n ? 'bg-primary text-white'\r\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\r\n \"\r\n (onClick)=\"\r\n setPathSegmentValue(\r\n segmentIndex,\r\n option.key\r\n );\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n @if (segment.optional) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.clear')\"\r\n [outlined]=\"true\"\r\n icon=\"general.x-close\"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"mt-2 w-full rounded-md border border-slate-200 py-1 text-xs text-slate-400 hover:bg-slate-50 dark:border-slate-700\"\r\n (onClick)=\"\r\n setPathSegmentValue(segmentIndex, null);\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </p-popover>\r\n\r\n @if (\r\n segment.canRemove &&\r\n segmentIndex === pathSegments().length - 1\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.x-close\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\r\n (onClick)=\"removePathSegment(segmentIndex)\"\r\n ></mt-button>\r\n }\r\n }\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n [disabled]=\"!canAddNextSegment()\"\r\n [class.hidden]=\"!canAddNextSegment()\"\r\n styleClass=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90 disabled:opacity-50\"\r\n (onClick)=\"nextOperatorPopover.toggle($event)\"\r\n ></mt-button>\r\n </div>\r\n }\r\n <p-popover\r\n #nextOperatorPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.add-segment\") }}\r\n </div>\r\n <div class=\"flex flex-col gap-1\">\r\n @for (\r\n option of nextOperatorOptions();\r\n track option.token\r\n ) {\r\n <mt-button\n type=\"button\"\n [label]=\"getOperatorDisplayName(option.token)\"\n icon=\"general.plus\"\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 onAddSegmentSelection(\r\n option.token,\r\n nextOperatorPopover\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 </div>\r\n\r\n <!-- Field Selection - Under Path -->\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >{{ t(\"properties.field\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n @if (isPropertyLoading()) {\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n } @else {\r\n <mt-select-field\r\n class=\"flex-1\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('properties.select')\"\r\n [(ngModel)]=\"propertyFieldKey\"\r\n [options]=\"propertyOptions()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n <div\r\n class=\"w-full truncate rounded-lg bg-amber-100 px-3 py-2 text-sm font-semibold text-amber-700 shadow-sm hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:hover:bg-amber-900/50\"\r\n [mtTooltip]=\"\r\n '@' +\r\n propertyTablePath() +\r\n '::' +\r\n (selectedProperty()?.key ?? '...')\r\n \"\r\n tooltipPosition=\"top\"\r\n >\r\n @{{ propertyTablePath() }}::{{\r\n selectedProperty()?.key ?? \"...\"\r\n }}\r\n </div>\r\n <div class=\"flex justify-end\">\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert')\"\r\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [disabled]=\"!canInsertProperty()\"\r\n (onClick)=\"insertSelectedProperty(insertBlock)\"\r\n ></mt-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-formula-toolbar>\r\n }\r\n\r\n <!-- Editor Area -->\r\n <div class=\"p-3\">\r\n <div\r\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\r\n >\r\n @if (showModeToggle()) {\r\n <div\r\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\r\n >\r\n <mt-toggle-field\r\n [label]=\"t('codeMode')\"\r\n labelPosition=\"end\"\r\n size=\"small\"\r\n [ngModel]=\"isCodeMode()\"\r\n [readonly]=\"isCodeModeLocked()\"\r\n [pInputs]=\"{ disabled: isCodeModeLocked() }\"\r\n (ngModelChange)=\"onModeToggle($event)\"\r\n />\r\n </div>\r\n }\r\n <div class=\"p-0\">\r\n @if (isCodeMode()) {\r\n <mt-formula-editor-code\r\n #formulaEditorCode\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialFormula]=\"expression()\"\r\n [borderless]=\"true\"\r\n [autocompleteProvider]=\"autocompleteProvider\"\r\n (formulaChange)=\"onCodeFormulaChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n } @else {\r\n <mt-formula-editor\r\n #formulaEditor\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialTokens]=\"tokens()\"\r\n [borderless]=\"true\"\r\n (formulaChange)=\"onFormulaChange($event)\"\r\n (tokensChange)=\"onTokensChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Status Bar - Pass data via inputs (pure component) -->\r\n @if (!hideStatusBar()) {\r\n <mt-formula-status-bar [validation]=\"validation()\" />\r\n }\r\n </div>\r\n</mt-card>\r\n" }]
|
|
1149
|
+
], template: "<mt-card\r\n headless\r\n [class.ring-2]=\"hasFocus()\"\r\n [class.ring-primary]=\"hasFocus()\"\r\n [paddingless]=\"true\"\r\n>\r\n <div\r\n *transloco=\"let t; prefix: 'formulaBuilder'\"\r\n class=\"flex flex-col overflow-hidden\"\r\n >\r\n <!-- Toolbar - Pass data via inputs (pure component) -->\r\n @if (!hideToolbar()) {\r\n <mt-formula-toolbar\r\n [functionCategories]=\"functionCategories()\"\r\n [visibleTabs]=\"resolvedToolbarTabs()\"\r\n (onBlockInsert)=\"onBlockInsert($event)\"\r\n >\r\n <ng-template #properties let-insertBlock=\"insertBlock\">\r\n @if (isContextLoading()) {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <p-skeleton\r\n width=\"18rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <p-skeleton\r\n width=\"7rem\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div class=\"flex items-center gap-4\">\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <p-skeleton\r\n width=\"3rem\"\r\n height=\"1rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n </div>\r\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex flex-col gap-4 p-4\">\r\n <!-- Scope Selection - Full width, centered -->\r\n <div class=\"flex items-center justify-between gap-4\">\r\n <mt-radio-cards\r\n [options]=\"scopeOptions()\"\r\n [activeId]=\"propertyScope()\"\r\n (selectionChange)=\"onScopeChange($event)\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Path + Field + Preview Row -->\r\n <div class=\"flex items-center gap-4\">\r\n <!-- Path Container - Flexible -->\r\n <div\r\n class=\"flex min-h-11 min-w-0 flex-1 flex-col gap-2 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 >{{ t(\"properties.path\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\r\n @if (isDirectAccess()) {\r\n <span\r\n class=\"rounded-md bg-emerald-100 px-2.5 py-1 text-xs font-medium text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\r\n >\r\n \u2713 {{ t(\"properties.direct-access\") }}\r\n </span>\r\n } @else if (pathSegments().length === 0) {\r\n <span class=\"text-xs italic text-slate-400\">{{\r\n t(\"properties.no-path-configured\")\r\n }}</span>\r\n } @else {\r\n <div\r\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\r\n >\r\n @for (\r\n segment of pathSegments();\r\n track $index;\r\n let segmentIndex = $index\r\n ) {\r\n @if (segmentIndex > 0) {\r\n <span\r\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\r\n >\u203A</span\r\n >\r\n }\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"\r\n segment.value ||\r\n t('properties.select') +\r\n ' ' +\r\n getOperatorDisplayName(segment.operatorToken)\r\n \"\r\n icon=\"arrow.chevron-down\"\r\n [outlined]=\"true\"\r\n size=\"small\"\r\n [severity]=\"\r\n segment.value ? 'primary' : 'secondary'\r\n \"\r\n [iconPos]=\"'end'\"\r\n (onClick)=\"pathPopover.toggle($event)\"\r\n ></mt-button>\r\n\r\n <p-popover\r\n #pathPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.select\") }}\r\n {{\r\n getOperatorDisplayName(\r\n segment.operatorToken\r\n )\r\n }}\r\n </div>\r\n <div\r\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\r\n >\r\n @for (\r\n option of segmentOptions(segmentIndex);\r\n track option.key\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"option.name\"\r\n [icon]=\"\r\n segment.value === option.key\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]=\"\r\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\r\n (segment.value === option.key\r\n ? 'bg-primary text-white'\r\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\r\n \"\r\n (onClick)=\"\r\n setPathSegmentValue(\r\n segmentIndex,\r\n option.key\r\n );\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n @if (segment.optional) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.clear')\"\r\n [outlined]=\"true\"\r\n icon=\"general.x-close\"\r\n [iconPos]=\"'end'\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"mt-2 w-full rounded-md border border-slate-200 py-1 text-xs text-slate-400 hover:bg-slate-50 dark:border-slate-700\"\r\n (onClick)=\"\r\n setPathSegmentValue(segmentIndex, null);\r\n pathPopover.hide()\r\n \"\r\n ></mt-button>\r\n }\r\n </div>\r\n </p-popover>\r\n\r\n @if (\r\n segment.canRemove &&\r\n segmentIndex === pathSegments().length - 1\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.x-close\"\r\n size=\"small\"\r\n severity=\"danger\"\r\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\r\n (onClick)=\"removePathSegment(segmentIndex)\"\r\n ></mt-button>\r\n }\r\n }\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n [disabled]=\"!canAddNextSegment()\"\r\n [class.hidden]=\"!canAddNextSegment()\"\r\n styleClass=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-primary text-xs font-bold text-white hover:opacity-90 disabled:opacity-50\"\r\n (onClick)=\"nextOperatorPopover.toggle($event)\"\r\n ></mt-button>\r\n </div>\r\n }\r\n <p-popover\r\n #nextOperatorPopover\r\n [style]=\"{ width: 'max-content' }\"\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 {{ t(\"properties.add-segment\") }}\r\n </div>\r\n <div\r\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\r\n >\r\n @for (\r\n option of nextOperatorOptions();\r\n track option.token\r\n ) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"getOperatorDisplayName(option.token)\"\r\n icon=\"general.plus\"\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 onAddSegmentSelection(\r\n option.token,\r\n nextOperatorPopover\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 </div>\r\n\r\n <!-- Field Selection - Under Path -->\r\n <div\r\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\r\n >\r\n <span\r\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\r\n >{{ t(\"properties.field\") }}</span\r\n >\r\n <div\r\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\r\n ></div>\r\n @if (isPropertyLoading()) {\r\n <p-skeleton\r\n class=\"flex-1\"\r\n height=\"2.5rem\"\r\n styleClass=\"rounded-md\"\r\n />\r\n } @else {\r\n <mt-select-field\r\n class=\"flex-1\"\r\n [label]=\"''\"\r\n [filter]=\"true\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('properties.select')\"\r\n [(ngModel)]=\"propertyFieldKey\"\r\n [options]=\"propertyOptions()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n <div\r\n class=\"w-full truncate rounded-lg bg-amber-100 px-3 py-2 text-sm font-semibold text-amber-700 shadow-sm hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:hover:bg-amber-900/50\"\r\n [mtTooltip]=\"\r\n '@' +\r\n propertyTablePath() +\r\n '::' +\r\n (selectedProperty()?.key ?? '...')\r\n \"\r\n tooltipPosition=\"top\"\r\n >\r\n @{{ propertyTablePath() }}::{{\r\n selectedProperty()?.key ?? \"...\"\r\n }}\r\n </div>\r\n <div class=\"flex justify-end\">\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert')\"\r\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [disabled]=\"!canInsertProperty()\"\r\n (onClick)=\"insertSelectedProperty(insertBlock)\"\r\n ></mt-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-formula-toolbar>\r\n }\r\n\r\n <!-- Editor Area -->\r\n <div class=\"p-3\">\r\n <div\r\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\r\n >\r\n @if (showModeToggle()) {\r\n <div\r\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\r\n >\r\n <mt-toggle-field\r\n [label]=\"t('codeMode')\"\r\n labelPosition=\"end\"\r\n size=\"small\"\r\n [ngModel]=\"isCodeMode()\"\r\n [readonly]=\"isCodeModeLocked()\"\r\n [pInputs]=\"{ disabled: isCodeModeLocked() }\"\r\n (ngModelChange)=\"onModeToggle($event)\"\r\n />\r\n </div>\r\n }\r\n <div class=\"p-0\">\r\n @if (isCodeMode()) {\r\n <mt-formula-editor-code\r\n #formulaEditorCode\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialFormula]=\"expression()\"\r\n [borderless]=\"true\"\r\n [autocompleteProvider]=\"autocompleteProvider\"\r\n (formulaChange)=\"onCodeFormulaChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n } @else {\r\n <mt-formula-editor\r\n #formulaEditor\r\n [placeholder]=\"placeholder() || t('placeholder')\"\r\n [initialTokens]=\"tokens()\"\r\n [borderless]=\"true\"\r\n (formulaChange)=\"onFormulaChange($event)\"\r\n (tokensChange)=\"onTokensChange($event)\"\r\n (onFocus)=\"onEditorFocus()\"\r\n (onBlur)=\"onEditorBlur()\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Status Bar - Pass data via inputs (pure component) -->\r\n @if (!hideStatusBar()) {\r\n <mt-formula-status-bar [validation]=\"validation()\" />\r\n }\r\n </div>\r\n</mt-card>\r\n" }]
|
|
1150
1150
|
}], ctorParameters: () => [], propDecorators: { editor: [{ type: i0.ViewChild, args: ['formulaEditor', { isSignal: true }] }], codeEditor: [{ type: i0.ViewChild, args: ['formulaEditorCode', { isSignal: true }] }], propertiesByPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertiesByPath", required: false }] }], levelSchemaId: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelSchemaId", required: false }] }], moduleId: [{ type: i0.Input, args: [{ isSignal: true, alias: "moduleId", required: false }] }], contextEntityTypeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextEntityTypeKey", required: false }] }], templateId: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateId", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hideToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideToolbar", required: false }] }], hideStatusBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideStatusBar", required: false }] }], toolbarTabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarTabs", required: false }] }], codeOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "codeOnly", required: false }] }], isProcessBuilder: [{ type: i0.Input, args: [{ isSignal: true, alias: "isProcessBuilder", required: false }] }], validationChange: [{ type: i0.Output, args: ["validationChange"] }], tokensChange: [{ type: i0.Output, args: ["tokensChange"] }] } });
|
|
1151
1151
|
|
|
1152
1152
|
/**
|