@formio/js 5.4.0-api98.1 → 5.4.0

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 (122) hide show
  1. package/dist/formio.builder.css +15 -0
  2. package/dist/formio.builder.min.css +1 -1
  3. package/dist/formio.embed.js +1 -1
  4. package/dist/formio.embed.min.js +1 -1
  5. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  6. package/dist/formio.form.css +15 -0
  7. package/dist/formio.form.js +917 -897
  8. package/dist/formio.form.min.css +1 -1
  9. package/dist/formio.form.min.js +1 -1
  10. package/dist/formio.form.min.js.LICENSE.txt +2 -2
  11. package/dist/formio.full.css +15 -0
  12. package/dist/formio.full.js +1211 -1191
  13. package/dist/formio.full.min.css +1 -1
  14. package/dist/formio.full.min.js +1 -1
  15. package/dist/formio.full.min.js.LICENSE.txt +2 -2
  16. package/dist/formio.js +832 -812
  17. package/dist/formio.min.js +1 -1
  18. package/dist/formio.min.js.LICENSE.txt +2 -2
  19. package/dist/formio.utils.js +774 -754
  20. package/dist/formio.utils.min.js +1 -1
  21. package/dist/formio.utils.min.js.LICENSE.txt +2 -2
  22. package/lib/cjs/Element.d.ts +11 -0
  23. package/lib/cjs/Element.js +24 -0
  24. package/lib/cjs/Embed.js +23 -2
  25. package/lib/cjs/Form.d.ts +1 -1
  26. package/lib/cjs/Formio.js +1 -1
  27. package/lib/cjs/PDFBuilder.js +6 -1
  28. package/lib/cjs/Webform.d.ts +1 -1
  29. package/lib/cjs/Webform.js +9 -6
  30. package/lib/cjs/WebformBuilder.js +14 -1
  31. package/lib/cjs/Wizard.js +15 -11
  32. package/lib/cjs/components/Components.d.ts +3 -0
  33. package/lib/cjs/components/Components.js +3 -1
  34. package/lib/cjs/components/_classes/component/Component.d.ts +8 -0
  35. package/lib/cjs/components/_classes/component/Component.js +121 -42
  36. package/lib/cjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
  37. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +1 -1
  38. package/lib/cjs/components/_classes/component/editForm/utils.d.ts +1 -0
  39. package/lib/cjs/components/_classes/component/editForm/utils.js +3 -0
  40. package/lib/cjs/components/_classes/nested/NestedComponent.js +5 -7
  41. package/lib/cjs/components/address/Address.js +2 -0
  42. package/lib/cjs/components/datagrid/DataGrid.js +31 -5
  43. package/lib/cjs/components/datamap/DataMap.d.ts +1 -4
  44. package/lib/cjs/components/datamap/DataMap.js +42 -10
  45. package/lib/cjs/components/datetime/DateTime.js +11 -1
  46. package/lib/cjs/components/datetime/editForm/DateTime.edit.date.d.ts +18 -1
  47. package/lib/cjs/components/datetime/editForm/DateTime.edit.date.js +3 -0
  48. package/lib/cjs/components/datetime/editForm/DateTime.edit.time.d.ts +13 -2
  49. package/lib/cjs/components/datetime/editForm/DateTime.edit.time.js +3 -0
  50. package/lib/cjs/components/day/Day.d.ts +0 -15
  51. package/lib/cjs/components/day/Day.js +8 -17
  52. package/lib/cjs/components/editgrid/EditGrid.js +13 -3
  53. package/lib/cjs/components/file/File.js +7 -6
  54. package/lib/cjs/components/form/Form.d.ts +1 -0
  55. package/lib/cjs/components/form/Form.js +20 -8
  56. package/lib/cjs/components/number/Number.d.ts +1 -0
  57. package/lib/cjs/components/number/Number.js +18 -0
  58. package/lib/cjs/components/select/Select.js +4 -0
  59. package/lib/cjs/components/signature/Signature.js +5 -5
  60. package/lib/cjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
  61. package/lib/cjs/components/signature/editForm/Signature.edit.display.js +0 -1
  62. package/lib/cjs/components/textfield/editForm/TextField.edit.display.d.ts +0 -10
  63. package/lib/cjs/components/textfield/editForm/TextField.edit.display.js +9 -23
  64. package/lib/cjs/formio.form.js +2 -5
  65. package/lib/cjs/package.json +1 -1
  66. package/lib/cjs/providers/storage/azure.js +9 -3
  67. package/lib/cjs/utils/formUtils.d.ts +2 -2
  68. package/lib/cjs/utils/index.d.ts +3 -3
  69. package/lib/cjs/utils/utils.d.ts +1 -1
  70. package/lib/cjs/utils/utils.js +23 -6
  71. package/lib/cjs/widgets/CalendarWidget.js +1 -1
  72. package/lib/mjs/Element.d.ts +11 -0
  73. package/lib/mjs/Element.js +23 -0
  74. package/lib/mjs/Embed.js +21 -2
  75. package/lib/mjs/Form.d.ts +1 -1
  76. package/lib/mjs/Formio.js +1 -1
  77. package/lib/mjs/PDFBuilder.js +6 -1
  78. package/lib/mjs/Webform.d.ts +1 -1
  79. package/lib/mjs/Webform.js +6 -3
  80. package/lib/mjs/WebformBuilder.js +13 -1
  81. package/lib/mjs/Wizard.js +9 -10
  82. package/lib/mjs/components/Components.d.ts +3 -0
  83. package/lib/mjs/components/Components.js +3 -1
  84. package/lib/mjs/components/_classes/component/Component.d.ts +8 -0
  85. package/lib/mjs/components/_classes/component/Component.js +119 -41
  86. package/lib/mjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
  87. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +1 -1
  88. package/lib/mjs/components/_classes/component/editForm/utils.d.ts +1 -0
  89. package/lib/mjs/components/_classes/component/editForm/utils.js +3 -0
  90. package/lib/mjs/components/_classes/nested/NestedComponent.js +5 -6
  91. package/lib/mjs/components/address/Address.js +2 -0
  92. package/lib/mjs/components/datagrid/DataGrid.js +34 -5
  93. package/lib/mjs/components/datamap/DataMap.d.ts +1 -4
  94. package/lib/mjs/components/datamap/DataMap.js +41 -10
  95. package/lib/mjs/components/datetime/DateTime.js +11 -1
  96. package/lib/mjs/components/datetime/editForm/DateTime.edit.date.d.ts +18 -1
  97. package/lib/mjs/components/datetime/editForm/DateTime.edit.date.js +3 -0
  98. package/lib/mjs/components/datetime/editForm/DateTime.edit.time.d.ts +13 -2
  99. package/lib/mjs/components/datetime/editForm/DateTime.edit.time.js +3 -0
  100. package/lib/mjs/components/day/Day.d.ts +0 -15
  101. package/lib/mjs/components/day/Day.js +8 -17
  102. package/lib/mjs/components/editgrid/EditGrid.js +12 -2
  103. package/lib/mjs/components/file/File.js +7 -6
  104. package/lib/mjs/components/form/Form.d.ts +1 -0
  105. package/lib/mjs/components/form/Form.js +18 -6
  106. package/lib/mjs/components/number/Number.d.ts +1 -0
  107. package/lib/mjs/components/number/Number.js +17 -0
  108. package/lib/mjs/components/select/Select.js +4 -0
  109. package/lib/mjs/components/signature/Signature.js +1 -1
  110. package/lib/mjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
  111. package/lib/mjs/components/signature/editForm/Signature.edit.display.js +0 -1
  112. package/lib/mjs/components/textfield/editForm/TextField.edit.display.d.ts +0 -10
  113. package/lib/mjs/components/textfield/editForm/TextField.edit.display.js +9 -23
  114. package/lib/mjs/formio.form.js +4 -7
  115. package/lib/mjs/package.json +1 -1
  116. package/lib/mjs/providers/storage/azure.js +9 -3
  117. package/lib/mjs/utils/formUtils.d.ts +2 -2
  118. package/lib/mjs/utils/index.d.ts +3 -3
  119. package/lib/mjs/utils/utils.d.ts +1 -1
  120. package/lib/mjs/utils/utils.js +22 -6
  121. package/lib/mjs/widgets/CalendarWidget.js +2 -2
  122. package/package.json +7 -6
@@ -140,7 +140,7 @@ export default class Component extends Element {
140
140
  unique: false,
141
141
  },
142
142
  /**
143
- * The simple conditional settings for a component.
143
+ * the simple conditional settings for a component.
144
144
  */
145
145
  conditional: {
146
146
  show: null,
@@ -426,6 +426,24 @@ export default class Component extends Element {
426
426
  get componentsMap() {
427
427
  return this.root?.childComponentsMap || {};
428
428
  }
429
+ /**
430
+ * Walks this component's root chain, invoking `fn` with each ancestor root's
431
+ * `childComponentsMap`. Component registration is propagated up the wizard /
432
+ * nested-form chain at create time, so any code that mutates a registration
433
+ * (creation, removal, path-driven re-key) must update every map in the chain.
434
+ * @param {(map: object) => void} fn - Called once per root that exposes a `childComponentsMap`.
435
+ */
436
+ eachRootChildComponentsMap(fn) {
437
+ let currentRoot = this.root;
438
+ let prevRootId = null;
439
+ while (currentRoot && currentRoot.id !== prevRootId) {
440
+ if (currentRoot.childComponentsMap) {
441
+ fn(currentRoot.childComponentsMap);
442
+ }
443
+ prevRootId = currentRoot.id;
444
+ currentRoot = currentRoot.root;
445
+ }
446
+ }
429
447
  /**
430
448
  * Returns if the parent should conditionally clear.
431
449
  *
@@ -1127,9 +1145,12 @@ export default class Component extends Element {
1127
1145
  * @returns {string} - The submission timezone.
1128
1146
  */
1129
1147
  get submissionTimezone() {
1130
- this.options.submissionTimezone =
1131
- this.options.submissionTimezone || _.get(this.root, 'options.submissionTimezone');
1132
- return this.options.submissionTimezone;
1148
+ if (!this.options.submissionTimezone) {
1149
+ this.options.submissionTimezone = _.get(this.root, 'options.submissionTimezone');
1150
+ }
1151
+ return (this.options.submissionTimezone ||
1152
+ _.get(this.root, '_submission.metadata.timezone') ||
1153
+ _.get(this.component, 'widget.submissionTimezone'));
1133
1154
  }
1134
1155
  /**
1135
1156
  * Return the current timezone.
@@ -1144,6 +1165,7 @@ export default class Component extends Element {
1144
1165
  * @returns {string} - The current timezone.
1145
1166
  */
1146
1167
  getTimezone(settings) {
1168
+ settings = settings || {};
1147
1169
  if (settings.timezone) {
1148
1170
  return settings.timezone;
1149
1171
  }
@@ -1151,12 +1173,19 @@ export default class Component extends Element {
1151
1173
  return 'UTC';
1152
1174
  }
1153
1175
  const submissionTimezone = this.submissionTimezone;
1176
+ const mode = settings.displayInTimezone === '' || settings.displayInTimezone == null
1177
+ ? 'viewer'
1178
+ : settings.displayInTimezone;
1179
+ if (this.options.pdf && submissionTimezone) {
1180
+ return submissionTimezone;
1181
+ }
1182
+ const staticSnapshot = this.options.server ||
1183
+ this.options.renderMode === 'html' ||
1184
+ !!this.options.viewAsHtml;
1154
1185
  if (submissionTimezone &&
1155
- (settings.displayInTimezone === 'submission' ||
1156
- ((this.options.pdf || this.options.server) && settings.displayInTimezone === 'viewer'))) {
1186
+ (mode === 'submission' || (staticSnapshot && (mode === 'viewer' || mode === 'location')))) {
1157
1187
  return submissionTimezone;
1158
1188
  }
1159
- // Return current timezone if none are provided.
1160
1189
  return currentTimezone();
1161
1190
  }
1162
1191
  /**
@@ -2304,29 +2333,23 @@ export default class Component extends Element {
2304
2333
  * @returns {void}
2305
2334
  */
2306
2335
  setErrorClasses(elements, dirty, hasErrors, hasMessages, element = this.element) {
2307
- this.clearErrorClasses();
2308
- elements.forEach((element) => {
2309
- this.setElementInvalid(this.performInputMapping(element), hasErrors);
2336
+ elements.forEach((el) => {
2337
+ this.setElementInvalid(this.performInputMapping(el), hasErrors);
2310
2338
  });
2311
2339
  this.setInputWidgetErrorClasses(elements, hasErrors);
2312
2340
  // do not set error classes for hidden components
2313
2341
  if (!this.visible) {
2342
+ this.clearErrorClasses(element);
2314
2343
  return;
2315
2344
  }
2316
- if (hasErrors) {
2317
- // Add error classes
2318
- elements.forEach((input) => {
2319
- this.setElementInvalid(this.performInputMapping(input), true);
2320
- });
2321
- if (dirty && this.options.highlightErrors) {
2322
- this.addClass(element, this.options.componentErrorClass);
2323
- }
2324
- else {
2325
- this.addClass(element, 'has-error');
2326
- }
2327
- }
2328
- if (hasMessages) {
2329
- this.addClass(element, 'has-message');
2345
+ const wantHighlight = hasErrors && !!dirty && !!this.options.highlightErrors;
2346
+ const wantHasError = hasErrors && !wantHighlight;
2347
+ this.toggleClass(element, this.options.componentErrorClass, wantHighlight);
2348
+ this.toggleClass(element, 'has-error', wantHasError);
2349
+ this.toggleClass(element, 'has-message', hasMessages);
2350
+ // Preserve previous clearErrorClasses() behavior: drop the 'alert alert-danger' pair if left over.
2351
+ if (element?.classList?.contains('alert-danger')) {
2352
+ this.removeClass(element, 'alert alert-danger');
2330
2353
  }
2331
2354
  }
2332
2355
  /**
@@ -2582,12 +2605,61 @@ export default class Component extends Element {
2582
2605
  { type: 'styles', src: `${Formio.cdn.quill}/quill.${settings.theme}.css` },
2583
2606
  ], true);
2584
2607
  // Lazy load the quill library.
2585
- return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.min.js`), true).then(() => {
2608
+ return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.js`), true).then(() => {
2586
2609
  return Formio.requireLibrary('quill-table', 'Quill', `${Formio.cdn.baseUrl}/quill/quill-table.js`, true).then(() => {
2587
2610
  if (!element.parentNode) {
2588
2611
  return Promise.reject();
2589
2612
  }
2590
2613
  this.quill = new Quill(element, isIEBrowser ? { ...settings, modules: {} } : settings);
2614
+ const root = element.getRootNode();
2615
+ if (root instanceof ShadowRoot && root.getSelection) {
2616
+ const sel = this.quill.selection;
2617
+ // 1. getNativeRange: read selection from shadowRoot instead of document
2618
+ sel.getNativeRange = () => {
2619
+ const shadowSelection = root.getSelection();
2620
+ if (shadowSelection == null || shadowSelection.rangeCount <= 0)
2621
+ return null;
2622
+ const nativeRange = shadowSelection.getRangeAt(0);
2623
+ if (nativeRange == null)
2624
+ return null;
2625
+ return sel.normalizeNative(nativeRange);
2626
+ };
2627
+ // 2. hasFocus: check shadowRoot.activeElement instead of document.activeElement
2628
+ sel.hasFocus = () => {
2629
+ return root.activeElement === sel.root ||
2630
+ (root.activeElement != null && sel.root.contains(root.activeElement));
2631
+ };
2632
+ // 3. setNativeRange: use shadowRoot's selection to add/remove ranges
2633
+ const origSetNativeRange = sel.setNativeRange.bind(sel);
2634
+ sel.setNativeRange = (startNode, startOffset, endNode, endOffset, force) => {
2635
+ // Delegate to original logic for the null case (blur)
2636
+ if (startNode == null) {
2637
+ origSetNativeRange(null);
2638
+ return;
2639
+ }
2640
+ if (!sel.hasFocus()) {
2641
+ sel.root.focus({ preventScroll: true });
2642
+ }
2643
+ const shadowSelection = root.getSelection();
2644
+ if (shadowSelection == null)
2645
+ return;
2646
+ const { native } = sel.getNativeRange() || {};
2647
+ endNode = endNode ?? startNode;
2648
+ endOffset = endOffset ?? startOffset;
2649
+ if (native == null || force ||
2650
+ startNode !== native.startContainer || startOffset !== native.startOffset ||
2651
+ endNode !== native.endContainer || endOffset !== native.endOffset) {
2652
+ const range = document.createRange();
2653
+ range.setStart(startNode, startOffset);
2654
+ range.setEnd(endNode, endOffset);
2655
+ shadowSelection.removeAllRanges();
2656
+ shadowSelection.addRange(range);
2657
+ }
2658
+ };
2659
+ document.addEventListener('selectionchange', () => {
2660
+ sel.update();
2661
+ });
2662
+ }
2591
2663
  /** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ */
2592
2664
  const txtArea = document.createElement('textarea');
2593
2665
  txtArea.setAttribute('class', 'quill-source-code');
@@ -2606,7 +2678,8 @@ export default class Component extends Element {
2606
2678
  // Make sure to select cursor when they click on the element.
2607
2679
  this.addEventListener(element, 'click', () => this.quill.focus());
2608
2680
  // Allows users to skip toolbar items when tabbing though form
2609
- const elm = document.querySelectorAll('.ql-formats > button');
2681
+ const queryRoot = element.getRootNode() || document;
2682
+ const elm = queryRoot.querySelectorAll('.ql-formats > button');
2610
2683
  for (let i = 0; i < elm.length; i++) {
2611
2684
  elm[i].setAttribute('tabindex', '-1');
2612
2685
  }
@@ -3234,7 +3307,7 @@ export default class Component extends Element {
3234
3307
  if (flags.silentCheck) {
3235
3308
  return [];
3236
3309
  }
3237
- let isDirty = flags.dirty === false ? false : this.dirty || flags.dirty;
3310
+ let isDirty = flags.dirty || this.dirty;
3238
3311
  if (this.options.alwaysDirty) {
3239
3312
  isDirty = true;
3240
3313
  }
@@ -3440,21 +3513,26 @@ export default class Component extends Element {
3440
3513
  }
3441
3514
  });
3442
3515
  this.addEventListener(element, 'blur', () => {
3443
- if (this.root) {
3444
- this.root.pendingBlur = FormioUtils.delay(() => {
3445
- this.emit('blur', this);
3446
- if (this.component.validateOn === 'blur') {
3447
- this.root.triggerChange?.({ fromBlur: true }, {
3448
- instance: this,
3449
- component: this.component,
3450
- value: this.dataValue,
3451
- flags: { fromBlur: true },
3452
- });
3453
- }
3454
- this.root.focusedComponent = null;
3455
- this.root.pendingBlur = null;
3456
- });
3516
+ const root = this.root;
3517
+ if (!root) {
3518
+ return;
3457
3519
  }
3520
+ root.pendingBlur = FormioUtils.delay(() => {
3521
+ if (!root) {
3522
+ return;
3523
+ }
3524
+ this.emit('blur', this);
3525
+ if (this.component.validateOn === 'blur') {
3526
+ root.triggerChange?.({ fromBlur: true }, {
3527
+ instance: this,
3528
+ component: this.component,
3529
+ value: this.dataValue,
3530
+ flags: { fromBlur: true },
3531
+ });
3532
+ }
3533
+ root.focusedComponent = null;
3534
+ root.pendingBlur = null;
3535
+ });
3458
3536
  });
3459
3537
  }
3460
3538
  setCustomValidity(messages, dirty, external) {
@@ -44,5 +44,5 @@ export default [
44
44
  },
45
45
  EditFormUtils.javaScriptValue('Advanced Conditions', 'customConditional', 'conditional.json', 110, '<p>You must assign the <strong>show</strong> variable a boolean result.</p>' +
46
46
  '<p><strong>Note: Advanced Conditional logic will override the results of the Simple Conditional logic.</strong></p>' +
47
- '<h5>Example</h5><pre>show = !!data.showMe;</pre>', '<p><a href="https://help.form.io/userguide/form-building/logic-and-conditions" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>'),
47
+ '<h5>Example</h5><pre>show = !!data.showMe;</pre>', '<p><a href="https://help.form.io/userguide/form-building/logic-and-conditions" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>', EditFormUtils.tokenVariableDescription()),
48
48
  ];
@@ -139,7 +139,7 @@ export default [
139
139
  input: true,
140
140
  },
141
141
  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>'),
142
- EditFormUtils.javaScriptValue('Calculated Value', 'calculateValue', 'calculateValue', 1100, '<p><h4>Example:</h4><pre>value = data.a + data.b + data.c;</pre></p>', '<p><h4>Example:</h4><pre>{"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}</pre><p><a href="https://help.form.io/userguide/form-building/logic-and-conditions#calculated-values" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>', '<tr><th>token</th><td>The decoded JWT token for the authenticated user.</td></tr>'),
142
+ EditFormUtils.javaScriptValue('Calculated Value', 'calculateValue', 'calculateValue', 1100, '<p><h4>Example:</h4><pre>value = data.a + data.b + data.c;</pre></p>', '<p><h4>Example:</h4><pre>{"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}</pre><p><a href="https://help.form.io/userguide/form-building/logic-and-conditions#calculated-values" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>', EditFormUtils.tokenVariableDescription()),
143
143
  {
144
144
  type: 'checkbox',
145
145
  input: true,
@@ -2,6 +2,7 @@ export default EditFormUtils;
2
2
  declare namespace EditFormUtils {
3
3
  function sortAndFilterComponents(components: any): any;
4
4
  function unifyComponents(objValue: any, srcValue: any): any;
5
+ function tokenVariableDescription(): string;
5
6
  function logicVariablesTable(additional: any): {
6
7
  type: string;
7
8
  tag: string;
@@ -32,6 +32,9 @@ const EditFormUtils = {
32
32
  }
33
33
  return _.isEqual(objValue, srcValue);
34
34
  },
35
+ tokenVariableDescription() {
36
+ return '<tr><th>token</th><td>The decoded JWT token for the authenticated user.</td></tr>';
37
+ },
35
38
  logicVariablesTable(additional) {
36
39
  additional = additional || '';
37
40
  return {
@@ -556,12 +556,11 @@ export default class NestedComponent extends Field {
556
556
  components = components || this.components;
557
557
  component.destroy(all);
558
558
  _.remove(components, { id: component.id });
559
- if (this.componentsMap[component.path]) {
560
- delete this.componentsMap[component.path];
561
- }
562
- if (this.root?.componentsMap[component.path]) {
563
- delete this.root?.componentsMap[component.path];
564
- }
559
+ component.eachRootChildComponentsMap((map) => {
560
+ if (map[component.path]) {
561
+ delete map[component.path];
562
+ }
563
+ });
565
564
  }
566
565
  /**
567
566
  * Removes a component provided the API key of that component.
@@ -261,6 +261,8 @@ export default class AddressComponent extends ContainerComponent {
261
261
  this.restoreComponentsContext();
262
262
  }
263
263
  if (changed || (!_.isEmpty(value) && flags.fromSubmission)) {
264
+ // Recheck conditions on child components before redraw so their visibility is updated
265
+ this.getComponents().forEach((comp) => comp.checkConditions(this.root?.data));
264
266
  this.redraw();
265
267
  }
266
268
  return changed;
@@ -450,8 +450,16 @@ export default class DataGridComponent extends NestedArrayComponent {
450
450
  }
451
451
  updateComponentsRowIndex(components, rowIndex) {
452
452
  components.forEach((component, colIndex) => {
453
- if (this.componentsMap[component.paths.dataPath]) {
454
- delete this.componentsMap[component.paths.dataPath];
453
+ // The rowIndex setter cascades into descendants and regenerates their
454
+ // paths, but does not re-key them in componentsMap. Collect the slot
455
+ // and every descendant up front so we can re-key them after paths
456
+ // regenerate. Required for nested-form / sub-wizard scenarios where
457
+ // the outer wizard validates against its own componentsMap copy.
458
+ const entries = [{ instance: component, oldPath: component.paths.dataPath }];
459
+ if (typeof component.everyComponent === 'function') {
460
+ component.everyComponent((descendant) => {
461
+ entries.push({ instance: descendant, oldPath: descendant.paths.dataPath });
462
+ });
455
463
  }
456
464
  if (component.options?.name) {
457
465
  const newName = `[${this.key}][${rowIndex}]`;
@@ -459,7 +467,14 @@ export default class DataGridComponent extends NestedArrayComponent {
459
467
  }
460
468
  component.rowIndex = rowIndex;
461
469
  component.row = `${rowIndex}-${colIndex}`;
462
- this.componentsMap[component.paths.dataPath] = component;
470
+ entries.forEach(({ instance, oldPath }) => {
471
+ instance.eachRootChildComponentsMap((map) => {
472
+ if (map[oldPath] === instance) {
473
+ delete map[oldPath];
474
+ }
475
+ map[instance.paths.dataPath] = instance;
476
+ });
477
+ });
463
478
  });
464
479
  }
465
480
  updateRowsComponents(rowIndex) {
@@ -530,8 +545,9 @@ export default class DataGridComponent extends NestedArrayComponent {
530
545
  options.row = `${rowIndex}-${colIndex}`;
531
546
  options.rowIndex = rowIndex;
532
547
  options.onChange = (flags, changed, modified) => {
533
- if (changed.component.type === 'form') {
534
- const formComp = getComponent(this.component.components, changed.component.key);
548
+ const changedComponent = changed.component;
549
+ if (changedComponent?.type === 'form' && changedComponent?.key) {
550
+ const formComp = getComponent(this.component.components, changedComponent.key);
535
551
  _.set(formComp, 'components', changed.component.components);
536
552
  }
537
553
  // If we're in a nested form we need to ensure our changes are triggered upstream
@@ -539,8 +555,21 @@ export default class DataGridComponent extends NestedArrayComponent {
539
555
  changed.instance.root.triggerChange?.(flags, changed, modified);
540
556
  }
541
557
  else {
558
+ if (modified && !flags.noPristineChangeOnModified) {
559
+ this.pristine = false;
560
+ }
561
+ this.triggerRootChange(flags, {
562
+ instance: this,
563
+ component: this.component,
564
+ value: this.dataValue,
565
+ flags,
566
+ }, modified);
542
567
  this.triggerRootChange(flags, changed, modified);
543
568
  }
569
+ this.processRow('checkData', null, {
570
+ ...flags,
571
+ changed,
572
+ }, row, _.toArray(this.rows[rowIndex]));
544
573
  };
545
574
  let columnComponent;
546
575
  if (this.builderMode) {
@@ -15,14 +15,11 @@ export default class DataMapComponent extends DataGridComponent {
15
15
  disableBuilderActions: boolean;
16
16
  };
17
17
  get valueKey(): any;
18
- get iteratableRows(): {
19
- components: any;
20
- data: any;
21
- }[][];
22
18
  hasHeader(): boolean;
23
19
  getRowKey(rowIndex: any): string;
24
20
  get defaultRowKey(): string;
25
21
  getValueAsString(value: any, options: any): any;
22
+ findComponentInstance(key: any): any;
26
23
  createRowComponents(row: any, rowIndex: any): {
27
24
  __key: any;
28
25
  };
@@ -118,15 +118,14 @@ export default class DataMapComponent extends DataGridComponent {
118
118
  }
119
119
  get iteratableRows() {
120
120
  return this.rows.map((row) => {
121
- return Object.keys(row).map((key) => ({
122
- components: row[key],
123
- data: row[key].dataValue,
124
- }));
121
+ return {
122
+ components: row,
123
+ data: _.mapValues(row, (comp) => comp.dataValue)
124
+ };
125
125
  });
126
126
  }
127
127
  componentContext(component) {
128
- return this.iteratableRows[component.row].find((comp) => comp.components.key === component.key)
129
- .data;
128
+ return this.iteratableRows[component.row].data[component.key];
130
129
  }
131
130
  hasHeader() {
132
131
  return true;
@@ -181,10 +180,14 @@ export default class DataMapComponent extends DataGridComponent {
181
180
  <tbody>
182
181
  `;
183
182
  result = Object.keys(value).reduce((result, key) => {
183
+ const componentInstance = this.findComponentInstance(key);
184
+ const viewValue = componentInstance
185
+ ? componentInstance.getView(value[key], options)
186
+ : this.getView(value[key], options);
184
187
  result += `
185
188
  <tr>
186
189
  <th style="padding: 5px 10px;">${key}</th>
187
- <td style="width:100%;padding:5px 10px;">${this.getView(value[key], options)}</td>
190
+ <td style="width:100%;padding:5px 10px;">${viewValue}</td>
188
191
  </tr>
189
192
  `;
190
193
  return result;
@@ -204,6 +207,18 @@ export default class DataMapComponent extends DataGridComponent {
204
207
  }
205
208
  return typeof value === 'object' ? '[Complex Data]' : value;
206
209
  }
210
+ findComponentInstance(key) {
211
+ if (!this.rows || !this.rows.length) {
212
+ return null;
213
+ }
214
+ // Find component instance with matching key
215
+ const foundRow = _.find(this.rows, (row) => row?.[this.valueKey]?.key === key);
216
+ if (foundRow?.[this.valueKey]) {
217
+ return foundRow[this.valueKey];
218
+ }
219
+ // If not found by key, return the first row's value component as fallback
220
+ return this.rows[0]?.[this.valueKey] || null;
221
+ }
207
222
  getDataValueAsTable(value, options) {
208
223
  let result = `
209
224
  <table border="1" style="width:100%">
@@ -211,10 +226,14 @@ export default class DataMapComponent extends DataGridComponent {
211
226
  `;
212
227
  if (this.visible && _.isObject(value)) {
213
228
  Object.keys(value).forEach((key) => {
229
+ const componentInstance = this.findComponentInstance(key);
230
+ const viewValue = componentInstance
231
+ ? componentInstance.getView(value[key], options)
232
+ : this.getView(value[key], options);
214
233
  result += `
215
234
  <tr>
216
235
  <th style="padding: 5px 10px;">${key}</th>
217
- <td style="width:100%;padding:5px 10px;">${this.getView(value[key], options)}</td>
236
+ <td style="width:100%;padding:5px 10px;">${viewValue}</td>
218
237
  </tr>
219
238
  `;
220
239
  });
@@ -249,9 +268,21 @@ export default class DataMapComponent extends DataGridComponent {
249
268
  });
250
269
  const valueComponent = _.clone(this.component.valueComponent);
251
270
  valueComponent.key = key;
252
- const componentOptions = this.options;
271
+ const componentOptions = _.clone(this.options);
253
272
  componentOptions.row = options.row;
254
- components[this.valueKey] = this.createComponent(valueComponent, componentOptions, this.dataValue);
273
+ if (this.submissionTimezone) {
274
+ componentOptions.submissionTimezone = this.submissionTimezone;
275
+ if (valueComponent.type === 'datetime') {
276
+ valueComponent.widget = { ...valueComponent.widget, submissionTimezone: this.submissionTimezone };
277
+ }
278
+ }
279
+ const createdComponent = this.createComponent(valueComponent, componentOptions, this.dataValue);
280
+ // Ensure submissionTimezone is set on datetime component instance's widget and options
281
+ if (createdComponent?.type === 'datetime' && this.submissionTimezone) {
282
+ createdComponent.component.widget = { ...createdComponent.component.widget, submissionTimezone: this.submissionTimezone };
283
+ createdComponent.options.submissionTimezone = this.submissionTimezone;
284
+ }
285
+ components[this.valueKey] = createdComponent;
255
286
  return components;
256
287
  }
257
288
  get canAddColumn() {
@@ -163,6 +163,13 @@ export default class DateTimeComponent extends Input {
163
163
  get momentFormat() {
164
164
  return FormioUtils.convertFormatToMoment(this.component.format);
165
165
  }
166
+ get timezone() {
167
+ const widget = this.component.widget;
168
+ if (widget && widget.type === 'calendar') {
169
+ return this.getTimezone(widget);
170
+ }
171
+ return super.timezone;
172
+ }
166
173
  isEmpty(value = this.dataValue) {
167
174
  if (value && value.toString() === 'Invalid Date') {
168
175
  return true;
@@ -194,7 +201,10 @@ export default class DateTimeComponent extends Input {
194
201
  let format = FormioUtils.convertFormatToMoment(this.component.format);
195
202
  format += format.match(/z$/) ? '' : ' z';
196
203
  const timezone = this.timezone;
197
- if (value && !this.attached && timezone) {
204
+ const useTimezoneAwareFormat = value &&
205
+ timezone &&
206
+ (this.options.pdf || options?.email);
207
+ if (useTimezoneAwareFormat) {
198
208
  if (Array.isArray(value) && this.component.multiple) {
199
209
  return value
200
210
  .map((item) => _.trim(FormioUtils.momentDate(item, format, timezone, options).format(format)))
@@ -5,8 +5,10 @@ declare const _default: ({
5
5
  label: string;
6
6
  weight: number;
7
7
  tooltip: string;
8
+ validate: {
9
+ custom: string;
10
+ };
8
11
  placeholder?: undefined;
9
- validate?: undefined;
10
12
  title?: undefined;
11
13
  collapsible?: undefined;
12
14
  collapsed?: undefined;
@@ -57,7 +59,22 @@ declare const _default: ({
57
59
  label?: undefined;
58
60
  weight?: undefined;
59
61
  tooltip?: undefined;
62
+ validate?: undefined;
60
63
  placeholder?: undefined;
64
+ } | {
65
+ type: string;
66
+ input: boolean;
67
+ key: string;
68
+ label: string;
69
+ tooltip: string;
70
+ weight: number;
61
71
  validate?: undefined;
72
+ placeholder?: undefined;
73
+ title?: undefined;
74
+ collapsible?: undefined;
75
+ collapsed?: undefined;
76
+ style?: undefined;
77
+ customConditional?: undefined;
78
+ components?: undefined;
62
79
  })[];
63
80
  export default _default;
@@ -8,6 +8,9 @@ export default [
8
8
  label: 'Enable Date Input',
9
9
  weight: 0,
10
10
  tooltip: 'Enables date input for this field.',
11
+ validate: {
12
+ custom: "valid = !data.enableTime && !input ? 'Cannot disable both Date and Time inputs at the same time' : true",
13
+ },
11
14
  },
12
15
  {
13
16
  type: 'tags',
@@ -1,9 +1,20 @@
1
- declare const _default: {
1
+ declare const _default: ({
2
2
  type: string;
3
3
  input: boolean;
4
4
  key: string;
5
5
  label: string;
6
6
  tooltip: string;
7
7
  weight: number;
8
- }[];
8
+ validate: {
9
+ custom: string;
10
+ };
11
+ } | {
12
+ type: string;
13
+ input: boolean;
14
+ key: string;
15
+ label: string;
16
+ tooltip: string;
17
+ weight: number;
18
+ validate?: undefined;
19
+ })[];
9
20
  export default _default;
@@ -6,6 +6,9 @@ export default [
6
6
  label: 'Enable Time Input',
7
7
  tooltip: 'Enables time input for this field.',
8
8
  weight: 0,
9
+ validate: {
10
+ custom: "valid = !data.enableDate && !input ? 'Cannot disable both Date and Time inputs at the same time' : true",
11
+ },
9
12
  },
10
13
  {
11
14
  type: 'number',
@@ -57,10 +57,6 @@ export default class DayComponent extends Field {
57
57
  value: string;
58
58
  label: any;
59
59
  }[];
60
- _days: {
61
- value: string;
62
- label: any;
63
- }[] | undefined;
64
60
  get months(): ({
65
61
  value: string;
66
62
  label: any;
@@ -68,21 +64,10 @@ export default class DayComponent extends Field {
68
64
  value: number;
69
65
  label: string;
70
66
  })[];
71
- _months: ({
72
- value: string;
73
- label: any;
74
- } | {
75
- value: number;
76
- label: string;
77
- })[] | undefined;
78
67
  get years(): {
79
68
  value: string;
80
69
  label: any;
81
70
  }[];
82
- _years: {
83
- value: string;
84
- label: any;
85
- }[] | undefined;
86
71
  setErrorClasses(elements: any, dirty: any, hasError: any): void;
87
72
  dayFirst: any;
88
73
  render(): string;