@formio/js 5.3.6 → 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 (132) hide show
  1. package/dist/formio.builder.css +31 -5
  2. package/dist/formio.builder.min.css +1 -1
  3. package/dist/formio.embed.css +1 -1
  4. package/dist/formio.embed.js +1 -1
  5. package/dist/formio.embed.min.css +1 -1
  6. package/dist/formio.embed.min.js +1 -1
  7. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  8. package/dist/formio.form.css +31 -5
  9. package/dist/formio.form.js +2837 -2818
  10. package/dist/formio.form.min.css +1 -1
  11. package/dist/formio.form.min.js +1 -1
  12. package/dist/formio.form.min.js.LICENSE.txt +1 -1
  13. package/dist/formio.full.css +31 -5
  14. package/dist/formio.full.js +3442 -3403
  15. package/dist/formio.full.min.css +1 -1
  16. package/dist/formio.full.min.js +1 -1
  17. package/dist/formio.full.min.js.LICENSE.txt +1 -1
  18. package/dist/formio.js +1152 -1154
  19. package/dist/formio.min.js +1 -1
  20. package/dist/formio.min.js.LICENSE.txt +1 -1
  21. package/dist/formio.utils.js +1084 -1086
  22. package/dist/formio.utils.min.js +1 -1
  23. package/dist/formio.utils.min.js.LICENSE.txt +1 -1
  24. package/lib/cjs/Embed.js +28 -1
  25. package/lib/cjs/Form.d.ts +4 -2
  26. package/lib/cjs/Form.js +2 -2
  27. package/lib/cjs/FormBuilder.d.ts +2 -2
  28. package/lib/cjs/FormBuilder.js +1 -1
  29. package/lib/cjs/Formio.js +1 -1
  30. package/lib/cjs/PDF.js +2 -0
  31. package/lib/cjs/PDFBuilder.js +6 -1
  32. package/lib/cjs/Webform.d.ts +1 -0
  33. package/lib/cjs/Webform.js +50 -1
  34. package/lib/cjs/WebformBuilder.js +29 -1
  35. package/lib/cjs/Wizard.d.ts +1 -0
  36. package/lib/cjs/Wizard.js +11 -0
  37. package/lib/cjs/components/Components.d.ts +3 -0
  38. package/lib/cjs/components/_classes/component/Component.d.ts +13 -3
  39. package/lib/cjs/components/_classes/component/Component.js +167 -47
  40. package/lib/cjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
  41. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
  42. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +2 -1
  43. package/lib/cjs/components/_classes/component/editForm/utils.d.ts +1 -0
  44. package/lib/cjs/components/_classes/component/editForm/utils.js +3 -0
  45. package/lib/cjs/components/_classes/nested/NestedComponent.js +5 -0
  46. package/lib/cjs/components/address/Address.js +18 -0
  47. package/lib/cjs/components/datagrid/DataGrid.js +12 -2
  48. package/lib/cjs/components/datamap/DataMap.d.ts +1 -0
  49. package/lib/cjs/components/datamap/DataMap.js +37 -4
  50. package/lib/cjs/components/datetime/DateTime.js +11 -1
  51. package/lib/cjs/components/day/Day.d.ts +0 -15
  52. package/lib/cjs/components/day/Day.js +8 -17
  53. package/lib/cjs/components/editgrid/EditGrid.js +11 -1
  54. package/lib/cjs/components/fieldset/Fieldset.js +1 -0
  55. package/lib/cjs/components/file/File.d.ts +3 -1
  56. package/lib/cjs/components/file/File.js +62 -17
  57. package/lib/cjs/components/form/Form.js +3 -1
  58. package/lib/cjs/components/number/Number.d.ts +1 -0
  59. package/lib/cjs/components/number/Number.js +18 -0
  60. package/lib/cjs/components/select/Select.js +5 -1
  61. package/lib/cjs/components/signature/Signature.js +5 -5
  62. package/lib/cjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
  63. package/lib/cjs/components/signature/editForm/Signature.edit.display.js +0 -1
  64. package/lib/cjs/components/table/editForm/Table.edit.display.d.ts +27 -0
  65. package/lib/cjs/components/table/editForm/Table.edit.display.js +10 -0
  66. package/lib/cjs/formio.form.js +2 -5
  67. package/lib/cjs/package.json +1 -1
  68. package/lib/cjs/providers/storage/azure.js +9 -3
  69. package/lib/cjs/templates/index.d.ts +3 -0
  70. package/lib/cjs/translations/en.d.ts +2 -0
  71. package/lib/cjs/translations/en.js +2 -0
  72. package/lib/cjs/utils/i18n.d.ts +1 -0
  73. package/lib/cjs/utils/i18n.js +2 -0
  74. package/lib/cjs/utils/index.d.ts +1 -1
  75. package/lib/cjs/utils/utils.d.ts +1 -1
  76. package/lib/cjs/utils/utils.js +23 -6
  77. package/lib/cjs/widgets/CalendarWidget.js +1 -1
  78. package/lib/mjs/Embed.js +26 -1
  79. package/lib/mjs/Form.d.ts +4 -2
  80. package/lib/mjs/Form.js +2 -2
  81. package/lib/mjs/FormBuilder.d.ts +2 -2
  82. package/lib/mjs/FormBuilder.js +1 -1
  83. package/lib/mjs/Formio.js +1 -1
  84. package/lib/mjs/PDF.js +2 -0
  85. package/lib/mjs/PDFBuilder.js +6 -1
  86. package/lib/mjs/Webform.d.ts +1 -0
  87. package/lib/mjs/Webform.js +48 -1
  88. package/lib/mjs/WebformBuilder.js +28 -1
  89. package/lib/mjs/Wizard.d.ts +1 -0
  90. package/lib/mjs/Wizard.js +12 -1
  91. package/lib/mjs/components/Components.d.ts +3 -0
  92. package/lib/mjs/components/_classes/component/Component.d.ts +13 -3
  93. package/lib/mjs/components/_classes/component/Component.js +164 -46
  94. package/lib/mjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
  95. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
  96. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +2 -1
  97. package/lib/mjs/components/_classes/component/editForm/utils.d.ts +1 -0
  98. package/lib/mjs/components/_classes/component/editForm/utils.js +3 -0
  99. package/lib/mjs/components/_classes/nested/NestedComponent.js +5 -0
  100. package/lib/mjs/components/address/Address.js +17 -0
  101. package/lib/mjs/components/datagrid/DataGrid.js +14 -1
  102. package/lib/mjs/components/datamap/DataMap.d.ts +1 -0
  103. package/lib/mjs/components/datamap/DataMap.js +36 -4
  104. package/lib/mjs/components/datetime/DateTime.js +11 -1
  105. package/lib/mjs/components/day/Day.d.ts +0 -15
  106. package/lib/mjs/components/day/Day.js +8 -17
  107. package/lib/mjs/components/editgrid/EditGrid.js +11 -1
  108. package/lib/mjs/components/fieldset/Fieldset.js +1 -0
  109. package/lib/mjs/components/file/File.d.ts +3 -1
  110. package/lib/mjs/components/file/File.js +60 -15
  111. package/lib/mjs/components/form/Form.js +3 -1
  112. package/lib/mjs/components/number/Number.d.ts +1 -0
  113. package/lib/mjs/components/number/Number.js +17 -0
  114. package/lib/mjs/components/select/Select.js +5 -1
  115. package/lib/mjs/components/signature/Signature.js +1 -1
  116. package/lib/mjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
  117. package/lib/mjs/components/signature/editForm/Signature.edit.display.js +0 -1
  118. package/lib/mjs/components/table/editForm/Table.edit.display.d.ts +27 -0
  119. package/lib/mjs/components/table/editForm/Table.edit.display.js +10 -0
  120. package/lib/mjs/formio.form.js +4 -7
  121. package/lib/mjs/package.json +1 -1
  122. package/lib/mjs/providers/storage/azure.js +9 -3
  123. package/lib/mjs/templates/index.d.ts +3 -0
  124. package/lib/mjs/translations/en.d.ts +2 -0
  125. package/lib/mjs/translations/en.js +2 -0
  126. package/lib/mjs/utils/i18n.d.ts +1 -0
  127. package/lib/mjs/utils/i18n.js +2 -0
  128. package/lib/mjs/utils/index.d.ts +1 -1
  129. package/lib/mjs/utils/utils.d.ts +1 -1
  130. package/lib/mjs/utils/utils.js +22 -6
  131. package/lib/mjs/widgets/CalendarWidget.js +2 -2
  132. package/package.json +8 -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,
@@ -460,6 +460,16 @@ export default class Component extends Element {
460
460
  }
461
461
  return false;
462
462
  }
463
+ hasCondionallyHiddenLayoutParent() {
464
+ let currentParent = this.parent;
465
+ while (currentParent) {
466
+ if (currentParent._conditionallyHidden && FormioUtils.isLayoutComponent(currentParent) && currentParent.component.clearOnHide === true) {
467
+ return true;
468
+ }
469
+ currentParent = currentParent.parent;
470
+ }
471
+ return false;
472
+ }
463
473
  parentConditionallyHidden() {
464
474
  let currentParent = this.parent;
465
475
  while (currentParent) {
@@ -735,7 +745,7 @@ export default class Component extends Element {
735
745
  this._conditionallyClear = true;
736
746
  return this._conditionallyClear;
737
747
  }
738
- this._conditionallyClear = this.hasSetValue ? false : this.parentShouldConditionallyClear();
748
+ this._conditionallyClear = this.hasSetValue ? this.hasCondionallyHiddenLayoutParent() : this.parentShouldConditionallyClear();
739
749
  return this._conditionallyClear;
740
750
  }
741
751
  /**
@@ -990,8 +1000,20 @@ export default class Component extends Element {
990
1000
  return this.options.renderMode === 'html';
991
1001
  }
992
1002
  renderTemplate(name, data = {}, modeOption = '') {
993
- // Need to make this fall back to form if renderMode is not found similar to how we search templates.
1003
+ // Allow more specific template names
1004
+ const names = [
1005
+ `${name}-${this.component.type}-${this.key}`,
1006
+ `${name}-${this.component.type}`,
1007
+ `${name}-${this.key}`,
1008
+ `${name}`,
1009
+ ];
1010
+ // Allow template alters.
994
1011
  const mode = modeOption || this.options.renderMode || 'form';
1012
+ const { referenceAttributeName, template } = this.getTemplate(names, mode);
1013
+ if (referenceAttributeName) {
1014
+ this._referenceAttributeName = referenceAttributeName;
1015
+ }
1016
+ // Need to make this fall back to form if renderMode is not found similar to how we search templates.
995
1017
  data.component = this.component;
996
1018
  data.self = this;
997
1019
  data.options = this.options;
@@ -1013,18 +1035,7 @@ export default class Component extends Element {
1013
1035
  };
1014
1036
  data.label = data.labelInfo || this.labelInfo;
1015
1037
  data.tooltip = this.getFormattedTooltip(this.component.tooltip);
1016
- // Allow more specific template names
1017
- const names = [
1018
- `${name}-${this.component.type}-${this.key}`,
1019
- `${name}-${this.component.type}`,
1020
- `${name}-${this.key}`,
1021
- `${name}`,
1022
- ];
1023
- // Allow template alters.
1024
- const { referenceAttributeName, template } = this.getTemplate(names, mode);
1025
- if (referenceAttributeName) {
1026
- this._referenceAttributeName = referenceAttributeName;
1027
- }
1038
+ data.customStyles = this.getCustomStyles(names);
1028
1039
  return this.hook(`render${name.charAt(0).toUpperCase() + name.substring(1, name.length)}`, this.interpolate(template, data), data, mode);
1029
1040
  }
1030
1041
  /**
@@ -1134,9 +1145,12 @@ export default class Component extends Element {
1134
1145
  * @returns {string} - The submission timezone.
1135
1146
  */
1136
1147
  get submissionTimezone() {
1137
- this.options.submissionTimezone =
1138
- this.options.submissionTimezone || _.get(this.root, 'options.submissionTimezone');
1139
- 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'));
1140
1154
  }
1141
1155
  /**
1142
1156
  * Return the current timezone.
@@ -1151,6 +1165,7 @@ export default class Component extends Element {
1151
1165
  * @returns {string} - The current timezone.
1152
1166
  */
1153
1167
  getTimezone(settings) {
1168
+ settings = settings || {};
1154
1169
  if (settings.timezone) {
1155
1170
  return settings.timezone;
1156
1171
  }
@@ -1158,12 +1173,19 @@ export default class Component extends Element {
1158
1173
  return 'UTC';
1159
1174
  }
1160
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;
1161
1185
  if (submissionTimezone &&
1162
- (settings.displayInTimezone === 'submission' ||
1163
- ((this.options.pdf || this.options.server) && settings.displayInTimezone === 'viewer'))) {
1186
+ (mode === 'submission' || (staticSnapshot && (mode === 'viewer' || mode === 'location')))) {
1164
1187
  return submissionTimezone;
1165
1188
  }
1166
- // Return current timezone if none are provided.
1167
1189
  return currentTimezone();
1168
1190
  }
1169
1191
  /**
@@ -1667,14 +1689,27 @@ export default class Component extends Element {
1667
1689
  dialogContents: 'single',
1668
1690
  dialogClose: 'single',
1669
1691
  });
1692
+ // Check if an element is inside shadow dom
1693
+ const isInShadowDOM = typeof ShadowRoot !== 'undefined' && this.element?.getRootNode() instanceof ShadowRoot;
1694
+ // if we render shadow dom inside <iframe>'s we need to get the body from the current iframe,
1695
+ // not the general body. This is necessary to hide and show the scroll bar correctly.
1696
+ const body = isInShadowDOM ? this.element.getRootNode().host.ownerDocument.body : document.body;
1697
+ const rootEl = isInShadowDOM ? this.element.closest('.formio-form-wrapper') : document.body;
1698
+ const checkModal = (method) => {
1699
+ if (isInShadowDOM) {
1700
+ body.style.overflow = method === 'add' ? 'hidden' : '';
1701
+ return;
1702
+ }
1703
+ body.classList[method]('modal-open');
1704
+ };
1670
1705
  dialog.refs.dialogContents.appendChild(element);
1671
- document.body.appendChild(dialog);
1672
- document.body.classList.add('modal-open');
1706
+ rootEl.appendChild(dialog);
1707
+ checkModal('add');
1673
1708
  dialog.close = () => {
1674
- document.body.classList.remove('modal-open');
1709
+ checkModal('remove');
1675
1710
  dialog.dispatchEvent(new CustomEvent('close'));
1676
1711
  };
1677
- this.addEventListener(dialog, 'close', () => this.removeChildFrom(dialog, document.body));
1712
+ this.addEventListener(dialog, 'close', () => this.removeChildFrom(dialog, rootEl));
1678
1713
  const close = (event) => {
1679
1714
  event.preventDefault();
1680
1715
  dialog.close();
@@ -1745,6 +1780,20 @@ export default class Component extends Element {
1745
1780
  });
1746
1781
  return customCSS;
1747
1782
  }
1783
+ /**
1784
+ * Build custom styles from the styles form option or global config.
1785
+ * @param {string[]} templateNames - The possible template names.
1786
+ * @returns {{ [refName: string]: string[] }} - The custom styles object for the named template.
1787
+ * @todo - Rename this to a better method name that doesn't clash
1788
+ */
1789
+ getCustomStyles(templateNames) {
1790
+ for (const name of templateNames) {
1791
+ if (this.options.styles?.[name]) {
1792
+ return this.options.styles[name];
1793
+ }
1794
+ }
1795
+ return {};
1796
+ }
1748
1797
  /**
1749
1798
  * Returns the component condition operator settings if available.
1750
1799
  * @returns {object} - The component condition operator settings.
@@ -2520,6 +2569,20 @@ export default class Component extends Element {
2520
2569
  return Promise.resolve(editor);
2521
2570
  }
2522
2571
  else {
2572
+ // Due to an issue with ckeditor not loading styles in the shadowdom (https://github.com/ckeditor/ckeditor5/issues/15824), we need to copy cke-styles to the shadowdom
2573
+ let current = element;
2574
+ while (current) {
2575
+ if (current instanceof ShadowRoot) {
2576
+ const ckeStyles = document.querySelector('style[data-cke="true"]');
2577
+ const clone = document.createElement('style');
2578
+ clone.setAttribute('data-cke', 'true');
2579
+ clone.textContent = ckeStyles.textContent;
2580
+ current.prepend(clone);
2581
+ break;
2582
+ }
2583
+ ;
2584
+ current = current.parentNode || current.host;
2585
+ }
2523
2586
  return ClassicEditor.create(element, settings).then((editor) => {
2524
2587
  editor.model.document.on('change', () => onChange(editor.data.get()));
2525
2588
  return editor;
@@ -2542,12 +2605,61 @@ export default class Component extends Element {
2542
2605
  { type: 'styles', src: `${Formio.cdn.quill}/quill.${settings.theme}.css` },
2543
2606
  ], true);
2544
2607
  // Lazy load the quill library.
2545
- 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(() => {
2546
2609
  return Formio.requireLibrary('quill-table', 'Quill', `${Formio.cdn.baseUrl}/quill/quill-table.js`, true).then(() => {
2547
2610
  if (!element.parentNode) {
2548
2611
  return Promise.reject();
2549
2612
  }
2550
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
+ }
2551
2663
  /** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ */
2552
2664
  const txtArea = document.createElement('textarea');
2553
2665
  txtArea.setAttribute('class', 'quill-source-code');
@@ -2566,7 +2678,8 @@ export default class Component extends Element {
2566
2678
  // Make sure to select cursor when they click on the element.
2567
2679
  this.addEventListener(element, 'click', () => this.quill.focus());
2568
2680
  // Allows users to skip toolbar items when tabbing though form
2569
- const elm = document.querySelectorAll('.ql-formats > button');
2681
+ const queryRoot = element.getRootNode() || document;
2682
+ const elm = queryRoot.querySelectorAll('.ql-formats > button');
2570
2683
  for (let i = 0; i < elm.length; i++) {
2571
2684
  elm[i].setAttribute('tabindex', '-1');
2572
2685
  }
@@ -2579,7 +2692,7 @@ export default class Component extends Element {
2579
2692
  });
2580
2693
  }
2581
2694
  get shouldSanitizeValue() {
2582
- // Sanitize value if sanitizing for thw whole content is turned off
2695
+ // Sanitize value if sanitizing for the whole content is turned off
2583
2696
  return this.options?.sanitize !== false;
2584
2697
  }
2585
2698
  addAce(element, settings, onChange) {
@@ -2597,6 +2710,7 @@ export default class Component extends Element {
2597
2710
  editor.setOptions(settings);
2598
2711
  editor.getSession().setMode(settings.mode);
2599
2712
  editor.on('change', () => onChange(editor.getValue()));
2713
+ editor.renderer.attachToShadowRoot();
2600
2714
  if (settings.isUseWorkerDisabled) {
2601
2715
  editor.session.setUseWorker(false);
2602
2716
  }
@@ -2657,7 +2771,6 @@ export default class Component extends Element {
2657
2771
  return;
2658
2772
  }
2659
2773
  _.set(this._data, this.key, value);
2660
- return;
2661
2774
  }
2662
2775
  /**
2663
2776
  * Splice a value from the dataValue.
@@ -2704,9 +2817,9 @@ export default class Component extends Element {
2704
2817
  this.component.defaultValue !== undefined));
2705
2818
  }
2706
2819
  /**
2707
- * Determine if we should add a default value for this component.
2708
- * @returns {boolean} - TRUE if a default value should be set
2709
- */
2820
+ * Determine if we should add a default value for this component.
2821
+ * @returns {boolean} - TRUE if a default value should be set
2822
+ */
2710
2823
  get shouldAddDefaultValue() {
2711
2824
  return this.pristine && this.allowData && (this.hasDefaultValue || !this.options.noDefaults);
2712
2825
  }
@@ -3400,21 +3513,26 @@ export default class Component extends Element {
3400
3513
  }
3401
3514
  });
3402
3515
  this.addEventListener(element, 'blur', () => {
3403
- if (this.root) {
3404
- this.root.pendingBlur = FormioUtils.delay(() => {
3405
- this.emit('blur', this);
3406
- if (this.component.validateOn === 'blur') {
3407
- this.root.triggerChange?.({ fromBlur: true }, {
3408
- instance: this,
3409
- component: this.component,
3410
- value: this.dataValue,
3411
- flags: { fromBlur: true },
3412
- });
3413
- }
3414
- this.root.focusedComponent = null;
3415
- this.root.pendingBlur = null;
3416
- });
3516
+ const root = this.root;
3517
+ if (!root) {
3518
+ return;
3417
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
+ });
3418
3536
  });
3419
3537
  }
3420
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
  ];
@@ -82,6 +82,7 @@ declare const _default: ({
82
82
  inline?: undefined;
83
83
  defaultValue?: undefined;
84
84
  values?: undefined;
85
+ customConditional?: undefined;
85
86
  logic?: undefined;
86
87
  dataSrc?: undefined;
87
88
  valueProperty?: undefined;
@@ -101,6 +102,7 @@ declare const _default: ({
101
102
  inline?: undefined;
102
103
  defaultValue?: undefined;
103
104
  values?: undefined;
105
+ customConditional?: undefined;
104
106
  logic?: undefined;
105
107
  dataSrc?: undefined;
106
108
  valueProperty?: undefined;
@@ -126,6 +128,7 @@ declare const _default: ({
126
128
  value: string;
127
129
  })[];
128
130
  placeholder?: undefined;
131
+ customConditional?: undefined;
129
132
  logic?: undefined;
130
133
  dataSrc?: undefined;
131
134
  valueProperty?: undefined;
@@ -141,6 +144,7 @@ declare const _default: ({
141
144
  tooltip: string;
142
145
  key: string;
143
146
  input: boolean;
147
+ customConditional: string;
144
148
  logic: ({
145
149
  name: string;
146
150
  trigger: {
@@ -211,6 +215,7 @@ declare const _default: ({
211
215
  inline?: undefined;
212
216
  defaultValue?: undefined;
213
217
  values?: undefined;
218
+ customConditional?: undefined;
214
219
  logic?: undefined;
215
220
  as?: undefined;
216
221
  editor?: undefined;
@@ -226,6 +231,7 @@ declare const _default: ({
226
231
  placeholder?: undefined;
227
232
  inline?: undefined;
228
233
  values?: undefined;
234
+ customConditional?: undefined;
229
235
  logic?: undefined;
230
236
  dataSrc?: undefined;
231
237
  valueProperty?: undefined;
@@ -248,6 +254,7 @@ declare const _default: ({
248
254
  placeholder?: undefined;
249
255
  inline?: undefined;
250
256
  values?: undefined;
257
+ customConditional?: undefined;
251
258
  logic?: undefined;
252
259
  dataSrc?: undefined;
253
260
  valueProperty?: undefined;
@@ -55,6 +55,7 @@ export default [
55
55
  tooltip: 'Encrypt this field on the server. This is two way encryption which is not suitable for passwords.',
56
56
  key: 'encrypted',
57
57
  input: true,
58
+ customConditional: 'show = data.encrypted;',
58
59
  logic: [
59
60
  {
60
61
  name: 'disabled',
@@ -138,7 +139,7 @@ export default [
138
139
  input: true,
139
140
  },
140
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>'),
141
- 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()),
142
143
  {
143
144
  type: 'checkbox',
144
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 {
@@ -828,6 +828,11 @@ export default class NestedComponent extends Field {
828
828
  else if (value && component.hasValue(value)) {
829
829
  return component.setValue(_.get(value, component.key), flags);
830
830
  }
831
+ // if no value is provided and noDefault flag, set empty value
832
+ else if (flags.noDefault && component.allowData) {
833
+ flags.resetValue = true;
834
+ return component.setValue(component.emptyValue, flags);
835
+ }
831
836
  else if ((!this.rootPristine || component.visible) &&
832
837
  (flags.resetValue || component.shouldAddDefaultValue)) {
833
838
  flags.noValidate = !flags.dirty;
@@ -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;
@@ -416,6 +418,20 @@ export default class AddressComponent extends ContainerComponent {
416
418
  [AddressComponent.removeValueIconRef]: 'multiple',
417
419
  [AddressComponent.searchInputRef]: 'multiple',
418
420
  });
421
+ // We define a container for rendering autocomplete.
422
+ // If isInShadowDOM=true then we render it in shadow dom otherwise in the document body.
423
+ const isInShadowDOM = typeof ShadowRoot !== 'undefined' && this.element?.getRootNode() instanceof ShadowRoot;
424
+ let container;
425
+ if (isInShadowDOM) {
426
+ const shadowRoot = this.element.getRootNode();
427
+ container = document.createElement('div');
428
+ const target = shadowRoot.querySelector('.formio-form-wrapper');
429
+ target.appendChild(container);
430
+ }
431
+ else {
432
+ container = document.createElement('div');
433
+ document.body.appendChild(container);
434
+ }
419
435
  this.searchInput.forEach((element, index) => {
420
436
  if (!this.builderMode && element && this.provider) {
421
437
  if (this.component.provider === 'google') {
@@ -424,6 +440,7 @@ export default class AddressComponent extends ContainerComponent {
424
440
  else {
425
441
  autocompleter({
426
442
  input: element,
443
+ container,
427
444
  debounceWaitMs: 300,
428
445
  fetch: (text, update) => {
429
446
  const query = text;
@@ -555,8 +555,21 @@ export default class DataGridComponent extends NestedArrayComponent {
555
555
  changed.instance.root.triggerChange?.(flags, changed, modified);
556
556
  }
557
557
  else {
558
- this.triggerChange?.({ modified });
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);
567
+ this.triggerRootChange(flags, changed, modified);
559
568
  }
569
+ this.processRow('checkData', null, {
570
+ ...flags,
571
+ changed,
572
+ }, row, _.toArray(this.rows[rowIndex]));
560
573
  };
561
574
  let columnComponent;
562
575
  if (this.builderMode) {
@@ -19,6 +19,7 @@ export default class DataMapComponent extends DataGridComponent {
19
19
  getRowKey(rowIndex: any): string;
20
20
  get defaultRowKey(): string;
21
21
  getValueAsString(value: any, options: any): any;
22
+ findComponentInstance(key: any): any;
22
23
  createRowComponents(row: any, rowIndex: any): {
23
24
  __key: any;
24
25
  };
@@ -180,10 +180,14 @@ export default class DataMapComponent extends DataGridComponent {
180
180
  <tbody>
181
181
  `;
182
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);
183
187
  result += `
184
188
  <tr>
185
189
  <th style="padding: 5px 10px;">${key}</th>
186
- <td style="width:100%;padding:5px 10px;">${this.getView(value[key], options)}</td>
190
+ <td style="width:100%;padding:5px 10px;">${viewValue}</td>
187
191
  </tr>
188
192
  `;
189
193
  return result;
@@ -203,6 +207,18 @@ export default class DataMapComponent extends DataGridComponent {
203
207
  }
204
208
  return typeof value === 'object' ? '[Complex Data]' : value;
205
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
+ }
206
222
  getDataValueAsTable(value, options) {
207
223
  let result = `
208
224
  <table border="1" style="width:100%">
@@ -210,10 +226,14 @@ export default class DataMapComponent extends DataGridComponent {
210
226
  `;
211
227
  if (this.visible && _.isObject(value)) {
212
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);
213
233
  result += `
214
234
  <tr>
215
235
  <th style="padding: 5px 10px;">${key}</th>
216
- <td style="width:100%;padding:5px 10px;">${this.getView(value[key], options)}</td>
236
+ <td style="width:100%;padding:5px 10px;">${viewValue}</td>
217
237
  </tr>
218
238
  `;
219
239
  });
@@ -248,9 +268,21 @@ export default class DataMapComponent extends DataGridComponent {
248
268
  });
249
269
  const valueComponent = _.clone(this.component.valueComponent);
250
270
  valueComponent.key = key;
251
- const componentOptions = this.options;
271
+ const componentOptions = _.clone(this.options);
252
272
  componentOptions.row = options.row;
253
- 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;
254
286
  return components;
255
287
  }
256
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)))
@@ -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;