@myrtex-org/form 1.1.27 → 1.1.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, Component, ChangeDetectionStrategy, EventEmitter, Directive, Input, Output, ChangeDetectorRef, Inject, ViewChild, NgModule } from '@angular/core';
2
+ import { Injectable, inject, ChangeDetectionStrategy, Component, EventEmitter, Output, Input, Directive, ChangeDetectorRef, Inject, ViewChild, NgModule } from '@angular/core';
3
3
  import * as i1$3 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i1$1 from '@angular/router';
@@ -863,6 +863,184 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
863
863
  type: Injectable
864
864
  }] });
865
865
 
866
+ class FormulaCalculateService {
867
+ constructor() {
868
+ this._operatorPrecedence = {
869
+ '+': 1,
870
+ '-': 1,
871
+ '*': 2,
872
+ '/': 2,
873
+ };
874
+ this._operators = ['+', '-', '*', '/'];
875
+ }
876
+ calculateMathExpression(expression, values) {
877
+ try {
878
+ return this._calculateMathExpression(expression, values);
879
+ }
880
+ catch {
881
+ return 0;
882
+ }
883
+ }
884
+ calculateStringConcatenation(expression, values) {
885
+ return expression.replace(/{(?<systemName>\w+)(?::(?<format>[^}]+))?}/g, (_match, systemName) => {
886
+ const value = values.find(x => x.sysName === systemName);
887
+ return value?.value?.toString?.() ?? '';
888
+ });
889
+ }
890
+ _calculateMathExpression(expression, values) {
891
+ const operatorStack = [];
892
+ const operandStack = [];
893
+ for (let i = 0; i < expression.length; i++) {
894
+ const charValue = expression[i];
895
+ if (/\s/.test(charValue)) {
896
+ continue;
897
+ }
898
+ if (charValue === '{') {
899
+ let sysNameOperand = '';
900
+ while (i < expression.length && expression[i] !== '}') {
901
+ sysNameOperand += expression[i];
902
+ i++;
903
+ }
904
+ sysNameOperand += expression[i] ?? '';
905
+ const value = this._getNumberValue(values, sysNameOperand);
906
+ if (value === null) {
907
+ return 0;
908
+ }
909
+ operandStack.push(value);
910
+ continue;
911
+ }
912
+ if (charValue === '(') {
913
+ const nestedExpression = this._getExpressionBetweenParentheses(expression, i);
914
+ operandStack.push(this._calculateMathExpression(nestedExpression, values));
915
+ i += nestedExpression.length + 1;
916
+ continue;
917
+ }
918
+ if (this._isOperator(charValue)) {
919
+ this._addOperator(operatorStack, operandStack, charValue);
920
+ continue;
921
+ }
922
+ if (/\d/.test(charValue)) {
923
+ let number = '';
924
+ while (i < expression.length && (/[\d.]/.test(expression[i]))) {
925
+ number += expression[i];
926
+ i++;
927
+ }
928
+ i--;
929
+ const value = this._getNumberValue(values, number);
930
+ if (value === null) {
931
+ return 0;
932
+ }
933
+ operandStack.push(value);
934
+ continue;
935
+ }
936
+ if (/[a-zA-Z]/.test(charValue)) {
937
+ i += this._addFunctionResult(operandStack, values, expression, i);
938
+ }
939
+ }
940
+ this._calculateAll(operandStack, operatorStack);
941
+ return operandStack.pop() ?? 0;
942
+ }
943
+ _addFunctionResult(operandStack, values, expression, startIndex) {
944
+ const idxEof = expression.indexOf(')', startIndex);
945
+ const operand = expression.substring(startIndex, idxEof - startIndex + 1);
946
+ const lowerOperand = operand.toLowerCase();
947
+ if (lowerOperand.startsWith('pow(') && lowerOperand.endsWith(')')) {
948
+ const innerExpression = operand.substring(4, operand.length - 1);
949
+ const innerOperands = innerExpression.split(',').map(x => x.trim());
950
+ const baseValue = this._getNumberValue(values, innerOperands[0]);
951
+ const exponent = this._getNumberValue(values, innerOperands[1]);
952
+ if (baseValue === null || exponent === null) {
953
+ operandStack.push(0);
954
+ }
955
+ else {
956
+ operandStack.push(Math.pow(baseValue, exponent));
957
+ }
958
+ return operand.length - 1;
959
+ }
960
+ if (lowerOperand.startsWith('sqrt(') && lowerOperand.endsWith(')')) {
961
+ const innerExpression = operand.substring(5, operand.length - 1);
962
+ const value = this._getNumberValue(values, innerExpression);
963
+ operandStack.push(value === null ? 0 : Math.sqrt(value));
964
+ return operand.length - 1;
965
+ }
966
+ throw new Error('Invalid function operand');
967
+ }
968
+ _addOperator(operatorStack, operandStack, operatorToken) {
969
+ while (operatorStack.length > 0 &&
970
+ this._getOperatorPrecedence(operatorToken) <= this._getOperatorPrecedence(operatorStack[operatorStack.length - 1])) {
971
+ const operand2 = operandStack.pop() ?? 0;
972
+ const operand1 = operandStack.pop() ?? 0;
973
+ const operatorTokenToApply = operatorStack.pop();
974
+ operandStack.push(this._calculate(operand1, operand2, operatorTokenToApply));
975
+ }
976
+ operatorStack.push(operatorToken);
977
+ }
978
+ _calculateAll(operandStack, operatorStack) {
979
+ while (operatorStack.length > 0) {
980
+ const operatorToken = operatorStack.pop();
981
+ const operand2 = operandStack.pop() ?? 0;
982
+ const operand1 = operandStack.pop() ?? 0;
983
+ operandStack.push(this._calculate(operand1, operand2, operatorToken));
984
+ }
985
+ }
986
+ _getOperatorPrecedence(operatorToken) {
987
+ return this._operatorPrecedence[operatorToken];
988
+ }
989
+ _getExpressionBetweenParentheses(expression, startIndex) {
990
+ let level = 0;
991
+ for (let i = startIndex; i < expression.length; i++) {
992
+ if (expression[i] === '(') {
993
+ level++;
994
+ }
995
+ else if (expression[i] === ')') {
996
+ level--;
997
+ if (level === 0) {
998
+ return expression.substring(startIndex + 1, i);
999
+ }
1000
+ }
1001
+ }
1002
+ throw new Error('Unbalanced parentheses');
1003
+ }
1004
+ _getNumberValue(values, operand) {
1005
+ const cleanOperand = operand.trim().replace(',', '.').replace(/^\{|\}$/g, '');
1006
+ const val = values.find(x => x.sysName === cleanOperand);
1007
+ if (val) {
1008
+ if (val.value === null || val.value === undefined || val.value === '') {
1009
+ return null;
1010
+ }
1011
+ const parsed = Number(String(val.value).replace(',', '.'));
1012
+ return Number.isFinite(parsed) ? parsed : null;
1013
+ }
1014
+ const asNumber = Number(cleanOperand);
1015
+ if (Number.isFinite(asNumber)) {
1016
+ return asNumber;
1017
+ }
1018
+ throw new Error(`Invalid operand ${operand}`);
1019
+ }
1020
+ _calculate(operand1, operand2, operatorToken) {
1021
+ switch (operatorToken) {
1022
+ case '+':
1023
+ return operand1 + operand2;
1024
+ case '-':
1025
+ return operand1 - operand2;
1026
+ case '*':
1027
+ return operand1 * operand2;
1028
+ case '/':
1029
+ return operand1 / operand2;
1030
+ default:
1031
+ throw new Error(`Invalid operator ${operatorToken}`);
1032
+ }
1033
+ }
1034
+ _isOperator(token) {
1035
+ return this._operators.includes(token);
1036
+ }
1037
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormulaCalculateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1038
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormulaCalculateService }); }
1039
+ }
1040
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormulaCalculateService, decorators: [{
1041
+ type: Injectable
1042
+ }] });
1043
+
866
1044
  class ObjectFormEffects {
867
1045
  constructor(_actions$, _store, _router, _applicationService, _toasterService) {
868
1046
  this._actions$ = _actions$;
@@ -1041,48 +1219,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1041
1219
  }], ctorParameters: () => [{ type: i1$1.Router }] });
1042
1220
 
1043
1221
  class ComponentFactoryDirective {
1044
- constructor(viewContainer, cdr) {
1222
+ constructor(viewContainer) {
1045
1223
  this.viewContainer = viewContainer;
1046
- this.cdr = cdr;
1047
1224
  this.values = [];
1048
1225
  this.changed = new EventEmitter();
1049
1226
  }
1050
1227
  ngOnChanges(changes) {
1051
- // 1. Сначала инициализируем виджет, если сменился тип
1052
- if ('type' in changes && this.type) {
1228
+ if ('type' in changes && this.type && changes['type'].previousValue !== changes['type'].currentValue) {
1053
1229
  this.initWidget(this.type);
1054
1230
  }
1055
- // 2. Если компонент уже есть (или только что создался), обновляем все свойства
1056
- if (this.dynamicComponent) {
1057
- if ('data' in changes && this.data) {
1058
- this.dynamicComponent.instance.data = this.data;
1059
- }
1060
- if ('valueMode' in changes && this.valueMode) {
1061
- this.dynamicComponent.instance.valueMode = this.valueMode;
1062
- }
1063
- // Используем currentValue из changes, чтобы быть уверенными в свежести данных
1064
- if ('values' in changes) {
1065
- this.dynamicComponent.instance.values = changes['values'].currentValue;
1066
- }
1067
- // КРИТИЧНО: уведомляем Angular, что в динамическом компоненте обновились данные
1068
- this.dynamicComponent.changeDetectorRef.detectChanges();
1231
+ if ('data' in changes && this.data) {
1232
+ this.applyData(this.data);
1233
+ }
1234
+ if ('valueMode' in changes && this.data && this.valueMode) {
1235
+ this.applyValueMode(this.valueMode);
1236
+ }
1237
+ if ('values' in changes && this.data) {
1238
+ this.applyValues(this.values);
1069
1239
  }
1070
1240
  }
1071
1241
  initWidget(type) {
1072
1242
  const componentType = this.map[type];
1073
- if (!componentType)
1243
+ if (!componentType) {
1074
1244
  return;
1075
- this.viewContainer.clear();
1245
+ }
1246
+ if (this.dynamicComponent) {
1247
+ this.viewContainer.clear();
1248
+ this.dynamicComponent = undefined;
1249
+ }
1076
1250
  this.dynamicComponent = this.viewContainer.createComponent(componentType);
1077
- // Сразу передаем всё, что есть на момент создания
1078
- const instance = this.dynamicComponent.instance;
1079
- if (this.data)
1080
- instance.data = this.data;
1081
- if (this.values)
1082
- instance.values = this.values;
1083
- if (this.valueMode)
1084
- instance.valueMode = this.valueMode;
1085
- if (this.dynamicComponent && this.dynamicComponent.instance.changed) {
1251
+ if (this.data) {
1252
+ this.dynamicComponent.instance.data = this.data;
1253
+ }
1254
+ if (this.dynamicComponent.instance.changed) {
1086
1255
  this.dynamicComponent.instance.changed.subscribe((res) => {
1087
1256
  this.changed.emit(res);
1088
1257
  });
@@ -1103,7 +1272,7 @@ class ComponentFactoryDirective {
1103
1272
  this.dynamicComponent.instance.valueMode = valueMode;
1104
1273
  }
1105
1274
  }
1106
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ComponentFactoryDirective, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1275
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ComponentFactoryDirective, deps: [{ token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1107
1276
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ComponentFactoryDirective, isStandalone: true, selector: "[appComponentFactory]", inputs: { type: "type", data: "data", values: "values", valueMode: "valueMode", map: "map" }, outputs: { changed: "changed" }, usesOnChanges: true, ngImport: i0 }); }
1108
1277
  }
1109
1278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ComponentFactoryDirective, decorators: [{
@@ -1112,7 +1281,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1112
1281
  selector: '[appComponentFactory]',
1113
1282
  standalone: true
1114
1283
  }]
1115
- }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }], propDecorators: { type: [{
1284
+ }], ctorParameters: () => [{ type: i0.ViewContainerRef }], propDecorators: { type: [{
1116
1285
  type: Input
1117
1286
  }], data: [{
1118
1287
  type: Input
@@ -1172,6 +1341,12 @@ class BaseFieldComponent {
1172
1341
  set values(value) {
1173
1342
  if (this.valueMode === 'manual') {
1174
1343
  this.manualValues = value;
1344
+ const isFormulaInputNumber = this.settings?.type === ComponentType.InputNumber &&
1345
+ this.settings?.options?.inputState === InputState.Formula;
1346
+ if (isFormulaInputNumber && this.model) {
1347
+ this.model.value = getValueModel(this.settings, this.manualValues)?.value;
1348
+ this._detector.detectChanges();
1349
+ }
1175
1350
  }
1176
1351
  }
1177
1352
  set data(settings) {
@@ -1403,6 +1578,12 @@ class InputNumberComponent extends BaseFieldComponent {
1403
1578
  this.validateValue = 0;
1404
1579
  }
1405
1580
  get isReadonly() {
1581
+ return this.hasFormula;
1582
+ }
1583
+ get isInputDisabled() {
1584
+ return this.disabled || this.hasFormula;
1585
+ }
1586
+ get hasFormula() {
1406
1587
  return this.settings.options.inputState === InputState.Formula;
1407
1588
  }
1408
1589
  onChangeInput() {
@@ -1415,11 +1596,11 @@ class InputNumberComponent extends BaseFieldComponent {
1415
1596
  this.settings.options.maxValue && model.value && this.settings.options.maxValue < model.value);
1416
1597
  }
1417
1598
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputNumberComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1418
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: InputNumberComponent, selector: "app-input-number", usesInheritance: true, ngImport: i0, template: "@if (settings) {\r\n <div class=\"input-number-content\">\r\n @if (settings.options.label) {\r\n <mrx-label\r\n [required]=\"settings.options.required\"\r\n [tooltip]=\"settings.options.tooltip || ''\"\r\n >\r\n {{ settings.options.label }}\r\n </mrx-label>\r\n }\r\n\r\n <mrx-input-number\r\n [(ngModel)]=\"model.value\"\r\n [fields]=\"autosaveFields\"\r\n [readonly]=\"isReadonly\"\r\n [allowNegative]=\"true\"\r\n [numberType]=\"isReadonly || settings.options.decimals ? 'float' : 'int'\"\r\n [decimalSeparator]=\"settings.options.decimalSeparator\"\r\n [decimals]=\"isReadonly ? (settings.options.decimals || 2) : settings.options.decimals\"\r\n [placeholder]=\"settings.options.placeholder\"\r\n [disabled]=\"disabled\"\r\n [invalid]=\"getInvalid\"\r\n [invalidMessage]=\"getInvalidMessage\"\r\n (modelChange)=\"dispatchModify($event)\"\r\n (change)=\"onChangeInput()\"\r\n ></mrx-input-number>\r\n\r\n @if (settings.options.minValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.minValue }}\"\r\n [value]=\"validateValue\"\r\n [minValue]=\"settings.options.minValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n @if (settings.options.maxValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.maxValue }}\"\r\n [value]=\"validateValue\"\r\n [maxValue]=\"settings.options.maxValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n </div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.LabelComponent, selector: "mrx-label", inputs: ["requiredHidden", "required", "boldLabel", "disabled", "placeholder", "label", "customClasses", "triggerTextPosition", "isPublicInfo", "publicInfoTooltip", "isSwitch", "switchLabel", "switchValue", "switchSize", "isCheckbox", "checkboxLabel", "checkboxValue", "counter", "linkText", "linkPrevent", "linkType", "linkMonochrome", "href", "triggerType", "tooltip", "tooltipInitialVisible", "isSaveToStorage"], outputs: ["changeSwitchValue", "changeCheckboxValue", "clickedLink"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$2.InputNumberComponent, selector: "mrx-input-number", inputs: ["fields", "placeholder", "innerClass", "customClasses", "required", "allowNegative", "size", "separator", "decimalSeparator", "decimals", "isNullableValue", "isAutoCorrectingValue", "invalid", "checkInvalid", "numberType", "invalidMessage", "disabled", "readonly", "minValue", "maxValue"], outputs: ["modelChange", "modelChangeBlur"] }, { kind: "component", type: i1$2.HintErrorMessageComponent, selector: "mrx-hint-error-message", inputs: ["message", "value", "maxValue", "minValue", "minLength", "maxLength", "checkInvalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1599
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: InputNumberComponent, selector: "app-input-number", usesInheritance: true, ngImport: i0, template: "@if (settings) {\r\n <div class=\"input-number-content\">\r\n @if (settings.options.label) {\r\n <mrx-label\r\n [required]=\"settings.options.required\"\r\n [tooltip]=\"settings.options.tooltip || ''\"\r\n >\r\n {{ settings.options.label }}\r\n </mrx-label>\r\n }\r\n\r\n <mrx-input-number\r\n [(ngModel)]=\"model.value\"\r\n [fields]=\"autosaveFields\"\r\n [readonly]=\"isReadonly\"\r\n [allowNegative]=\"true\"\r\n [numberType]=\"isReadonly || settings.options.decimals ? 'float' : 'int'\"\r\n [decimalSeparator]=\"settings.options.decimalSeparator\"\r\n [decimals]=\"isReadonly ? (settings.options.decimals || 2) : settings.options.decimals\"\r\n [placeholder]=\"settings.options.placeholder\"\r\n [disabled]=\"isInputDisabled\"\r\n [invalid]=\"getInvalid\"\r\n [invalidMessage]=\"getInvalidMessage\"\r\n (modelChange)=\"dispatchModify($event)\"\r\n (change)=\"onChangeInput()\"\r\n ></mrx-input-number>\r\n\r\n @if (settings.options.minValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.minValue }}\"\r\n [value]=\"validateValue\"\r\n [minValue]=\"settings.options.minValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n @if (settings.options.maxValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.maxValue }}\"\r\n [value]=\"validateValue\"\r\n [maxValue]=\"settings.options.maxValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n </div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.LabelComponent, selector: "mrx-label", inputs: ["requiredHidden", "required", "boldLabel", "disabled", "placeholder", "label", "customClasses", "triggerTextPosition", "isPublicInfo", "publicInfoTooltip", "isSwitch", "switchLabel", "switchValue", "switchSize", "isCheckbox", "checkboxLabel", "checkboxValue", "counter", "linkText", "linkPrevent", "linkType", "linkMonochrome", "href", "triggerType", "tooltip", "tooltipInitialVisible", "isSaveToStorage"], outputs: ["changeSwitchValue", "changeCheckboxValue", "clickedLink"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$2.InputNumberComponent, selector: "mrx-input-number", inputs: ["fields", "placeholder", "innerClass", "customClasses", "required", "allowNegative", "size", "separator", "decimalSeparator", "decimals", "isNullableValue", "isAutoCorrectingValue", "invalid", "checkInvalid", "numberType", "invalidMessage", "disabled", "readonly", "minValue", "maxValue"], outputs: ["modelChange", "modelChangeBlur"] }, { kind: "component", type: i1$2.HintErrorMessageComponent, selector: "mrx-hint-error-message", inputs: ["message", "value", "maxValue", "minValue", "minLength", "maxLength", "checkInvalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1419
1600
  }
1420
1601
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputNumberComponent, decorators: [{
1421
1602
  type: Component,
1422
- args: [{ selector: 'app-input-number', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (settings) {\r\n <div class=\"input-number-content\">\r\n @if (settings.options.label) {\r\n <mrx-label\r\n [required]=\"settings.options.required\"\r\n [tooltip]=\"settings.options.tooltip || ''\"\r\n >\r\n {{ settings.options.label }}\r\n </mrx-label>\r\n }\r\n\r\n <mrx-input-number\r\n [(ngModel)]=\"model.value\"\r\n [fields]=\"autosaveFields\"\r\n [readonly]=\"isReadonly\"\r\n [allowNegative]=\"true\"\r\n [numberType]=\"isReadonly || settings.options.decimals ? 'float' : 'int'\"\r\n [decimalSeparator]=\"settings.options.decimalSeparator\"\r\n [decimals]=\"isReadonly ? (settings.options.decimals || 2) : settings.options.decimals\"\r\n [placeholder]=\"settings.options.placeholder\"\r\n [disabled]=\"disabled\"\r\n [invalid]=\"getInvalid\"\r\n [invalidMessage]=\"getInvalidMessage\"\r\n (modelChange)=\"dispatchModify($event)\"\r\n (change)=\"onChangeInput()\"\r\n ></mrx-input-number>\r\n\r\n @if (settings.options.minValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.minValue }}\"\r\n [value]=\"validateValue\"\r\n [minValue]=\"settings.options.minValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n @if (settings.options.maxValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.maxValue }}\"\r\n [value]=\"validateValue\"\r\n [maxValue]=\"settings.options.maxValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n </div>\r\n}\r\n" }]
1603
+ args: [{ selector: 'app-input-number', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (settings) {\r\n <div class=\"input-number-content\">\r\n @if (settings.options.label) {\r\n <mrx-label\r\n [required]=\"settings.options.required\"\r\n [tooltip]=\"settings.options.tooltip || ''\"\r\n >\r\n {{ settings.options.label }}\r\n </mrx-label>\r\n }\r\n\r\n <mrx-input-number\r\n [(ngModel)]=\"model.value\"\r\n [fields]=\"autosaveFields\"\r\n [readonly]=\"isReadonly\"\r\n [allowNegative]=\"true\"\r\n [numberType]=\"isReadonly || settings.options.decimals ? 'float' : 'int'\"\r\n [decimalSeparator]=\"settings.options.decimalSeparator\"\r\n [decimals]=\"isReadonly ? (settings.options.decimals || 2) : settings.options.decimals\"\r\n [placeholder]=\"settings.options.placeholder\"\r\n [disabled]=\"isInputDisabled\"\r\n [invalid]=\"getInvalid\"\r\n [invalidMessage]=\"getInvalidMessage\"\r\n (modelChange)=\"dispatchModify($event)\"\r\n (change)=\"onChangeInput()\"\r\n ></mrx-input-number>\r\n\r\n @if (settings.options.minValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.minValue }}\"\r\n [value]=\"validateValue\"\r\n [minValue]=\"settings.options.minValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n @if (settings.options.maxValue) {\r\n <mrx-hint-error-message\r\n message=\"\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 {{ settings.options.maxValue }}\"\r\n [value]=\"validateValue\"\r\n [maxValue]=\"settings.options.maxValue\"\r\n ></mrx-hint-error-message>\r\n }\r\n </div>\r\n}\r\n" }]
1423
1604
  }] });
1424
1605
 
1425
1606
  class InputSelectComponent extends BaseFieldComponent {
@@ -1994,11 +2175,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1994
2175
  }] } });
1995
2176
 
1996
2177
  class InputTableModalComponent extends ModalServiceComponent {
1997
- constructor(dialogRef, store, cdr, data) {
2178
+ constructor(dialogRef, store, formulaCalculateService, data) {
1998
2179
  super();
1999
2180
  this.dialogRef = dialogRef;
2000
2181
  this.store = store;
2001
- this.cdr = cdr;
2182
+ this.formulaCalculateService = formulaCalculateService;
2002
2183
  this.emptyRow = true;
2003
2184
  this.title = data.title;
2004
2185
  this.okText = data.okText;
@@ -2016,6 +2197,7 @@ class InputTableModalComponent extends ModalServiceComponent {
2016
2197
  else {
2017
2198
  this._transformValues(valueModel);
2018
2199
  }
2200
+ this._recalculateFormulaFields();
2019
2201
  this.emptyRow = this.rowModel.data.every(x => this.isEmpty(x.value, x.valueType));
2020
2202
  }
2021
2203
  ok() {
@@ -2036,14 +2218,52 @@ class InputTableModalComponent extends ModalServiceComponent {
2036
2218
  this.dialogRef.close(this.result);
2037
2219
  }
2038
2220
  _transformValues(value) {
2039
- console.log('Update field:', value.sysName, 'with value:', value.value);
2040
2221
  const cloneRowModel = structuredClone(this.rowModel);
2041
- const findValue = cloneRowModel.data.find(c => c.sysName === value.sysName);
2222
+ const findValue = cloneRowModel.data.find((c) => c.sysName === value.sysName);
2042
2223
  if (findValue) {
2043
2224
  findValue.value = value.value;
2044
2225
  }
2045
2226
  this.rowModel = cloneRowModel;
2046
- this.cdr.detectChanges(); // Явно уведомляем об изменениях
2227
+ }
2228
+ _recalculateFormulaFields() {
2229
+ const formulaComponents = this._getFormulaNumberComponents(this.settings.components);
2230
+ if (!formulaComponents.length) {
2231
+ return;
2232
+ }
2233
+ const cloneRowModel = structuredClone(this.rowModel);
2234
+ for (let i = 0; i < formulaComponents.length; i++) {
2235
+ let hasUpdates = false;
2236
+ for (const component of formulaComponents) {
2237
+ const formula = component.options?.formula?.trim();
2238
+ if (!formula) {
2239
+ continue;
2240
+ }
2241
+ const result = this.formulaCalculateService.calculateMathExpression(formula, cloneRowModel.data);
2242
+ const targetValue = cloneRowModel.data.find(x => x.sysName === component.sysName);
2243
+ if (!targetValue || targetValue.value === result) {
2244
+ continue;
2245
+ }
2246
+ targetValue.value = result;
2247
+ hasUpdates = true;
2248
+ }
2249
+ if (!hasUpdates) {
2250
+ break;
2251
+ }
2252
+ }
2253
+ this.rowModel = cloneRowModel;
2254
+ }
2255
+ _getFormulaNumberComponents(components) {
2256
+ const result = [];
2257
+ for (const component of components) {
2258
+ if (component.type === ComponentType.InputNumber &&
2259
+ component.options?.inputState === InputState.Formula) {
2260
+ result.push(component);
2261
+ }
2262
+ if (component.components?.length) {
2263
+ result.push(...this._getFormulaNumberComponents(component.components));
2264
+ }
2265
+ }
2266
+ return result;
2047
2267
  }
2048
2268
  _isValid() {
2049
2269
  var hasError = false;
@@ -2076,9 +2296,10 @@ class InputTableModalComponent extends ModalServiceComponent {
2076
2296
  return value.length === 0;
2077
2297
  return false;
2078
2298
  }
2079
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputTableModalComponent, deps: [{ token: i1$2.ModalRef }, { token: i2.Store }, { token: i0.ChangeDetectorRef }, { token: MODAL_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
2299
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputTableModalComponent, deps: [{ token: i1$2.ModalRef }, { token: i2.Store }, { token: FormulaCalculateService }, { token: MODAL_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
2080
2300
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: InputTableModalComponent, isStandalone: true, selector: "app-input-table-modal", providers: [
2081
2301
  provideNgxMask(),
2302
+ FormulaCalculateService,
2082
2303
  ReferenceService // для компонента список
2083
2304
  ], usesInheritance: true, ngImport: i0, template: "<mrx-modal\r\n [title]=\"title\"\r\n size=\"large\"\r\n (close)=\"close()\"\r\n (ok)=\"ok()\"\r\n>\r\n <app-form-dispenser-modal\r\n [sectionSettings]=\"settings\"\r\n [values]=\"rowModel.data\"\r\n [valueMode]=\"'manual'\"\r\n (changed)=\"componentValueChanged($event)\"\r\n ></app-form-dispenser-modal>\r\n\r\n <ng-template #footerContent>\r\n <mrx-button\r\n [customClasses]=\"'mr-3'\"\r\n [type]=\"'secondary'\"\r\n [size]=\"'medium'\"\r\n (click)=\"close()\"\r\n >\u041E\u0442\u043C\u0435\u043D\u0430\r\n </mrx-button>\r\n <mrx-button\r\n [type]=\"'primary'\"\r\n [size]=\"'medium'\"\r\n [disabled]=\"emptyRow\"\r\n (click)=\"ok()\"\r\n >{{ okText }}\r\n </mrx-button>\r\n </ng-template>\r\n</mrx-modal>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ModalModule }, { kind: "component", type: i1$2.ModalComponent, selector: "mrx-modal", inputs: ["title", "message", "alert", "okText", "closeText", "size", "color", "customClasses", "expandable", "isEmbed", "isClose", "isBack", "backText", "enableFooter", "alignButtons", "isLoading", "iconPosition"], outputs: ["ok", "close", "back", "expand"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.ButtonComponent, selector: "mrx-button", inputs: ["size", "type", "color", "iconPosition", "active", "disabled", "isLoading", "iconOnly", "customClasses", "label", "icon", "iconClass", "buttonType", "href", "target", "routerLink", "queryParams"], outputs: ["mrxClick"] }, { kind: "component", type: FormDispenserModal, selector: "app-form-dispenser-modal", inputs: ["sectionSettings", "values", "valueMode"], outputs: ["changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2084
2305
  }
@@ -2090,9 +2311,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2090
2311
  FormDispenserModal,
2091
2312
  ], providers: [
2092
2313
  provideNgxMask(),
2314
+ FormulaCalculateService,
2093
2315
  ReferenceService // для компонента список
2094
2316
  ], template: "<mrx-modal\r\n [title]=\"title\"\r\n size=\"large\"\r\n (close)=\"close()\"\r\n (ok)=\"ok()\"\r\n>\r\n <app-form-dispenser-modal\r\n [sectionSettings]=\"settings\"\r\n [values]=\"rowModel.data\"\r\n [valueMode]=\"'manual'\"\r\n (changed)=\"componentValueChanged($event)\"\r\n ></app-form-dispenser-modal>\r\n\r\n <ng-template #footerContent>\r\n <mrx-button\r\n [customClasses]=\"'mr-3'\"\r\n [type]=\"'secondary'\"\r\n [size]=\"'medium'\"\r\n (click)=\"close()\"\r\n >\u041E\u0442\u043C\u0435\u043D\u0430\r\n </mrx-button>\r\n <mrx-button\r\n [type]=\"'primary'\"\r\n [size]=\"'medium'\"\r\n [disabled]=\"emptyRow\"\r\n (click)=\"ok()\"\r\n >{{ okText }}\r\n </mrx-button>\r\n </ng-template>\r\n</mrx-modal>\r\n" }]
2095
- }], ctorParameters: () => [{ type: i1$2.ModalRef }, { type: i2.Store }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
2317
+ }], ctorParameters: () => [{ type: i1$2.ModalRef }, { type: i2.Store }, { type: FormulaCalculateService }, { type: undefined, decorators: [{
2096
2318
  type: Inject,
2097
2319
  args: [MODAL_DATA]
2098
2320
  }] }] });
@@ -2193,7 +2415,7 @@ class InputTableComponent {
2193
2415
  });
2194
2416
  }
2195
2417
  deleteRow(event) {
2196
- const findRow = structuredClone(this.model.data.find(item => item.id === event.row.data.id));
2418
+ const findRow = this.model.data.find(item => item.id === event.row.data.id);
2197
2419
  if (findRow) {
2198
2420
  this.model.data = this.model.data.filter(item => item.id !== event.row.data.id);
2199
2421
  this._initDataSource(this.model.data);
@@ -2241,8 +2463,6 @@ class InputTableComponent {
2241
2463
  switch (component.type) {
2242
2464
  case ComponentType.InputDate:
2243
2465
  return 'date';
2244
- case ComponentType.InputSwitch:
2245
- return 'boolean';
2246
2466
  default:
2247
2467
  return 'string';
2248
2468
  }
@@ -2299,7 +2519,6 @@ class InputTableComponent {
2299
2519
  this._detector.detectChanges();
2300
2520
  }
2301
2521
  _transformColumn(component) {
2302
- const dataType = this.getDataType(component);
2303
2522
  const column = {
2304
2523
  caption: component.options.label || '',
2305
2524
  columns: component.components.map(c => this._transformColumn(structuredClone(c))),
@@ -2308,21 +2527,6 @@ class InputTableComponent {
2308
2527
  format: this.getFormat(component),
2309
2528
  minWidth: 160
2310
2529
  };
2311
- if (dataType === 'boolean') {
2312
- column.calculateCellValue = (rowData) => {
2313
- const value = rowData[component.sysName];
2314
- // Если true — Да, если false, "" или null — Нет
2315
- return !!value;
2316
- };
2317
- column.lookup = {
2318
- dataSource: [
2319
- { value: true, displayValue: 'Да' },
2320
- { value: false, displayValue: 'Нет' }
2321
- ],
2322
- valueExpr: 'value',
2323
- displayExpr: 'displayValue'
2324
- };
2325
- }
2326
2530
  if (this.settings.options.groups.length) {
2327
2531
  const findIdx = this.settings.options.groups.findIndex(x => x === component.sysName);
2328
2532
  if (findIdx !== -1) {