@formio/js 5.0.0-dev.5913.d415fcc → 5.0.0-dev.5914.76c64d9

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.
Files changed (75) hide show
  1. package/dist/formio.form.js +579 -589
  2. package/dist/formio.form.min.js +1 -1
  3. package/dist/formio.form.min.js.LICENSE.txt +1 -3
  4. package/dist/formio.full.js +580 -590
  5. package/dist/formio.full.min.js +1 -1
  6. package/dist/formio.full.min.js.LICENSE.txt +1 -3
  7. package/dist/formio.js +3006 -287
  8. package/dist/formio.min.js +1 -1
  9. package/dist/formio.min.js.LICENSE.txt +12 -0
  10. package/dist/formio.utils.js +41 -51
  11. package/dist/formio.utils.min.js +1 -1
  12. package/dist/formio.utils.min.js.LICENSE.txt +1 -3
  13. package/lib/cjs/Webform.d.ts +1 -1
  14. package/lib/cjs/Webform.js +27 -28
  15. package/lib/cjs/WebformBuilder.js +21 -18
  16. package/lib/cjs/Wizard.d.ts +2 -2
  17. package/lib/cjs/Wizard.js +42 -50
  18. package/lib/cjs/components/Components.d.ts +0 -7
  19. package/lib/cjs/components/Components.js +1 -33
  20. package/lib/cjs/components/_classes/component/Component.d.ts +24 -7
  21. package/lib/cjs/components/_classes/component/Component.js +47 -13
  22. package/lib/cjs/components/_classes/componentModal/ComponentModal.d.ts +1 -0
  23. package/lib/cjs/components/_classes/componentModal/ComponentModal.js +1 -0
  24. package/lib/cjs/components/_classes/nested/NestedComponent.d.ts +4 -19
  25. package/lib/cjs/components/_classes/nested/NestedComponent.js +39 -54
  26. package/lib/cjs/components/_classes/nestedarray/NestedArrayComponent.d.ts +2 -1
  27. package/lib/cjs/components/_classes/nestedarray/NestedArrayComponent.js +9 -46
  28. package/lib/cjs/components/datagrid/DataGrid.d.ts +0 -1
  29. package/lib/cjs/components/datagrid/DataGrid.js +1 -45
  30. package/lib/cjs/components/datamap/DataMap.js +1 -2
  31. package/lib/cjs/components/editgrid/EditGrid.js +6 -6
  32. package/lib/cjs/components/form/Form.d.ts +1 -3
  33. package/lib/cjs/components/form/Form.js +16 -24
  34. package/lib/cjs/components/selectboxes/SelectBoxes.js +8 -1
  35. package/lib/cjs/utils/conditionOperators/DateGreaterThan.js +2 -2
  36. package/lib/cjs/utils/conditionOperators/IsEmptyValue.d.ts +2 -2
  37. package/lib/cjs/utils/conditionOperators/IsEmptyValue.js +2 -2
  38. package/lib/cjs/utils/conditionOperators/IsEqualTo.d.ts +2 -2
  39. package/lib/cjs/utils/conditionOperators/IsEqualTo.js +2 -2
  40. package/lib/cjs/utils/formUtils.d.ts +25 -14
  41. package/lib/cjs/utils/formUtils.js +11 -16
  42. package/lib/cjs/utils/utils.d.ts +1 -2
  43. package/lib/cjs/utils/utils.js +15 -31
  44. package/lib/mjs/Webform.d.ts +1 -1
  45. package/lib/mjs/Webform.js +24 -27
  46. package/lib/mjs/WebformBuilder.js +21 -18
  47. package/lib/mjs/Wizard.d.ts +2 -2
  48. package/lib/mjs/Wizard.js +38 -45
  49. package/lib/mjs/components/Components.d.ts +0 -7
  50. package/lib/mjs/components/Components.js +1 -32
  51. package/lib/mjs/components/_classes/component/Component.d.ts +24 -7
  52. package/lib/mjs/components/_classes/component/Component.js +49 -14
  53. package/lib/mjs/components/_classes/componentModal/ComponentModal.d.ts +1 -0
  54. package/lib/mjs/components/_classes/componentModal/ComponentModal.js +1 -0
  55. package/lib/mjs/components/_classes/nested/NestedComponent.d.ts +4 -19
  56. package/lib/mjs/components/_classes/nested/NestedComponent.js +40 -55
  57. package/lib/mjs/components/_classes/nestedarray/NestedArrayComponent.d.ts +2 -1
  58. package/lib/mjs/components/_classes/nestedarray/NestedArrayComponent.js +8 -43
  59. package/lib/mjs/components/datagrid/DataGrid.d.ts +0 -1
  60. package/lib/mjs/components/datagrid/DataGrid.js +1 -45
  61. package/lib/mjs/components/datamap/DataMap.js +1 -2
  62. package/lib/mjs/components/editgrid/EditGrid.js +9 -6
  63. package/lib/mjs/components/form/Form.d.ts +1 -3
  64. package/lib/mjs/components/form/Form.js +17 -24
  65. package/lib/mjs/components/selectboxes/SelectBoxes.js +8 -1
  66. package/lib/mjs/utils/conditionOperators/DateGreaterThan.js +2 -2
  67. package/lib/mjs/utils/conditionOperators/IsEmptyValue.d.ts +2 -2
  68. package/lib/mjs/utils/conditionOperators/IsEmptyValue.js +2 -2
  69. package/lib/mjs/utils/conditionOperators/IsEqualTo.d.ts +2 -2
  70. package/lib/mjs/utils/conditionOperators/IsEqualTo.js +2 -2
  71. package/lib/mjs/utils/formUtils.d.ts +25 -14
  72. package/lib/mjs/utils/formUtils.js +2 -12
  73. package/lib/mjs/utils/utils.d.ts +1 -2
  74. package/lib/mjs/utils/utils.js +14 -29
  75. package/package.json +4 -4
package/lib/mjs/Wizard.js CHANGED
@@ -29,6 +29,7 @@ export default class Wizard extends Webform {
29
29
  this.originalComponents = [];
30
30
  this.page = 0;
31
31
  this.currentPanel = null;
32
+ this.currentPanels = null;
32
33
  this.currentNextPage = 0;
33
34
  this._seenPages = [0];
34
35
  this.subWizards = [];
@@ -47,12 +48,12 @@ export default class Wizard extends Webform {
47
48
  }
48
49
  getPages(args = {}) {
49
50
  const { all = false } = args;
50
- const pages = this.hasSubWizards ? this.components : this.pages;
51
+ const pages = this.hasExtraPages ? this.components : this.pages;
51
52
  const filteredPages = pages
52
53
  .filter(all ? _.identity : (p, index) => this._seenPages.includes(index));
53
54
  return filteredPages;
54
55
  }
55
- get hasSubWizards() {
56
+ get hasExtraPages() {
56
57
  return !_.isEmpty(this.subWizards);
57
58
  }
58
59
  get data() {
@@ -176,9 +177,9 @@ export default class Wizard extends Webform {
176
177
  }
177
178
  render() {
178
179
  const ctx = this.renderContext;
179
- if (this.component.id) {
180
- ctx.panels.forEach(panel => {
181
- if (panel.id === this.component.id) {
180
+ if (this.component.key) {
181
+ ctx.panels.map(panel => {
182
+ if (panel.key === this.component.key) {
182
183
  this.currentPanel = panel;
183
184
  ctx.wizardPageTooltip = this.getFormattedTooltip(panel.tooltip);
184
185
  }
@@ -368,7 +369,7 @@ export default class Wizard extends Webform {
368
369
  }
369
370
  attachHeader() {
370
371
  const isAllowPrevious = this.isAllowPrevious();
371
- this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel.tooltip);
372
+ this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel?.tooltip);
372
373
  if (this.isBreadcrumbClickable() || isAllowPrevious) {
373
374
  this.refs[`${this.wizardKey}-link`]?.forEach((link, index) => {
374
375
  if (!isAllowPrevious || index <= this.enabledIndex) {
@@ -579,7 +580,7 @@ export default class Wizard extends Webform {
579
580
  this.pageFieldLogic(num);
580
581
  this.getNextPage();
581
582
  let parentNum = num;
582
- if (this.hasSubWizards) {
583
+ if (this.hasExtraPages) {
583
584
  const pageFromPages = this.pages[num];
584
585
  const pageFromComponents = this.components[num];
585
586
  if (!pageFromComponents || pageFromPages?.id !== pageFromComponents.id) {
@@ -593,7 +594,10 @@ export default class Wizard extends Webform {
593
594
  }
594
595
  this.redraw().then(() => {
595
596
  this.checkData(this.submission.data);
596
- 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
+ }
597
601
  });
598
602
  return Promise.resolve();
599
603
  }
@@ -688,9 +692,11 @@ export default class Wizard extends Webform {
688
692
  });
689
693
  });
690
694
  }
691
- // Validate the form, before go to the next page
692
- const errors = this.validateCurrentPage({ dirty: true });
693
- if (errors.length === 0) {
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) {
694
700
  this.checkData(this.submission.data);
695
701
  return this.beforePage(true).then(() => {
696
702
  return this.setPage(this.getNextPage()).then(() => {
@@ -705,12 +711,13 @@ export default class Wizard extends Webform {
705
711
  else {
706
712
  this.currentPage.components.forEach((comp) => comp.setPristine(false));
707
713
  this.scrollIntoView(this.element, true);
708
- return Promise.reject(super.showErrors(errors, true));
714
+ return Promise.reject(this.showErrors(errors, true));
709
715
  }
710
716
  }
711
717
  validateCurrentPage(flags = {}) {
718
+ const components = this.currentPage?.components.map((component) => component.component);
712
719
  // Accessing the parent ensures the right instance (whether it's the parent Wizard or a nested Wizard) performs its validation
713
- return this.currentPage?.parent.validateComponents(this.currentPage.component.components, this.currentPage.parent.data, flags);
720
+ return this.currentPage?.parent.validateComponents(components, this.root.data, flags);
714
721
  }
715
722
  emitPrevPage() {
716
723
  this.emit('prevPage', { page: this.page, submission: this.submission });
@@ -851,23 +858,29 @@ export default class Wizard extends Webform {
851
858
  }
852
859
  onChange(flags, changed, modified, changes) {
853
860
  super.onChange(flags, changed, modified, changes);
854
- const errors = this.validate(this.localData, { dirty: false });
861
+ // The onChange loop doesn't need all components for wizards
862
+ const errors = this.submitted ? this.validate(this.localData, { dirty: true }) : this.validateCurrentPage();
855
863
  if (this.alert) {
856
864
  this.showErrors(errors, true, true);
857
865
  }
858
866
  // If the pages change, need to redraw the header.
859
- const currentPanels = this.pages;
860
- // calling this.establishPages() updates/mutates this.pages to be the current pages
861
- this.establishPages();
862
- const newPanels = this.pages;
867
+ let currentPanels;
868
+ let panels;
863
869
  const currentNextPage = this.currentNextPage;
864
- const panelsUpdated = !_.isEqual(newPanels, currentPanels);
865
- if (this.currentPanel?.id && this.pages.length && (!this.hasSubWizards || (this.hasSubWizards && panelsUpdated))) {
866
- const newIndex = this.pages.findIndex(page => page.id === this.currentPanel.id);
867
- if (newIndex !== -1)
868
- this.setPage(newIndex);
870
+ if (this.hasExtraPages) {
871
+ currentPanels = this.pages.map(page => page.component.key);
872
+ this.establishPages();
873
+ panels = this.pages.map(page => page.component.key);
874
+ }
875
+ else {
876
+ currentPanels = this.currentPanels || this.pages.map(page => page.component.key);
877
+ panels = this.establishPages().map(panel => panel.key);
878
+ this.currentPanels = panels;
879
+ if (this.currentPanel?.key && this.currentPanels?.length) {
880
+ this.setPage(this.currentPanels.findIndex(panel => panel === this.currentPanel.key));
881
+ }
869
882
  }
870
- if (panelsUpdated || (flags && flags.fromSubmission)) {
883
+ if (!_.isEqual(panels, currentPanels) || (flags && flags.fromSubmission)) {
871
884
  this.redrawHeader();
872
885
  }
873
886
  // If the next page changes, then make sure to redraw navigation.
@@ -878,12 +891,6 @@ export default class Wizard extends Webform {
878
891
  this.redraw();
879
892
  }
880
893
  }
881
- redraw() {
882
- if (this.parent?.component?.modalEdit) {
883
- return this.parent.redraw();
884
- }
885
- return super.redraw();
886
- }
887
894
  rebuild() {
888
895
  const currentPage = this.page;
889
896
  const setCurrentPage = () => this.setPage(currentPage);
@@ -900,21 +907,7 @@ export default class Wizard extends Webform {
900
907
  return components.reduce((check, comp) => comp.checkValidity(data, dirty, row, currentPageOnly, childErrors) && check, true);
901
908
  }
902
909
  get errors() {
903
- if (!this.isLastPage()) {
904
- return this.currentPage.errors;
905
- }
906
- return super.errors;
907
- }
908
- showErrors(errors, triggerEvent) {
909
- if (this.hasSubWizards) {
910
- this.subWizards.forEach((subWizard) => {
911
- if (Array.isArray(subWizard.errors)) {
912
- errors = [...errors, ...subWizard.errors];
913
- }
914
- });
915
- }
916
- ;
917
- return super.showErrors(errors, triggerEvent);
910
+ return !this.isLastPage() && !this.submitted ? this.currentPage.errors : super.errors;
918
911
  }
919
912
  focusOnComponent(key) {
920
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
- const path = Components.getComponentPath(comp);
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
- * References to dom elements
45
+ * The row index for this component.
46
46
  */
47
- refs: {};
47
+ _rowIndex: number | undefined;
48
48
  /**
49
- * The data path to this specific component instance.
50
- * @type {string}
49
+ * References to dom elements
51
50
  */
52
- path: string;
51
+ refs: {};
53
52
  /**
54
53
  * An array of all the children components errors.
55
54
  */
@@ -114,6 +113,11 @@ 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;
118
122
  /**
119
123
  * Determines if this component is visible, or not.
@@ -158,7 +162,7 @@ declare class Component extends Element {
158
162
  * @type {*}
159
163
  */
160
164
  info: any;
161
- get componentsMap(): any;
165
+ get componentsMap(): object;
162
166
  set data(value: any);
163
167
  get data(): any;
164
168
  mergeSchema(component?: {}): any;
@@ -182,6 +186,17 @@ declare class Component extends Element {
182
186
  * @returns {boolean} - TRUE if the component is disabled.
183
187
  */
184
188
  get disabled(): boolean;
189
+ /**
190
+ * Set Row Index to row and update each component.
191
+ * @param {number} value - The row index.
192
+ * @returns {void}
193
+ */
194
+ set rowIndex(value: number);
195
+ /**
196
+ * Get Row Index.
197
+ * @returns {number} - The row index.
198
+ */
199
+ get rowIndex(): number;
185
200
  afterComponentAssign(): void;
186
201
  createAddon(addonConfiguration: any): any;
187
202
  get shouldDisabled(): any;
@@ -190,6 +205,8 @@ declare class Component extends Element {
190
205
  get hasInput(): any;
191
206
  get defaultSchema(): any;
192
207
  get key(): any;
208
+ set path(path: string | undefined);
209
+ get path(): string | undefined;
193
210
  set parentVisible(value: any);
194
211
  get parentVisible(): any;
195
212
  set parentDisabled(value: boolean);
@@ -212,7 +229,7 @@ declare class Component extends Element {
212
229
  _currentForm: any;
213
230
  get fullMode(): boolean;
214
231
  get builderMode(): boolean;
215
- get calculatedPath(): string;
232
+ get calculatedPath(): string | undefined;
216
233
  get labelPosition(): any;
217
234
  get labelWidth(): any;
218
235
  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, getComponentPath, isInsideScopingComponent, currentTimezone, getScriptPlugin } from '../../../utils/utils';
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,6 +303,14 @@ 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
@@ -409,12 +416,7 @@ export default class Component extends Element {
409
416
  }
410
417
  /* eslint-enable max-statements */
411
418
  get componentsMap() {
412
- if (this.localRoot?.childComponentsMap) {
413
- return this.localRoot.childComponentsMap;
414
- }
415
- const localMap = {};
416
- localMap[this.path] = this;
417
- return localMap;
419
+ return this.root?.childComponentsMap || {};
418
420
  }
419
421
  get data() {
420
422
  return this._data;
@@ -460,6 +462,25 @@ export default class Component extends Element {
460
462
  this.component.addons.forEach((addon) => this.createAddon(addon));
461
463
  }
462
464
  }
465
+ /**
466
+ * Get Row Index.
467
+ * @returns {number} - The row index.
468
+ */
469
+ get rowIndex() {
470
+ return this._rowIndex;
471
+ }
472
+ /**
473
+ * Set Row Index to row and update each component.
474
+ * @param {number} value - The row index.
475
+ * @returns {void}
476
+ */
477
+ set rowIndex(value) {
478
+ this.paths = FormioUtils.getComponentPaths(this.component, this.parent?.component, {
479
+ ...(this.parent?.paths || {}),
480
+ ...{ dataIndex: value }
481
+ });
482
+ this._rowIndex = value;
483
+ }
463
484
  afterComponentAssign() {
464
485
  //implement in extended classes
465
486
  }
@@ -528,6 +549,12 @@ export default class Component extends Element {
528
549
  get key() {
529
550
  return _.get(this.component, 'key', '');
530
551
  }
552
+ get path() {
553
+ return this.paths.dataPath;
554
+ }
555
+ set path(path) {
556
+ throw new Error('Should not be setting the path of a component.');
557
+ }
531
558
  set parentVisible(value) {
532
559
  this._parentVisible = value;
533
560
  }
@@ -1246,7 +1273,7 @@ export default class Component extends Element {
1246
1273
  if (refreshData === 'data') {
1247
1274
  this.refresh(this.data, changed, flags);
1248
1275
  }
1249
- else if ((changePath && getComponentPath(changed.instance) === refreshData) && changed && changed.instance &&
1276
+ else if ((changePath && (changed.instance?.paths?.localPath === refreshData)) && changed && changed.instance &&
1250
1277
  // Make sure the changed component is not in a different "context". Solves issues where refreshOn being set
1251
1278
  // in fields inside EditGrids could alter their state from other rows (which is bad).
1252
1279
  this.inContext(changed.instance)) {
@@ -2857,6 +2884,9 @@ export default class Component extends Element {
2857
2884
  * @returns {string} - The message to show when the component is invalid.
2858
2885
  */
2859
2886
  invalidMessage(data, dirty, ignoreCondition, row) {
2887
+ if (!row) {
2888
+ row = getContextualRowData(this.component, data, this.paths);
2889
+ }
2860
2890
  if (!ignoreCondition && !this.checkCondition(row, data)) {
2861
2891
  return '';
2862
2892
  }
@@ -2874,6 +2904,8 @@ export default class Component extends Element {
2874
2904
  data,
2875
2905
  row,
2876
2906
  path: this.path || this.component.key,
2907
+ parent: this.parent?.component,
2908
+ paths: this.paths,
2877
2909
  scope: validationScope,
2878
2910
  instance: this,
2879
2911
  processors: [
@@ -2926,7 +2958,7 @@ export default class Component extends Element {
2926
2958
  if (flags.silentCheck) {
2927
2959
  return [];
2928
2960
  }
2929
- let isDirty = this.dirty || flags.dirty;
2961
+ let isDirty = (flags.dirty === false) ? false : (this.dirty || flags.dirty);
2930
2962
  if (this.options.alwaysDirty) {
2931
2963
  isDirty = true;
2932
2964
  }
@@ -2951,7 +2983,10 @@ export default class Component extends Element {
2951
2983
  component: this.component,
2952
2984
  data,
2953
2985
  row,
2986
+ local: !!flags.local,
2954
2987
  value: this.validationValue,
2988
+ parent: this.parent?.component,
2989
+ paths: this.paths,
2955
2990
  path: this.path || this.component.key,
2956
2991
  instance: this,
2957
2992
  form: this.root ? this.root._form : {},
@@ -22,6 +22,7 @@ export default class ComponentModal {
22
22
  modalOverlay: string;
23
23
  modalContents: string;
24
24
  modalClose: string;
25
+ componentContent: string;
25
26
  openModalWrapper: string;
26
27
  openModal: string;
27
28
  modalSave: string;
@@ -61,6 +61,7 @@ export default class ComponentModal {
61
61
  modalOverlay: 'single',
62
62
  modalContents: 'single',
63
63
  modalClose: 'single',
64
+ componentContent: 'single',
64
65
  openModalWrapper: 'single',
65
66
  openModal: 'single',
66
67
  modalSave: 'single',
@@ -56,18 +56,6 @@ export default class NestedComponent extends Field {
56
56
  * @returns {object} - The current form object.
57
57
  */
58
58
  get currentForm(): object;
59
- /**
60
- * Set Row Index to row and update each component.
61
- * @param {number} value - The row index.
62
- * @returns {void}
63
- */
64
- set rowIndex(value: number);
65
- /**
66
- * Get Row Index.
67
- * @returns {number} - The row index.
68
- */
69
- get rowIndex(): number;
70
- _rowIndex: number | undefined;
71
59
  /**
72
60
  * Get Contextual data of the component.
73
61
  * @returns {object} - The contextual data of the component.
@@ -115,14 +103,11 @@ export default class NestedComponent extends Field {
115
103
  */
116
104
  eachComponent(fn: Function): void;
117
105
  /**
118
- * Returns a component provided a key. This performs a deep search within the
119
- * component tree.
106
+ * Returns a component provided a key. This performs a deep search within the component tree.
120
107
  * @param {string} path - The path to the component.
121
- * @param {Function} [fn] - Called with the component once found.
122
- * @param {string} [originalPath] - The original path to the component.
123
108
  * @returns {any} - The component that is located.
124
109
  */
125
- getComponent(path: string, fn?: Function | undefined, originalPath?: string | undefined): any;
110
+ getComponent(path: string): any;
126
111
  /**
127
112
  * Return a component provided the Id of the component.
128
113
  * @param {string} id - The Id of the component.
@@ -211,12 +196,12 @@ export default class NestedComponent extends Field {
211
196
  calculateValue(data: any, flags: any, row: any): any;
212
197
  isLastPage(): boolean;
213
198
  isValid(data: any, dirty: any): any;
214
- validationProcessor({ scope, data, row, instance, component }: {
199
+ validationProcessor({ scope, data, row, instance, paths }: {
215
200
  scope: any;
216
201
  data: any;
217
202
  row: any;
218
203
  instance: any;
219
- component: any;
204
+ paths: any;
220
205
  }, flags: any): void;
221
206
  /**
222
207
  * Perform a validation on all child components of this nested component.
@@ -2,7 +2,8 @@
2
2
  import _ from 'lodash';
3
3
  import Field from '../field/Field';
4
4
  import Components from '../../Components';
5
- import { getArrayFromComponentPath, getStringFromComponentPath, getRandomComponentId } from '../../../utils/utils';
5
+ '';
6
+ import { getComponentPaths, getRandomComponentId, componentMatches, getBestMatch, getStringFromComponentPath } from '../../../utils/utils';
6
7
  import { process as processAsync, processSync } from '@formio/core/process';
7
8
  /**
8
9
  * NestedComponent class.
@@ -198,6 +199,10 @@ export default class NestedComponent extends Field {
198
199
  */
199
200
  set rowIndex(value) {
200
201
  this._rowIndex = value;
202
+ this.paths = getComponentPaths(this.component, this.parent?.component, {
203
+ ...(this.parent?.paths || {}),
204
+ ...{ dataIndex: value }
205
+ });
201
206
  this.eachComponent((component) => {
202
207
  component.rowIndex = value;
203
208
  });
@@ -293,56 +298,36 @@ export default class NestedComponent extends Field {
293
298
  });
294
299
  }
295
300
  /**
296
- * Returns a component provided a key. This performs a deep search within the
297
- * component tree.
301
+ * Returns a component provided a key. This performs a deep search within the component tree.
298
302
  * @param {string} path - The path to the component.
299
- * @param {Function} [fn] - Called with the component once found.
300
- * @param {string} [originalPath] - The original path to the component.
301
303
  * @returns {any} - The component that is located.
302
304
  */
303
- getComponent(path, fn, originalPath) {
304
- originalPath = originalPath || getStringFromComponentPath(path);
305
- if (this.componentsMap.hasOwnProperty(originalPath)) {
306
- if (fn) {
307
- return fn(this.componentsMap[originalPath]);
308
- }
309
- else {
310
- return this.componentsMap[originalPath];
311
- }
312
- }
313
- path = getArrayFromComponentPath(path);
314
- const pathStr = originalPath;
315
- const newPath = _.clone(path);
316
- let key = newPath.shift();
317
- const remainingPath = newPath;
318
- let comp = null;
319
- let possibleComp = null;
320
- if (_.isNumber(key)) {
321
- key = remainingPath.shift();
322
- }
323
- if (!_.isString(key)) {
324
- return comp;
325
- }
326
- this.everyComponent((component, components) => {
327
- const matchPath = component.hasInput && component.path ? pathStr.includes(component.path) : true;
328
- if (component.component.key === key) {
329
- possibleComp = component;
330
- if (matchPath) {
331
- comp = component;
332
- if (remainingPath.length > 0 && 'getComponent' in component) {
333
- comp = component.getComponent(remainingPath, fn, originalPath);
334
- }
335
- else if (fn) {
336
- fn(component, components);
337
- }
338
- return false;
339
- }
340
- }
305
+ getComponent(path) {
306
+ path = getStringFromComponentPath(path);
307
+ const matches = {
308
+ path: undefined,
309
+ fullPath: undefined,
310
+ localPath: undefined,
311
+ fullLocalPath: undefined,
312
+ dataPath: undefined,
313
+ localDataPath: undefined,
314
+ key: undefined,
315
+ };
316
+ this.everyComponent((component) => {
317
+ // All searches are relative to this component so replace this path from the child paths.
318
+ componentMatches(component.component, {
319
+ path: component.paths?.path?.replace(new RegExp(`^${this.paths?.path}\\.?`), ''),
320
+ fullPath: component.paths?.fullPath?.replace(new RegExp(`^${this.paths?.fullPath}\\.?`), ''),
321
+ localPath: component.paths?.localPath?.replace(new RegExp(`^${this.paths?.localPath}\\.?`), ''),
322
+ fullLocalPath: component.paths?.fullLocalPath?.replace(new RegExp(`^${this.paths?.fullLocalPath}\\.?`), ''),
323
+ dataPath: component.paths?.dataPath?.replace(new RegExp(`^${this.paths?.dataPath}\\.?`), ''),
324
+ localDataPath: component.paths?.localDataPath?.replace(new RegExp(`^${this.paths?.localDataPath}\\.?`), ''),
325
+ }, path, this.rowIndex, matches, (type, match) => {
326
+ match.instance = component;
327
+ return match;
328
+ });
341
329
  });
342
- if (!comp) {
343
- comp = possibleComp;
344
- }
345
- return comp;
330
+ return getBestMatch(matches)?.instance;
346
331
  }
347
332
  /**
348
333
  * Return a component provided the Id of the component.
@@ -678,19 +663,16 @@ export default class NestedComponent extends Field {
678
663
  isValid(data, dirty) {
679
664
  return this.getComponents().reduce((valid, comp) => comp.isValid(data, dirty) && valid, super.isValid(data, dirty));
680
665
  }
681
- validationProcessor({ scope, data, row, instance, component }, flags) {
666
+ validationProcessor({ scope, data, row, instance, paths }, flags) {
682
667
  const { dirty } = flags;
683
- if (this.root.hasSubWizards && this.page !== this.root.page) {
684
- instance = this.childComponentsMap?.hasOwnProperty(component.path)
685
- ? this.childComponentsMap[component.path]
686
- : this.getComponent(component.path);
668
+ if (this.root.hasExtraPages && this.page !== this.root.page) {
669
+ instance = this.componentsMap?.hasOwnProperty(paths.dataPath)
670
+ ? this.componentsMap[paths.dataPath]
671
+ : this.getComponent(paths.dataPath);
687
672
  }
688
673
  if (!instance) {
689
674
  return;
690
675
  }
691
- if (!instance.component.path) {
692
- instance.component.path = component.path;
693
- }
694
676
  instance.checkComponentValidity(data, dirty, row, flags, scope.errors);
695
677
  if (instance.processOwnValidation) {
696
678
  scope.noRecurse = true;
@@ -722,7 +704,10 @@ export default class NestedComponent extends Field {
722
704
  components,
723
705
  instances: this.componentsMap,
724
706
  data: data,
707
+ local: !!flags.local,
725
708
  scope: { errors: [] },
709
+ parent: this.component,
710
+ parentPaths: this.paths,
726
711
  processors: [
727
712
  {
728
713
  process: validationProcessorProcess,