@formio/js 5.4.0-api98.1 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formio.builder.css +15 -0
- 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 +15 -0
- package/dist/formio.form.js +917 -897
- 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 +15 -0
- package/dist/formio.full.js +1211 -1191
- 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 +832 -812
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +2 -2
- package/dist/formio.utils.js +774 -754
- 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 +8 -0
- package/lib/cjs/components/_classes/component/Component.js +121 -42
- 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/datagrid/DataGrid.js +31 -5
- 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 +4 -0
- 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/storage/azure.js +9 -3
- 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 +23 -6
- 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 +8 -0
- package/lib/mjs/components/_classes/component/Component.js +119 -41
- 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/datagrid/DataGrid.js +34 -5
- 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 +4 -0
- 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/storage/azure.js +9 -3
- 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 +22 -6
- package/lib/mjs/widgets/CalendarWidget.js +2 -2
- package/package.json +7 -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,
|
|
@@ -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
|
/**
|
|
@@ -2304,29 +2333,23 @@ export default class Component extends Element {
|
|
|
2304
2333
|
* @returns {void}
|
|
2305
2334
|
*/
|
|
2306
2335
|
setErrorClasses(elements, dirty, hasErrors, hasMessages, element = this.element) {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
this.setElementInvalid(this.performInputMapping(element), hasErrors);
|
|
2336
|
+
elements.forEach((el) => {
|
|
2337
|
+
this.setElementInvalid(this.performInputMapping(el), hasErrors);
|
|
2310
2338
|
});
|
|
2311
2339
|
this.setInputWidgetErrorClasses(elements, hasErrors);
|
|
2312
2340
|
// do not set error classes for hidden components
|
|
2313
2341
|
if (!this.visible) {
|
|
2342
|
+
this.clearErrorClasses(element);
|
|
2314
2343
|
return;
|
|
2315
2344
|
}
|
|
2316
|
-
|
|
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');
|
|
2345
|
+
const wantHighlight = hasErrors && !!dirty && !!this.options.highlightErrors;
|
|
2346
|
+
const wantHasError = hasErrors && !wantHighlight;
|
|
2347
|
+
this.toggleClass(element, this.options.componentErrorClass, wantHighlight);
|
|
2348
|
+
this.toggleClass(element, 'has-error', wantHasError);
|
|
2349
|
+
this.toggleClass(element, 'has-message', hasMessages);
|
|
2350
|
+
// Preserve previous clearErrorClasses() behavior: drop the 'alert alert-danger' pair if left over.
|
|
2351
|
+
if (element?.classList?.contains('alert-danger')) {
|
|
2352
|
+
this.removeClass(element, 'alert alert-danger');
|
|
2330
2353
|
}
|
|
2331
2354
|
}
|
|
2332
2355
|
/**
|
|
@@ -2582,12 +2605,61 @@ export default class Component extends Element {
|
|
|
2582
2605
|
{ type: 'styles', src: `${Formio.cdn.quill}/quill.${settings.theme}.css` },
|
|
2583
2606
|
], true);
|
|
2584
2607
|
// Lazy load the quill library.
|
|
2585
|
-
return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.
|
|
2608
|
+
return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.js`), true).then(() => {
|
|
2586
2609
|
return Formio.requireLibrary('quill-table', 'Quill', `${Formio.cdn.baseUrl}/quill/quill-table.js`, true).then(() => {
|
|
2587
2610
|
if (!element.parentNode) {
|
|
2588
2611
|
return Promise.reject();
|
|
2589
2612
|
}
|
|
2590
2613
|
this.quill = new Quill(element, isIEBrowser ? { ...settings, modules: {} } : settings);
|
|
2614
|
+
const root = element.getRootNode();
|
|
2615
|
+
if (root instanceof ShadowRoot && root.getSelection) {
|
|
2616
|
+
const sel = this.quill.selection;
|
|
2617
|
+
// 1. getNativeRange: read selection from shadowRoot instead of document
|
|
2618
|
+
sel.getNativeRange = () => {
|
|
2619
|
+
const shadowSelection = root.getSelection();
|
|
2620
|
+
if (shadowSelection == null || shadowSelection.rangeCount <= 0)
|
|
2621
|
+
return null;
|
|
2622
|
+
const nativeRange = shadowSelection.getRangeAt(0);
|
|
2623
|
+
if (nativeRange == null)
|
|
2624
|
+
return null;
|
|
2625
|
+
return sel.normalizeNative(nativeRange);
|
|
2626
|
+
};
|
|
2627
|
+
// 2. hasFocus: check shadowRoot.activeElement instead of document.activeElement
|
|
2628
|
+
sel.hasFocus = () => {
|
|
2629
|
+
return root.activeElement === sel.root ||
|
|
2630
|
+
(root.activeElement != null && sel.root.contains(root.activeElement));
|
|
2631
|
+
};
|
|
2632
|
+
// 3. setNativeRange: use shadowRoot's selection to add/remove ranges
|
|
2633
|
+
const origSetNativeRange = sel.setNativeRange.bind(sel);
|
|
2634
|
+
sel.setNativeRange = (startNode, startOffset, endNode, endOffset, force) => {
|
|
2635
|
+
// Delegate to original logic for the null case (blur)
|
|
2636
|
+
if (startNode == null) {
|
|
2637
|
+
origSetNativeRange(null);
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
if (!sel.hasFocus()) {
|
|
2641
|
+
sel.root.focus({ preventScroll: true });
|
|
2642
|
+
}
|
|
2643
|
+
const shadowSelection = root.getSelection();
|
|
2644
|
+
if (shadowSelection == null)
|
|
2645
|
+
return;
|
|
2646
|
+
const { native } = sel.getNativeRange() || {};
|
|
2647
|
+
endNode = endNode ?? startNode;
|
|
2648
|
+
endOffset = endOffset ?? startOffset;
|
|
2649
|
+
if (native == null || force ||
|
|
2650
|
+
startNode !== native.startContainer || startOffset !== native.startOffset ||
|
|
2651
|
+
endNode !== native.endContainer || endOffset !== native.endOffset) {
|
|
2652
|
+
const range = document.createRange();
|
|
2653
|
+
range.setStart(startNode, startOffset);
|
|
2654
|
+
range.setEnd(endNode, endOffset);
|
|
2655
|
+
shadowSelection.removeAllRanges();
|
|
2656
|
+
shadowSelection.addRange(range);
|
|
2657
|
+
}
|
|
2658
|
+
};
|
|
2659
|
+
document.addEventListener('selectionchange', () => {
|
|
2660
|
+
sel.update();
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2591
2663
|
/** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ */
|
|
2592
2664
|
const txtArea = document.createElement('textarea');
|
|
2593
2665
|
txtArea.setAttribute('class', 'quill-source-code');
|
|
@@ -2606,7 +2678,8 @@ export default class Component extends Element {
|
|
|
2606
2678
|
// Make sure to select cursor when they click on the element.
|
|
2607
2679
|
this.addEventListener(element, 'click', () => this.quill.focus());
|
|
2608
2680
|
// Allows users to skip toolbar items when tabbing though form
|
|
2609
|
-
const
|
|
2681
|
+
const queryRoot = element.getRootNode() || document;
|
|
2682
|
+
const elm = queryRoot.querySelectorAll('.ql-formats > button');
|
|
2610
2683
|
for (let i = 0; i < elm.length; i++) {
|
|
2611
2684
|
elm[i].setAttribute('tabindex', '-1');
|
|
2612
2685
|
}
|
|
@@ -3234,7 +3307,7 @@ export default class Component extends Element {
|
|
|
3234
3307
|
if (flags.silentCheck) {
|
|
3235
3308
|
return [];
|
|
3236
3309
|
}
|
|
3237
|
-
let isDirty = flags.dirty
|
|
3310
|
+
let isDirty = flags.dirty || this.dirty;
|
|
3238
3311
|
if (this.options.alwaysDirty) {
|
|
3239
3312
|
isDirty = true;
|
|
3240
3313
|
}
|
|
@@ -3440,21 +3513,26 @@ export default class Component extends Element {
|
|
|
3440
3513
|
}
|
|
3441
3514
|
});
|
|
3442
3515
|
this.addEventListener(element, 'blur', () => {
|
|
3443
|
-
|
|
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
|
-
});
|
|
3516
|
+
const root = this.root;
|
|
3517
|
+
if (!root) {
|
|
3518
|
+
return;
|
|
3457
3519
|
}
|
|
3520
|
+
root.pendingBlur = FormioUtils.delay(() => {
|
|
3521
|
+
if (!root) {
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
this.emit('blur', this);
|
|
3525
|
+
if (this.component.validateOn === 'blur') {
|
|
3526
|
+
root.triggerChange?.({ fromBlur: true }, {
|
|
3527
|
+
instance: this,
|
|
3528
|
+
component: this.component,
|
|
3529
|
+
value: this.dataValue,
|
|
3530
|
+
flags: { fromBlur: true },
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
root.focusedComponent = null;
|
|
3534
|
+
root.pendingBlur = null;
|
|
3535
|
+
});
|
|
3458
3536
|
});
|
|
3459
3537
|
}
|
|
3460
3538
|
setCustomValidity(messages, dirty, external) {
|
|
@@ -44,5 +44,5 @@ export default [
|
|
|
44
44
|
},
|
|
45
45
|
EditFormUtils.javaScriptValue('Advanced Conditions', 'customConditional', 'conditional.json', 110, '<p>You must assign the <strong>show</strong> variable a boolean result.</p>' +
|
|
46
46
|
'<p><strong>Note: Advanced Conditional logic will override the results of the Simple Conditional logic.</strong></p>' +
|
|
47
|
-
'<h5>Example</h5><pre>show = !!data.showMe;</pre>', '<p><a href="https://help.form.io/userguide/form-building/logic-and-conditions" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>'),
|
|
47
|
+
'<h5>Example</h5><pre>show = !!data.showMe;</pre>', '<p><a href="https://help.form.io/userguide/form-building/logic-and-conditions" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>', EditFormUtils.tokenVariableDescription()),
|
|
48
48
|
];
|
|
@@ -139,7 +139,7 @@ export default [
|
|
|
139
139
|
input: true,
|
|
140
140
|
},
|
|
141
141
|
EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 1000, '<p><h4>Example:</h4><pre>value = data.firstName + " " + data.lastName;</pre></p>', '<p><h4>Example:</h4><pre>{"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}</pre>'),
|
|
142
|
-
EditFormUtils.javaScriptValue('Calculated Value', 'calculateValue', 'calculateValue', 1100, '<p><h4>Example:</h4><pre>value = data.a + data.b + data.c;</pre></p>', '<p><h4>Example:</h4><pre>{"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}</pre><p><a href="https://help.form.io/userguide/form-building/logic-and-conditions#calculated-values" target="_blank" rel="noopener noreferrer">Click here for an example</a></p>',
|
|
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;
|
|
@@ -450,8 +450,16 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
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) {
|
|
@@ -530,8 +545,9 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
530
545
|
options.row = `${rowIndex}-${colIndex}`;
|
|
531
546
|
options.rowIndex = rowIndex;
|
|
532
547
|
options.onChange = (flags, changed, modified) => {
|
|
533
|
-
|
|
534
|
-
|
|
548
|
+
const changedComponent = changed.component;
|
|
549
|
+
if (changedComponent?.type === 'form' && changedComponent?.key) {
|
|
550
|
+
const formComp = getComponent(this.component.components, changedComponent.key);
|
|
535
551
|
_.set(formComp, 'components', changed.component.components);
|
|
536
552
|
}
|
|
537
553
|
// If we're in a nested form we need to ensure our changes are triggered upstream
|
|
@@ -539,8 +555,21 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
539
555
|
changed.instance.root.triggerChange?.(flags, changed, modified);
|
|
540
556
|
}
|
|
541
557
|
else {
|
|
558
|
+
if (modified && !flags.noPristineChangeOnModified) {
|
|
559
|
+
this.pristine = false;
|
|
560
|
+
}
|
|
561
|
+
this.triggerRootChange(flags, {
|
|
562
|
+
instance: this,
|
|
563
|
+
component: this.component,
|
|
564
|
+
value: this.dataValue,
|
|
565
|
+
flags,
|
|
566
|
+
}, modified);
|
|
542
567
|
this.triggerRootChange(flags, changed, modified);
|
|
543
568
|
}
|
|
569
|
+
this.processRow('checkData', null, {
|
|
570
|
+
...flags,
|
|
571
|
+
changed,
|
|
572
|
+
}, row, _.toArray(this.rows[rowIndex]));
|
|
544
573
|
};
|
|
545
574
|
let columnComponent;
|
|
546
575
|
if (this.builderMode) {
|
|
@@ -15,14 +15,11 @@ export default class DataMapComponent extends DataGridComponent {
|
|
|
15
15
|
disableBuilderActions: boolean;
|
|
16
16
|
};
|
|
17
17
|
get valueKey(): any;
|
|
18
|
-
get iteratableRows(): {
|
|
19
|
-
components: any;
|
|
20
|
-
data: any;
|
|
21
|
-
}[][];
|
|
22
18
|
hasHeader(): boolean;
|
|
23
19
|
getRowKey(rowIndex: any): string;
|
|
24
20
|
get defaultRowKey(): string;
|
|
25
21
|
getValueAsString(value: any, options: any): any;
|
|
22
|
+
findComponentInstance(key: any): any;
|
|
26
23
|
createRowComponents(row: any, rowIndex: any): {
|
|
27
24
|
__key: any;
|
|
28
25
|
};
|
|
@@ -118,15 +118,14 @@ export default class DataMapComponent extends DataGridComponent {
|
|
|
118
118
|
}
|
|
119
119
|
get iteratableRows() {
|
|
120
120
|
return this.rows.map((row) => {
|
|
121
|
-
return
|
|
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() {
|
|
@@ -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)))
|
|
@@ -5,8 +5,10 @@ declare const _default: ({
|
|
|
5
5
|
label: string;
|
|
6
6
|
weight: number;
|
|
7
7
|
tooltip: string;
|
|
8
|
+
validate: {
|
|
9
|
+
custom: string;
|
|
10
|
+
};
|
|
8
11
|
placeholder?: undefined;
|
|
9
|
-
validate?: undefined;
|
|
10
12
|
title?: undefined;
|
|
11
13
|
collapsible?: undefined;
|
|
12
14
|
collapsed?: undefined;
|
|
@@ -57,7 +59,22 @@ declare const _default: ({
|
|
|
57
59
|
label?: undefined;
|
|
58
60
|
weight?: undefined;
|
|
59
61
|
tooltip?: undefined;
|
|
62
|
+
validate?: undefined;
|
|
60
63
|
placeholder?: undefined;
|
|
64
|
+
} | {
|
|
65
|
+
type: string;
|
|
66
|
+
input: boolean;
|
|
67
|
+
key: string;
|
|
68
|
+
label: string;
|
|
69
|
+
tooltip: string;
|
|
70
|
+
weight: number;
|
|
61
71
|
validate?: undefined;
|
|
72
|
+
placeholder?: undefined;
|
|
73
|
+
title?: undefined;
|
|
74
|
+
collapsible?: undefined;
|
|
75
|
+
collapsed?: undefined;
|
|
76
|
+
style?: undefined;
|
|
77
|
+
customConditional?: undefined;
|
|
78
|
+
components?: undefined;
|
|
62
79
|
})[];
|
|
63
80
|
export default _default;
|
|
@@ -8,6 +8,9 @@ export default [
|
|
|
8
8
|
label: 'Enable Date Input',
|
|
9
9
|
weight: 0,
|
|
10
10
|
tooltip: 'Enables date input for this field.',
|
|
11
|
+
validate: {
|
|
12
|
+
custom: "valid = !data.enableTime && !input ? 'Cannot disable both Date and Time inputs at the same time' : true",
|
|
13
|
+
},
|
|
11
14
|
},
|
|
12
15
|
{
|
|
13
16
|
type: 'tags',
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
declare const _default: {
|
|
1
|
+
declare const _default: ({
|
|
2
2
|
type: string;
|
|
3
3
|
input: boolean;
|
|
4
4
|
key: string;
|
|
5
5
|
label: string;
|
|
6
6
|
tooltip: string;
|
|
7
7
|
weight: number;
|
|
8
|
-
|
|
8
|
+
validate: {
|
|
9
|
+
custom: string;
|
|
10
|
+
};
|
|
11
|
+
} | {
|
|
12
|
+
type: string;
|
|
13
|
+
input: boolean;
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
tooltip: string;
|
|
17
|
+
weight: number;
|
|
18
|
+
validate?: undefined;
|
|
19
|
+
})[];
|
|
9
20
|
export default _default;
|
|
@@ -6,6 +6,9 @@ export default [
|
|
|
6
6
|
label: 'Enable Time Input',
|
|
7
7
|
tooltip: 'Enables time input for this field.',
|
|
8
8
|
weight: 0,
|
|
9
|
+
validate: {
|
|
10
|
+
custom: "valid = !data.enableDate && !input ? 'Cannot disable both Date and Time inputs at the same time' : true",
|
|
11
|
+
},
|
|
9
12
|
},
|
|
10
13
|
{
|
|
11
14
|
type: 'number',
|
|
@@ -57,10 +57,6 @@ export default class DayComponent extends Field {
|
|
|
57
57
|
value: string;
|
|
58
58
|
label: any;
|
|
59
59
|
}[];
|
|
60
|
-
_days: {
|
|
61
|
-
value: string;
|
|
62
|
-
label: any;
|
|
63
|
-
}[] | undefined;
|
|
64
60
|
get months(): ({
|
|
65
61
|
value: string;
|
|
66
62
|
label: any;
|
|
@@ -68,21 +64,10 @@ export default class DayComponent extends Field {
|
|
|
68
64
|
value: number;
|
|
69
65
|
label: string;
|
|
70
66
|
})[];
|
|
71
|
-
_months: ({
|
|
72
|
-
value: string;
|
|
73
|
-
label: any;
|
|
74
|
-
} | {
|
|
75
|
-
value: number;
|
|
76
|
-
label: string;
|
|
77
|
-
})[] | undefined;
|
|
78
67
|
get years(): {
|
|
79
68
|
value: string;
|
|
80
69
|
label: any;
|
|
81
70
|
}[];
|
|
82
|
-
_years: {
|
|
83
|
-
value: string;
|
|
84
|
-
label: any;
|
|
85
|
-
}[] | undefined;
|
|
86
71
|
setErrorClasses(elements: any, dirty: any, hasError: any): void;
|
|
87
72
|
dayFirst: any;
|
|
88
73
|
render(): string;
|