@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.
- package/dist/formio.builder.css +31 -5
- package/dist/formio.builder.min.css +1 -1
- package/dist/formio.embed.css +1 -1
- package/dist/formio.embed.js +1 -1
- package/dist/formio.embed.min.css +1 -1
- package/dist/formio.embed.min.js +1 -1
- package/dist/formio.embed.min.js.LICENSE.txt +1 -1
- package/dist/formio.form.css +31 -5
- package/dist/formio.form.js +2837 -2818
- package/dist/formio.form.min.css +1 -1
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.form.min.js.LICENSE.txt +1 -1
- package/dist/formio.full.css +31 -5
- package/dist/formio.full.js +3442 -3403
- package/dist/formio.full.min.css +1 -1
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.full.min.js.LICENSE.txt +1 -1
- package/dist/formio.js +1152 -1154
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +1 -1
- package/dist/formio.utils.js +1084 -1086
- package/dist/formio.utils.min.js +1 -1
- package/dist/formio.utils.min.js.LICENSE.txt +1 -1
- package/lib/cjs/Embed.js +28 -1
- package/lib/cjs/Form.d.ts +4 -2
- package/lib/cjs/Form.js +2 -2
- package/lib/cjs/FormBuilder.d.ts +2 -2
- package/lib/cjs/FormBuilder.js +1 -1
- package/lib/cjs/Formio.js +1 -1
- package/lib/cjs/PDF.js +2 -0
- package/lib/cjs/PDFBuilder.js +6 -1
- package/lib/cjs/Webform.d.ts +1 -0
- package/lib/cjs/Webform.js +50 -1
- package/lib/cjs/WebformBuilder.js +29 -1
- package/lib/cjs/Wizard.d.ts +1 -0
- package/lib/cjs/Wizard.js +11 -0
- package/lib/cjs/components/Components.d.ts +3 -0
- package/lib/cjs/components/_classes/component/Component.d.ts +13 -3
- package/lib/cjs/components/_classes/component/Component.js +167 -47
- package/lib/cjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
- package/lib/cjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
- package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +2 -1
- package/lib/cjs/components/_classes/component/editForm/utils.d.ts +1 -0
- package/lib/cjs/components/_classes/component/editForm/utils.js +3 -0
- package/lib/cjs/components/_classes/nested/NestedComponent.js +5 -0
- package/lib/cjs/components/address/Address.js +18 -0
- package/lib/cjs/components/datagrid/DataGrid.js +12 -2
- package/lib/cjs/components/datamap/DataMap.d.ts +1 -0
- package/lib/cjs/components/datamap/DataMap.js +37 -4
- package/lib/cjs/components/datetime/DateTime.js +11 -1
- package/lib/cjs/components/day/Day.d.ts +0 -15
- package/lib/cjs/components/day/Day.js +8 -17
- package/lib/cjs/components/editgrid/EditGrid.js +11 -1
- package/lib/cjs/components/fieldset/Fieldset.js +1 -0
- package/lib/cjs/components/file/File.d.ts +3 -1
- package/lib/cjs/components/file/File.js +62 -17
- package/lib/cjs/components/form/Form.js +3 -1
- package/lib/cjs/components/number/Number.d.ts +1 -0
- package/lib/cjs/components/number/Number.js +18 -0
- package/lib/cjs/components/select/Select.js +5 -1
- package/lib/cjs/components/signature/Signature.js +5 -5
- package/lib/cjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
- package/lib/cjs/components/signature/editForm/Signature.edit.display.js +0 -1
- package/lib/cjs/components/table/editForm/Table.edit.display.d.ts +27 -0
- package/lib/cjs/components/table/editForm/Table.edit.display.js +10 -0
- package/lib/cjs/formio.form.js +2 -5
- package/lib/cjs/package.json +1 -1
- package/lib/cjs/providers/storage/azure.js +9 -3
- package/lib/cjs/templates/index.d.ts +3 -0
- package/lib/cjs/translations/en.d.ts +2 -0
- package/lib/cjs/translations/en.js +2 -0
- package/lib/cjs/utils/i18n.d.ts +1 -0
- package/lib/cjs/utils/i18n.js +2 -0
- package/lib/cjs/utils/index.d.ts +1 -1
- package/lib/cjs/utils/utils.d.ts +1 -1
- package/lib/cjs/utils/utils.js +23 -6
- package/lib/cjs/widgets/CalendarWidget.js +1 -1
- package/lib/mjs/Embed.js +26 -1
- package/lib/mjs/Form.d.ts +4 -2
- package/lib/mjs/Form.js +2 -2
- package/lib/mjs/FormBuilder.d.ts +2 -2
- package/lib/mjs/FormBuilder.js +1 -1
- package/lib/mjs/Formio.js +1 -1
- package/lib/mjs/PDF.js +2 -0
- package/lib/mjs/PDFBuilder.js +6 -1
- package/lib/mjs/Webform.d.ts +1 -0
- package/lib/mjs/Webform.js +48 -1
- package/lib/mjs/WebformBuilder.js +28 -1
- package/lib/mjs/Wizard.d.ts +1 -0
- package/lib/mjs/Wizard.js +12 -1
- package/lib/mjs/components/Components.d.ts +3 -0
- package/lib/mjs/components/_classes/component/Component.d.ts +13 -3
- package/lib/mjs/components/_classes/component/Component.js +164 -46
- package/lib/mjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
- package/lib/mjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
- package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +2 -1
- package/lib/mjs/components/_classes/component/editForm/utils.d.ts +1 -0
- package/lib/mjs/components/_classes/component/editForm/utils.js +3 -0
- package/lib/mjs/components/_classes/nested/NestedComponent.js +5 -0
- package/lib/mjs/components/address/Address.js +17 -0
- package/lib/mjs/components/datagrid/DataGrid.js +14 -1
- package/lib/mjs/components/datamap/DataMap.d.ts +1 -0
- package/lib/mjs/components/datamap/DataMap.js +36 -4
- package/lib/mjs/components/datetime/DateTime.js +11 -1
- package/lib/mjs/components/day/Day.d.ts +0 -15
- package/lib/mjs/components/day/Day.js +8 -17
- package/lib/mjs/components/editgrid/EditGrid.js +11 -1
- package/lib/mjs/components/fieldset/Fieldset.js +1 -0
- package/lib/mjs/components/file/File.d.ts +3 -1
- package/lib/mjs/components/file/File.js +60 -15
- package/lib/mjs/components/form/Form.js +3 -1
- package/lib/mjs/components/number/Number.d.ts +1 -0
- package/lib/mjs/components/number/Number.js +17 -0
- package/lib/mjs/components/select/Select.js +5 -1
- package/lib/mjs/components/signature/Signature.js +1 -1
- package/lib/mjs/components/signature/editForm/Signature.edit.display.d.ts +0 -6
- package/lib/mjs/components/signature/editForm/Signature.edit.display.js +0 -1
- package/lib/mjs/components/table/editForm/Table.edit.display.d.ts +27 -0
- package/lib/mjs/components/table/editForm/Table.edit.display.js +10 -0
- package/lib/mjs/formio.form.js +4 -7
- package/lib/mjs/package.json +1 -1
- package/lib/mjs/providers/storage/azure.js +9 -3
- package/lib/mjs/templates/index.d.ts +3 -0
- package/lib/mjs/translations/en.d.ts +2 -0
- package/lib/mjs/translations/en.js +2 -0
- package/lib/mjs/utils/i18n.d.ts +1 -0
- package/lib/mjs/utils/i18n.js +2 -0
- package/lib/mjs/utils/index.d.ts +1 -1
- package/lib/mjs/utils/utils.d.ts +1 -1
- package/lib/mjs/utils/utils.js +22 -6
- package/lib/mjs/widgets/CalendarWidget.js +2 -2
- package/package.json +8 -6
|
@@ -140,7 +140,7 @@ export default class Component extends Element {
|
|
|
140
140
|
unique: false,
|
|
141
141
|
},
|
|
142
142
|
/**
|
|
143
|
-
*
|
|
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 ?
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
1139
|
-
|
|
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
|
-
(
|
|
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
|
-
|
|
1672
|
-
|
|
1706
|
+
rootEl.appendChild(dialog);
|
|
1707
|
+
checkModal('add');
|
|
1673
1708
|
dialog.close = () => {
|
|
1674
|
-
|
|
1709
|
+
checkModal('remove');
|
|
1675
1710
|
dialog.dispatchEvent(new CustomEvent('close'));
|
|
1676
1711
|
};
|
|
1677
|
-
this.addEventListener(dialog, 'close', () => this.removeChildFrom(dialog,
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
2708
|
-
|
|
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
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
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>',
|
|
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
|
-
|
|
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;">${
|
|
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;">${
|
|
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
|
-
|
|
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
|
-
|
|
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;
|