@bgroup/wise-form 1.0.6 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,208 +1,161 @@
|
|
|
1
1
|
import type { FormulaManager } from '..';
|
|
2
2
|
import { conditionsTypes } from '../helpers/condition-types';
|
|
3
3
|
import { EvaluationsManager } from '../helpers/evaluations';
|
|
4
|
-
import {
|
|
5
|
-
EvaluatedFormula,
|
|
6
|
-
FormulaObserver,
|
|
7
|
-
IComplexCondition,
|
|
8
|
-
IConditionalField,
|
|
9
|
-
} from '../types/formulas';
|
|
4
|
+
import { EvaluatedFormula, FormulaObserver, IComplexCondition, IConditionalField } from '../types/formulas';
|
|
10
5
|
import { parse } from 'mathjs';
|
|
11
6
|
|
|
12
7
|
export class FormulaConditional {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
8
|
+
#plugin: any;
|
|
9
|
+
#specs: FormulaObserver;
|
|
10
|
+
#emptyValue: undefined;
|
|
11
|
+
get formula() {
|
|
12
|
+
return this.#specs.formula;
|
|
13
|
+
}
|
|
14
|
+
get base() {
|
|
15
|
+
const formula = <IComplexCondition>this.#specs.formula;
|
|
16
|
+
return formula.base;
|
|
17
|
+
}
|
|
18
|
+
#value: string | number | undefined | 0;
|
|
19
|
+
get value() {
|
|
20
|
+
return this.#value;
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return this.#specs.name;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Represents the fields defined in the plugin settings
|
|
27
|
+
*/
|
|
28
|
+
get fields() {
|
|
29
|
+
const formula = <IComplexCondition>this.formula;
|
|
30
|
+
return typeof formula?.fields === 'string' ? [formula?.fields] : formula?.fields;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get conditions() {
|
|
34
|
+
if (typeof this.#specs.formula === 'string') return;
|
|
35
|
+
const formula = this.#specs.formula as IComplexCondition;
|
|
36
|
+
return formula.conditions;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* FormField type
|
|
41
|
+
*/
|
|
42
|
+
#fields: any;
|
|
43
|
+
|
|
44
|
+
#parent: FormulaManager;
|
|
45
|
+
#ceil: boolean;
|
|
46
|
+
#round: boolean;
|
|
47
|
+
#isNotListenToChanges = false;
|
|
48
|
+
constructor(parent, plugin, specs) {
|
|
49
|
+
this.#parent = parent;
|
|
50
|
+
this.#plugin = plugin;
|
|
51
|
+
this.#specs = specs;
|
|
52
|
+
this.#round = specs.round;
|
|
53
|
+
this.#ceil = specs.ceil;
|
|
54
|
+
this.#emptyValue = specs.emptyValue;
|
|
55
|
+
|
|
56
|
+
if (specs.isNotListenToChanges) this.#isNotListenToChanges = specs.isNotListenToChanges;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
initialize() {
|
|
60
|
+
try {
|
|
61
|
+
const { form } = this.#plugin;
|
|
62
|
+
|
|
63
|
+
if (!this.fields) {
|
|
64
|
+
throw new Error(`Fields not found in formula ${this.name}`);
|
|
65
|
+
}
|
|
66
|
+
const fields = this.fields.map(name => {
|
|
67
|
+
const formula = this.#plugin.formulas.get(name);
|
|
68
|
+
if (formula) return formula;
|
|
69
|
+
const field = form.getField(name);
|
|
70
|
+
return field;
|
|
71
|
+
});
|
|
72
|
+
this.#fields = fields;
|
|
73
|
+
if (this.name === 'costoTotalGrafico') {
|
|
74
|
+
console.log('fields', fields);
|
|
75
|
+
}
|
|
76
|
+
if (!this.#isNotListenToChanges)
|
|
77
|
+
fields.forEach(field => {
|
|
78
|
+
if (!field) {
|
|
79
|
+
throw new Error(`Field ${this.name} not found in form ${form.name}`);
|
|
80
|
+
}
|
|
81
|
+
field.on('change', this.calculate.bind(this));
|
|
82
|
+
});
|
|
83
|
+
} catch (e) {}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
evaluate() {
|
|
87
|
+
const formula = <IComplexCondition>this.#specs.formula;
|
|
88
|
+
let evaluatedFormula: any = { formula: formula.base }; // Use the base formula by default
|
|
89
|
+
if (formula.conditions) {
|
|
90
|
+
const conditionsArray = Array.isArray(formula.conditions) ? formula.conditions : [formula.conditions];
|
|
91
|
+
for (const condition of conditionsArray) {
|
|
92
|
+
let conditionMet = false;
|
|
93
|
+
if (condition.conditions) {
|
|
94
|
+
// If there are nested conditions, all must be met
|
|
95
|
+
conditionMet = condition.conditions.every(subCondition => {
|
|
96
|
+
const fieldValues = subCondition.fields.map(fieldName => {
|
|
97
|
+
const field = this.#fields.find(f => f.name === fieldName);
|
|
98
|
+
return field ? field.value : this.#emptyValue;
|
|
99
|
+
});
|
|
100
|
+
return EvaluationsManager.validateAll(subCondition.condition, fieldValues, subCondition.value);
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
const fieldValues = condition.fields.map(fieldName => {
|
|
104
|
+
const field = this.#fields.find(f => f.name === fieldName);
|
|
105
|
+
return field ? field.value : this.#emptyValue;
|
|
106
|
+
});
|
|
107
|
+
const conditionType = !!condition.type && conditionsTypes[condition.type] ? conditionsTypes[condition.type] : conditionsTypes.some;
|
|
108
|
+
// Check if any of the specified fields meet the condition
|
|
109
|
+
conditionMet = EvaluationsManager[conditionType](condition.condition, fieldValues, condition.value);
|
|
110
|
+
}
|
|
39
111
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
112
|
+
if (conditionMet) {
|
|
113
|
+
evaluatedFormula.formula = condition.formula;
|
|
114
|
+
evaluatedFormula.fi = condition;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
44
118
|
}
|
|
119
|
+
return evaluatedFormula;
|
|
120
|
+
}
|
|
45
121
|
|
|
122
|
+
async calculate() {
|
|
46
123
|
/**
|
|
47
|
-
*
|
|
124
|
+
* the formula is taken from the evaluate method since the conditions are evaluated there and
|
|
125
|
+
* can change the formula to be applied
|
|
48
126
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.#plugin = plugin;
|
|
58
|
-
this.#specs = specs;
|
|
59
|
-
this.#round = specs.round;
|
|
60
|
-
this.#ceil = specs.ceil;
|
|
61
|
-
this.#emptyValue = specs.emptyValue;
|
|
62
|
-
|
|
63
|
-
if (specs.isNotListenToChanges)
|
|
64
|
-
this.#isNotListenToChanges = specs.isNotListenToChanges;
|
|
127
|
+
const model = this.#plugin.form.getField(this.name);
|
|
128
|
+
const formula = this.evaluate();
|
|
129
|
+
|
|
130
|
+
if (!formula.formula) {
|
|
131
|
+
this.#value = this.#emptyValue !== undefined ? this.#emptyValue : '';
|
|
132
|
+
model && model.set({ value: this.#value });
|
|
133
|
+
this.#parent.trigger('change');
|
|
134
|
+
return this.#value;
|
|
65
135
|
}
|
|
136
|
+
// todo: Review if this section can be replaced by formulaManager.variables property.
|
|
137
|
+
const { tokens } = this.#parent.getParser(formula);
|
|
138
|
+
const variables = tokens.filter(token => token.type === 'variable').map(item => item.value);
|
|
66
139
|
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const { form } = this.#plugin;
|
|
70
|
-
|
|
71
|
-
if (!this.fields) {
|
|
72
|
-
throw new Error(`Fields not found in formula ${this.name}`);
|
|
73
|
-
}
|
|
74
|
-
const fields = this.fields.map((name) => {
|
|
75
|
-
const formula = this.#plugin.formulas.get(name);
|
|
76
|
-
if (formula) return formula;
|
|
77
|
-
const field = form.getField(name);
|
|
78
|
-
return field;
|
|
79
|
-
});
|
|
80
|
-
this.#fields = fields;
|
|
81
|
-
if (this.name === 'costoTotalGrafico') {
|
|
82
|
-
console.log('fields', fields);
|
|
83
|
-
}
|
|
84
|
-
if (!this.#isNotListenToChanges)
|
|
85
|
-
fields.forEach((field) => {
|
|
86
|
-
if (!field) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`Field ${this.name} not found in form ${form.name}`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
field.on('change', this.calculate.bind(this));
|
|
92
|
-
});
|
|
93
|
-
} catch (e) {}
|
|
94
|
-
}
|
|
140
|
+
const params = await this.#parent.getParams(variables);
|
|
95
141
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (formula.conditions) {
|
|
100
|
-
const conditionsArray = Array.isArray(formula.conditions) ? formula.conditions : [formula.conditions];
|
|
101
|
-
for (const condition of conditionsArray) {
|
|
102
|
-
let conditionMet = false;
|
|
103
|
-
if (condition.conditions) {
|
|
104
|
-
// If there are nested conditions, all must be met
|
|
105
|
-
conditionMet = condition.conditions.every(
|
|
106
|
-
(subCondition) => {
|
|
107
|
-
const fieldValues = subCondition.fields.map(
|
|
108
|
-
(fieldName) => {
|
|
109
|
-
const field = this.#fields.find(
|
|
110
|
-
(f) => f.name === fieldName
|
|
111
|
-
);
|
|
112
|
-
return field
|
|
113
|
-
? field.value
|
|
114
|
-
: this.#emptyValue;
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
return EvaluationsManager.validateAll(
|
|
118
|
-
subCondition.condition,
|
|
119
|
-
fieldValues,
|
|
120
|
-
subCondition.value
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
} else {
|
|
125
|
-
const fieldValues = condition.fields.map((fieldName) => {
|
|
126
|
-
const field = this.#fields.find(
|
|
127
|
-
(f) => f.name === fieldName
|
|
128
|
-
);
|
|
129
|
-
return field ? field.value : this.#emptyValue;
|
|
130
|
-
});
|
|
131
|
-
const conditionType =
|
|
132
|
-
!!condition.type && conditionsTypes[condition.type]
|
|
133
|
-
? conditionsTypes[condition.type]
|
|
134
|
-
: conditionsTypes.some;
|
|
135
|
-
// Check if any of the specified fields meet the condition
|
|
136
|
-
conditionMet = EvaluationsManager[conditionType](
|
|
137
|
-
condition.condition,
|
|
138
|
-
fieldValues,
|
|
139
|
-
condition.value
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (conditionMet) {
|
|
144
|
-
evaluatedFormula.formula = condition.formula;
|
|
145
|
-
evaluatedFormula.fi = condition;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return evaluatedFormula;
|
|
151
|
-
}
|
|
142
|
+
try {
|
|
143
|
+
const keys = Object.keys(params);
|
|
144
|
+
let result = keys.length === 1 ? params[keys[0]] : parse(formula.formula as string).evaluate(params);
|
|
152
145
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return this.#value;
|
|
167
|
-
}
|
|
168
|
-
// todo: Review if this section can be replaced by formulaManager.variables property.
|
|
169
|
-
const { tokens } = this.#parent.getParser(formula);
|
|
170
|
-
const variables = tokens
|
|
171
|
-
.filter((token) => token.type === 'variable')
|
|
172
|
-
.map((item) => item.value);
|
|
173
|
-
|
|
174
|
-
const params = await this.#parent.getParams(variables);
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
const keys = Object.keys(params);
|
|
178
|
-
let result =
|
|
179
|
-
keys.length === 1
|
|
180
|
-
? params[keys[0]]
|
|
181
|
-
: parse(formula.formula as string).evaluate(params);
|
|
182
|
-
|
|
183
|
-
const isInvalidResult = [
|
|
184
|
-
-Infinity,
|
|
185
|
-
Infinity,
|
|
186
|
-
undefined,
|
|
187
|
-
null,
|
|
188
|
-
NaN,
|
|
189
|
-
].includes(result);
|
|
190
|
-
if (this.#round && !isInvalidResult) result = Math.round(result);
|
|
191
|
-
if (this.#ceil && !isInvalidResult) result = Math.ceil(result);
|
|
192
|
-
this.#value =
|
|
193
|
-
isInvalidResult || typeof result === 'object'
|
|
194
|
-
? this.#emptyValue
|
|
195
|
-
: Number(result.toFixed(2));
|
|
196
|
-
|
|
197
|
-
this.#parent.trigger('change');
|
|
198
|
-
|
|
199
|
-
model && model.set({ value: this.#value });
|
|
200
|
-
return this.#value;
|
|
201
|
-
} catch (e) {
|
|
202
|
-
console.log('formula', this.name, formula.formula, params);
|
|
203
|
-
console.error(e);
|
|
204
|
-
throw new Error('Error calculating the formula');
|
|
205
|
-
}
|
|
146
|
+
const isInvalidResult = [-Infinity, Infinity, undefined, null, NaN].includes(result);
|
|
147
|
+
if (this.#round && !isInvalidResult) result = Math.round(result);
|
|
148
|
+
if (this.#ceil && !isInvalidResult) result = Math.ceil(result);
|
|
149
|
+
this.#value = isInvalidResult || typeof result === 'object' ? this.#emptyValue : Number(Number(result).toFixed(2));
|
|
150
|
+
|
|
151
|
+
this.#parent.trigger('change');
|
|
152
|
+
|
|
153
|
+
model && model.set({ value: this.#value });
|
|
154
|
+
return this.#value;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.log('formula', this.name, formula.formula, params);
|
|
157
|
+
console.error(e);
|
|
158
|
+
throw new Error('Error calculating the formula');
|
|
206
159
|
}
|
|
160
|
+
}
|
|
207
161
|
}
|
|
208
|
-
|
|
@@ -33,7 +33,7 @@ export class CallbackManager {
|
|
|
33
33
|
|
|
34
34
|
initialize() {
|
|
35
35
|
const instance = this.#field;
|
|
36
|
-
const checkField = async settings => {
|
|
36
|
+
const checkField = async (settings, index) => {
|
|
37
37
|
const dependency = this.#model.getField(this.#model.getFieldName(settings.field));
|
|
38
38
|
await dependency.isReady;
|
|
39
39
|
const required = ['field', 'callback'];
|
|
@@ -47,6 +47,12 @@ export class CallbackManager {
|
|
|
47
47
|
throw new Error(`${settings.callback} is not a registered callback ${settings.name}`);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// Asignar un ID único a cada configuración de callback para tracking individual
|
|
51
|
+
if (!settings.__callbackId) {
|
|
52
|
+
const instanceName = (instance as any).name || 'unknown';
|
|
53
|
+
settings.__callbackId = `${instanceName}_${settings.callback}_${settings.field}_${index}_${Date.now()}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
// saved in listener array to be able to remove the listener if is required.
|
|
51
57
|
const event = settings.event || 'value.change';
|
|
52
58
|
const caller = () => this.executeCallback(settings);
|
|
@@ -61,12 +67,18 @@ export class CallbackManager {
|
|
|
61
67
|
|
|
62
68
|
/**
|
|
63
69
|
* Generates a unique execution key for tracking callback recursion.
|
|
64
|
-
*
|
|
70
|
+
* Uses the unique __callbackId from settings if available, otherwise falls back to name-based key.
|
|
65
71
|
*/
|
|
66
|
-
#getExecutionKey(
|
|
72
|
+
#getExecutionKey(settings: any, fieldName: string): string {
|
|
73
|
+
// Si el settings tiene un __callbackId único, usarlo para permitir múltiples callbacks del mismo tipo
|
|
74
|
+
if (settings?.__callbackId) {
|
|
75
|
+
return settings.__callbackId;
|
|
76
|
+
}
|
|
77
|
+
// Fallback para compatibilidad con código legacy
|
|
78
|
+
const callbackName = settings?.callback || 'unknown';
|
|
67
79
|
const fieldNameStr = fieldName || 'unknown';
|
|
68
|
-
const
|
|
69
|
-
return `${callbackName}:${fieldNameStr}:${
|
|
80
|
+
const dependencyFieldName = typeof settings?.field === 'string' ? settings.field : settings?.field?.field || 'unknown';
|
|
81
|
+
return `${callbackName}:${fieldNameStr}:${dependencyFieldName}`;
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
executeCallback = async settings => {
|
|
@@ -84,7 +96,7 @@ export class CallbackManager {
|
|
|
84
96
|
const fieldName = (this.#field as { name?: string })?.name || 'unknown';
|
|
85
97
|
const dependencyFieldName = typeof settings.field === 'string' ? settings.field : settings.field?.field || 'unknown';
|
|
86
98
|
|
|
87
|
-
const executionKey = this.#getExecutionKey(
|
|
99
|
+
const executionKey = this.#getExecutionKey(settings, fieldName);
|
|
88
100
|
|
|
89
101
|
// Check if already executing (prevent concurrent executions)
|
|
90
102
|
if (currentlyExecutingCallbacks.has(executionKey)) {
|
|
@@ -105,6 +117,7 @@ export class CallbackManager {
|
|
|
105
117
|
`\n Dependency: ${dependencyFieldName}`,
|
|
106
118
|
`\n Current depth: ${currentDepth + 1}`,
|
|
107
119
|
`\n Execution key: ${executionKey}`,
|
|
120
|
+
`\n Callback ID: ${settings?.__callbackId || 'N/A'}`,
|
|
108
121
|
`\n This indicates a circular dependency in callbacks. Please review the form configuration.`
|
|
109
122
|
);
|
|
110
123
|
return;
|
package/src/models/field.ts
CHANGED
|
@@ -33,20 +33,78 @@ export class FormField extends ReactiveModel<IFormField> {
|
|
|
33
33
|
get disabled() {
|
|
34
34
|
if (typeof this.#disabled !== 'object' || !this.#disabled?.fields) return this.#disabled;
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const { action, operator = 'or' } = this.#disabled;
|
|
37
|
+
const isEnableAction = action === 'enable';
|
|
38
|
+
|
|
39
|
+
const validate = fieldSettings => {
|
|
40
|
+
// 1. Get the target field instance
|
|
41
|
+
const name = typeof fieldSettings === 'string' ? fieldSettings : fieldSettings.name;
|
|
42
|
+
const fieldInstance = this.#parent.form.getField(name);
|
|
43
|
+
|
|
44
|
+
// If field dependency is missing, we can't evaluate.
|
|
40
45
|
if (!fieldInstance) return false;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
|
|
47
|
+
// 2. Determine the value to check from that field
|
|
48
|
+
let currentValue;
|
|
49
|
+
if (typeof fieldSettings === 'object' && fieldSettings.property) {
|
|
50
|
+
// Access specific property (e.g. 'entries', 'length', 'value')
|
|
51
|
+
const props = fieldInstance.getProperties();
|
|
52
|
+
const propPath = fieldSettings.property.split('.');
|
|
53
|
+
|
|
54
|
+
let val: any = props;
|
|
55
|
+
// If property is 'value', start from fieldInstance.value
|
|
56
|
+
if (propPath[0] === 'value') {
|
|
57
|
+
val = fieldInstance.value;
|
|
58
|
+
propPath.shift(); // consume 'value'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Traverse path safely
|
|
62
|
+
for (const p of propPath) {
|
|
63
|
+
if (val === undefined || val === null) break;
|
|
64
|
+
val = val[p];
|
|
65
|
+
}
|
|
66
|
+
currentValue = val;
|
|
67
|
+
} else {
|
|
68
|
+
// Default: use the field's main value
|
|
69
|
+
currentValue = fieldInstance.value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 3. Simple boolean check if settings is just a string
|
|
73
|
+
if (typeof fieldSettings !== 'object') {
|
|
74
|
+
return !currentValue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 4. Determine comparison value
|
|
78
|
+
let comparisonValue = fieldSettings.value;
|
|
79
|
+
if (fieldSettings.valueFromField) {
|
|
80
|
+
const compareField = this.#parent.form.getField(fieldSettings.valueFromField);
|
|
81
|
+
comparisonValue = compareField ? compareField.value : undefined;
|
|
44
82
|
}
|
|
45
|
-
|
|
46
|
-
|
|
83
|
+
|
|
84
|
+
// 5. Evaluate condition
|
|
85
|
+
const condition = fieldSettings.condition;
|
|
86
|
+
if (condition && this.evaluations[condition]) {
|
|
87
|
+
return this.evaluations[condition](currentValue, comparisonValue);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Default behavior
|
|
91
|
+
return currentValue !== comparisonValue;
|
|
47
92
|
};
|
|
48
93
|
|
|
49
|
-
|
|
94
|
+
const results = this.#disabled.fields.map(validate);
|
|
95
|
+
|
|
96
|
+
let conditionsMet = false;
|
|
97
|
+
if (operator === 'and') {
|
|
98
|
+
conditionsMet = results.every(r => r === true);
|
|
99
|
+
} else {
|
|
100
|
+
conditionsMet = results.some(r => r === true);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (isEnableAction) {
|
|
104
|
+
return !conditionsMet;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return conditionsMet;
|
|
50
108
|
}
|
|
51
109
|
|
|
52
110
|
set disabled(value) {
|
|
@@ -184,34 +242,34 @@ export class FormField extends ReactiveModel<IFormField> {
|
|
|
184
242
|
if (typeof props.disabled !== 'object') {
|
|
185
243
|
throw new Error(`The disabled property of the field ${props.name} must be a boolean or an object`);
|
|
186
244
|
}
|
|
187
|
-
if (!props.disabled.fields && !props.disabled.mode) {
|
|
188
|
-
throw new Error(`The disabled property of the field ${props.name} must have a fields property or a mode defined`);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
245
|
if (props.disabled.mode) {
|
|
192
246
|
// posible modes : create, update;
|
|
193
247
|
this.#disabled = this.#parent.form.mode === props.disabled.mode;
|
|
194
248
|
return;
|
|
195
249
|
}
|
|
196
250
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.#listeningItems.set(name, {
|
|
207
|
-
item: instance,
|
|
208
|
-
listener: this.#listenSiblings,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
251
|
+
if (props.disabled.fields && Array.isArray(props.disabled.fields)) {
|
|
252
|
+
props.disabled.fields.forEach(item => {
|
|
253
|
+
const namesToListen: string[] = [];
|
|
254
|
+
if (typeof item === 'string') {
|
|
255
|
+
namesToListen.push(item);
|
|
256
|
+
} else {
|
|
257
|
+
if (item.name) namesToListen.push(item.name);
|
|
258
|
+
if (item.valueFromField) namesToListen.push(item.valueFromField);
|
|
259
|
+
}
|
|
211
260
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
261
|
+
namesToListen.forEach(name => {
|
|
262
|
+
const instance = this.#parent.form.getField(name);
|
|
263
|
+
if (!instance) return;
|
|
264
|
+
|
|
265
|
+
instance.on('change', this.#listenSiblings);
|
|
266
|
+
instance.on('value.change', this.#listenSiblings);
|
|
267
|
+
this.#listeningItems.set(name, {
|
|
268
|
+
item: instance,
|
|
269
|
+
listener: this.#listenSiblings,
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|
|
215
273
|
}
|
|
216
274
|
this.#disabled = props.disabled;
|
|
217
275
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
export type TDisabledSettings = {
|
|
2
2
|
name: string;
|
|
3
3
|
value: any;
|
|
4
|
+
condition?: string;
|
|
5
|
+
property?: string;
|
|
6
|
+
operator?: 'and' | 'or';
|
|
7
|
+
valueFromField?: string;
|
|
4
8
|
};
|
|
9
|
+
|
|
5
10
|
export interface IDisabled {
|
|
6
11
|
fields: string[] | TDisabledSettings[];
|
|
12
|
+
action?: 'enable' | 'disable';
|
|
13
|
+
operator?: 'and' | 'or'; // Operator for the top-level list of fields/conditions
|
|
7
14
|
}
|
|
8
15
|
|