@formio/js 5.4.0-api98.1 → 5.4.1
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 +26 -5
- package/dist/formio.builder.min.css +1 -1
- package/dist/formio.embed.js +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 +26 -5
- package/dist/formio.form.js +3462 -3448
- package/dist/formio.form.min.css +1 -1
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.form.min.js.LICENSE.txt +2 -2
- package/dist/formio.full.css +26 -5
- package/dist/formio.full.js +4277 -4263
- package/dist/formio.full.min.css +1 -1
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.full.min.js.LICENSE.txt +2 -2
- package/dist/formio.js +1738 -1724
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +2 -2
- package/dist/formio.utils.js +1631 -1617
- package/dist/formio.utils.min.js +1 -1
- package/dist/formio.utils.min.js.LICENSE.txt +2 -2
- package/lib/cjs/Element.d.ts +11 -0
- package/lib/cjs/Element.js +24 -0
- package/lib/cjs/Embed.js +23 -2
- package/lib/cjs/Form.d.ts +1 -1
- package/lib/cjs/Formio.js +1 -1
- package/lib/cjs/PDFBuilder.js +6 -1
- package/lib/cjs/Webform.d.ts +1 -1
- package/lib/cjs/Webform.js +9 -6
- package/lib/cjs/WebformBuilder.js +14 -1
- package/lib/cjs/Wizard.js +15 -11
- package/lib/cjs/components/Components.d.ts +3 -0
- package/lib/cjs/components/Components.js +3 -1
- package/lib/cjs/components/_classes/component/Component.d.ts +13 -0
- package/lib/cjs/components/_classes/component/Component.js +137 -44
- package/lib/cjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
- package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +1 -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 -7
- package/lib/cjs/components/address/Address.js +2 -0
- package/lib/cjs/components/button/Button.d.ts +1 -0
- package/lib/cjs/components/button/Button.js +22 -1
- package/lib/cjs/components/datagrid/DataGrid.js +35 -10
- package/lib/cjs/components/datamap/DataMap.d.ts +1 -4
- package/lib/cjs/components/datamap/DataMap.js +42 -10
- package/lib/cjs/components/datetime/DateTime.js +11 -1
- package/lib/cjs/components/datetime/editForm/DateTime.edit.date.d.ts +18 -1
- package/lib/cjs/components/datetime/editForm/DateTime.edit.date.js +3 -0
- package/lib/cjs/components/datetime/editForm/DateTime.edit.time.d.ts +13 -2
- package/lib/cjs/components/datetime/editForm/DateTime.edit.time.js +3 -0
- 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 +13 -3
- package/lib/cjs/components/file/File.js +7 -6
- package/lib/cjs/components/form/Form.d.ts +1 -0
- package/lib/cjs/components/form/Form.js +20 -8
- 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 +6 -3
- 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/textfield/editForm/TextField.edit.display.d.ts +0 -10
- package/lib/cjs/components/textfield/editForm/TextField.edit.display.js +9 -23
- package/lib/cjs/formio.form.js +2 -5
- package/lib/cjs/package.json +1 -1
- package/lib/cjs/providers/address/GoogleAddressProvider.js +1 -1
- package/lib/cjs/providers/storage/azure.js +9 -3
- package/lib/cjs/translations/en.d.ts +1 -0
- package/lib/cjs/translations/en.js +3 -1
- package/lib/cjs/utils/formUtils.d.ts +2 -2
- package/lib/cjs/utils/index.d.ts +3 -3
- package/lib/cjs/utils/utils.d.ts +1 -1
- package/lib/cjs/utils/utils.js +28 -11
- package/lib/cjs/widgets/CalendarWidget.js +1 -1
- package/lib/mjs/Element.d.ts +11 -0
- package/lib/mjs/Element.js +23 -0
- package/lib/mjs/Embed.js +21 -2
- package/lib/mjs/Form.d.ts +1 -1
- package/lib/mjs/Formio.js +1 -1
- package/lib/mjs/PDFBuilder.js +6 -1
- package/lib/mjs/Webform.d.ts +1 -1
- package/lib/mjs/Webform.js +6 -3
- package/lib/mjs/WebformBuilder.js +13 -1
- package/lib/mjs/Wizard.js +9 -10
- package/lib/mjs/components/Components.d.ts +3 -0
- package/lib/mjs/components/Components.js +3 -1
- package/lib/mjs/components/_classes/component/Component.d.ts +13 -0
- package/lib/mjs/components/_classes/component/Component.js +135 -43
- package/lib/mjs/components/_classes/component/editForm/Component.edit.conditional.js +1 -1
- package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +1 -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 -6
- package/lib/mjs/components/address/Address.js +2 -0
- package/lib/mjs/components/button/Button.d.ts +1 -0
- package/lib/mjs/components/button/Button.js +21 -1
- package/lib/mjs/components/datagrid/DataGrid.js +39 -11
- package/lib/mjs/components/datamap/DataMap.d.ts +1 -4
- package/lib/mjs/components/datamap/DataMap.js +41 -10
- package/lib/mjs/components/datetime/DateTime.js +11 -1
- package/lib/mjs/components/datetime/editForm/DateTime.edit.date.d.ts +18 -1
- package/lib/mjs/components/datetime/editForm/DateTime.edit.date.js +3 -0
- package/lib/mjs/components/datetime/editForm/DateTime.edit.time.d.ts +13 -2
- package/lib/mjs/components/datetime/editForm/DateTime.edit.time.js +3 -0
- 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 +12 -2
- package/lib/mjs/components/file/File.js +7 -6
- package/lib/mjs/components/form/Form.d.ts +1 -0
- package/lib/mjs/components/form/Form.js +18 -6
- 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 +6 -3
- 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/textfield/editForm/TextField.edit.display.d.ts +0 -10
- package/lib/mjs/components/textfield/editForm/TextField.edit.display.js +9 -23
- package/lib/mjs/formio.form.js +4 -7
- package/lib/mjs/package.json +1 -1
- package/lib/mjs/providers/address/GoogleAddressProvider.js +1 -1
- package/lib/mjs/providers/storage/azure.js +9 -3
- package/lib/mjs/translations/en.d.ts +1 -0
- package/lib/mjs/translations/en.js +3 -1
- package/lib/mjs/utils/formUtils.d.ts +2 -2
- package/lib/mjs/utils/index.d.ts +3 -3
- package/lib/mjs/utils/utils.d.ts +1 -1
- package/lib/mjs/utils/utils.js +27 -11
- package/lib/mjs/widgets/CalendarWidget.js +2 -2
- package/package.json +8 -7
|
@@ -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,
|
|
@@ -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
|
|
1132
|
-
|
|
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
|
-
(
|
|
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
|
/**
|
|
@@ -1183,6 +1212,19 @@ export default class Component extends Element {
|
|
|
1183
1212
|
}
|
|
1184
1213
|
}
|
|
1185
1214
|
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Announces a message to screen readers via the component's live region.
|
|
1217
|
+
* @param {string} message - The message to announce.
|
|
1218
|
+
*/
|
|
1219
|
+
announce(message) {
|
|
1220
|
+
const liveRegion = this.refs.liveRegion;
|
|
1221
|
+
if (liveRegion) {
|
|
1222
|
+
liveRegion.textContent = '';
|
|
1223
|
+
setTimeout(() => {
|
|
1224
|
+
liveRegion.textContent = message;
|
|
1225
|
+
}, 50);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1186
1228
|
/**
|
|
1187
1229
|
* Opens the modal element.
|
|
1188
1230
|
* @param {string} template - The template to use for the modal dialog.
|
|
@@ -1323,6 +1365,7 @@ export default class Component extends Element {
|
|
|
1323
1365
|
this.loadRefs(element, {
|
|
1324
1366
|
messageContainer: 'single',
|
|
1325
1367
|
tooltip: 'multiple',
|
|
1368
|
+
liveRegion: 'single',
|
|
1326
1369
|
});
|
|
1327
1370
|
this.attachTooltips(this.refs.tooltip);
|
|
1328
1371
|
// Attach logic.
|
|
@@ -1562,13 +1605,13 @@ export default class Component extends Element {
|
|
|
1562
1605
|
value.forEach((val, index) => {
|
|
1563
1606
|
const widget = this.refs.input[index] && this.refs.input[index].widget;
|
|
1564
1607
|
if (widget) {
|
|
1565
|
-
values.push(widget.getValueAsString(val
|
|
1608
|
+
values.push(widget.getValueAsString(val));
|
|
1566
1609
|
}
|
|
1567
1610
|
});
|
|
1568
1611
|
return values;
|
|
1569
1612
|
}
|
|
1570
1613
|
const widget = this.refs.input[0].widget;
|
|
1571
|
-
return widget.getValueAsString(value
|
|
1614
|
+
return widget.getValueAsString(value);
|
|
1572
1615
|
}
|
|
1573
1616
|
/**
|
|
1574
1617
|
* Returns the value of the component as a string.
|
|
@@ -2304,29 +2347,23 @@ export default class Component extends Element {
|
|
|
2304
2347
|
* @returns {void}
|
|
2305
2348
|
*/
|
|
2306
2349
|
setErrorClasses(elements, dirty, hasErrors, hasMessages, element = this.element) {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
this.setElementInvalid(this.performInputMapping(element), hasErrors);
|
|
2350
|
+
elements.forEach((el) => {
|
|
2351
|
+
this.setElementInvalid(this.performInputMapping(el), hasErrors);
|
|
2310
2352
|
});
|
|
2311
2353
|
this.setInputWidgetErrorClasses(elements, hasErrors);
|
|
2312
2354
|
// do not set error classes for hidden components
|
|
2313
2355
|
if (!this.visible) {
|
|
2356
|
+
this.clearErrorClasses(element);
|
|
2314
2357
|
return;
|
|
2315
2358
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
else {
|
|
2325
|
-
this.addClass(element, 'has-error');
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
if (hasMessages) {
|
|
2329
|
-
this.addClass(element, 'has-message');
|
|
2359
|
+
const wantHighlight = hasErrors && !!dirty && !!this.options.highlightErrors;
|
|
2360
|
+
const wantHasError = hasErrors && !wantHighlight;
|
|
2361
|
+
this.toggleClass(element, this.options.componentErrorClass, wantHighlight);
|
|
2362
|
+
this.toggleClass(element, 'has-error', wantHasError);
|
|
2363
|
+
this.toggleClass(element, 'has-message', hasMessages);
|
|
2364
|
+
// Preserve previous clearErrorClasses() behavior: drop the 'alert alert-danger' pair if left over.
|
|
2365
|
+
if (element?.classList?.contains('alert-danger')) {
|
|
2366
|
+
this.removeClass(element, 'alert alert-danger');
|
|
2330
2367
|
}
|
|
2331
2368
|
}
|
|
2332
2369
|
/**
|
|
@@ -2582,12 +2619,61 @@ export default class Component extends Element {
|
|
|
2582
2619
|
{ type: 'styles', src: `${Formio.cdn.quill}/quill.${settings.theme}.css` },
|
|
2583
2620
|
], true);
|
|
2584
2621
|
// Lazy load the quill library.
|
|
2585
|
-
return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.
|
|
2622
|
+
return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.js`), true).then(() => {
|
|
2586
2623
|
return Formio.requireLibrary('quill-table', 'Quill', `${Formio.cdn.baseUrl}/quill/quill-table.js`, true).then(() => {
|
|
2587
2624
|
if (!element.parentNode) {
|
|
2588
2625
|
return Promise.reject();
|
|
2589
2626
|
}
|
|
2590
2627
|
this.quill = new Quill(element, isIEBrowser ? { ...settings, modules: {} } : settings);
|
|
2628
|
+
const root = element.getRootNode();
|
|
2629
|
+
if (root instanceof ShadowRoot && root.getSelection) {
|
|
2630
|
+
const sel = this.quill.selection;
|
|
2631
|
+
// 1. getNativeRange: read selection from shadowRoot instead of document
|
|
2632
|
+
sel.getNativeRange = () => {
|
|
2633
|
+
const shadowSelection = root.getSelection();
|
|
2634
|
+
if (shadowSelection == null || shadowSelection.rangeCount <= 0)
|
|
2635
|
+
return null;
|
|
2636
|
+
const nativeRange = shadowSelection.getRangeAt(0);
|
|
2637
|
+
if (nativeRange == null)
|
|
2638
|
+
return null;
|
|
2639
|
+
return sel.normalizeNative(nativeRange);
|
|
2640
|
+
};
|
|
2641
|
+
// 2. hasFocus: check shadowRoot.activeElement instead of document.activeElement
|
|
2642
|
+
sel.hasFocus = () => {
|
|
2643
|
+
return root.activeElement === sel.root ||
|
|
2644
|
+
(root.activeElement != null && sel.root.contains(root.activeElement));
|
|
2645
|
+
};
|
|
2646
|
+
// 3. setNativeRange: use shadowRoot's selection to add/remove ranges
|
|
2647
|
+
const origSetNativeRange = sel.setNativeRange.bind(sel);
|
|
2648
|
+
sel.setNativeRange = (startNode, startOffset, endNode, endOffset, force) => {
|
|
2649
|
+
// Delegate to original logic for the null case (blur)
|
|
2650
|
+
if (startNode == null) {
|
|
2651
|
+
origSetNativeRange(null);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
if (!sel.hasFocus()) {
|
|
2655
|
+
sel.root.focus({ preventScroll: true });
|
|
2656
|
+
}
|
|
2657
|
+
const shadowSelection = root.getSelection();
|
|
2658
|
+
if (shadowSelection == null)
|
|
2659
|
+
return;
|
|
2660
|
+
const { native } = sel.getNativeRange() || {};
|
|
2661
|
+
endNode = endNode ?? startNode;
|
|
2662
|
+
endOffset = endOffset ?? startOffset;
|
|
2663
|
+
if (native == null || force ||
|
|
2664
|
+
startNode !== native.startContainer || startOffset !== native.startOffset ||
|
|
2665
|
+
endNode !== native.endContainer || endOffset !== native.endOffset) {
|
|
2666
|
+
const range = document.createRange();
|
|
2667
|
+
range.setStart(startNode, startOffset);
|
|
2668
|
+
range.setEnd(endNode, endOffset);
|
|
2669
|
+
shadowSelection.removeAllRanges();
|
|
2670
|
+
shadowSelection.addRange(range);
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
document.addEventListener('selectionchange', () => {
|
|
2674
|
+
sel.update();
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2591
2677
|
/** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ */
|
|
2592
2678
|
const txtArea = document.createElement('textarea');
|
|
2593
2679
|
txtArea.setAttribute('class', 'quill-source-code');
|
|
@@ -2606,7 +2692,8 @@ export default class Component extends Element {
|
|
|
2606
2692
|
// Make sure to select cursor when they click on the element.
|
|
2607
2693
|
this.addEventListener(element, 'click', () => this.quill.focus());
|
|
2608
2694
|
// Allows users to skip toolbar items when tabbing though form
|
|
2609
|
-
const
|
|
2695
|
+
const queryRoot = element.getRootNode() || document;
|
|
2696
|
+
const elm = queryRoot.querySelectorAll('.ql-formats > button');
|
|
2610
2697
|
for (let i = 0; i < elm.length; i++) {
|
|
2611
2698
|
elm[i].setAttribute('tabindex', '-1');
|
|
2612
2699
|
}
|
|
@@ -3234,7 +3321,7 @@ export default class Component extends Element {
|
|
|
3234
3321
|
if (flags.silentCheck) {
|
|
3235
3322
|
return [];
|
|
3236
3323
|
}
|
|
3237
|
-
let isDirty = flags.dirty
|
|
3324
|
+
let isDirty = flags.dirty || this.dirty;
|
|
3238
3325
|
if (this.options.alwaysDirty) {
|
|
3239
3326
|
isDirty = true;
|
|
3240
3327
|
}
|
|
@@ -3440,21 +3527,26 @@ export default class Component extends Element {
|
|
|
3440
3527
|
}
|
|
3441
3528
|
});
|
|
3442
3529
|
this.addEventListener(element, 'blur', () => {
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
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
|
-
});
|
|
3530
|
+
const root = this.root;
|
|
3531
|
+
if (!root) {
|
|
3532
|
+
return;
|
|
3457
3533
|
}
|
|
3534
|
+
root.pendingBlur = FormioUtils.delay(() => {
|
|
3535
|
+
if (!root) {
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
this.emit('blur', this);
|
|
3539
|
+
if (this.component.validateOn === 'blur') {
|
|
3540
|
+
root.triggerChange?.({ fromBlur: true }, {
|
|
3541
|
+
instance: this,
|
|
3542
|
+
component: this.component,
|
|
3543
|
+
value: this.dataValue,
|
|
3544
|
+
flags: { fromBlur: true },
|
|
3545
|
+
});
|
|
3546
|
+
}
|
|
3547
|
+
root.focusedComponent = null;
|
|
3548
|
+
root.pendingBlur = null;
|
|
3549
|
+
});
|
|
3458
3550
|
});
|
|
3459
3551
|
}
|
|
3460
3552
|
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>',
|
|
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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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;
|
|
@@ -29,6 +29,7 @@ export default class ButtonComponent extends Field {
|
|
|
29
29
|
detach(element: any): void;
|
|
30
30
|
onClick(event: any): void;
|
|
31
31
|
openOauth(settings: any): void;
|
|
32
|
+
_handleOauthSessionExpired(): void;
|
|
32
33
|
get oauthComponentPath(): any;
|
|
33
34
|
focus(): void;
|
|
34
35
|
triggerCaptcha(): void;
|
|
@@ -363,6 +363,12 @@ export default class ButtonComponent extends Field {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
openOauth(settings) {
|
|
366
|
+
// this is if the temp session (storing the state and code verifiers) expires in the db
|
|
367
|
+
// and we need to fetch new oauth state
|
|
368
|
+
if (settings.sessionExpireAt && Date.now() >= settings.sessionExpireAt) {
|
|
369
|
+
this._handleOauthSessionExpired();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
366
372
|
if (!this.root?.formio) {
|
|
367
373
|
console.warn('You must attach a Form API url to your form in order to use OAuth buttons.');
|
|
368
374
|
return;
|
|
@@ -378,7 +384,8 @@ export default class ButtonComponent extends Field {
|
|
|
378
384
|
if (settings.state) {
|
|
379
385
|
params.state = settings.state;
|
|
380
386
|
}
|
|
381
|
-
|
|
387
|
+
// okta requires both a state and a code challenge for PKCE
|
|
388
|
+
if (settings.code_challenge) {
|
|
382
389
|
params.code_challenge = settings.code_challenge;
|
|
383
390
|
params.code_challenge_method = 'S256';
|
|
384
391
|
}
|
|
@@ -420,6 +427,9 @@ export default class ButtonComponent extends Field {
|
|
|
420
427
|
this.root?.setAlert('danger', 'OAuth state does not match. Please try logging in again.');
|
|
421
428
|
return;
|
|
422
429
|
}
|
|
430
|
+
if (settings.sessionId) {
|
|
431
|
+
params.sessionId = settings.sessionId;
|
|
432
|
+
}
|
|
423
433
|
// Depending on where the settings came from, submit to either the submission endpoint (old) or oauth endpoint (new).
|
|
424
434
|
let requestPromise = Promise.resolve();
|
|
425
435
|
if (_.has(this, 'root.form.config.oauth') &&
|
|
@@ -446,6 +456,11 @@ export default class ButtonComponent extends Field {
|
|
|
446
456
|
this.root?.onSubmit(result, true);
|
|
447
457
|
})
|
|
448
458
|
.catch((err) => {
|
|
459
|
+
console.log(err);
|
|
460
|
+
if (settings.sessionExpireAt && Date.now() >= settings.sessionExpireAt) {
|
|
461
|
+
this._handleOauthSessionExpired();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
449
464
|
this.root?.onSubmissionError(err);
|
|
450
465
|
});
|
|
451
466
|
}
|
|
@@ -461,6 +476,11 @@ export default class ButtonComponent extends Field {
|
|
|
461
476
|
}
|
|
462
477
|
}, 100);
|
|
463
478
|
}
|
|
479
|
+
_handleOauthSessionExpired() {
|
|
480
|
+
this.root?.setAlert('warning', this.t('oauthSessionExpired'));
|
|
481
|
+
this.loading = true;
|
|
482
|
+
setTimeout(() => window.location.reload(), 2000);
|
|
483
|
+
}
|
|
464
484
|
get oauthComponentPath() {
|
|
465
485
|
const pathArray = getArrayFromComponentPath(this.path);
|
|
466
486
|
return _.chain(pathArray)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import NestedArrayComponent from '../_classes/nestedarray/NestedArrayComponent';
|
|
3
|
-
import { fastCloneDeep, getFocusableElements, getComponent, eachComponent
|
|
3
|
+
import { fastCloneDeep, getFocusableElements, getComponent, eachComponent } from '../../utils';
|
|
4
4
|
import dragula from 'dragula';
|
|
5
5
|
export default class DataGridComponent extends NestedArrayComponent {
|
|
6
6
|
static schema(...extend) {
|
|
@@ -441,17 +441,25 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
441
441
|
component: this.component,
|
|
442
442
|
row,
|
|
443
443
|
});
|
|
444
|
-
screenReaderSpeech('Row has been added');
|
|
445
444
|
this.checkConditions();
|
|
446
445
|
this.triggerChange?.({ modified: true, noPristineChangeOnModified: true });
|
|
447
446
|
this.redraw().then(() => {
|
|
448
447
|
this.focusOnNewRowElement(this.rows[index]);
|
|
448
|
+
this.announce(this.t('Row has been added'));
|
|
449
449
|
});
|
|
450
450
|
}
|
|
451
451
|
updateComponentsRowIndex(components, rowIndex) {
|
|
452
452
|
components.forEach((component, colIndex) => {
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -472,15 +487,14 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
472
487
|
const flags = { isReordered: !makeEmpty, resetValue: makeEmpty };
|
|
473
488
|
this.splice(index, flags);
|
|
474
489
|
this.emit('dataGridDeleteRow', { index });
|
|
475
|
-
if (this.rows.length > 1) {
|
|
476
|
-
screenReaderSpeech('Row has been deleted');
|
|
477
|
-
}
|
|
478
490
|
const [row,] = this.rows.splice(index, 1);
|
|
479
491
|
this.removeSubmissionMetadataRow(index);
|
|
480
492
|
this.removeRowComponents(row);
|
|
481
493
|
this.updateRowsComponents(index);
|
|
482
494
|
this.setValue(this.dataValue, flags);
|
|
483
|
-
this.redraw()
|
|
495
|
+
this.redraw().then(() => {
|
|
496
|
+
this.announce(this.t('Row has been deleted'));
|
|
497
|
+
});
|
|
484
498
|
}
|
|
485
499
|
removeRowComponents(row) {
|
|
486
500
|
_.each(row, (component) => this.removeComponent(component));
|
|
@@ -530,8 +544,9 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
530
544
|
options.row = `${rowIndex}-${colIndex}`;
|
|
531
545
|
options.rowIndex = rowIndex;
|
|
532
546
|
options.onChange = (flags, changed, modified) => {
|
|
533
|
-
|
|
534
|
-
|
|
547
|
+
const changedComponent = changed.component;
|
|
548
|
+
if (changedComponent?.type === 'form' && changedComponent?.key) {
|
|
549
|
+
const formComp = getComponent(this.component.components, changedComponent.key);
|
|
535
550
|
_.set(formComp, 'components', changed.component.components);
|
|
536
551
|
}
|
|
537
552
|
// If we're in a nested form we need to ensure our changes are triggered upstream
|
|
@@ -539,8 +554,21 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
539
554
|
changed.instance.root.triggerChange?.(flags, changed, modified);
|
|
540
555
|
}
|
|
541
556
|
else {
|
|
557
|
+
if (modified && !flags.noPristineChangeOnModified) {
|
|
558
|
+
this.pristine = false;
|
|
559
|
+
}
|
|
560
|
+
this.triggerRootChange(flags, {
|
|
561
|
+
instance: this,
|
|
562
|
+
component: this.component,
|
|
563
|
+
value: this.dataValue,
|
|
564
|
+
flags,
|
|
565
|
+
}, modified);
|
|
542
566
|
this.triggerRootChange(flags, changed, modified);
|
|
543
567
|
}
|
|
568
|
+
this.processRow('checkData', null, {
|
|
569
|
+
...flags,
|
|
570
|
+
changed,
|
|
571
|
+
}, row, _.toArray(this.rows[rowIndex]));
|
|
544
572
|
};
|
|
545
573
|
let columnComponent;
|
|
546
574
|
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
|
|
122
|
-
components: row
|
|
123
|
-
data: row
|
|
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].
|
|
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;">${
|
|
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;">${
|
|
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
|
-
|
|
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() {
|