@formio/js 5.0.0-dev.5907.acfdf08 → 5.0.0-dev.5909.45b9e00
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/Changelog.md +307 -10
- package/dist/formio.form.js +604 -592
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.form.min.js.LICENSE.txt +2 -2
- package/dist/formio.full.js +606 -594
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.full.min.js.LICENSE.txt +2 -2
- package/dist/formio.js +3131 -209
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +14 -0
- package/dist/formio.utils.js +58 -46
- package/dist/formio.utils.min.js +1 -1
- package/dist/formio.utils.min.js.LICENSE.txt +2 -2
- package/lib/cjs/Webform.d.ts +1 -1
- package/lib/cjs/Webform.js +27 -28
- package/lib/cjs/WebformBuilder.js +28 -36
- package/lib/cjs/Wizard.d.ts +0 -1
- package/lib/cjs/Wizard.js +19 -33
- package/lib/cjs/components/Components.d.ts +0 -7
- package/lib/cjs/components/Components.js +1 -33
- package/lib/cjs/components/_classes/component/Component.d.ts +39 -7
- package/lib/cjs/components/_classes/component/Component.js +105 -36
- package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +2 -2
- package/lib/cjs/components/_classes/component/editForm/Component.edit.validation.js +1 -1
- package/lib/cjs/components/_classes/componentModal/ComponentModal.d.ts +1 -0
- package/lib/cjs/components/_classes/componentModal/ComponentModal.js +1 -0
- package/lib/cjs/components/_classes/nested/NestedComponent.d.ts +4 -19
- package/lib/cjs/components/_classes/nested/NestedComponent.js +54 -60
- package/lib/cjs/components/_classes/nestedarray/NestedArrayComponent.d.ts +2 -1
- package/lib/cjs/components/_classes/nestedarray/NestedArrayComponent.js +9 -46
- package/lib/cjs/components/datagrid/DataGrid.d.ts +0 -1
- package/lib/cjs/components/datagrid/DataGrid.js +1 -45
- package/lib/cjs/components/datamap/DataMap.js +2 -3
- package/lib/cjs/components/editgrid/EditGrid.d.ts +1 -1
- package/lib/cjs/components/editgrid/EditGrid.js +20 -15
- package/lib/cjs/components/form/Form.d.ts +1 -3
- package/lib/cjs/components/form/Form.js +21 -28
- package/lib/cjs/components/hidden/Hidden.d.ts +0 -1
- package/lib/cjs/components/hidden/Hidden.js +1 -1
- package/lib/cjs/components/html/HTML.js +15 -3
- package/lib/cjs/components/number/Number.js +11 -4
- package/lib/cjs/components/select/editForm/Select.edit.data.d.ts +1 -1
- package/lib/cjs/components/select/editForm/Select.edit.data.js +1 -0
- package/lib/cjs/components/selectboxes/SelectBoxes.js +8 -1
- package/lib/cjs/components/signature/Signature.js +3 -1
- package/lib/cjs/formio.form.js +1 -0
- package/lib/cjs/utils/conditionOperators/DateGreaterThan.js +2 -2
- package/lib/cjs/utils/conditionOperators/IsEmptyValue.d.ts +2 -2
- package/lib/cjs/utils/conditionOperators/IsEmptyValue.js +2 -2
- package/lib/cjs/utils/conditionOperators/IsEqualTo.d.ts +2 -2
- package/lib/cjs/utils/conditionOperators/IsEqualTo.js +2 -2
- package/lib/cjs/utils/formUtils.d.ts +25 -14
- package/lib/cjs/utils/formUtils.js +11 -16
- package/lib/cjs/utils/i18n.js +5 -0
- package/lib/cjs/utils/utils.d.ts +1 -2
- package/lib/cjs/utils/utils.js +15 -31
- package/lib/mjs/Webform.d.ts +1 -1
- package/lib/mjs/Webform.js +24 -27
- package/lib/mjs/WebformBuilder.js +28 -36
- package/lib/mjs/Wizard.d.ts +0 -1
- package/lib/mjs/Wizard.js +16 -29
- package/lib/mjs/components/Components.d.ts +0 -7
- package/lib/mjs/components/Components.js +1 -32
- package/lib/mjs/components/_classes/component/Component.d.ts +39 -7
- package/lib/mjs/components/_classes/component/Component.js +107 -37
- package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +2 -2
- package/lib/mjs/components/_classes/component/editForm/Component.edit.validation.js +1 -1
- package/lib/mjs/components/_classes/componentModal/ComponentModal.d.ts +1 -0
- package/lib/mjs/components/_classes/componentModal/ComponentModal.js +1 -0
- package/lib/mjs/components/_classes/nested/NestedComponent.d.ts +4 -19
- package/lib/mjs/components/_classes/nested/NestedComponent.js +55 -61
- package/lib/mjs/components/_classes/nestedarray/NestedArrayComponent.d.ts +2 -1
- package/lib/mjs/components/_classes/nestedarray/NestedArrayComponent.js +8 -43
- package/lib/mjs/components/datagrid/DataGrid.d.ts +0 -1
- package/lib/mjs/components/datagrid/DataGrid.js +1 -45
- package/lib/mjs/components/datamap/DataMap.js +2 -3
- package/lib/mjs/components/editgrid/EditGrid.d.ts +1 -1
- package/lib/mjs/components/editgrid/EditGrid.js +22 -14
- package/lib/mjs/components/form/Form.d.ts +1 -3
- package/lib/mjs/components/form/Form.js +22 -28
- package/lib/mjs/components/hidden/Hidden.d.ts +0 -1
- package/lib/mjs/components/hidden/Hidden.js +1 -1
- package/lib/mjs/components/html/HTML.js +15 -3
- package/lib/mjs/components/number/Number.js +11 -4
- package/lib/mjs/components/select/editForm/Select.edit.data.d.ts +1 -1
- package/lib/mjs/components/select/editForm/Select.edit.data.js +1 -0
- package/lib/mjs/components/selectboxes/SelectBoxes.js +8 -1
- package/lib/mjs/components/signature/Signature.js +3 -1
- package/lib/mjs/formio.form.js +1 -0
- package/lib/mjs/utils/conditionOperators/DateGreaterThan.js +2 -2
- package/lib/mjs/utils/conditionOperators/IsEmptyValue.d.ts +2 -2
- package/lib/mjs/utils/conditionOperators/IsEmptyValue.js +2 -2
- package/lib/mjs/utils/conditionOperators/IsEqualTo.d.ts +2 -2
- package/lib/mjs/utils/conditionOperators/IsEqualTo.js +2 -2
- package/lib/mjs/utils/formUtils.d.ts +25 -14
- package/lib/mjs/utils/formUtils.js +2 -12
- package/lib/mjs/utils/i18n.js +4 -0
- package/lib/mjs/utils/utils.d.ts +1 -2
- package/lib/mjs/utils/utils.js +14 -29
- package/package.json +4 -4
package/lib/mjs/Wizard.js
CHANGED
|
@@ -369,7 +369,7 @@ export default class Wizard extends Webform {
|
|
|
369
369
|
}
|
|
370
370
|
attachHeader() {
|
|
371
371
|
const isAllowPrevious = this.isAllowPrevious();
|
|
372
|
-
this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel
|
|
372
|
+
this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel?.tooltip);
|
|
373
373
|
if (this.isBreadcrumbClickable() || isAllowPrevious) {
|
|
374
374
|
this.refs[`${this.wizardKey}-link`]?.forEach((link, index) => {
|
|
375
375
|
if (!isAllowPrevious || index <= this.enabledIndex) {
|
|
@@ -594,7 +594,10 @@ export default class Wizard extends Webform {
|
|
|
594
594
|
}
|
|
595
595
|
this.redraw().then(() => {
|
|
596
596
|
this.checkData(this.submission.data);
|
|
597
|
-
this.validateCurrentPage();
|
|
597
|
+
const errors = this.submitted ? this.validate(this.localData, { dirty: true }) : this.validateCurrentPage();
|
|
598
|
+
if (this.alert) {
|
|
599
|
+
this.showErrors(errors, true, true);
|
|
600
|
+
}
|
|
598
601
|
});
|
|
599
602
|
return Promise.resolve();
|
|
600
603
|
}
|
|
@@ -689,9 +692,11 @@ export default class Wizard extends Webform {
|
|
|
689
692
|
});
|
|
690
693
|
});
|
|
691
694
|
}
|
|
692
|
-
// Validate the form
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
+
// Validate the form before going to the next page
|
|
696
|
+
const currentPageErrors = this.validateCurrentPage({ dirty: true });
|
|
697
|
+
const errors = this.submitted ? this.validate(this.localData, { dirty: true }) : currentPageErrors;
|
|
698
|
+
// allow going to the next page if the current page is valid, even if there are form level errors
|
|
699
|
+
if (currentPageErrors.length === 0) {
|
|
695
700
|
this.checkData(this.submission.data);
|
|
696
701
|
return this.beforePage(true).then(() => {
|
|
697
702
|
return this.setPage(this.getNextPage()).then(() => {
|
|
@@ -706,12 +711,13 @@ export default class Wizard extends Webform {
|
|
|
706
711
|
else {
|
|
707
712
|
this.currentPage.components.forEach((comp) => comp.setPristine(false));
|
|
708
713
|
this.scrollIntoView(this.element, true);
|
|
709
|
-
return Promise.reject(
|
|
714
|
+
return Promise.reject(this.showErrors(errors, true));
|
|
710
715
|
}
|
|
711
716
|
}
|
|
712
717
|
validateCurrentPage(flags = {}) {
|
|
718
|
+
const components = this.currentPage?.components.map((component) => component.component);
|
|
713
719
|
// Accessing the parent ensures the right instance (whether it's the parent Wizard or a nested Wizard) performs its validation
|
|
714
|
-
return this.currentPage?.parent.validateComponents(
|
|
720
|
+
return this.currentPage?.parent.validateComponents(components, this.root.data, flags);
|
|
715
721
|
}
|
|
716
722
|
emitPrevPage() {
|
|
717
723
|
this.emit('prevPage', { page: this.page, submission: this.submission });
|
|
@@ -852,7 +858,8 @@ export default class Wizard extends Webform {
|
|
|
852
858
|
}
|
|
853
859
|
onChange(flags, changed, modified, changes) {
|
|
854
860
|
super.onChange(flags, changed, modified, changes);
|
|
855
|
-
|
|
861
|
+
// The onChange loop doesn't need all components for wizards
|
|
862
|
+
const errors = this.submitted ? this.validate(this.localData, { dirty: true }) : this.validateCurrentPage();
|
|
856
863
|
if (this.alert) {
|
|
857
864
|
this.showErrors(errors, true, true);
|
|
858
865
|
}
|
|
@@ -884,12 +891,6 @@ export default class Wizard extends Webform {
|
|
|
884
891
|
this.redraw();
|
|
885
892
|
}
|
|
886
893
|
}
|
|
887
|
-
redraw() {
|
|
888
|
-
if (this.parent?.component?.modalEdit) {
|
|
889
|
-
return this.parent.redraw();
|
|
890
|
-
}
|
|
891
|
-
return super.redraw();
|
|
892
|
-
}
|
|
893
894
|
rebuild() {
|
|
894
895
|
const currentPage = this.page;
|
|
895
896
|
const setCurrentPage = () => this.setPage(currentPage);
|
|
@@ -906,21 +907,7 @@ export default class Wizard extends Webform {
|
|
|
906
907
|
return components.reduce((check, comp) => comp.checkValidity(data, dirty, row, currentPageOnly, childErrors) && check, true);
|
|
907
908
|
}
|
|
908
909
|
get errors() {
|
|
909
|
-
|
|
910
|
-
return this.currentPage.errors;
|
|
911
|
-
}
|
|
912
|
-
return super.errors;
|
|
913
|
-
}
|
|
914
|
-
showErrors(errors, triggerEvent) {
|
|
915
|
-
if (this.hasExtraPages) {
|
|
916
|
-
this.subWizards.forEach((subWizard) => {
|
|
917
|
-
if (Array.isArray(subWizard.errors)) {
|
|
918
|
-
errors = [...errors, ...subWizard.errors];
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
;
|
|
923
|
-
return super.showErrors(errors, triggerEvent);
|
|
910
|
+
return !this.isLastPage() && !this.submitted ? this.currentPage.errors : super.errors;
|
|
924
911
|
}
|
|
925
912
|
focusOnComponent(key) {
|
|
926
913
|
const component = this.getComponent(key);
|
|
@@ -256,13 +256,6 @@ export default class Components {
|
|
|
256
256
|
static setComponents(comps: any): void;
|
|
257
257
|
static addComponent(name: any, comp: any): void;
|
|
258
258
|
static setComponent(name: any, comp: any): void;
|
|
259
|
-
/**
|
|
260
|
-
* Return a path of component's value.
|
|
261
|
-
* @param {Component} component - The component instance.
|
|
262
|
-
* @returns {string} - The component's value path.
|
|
263
|
-
*/
|
|
264
|
-
static getComponentPath(component: Component): string;
|
|
265
259
|
static create(component: any, options: any, data: any): any;
|
|
266
260
|
}
|
|
267
261
|
import BaseEditForm from './_classes/component/Component.form';
|
|
268
|
-
import Component from './_classes/component/Component';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import Component from './_classes/component/Component';
|
|
2
2
|
import EditFormUtils from './_classes/component/editForm/utils';
|
|
3
3
|
import BaseEditForm from './_classes/component/Component.form';
|
|
4
|
-
import { getComponentKey, getModelType } from '../utils/utils';
|
|
5
4
|
import _ from 'lodash';
|
|
6
5
|
export default class Components {
|
|
7
6
|
static _editFormUtils = EditFormUtils;
|
|
@@ -46,34 +45,6 @@ export default class Components {
|
|
|
46
45
|
static setComponent(name, comp) {
|
|
47
46
|
Components.components[name] = comp;
|
|
48
47
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Return a path of component's value.
|
|
51
|
-
* @param {Component} component - The component instance.
|
|
52
|
-
* @returns {string} - The component's value path.
|
|
53
|
-
*/
|
|
54
|
-
static getComponentPath(component) {
|
|
55
|
-
let path = '';
|
|
56
|
-
const componentKey = getComponentKey(component.component);
|
|
57
|
-
if (componentKey) {
|
|
58
|
-
let thisPath = component.options?.parent || component;
|
|
59
|
-
while (thisPath && !thisPath.allowData && thisPath.parent) {
|
|
60
|
-
thisPath = thisPath.parent;
|
|
61
|
-
}
|
|
62
|
-
// TODO: any component that is nested in e.g. a Data Grid or an Edit Grid is going to receive a row prop; the problem
|
|
63
|
-
// is that options.row is passed to each further nested component, which results in erroneous paths like
|
|
64
|
-
// `editGrid[0].container[0].textField` rather than `editGrid[0].container.textField`. This should be adapted for other
|
|
65
|
-
// components with a tree-like data model
|
|
66
|
-
const rowIndex = component.row;
|
|
67
|
-
const rowIndexPath = rowIndex && !['container'].includes(thisPath.component.type) ? `[${Number.parseInt(rowIndex)}]` : '';
|
|
68
|
-
path = `${thisPath.path}${rowIndexPath}.`;
|
|
69
|
-
if (rowIndexPath && getModelType(thisPath) === 'nestedDataArray') {
|
|
70
|
-
path = `${path}data.`;
|
|
71
|
-
}
|
|
72
|
-
path += componentKey;
|
|
73
|
-
return _.trim(path, '.');
|
|
74
|
-
}
|
|
75
|
-
return path;
|
|
76
|
-
}
|
|
77
48
|
static create(component, options, data) {
|
|
78
49
|
let comp = null;
|
|
79
50
|
if (component.type && Components.components.hasOwnProperty(component.type)) {
|
|
@@ -98,9 +69,7 @@ export default class Components {
|
|
|
98
69
|
else {
|
|
99
70
|
comp = new Component(component, options, data);
|
|
100
71
|
}
|
|
101
|
-
|
|
102
|
-
if (path) {
|
|
103
|
-
comp.path = path;
|
|
72
|
+
if (comp.path) {
|
|
104
73
|
comp.componentsMap[comp.path] = comp;
|
|
105
74
|
}
|
|
106
75
|
return comp;
|
|
@@ -42,14 +42,13 @@ declare class Component extends Element {
|
|
|
42
42
|
*/
|
|
43
43
|
private _hasCondition;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* The row index for this component.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
_rowIndex: number | undefined;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @type {string}
|
|
49
|
+
* References to dom elements
|
|
51
50
|
*/
|
|
52
|
-
|
|
51
|
+
refs: {};
|
|
53
52
|
/**
|
|
54
53
|
* An array of all the children components errors.
|
|
55
54
|
*/
|
|
@@ -114,7 +113,19 @@ declare class Component extends Element {
|
|
|
114
113
|
* @type {Component}
|
|
115
114
|
*/
|
|
116
115
|
parent: Component;
|
|
116
|
+
/**
|
|
117
|
+
* The component paths for this component.
|
|
118
|
+
* @type {import('@formio/core').ComponentPaths} - The component paths.
|
|
119
|
+
*/
|
|
120
|
+
paths: import('@formio/core').ComponentPaths;
|
|
117
121
|
_path: string;
|
|
122
|
+
/**
|
|
123
|
+
* Determines if this component is conditionally hidden. Should generally not be set outside of conditional logic pipeline.
|
|
124
|
+
* This is necessary because of clearOnHide behavior that only clears when conditionally hidden - we need to track
|
|
125
|
+
* conditionallyHidden separately from "regular" visibility.
|
|
126
|
+
*/
|
|
127
|
+
_parentConditionallyHidden: any;
|
|
128
|
+
_conditionallyHidden: any;
|
|
118
129
|
/**
|
|
119
130
|
* Determines if this component is visible, or not.
|
|
120
131
|
*/
|
|
@@ -158,7 +169,7 @@ declare class Component extends Element {
|
|
|
158
169
|
* @type {*}
|
|
159
170
|
*/
|
|
160
171
|
info: any;
|
|
161
|
-
get componentsMap():
|
|
172
|
+
get componentsMap(): object;
|
|
162
173
|
set data(value: any);
|
|
163
174
|
get data(): any;
|
|
164
175
|
mergeSchema(component?: {}): any;
|
|
@@ -182,6 +193,17 @@ declare class Component extends Element {
|
|
|
182
193
|
* @returns {boolean} - TRUE if the component is disabled.
|
|
183
194
|
*/
|
|
184
195
|
get disabled(): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Set Row Index to row and update each component.
|
|
198
|
+
* @param {number} value - The row index.
|
|
199
|
+
* @returns {void}
|
|
200
|
+
*/
|
|
201
|
+
set rowIndex(value: number);
|
|
202
|
+
/**
|
|
203
|
+
* Get Row Index.
|
|
204
|
+
* @returns {number} - The row index.
|
|
205
|
+
*/
|
|
206
|
+
get rowIndex(): number;
|
|
185
207
|
afterComponentAssign(): void;
|
|
186
208
|
createAddon(addonConfiguration: any): any;
|
|
187
209
|
get shouldDisabled(): any;
|
|
@@ -190,6 +212,8 @@ declare class Component extends Element {
|
|
|
190
212
|
get hasInput(): any;
|
|
191
213
|
get defaultSchema(): any;
|
|
192
214
|
get key(): any;
|
|
215
|
+
set path(path: string | undefined);
|
|
216
|
+
get path(): string | undefined;
|
|
193
217
|
set parentVisible(value: any);
|
|
194
218
|
get parentVisible(): any;
|
|
195
219
|
set parentDisabled(value: boolean);
|
|
@@ -207,12 +231,20 @@ declare class Component extends Element {
|
|
|
207
231
|
* @returns {boolean} - Whether the component is visible or not.
|
|
208
232
|
*/
|
|
209
233
|
get visible(): boolean;
|
|
234
|
+
get conditionallyHidden(): any;
|
|
235
|
+
/**
|
|
236
|
+
* Evaluates whether the component is conditionally hidden (as opposed to intentionally hidden, e.g. via the `hidden` component schema property).
|
|
237
|
+
* @param {object} data - The data object to evaluate the condition against.
|
|
238
|
+
* @param {object} row - The row object to evaluate the condition against.
|
|
239
|
+
* @returns {boolean} - Whether the component is conditionally hidden.
|
|
240
|
+
*/
|
|
241
|
+
checkConditionallyHidden(data?: object, row?: object): boolean;
|
|
210
242
|
set currentForm(instance: any);
|
|
211
243
|
get currentForm(): any;
|
|
212
244
|
_currentForm: any;
|
|
213
245
|
get fullMode(): boolean;
|
|
214
246
|
get builderMode(): boolean;
|
|
215
|
-
get calculatedPath(): string;
|
|
247
|
+
get calculatedPath(): string | undefined;
|
|
216
248
|
get labelPosition(): any;
|
|
217
249
|
get labelWidth(): any;
|
|
218
250
|
get labelMargin(): any;
|
|
@@ -6,7 +6,7 @@ import isMobile from 'ismobilejs';
|
|
|
6
6
|
import { processOne, processOneSync, validateProcessInfo } from '@formio/core/process';
|
|
7
7
|
import { Formio } from '../../../Formio';
|
|
8
8
|
import * as FormioUtils from '../../../utils/utils';
|
|
9
|
-
import { fastCloneDeep, boolValue,
|
|
9
|
+
import { fastCloneDeep, boolValue, isInsideScopingComponent, currentTimezone, getScriptPlugin, getContextualRowData } from '../../../utils/utils';
|
|
10
10
|
import Element from '../../../Element';
|
|
11
11
|
import ComponentModal from '../componentModal/ComponentModal';
|
|
12
12
|
import Widgets from '../../../widgets';
|
|
@@ -221,6 +221,10 @@ export default class Component extends Element {
|
|
|
221
221
|
* @private
|
|
222
222
|
*/
|
|
223
223
|
this._hasCondition = null;
|
|
224
|
+
/**
|
|
225
|
+
* The row index for this component.
|
|
226
|
+
*/
|
|
227
|
+
this._rowIndex = undefined;
|
|
224
228
|
/**
|
|
225
229
|
* References to dom elements
|
|
226
230
|
*/
|
|
@@ -231,11 +235,6 @@ export default class Component extends Element {
|
|
|
231
235
|
this.options.components[component.type]) {
|
|
232
236
|
_.merge(component, this.options.components[component.type]);
|
|
233
237
|
}
|
|
234
|
-
/**
|
|
235
|
-
* The data path to this specific component instance.
|
|
236
|
-
* @type {string}
|
|
237
|
-
*/
|
|
238
|
-
this.path = component?.key || '';
|
|
239
238
|
/**
|
|
240
239
|
* An array of all the children components errors.
|
|
241
240
|
*/
|
|
@@ -304,15 +303,30 @@ export default class Component extends Element {
|
|
|
304
303
|
* @type {Component}
|
|
305
304
|
*/
|
|
306
305
|
this.parent = this.options.parent;
|
|
306
|
+
/**
|
|
307
|
+
* The component paths for this component.
|
|
308
|
+
* @type {import('@formio/core').ComponentPaths} - The component paths.
|
|
309
|
+
*/
|
|
310
|
+
this.paths = FormioUtils.getComponentPaths(this.component, this.parent?.component, {
|
|
311
|
+
...this.parent?.paths,
|
|
312
|
+
dataIndex: this.options.rowIndex === undefined ? this.parent?.paths?.dataIndex : this.options.rowIndex
|
|
313
|
+
});
|
|
307
314
|
this.options.name = this.options.name || 'data';
|
|
308
315
|
this._path = '';
|
|
309
316
|
// Needs for Nextgen Rules Engine
|
|
310
317
|
this.resetCaches();
|
|
318
|
+
/**
|
|
319
|
+
* Determines if this component is conditionally hidden. Should generally not be set outside of conditional logic pipeline.
|
|
320
|
+
* This is necessary because of clearOnHide behavior that only clears when conditionally hidden - we need to track
|
|
321
|
+
* conditionallyHidden separately from "regular" visibility.
|
|
322
|
+
*/
|
|
323
|
+
this._parentConditionallyHidden = this.options.hasOwnProperty('parentConditionallyHidden') ? this.options.parentConditionallyHidden : false;
|
|
324
|
+
this._conditionallyHidden = this.checkConditionallyHidden(null, data) || this._parentConditionallyHidden;
|
|
311
325
|
/**
|
|
312
326
|
* Determines if this component is visible, or not.
|
|
313
327
|
*/
|
|
314
328
|
this._parentVisible = this.options.hasOwnProperty('parentVisible') ? this.options.parentVisible : true;
|
|
315
|
-
this._visible = this._parentVisible && this.
|
|
329
|
+
this._visible = this._parentVisible && (this.hasCondition() ? !this._conditionallyHidden : !this.component.hidden);
|
|
316
330
|
this._parentDisabled = false;
|
|
317
331
|
/**
|
|
318
332
|
* The reference attribute name for this component
|
|
@@ -381,7 +395,7 @@ export default class Component extends Element {
|
|
|
381
395
|
if (this.allowData && this.key) {
|
|
382
396
|
this.options.name += `[${this.key}]`;
|
|
383
397
|
// If component is visible or not set to clear on hide, set the default value.
|
|
384
|
-
if (this.
|
|
398
|
+
if (!(this.conditionallyHidden && this.component.clearOnHide)) {
|
|
385
399
|
if (!this.hasValue()) {
|
|
386
400
|
if (this.shouldAddDefaultValue) {
|
|
387
401
|
this.dataValue = this.defaultValue;
|
|
@@ -409,12 +423,7 @@ export default class Component extends Element {
|
|
|
409
423
|
}
|
|
410
424
|
/* eslint-enable max-statements */
|
|
411
425
|
get componentsMap() {
|
|
412
|
-
|
|
413
|
-
return this.localRoot.childComponentsMap;
|
|
414
|
-
}
|
|
415
|
-
const localMap = {};
|
|
416
|
-
localMap[this.path] = this;
|
|
417
|
-
return localMap;
|
|
426
|
+
return this.root?.childComponentsMap || {};
|
|
418
427
|
}
|
|
419
428
|
get data() {
|
|
420
429
|
return this._data;
|
|
@@ -455,11 +464,31 @@ export default class Component extends Element {
|
|
|
455
464
|
}
|
|
456
465
|
init() {
|
|
457
466
|
this.disabled = this.shouldDisabled;
|
|
458
|
-
this.
|
|
467
|
+
this._conditionallyHidden = this.checkConditionallyHidden();
|
|
468
|
+
this._visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden);
|
|
459
469
|
if (this.component.addons?.length) {
|
|
460
470
|
this.component.addons.forEach((addon) => this.createAddon(addon));
|
|
461
471
|
}
|
|
462
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Get Row Index.
|
|
475
|
+
* @returns {number} - The row index.
|
|
476
|
+
*/
|
|
477
|
+
get rowIndex() {
|
|
478
|
+
return this._rowIndex;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Set Row Index to row and update each component.
|
|
482
|
+
* @param {number} value - The row index.
|
|
483
|
+
* @returns {void}
|
|
484
|
+
*/
|
|
485
|
+
set rowIndex(value) {
|
|
486
|
+
this.paths = FormioUtils.getComponentPaths(this.component, this.parent?.component, {
|
|
487
|
+
...(this.parent?.paths || {}),
|
|
488
|
+
...{ dataIndex: value }
|
|
489
|
+
});
|
|
490
|
+
this._rowIndex = value;
|
|
491
|
+
}
|
|
463
492
|
afterComponentAssign() {
|
|
464
493
|
//implement in extended classes
|
|
465
494
|
}
|
|
@@ -528,6 +557,12 @@ export default class Component extends Element {
|
|
|
528
557
|
get key() {
|
|
529
558
|
return _.get(this.component, 'key', '');
|
|
530
559
|
}
|
|
560
|
+
get path() {
|
|
561
|
+
return this.paths.dataPath;
|
|
562
|
+
}
|
|
563
|
+
set path(path) {
|
|
564
|
+
throw new Error('Should not be setting the path of a component.');
|
|
565
|
+
}
|
|
531
566
|
set parentVisible(value) {
|
|
532
567
|
this._parentVisible = value;
|
|
533
568
|
}
|
|
@@ -573,7 +608,6 @@ export default class Component extends Element {
|
|
|
573
608
|
return;
|
|
574
609
|
}
|
|
575
610
|
this._visible = value;
|
|
576
|
-
this.clearOnHide();
|
|
577
611
|
this.redraw();
|
|
578
612
|
}
|
|
579
613
|
}
|
|
@@ -594,6 +628,21 @@ export default class Component extends Element {
|
|
|
594
628
|
}
|
|
595
629
|
return this._visible && this._parentVisible;
|
|
596
630
|
}
|
|
631
|
+
get conditionallyHidden() {
|
|
632
|
+
return this._conditionallyHidden || this._parentConditionallyHidden;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Evaluates whether the component is conditionally hidden (as opposed to intentionally hidden, e.g. via the `hidden` component schema property).
|
|
636
|
+
* @param {object} data - The data object to evaluate the condition against.
|
|
637
|
+
* @param {object} row - The row object to evaluate the condition against.
|
|
638
|
+
* @returns {boolean} - Whether the component is conditionally hidden.
|
|
639
|
+
*/
|
|
640
|
+
checkConditionallyHidden(data = null, row = null) {
|
|
641
|
+
if (!this.hasCondition()) {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
return !this.conditionallyVisible(data, row);
|
|
645
|
+
}
|
|
597
646
|
get currentForm() {
|
|
598
647
|
return this._currentForm;
|
|
599
648
|
}
|
|
@@ -1246,7 +1295,7 @@ export default class Component extends Element {
|
|
|
1246
1295
|
if (refreshData === 'data') {
|
|
1247
1296
|
this.refresh(this.data, changed, flags);
|
|
1248
1297
|
}
|
|
1249
|
-
else if ((changePath &&
|
|
1298
|
+
else if ((changePath && (changed.instance?.paths?.localPath === refreshData)) && changed && changed.instance &&
|
|
1250
1299
|
// Make sure the changed component is not in a different "context". Solves issues where refreshOn being set
|
|
1251
1300
|
// in fields inside EditGrids could alter their state from other rows (which is bad).
|
|
1252
1301
|
this.inContext(changed.instance)) {
|
|
@@ -1764,7 +1813,7 @@ export default class Component extends Element {
|
|
|
1764
1813
|
rebuild() {
|
|
1765
1814
|
this.destroy();
|
|
1766
1815
|
this.init();
|
|
1767
|
-
this.visible = this.
|
|
1816
|
+
this.visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden;
|
|
1768
1817
|
return this.redraw();
|
|
1769
1818
|
}
|
|
1770
1819
|
/**
|
|
@@ -1831,8 +1880,8 @@ export default class Component extends Element {
|
|
|
1831
1880
|
conditionallyVisible(data, row) {
|
|
1832
1881
|
data = data || this.rootValue;
|
|
1833
1882
|
row = row || this.data;
|
|
1834
|
-
if (this.builderMode || this.previewMode
|
|
1835
|
-
return
|
|
1883
|
+
if (this.builderMode || this.previewMode) {
|
|
1884
|
+
return true;
|
|
1836
1885
|
}
|
|
1837
1886
|
data = data || (this.root ? this.root.data : {});
|
|
1838
1887
|
return this.checkCondition(row, data);
|
|
@@ -1862,8 +1911,14 @@ export default class Component extends Element {
|
|
|
1862
1911
|
if (!this.builderMode & !this.previewMode && this.fieldLogic(data, row)) {
|
|
1863
1912
|
this.redraw();
|
|
1864
1913
|
}
|
|
1865
|
-
// Check advanced conditions
|
|
1866
|
-
const
|
|
1914
|
+
// Check advanced conditions (and cache the result)
|
|
1915
|
+
const isConditionallyHidden = this.checkConditionallyHidden(data, row) || this._parentConditionallyHidden;
|
|
1916
|
+
if (isConditionallyHidden !== this._conditionallyHidden) {
|
|
1917
|
+
this._conditionallyHidden = isConditionallyHidden;
|
|
1918
|
+
this.clearOnHide();
|
|
1919
|
+
}
|
|
1920
|
+
// Check visibility
|
|
1921
|
+
const visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden);
|
|
1867
1922
|
if (this.visible !== visible) {
|
|
1868
1923
|
this.visible = visible;
|
|
1869
1924
|
}
|
|
@@ -1973,6 +2028,12 @@ export default class Component extends Element {
|
|
|
1973
2028
|
FormioUtils.setActionProperty(newComponent, action, result, row, data, this);
|
|
1974
2029
|
const property = action.property.value;
|
|
1975
2030
|
if (!_.isEqual(_.get(this.component, property), _.get(newComponent, property))) {
|
|
2031
|
+
// Advanced Logic can modify the component's hidden property; because we track conditionally hidden state
|
|
2032
|
+
// separately from the component's hidden property, and technically this Advanced Logic conditionally hides
|
|
2033
|
+
// a component, we need to set _conditionallyHidden to the new value
|
|
2034
|
+
if (property === 'hidden') {
|
|
2035
|
+
this._conditionallyHidden = newComponent.hidden;
|
|
2036
|
+
}
|
|
1976
2037
|
changed = true;
|
|
1977
2038
|
}
|
|
1978
2039
|
break;
|
|
@@ -1986,7 +2047,7 @@ export default class Component extends Element {
|
|
|
1986
2047
|
component: newComponent,
|
|
1987
2048
|
result,
|
|
1988
2049
|
});
|
|
1989
|
-
if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide &&
|
|
2050
|
+
if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) {
|
|
1990
2051
|
this.setValue(newValue);
|
|
1991
2052
|
if (this.viewOnly) {
|
|
1992
2053
|
this.dataValue = newValue;
|
|
@@ -2019,7 +2080,7 @@ export default class Component extends Element {
|
|
|
2019
2080
|
component: newComponent,
|
|
2020
2081
|
result,
|
|
2021
2082
|
}, 'value');
|
|
2022
|
-
if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide &&
|
|
2083
|
+
if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) {
|
|
2023
2084
|
this.setValue(newValue);
|
|
2024
2085
|
if (this.viewOnly) {
|
|
2025
2086
|
this.dataValue = newValue;
|
|
@@ -2130,7 +2191,7 @@ export default class Component extends Element {
|
|
|
2130
2191
|
this.component.clearOnHide !== false &&
|
|
2131
2192
|
!this.options.readOnly &&
|
|
2132
2193
|
!this.options.showHiddenFields) {
|
|
2133
|
-
if (
|
|
2194
|
+
if (this.conditionallyHidden) {
|
|
2134
2195
|
this.deleteValue();
|
|
2135
2196
|
}
|
|
2136
2197
|
else if (!this.hasValue() && this.shouldAddDefaultValue) {
|
|
@@ -2392,7 +2453,7 @@ export default class Component extends Element {
|
|
|
2392
2453
|
*/
|
|
2393
2454
|
get dataValue() {
|
|
2394
2455
|
if (!this.key ||
|
|
2395
|
-
(
|
|
2456
|
+
(this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine)) {
|
|
2396
2457
|
return this.emptyValue;
|
|
2397
2458
|
}
|
|
2398
2459
|
if (!this.hasValue() && this.shouldAddDefaultValue) {
|
|
@@ -2411,7 +2472,7 @@ export default class Component extends Element {
|
|
|
2411
2472
|
set dataValue(value) {
|
|
2412
2473
|
if (!this.allowData ||
|
|
2413
2474
|
!this.key ||
|
|
2414
|
-
(
|
|
2475
|
+
(this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine)) {
|
|
2415
2476
|
return;
|
|
2416
2477
|
}
|
|
2417
2478
|
if ((value !== null) && (value !== undefined)) {
|
|
@@ -2731,7 +2792,7 @@ export default class Component extends Element {
|
|
|
2731
2792
|
// If no calculated value or
|
|
2732
2793
|
// hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden)
|
|
2733
2794
|
const { clearOnHide } = this.component;
|
|
2734
|
-
const shouldBeCleared =
|
|
2795
|
+
const shouldBeCleared = this.conditionallyHidden && clearOnHide;
|
|
2735
2796
|
const allowOverride = _.get(this.component, 'allowCalculateOverride', false);
|
|
2736
2797
|
if (shouldBeCleared) {
|
|
2737
2798
|
// remove calculated value so that the value is recalculated once component becomes visible
|
|
@@ -2857,6 +2918,9 @@ export default class Component extends Element {
|
|
|
2857
2918
|
* @returns {string} - The message to show when the component is invalid.
|
|
2858
2919
|
*/
|
|
2859
2920
|
invalidMessage(data, dirty, ignoreCondition, row) {
|
|
2921
|
+
if (!row) {
|
|
2922
|
+
row = getContextualRowData(this.component, data, this.paths);
|
|
2923
|
+
}
|
|
2860
2924
|
if (!ignoreCondition && !this.checkCondition(row, data)) {
|
|
2861
2925
|
return '';
|
|
2862
2926
|
}
|
|
@@ -2874,6 +2938,8 @@ export default class Component extends Element {
|
|
|
2874
2938
|
data,
|
|
2875
2939
|
row,
|
|
2876
2940
|
path: this.path || this.component.key,
|
|
2941
|
+
parent: this.parent?.component,
|
|
2942
|
+
paths: this.paths,
|
|
2877
2943
|
scope: validationScope,
|
|
2878
2944
|
instance: this,
|
|
2879
2945
|
processors: [
|
|
@@ -2926,7 +2992,7 @@ export default class Component extends Element {
|
|
|
2926
2992
|
if (flags.silentCheck) {
|
|
2927
2993
|
return [];
|
|
2928
2994
|
}
|
|
2929
|
-
let isDirty = this.dirty || flags.dirty;
|
|
2995
|
+
let isDirty = (flags.dirty === false) ? false : (this.dirty || flags.dirty);
|
|
2930
2996
|
if (this.options.alwaysDirty) {
|
|
2931
2997
|
isDirty = true;
|
|
2932
2998
|
}
|
|
@@ -2951,7 +3017,10 @@ export default class Component extends Element {
|
|
|
2951
3017
|
component: this.component,
|
|
2952
3018
|
data,
|
|
2953
3019
|
row,
|
|
3020
|
+
local: !!flags.local,
|
|
2954
3021
|
value: this.validationValue,
|
|
3022
|
+
parent: this.parent?.component,
|
|
3023
|
+
paths: this.paths,
|
|
2955
3024
|
path: this.path || this.component.key,
|
|
2956
3025
|
instance: this,
|
|
2957
3026
|
form: this.root ? this.root._form : {},
|
|
@@ -3214,12 +3283,6 @@ export default class Component extends Element {
|
|
|
3214
3283
|
return (this.component.protected || !this.component.persistent || (this.component.persistent === 'client-only'));
|
|
3215
3284
|
}
|
|
3216
3285
|
shouldSkipValidation(data, row, flags = {}) {
|
|
3217
|
-
const { validateWhenHidden = false } = this.component || {};
|
|
3218
|
-
const forceValidOnHidden = (!this.visible || !this.checkCondition(row, data)) && !validateWhenHidden;
|
|
3219
|
-
if (forceValidOnHidden) {
|
|
3220
|
-
// If this component is forced valid when it is hidden, then we also need to reset the errors for this component.
|
|
3221
|
-
this._errors = [];
|
|
3222
|
-
}
|
|
3223
3286
|
const rules = [
|
|
3224
3287
|
// Do not validate if the flags say not too.
|
|
3225
3288
|
() => flags.noValidate,
|
|
@@ -3230,7 +3293,14 @@ export default class Component extends Element {
|
|
|
3230
3293
|
// Check to see if we are editing and if so, check component persistence.
|
|
3231
3294
|
() => this.isValueHidden(),
|
|
3232
3295
|
// Force valid if component is hidden.
|
|
3233
|
-
() =>
|
|
3296
|
+
() => {
|
|
3297
|
+
if (!this.component.validateWhenHidden && (!this.visible || !this.checkCondition(row, data))) {
|
|
3298
|
+
// If this component is forced valid when it is hidden, then we also need to reset the errors for this component.
|
|
3299
|
+
this._errors = [];
|
|
3300
|
+
return true;
|
|
3301
|
+
}
|
|
3302
|
+
return false;
|
|
3303
|
+
}
|
|
3234
3304
|
];
|
|
3235
3305
|
return rules.some(pred => pred());
|
|
3236
3306
|
}
|
|
@@ -3367,7 +3437,7 @@ export default class Component extends Element {
|
|
|
3367
3437
|
// If component definition changed, replace it.
|
|
3368
3438
|
if (!_.isEqual(this.component, newComponent)) {
|
|
3369
3439
|
this.component = newComponent;
|
|
3370
|
-
const visible = this.
|
|
3440
|
+
const visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden;
|
|
3371
3441
|
const disabled = this.shouldDisabled;
|
|
3372
3442
|
// Change states which won't be recalculated during redrawing
|
|
3373
3443
|
if (this.visible !== visible) {
|
|
@@ -128,10 +128,10 @@ export default [
|
|
|
128
128
|
{
|
|
129
129
|
weight: 700,
|
|
130
130
|
type: 'checkbox',
|
|
131
|
-
label: '
|
|
131
|
+
label: 'Omit Value From Submission Data When Conditionally Hidden',
|
|
132
132
|
key: 'clearOnHide',
|
|
133
133
|
defaultValue: true,
|
|
134
|
-
tooltip: 'When a field is hidden,
|
|
134
|
+
tooltip: 'When a field is conditionally hidden, omit the value from the submission data.',
|
|
135
135
|
input: true
|
|
136
136
|
},
|
|
137
137
|
EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 1000, '<p><h4>Example:</h4><pre>value = data.firstName + " " + data.lastName;</pre></p>', '<p><h4>Example:</h4><pre>{"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}</pre>'),
|
|
@@ -22,7 +22,7 @@ export default [
|
|
|
22
22
|
weight: 100,
|
|
23
23
|
type: 'checkbox',
|
|
24
24
|
label: 'Validate When Hidden',
|
|
25
|
-
tooltip: 'Validates the component when it is hidden/conditionally hidden. Vaildation errors are displayed in the error alert on the form submission.',
|
|
25
|
+
tooltip: 'Validates the component when it is hidden/conditionally hidden. Vaildation errors are displayed in the error alert on the form submission. Use caution when enabling this setting, as it can cause a hidden component to be invalid with no way for the form user to correct it.',
|
|
26
26
|
key: 'validateWhenHidden',
|
|
27
27
|
input: true
|
|
28
28
|
},
|