@masterteam/formula-builder 0.0.26 → 0.0.28

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.
@@ -14,7 +14,7 @@ import * as i2 from 'primeng/popover';
14
14
  import { PopoverModule } from 'primeng/popover';
15
15
  import { Skeleton } from 'primeng/skeleton';
16
16
  import { Subject, debounceTime, distinctUntilChanged, tap, switchMap, of, map, catchError } from 'rxjs';
17
- import { VALIDATION_FUNCTION_CATEGORIES, serializeTokens, tokenizeFormulaTemplate, createPropertyBlock, createLiteralBlock, FormulaToolbar, FormulaStatusBar, FormulaEditor, FormulaEditorCode } from '@masterteam/components/formula';
17
+ import { DEFAULT_OPERATORS, VALIDATION_FUNCTION_CATEGORIES, serializeTokens, tokenizeFormulaTemplate, createPropertyBlock, createLiteralBlock, FormulaToolbar, FormulaStatusBar, FormulaEditor, FormulaEditorCode } from '@masterteam/components/formula';
18
18
  export { createFunctionBlock, createLiteralBlock, createOperatorBlock, createPropertyBlock } from '@masterteam/components/formula';
19
19
  import { HttpClient } from '@angular/common/http';
20
20
 
@@ -92,6 +92,7 @@ class FormulaValidatorService {
92
92
  a.levelSchemaId === b.levelSchemaId &&
93
93
  a.moduleId === b.moduleId &&
94
94
  a.templateId === b.templateId &&
95
+ a.contextKey === b.contextKey &&
95
96
  a.apiMode === b.apiMode &&
96
97
  JSON.stringify(a.knownProperties) ===
97
98
  JSON.stringify(b.knownProperties)), tap(() => this._isValidating.set(true)), switchMap((request) => this.callValidateApi(request)))
@@ -101,7 +102,7 @@ class FormulaValidatorService {
101
102
  });
102
103
  }
103
104
  /** Validate formula (debounced) */
104
- validate(formula, knownProperties, levelSchemaId, moduleId, templateId, apiMode = 'default') {
105
+ validate(formula, knownProperties, levelSchemaId, moduleId, templateId, apiMode = 'default', contextKey) {
105
106
  // Handle empty formula
106
107
  if (!formula || formula.trim() === '') {
107
108
  this._validation.set(EMPTY_VALIDATION);
@@ -115,10 +116,11 @@ class FormulaValidatorService {
115
116
  moduleId,
116
117
  templateId,
117
118
  apiMode,
119
+ contextKey,
118
120
  });
119
121
  }
120
122
  /** Validate formula immediately (no debounce) */
121
- validateImmediate(formula, knownProperties, levelSchemaId, moduleId, templateId, apiMode = 'default') {
123
+ validateImmediate(formula, knownProperties, levelSchemaId, moduleId, templateId, apiMode = 'default', contextKey) {
122
124
  if (!formula || formula.trim() === '') {
123
125
  return of(EMPTY_VALIDATION);
124
126
  }
@@ -130,6 +132,7 @@ class FormulaValidatorService {
130
132
  moduleId,
131
133
  templateId,
132
134
  apiMode,
135
+ contextKey,
133
136
  }).pipe(tap((result) => {
134
137
  this._validation.set(result);
135
138
  this._isValidating.set(false);
@@ -193,6 +196,18 @@ class FormulaContextService {
193
196
  ? `${this.apiUrl}/current-only`
194
197
  : this.apiUrl;
195
198
  }
199
+ buildLegacyContextUrl(schemaId, moduleId, contextEntityTypeKey, mode) {
200
+ const params = new URLSearchParams();
201
+ if (mode === 'default' && contextEntityTypeKey) {
202
+ params.set('contextEntityTypeKey', contextEntityTypeKey);
203
+ }
204
+ if (moduleId !== undefined) {
205
+ params.set('moduleId', moduleId.toString());
206
+ }
207
+ const queryString = params.toString();
208
+ const query = queryString ? `?${queryString}` : '';
209
+ return `${this.getBaseUrl(mode)}/builder/context/${schemaId}${query}`;
210
+ }
196
211
  unwrapResponse(response) {
197
212
  if (!response)
198
213
  return null;
@@ -207,25 +222,28 @@ class FormulaContextService {
207
222
  propertiesCache = new Map();
208
223
  nextTokensCache = new Map();
209
224
  propertyItemsCache = new Map();
210
- loadContext(schemaId, moduleId, contextEntityTypeKey, mode = 'default') {
211
- if (!schemaId)
225
+ /**
226
+ * Load the builder context. When `contextKey` is provided it is the canonical
227
+ * context identifier (`Module:123` / `Level:5` / `Level:5/Module:10`) and the
228
+ * context-key endpoint is used — this is the only way to address a standalone
229
+ * module, which has no workspace/level schema id. Otherwise the legacy
230
+ * schema-id path endpoint is used unchanged.
231
+ */
232
+ loadContext(schemaId, moduleId, contextEntityTypeKey, mode = 'default', contextKey) {
233
+ if (!schemaId && !contextKey)
212
234
  return of(null);
213
- const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${contextEntityTypeKey ?? ''}`;
235
+ const cacheKey = `${mode}:${schemaId ?? ''}:${moduleId ?? ''}:${contextEntityTypeKey ?? ''}:${contextKey ?? ''}`;
214
236
  const cached = this.contextCache.get(cacheKey);
215
237
  if (cached) {
216
238
  return of(cached);
217
239
  }
218
- const params = new URLSearchParams();
219
- if (mode === 'default' && contextEntityTypeKey) {
220
- params.set('contextEntityTypeKey', contextEntityTypeKey);
221
- }
222
- if (moduleId !== undefined) {
223
- params.set('moduleId', moduleId.toString());
224
- }
225
- const queryString = params.toString();
226
- const query = queryString ? `?${queryString}` : '';
240
+ // Context-key endpoint is authoritative for the context kind/execution
241
+ // profile; the backend resolves the schema and scope tokens from the key.
242
+ const url = contextKey
243
+ ? `${this.getBaseUrl(mode)}/builder/context?contextKey=${encodeURIComponent(contextKey)}`
244
+ : this.buildLegacyContextUrl(schemaId, moduleId, contextEntityTypeKey, mode);
227
245
  return this.http
228
- .get(`${this.getBaseUrl(mode)}/builder/context/${schemaId}${query}`)
246
+ .get(url)
229
247
  .pipe(map((response) => this.unwrapResponse(response)), tap((context) => {
230
248
  if (context) {
231
249
  this.contextCache.set(cacheKey, context);
@@ -235,10 +253,10 @@ class FormulaContextService {
235
253
  return of(null);
236
254
  }));
237
255
  }
238
- getScopeProperties(schemaId, component, tablePath, moduleId, mode = 'default') {
256
+ getScopeProperties(schemaId, component, tablePath, moduleId, mode = 'default', contextKey) {
239
257
  if (!schemaId || !tablePath)
240
258
  return of([]);
241
- const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}`;
259
+ const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}:${contextKey ?? ''}`;
242
260
  const cached = this.propertiesCache.get(cacheKey);
243
261
  if (cached) {
244
262
  return of(cached);
@@ -253,6 +271,9 @@ class FormulaContextService {
253
271
  if (moduleId !== undefined) {
254
272
  params.set('moduleId', moduleId.toString());
255
273
  }
274
+ if (contextKey) {
275
+ params.set('contextKey', contextKey);
276
+ }
256
277
  return this.http
257
278
  .get(`${this.getBaseUrl(mode)}/builder/properties?${params.toString()}`)
258
279
  .pipe(map((response) => this.unwrapResponse(response) ?? []), tap((response) => {
@@ -262,10 +283,10 @@ class FormulaContextService {
262
283
  return of([]);
263
284
  }));
264
285
  }
265
- getNextTokens(schemaId, component, tablePath, moduleId, mode = 'default') {
286
+ getNextTokens(schemaId, component, tablePath, moduleId, mode = 'default', contextKey) {
266
287
  if (!schemaId || !component || !tablePath)
267
288
  return of(null);
268
- const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}`;
289
+ const cacheKey = `${mode}:${schemaId}:${moduleId ?? ''}:${component}:${tablePath}:${contextKey ?? ''}`;
269
290
  const cached = this.nextTokensCache.get(cacheKey);
270
291
  if (cached) {
271
292
  return of(cached);
@@ -278,6 +299,9 @@ class FormulaContextService {
278
299
  if (moduleId !== undefined) {
279
300
  params.set('moduleId', moduleId.toString());
280
301
  }
302
+ if (contextKey) {
303
+ params.set('contextKey', contextKey);
304
+ }
281
305
  return this.http
282
306
  .get(`${this.getBaseUrl(mode)}/builder/next-tokens?${params.toString()}`)
283
307
  .pipe(map((response) => this.unwrapResponse(response)), tap((payload) => {
@@ -460,6 +484,15 @@ function appendToPath(basePath, operatorToken, value, isBase) {
460
484
  return value ? `${withOperator}.${value}` : withOperator;
461
485
  }
462
486
 
487
+ /** Resolve a host override before API context and the package fallback catalog. */
488
+ function resolveFormulaFunctionCategories(configured, context, fallback) {
489
+ if (configured.length > 0)
490
+ return configured;
491
+ if (context.length > 0)
492
+ return context;
493
+ return fallback;
494
+ }
495
+
463
496
  const DEFAULT_SCOPE_ICON = 'general.placeholder';
464
497
  const DEFAULT_TOOLBAR_TABS = [
465
498
  'functions',
@@ -509,6 +542,15 @@ class FormulaBuilder {
509
542
  levelSchemaId = input(...(ngDevMode ? [undefined, { debugName: "levelSchemaId" }] : /* istanbul ignore next */ []));
510
543
  /** Optional module ID when the formula is scoped inside a level-linked module */
511
544
  moduleId = input(...(ngDevMode ? [undefined, { debugName: "moduleId" }] : /* istanbul ignore next */ []));
545
+ /**
546
+ * Canonical context identifier — `Module:{id}` / `Level:{id}` /
547
+ * `Level:{id}/Module:{id}`. When set it is the source of truth for the
548
+ * context kind and execution profile: it is the only way to address a
549
+ * standalone module (no workspace/level schema), and the builder loads its
550
+ * context, scopes, functions and validation through it. When omitted the
551
+ * legacy `levelSchemaId` + `moduleId` flow is used unchanged.
552
+ */
553
+ contextKey = input(...(ngDevMode ? [undefined, { debugName: "contextKey" }] : /* istanbul ignore next */ []));
512
554
  /** Optional current context entity key, e.g. LevelData or Risk */
513
555
  contextEntityTypeKey = input(...(ngDevMode ? [undefined, { debugName: "contextEntityTypeKey" }] : /* istanbul ignore next */ []));
514
556
  /** Template ID for validation context */
@@ -521,6 +563,10 @@ class FormulaBuilder {
521
563
  hideStatusBar = input(false, ...(ngDevMode ? [{ debugName: "hideStatusBar" }] : /* istanbul ignore next */ []));
522
564
  /** Visible toolbar tabs */
523
565
  toolbarTabs = input(DEFAULT_TOOLBAR_TABS, ...(ngDevMode ? [{ debugName: "toolbarTabs" }] : /* istanbul ignore next */ []));
566
+ /** Optional host-owned function catalog. Takes precedence over API context. */
567
+ functionCategories = input([], ...(ngDevMode ? [{ debugName: "functionCategories" }] : /* istanbul ignore next */ []));
568
+ /** Operators shown in the formula toolbar. */
569
+ operators = input(DEFAULT_OPERATORS, ...(ngDevMode ? [{ debugName: "operators" }] : /* istanbul ignore next */ []));
524
570
  /** Force code editor mode and hide the mode toggle */
525
571
  codeOnly = input(false, ...(ngDevMode ? [{ debugName: "codeOnly" }] : /* istanbul ignore next */ []));
526
572
  /** Keep the block builder active and hide the code-mode toggle. */
@@ -560,6 +606,50 @@ class FormulaBuilder {
560
606
  const normalized = contextType.trim().toLowerCase();
561
607
  return normalized.length > 0 && normalized !== 'leveldata';
562
608
  }, ...(ngDevMode ? [{ debugName: "isModuleContext" }] : /* istanbul ignore next */ []));
609
+ /** Trimmed canonical context key, or undefined when not supplied. */
610
+ resolvedContextKey = computed(() => {
611
+ const value = this.contextKey();
612
+ if (typeof value !== 'string') {
613
+ return undefined;
614
+ }
615
+ const normalized = value.trim();
616
+ return normalized.length > 0 ? normalized : undefined;
617
+ }, ...(ngDevMode ? [{ debugName: "resolvedContextKey" }] : /* istanbul ignore next */ []));
618
+ /**
619
+ * True when authoring is current-only (a standalone module / `Module:{id}`).
620
+ * Prefers the resolved context (execution profile / kind) once loaded, and
621
+ * falls back to the shape of the context key before the context arrives so
622
+ * navigation scopes never flash for a standalone module.
623
+ */
624
+ isCurrentOnlyContext = computed(() => {
625
+ const context = this.builderContext();
626
+ if (context?.executionProfile) {
627
+ return context.executionProfile === 'InlineCurrentOnly';
628
+ }
629
+ if (context?.contextKind === 'StandaloneModule') {
630
+ return true;
631
+ }
632
+ const key = this.resolvedContextKey();
633
+ return key ? /^module:/i.test(key) : false;
634
+ }, ...(ngDevMode ? [{ debugName: "isCurrentOnlyContext" }] : /* istanbul ignore next */ []));
635
+ /**
636
+ * Scope tokens the backend allows for this context, or null when it doesn't
637
+ * constrain them (legacy contexts). Used to gate the visible navigation.
638
+ */
639
+ supportedScopeTokens = computed(() => {
640
+ const tokens = this.builderContext()?.supportedScopeTokens;
641
+ return tokens && tokens.length > 0 ? tokens : null;
642
+ }, ...(ngDevMode ? [{ debugName: "supportedScopeTokens" }] : /* istanbul ignore next */ []));
643
+ /**
644
+ * Schema id for downstream properties / next-token / validation calls. The
645
+ * context response is authoritative (a standalone module resolves its own
646
+ * `formulaSchemaId`); falls back to the `levelSchemaId` input for legacy
647
+ * flows and before the context loads.
648
+ */
649
+ effectiveSchemaId = computed(() => {
650
+ const context = this.builderContext();
651
+ return (context?.formulaSchemaId ?? context?.schemaId ?? this.levelSchemaId());
652
+ }, ...(ngDevMode ? [{ debugName: "effectiveSchemaId" }] : /* istanbul ignore next */ []));
563
653
  autocompleteProvider = (request) => {
564
654
  const apiMode = this.formulaApiMode();
565
655
  // Code-mode value picking: when the cursor sits on the right-hand side of a
@@ -579,10 +669,14 @@ class FormulaBuilder {
579
669
  return this.fetchAutocomplete$(request, apiMode);
580
670
  };
581
671
  fetchAutocomplete$(request, apiMode) {
672
+ const currentOnly = this.isCurrentOnlyContext();
582
673
  return this.autocompleteService.getSuggestions({
583
674
  ...request,
584
- levelSchemaId: this.levelSchemaId(),
585
- moduleId: this.moduleId(),
675
+ // A standalone module is addressed only by its context key; a
676
+ // workspace/level schema id alongside it is rejected by the backend.
677
+ levelSchemaId: currentOnly ? undefined : this.levelSchemaId(),
678
+ moduleId: currentOnly ? undefined : this.moduleId(),
679
+ contextKey: this.resolvedContextKey(),
586
680
  contextEntityTypeKey: this.resolvedContextEntityTypeKey() ??
587
681
  this.builderContext()?.contextEntityTypeKey,
588
682
  }, apiMode);
@@ -607,11 +701,8 @@ class FormulaBuilder {
607
701
  isValidating = this.validatorService.isValidating;
608
702
  /** Builder context from API */
609
703
  builderContext = signal(null, ...(ngDevMode ? [{ debugName: "builderContext" }] : /* istanbul ignore next */ []));
610
- /** Function categories from context */
611
- functionCategories = computed(() => {
612
- const categories = this.builderContext()?.functions?.categories ?? [];
613
- return categories.length ? categories : VALIDATION_FUNCTION_CATEGORIES;
614
- }, ...(ngDevMode ? [{ debugName: "functionCategories" }] : /* istanbul ignore next */ []));
704
+ /** Function categories from the host, context API, or validation fallback. */
705
+ resolvedFunctionCategories = computed(() => resolveFormulaFunctionCategories(this.functionCategories(), this.builderContext()?.functions?.categories ?? [], VALIDATION_FUNCTION_CATEGORIES), ...(ngDevMode ? [{ debugName: "resolvedFunctionCategories" }] : /* istanbul ignore next */ []));
615
706
  /** Is builder context loading */
616
707
  isContextLoading = signal(false, ...(ngDevMode ? [{ debugName: "isContextLoading" }] : /* istanbul ignore next */ []));
617
708
  contextRequestId = 0;
@@ -633,10 +724,11 @@ class FormulaBuilder {
633
724
  // Validate when formula changes
634
725
  effect(() => {
635
726
  const formula = this.expressionValue();
636
- const moduleId = this.moduleId();
637
727
  const apiMode = this.formulaApiMode();
728
+ const contextKey = this.resolvedContextKey();
729
+ const currentOnly = this.isCurrentOnlyContext();
638
730
  if (formula && formula.trim().length > 0) {
639
- this.validatorService.validate(formula, undefined, this.levelSchemaId(), moduleId, this.templateId(), apiMode);
731
+ this.validatorService.validate(formula, undefined, currentOnly ? undefined : this.levelSchemaId(), currentOnly ? undefined : this.moduleId(), this.templateId(), apiMode, contextKey);
640
732
  }
641
733
  });
642
734
  // Emit validation changes
@@ -660,13 +752,16 @@ class FormulaBuilder {
660
752
  this.emitValueChange();
661
753
  }
662
754
  });
663
- // Load builder context when schema changes
755
+ // Load builder context when schema or context key changes
664
756
  effect(() => {
665
757
  const schemaId = this.levelSchemaId();
666
758
  const moduleId = this.moduleId();
667
759
  const apiMode = this.formulaApiMode();
668
760
  const contextEntityTypeKey = this.resolvedContextEntityTypeKey();
669
- if (!schemaId) {
761
+ const contextKey = this.resolvedContextKey();
762
+ // A standalone module has no level schema id — the context key alone is
763
+ // enough to load its context.
764
+ if (!schemaId && !contextKey) {
670
765
  this.builderContext.set(null);
671
766
  this.propertiesByPathApi.set({});
672
767
  this.nextTokenRulesByPath.set({});
@@ -678,7 +773,7 @@ class FormulaBuilder {
678
773
  this.propertiesByPathApi.set({});
679
774
  this.nextTokenRulesByPath.set({});
680
775
  this.contextService
681
- .loadContext(schemaId, moduleId, contextEntityTypeKey, apiMode)
776
+ .loadContext(schemaId, moduleId, contextEntityTypeKey, apiMode, contextKey)
682
777
  .subscribe((context) => {
683
778
  if (requestId !== this.contextRequestId) {
684
779
  return;
@@ -734,8 +829,9 @@ class FormulaBuilder {
734
829
  });
735
830
  // Fetch properties for the selected table path
736
831
  effect(() => {
737
- const schemaId = this.levelSchemaId();
738
- const moduleId = this.moduleId();
832
+ const schemaId = this.effectiveSchemaId();
833
+ const contextKey = this.resolvedContextKey();
834
+ const moduleId = contextKey ? undefined : this.moduleId();
739
835
  const apiMode = this.formulaApiMode();
740
836
  const tablePath = this.resolvedTablePath();
741
837
  const component = this.propertiesComponent();
@@ -751,7 +847,7 @@ class FormulaBuilder {
751
847
  }
752
848
  this.propertyLoadingPath.set(tablePath);
753
849
  this.contextService
754
- .getScopeProperties(schemaId, component, tablePath, moduleId, apiMode)
850
+ .getScopeProperties(schemaId, component, tablePath, moduleId, apiMode, contextKey)
755
851
  .subscribe((props) => {
756
852
  this.propertiesByPathApi.update((current) => ({
757
853
  ...current,
@@ -765,8 +861,9 @@ class FormulaBuilder {
765
861
  // Fetch next-token rules for the selected base scope so the first
766
862
  // parameter popover can use localized allowedValueItems.
767
863
  effect(() => {
768
- const schemaId = this.levelSchemaId();
769
- const moduleId = this.moduleId();
864
+ const schemaId = this.effectiveSchemaId();
865
+ const contextKey = this.resolvedContextKey();
866
+ const moduleId = contextKey ? undefined : this.moduleId();
770
867
  const isProcessBuilder = this.isProcessBuilder();
771
868
  const scope = this.propertyScope();
772
869
  const component = this.propertiesComponent();
@@ -787,7 +884,7 @@ class FormulaBuilder {
787
884
  if (this.nextTokenRulesByPath()[scope])
788
885
  return;
789
886
  this.contextService
790
- .getNextTokens(schemaId, component, scope, moduleId)
887
+ .getNextTokens(schemaId, component, scope, moduleId, 'default', contextKey)
791
888
  .subscribe((response) => {
792
889
  if (!response)
793
890
  return;
@@ -799,8 +896,9 @@ class FormulaBuilder {
799
896
  });
800
897
  // Fetch next-token rules for the current table path
801
898
  effect(() => {
802
- const schemaId = this.levelSchemaId();
803
- const moduleId = this.moduleId();
899
+ const schemaId = this.effectiveSchemaId();
900
+ const contextKey = this.resolvedContextKey();
901
+ const moduleId = contextKey ? undefined : this.moduleId();
804
902
  const isProcessBuilder = this.isProcessBuilder();
805
903
  const tablePath = this.resolvedTablePath();
806
904
  const component = this.propertiesComponent();
@@ -809,7 +907,7 @@ class FormulaBuilder {
809
907
  if (this.nextTokenRulesByPath()[tablePath])
810
908
  return;
811
909
  this.contextService
812
- .getNextTokens(schemaId, component, tablePath, moduleId)
910
+ .getNextTokens(schemaId, component, tablePath, moduleId, 'default', contextKey)
813
911
  .subscribe((response) => {
814
912
  if (!response)
815
913
  return;
@@ -1098,11 +1196,15 @@ class FormulaBuilder {
1098
1196
  return (this.nextTokenRulesByPath()[scope]?.[scope] ?? this.contextBaseRule());
1099
1197
  }, ...(ngDevMode ? [{ debugName: "baseRule" }] : /* istanbul ignore next */ []));
1100
1198
  availableScopes = computed(() => {
1199
+ // When the backend constrains scope tokens (e.g. a standalone module allows
1200
+ // only `Current`), never surface a navigation scope outside that set.
1201
+ const allowedTokens = this.supportedScopeTokens();
1202
+ const isAllowedScope = (token) => !allowedTokens || allowedTokens.includes(token);
1101
1203
  const operators = this.contextOperators();
1102
1204
  if (operators.length === 0) {
1103
1205
  const propertiesByPath = this.mergedPropertiesByPath();
1104
1206
  return Object.keys(propertiesByPath)
1105
- .filter((path) => (propertiesByPath[path] ?? []).length > 0)
1207
+ .filter((path) => (propertiesByPath[path] ?? []).length > 0 && isAllowedScope(path))
1106
1208
  .map((path) => ({
1107
1209
  key: path,
1108
1210
  label: `@${path}`,
@@ -1112,7 +1214,7 @@ class FormulaBuilder {
1112
1214
  }
1113
1215
  const baseRules = this.builderContext()?.schemaDescriptor?.nextTokenRules ?? {};
1114
1216
  const priority = new Map(PROPERTY_SCOPE_PRIORITY.map((token, index) => [token, index]));
1115
- const scopedOperators = operators.filter((operator) => operator.token.trim().length > 0);
1217
+ const scopedOperators = operators.filter((operator) => operator.token.trim().length > 0 && isAllowedScope(operator.token));
1116
1218
  return [...scopedOperators]
1117
1219
  .filter((operator) => {
1118
1220
  const parameterKind = baseRules[operator.token]?.parameterKind ?? operator.parameterKind;
@@ -1390,13 +1492,13 @@ class FormulaBuilder {
1390
1492
  return translated && translated !== key ? translated : token;
1391
1493
  }
1392
1494
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
1393
- 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 }, builderOnly: { classPropertyName: "builderOnly", publicName: "builderOnly", isSignal: true, isRequired: false, transformFunction: null }, valueMode: { classPropertyName: "valueMode", publicName: "valueMode", 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: [
1495
+ 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 }, contextKey: { classPropertyName: "contextKey", publicName: "contextKey", 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 }, functionCategories: { classPropertyName: "functionCategories", publicName: "functionCategories", isSignal: true, isRequired: false, transformFunction: null }, operators: { classPropertyName: "operators", publicName: "operators", isSignal: true, isRequired: false, transformFunction: null }, codeOnly: { classPropertyName: "codeOnly", publicName: "codeOnly", isSignal: true, isRequired: false, transformFunction: null }, builderOnly: { classPropertyName: "builderOnly", publicName: "builderOnly", isSignal: true, isRequired: false, transformFunction: null }, valueMode: { classPropertyName: "valueMode", publicName: "valueMode", 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: [
1394
1496
  {
1395
1497
  provide: NG_VALUE_ACCESSOR,
1396
1498
  useExisting: forwardRef(() => FormulaBuilder),
1397
1499
  multi: true,
1398
1500
  },
1399
- ], 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\r\n <!-- Value Selection - Only for lookup / status / lookup-matrix fields -->\r\n @if (isLookupProperty()) {\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.value\") }}</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 (isLookupItemsLoading()) {\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-value')\"\r\n [(ngModel)]=\"lookupValueKey\"\r\n [options]=\"lookupItems()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n }\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 gap-2\">\r\n @if (isLookupProperty()) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert-value')\"\r\n icon=\"general.plus\"\r\n severity=\"secondary\"\r\n [outlined]=\"true\"\r\n [disabled]=\"!canInsertLookupValue()\"\r\n (onClick)=\"insertSelectedLookupValue(insertBlock)\"\r\n ></mt-button>\r\n }\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 (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", "hint", "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", "markCurrentUser"], 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"] }] });
1501
+ ], 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\n headless\n [class.ring-2]=\"hasFocus()\"\n [class.ring-primary]=\"hasFocus()\"\n [paddingless]=\"true\"\n>\n <div\n *transloco=\"let t; prefix: 'formulaBuilder'\"\n class=\"flex flex-col overflow-hidden\"\n >\n <!-- Toolbar - Pass data via inputs (pure component) -->\n @if (!hideToolbar()) {\n <mt-formula-toolbar\n [functionCategories]=\"resolvedFunctionCategories()\"\n [operators]=\"operators()\"\n [visibleTabs]=\"resolvedToolbarTabs()\"\n (onBlockInsert)=\"onBlockInsert($event)\"\n >\n <ng-template #properties let-insertBlock=\"insertBlock\">\n @if (isContextLoading()) {\n <div class=\"flex flex-col gap-4 p-4\">\n <div class=\"flex items-center justify-between gap-4\">\n <p-skeleton\n width=\"18rem\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n <p-skeleton\n width=\"7rem\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <div class=\"flex items-center gap-4\">\n <div\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\"\n >\n <div class=\"flex items-center gap-2\">\n <p-skeleton\n width=\"3rem\"\n height=\"1rem\"\n styleClass=\"rounded-md\"\n />\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <p-skeleton\n class=\"flex-1\"\n height=\"2rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <p-skeleton\n width=\"3rem\"\n height=\"1rem\"\n styleClass=\"rounded-md\"\n />\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\n </div>\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-4 p-4\">\n <!-- Scope Selection - Full width, centered -->\n <div class=\"flex items-center justify-between gap-4\">\n <mt-radio-cards\n [options]=\"scopeOptions()\"\n [activeId]=\"propertyScope()\"\n (selectionChange)=\"onScopeChange($event)\"\n size=\"small\"\n />\n </div>\n\n <!-- Path + Field + Preview Row -->\n <div class=\"flex items-center gap-4\">\n <!-- Path Container - Flexible -->\n <div\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\"\n >\n <div class=\"flex items-center gap-2\">\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.path\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (isDirectAccess()) {\n <span\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\"\n >\n \u2713 {{ t(\"properties.direct-access\") }}\n </span>\n } @else if (pathSegments().length === 0) {\n <span class=\"text-xs italic text-slate-400\">{{\n t(\"properties.no-path-configured\")\n }}</span>\n } @else {\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\n >\n @for (\n segment of pathSegments();\n track $index;\n let segmentIndex = $index\n ) {\n @if (segmentIndex > 0) {\n <span\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\n >\u203A</span\n >\n }\n <mt-button\n type=\"button\"\n [label]=\"\n segment.value ||\n t('properties.select') +\n ' ' +\n getOperatorDisplayName(segment.operatorToken)\n \"\n icon=\"arrow.chevron-down\"\n [outlined]=\"true\"\n size=\"small\"\n [severity]=\"\n segment.value ? 'primary' : 'secondary'\n \"\n [iconPos]=\"'end'\"\n (onClick)=\"pathPopover.toggle($event)\"\n ></mt-button>\n\n <p-popover\n #pathPopover\n [style]=\"{ width: 'max-content' }\"\n appendTo=\"body\"\n >\n <div class=\"p-2\">\n <div\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\n >\n {{ t(\"properties.select\") }}\n {{\n getOperatorDisplayName(\n segment.operatorToken\n )\n }}\n </div>\n <div\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\n >\n @for (\n option of segmentOptions(segmentIndex);\n track option.key\n ) {\n <mt-button\n type=\"button\"\n [label]=\"option.name\"\n [icon]=\"\n segment.value === option.key\n ? 'general.check'\n : 'general.minus'\n \"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"secondary\"\n [styleClass]=\"\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\n (segment.value === option.key\n ? 'bg-primary text-white'\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\n \"\n (onClick)=\"\n setPathSegmentValue(\n segmentIndex,\n option.key\n );\n pathPopover.hide()\n \"\n ></mt-button>\n }\n </div>\n @if (segment.optional) {\n <mt-button\n type=\"button\"\n [label]=\"t('actions.clear')\"\n [outlined]=\"true\"\n icon=\"general.x-close\"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"danger\"\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\"\n (onClick)=\"\n setPathSegmentValue(segmentIndex, null);\n pathPopover.hide()\n \"\n ></mt-button>\n }\n </div>\n </p-popover>\n\n @if (\n segment.canRemove &&\n segmentIndex === pathSegments().length - 1\n ) {\n <mt-button\n type=\"button\"\n icon=\"general.x-close\"\n size=\"small\"\n severity=\"danger\"\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\n (onClick)=\"removePathSegment(segmentIndex)\"\n ></mt-button>\n }\n }\n <mt-button\n type=\"button\"\n icon=\"general.plus\"\n size=\"small\"\n severity=\"primary\"\n [disabled]=\"!canAddNextSegment()\"\n [class.hidden]=\"!canAddNextSegment()\"\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\"\n (onClick)=\"nextOperatorPopover.toggle($event)\"\n ></mt-button>\n </div>\n }\n <p-popover\n #nextOperatorPopover\n [style]=\"{ width: 'max-content' }\"\n appendTo=\"body\"\n >\n <div class=\"p-2\">\n <div\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\n >\n {{ t(\"properties.add-segment\") }}\n </div>\n <div\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\n >\n @for (\n option of nextOperatorOptions();\n track option.token\n ) {\n <mt-button\n type=\"button\"\n [label]=\"getOperatorDisplayName(option.token)\"\n icon=\"general.plus\"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"secondary\"\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\"\n (onClick)=\"\n onAddSegmentSelection(\n option.token,\n nextOperatorPopover\n )\n \"\n ></mt-button>\n }\n </div>\n </div>\n </p-popover>\n </div>\n </div>\n\n <!-- Field Selection - Under Path -->\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.field\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n @if (isPropertyLoading()) {\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n } @else {\n <mt-select-field\n class=\"flex-1\"\n [label]=\"''\"\n [filter]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('properties.select')\"\n [(ngModel)]=\"propertyFieldKey\"\n [options]=\"propertyOptions()\"\n optionLabel=\"name\"\n optionValue=\"key\"\n [size]=\"'small'\"\n />\n }\n </div>\n\n <!-- Value Selection - Only for lookup / status / lookup-matrix fields -->\n @if (isLookupProperty()) {\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.value\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n @if (isLookupItemsLoading()) {\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n } @else {\n <mt-select-field\n class=\"flex-1\"\n [label]=\"''\"\n [filter]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('properties.select-value')\"\n [(ngModel)]=\"lookupValueKey\"\n [options]=\"lookupItems()\"\n optionLabel=\"name\"\n optionValue=\"key\"\n [size]=\"'small'\"\n />\n }\n </div>\n }\n <div\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\"\n [mtTooltip]=\"\n '@' +\n propertyTablePath() +\n '::' +\n (selectedProperty()?.key ?? '...')\n \"\n tooltipPosition=\"top\"\n >\n @{{ propertyTablePath() }}::{{\n selectedProperty()?.key ?? \"...\"\n }}\n </div>\n <div class=\"flex justify-end gap-2\">\n @if (isLookupProperty()) {\n <mt-button\n type=\"button\"\n [label]=\"t('actions.insert-value')\"\n icon=\"general.plus\"\n severity=\"secondary\"\n [outlined]=\"true\"\n [disabled]=\"!canInsertLookupValue()\"\n (onClick)=\"insertSelectedLookupValue(insertBlock)\"\n ></mt-button>\n }\n <mt-button\n type=\"button\"\n [label]=\"t('actions.insert')\"\n icon=\"general.plus\"\n severity=\"primary\"\n [disabled]=\"!canInsertProperty()\"\n (onClick)=\"insertSelectedProperty(insertBlock)\"\n ></mt-button>\n </div>\n </div>\n </div>\n </div>\n }\n </ng-template>\n </mt-formula-toolbar>\n }\n\n <!-- Editor Area -->\n <div class=\"p-3\">\n <div\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\n >\n @if (showModeToggle()) {\n <div\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\n >\n <mt-toggle-field\n [label]=\"t('codeMode')\"\n labelPosition=\"end\"\n size=\"small\"\n [ngModel]=\"isCodeMode()\"\n (ngModelChange)=\"onModeToggle($event)\"\n />\n </div>\n }\n <div class=\"p-0\">\n @if (isCodeMode()) {\n <mt-formula-editor-code\n #formulaEditorCode\n [placeholder]=\"placeholder() || t('placeholder')\"\n [initialFormula]=\"expression()\"\n [borderless]=\"true\"\n [autocompleteProvider]=\"autocompleteProvider\"\n (formulaChange)=\"onCodeFormulaChange($event)\"\n (onFocus)=\"onEditorFocus()\"\n (onBlur)=\"onEditorBlur()\"\n />\n } @else {\n <mt-formula-editor\n #formulaEditor\n [placeholder]=\"placeholder() || t('placeholder')\"\n [initialTokens]=\"tokens()\"\n [borderless]=\"true\"\n (formulaChange)=\"onFormulaChange($event)\"\n (tokensChange)=\"onTokensChange($event)\"\n (onFocus)=\"onEditorFocus()\"\n (onBlur)=\"onEditorBlur()\"\n />\n }\n </div>\n </div>\n </div>\n\n <!-- Status Bar - Pass data via inputs (pure component) -->\n @if (!hideStatusBar()) {\n <mt-formula-status-bar [validation]=\"validation()\" />\n }\n </div>\n</mt-card>\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", "hint", "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", "markCurrentUser"], 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"] }] });
1400
1502
  }
1401
1503
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FormulaBuilder, decorators: [{
1402
1504
  type: Component,
@@ -1424,8 +1526,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1424
1526
  useExisting: forwardRef(() => FormulaBuilder),
1425
1527
  multi: true,
1426
1528
  },
1427
- ], 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\r\n <!-- Value Selection - Only for lookup / status / lookup-matrix fields -->\r\n @if (isLookupProperty()) {\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.value\") }}</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 (isLookupItemsLoading()) {\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-value')\"\r\n [(ngModel)]=\"lookupValueKey\"\r\n [options]=\"lookupItems()\"\r\n optionLabel=\"name\"\r\n optionValue=\"key\"\r\n [size]=\"'small'\"\r\n />\r\n }\r\n </div>\r\n }\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 gap-2\">\r\n @if (isLookupProperty()) {\r\n <mt-button\r\n type=\"button\"\r\n [label]=\"t('actions.insert-value')\"\r\n icon=\"general.plus\"\r\n severity=\"secondary\"\r\n [outlined]=\"true\"\r\n [disabled]=\"!canInsertLookupValue()\"\r\n (onClick)=\"insertSelectedLookupValue(insertBlock)\"\r\n ></mt-button>\r\n }\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 (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" }]
1428
- }], 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 }] }], builderOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "builderOnly", required: false }] }], valueMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueMode", 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"] }] } });
1529
+ ], template: "<mt-card\n headless\n [class.ring-2]=\"hasFocus()\"\n [class.ring-primary]=\"hasFocus()\"\n [paddingless]=\"true\"\n>\n <div\n *transloco=\"let t; prefix: 'formulaBuilder'\"\n class=\"flex flex-col overflow-hidden\"\n >\n <!-- Toolbar - Pass data via inputs (pure component) -->\n @if (!hideToolbar()) {\n <mt-formula-toolbar\n [functionCategories]=\"resolvedFunctionCategories()\"\n [operators]=\"operators()\"\n [visibleTabs]=\"resolvedToolbarTabs()\"\n (onBlockInsert)=\"onBlockInsert($event)\"\n >\n <ng-template #properties let-insertBlock=\"insertBlock\">\n @if (isContextLoading()) {\n <div class=\"flex flex-col gap-4 p-4\">\n <div class=\"flex items-center justify-between gap-4\">\n <p-skeleton\n width=\"18rem\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n <p-skeleton\n width=\"7rem\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <div class=\"flex items-center gap-4\">\n <div\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\"\n >\n <div class=\"flex items-center gap-2\">\n <p-skeleton\n width=\"3rem\"\n height=\"1rem\"\n styleClass=\"rounded-md\"\n />\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <p-skeleton\n class=\"flex-1\"\n height=\"2rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <p-skeleton\n width=\"3rem\"\n height=\"1rem\"\n styleClass=\"rounded-md\"\n />\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n </div>\n <p-skeleton height=\"2rem\" styleClass=\"rounded-md\" />\n </div>\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-4 p-4\">\n <!-- Scope Selection - Full width, centered -->\n <div class=\"flex items-center justify-between gap-4\">\n <mt-radio-cards\n [options]=\"scopeOptions()\"\n [activeId]=\"propertyScope()\"\n (selectionChange)=\"onScopeChange($event)\"\n size=\"small\"\n />\n </div>\n\n <!-- Path + Field + Preview Row -->\n <div class=\"flex items-center gap-4\">\n <!-- Path Container - Flexible -->\n <div\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\"\n >\n <div class=\"flex items-center gap-2\">\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.path\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n @if (isDirectAccess()) {\n <span\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\"\n >\n \u2713 {{ t(\"properties.direct-access\") }}\n </span>\n } @else if (pathSegments().length === 0) {\n <span class=\"text-xs italic text-slate-400\">{{\n t(\"properties.no-path-configured\")\n }}</span>\n } @else {\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5 overflow-x-auto\"\n >\n @for (\n segment of pathSegments();\n track $index;\n let segmentIndex = $index\n ) {\n @if (segmentIndex > 0) {\n <span\n class=\"shrink-0 text-slate-300 dark:text-slate-600\"\n >\u203A</span\n >\n }\n <mt-button\n type=\"button\"\n [label]=\"\n segment.value ||\n t('properties.select') +\n ' ' +\n getOperatorDisplayName(segment.operatorToken)\n \"\n icon=\"arrow.chevron-down\"\n [outlined]=\"true\"\n size=\"small\"\n [severity]=\"\n segment.value ? 'primary' : 'secondary'\n \"\n [iconPos]=\"'end'\"\n (onClick)=\"pathPopover.toggle($event)\"\n ></mt-button>\n\n <p-popover\n #pathPopover\n [style]=\"{ width: 'max-content' }\"\n appendTo=\"body\"\n >\n <div class=\"p-2\">\n <div\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\n >\n {{ t(\"properties.select\") }}\n {{\n getOperatorDisplayName(\n segment.operatorToken\n )\n }}\n </div>\n <div\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\n >\n @for (\n option of segmentOptions(segmentIndex);\n track option.key\n ) {\n <mt-button\n type=\"button\"\n [label]=\"option.name\"\n [icon]=\"\n segment.value === option.key\n ? 'general.check'\n : 'general.minus'\n \"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"secondary\"\n [styleClass]=\"\n 'w-full justify-start rounded-md px-2.5 py-1.5 text-left text-xs font-medium transition-colors ' +\n (segment.value === option.key\n ? 'bg-primary text-white'\n : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700')\n \"\n (onClick)=\"\n setPathSegmentValue(\n segmentIndex,\n option.key\n );\n pathPopover.hide()\n \"\n ></mt-button>\n }\n </div>\n @if (segment.optional) {\n <mt-button\n type=\"button\"\n [label]=\"t('actions.clear')\"\n [outlined]=\"true\"\n icon=\"general.x-close\"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"danger\"\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\"\n (onClick)=\"\n setPathSegmentValue(segmentIndex, null);\n pathPopover.hide()\n \"\n ></mt-button>\n }\n </div>\n </p-popover>\n\n @if (\n segment.canRemove &&\n segmentIndex === pathSegments().length - 1\n ) {\n <mt-button\n type=\"button\"\n icon=\"general.x-close\"\n size=\"small\"\n severity=\"danger\"\n styleClass=\"flex size-5 shrink-0 items-center justify-center rounded-full bg-red-500 text-white hover:bg-red-600\"\n (onClick)=\"removePathSegment(segmentIndex)\"\n ></mt-button>\n }\n }\n <mt-button\n type=\"button\"\n icon=\"general.plus\"\n size=\"small\"\n severity=\"primary\"\n [disabled]=\"!canAddNextSegment()\"\n [class.hidden]=\"!canAddNextSegment()\"\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\"\n (onClick)=\"nextOperatorPopover.toggle($event)\"\n ></mt-button>\n </div>\n }\n <p-popover\n #nextOperatorPopover\n [style]=\"{ width: 'max-content' }\"\n appendTo=\"body\"\n >\n <div class=\"p-2\">\n <div\n class=\"mb-2 text-xs font-semibold uppercase text-slate-400\"\n >\n {{ t(\"properties.add-segment\") }}\n </div>\n <div\n class=\"flex max-h-[min(24rem,calc(100vh-10rem))] flex-col gap-1 overflow-y-auto pr-1\"\n >\n @for (\n option of nextOperatorOptions();\n track option.token\n ) {\n <mt-button\n type=\"button\"\n [label]=\"getOperatorDisplayName(option.token)\"\n icon=\"general.plus\"\n [iconPos]=\"'end'\"\n size=\"small\"\n severity=\"secondary\"\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\"\n (onClick)=\"\n onAddSegmentSelection(\n option.token,\n nextOperatorPopover\n )\n \"\n ></mt-button>\n }\n </div>\n </div>\n </p-popover>\n </div>\n </div>\n\n <!-- Field Selection - Under Path -->\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.field\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n @if (isPropertyLoading()) {\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n } @else {\n <mt-select-field\n class=\"flex-1\"\n [label]=\"''\"\n [filter]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('properties.select')\"\n [(ngModel)]=\"propertyFieldKey\"\n [options]=\"propertyOptions()\"\n optionLabel=\"name\"\n optionValue=\"key\"\n [size]=\"'small'\"\n />\n }\n </div>\n\n <!-- Value Selection - Only for lookup / status / lookup-matrix fields -->\n @if (isLookupProperty()) {\n <div\n class=\"flex items-center gap-2 border-t border-slate-200 pt-2 dark:border-slate-700\"\n >\n <span\n class=\"shrink-0 text-xs font-semibold uppercase tracking-wider text-slate-400\"\n >{{ t(\"properties.value\") }}</span\n >\n <div\n class=\"h-4 w-px shrink-0 bg-slate-200 dark:bg-slate-700\"\n ></div>\n @if (isLookupItemsLoading()) {\n <p-skeleton\n class=\"flex-1\"\n height=\"2.5rem\"\n styleClass=\"rounded-md\"\n />\n } @else {\n <mt-select-field\n class=\"flex-1\"\n [label]=\"''\"\n [filter]=\"true\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('properties.select-value')\"\n [(ngModel)]=\"lookupValueKey\"\n [options]=\"lookupItems()\"\n optionLabel=\"name\"\n optionValue=\"key\"\n [size]=\"'small'\"\n />\n }\n </div>\n }\n <div\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\"\n [mtTooltip]=\"\n '@' +\n propertyTablePath() +\n '::' +\n (selectedProperty()?.key ?? '...')\n \"\n tooltipPosition=\"top\"\n >\n @{{ propertyTablePath() }}::{{\n selectedProperty()?.key ?? \"...\"\n }}\n </div>\n <div class=\"flex justify-end gap-2\">\n @if (isLookupProperty()) {\n <mt-button\n type=\"button\"\n [label]=\"t('actions.insert-value')\"\n icon=\"general.plus\"\n severity=\"secondary\"\n [outlined]=\"true\"\n [disabled]=\"!canInsertLookupValue()\"\n (onClick)=\"insertSelectedLookupValue(insertBlock)\"\n ></mt-button>\n }\n <mt-button\n type=\"button\"\n [label]=\"t('actions.insert')\"\n icon=\"general.plus\"\n severity=\"primary\"\n [disabled]=\"!canInsertProperty()\"\n (onClick)=\"insertSelectedProperty(insertBlock)\"\n ></mt-button>\n </div>\n </div>\n </div>\n </div>\n }\n </ng-template>\n </mt-formula-toolbar>\n }\n\n <!-- Editor Area -->\n <div class=\"p-3\">\n <div\n class=\"overflow-hidden rounded-lg border border-surface-200 bg-white dark:border-surface-700 dark:bg-surface-800\"\n >\n @if (showModeToggle()) {\n <div\n class=\"flex items-center justify-end border-b border-surface-200 px-3 py-2 dark:border-surface-700\"\n >\n <mt-toggle-field\n [label]=\"t('codeMode')\"\n labelPosition=\"end\"\n size=\"small\"\n [ngModel]=\"isCodeMode()\"\n (ngModelChange)=\"onModeToggle($event)\"\n />\n </div>\n }\n <div class=\"p-0\">\n @if (isCodeMode()) {\n <mt-formula-editor-code\n #formulaEditorCode\n [placeholder]=\"placeholder() || t('placeholder')\"\n [initialFormula]=\"expression()\"\n [borderless]=\"true\"\n [autocompleteProvider]=\"autocompleteProvider\"\n (formulaChange)=\"onCodeFormulaChange($event)\"\n (onFocus)=\"onEditorFocus()\"\n (onBlur)=\"onEditorBlur()\"\n />\n } @else {\n <mt-formula-editor\n #formulaEditor\n [placeholder]=\"placeholder() || t('placeholder')\"\n [initialTokens]=\"tokens()\"\n [borderless]=\"true\"\n (formulaChange)=\"onFormulaChange($event)\"\n (tokensChange)=\"onTokensChange($event)\"\n (onFocus)=\"onEditorFocus()\"\n (onBlur)=\"onEditorBlur()\"\n />\n }\n </div>\n </div>\n </div>\n\n <!-- Status Bar - Pass data via inputs (pure component) -->\n @if (!hideStatusBar()) {\n <mt-formula-status-bar [validation]=\"validation()\" />\n }\n </div>\n</mt-card>\n" }]
1530
+ }], 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 }] }], contextKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextKey", 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 }] }], functionCategories: [{ type: i0.Input, args: [{ isSignal: true, alias: "functionCategories", required: false }] }], operators: [{ type: i0.Input, args: [{ isSignal: true, alias: "operators", required: false }] }], codeOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "codeOnly", required: false }] }], builderOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "builderOnly", required: false }] }], valueMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueMode", 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"] }] } });
1429
1531
 
1430
1532
  /**
1431
1533
  * Services barrel export