@bgroup/wise-form 1.0.3 → 1.0.5
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/README.md +1 -1
- package/package.json +23 -4
- package/postcss.config.js +6 -0
- package/src/form/styles.css +11 -37
- package/src/form/view/components/containers/index.tsx +33 -4
- package/src/form/view/components/error.tsx +0 -3
- package/src/form/view/components/field/container.tsx +1 -1
- package/src/form/view/components/field/index.tsx +42 -7
- package/src/form/view/components/rows/row-container.tsx +37 -9
- package/src/form/view/components/rows/wrapper.tsx +17 -6
- package/src/form/view/components/wrapped-form.tsx +38 -5
- package/src/form/view/hooks/use-model.ts +91 -25
- package/src/form/view/index.tsx +15 -3
- package/src/models/field.ts +504 -538
- package/tailwind.config.js +11 -0
- package/tsconfig.json +2 -0
- package/vite.config.ts +59 -0
- package/dist/components/ui/Checkbox.d.ts +0 -14
- package/dist/components/ui/Checkbox.d.ts.map +0 -1
- package/dist/components/ui/Checkbox.js +0 -43
- package/dist/components/ui/Checkbox.js.map +0 -1
- package/dist/components/ui/CheckboxGroup.d.ts +0 -15
- package/dist/components/ui/CheckboxGroup.d.ts.map +0 -1
- package/dist/components/ui/CheckboxGroup.js +0 -33
- package/dist/components/ui/CheckboxGroup.js.map +0 -1
- package/dist/components/ui/Input.d.ts +0 -14
- package/dist/components/ui/Input.d.ts.map +0 -1
- package/dist/components/ui/Input.js +0 -49
- package/dist/components/ui/Input.js.map +0 -1
- package/dist/components/ui/Radio.d.ts +0 -14
- package/dist/components/ui/Radio.d.ts.map +0 -1
- package/dist/components/ui/Radio.js +0 -43
- package/dist/components/ui/Radio.js.map +0 -1
- package/dist/components/ui/Select.d.ts +0 -18
- package/dist/components/ui/Select.d.ts.map +0 -1
- package/dist/components/ui/Select.js +0 -44
- package/dist/components/ui/Select.js.map +0 -1
- package/dist/components/ui/Textarea.d.ts +0 -13
- package/dist/components/ui/Textarea.d.ts.map +0 -1
- package/dist/components/ui/Textarea.js +0 -42
- package/dist/components/ui/Textarea.js.map +0 -1
- package/dist/components/ui/index.d.ts +0 -13
- package/dist/components/ui/index.d.ts.map +0 -1
- package/dist/components/ui/index.js +0 -7
- package/dist/components/ui/index.js.map +0 -1
- package/dist/form/index.d.ts +0 -10
- package/dist/form/index.d.ts.map +0 -1
- package/dist/form/index.js +0 -5
- package/dist/form/index.js.map +0 -1
- package/dist/form/interfaces/field-container.d.ts +0 -8
- package/dist/form/interfaces/field-container.d.ts.map +0 -1
- package/dist/form/interfaces/field-container.js +0 -2
- package/dist/form/interfaces/field-container.js.map +0 -1
- package/dist/form/interfaces/interfaces.d.ts +0 -8
- package/dist/form/interfaces/interfaces.d.ts.map +0 -1
- package/dist/form/interfaces/interfaces.js +0 -2
- package/dist/form/interfaces/interfaces.js.map +0 -1
- package/dist/form/interfaces/settings.d.ts +0 -10
- package/dist/form/interfaces/settings.d.ts.map +0 -1
- package/dist/form/interfaces/settings.js +0 -2
- package/dist/form/interfaces/settings.js.map +0 -1
- package/dist/form/interfaces/template.d.ts +0 -6
- package/dist/form/interfaces/template.d.ts.map +0 -1
- package/dist/form/interfaces/template.js +0 -2
- package/dist/form/interfaces/template.js.map +0 -1
- package/dist/form/interfaces/wise-form-specs.d.ts +0 -9
- package/dist/form/interfaces/wise-form-specs.d.ts.map +0 -1
- package/dist/form/interfaces/wise-form-specs.js +0 -2
- package/dist/form/interfaces/wise-form-specs.js.map +0 -1
- package/dist/form/view/components/containers/index.d.ts +0 -3
- package/dist/form/view/components/containers/index.d.ts.map +0 -1
- package/dist/form/view/components/containers/index.js +0 -12
- package/dist/form/view/components/containers/index.js.map +0 -1
- package/dist/form/view/components/error.d.ts +0 -5
- package/dist/form/view/components/error.d.ts.map +0 -1
- package/dist/form/view/components/error.js +0 -8
- package/dist/form/view/components/error.js.map +0 -1
- package/dist/form/view/components/field/container.d.ts +0 -5
- package/dist/form/view/components/field/container.d.ts.map +0 -1
- package/dist/form/view/components/field/container.js +0 -5
- package/dist/form/view/components/field/container.js.map +0 -1
- package/dist/form/view/components/field/index.d.ts +0 -18
- package/dist/form/view/components/field/index.d.ts.map +0 -1
- package/dist/form/view/components/field/index.js +0 -89
- package/dist/form/view/components/field/index.js.map +0 -1
- package/dist/form/view/components/field/selection.d.ts +0 -2
- package/dist/form/view/components/field/selection.d.ts.map +0 -1
- package/dist/form/view/components/field/selection.js +0 -35
- package/dist/form/view/components/field/selection.js.map +0 -1
- package/dist/form/view/components/field/use-field.d.ts +0 -4
- package/dist/form/view/components/field/use-field.d.ts.map +0 -1
- package/dist/form/view/components/field/use-field.js +0 -41
- package/dist/form/view/components/field/use-field.js.map +0 -1
- package/dist/form/view/components/rows/row-container.d.ts +0 -18
- package/dist/form/view/components/rows/row-container.d.ts.map +0 -1
- package/dist/form/view/components/rows/row-container.js +0 -89
- package/dist/form/view/components/rows/row-container.js.map +0 -1
- package/dist/form/view/components/rows/wrapper.d.ts +0 -12
- package/dist/form/view/components/rows/wrapper.d.ts.map +0 -1
- package/dist/form/view/components/rows/wrapper.js +0 -27
- package/dist/form/view/components/rows/wrapper.js.map +0 -1
- package/dist/form/view/components/wrapped-form.d.ts +0 -6
- package/dist/form/view/components/wrapped-form.d.ts.map +0 -1
- package/dist/form/view/components/wrapped-form.js +0 -26
- package/dist/form/view/components/wrapped-form.js.map +0 -1
- package/dist/form/view/context.d.ts +0 -23
- package/dist/form/view/context.d.ts.map +0 -1
- package/dist/form/view/context.js +0 -7
- package/dist/form/view/context.js.map +0 -1
- package/dist/form/view/hooks/use-model.d.ts +0 -10
- package/dist/form/view/hooks/use-model.d.ts.map +0 -1
- package/dist/form/view/hooks/use-model.js +0 -31
- package/dist/form/view/hooks/use-model.js.map +0 -1
- package/dist/form/view/hooks/use-template.d.ts +0 -14
- package/dist/form/view/hooks/use-template.d.ts.map +0 -1
- package/dist/form/view/hooks/use-template.js +0 -57
- package/dist/form/view/hooks/use-template.js.map +0 -1
- package/dist/form/view/hooks/use-types.d.ts +0 -2
- package/dist/form/view/hooks/use-types.d.ts.map +0 -1
- package/dist/form/view/hooks/use-types.js +0 -19
- package/dist/form/view/hooks/use-types.js.map +0 -1
- package/dist/form/view/index.d.ts +0 -3
- package/dist/form/view/index.d.ts.map +0 -1
- package/dist/form/view/index.js +0 -38
- package/dist/form/view/index.js.map +0 -1
- package/dist/formulas/helpers/condition-types.d.ts +0 -5
- package/dist/formulas/helpers/condition-types.d.ts.map +0 -1
- package/dist/formulas/helpers/condition-types.js +0 -5
- package/dist/formulas/helpers/condition-types.js.map +0 -1
- package/dist/formulas/helpers/evaluations.d.ts +0 -15
- package/dist/formulas/helpers/evaluations.d.ts.map +0 -1
- package/dist/formulas/helpers/evaluations.js +0 -44
- package/dist/formulas/helpers/evaluations.js.map +0 -1
- package/dist/formulas/helpers/formula.d.ts +0 -6
- package/dist/formulas/helpers/formula.d.ts.map +0 -1
- package/dist/formulas/helpers/formula.js +0 -26
- package/dist/formulas/helpers/formula.js.map +0 -1
- package/dist/formulas/helpers/lexer.d.ts +0 -10
- package/dist/formulas/helpers/lexer.d.ts.map +0 -1
- package/dist/formulas/helpers/lexer.js +0 -73
- package/dist/formulas/helpers/lexer.js.map +0 -1
- package/dist/formulas/helpers/parser.d.ts +0 -24
- package/dist/formulas/helpers/parser.d.ts.map +0 -1
- package/dist/formulas/helpers/parser.js +0 -48
- package/dist/formulas/helpers/parser.js.map +0 -1
- package/dist/formulas/helpers/token.d.ts +0 -14
- package/dist/formulas/helpers/token.d.ts.map +0 -1
- package/dist/formulas/helpers/token.js +0 -14
- package/dist/formulas/helpers/token.js.map +0 -1
- package/dist/formulas/index.d.ts +0 -59
- package/dist/formulas/index.d.ts.map +0 -1
- package/dist/formulas/index.js +0 -186
- package/dist/formulas/index.js.map +0 -1
- package/dist/formulas/types/formulas.d.ts +0 -68
- package/dist/formulas/types/formulas.d.ts.map +0 -1
- package/dist/formulas/types/formulas.js +0 -2
- package/dist/formulas/types/formulas.js.map +0 -1
- package/dist/formulas/types/index.d.ts +0 -5
- package/dist/formulas/types/index.d.ts.map +0 -1
- package/dist/formulas/types/index.js +0 -2
- package/dist/formulas/types/index.js.map +0 -1
- package/dist/formulas/variants/array-formula.d.ts +0 -24
- package/dist/formulas/variants/array-formula.d.ts.map +0 -1
- package/dist/formulas/variants/array-formula.js +0 -142
- package/dist/formulas/variants/array-formula.js.map +0 -1
- package/dist/formulas/variants/base.d.ts +0 -6
- package/dist/formulas/variants/base.d.ts.map +0 -1
- package/dist/formulas/variants/base.js +0 -3
- package/dist/formulas/variants/base.js.map +0 -1
- package/dist/formulas/variants/basic.d.ts +0 -18
- package/dist/formulas/variants/basic.d.ts.map +0 -1
- package/dist/formulas/variants/basic.js +0 -128
- package/dist/formulas/variants/basic.js.map +0 -1
- package/dist/formulas/variants/comparison.d.ts +0 -25
- package/dist/formulas/variants/comparison.d.ts.map +0 -1
- package/dist/formulas/variants/comparison.js +0 -153
- package/dist/formulas/variants/comparison.js.map +0 -1
- package/dist/formulas/variants/conditional.d.ts +0 -18
- package/dist/formulas/variants/conditional.d.ts.map +0 -1
- package/dist/formulas/variants/conditional.js +0 -183
- package/dist/formulas/variants/conditional.js.map +0 -1
- package/dist/formulas/variants/iterative-array.d.ts +0 -20
- package/dist/formulas/variants/iterative-array.d.ts.map +0 -1
- package/dist/formulas/variants/iterative-array.js +0 -155
- package/dist/formulas/variants/iterative-array.js.map +0 -1
- package/dist/formulas/variants/per-value.d.ts +0 -20
- package/dist/formulas/variants/per-value.d.ts.map +0 -1
- package/dist/formulas/variants/per-value.js +0 -154
- package/dist/formulas/variants/per-value.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +0 -1
- package/dist/models/base.d.ts +0 -55
- package/dist/models/base.d.ts.map +0 -1
- package/dist/models/base.js +0 -146
- package/dist/models/base.js.map +0 -1
- package/dist/models/callback-manager.d.ts +0 -7
- package/dist/models/callback-manager.d.ts.map +0 -1
- package/dist/models/callback-manager.js +0 -89
- package/dist/models/callback-manager.js.map +0 -1
- package/dist/models/field.d.ts +0 -121
- package/dist/models/field.d.ts.map +0 -1
- package/dist/models/field.js +0 -515
- package/dist/models/field.js.map +0 -1
- package/dist/models/index.d.ts +0 -13
- package/dist/models/index.d.ts.map +0 -1
- package/dist/models/index.js +0 -7
- package/dist/models/index.js.map +0 -1
- package/dist/models/model.d.ts +0 -37
- package/dist/models/model.d.ts.map +0 -1
- package/dist/models/model.js +0 -245
- package/dist/models/model.js.map +0 -1
- package/dist/models/plugins/base.d.ts +0 -9
- package/dist/models/plugins/base.d.ts.map +0 -1
- package/dist/models/plugins/base.js +0 -3
- package/dist/models/plugins/base.js.map +0 -1
- package/dist/models/plugins/formula.d.ts +0 -18
- package/dist/models/plugins/formula.d.ts.map +0 -1
- package/dist/models/plugins/formula.js +0 -82
- package/dist/models/plugins/formula.js.map +0 -1
- package/dist/models/plugins/index.d.ts +0 -11
- package/dist/models/plugins/index.d.ts.map +0 -1
- package/dist/models/plugins/index.js +0 -52
- package/dist/models/plugins/index.js.map +0 -1
- package/dist/models/plugins/plugins.d.ts +0 -7
- package/dist/models/plugins/plugins.d.ts.map +0 -1
- package/dist/models/plugins/plugins.js +0 -7
- package/dist/models/plugins/plugins.js.map +0 -1
- package/dist/models/types/base-wise-model.d.ts +0 -7
- package/dist/models/types/base-wise-model.d.ts.map +0 -1
- package/dist/models/types/base-wise-model.js +0 -2
- package/dist/models/types/base-wise-model.js.map +0 -1
- package/dist/models/types/callbacks.d.ts +0 -19
- package/dist/models/types/callbacks.d.ts.map +0 -1
- package/dist/models/types/callbacks.js +0 -2
- package/dist/models/types/callbacks.js.map +0 -1
- package/dist/models/types/disabled.d.ts +0 -8
- package/dist/models/types/disabled.d.ts.map +0 -1
- package/dist/models/types/disabled.js +0 -2
- package/dist/models/types/disabled.js.map +0 -1
- package/dist/models/types/form-field.d.ts +0 -25
- package/dist/models/types/form-field.d.ts.map +0 -1
- package/dist/models/types/form-field.js +0 -2
- package/dist/models/types/form-field.js.map +0 -1
- package/dist/models/types/model.d.ts +0 -13
- package/dist/models/types/model.d.ts.map +0 -1
- package/dist/models/types/model.js +0 -2
- package/dist/models/types/model.js.map +0 -1
- package/dist/models/types/plugins.d.ts +0 -13
- package/dist/models/types/plugins.d.ts.map +0 -1
- package/dist/models/types/plugins.js +0 -2
- package/dist/models/types/plugins.js.map +0 -1
- package/dist/models/types/wrapped-form-model-props.d.ts +0 -11
- package/dist/models/types/wrapped-form-model-props.d.ts.map +0 -1
- package/dist/models/types/wrapped-form-model-props.js +0 -2
- package/dist/models/types/wrapped-form-model-props.js.map +0 -1
- package/dist/models/wrapper.d.ts +0 -30
- package/dist/models/wrapper.d.ts.map +0 -1
- package/dist/models/wrapper.js +0 -213
- package/dist/models/wrapper.js.map +0 -1
- package/dist/settings/index.d.ts +0 -7
- package/dist/settings/index.d.ts.map +0 -1
- package/dist/settings/index.js +0 -26
- package/dist/settings/index.js.map +0 -1
- package/dist/utils/pending-promise.d.ts +0 -6
- package/dist/utils/pending-promise.d.ts.map +0 -1
- package/dist/utils/pending-promise.js +0 -24
- package/dist/utils/pending-promise.js.map +0 -1
package/src/models/field.ts
CHANGED
|
@@ -11,580 +11,546 @@ import { IDisabled } from './types/disabled';
|
|
|
11
11
|
* @extends ReactiveModel<IFormField>
|
|
12
12
|
*/
|
|
13
13
|
export class FormField extends ReactiveModel<IFormField> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
// The parent model, either FormModel or WrappedFormModel, containing this field.
|
|
15
|
+
#parent: WrappedFormModel | FormModel;
|
|
16
|
+
get parent() {
|
|
17
|
+
return this.#parent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#NATIVE_ACTIONS = ['hide', 'disable', 'enable', 'show', 'reset'];
|
|
21
|
+
#EVENTS = ['onClick', 'onChange', 'onKeyup'];
|
|
22
|
+
setEvents(events: string[]) {
|
|
23
|
+
this.#EVENTS.concat(events);
|
|
24
|
+
}
|
|
25
|
+
#isReady: boolean = false;
|
|
26
|
+
// Can be a boolean or an object specifying dynamic disablingvas logic based on other fields' values.
|
|
27
|
+
#disabled: boolean | IDisabled = false;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Evaluates and returns the disabled state of the field. If `#disabled` is an object, it checks the specified fields' values to determine the disabled state dynamically.
|
|
31
|
+
* @returns {boolean} The disabled state of the field.
|
|
32
|
+
*/
|
|
33
|
+
get disabled() {
|
|
34
|
+
if (typeof this.#disabled !== 'object' || !this.#disabled?.fields) return this.#disabled;
|
|
35
|
+
|
|
36
|
+
const validate = field => {
|
|
37
|
+
if (typeof field !== 'object') return !this.#parent.form.getField(field).value;
|
|
38
|
+
const { name, value } = field;
|
|
39
|
+
const fieldInstance = this.#parent.getField(name);
|
|
40
|
+
if (!fieldInstance) return false;
|
|
41
|
+
if (field.hasOwnProperty('condition')) {
|
|
42
|
+
const compare = this.evaluations[field.condition](fieldInstance.value, field.value);
|
|
43
|
+
return compare;
|
|
44
|
+
}
|
|
45
|
+
const { value: fieldValue } = fieldInstance;
|
|
46
|
+
return value !== fieldValue;
|
|
47
|
+
};
|
|
28
48
|
|
|
49
|
+
return this.#disabled.fields.some(validate);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
set disabled(value) {
|
|
53
|
+
if (value === this.#disabled) return;
|
|
54
|
+
this.#disabled = value;
|
|
55
|
+
this.triggerEvent();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Field specifications including its type, validation rules, and other metadata.
|
|
59
|
+
#specs: Record<string, any>;
|
|
60
|
+
get specs() {
|
|
61
|
+
return this.#specs;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get attributes() {
|
|
65
|
+
const props = this.getProperties();
|
|
66
|
+
return {
|
|
67
|
+
...props,
|
|
68
|
+
disabled: this.#disabled,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#value: string;
|
|
73
|
+
get value() {
|
|
74
|
+
return this.#value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set value(value) {
|
|
78
|
+
this.setValue(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Tracks other fields this field listens to for changes, enabling reactive behavior and allowing the cleanup of event listeners.
|
|
82
|
+
#listeningItems = new Map();
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Constructs a FormField instance with specified properties and parent form model.
|
|
86
|
+
* @param {Object} params - Construction parameters including the parent form model and field specifications.
|
|
87
|
+
*/
|
|
88
|
+
constructor({ parent, specs }: { parent; specs: IFormFieldProps }) {
|
|
89
|
+
let { properties, disabled, ...props } = specs;
|
|
90
|
+
super({
|
|
91
|
+
...props,
|
|
92
|
+
properties: ['name', 'type', 'placeholder', 'required', 'label', 'variant', 'options', 'className', 'checked', 'id', 'icon', 'hidden', ...properties],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
(this as any).__instanceID = `${specs.name}.${this.generateRandomNumber()}`;
|
|
96
|
+
|
|
97
|
+
this.#specs = specs;
|
|
98
|
+
this.#parent = parent;
|
|
99
|
+
(this as any).__instance = Math.random();
|
|
100
|
+
|
|
101
|
+
const toSet: Record<string, any> = {};
|
|
29
102
|
/**
|
|
30
|
-
*
|
|
31
|
-
* @returns {boolean} The disabled state of the field.
|
|
103
|
+
* @todo: review this code
|
|
32
104
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return this.#disabled;
|
|
36
|
-
|
|
37
|
-
const validate = (field) => {
|
|
38
|
-
if (typeof field !== 'object')
|
|
39
|
-
return !this.#parent.form.getField(field).value;
|
|
40
|
-
const { name, value } = field;
|
|
41
|
-
const fieldInstance = this.#parent.getField(name);
|
|
42
|
-
if (!fieldInstance) return false;
|
|
43
|
-
if (field.hasOwnProperty('condition')) {
|
|
44
|
-
const compare = this.evaluations[field.condition](
|
|
45
|
-
fieldInstance.value,
|
|
46
|
-
field.value
|
|
47
|
-
);
|
|
48
|
-
return compare;
|
|
49
|
-
}
|
|
50
|
-
const { value: fieldValue } = fieldInstance;
|
|
51
|
-
return value !== fieldValue;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return this.#disabled.fields.some(validate);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
set disabled(value) {
|
|
58
|
-
if (value === this.#disabled) return;
|
|
59
|
-
this.#disabled = value;
|
|
60
|
-
this.triggerEvent();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Field specifications including its type, validation rules, and other metadata.
|
|
64
|
-
#specs: Record<string, any>;
|
|
65
|
-
get specs() {
|
|
66
|
-
return this.#specs;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get attributes() {
|
|
70
|
-
const props = this.getProperties();
|
|
71
|
-
return {
|
|
72
|
-
...props,
|
|
73
|
-
disabled: this.#disabled,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
#value: string;
|
|
78
|
-
get value() {
|
|
79
|
-
return this.#value;
|
|
80
|
-
}
|
|
105
|
+
Object.keys(props).forEach(key => {
|
|
106
|
+
if (key === 'properties') return;
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
if (typeof props[key] === 'string' && props[key]?.includes('state:')) {
|
|
109
|
+
const state = props[key].split('state:')[1];
|
|
110
|
+
if (state === 'create' && !this.#parent.form.update) {
|
|
111
|
+
props[key] = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
toSet[key] = props[key];
|
|
115
|
+
});
|
|
116
|
+
// this.#disabled = disabled;
|
|
117
|
+
// this.set(toSet);
|
|
118
|
+
|
|
119
|
+
this.set(specs);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getProperties() {
|
|
123
|
+
const properties = super.getProperties();
|
|
124
|
+
return { ...properties, value: this.#value };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* This method is used to set the value property of the field and fire the value.change event
|
|
129
|
+
*
|
|
130
|
+
* @param value
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
setValue(value: string) {
|
|
134
|
+
if (value === this.value) return;
|
|
135
|
+
this.#value = value;
|
|
136
|
+
this.trigger('change');
|
|
137
|
+
this.trigger('value.change', this);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
generateRandomNumber = () => {
|
|
141
|
+
return Math.floor(Math.random() * (1000000 - 10000 + 1)) + 10000;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Performs initial setup based on the field's specifications, setting up validation, default values, and any specified dynamic behavior.
|
|
146
|
+
*/
|
|
147
|
+
initialize = () => {
|
|
148
|
+
this.checkSettings(this.#specs);
|
|
149
|
+
this.on('change', this.listenerEvents);
|
|
150
|
+
// this.on('value.change', this.listenerEvents);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resets the field to its initial value and state, including resetting the disabled state if it's statically defined.
|
|
155
|
+
*/
|
|
156
|
+
clear = () => {
|
|
157
|
+
// Get initial values from specs or use empty object
|
|
158
|
+
const initValues = this.#specs || {};
|
|
159
|
+
this.set(initValues);
|
|
160
|
+
if (initValues.hasOwnProperty('disabled')) this.disabled = initValues.disabled;
|
|
161
|
+
this.triggerEvent('clear');
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Listens to changes in sibling fields (specified in dynamic disabling logic) and updates its state accordingly.
|
|
166
|
+
*/
|
|
167
|
+
#listenSiblings = () => {
|
|
168
|
+
this.triggerEvent('change');
|
|
169
|
+
this.triggerEvent();
|
|
170
|
+
this.triggerEvent('value.change');
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Checks and applies the field's settings, particularly for dynamic disabling, establishing listeners on related fields as necessary.
|
|
175
|
+
* @param {Object} props - The field's properties and settings to check and apply.
|
|
176
|
+
*/
|
|
177
|
+
checkSettings(props) {
|
|
178
|
+
if (props.hasOwnProperty('disabled')) {
|
|
179
|
+
if (typeof props.disabled === 'boolean') {
|
|
180
|
+
this.#disabled = props.disabled;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (typeof props.disabled !== 'object') {
|
|
185
|
+
throw new Error(`The disabled property of the field ${props.name} must be a boolean or an object`);
|
|
186
|
+
}
|
|
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
|
+
if (props.disabled.mode) {
|
|
192
|
+
// posible modes : create, update;
|
|
193
|
+
this.#disabled = this.#parent.form.mode === props.disabled.mode;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let allValid;
|
|
198
|
+
props.disabled.fields.forEach(item => {
|
|
199
|
+
const name = typeof item === 'string' ? item : item.name;
|
|
200
|
+
|
|
201
|
+
const instance = this.#parent.form.getField(name);
|
|
202
|
+
allValid = instance;
|
|
203
|
+
if (!allValid) return;
|
|
204
|
+
instance.on('change', this.#listenSiblings);
|
|
205
|
+
instance.on('value.change', this.#listenSiblings);
|
|
206
|
+
this.#listeningItems.set(name, {
|
|
207
|
+
item: instance,
|
|
208
|
+
listener: this.#listenSiblings,
|
|
113
209
|
});
|
|
210
|
+
});
|
|
114
211
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
212
|
+
if (!allValid) {
|
|
213
|
+
const fieldName = this.getProperties().name || 'unknown';
|
|
214
|
+
throw new Error(`the field ${allValid} does not exist in the form ${(this.#parent as any).name}, field passed in invalid settings of field "${fieldName}"`);
|
|
215
|
+
}
|
|
216
|
+
this.#disabled = props.disabled;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* En este metodo se recorre el objeto asociado al evento y ejecuta cada una de las acciones asociadas
|
|
222
|
+
* como las acciones nativas del FormModel (HIDE, SHOW, DISABLE, ENABLE), hace el seteo de propiedades
|
|
223
|
+
* en caso de recibir field y ejecuta las callbacks asociadas
|
|
224
|
+
* @param actions objeto con las acciones que se van a realizar al ejecutarse el evento asociado
|
|
225
|
+
* @returns
|
|
226
|
+
*/
|
|
227
|
+
async #executeEvent(actions) {
|
|
228
|
+
if (typeof actions !== 'object' || Array.isArray(actions)) return;
|
|
229
|
+
|
|
230
|
+
const formModel = this.#parent.form;
|
|
231
|
+
|
|
232
|
+
const sortedKeys = Object.keys(actions).sort((a, b) => actions[a]?.__order - actions[b]?.__order);
|
|
233
|
+
|
|
234
|
+
for (let action of sortedKeys) {
|
|
235
|
+
if (action === 'fields') {
|
|
236
|
+
for (let fieldName in actions[action]) {
|
|
237
|
+
const field = this.#parent.form.getField(fieldName);
|
|
238
|
+
|
|
239
|
+
if (!field) continue;
|
|
240
|
+
await field.isReady;
|
|
241
|
+
field.set(actions[action][fieldName]);
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (formModel.callbacks.hasOwnProperty(action)) {
|
|
246
|
+
formModel.callbacks[action]({
|
|
247
|
+
...actions[action],
|
|
248
|
+
form: formModel,
|
|
140
249
|
});
|
|
141
|
-
|
|
142
|
-
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
143
252
|
|
|
144
|
-
|
|
253
|
+
if (this.#NATIVE_ACTIONS.includes(action) && formModel.hasOwnProperty(action)) {
|
|
254
|
+
formModel[action](actions[action].target);
|
|
255
|
+
}
|
|
145
256
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Busca el evento configurado en el field
|
|
261
|
+
* @param item objeto que tiene el evento
|
|
262
|
+
* @returns
|
|
263
|
+
*/
|
|
264
|
+
#getEvent(item) {
|
|
265
|
+
let event: string;
|
|
266
|
+
const keys = Object.keys(item);
|
|
267
|
+
keys.forEach(key => {
|
|
268
|
+
if (event) return;
|
|
269
|
+
if (this.#EVENTS.includes(key)) event = key;
|
|
270
|
+
});
|
|
271
|
+
return event;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Metodo para identificar si es field con multiples eventos configurados o solo es un evento configurado
|
|
276
|
+
* hace la busqueda del evento lanzado al haber multiples
|
|
277
|
+
* @returns
|
|
278
|
+
*/
|
|
279
|
+
listenerEvents = event2 => {
|
|
280
|
+
if (!this.#isReady) {
|
|
281
|
+
this.#isReady = true;
|
|
282
|
+
return;
|
|
150
283
|
}
|
|
151
284
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
*/
|
|
158
|
-
setValue(value: string) {
|
|
159
|
-
if (value === this.value) return;
|
|
160
|
-
this.#value = value;
|
|
161
|
-
this.trigger('change');
|
|
162
|
-
this.trigger('value.change', this);
|
|
285
|
+
if (!this.specs?.events) {
|
|
286
|
+
const event = this.#getEvent(this.specs);
|
|
287
|
+
if (!event) return;
|
|
288
|
+
this.#executeEvent(this.specs[event]);
|
|
289
|
+
return;
|
|
163
290
|
}
|
|
164
291
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
292
|
+
const event = this.#getEvent(this.specs.events);
|
|
293
|
+
if (!event) return;
|
|
294
|
+
const item = this.specs.events[event].hasOwnProperty(this.value) ? this.specs.events[event][this.value] : null;
|
|
295
|
+
if (!item) return;
|
|
296
|
+
this.#executeEvent(item);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Cleans up any established listeners and internal state when the field is removed or the form is reset, ensuring no memory leaks or stale data.
|
|
301
|
+
*/
|
|
302
|
+
cleanUp() {
|
|
303
|
+
this.#listeningItems.forEach(({ item, listener }) => item.off('change', listener));
|
|
304
|
+
// todo: remove all events
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* The `set` method sets one or more properties on the model.
|
|
309
|
+
*
|
|
310
|
+
*
|
|
311
|
+
* This method overwrites the original reactiveModel set to pass the object as param
|
|
312
|
+
* when the change event is fired.
|
|
313
|
+
* Eventually this method will be removed and the original set method will be used, but
|
|
314
|
+
* it requires an upgrade in the reactive model package.
|
|
315
|
+
* @param {keyof ReactiveModelPublic<T>} property - The name of the property to set.
|
|
316
|
+
* @param {*} value - The value to set the property to.
|
|
317
|
+
* @returns {void}
|
|
318
|
+
*/
|
|
319
|
+
set(properties: Partial<IFormField>): any {
|
|
320
|
+
let updated = false;
|
|
321
|
+
try {
|
|
322
|
+
Object.keys(properties).forEach(prop => {
|
|
323
|
+
const currentProperties = Object.keys(this.getProperties());
|
|
324
|
+
|
|
325
|
+
// Verificar si la propiedad está registrada en el modelo reactivo
|
|
326
|
+
// Si no está en getProperties(), verificar si está en la lista de properties del constructor
|
|
327
|
+
const isRegisteredProperty = currentProperties.includes(prop) || (this.#specs?.properties && this.#specs.properties.includes(prop));
|
|
328
|
+
|
|
329
|
+
if (!isRegisteredProperty) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
168
332
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*/
|
|
172
|
-
initialize = () => {
|
|
173
|
-
this.checkSettings(this.#specs);
|
|
174
|
-
this.on('change', this.listenerEvents);
|
|
175
|
-
// this.on('value.change', this.listenerEvents);
|
|
176
|
-
};
|
|
333
|
+
const newValue = properties[prop];
|
|
334
|
+
const currentValue = this[prop];
|
|
177
335
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
*/
|
|
181
|
-
clear = () => {
|
|
182
|
-
// Get initial values from specs or use empty object
|
|
183
|
-
const initValues = this.#specs || {};
|
|
184
|
-
this.set(initValues);
|
|
185
|
-
if (initValues.hasOwnProperty('disabled'))
|
|
186
|
-
this.disabled = initValues.disabled;
|
|
187
|
-
this.triggerEvent('clear');
|
|
188
|
-
};
|
|
336
|
+
// Si los valores son iguales por referencia, no actualizar
|
|
337
|
+
if (currentValue === newValue) return;
|
|
189
338
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.triggerEvent('change');
|
|
195
|
-
this.triggerEvent();
|
|
196
|
-
this.triggerEvent('value.change');
|
|
197
|
-
};
|
|
339
|
+
// Si ambos son arrays y tienen la misma referencia, no actualizar
|
|
340
|
+
if (Array.isArray(currentValue) && Array.isArray(newValue) && currentValue === newValue) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
198
343
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
344
|
+
// Si el tipo cambia (array a string, string a array, etc.), siempre actualizar
|
|
345
|
+
const currentIsArray = Array.isArray(currentValue);
|
|
346
|
+
const newIsArray = Array.isArray(newValue);
|
|
347
|
+
const currentIsObject = typeof currentValue === 'object' && currentValue !== null && !currentIsArray;
|
|
348
|
+
const newIsObject = typeof newValue === 'object' && newValue !== null && !newIsArray;
|
|
349
|
+
|
|
350
|
+
if ((currentIsArray && !newIsArray) || (!currentIsArray && newIsArray) || (currentIsObject && !newIsObject) || (!currentIsObject && newIsObject)) {
|
|
351
|
+
// Deep clone objects before setting to avoid reference issues
|
|
352
|
+
let valueToSet = newValue;
|
|
353
|
+
if (typeof newValue === 'object' && newValue !== null) {
|
|
354
|
+
if (Array.isArray(newValue)) {
|
|
355
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
356
|
+
} else {
|
|
357
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
208
358
|
}
|
|
359
|
+
}
|
|
360
|
+
this[prop] = valueToSet;
|
|
361
|
+
updated = true;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
209
364
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
365
|
+
// Para arrays y objetos, hacer comparación más robusta
|
|
366
|
+
if (typeof newValue === 'object' && newValue !== null) {
|
|
367
|
+
// Si currentValue es undefined/null, siempre actualizar
|
|
368
|
+
if (currentValue === undefined || currentValue === null) {
|
|
369
|
+
// Deep clone objects before setting to avoid reference issues
|
|
370
|
+
let valueToSet = newValue;
|
|
371
|
+
if (Array.isArray(newValue)) {
|
|
372
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
373
|
+
} else {
|
|
374
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
214
375
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
376
|
+
this[prop] = valueToSet;
|
|
377
|
+
updated = true;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Comparar arrays
|
|
382
|
+
if (newIsArray && currentIsArray) {
|
|
383
|
+
// Si las longitudes son diferentes, actualizar
|
|
384
|
+
if (newValue.length !== currentValue.length) {
|
|
385
|
+
this[prop] = newValue;
|
|
386
|
+
updated = true;
|
|
387
|
+
return;
|
|
219
388
|
}
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
this.#disabled = this.#parent.form.mode === props.disabled.mode;
|
|
224
|
-
return;
|
|
389
|
+
// Si el array está vacío y ambos están vacíos, no actualizar
|
|
390
|
+
if (newValue.length === 0 && currentValue.length === 0) {
|
|
391
|
+
return;
|
|
225
392
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
393
|
+
// Comparar contenido del array de forma más robusta
|
|
394
|
+
// Primero intentar comparación rápida por referencia de todo el array usando JSON.stringify
|
|
395
|
+
// Esto es más eficiente para detectar si el contenido es realmente diferente
|
|
396
|
+
try {
|
|
397
|
+
// Normalizar ambos arrays antes de comparar para evitar problemas con orden de propiedades
|
|
398
|
+
const normalizeForComparison = (arr: any[]) => {
|
|
399
|
+
return arr.map(item => {
|
|
400
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
401
|
+
// Ordenar propiedades del objeto para comparación estable
|
|
402
|
+
const sorted = Object.keys(item)
|
|
403
|
+
.sort()
|
|
404
|
+
.reduce((acc, key) => {
|
|
405
|
+
acc[key] = item[key];
|
|
406
|
+
return acc;
|
|
407
|
+
}, {} as any);
|
|
408
|
+
return sorted;
|
|
409
|
+
}
|
|
410
|
+
return item;
|
|
239
411
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (!allValid) {
|
|
243
|
-
const fieldName = this.getProperties().name || 'unknown';
|
|
244
|
-
throw new Error(
|
|
245
|
-
`the field ${allValid} does not exist in the form ${
|
|
246
|
-
(this.#parent as any).name
|
|
247
|
-
}, field passed in invalid settings of field "${fieldName}"`
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
this.#disabled = props.disabled;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
412
|
+
};
|
|
253
413
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
* como las acciones nativas del FormModel (HIDE, SHOW, DISABLE, ENABLE), hace el seteo de propiedades
|
|
257
|
-
* en caso de recibir field y ejecuta las callbacks asociadas
|
|
258
|
-
* @param actions objeto con las acciones que se van a realizar al ejecutarse el evento asociado
|
|
259
|
-
* @returns
|
|
260
|
-
*/
|
|
261
|
-
async #executeEvent(actions) {
|
|
262
|
-
if (typeof actions !== 'object' || Array.isArray(actions)) return;
|
|
263
|
-
|
|
264
|
-
const formModel = this.#parent.form;
|
|
265
|
-
|
|
266
|
-
const sortedKeys = Object.keys(actions).sort(
|
|
267
|
-
(a, b) => actions[a]?.__order - actions[b]?.__order
|
|
268
|
-
);
|
|
414
|
+
const normalizedCurrent = normalizeForComparison(currentValue);
|
|
415
|
+
const normalizedNew = normalizeForComparison(newValue);
|
|
269
416
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
for (let fieldName in actions[action]) {
|
|
273
|
-
const field = this.#parent.form.getField(fieldName);
|
|
417
|
+
const currentStr = JSON.stringify(normalizedCurrent);
|
|
418
|
+
const newStr = JSON.stringify(normalizedNew);
|
|
274
419
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
420
|
+
if (currentStr === newStr) {
|
|
421
|
+
// Si el contenido es igual, no actualizar para evitar bucles infinitos
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
} catch (e) {
|
|
425
|
+
// Si JSON.stringify falla, hacer comparación elemento por elemento
|
|
426
|
+
let arraysEqual = true;
|
|
427
|
+
for (let idx = 0; idx < newValue.length; idx++) {
|
|
428
|
+
const newVal = newValue[idx];
|
|
429
|
+
const currentVal = currentValue[idx];
|
|
430
|
+
|
|
431
|
+
// Comparación por referencia primero (más rápida)
|
|
432
|
+
if (newVal === currentVal) continue;
|
|
433
|
+
|
|
434
|
+
// Si son objetos, comparar con JSON.stringify
|
|
435
|
+
if (typeof newVal === 'object' && newVal !== null && typeof currentVal === 'object' && currentVal !== null) {
|
|
436
|
+
try {
|
|
437
|
+
const newStr = JSON.stringify(newVal);
|
|
438
|
+
const currentStr = JSON.stringify(currentVal);
|
|
439
|
+
if (newStr !== currentStr) {
|
|
440
|
+
arraysEqual = false;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
} catch (e) {
|
|
444
|
+
// Si JSON.stringify falla, comparar por referencia
|
|
445
|
+
if (newVal !== currentVal) {
|
|
446
|
+
arraysEqual = false;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
// Para valores primitivos, comparación directa
|
|
452
|
+
if (newVal !== currentVal) {
|
|
453
|
+
arraysEqual = false;
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
278
456
|
}
|
|
279
|
-
|
|
457
|
+
}
|
|
458
|
+
if (arraysEqual) return;
|
|
280
459
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
460
|
+
// Deep clone arrays before setting to avoid reference issues
|
|
461
|
+
let arrayToSet = JSON.parse(JSON.stringify(newValue));
|
|
462
|
+
this[prop] = arrayToSet;
|
|
463
|
+
updated = true;
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Para objetos, comparar con JSON.stringify pero manejar undefined
|
|
468
|
+
if (newIsObject && currentIsObject) {
|
|
469
|
+
try {
|
|
470
|
+
const currentStr = JSON.stringify(currentValue);
|
|
471
|
+
const newStr = JSON.stringify(newValue);
|
|
472
|
+
if (currentStr === newStr) return;
|
|
473
|
+
// Si son diferentes, continuar para actualizar
|
|
474
|
+
} catch (e) {
|
|
475
|
+
// Si JSON.stringify falla (por ejemplo, con funciones), comparar por referencia
|
|
476
|
+
if (currentValue === newValue) return;
|
|
477
|
+
// Si son diferentes, continuar para actualizar
|
|
294
478
|
}
|
|
479
|
+
}
|
|
295
480
|
}
|
|
296
|
-
}
|
|
297
481
|
|
|
298
|
-
|
|
299
|
-
* Busca el evento configurado en el field
|
|
300
|
-
* @param item objeto que tiene el evento
|
|
301
|
-
* @returns
|
|
302
|
-
*/
|
|
303
|
-
#getEvent(item) {
|
|
304
|
-
let event: string;
|
|
305
|
-
const keys = Object.keys(item);
|
|
306
|
-
keys.forEach((key) => {
|
|
307
|
-
if (event) return;
|
|
308
|
-
if (this.#EVENTS.includes(key)) event = key;
|
|
309
|
-
});
|
|
310
|
-
return event;
|
|
311
|
-
}
|
|
482
|
+
const descriptor = Object.getOwnPropertyDescriptor(this, prop);
|
|
312
483
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return;
|
|
484
|
+
// Deep clone objects before setting to avoid reference issues
|
|
485
|
+
let valueToSet = newValue;
|
|
486
|
+
if (typeof newValue === 'object' && newValue !== null) {
|
|
487
|
+
if (Array.isArray(newValue)) {
|
|
488
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
489
|
+
} else {
|
|
490
|
+
valueToSet = JSON.parse(JSON.stringify(newValue));
|
|
491
|
+
}
|
|
322
492
|
}
|
|
323
493
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
494
|
+
// Si la propiedad tiene un setter, usarlo en lugar de retornar
|
|
495
|
+
if (descriptor?.set) {
|
|
496
|
+
// Llamar al setter con el valor clonado
|
|
497
|
+
// El setter del ReactiveModel hará su propia comparación y clonado
|
|
498
|
+
descriptor.set.call(this, valueToSet);
|
|
499
|
+
updated = true;
|
|
500
|
+
return;
|
|
329
501
|
}
|
|
330
502
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
*/
|
|
343
|
-
cleanUp() {
|
|
344
|
-
this.#listeningItems.forEach(({ item, listener }) =>
|
|
345
|
-
item.off('change', listener)
|
|
346
|
-
);
|
|
347
|
-
// todo: remove all events
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* The `set` method sets one or more properties on the model.
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* This method overwrites the original reactiveModel set to pass the object as param
|
|
355
|
-
* when the change event is fired.
|
|
356
|
-
* Eventually this method will be removed and the original set method will be used, but
|
|
357
|
-
* it requires an upgrade in the reactive model package.
|
|
358
|
-
* @param {keyof ReactiveModelPublic<T>} property - The name of the property to set.
|
|
359
|
-
* @param {*} value - The value to set the property to.
|
|
360
|
-
* @returns {void}
|
|
361
|
-
*/
|
|
362
|
-
set(properties: Partial<IFormField>): any {
|
|
363
|
-
let updated = false;
|
|
364
|
-
try {
|
|
365
|
-
Object.keys(properties).forEach((prop) => {
|
|
366
|
-
const currentProperties = Object.keys(this.getProperties());
|
|
367
|
-
|
|
368
|
-
// Verificar si la propiedad está registrada en el modelo reactivo
|
|
369
|
-
// Si no está en getProperties(), verificar si está en la lista de properties del constructor
|
|
370
|
-
const isRegisteredProperty =
|
|
371
|
-
currentProperties.includes(prop) ||
|
|
372
|
-
(this.#specs?.properties &&
|
|
373
|
-
this.#specs.properties.includes(prop));
|
|
374
|
-
|
|
375
|
-
if (!isRegisteredProperty) {
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const newValue = properties[prop];
|
|
380
|
-
const currentValue = this[prop];
|
|
381
|
-
|
|
382
|
-
// Si los valores son iguales por referencia, no actualizar
|
|
383
|
-
if (currentValue === newValue) return;
|
|
384
|
-
|
|
385
|
-
// Si ambos son arrays y tienen la misma referencia, no actualizar
|
|
386
|
-
if (
|
|
387
|
-
Array.isArray(currentValue) &&
|
|
388
|
-
Array.isArray(newValue) &&
|
|
389
|
-
currentValue === newValue
|
|
390
|
-
) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Si el tipo cambia (array a string, string a array, etc.), siempre actualizar
|
|
395
|
-
const currentIsArray = Array.isArray(currentValue);
|
|
396
|
-
const newIsArray = Array.isArray(newValue);
|
|
397
|
-
const currentIsObject =
|
|
398
|
-
typeof currentValue === 'object' &&
|
|
399
|
-
currentValue !== null &&
|
|
400
|
-
!currentIsArray;
|
|
401
|
-
const newIsObject =
|
|
402
|
-
typeof newValue === 'object' &&
|
|
403
|
-
newValue !== null &&
|
|
404
|
-
!newIsArray;
|
|
405
|
-
|
|
406
|
-
if (
|
|
407
|
-
(currentIsArray && !newIsArray) ||
|
|
408
|
-
(!currentIsArray && newIsArray) ||
|
|
409
|
-
(currentIsObject && !newIsObject) ||
|
|
410
|
-
(!currentIsObject && newIsObject)
|
|
411
|
-
) {
|
|
412
|
-
this[prop] = newValue;
|
|
413
|
-
updated = true;
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Para arrays y objetos, hacer comparación más robusta
|
|
418
|
-
if (typeof newValue === 'object' && newValue !== null) {
|
|
419
|
-
// Si currentValue es undefined/null, siempre actualizar
|
|
420
|
-
if (currentValue === undefined || currentValue === null) {
|
|
421
|
-
this[prop] = newValue;
|
|
422
|
-
updated = true;
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Comparar arrays
|
|
427
|
-
if (newIsArray && currentIsArray) {
|
|
428
|
-
// Si las longitudes son diferentes, actualizar
|
|
429
|
-
if (newValue.length !== currentValue.length) {
|
|
430
|
-
this[prop] = newValue;
|
|
431
|
-
updated = true;
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
// Si el array está vacío y ambos están vacíos, no actualizar
|
|
435
|
-
if (
|
|
436
|
-
newValue.length === 0 &&
|
|
437
|
-
currentValue.length === 0
|
|
438
|
-
) {
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
// Comparar contenido del array de forma más robusta
|
|
442
|
-
// Primero intentar comparación rápida por referencia de todo el array usando JSON.stringify
|
|
443
|
-
// Esto es más eficiente para detectar si el contenido es realmente diferente
|
|
444
|
-
try {
|
|
445
|
-
// Normalizar ambos arrays antes de comparar para evitar problemas con orden de propiedades
|
|
446
|
-
const normalizeForComparison = (arr: any[]) => {
|
|
447
|
-
return arr.map((item) => {
|
|
448
|
-
if (
|
|
449
|
-
typeof item === 'object' &&
|
|
450
|
-
item !== null &&
|
|
451
|
-
!Array.isArray(item)
|
|
452
|
-
) {
|
|
453
|
-
// Ordenar propiedades del objeto para comparación estable
|
|
454
|
-
const sorted = Object.keys(item)
|
|
455
|
-
.sort()
|
|
456
|
-
.reduce((acc, key) => {
|
|
457
|
-
acc[key] = item[key];
|
|
458
|
-
return acc;
|
|
459
|
-
}, {} as any);
|
|
460
|
-
return sorted;
|
|
461
|
-
}
|
|
462
|
-
return item;
|
|
463
|
-
});
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
const normalizedCurrent =
|
|
467
|
-
normalizeForComparison(currentValue);
|
|
468
|
-
const normalizedNew =
|
|
469
|
-
normalizeForComparison(newValue);
|
|
470
|
-
|
|
471
|
-
const currentStr =
|
|
472
|
-
JSON.stringify(normalizedCurrent);
|
|
473
|
-
const newStr = JSON.stringify(normalizedNew);
|
|
474
|
-
|
|
475
|
-
if (currentStr === newStr) {
|
|
476
|
-
// Si el contenido es igual, no actualizar para evitar bucles infinitos
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
} catch (e) {
|
|
480
|
-
// Si JSON.stringify falla, hacer comparación elemento por elemento
|
|
481
|
-
let arraysEqual = true;
|
|
482
|
-
for (let idx = 0; idx < newValue.length; idx++) {
|
|
483
|
-
const newVal = newValue[idx];
|
|
484
|
-
const currentVal = currentValue[idx];
|
|
485
|
-
|
|
486
|
-
// Comparación por referencia primero (más rápida)
|
|
487
|
-
if (newVal === currentVal) continue;
|
|
488
|
-
|
|
489
|
-
// Si son objetos, comparar con JSON.stringify
|
|
490
|
-
if (
|
|
491
|
-
typeof newVal === 'object' &&
|
|
492
|
-
newVal !== null &&
|
|
493
|
-
typeof currentVal === 'object' &&
|
|
494
|
-
currentVal !== null
|
|
495
|
-
) {
|
|
496
|
-
try {
|
|
497
|
-
const newStr = JSON.stringify(newVal);
|
|
498
|
-
const currentStr =
|
|
499
|
-
JSON.stringify(currentVal);
|
|
500
|
-
if (newStr !== currentStr) {
|
|
501
|
-
arraysEqual = false;
|
|
502
|
-
break;
|
|
503
|
-
}
|
|
504
|
-
} catch (e) {
|
|
505
|
-
// Si JSON.stringify falla, comparar por referencia
|
|
506
|
-
if (newVal !== currentVal) {
|
|
507
|
-
arraysEqual = false;
|
|
508
|
-
break;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
} else {
|
|
512
|
-
// Para valores primitivos, comparación directa
|
|
513
|
-
if (newVal !== currentVal) {
|
|
514
|
-
arraysEqual = false;
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
if (arraysEqual) return;
|
|
520
|
-
}
|
|
521
|
-
this[prop] = newValue;
|
|
522
|
-
updated = true;
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Para objetos, comparar con JSON.stringify pero manejar undefined
|
|
527
|
-
if (newIsObject && currentIsObject) {
|
|
528
|
-
try {
|
|
529
|
-
const currentStr = JSON.stringify(currentValue);
|
|
530
|
-
const newStr = JSON.stringify(newValue);
|
|
531
|
-
if (currentStr === newStr) return;
|
|
532
|
-
} catch (e) {
|
|
533
|
-
// Si JSON.stringify falla (por ejemplo, con funciones), comparar por referencia
|
|
534
|
-
if (currentValue === newValue) return;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const descriptor = Object.getOwnPropertyDescriptor(this, prop);
|
|
540
|
-
|
|
541
|
-
if (descriptor?.set) return;
|
|
542
|
-
this[prop] = newValue;
|
|
543
|
-
updated = true;
|
|
544
|
-
});
|
|
545
|
-
} catch (e) {
|
|
546
|
-
console.error(`Error setting properties:`, e);
|
|
547
|
-
throw new Error(`Error setting properties: ${e}`);
|
|
548
|
-
} finally {
|
|
549
|
-
if (updated) this.trigger('change', this);
|
|
503
|
+
// Si no hay setter, asignar directamente
|
|
504
|
+
// Si la propiedad está definida como reactiva en el padre, usar el descriptor del padre
|
|
505
|
+
const parentProto = Object.getPrototypeOf(this);
|
|
506
|
+
if (parentProto && parentProto !== Object.prototype) {
|
|
507
|
+
const parentDescriptor = Object.getOwnPropertyDescriptor(parentProto, prop);
|
|
508
|
+
if (parentDescriptor?.set) {
|
|
509
|
+
// Usar el setter del padre con el valor clonado
|
|
510
|
+
parentDescriptor.set.call(this, valueToSet);
|
|
511
|
+
updated = true;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
550
514
|
}
|
|
551
|
-
}
|
|
552
515
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
516
|
+
// Asignación directa solo si no hay setter en ningún nivel
|
|
517
|
+
this[prop] = valueToSet;
|
|
518
|
+
updated = true;
|
|
519
|
+
});
|
|
520
|
+
} catch (e) {
|
|
521
|
+
console.error(`Error setting properties:`, e);
|
|
522
|
+
throw new Error(`Error setting properties: ${e}`);
|
|
523
|
+
} finally {
|
|
524
|
+
if (updated) this.trigger('change', this);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
hide = () => {
|
|
529
|
+
const className = this.getProperties().className || '';
|
|
530
|
+
const isHidden = className.includes('hidden');
|
|
531
|
+
const cls = isHidden ? className : `${className} hidden`;
|
|
532
|
+
if (cls !== className) this.set({ className: cls });
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
show = () => {
|
|
536
|
+
const className = this.getProperties().className || '';
|
|
537
|
+
const isHidden = className.includes('hidden');
|
|
538
|
+
const cls = isHidden ? className.replace(/\bhidden\b/g, '').trim() : className;
|
|
539
|
+
if (cls !== className) this.set({ className: cls });
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
private evaluations: Record<string, (value: any, comparisonValue?: any) => boolean> = {
|
|
543
|
+
equal: (value, comparisonValue) => value == comparisonValue,
|
|
544
|
+
lower: (value, comparisonValue) => Number(value) < Number(comparisonValue),
|
|
545
|
+
upper: (value, comparisonValue) => Number(value) > Number(comparisonValue),
|
|
546
|
+
between: (value, [min, max]) => {
|
|
547
|
+
const numValue = Number(value);
|
|
548
|
+
return numValue >= Number(min) && numValue <= Number(max);
|
|
549
|
+
},
|
|
550
|
+
different: (value, comparisonValue) => value != comparisonValue,
|
|
551
|
+
hasValue: value => ![undefined, null, '', false].includes(value),
|
|
552
|
+
empty: value => [undefined, null, ''].includes(value),
|
|
553
|
+
lessOrEqual: (value, comparisonValue) => Number(value) <= Number(comparisonValue),
|
|
554
|
+
greaterOrEqual: (value, comparisonValue) => Number(value) >= Number(comparisonValue),
|
|
555
|
+
};
|
|
590
556
|
}
|