@formio/js 5.0.0-rc.37 → 5.0.0-rc.38
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/fonts/bootstrap-icons.woff +0 -0
- package/dist/fonts/bootstrap-icons.woff2 +0 -0
- 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.js +853 -132
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.form.min.js.LICENSE.txt +5 -3
- package/dist/formio.full.css +4 -4
- package/dist/formio.full.js +827 -136
- package/dist/formio.full.min.css +3 -3
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.full.min.js.LICENSE.txt +5 -3
- package/dist/formio.js +4 -4
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +1 -1
- package/dist/formio.utils.js +25 -14
- package/dist/formio.utils.min.js +1 -1
- package/dist/formio.utils.min.js.LICENSE.txt +5 -3
- package/lib/cjs/Element.js +2 -2
- package/lib/cjs/components/_classes/component/Component.js +21 -7
- package/lib/cjs/components/_classes/component/fixtures/comp5.js +2 -2
- package/lib/cjs/components/_classes/list/ListComponent.js +5 -12
- package/lib/cjs/components/_classes/nested/NestedComponent.js +17 -9
- package/lib/cjs/components/columns/editForm/Columns.edit.display.js +1 -1
- package/lib/cjs/components/editgrid/EditGrid.js +11 -4
- package/lib/cjs/components/form/Form.js +3 -1
- package/lib/cjs/components/html/HTML.js +2 -2
- package/lib/cjs/components/html/fixtures/comp3.js +31 -0
- package/lib/cjs/components/html/fixtures/index.js +3 -1
- package/lib/cjs/components/radio/Radio.js +2 -1
- package/lib/cjs/components/radio/fixtures/comp10.js +23 -0
- package/lib/cjs/components/radio/fixtures/index.js +3 -1
- package/lib/cjs/components/recaptcha/ReCaptcha.js +3 -0
- package/lib/cjs/components/select/Select.js +84 -9
- package/lib/cjs/components/survey/Survey.js +10 -0
- package/lib/cjs/utils/conditionOperators/IsEqualTo.js +19 -1
- package/lib/cjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
- package/lib/cjs/utils/utils.js +40 -2
- package/lib/mjs/Element.js +2 -2
- package/lib/mjs/components/_classes/component/Component.js +24 -8
- package/lib/mjs/components/_classes/component/fixtures/comp5.js +2 -2
- package/lib/mjs/components/_classes/list/ListComponent.js +5 -12
- package/lib/mjs/components/_classes/nested/NestedComponent.js +17 -9
- package/lib/mjs/components/columns/editForm/Columns.edit.display.js +1 -1
- package/lib/mjs/components/editgrid/EditGrid.js +11 -4
- package/lib/mjs/components/form/Form.js +3 -1
- package/lib/mjs/components/html/HTML.js +2 -2
- package/lib/mjs/components/html/fixtures/comp3.js +29 -0
- package/lib/mjs/components/html/fixtures/index.js +2 -1
- package/lib/mjs/components/radio/Radio.js +2 -1
- package/lib/mjs/components/radio/fixtures/comp10.js +21 -0
- package/lib/mjs/components/radio/fixtures/index.js +2 -1
- package/lib/mjs/components/recaptcha/ReCaptcha.js +3 -0
- package/lib/mjs/components/select/Select.js +83 -10
- package/lib/mjs/components/survey/Survey.js +10 -0
- package/lib/mjs/utils/conditionOperators/IsEqualTo.js +18 -1
- package/lib/mjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
- package/lib/mjs/utils/utils.js +35 -0
- package/package.json +2 -2
- package/types/formio.d.ts +4 -0
package/lib/cjs/utils/utils.js
CHANGED
@@ -30,8 +30,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
30
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
31
31
|
};
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
33
|
-
exports.
|
34
|
-
exports.interpolateErrors = exports.getComponentSavedTypes = exports.componentValueTypes = exports._ = exports.getFocusableElements = exports.isInsideScopingComponent = exports.isPromise = exports.getDataParentComponent = exports.getComponentPath = exports.getComponentPathWithoutIndicies = exports.getBrowserInfo = exports.getIEBrowserVersion = exports.round = exports.getStringFromComponentPath = exports.isChildOf = exports.getArrayFromComponentPath = exports.isInputComponent = exports.interpolate = exports.Evaluator = exports.fastCloneDeep = exports.sanitize = exports.translateHTMLTemplate = exports.getContextButtons = exports.getContextComponents = void 0;
|
33
|
+
exports.withSwitch = exports.firstNonNil = exports.unfold = exports.bootstrapVersion = exports.uniqueKey = exports.iterateKey = exports.delay = exports.fieldData = exports.getCurrencyAffixes = exports.getNumberDecimalLimit = exports.getNumberSeparators = exports.matchInputMask = exports.unmaskValue = exports.getInputMask = exports.convertFormatToMask = exports.convertFormatToMoment = exports.convertFormatToFlatpickr = exports.getLocaleDateFormatInfo = exports.formatOffset = exports.formatDate = exports.momentDate = exports.loadZones = exports.shouldLoadZones = exports.zonesLoaded = exports.offsetDate = exports.currentTimezone = exports.isValidDate = exports.getDateSetting = exports.guid = exports.uniqueName = exports.convertStringToHTMLElement = exports.escapeHTML = exports.unescapeHTML = exports.setActionProperty = exports.checkTrigger = exports.checkCondition = exports.checkJsonConditional = exports.checkCustomConditional = exports.getComponentActualValue = exports.checkSimpleConditional = exports.checkCalculated = exports.isMongoId = exports.boolValue = exports.getElementRect = exports.getPropertyValue = exports.getRandomComponentId = exports.evaluate = exports.moment = exports.ConditionOperators = exports.jsonLogic = void 0;
|
34
|
+
exports.isSelectResourceWithObjectValue = exports.getItemTemplateKeys = exports.interpolateErrors = exports.getComponentSavedTypes = exports.componentValueTypes = exports._ = exports.getFocusableElements = exports.isInsideScopingComponent = exports.isPromise = exports.getDataParentComponent = exports.getComponentPath = exports.getComponentPathWithoutIndicies = exports.getBrowserInfo = exports.getIEBrowserVersion = exports.round = exports.getStringFromComponentPath = exports.isChildOf = exports.getArrayFromComponentPath = exports.isInputComponent = exports.interpolate = exports.Evaluator = exports.fastCloneDeep = exports.sanitize = exports.translateHTMLTemplate = exports.getContextButtons = exports.getContextComponents = exports.observeOverload = void 0;
|
35
35
|
const lodash_1 = __importDefault(require("lodash"));
|
36
36
|
exports._ = lodash_1.default;
|
37
37
|
const fetch_ponyfill_1 = __importDefault(require("fetch-ponyfill"));
|
@@ -447,6 +447,22 @@ function unescapeHTML(str) {
|
|
447
447
|
return doc.documentElement.textContent;
|
448
448
|
}
|
449
449
|
exports.unescapeHTML = unescapeHTML;
|
450
|
+
/**
|
451
|
+
* Escape HTML characters like <, >, & and etc.
|
452
|
+
* @param str
|
453
|
+
* @returns {string}
|
454
|
+
*/
|
455
|
+
function escapeHTML(html) {
|
456
|
+
if (html) {
|
457
|
+
return html.replace(/&/g, '&')
|
458
|
+
.replace(/</g, '<')
|
459
|
+
.replace(/>/g, '>')
|
460
|
+
.replace(/"/g, '"')
|
461
|
+
.replace(/'/g, ''');
|
462
|
+
}
|
463
|
+
return '';
|
464
|
+
}
|
465
|
+
exports.escapeHTML = escapeHTML;
|
450
466
|
/**
|
451
467
|
* Make HTML element from string
|
452
468
|
* @param str
|
@@ -1509,3 +1525,25 @@ const interpolateErrors = (component, errors, interpolateFn) => {
|
|
1509
1525
|
});
|
1510
1526
|
};
|
1511
1527
|
exports.interpolateErrors = interpolateErrors;
|
1528
|
+
function getItemTemplateKeys(template) {
|
1529
|
+
const templateKeys = [];
|
1530
|
+
if (!template) {
|
1531
|
+
return templateKeys;
|
1532
|
+
}
|
1533
|
+
const keys = template.match(/({{\s*(.*?)\s*}})/g);
|
1534
|
+
if (keys) {
|
1535
|
+
keys.forEach((key) => {
|
1536
|
+
const propKey = key.match(/{{\s*item\.(.*?)\s*}}/);
|
1537
|
+
if (propKey && propKey.length > 1) {
|
1538
|
+
templateKeys.push(propKey[1]);
|
1539
|
+
}
|
1540
|
+
});
|
1541
|
+
}
|
1542
|
+
return templateKeys;
|
1543
|
+
}
|
1544
|
+
exports.getItemTemplateKeys = getItemTemplateKeys;
|
1545
|
+
function isSelectResourceWithObjectValue(comp = {}) {
|
1546
|
+
const { reference, dataSrc, valueProperty } = comp;
|
1547
|
+
return reference || (dataSrc === 'resource' && (!valueProperty || valueProperty === 'data'));
|
1548
|
+
}
|
1549
|
+
exports.isSelectResourceWithObjectValue = isSelectResourceWithObjectValue;
|
package/lib/mjs/Element.js
CHANGED
@@ -179,7 +179,7 @@ export default class Element {
|
|
179
179
|
* @param persistent
|
180
180
|
* If this listener should persist beyond "destroy" commands.
|
181
181
|
*/
|
182
|
-
addEventListener(obj, type, func, persistent) {
|
182
|
+
addEventListener(obj, type, func, persistent, capture) {
|
183
183
|
if (!obj) {
|
184
184
|
return;
|
185
185
|
}
|
@@ -187,7 +187,7 @@ export default class Element {
|
|
187
187
|
this.eventHandlers.push({ id: this.id, obj, type, func });
|
188
188
|
}
|
189
189
|
if ('addEventListener' in obj) {
|
190
|
-
obj.addEventListener(type, func,
|
190
|
+
obj.addEventListener(type, func, !!capture);
|
191
191
|
}
|
192
192
|
else if ('attachEvent' in obj) {
|
193
193
|
obj.attachEvent(`on${type}`, func);
|
@@ -3,7 +3,7 @@ import { conformToMask } from '@formio/vanilla-text-mask';
|
|
3
3
|
import tippy from 'tippy.js';
|
4
4
|
import _ from 'lodash';
|
5
5
|
import isMobile from 'ismobilejs';
|
6
|
-
import { processOne, processOneSync,
|
6
|
+
import { processOne, processOneSync, validateProcessInfo } from '@formio/core/process';
|
7
7
|
import { Formio } from '../../../Formio';
|
8
8
|
import * as FormioUtils from '../../../utils/utils';
|
9
9
|
import { fastCloneDeep, boolValue, getComponentPath, isInsideScopingComponent, currentTimezone } from '../../../utils/utils';
|
@@ -819,7 +819,13 @@ export default class Component extends Element {
|
|
819
819
|
renderTemplate(name, data = {}, modeOption) {
|
820
820
|
// Need to make this fall back to form if renderMode is not found similar to how we search templates.
|
821
821
|
const mode = modeOption || this.options.renderMode || 'form';
|
822
|
-
data.component =
|
822
|
+
data.component = {
|
823
|
+
...this.component,
|
824
|
+
};
|
825
|
+
// Escape HTML provided in component description and render it as a string instead
|
826
|
+
if (this.component.description) {
|
827
|
+
data.component.description = FormioUtils.escapeHTML(this.component.description);
|
828
|
+
}
|
823
829
|
data.self = this;
|
824
830
|
data.options = this.options;
|
825
831
|
data.readOnly = this.options.readOnly;
|
@@ -1030,12 +1036,12 @@ export default class Component extends Element {
|
|
1030
1036
|
const tooltipText = this.interpolate(tooltipDataTitle || tooltipAttribute)
|
1031
1037
|
.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
1032
1038
|
this.tooltips[index] = tippy(tooltip, {
|
1033
|
-
allowHTML:
|
1039
|
+
allowHTML: false,
|
1034
1040
|
trigger: 'mouseenter click focus',
|
1035
1041
|
placement: 'right',
|
1036
1042
|
zIndex: 10000,
|
1037
1043
|
interactive: true,
|
1038
|
-
content: this.t(
|
1044
|
+
content: this.t(tooltipText, { _userInput: true }),
|
1039
1045
|
});
|
1040
1046
|
}
|
1041
1047
|
});
|
@@ -2574,7 +2580,7 @@ export default class Component extends Element {
|
|
2574
2580
|
scope: validationScope,
|
2575
2581
|
instance: this,
|
2576
2582
|
processors: [
|
2577
|
-
|
2583
|
+
validateProcessInfo
|
2578
2584
|
]
|
2579
2585
|
});
|
2580
2586
|
const errors = validationScope.errors;
|
@@ -2655,7 +2661,7 @@ export default class Component extends Element {
|
|
2655
2661
|
instance: this,
|
2656
2662
|
scope: { errors: [] },
|
2657
2663
|
processors: [
|
2658
|
-
|
2664
|
+
validateProcessInfo
|
2659
2665
|
]
|
2660
2666
|
};
|
2661
2667
|
if (async) {
|
@@ -2684,7 +2690,12 @@ export default class Component extends Element {
|
|
2684
2690
|
return this.validateComponent(data, row, flags).then((errors) => {
|
2685
2691
|
allErrors.push(...errors);
|
2686
2692
|
if (this.parent && this.parent.childErrors) {
|
2687
|
-
|
2693
|
+
if (errors.length) {
|
2694
|
+
this.parent.childErrors.push(...errors);
|
2695
|
+
}
|
2696
|
+
else {
|
2697
|
+
_.remove(this.parent.childErrors, (err) => err.component.key === this.component.key);
|
2698
|
+
}
|
2688
2699
|
}
|
2689
2700
|
this.showValidationErrors(errors, data, row, flags);
|
2690
2701
|
return errors.length === 0;
|
@@ -2695,7 +2706,12 @@ export default class Component extends Element {
|
|
2695
2706
|
this.showValidationErrors(errors, data, row, flags);
|
2696
2707
|
allErrors.push(...errors);
|
2697
2708
|
if (this.parent && this.parent.childErrors) {
|
2698
|
-
|
2709
|
+
if (errors.length) {
|
2710
|
+
this.parent.childErrors.push(...errors);
|
2711
|
+
}
|
2712
|
+
else {
|
2713
|
+
_.remove(this.parent.childErrors, (err) => err.component.key === this.component.key);
|
2714
|
+
}
|
2699
2715
|
}
|
2700
2716
|
return errors.length === 0;
|
2701
2717
|
}
|
@@ -4,8 +4,8 @@ export default {
|
|
4
4
|
components: [
|
5
5
|
{
|
6
6
|
label: 'Text Field',
|
7
|
-
description: "<img
|
8
|
-
tooltip: "<img src='https://somesite' onerror='var _ee = 1 >",
|
7
|
+
description: "<img src='https://somesite' onerror='var _ee = 2' >",
|
8
|
+
tooltip: "<img src='https://somesite' onerror='var _ee = 1' >",
|
9
9
|
applyMaskOn: 'change',
|
10
10
|
tableView: true,
|
11
11
|
key: 'textField',
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import Field from '../field/Field';
|
2
2
|
import { Formio } from '../../../Formio';
|
3
3
|
import _ from 'lodash';
|
4
|
+
import { getItemTemplateKeys } from '../../../utils/utils';
|
4
5
|
export default class ListComponent extends Field {
|
5
6
|
static schema(...extend) {
|
6
7
|
return Field.schema({
|
@@ -43,18 +44,10 @@ export default class ListComponent extends Field {
|
|
43
44
|
return true;
|
44
45
|
}
|
45
46
|
getTemplateKeys() {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
keys.forEach((key) => {
|
51
|
-
const propKey = key.match(/{{\s*item\.(.*?)\s*}}/);
|
52
|
-
if (propKey && propKey.length > 1) {
|
53
|
-
this.templateKeys.push(propKey[1]);
|
54
|
-
}
|
55
|
-
});
|
56
|
-
}
|
57
|
-
}
|
47
|
+
const template = this.component.template;
|
48
|
+
this.templateKeys = this.options.readOnly && template
|
49
|
+
? getItemTemplateKeys(template)
|
50
|
+
: [];
|
58
51
|
}
|
59
52
|
get requestHeaders() {
|
60
53
|
// Create the headers object.
|
@@ -582,6 +582,16 @@ export default class NestedComponent extends Field {
|
|
582
582
|
components = components || this.component.components;
|
583
583
|
data = data || this.rootValue;
|
584
584
|
const { async, dirty, process } = flags;
|
585
|
+
const validationProcessorProcess = (context) => this.validationProcessor(context, flags);
|
586
|
+
const checkModalProcessorProcess = ({ instance, component, components }) => {
|
587
|
+
// If we just validated the last component, and there are errors from our parent, then we need to show a model of those errors.
|
588
|
+
if (instance &&
|
589
|
+
instance.parent &&
|
590
|
+
(component === components[components.length - 1]) &&
|
591
|
+
instance.parent.componentModal) {
|
592
|
+
instance.parent.checkModal(instance.parent.childErrors, dirty);
|
593
|
+
}
|
594
|
+
};
|
585
595
|
const processorContext = {
|
586
596
|
process: process || 'unknown',
|
587
597
|
components,
|
@@ -589,15 +599,13 @@ export default class NestedComponent extends Field {
|
|
589
599
|
data: data,
|
590
600
|
scope: { errors: [] },
|
591
601
|
processors: [
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
instance.parent.checkModal(instance.parent.childErrors, dirty);
|
600
|
-
}
|
602
|
+
{
|
603
|
+
process: validationProcessorProcess,
|
604
|
+
processSync: validationProcessorProcess
|
605
|
+
},
|
606
|
+
{
|
607
|
+
process: checkModalProcessorProcess,
|
608
|
+
processSync: checkModalProcessorProcess
|
601
609
|
}
|
602
610
|
]
|
603
611
|
};
|
@@ -56,7 +56,7 @@ export default [
|
|
56
56
|
key: 'columns',
|
57
57
|
label: 'Column Properties',
|
58
58
|
addAnother: 'Add Column',
|
59
|
-
tooltip: 'The
|
59
|
+
tooltip: 'The size and width settings for each column. One row is equal to 12. (e.g., a row with two columns spanning the entire page should be 6 and 6)',
|
60
60
|
reorder: true,
|
61
61
|
components: [
|
62
62
|
{
|
@@ -467,7 +467,7 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
467
467
|
].forEach(({ className, event, action, }) => {
|
468
468
|
const elements = row.getElementsByClassName(className);
|
469
469
|
Array.prototype.forEach.call(elements, (element) => {
|
470
|
-
if (this.options.
|
470
|
+
if (this.options.pdf && _.intersection(element.classList, ['editRow', 'removeRow']).length) {
|
471
471
|
element.style.display = 'none';
|
472
472
|
}
|
473
473
|
else {
|
@@ -599,6 +599,9 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
599
599
|
const dataObj = {};
|
600
600
|
const rowIndex = this.editRows.length;
|
601
601
|
const editRow = this.createRow(dataObj, rowIndex);
|
602
|
+
if (editRow.state === EditRowState.New) {
|
603
|
+
this.emptyRow = fastCloneDeep(editRow.data);
|
604
|
+
}
|
602
605
|
if (this.inlineEditMode) {
|
603
606
|
this.triggerChange();
|
604
607
|
}
|
@@ -669,7 +672,7 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
669
672
|
}
|
670
673
|
showDialog(rowIndex) {
|
671
674
|
const editRow = this.editRows[rowIndex];
|
672
|
-
if (_.isEqual(editRow.backup, editRow.data)) {
|
675
|
+
if (editRow.state === EditRowState.New ? _.isEqual(this.emptyRow, editRow.data) : _.isEqual(editRow.backup, editRow.data)) {
|
673
676
|
return Promise.resolve();
|
674
677
|
}
|
675
678
|
const wrapper = this.ce('div', { ref: 'confirmationDialog' });
|
@@ -969,6 +972,7 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
969
972
|
const editGridValue = _.get(rootValue, this.path, []);
|
970
973
|
editGridValue[editRow.rowIndex] = editRow.data;
|
971
974
|
_.set(rootValue, this.path, editGridValue);
|
975
|
+
const validationProcessorProcess = (context) => this.validationProcessor(context, { dirty, silentCheck });
|
972
976
|
editRow.errors = processSync({
|
973
977
|
components: fastCloneDeep(this.component.components).map((component) => {
|
974
978
|
component.parentPath = `${this.path}[${editRow.rowIndex}]`;
|
@@ -980,7 +984,10 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
980
984
|
instances: this.componentsMap,
|
981
985
|
scope: { errors: [] },
|
982
986
|
processors: [
|
983
|
-
|
987
|
+
{
|
988
|
+
process: validationProcessorProcess,
|
989
|
+
processSync: validationProcessorProcess
|
990
|
+
}
|
984
991
|
]
|
985
992
|
}).errors;
|
986
993
|
}
|
@@ -1070,7 +1077,7 @@ export default class EditGridComponent extends NestedArrayComponent {
|
|
1070
1077
|
this.setCustomValidity(this.t(this.errorMessage('unsavedRowsError')), dirty);
|
1071
1078
|
return false;
|
1072
1079
|
}
|
1073
|
-
const message = this.invalid || this.invalidMessage(data, dirty);
|
1080
|
+
const message = this.invalid || this.invalidMessage(data, dirty, false, row);
|
1074
1081
|
if (allRowErrors.length && this.root?.submitted && !message) {
|
1075
1082
|
this._errors = this.setCustomValidity(message, dirty);
|
1076
1083
|
errors.push(...this._errors);
|
@@ -600,7 +600,9 @@ export default class FormComponent extends Component {
|
|
600
600
|
const formId = submission.form || this.formObj.form || this.component.form;
|
601
601
|
const submissionUrl = `${this.subForm.formio.formsUrl}/${formId}/submission/${submission._id}`;
|
602
602
|
this.subForm.setUrl(submissionUrl, this.options);
|
603
|
-
this.subForm.loadSubmission()
|
603
|
+
this.subForm.loadSubmission().catch((err) => {
|
604
|
+
console.error(`Unable to load subform submission ${submission._id}:`, err);
|
605
|
+
});
|
604
606
|
}
|
605
607
|
else {
|
606
608
|
this.subForm.setValue(submission, flags);
|
@@ -38,13 +38,13 @@ export default class HTMLComponent extends Component {
|
|
38
38
|
return ` ${this.component.content} `;
|
39
39
|
}
|
40
40
|
const submission = _.get(this.root, 'submission', {});
|
41
|
-
const content = this.component.content ? this.interpolate(this.component.content, {
|
41
|
+
const content = this.component.content ? this.interpolate(this.sanitize(this.component.content, this.shouldSanitizeValue), {
|
42
42
|
metadata: submission.metadata || {},
|
43
43
|
submission: submission,
|
44
44
|
data: this.rootValue,
|
45
45
|
row: this.data
|
46
46
|
}) : '';
|
47
|
-
return
|
47
|
+
return content;
|
48
48
|
}
|
49
49
|
get singleTags() {
|
50
50
|
return ['br', 'img', 'hr'];
|
@@ -0,0 +1,29 @@
|
|
1
|
+
export default {
|
2
|
+
type: 'form',
|
3
|
+
display: 'form',
|
4
|
+
components: [
|
5
|
+
{
|
6
|
+
label: 'HTML',
|
7
|
+
attrs: [
|
8
|
+
{
|
9
|
+
attr: '',
|
10
|
+
value: '',
|
11
|
+
},
|
12
|
+
],
|
13
|
+
content: '<img src=1 onerror=alert("htmlContent")>',
|
14
|
+
refreshOnChange: false,
|
15
|
+
key: 'html',
|
16
|
+
type: 'htmlelement',
|
17
|
+
input: false,
|
18
|
+
tableView: false,
|
19
|
+
},
|
20
|
+
{
|
21
|
+
type: 'button',
|
22
|
+
label: 'Submit',
|
23
|
+
key: 'submit',
|
24
|
+
disableOnInvalid: true,
|
25
|
+
input: true,
|
26
|
+
tableView: false,
|
27
|
+
},
|
28
|
+
],
|
29
|
+
};
|
@@ -349,7 +349,8 @@ export default class RadioComponent extends ListComponent {
|
|
349
349
|
if (value === this.emptyValue) {
|
350
350
|
return value;
|
351
351
|
}
|
352
|
-
|
352
|
+
const isEquivalent = _.toString(value) === Number(value).toString();
|
353
|
+
if (!isNaN(parseFloat(value)) && isFinite(value) && isEquivalent) {
|
353
354
|
value = +value;
|
354
355
|
}
|
355
356
|
if (value === 'true') {
|
@@ -0,0 +1,21 @@
|
|
1
|
+
export default {
|
2
|
+
'label': 'Radio',
|
3
|
+
'optionsLabelPosition': 'right',
|
4
|
+
'inline': false,
|
5
|
+
'tableView': false,
|
6
|
+
'values': [
|
7
|
+
{
|
8
|
+
'label': '01',
|
9
|
+
'value': '01',
|
10
|
+
'shortcut': ''
|
11
|
+
},
|
12
|
+
{
|
13
|
+
'label': '1',
|
14
|
+
'value': '1',
|
15
|
+
'shortcut': ''
|
16
|
+
}
|
17
|
+
],
|
18
|
+
'key': 'radio',
|
19
|
+
'type': 'radio',
|
20
|
+
'input': true
|
21
|
+
};
|
@@ -7,4 +7,5 @@ import comp6 from './comp6';
|
|
7
7
|
import comp7 from './comp7';
|
8
8
|
import comp8 from './comp8';
|
9
9
|
import comp9 from './comp9';
|
10
|
-
|
10
|
+
import comp10 from './comp10';
|
11
|
+
export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9, comp10 };
|
@@ -3,7 +3,7 @@ import { Formio } from '../../Formio';
|
|
3
3
|
import ListComponent from '../_classes/list/ListComponent';
|
4
4
|
import Input from '../_classes/input/Input';
|
5
5
|
import Form from '../../Form';
|
6
|
-
import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes } from '../../utils/utils';
|
6
|
+
import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes, unescapeHTML, isSelectResourceWithObjectValue } from '../../utils/utils';
|
7
7
|
import Choices from '../../utils/ChoicesWrapper';
|
8
8
|
export default class SelectComponent extends ListComponent {
|
9
9
|
static schema(...extend) {
|
@@ -61,7 +61,19 @@ export default class SelectComponent extends ListComponent {
|
|
61
61
|
return {
|
62
62
|
...super.conditionOperatorsSettings,
|
63
63
|
valueComponent(classComp) {
|
64
|
-
|
64
|
+
const valueComp = { ...classComp, type: 'select' };
|
65
|
+
if (isSelectResourceWithObjectValue(classComp)) {
|
66
|
+
valueComp.reference = false;
|
67
|
+
valueComp.onSetItems = `
|
68
|
+
var templateKeys = utils.getItemTemplateKeys(component.template) || [];
|
69
|
+
items = _.map(items || [], i => {
|
70
|
+
var item = {};
|
71
|
+
_.each(templateKeys, k => _.set(item, k, _.get(i, k)));
|
72
|
+
return item;
|
73
|
+
})
|
74
|
+
`;
|
75
|
+
}
|
76
|
+
return valueComp;
|
65
77
|
}
|
66
78
|
};
|
67
79
|
}
|
@@ -120,6 +132,7 @@ export default class SelectComponent extends ListComponent {
|
|
120
132
|
this.itemsLoaded = new Promise((resolve) => {
|
121
133
|
this.itemsLoadedResolve = resolve;
|
122
134
|
});
|
135
|
+
this.shouldPositionDropdown = this.hasDataGridAncestor();
|
123
136
|
if (this.isHtmlRenderMode()) {
|
124
137
|
this.activate();
|
125
138
|
}
|
@@ -851,6 +864,11 @@ export default class SelectComponent extends ListComponent {
|
|
851
864
|
});
|
852
865
|
}
|
853
866
|
}
|
867
|
+
if (window && this.choices && this.shouldPositionDropdown) {
|
868
|
+
this.addEventListener(window.document, 'scroll', () => {
|
869
|
+
this.positionDropdown(true);
|
870
|
+
}, false, true);
|
871
|
+
}
|
854
872
|
this.focusableElement.setAttribute('tabIndex', tabIndex);
|
855
873
|
// If a search field is provided, then add an event listener to update items on search.
|
856
874
|
if (this.component.searchField) {
|
@@ -880,7 +898,10 @@ export default class SelectComponent extends ListComponent {
|
|
880
898
|
const updateComponent = (evt) => {
|
881
899
|
this.triggerUpdate(evt.detail.value);
|
882
900
|
};
|
883
|
-
this.addEventListener(input, 'search', _.debounce(
|
901
|
+
this.addEventListener(input, 'search', _.debounce((e) => {
|
902
|
+
updateComponent(e);
|
903
|
+
this.positionDropdown();
|
904
|
+
}, debounceTimeout));
|
884
905
|
this.addEventListener(input, 'stopSearch', () => this.triggerUpdate());
|
885
906
|
this.addEventListener(input, 'hideDropdown', () => {
|
886
907
|
if (this.choices && this.choices.input && this.choices.input.element) {
|
@@ -889,7 +910,15 @@ export default class SelectComponent extends ListComponent {
|
|
889
910
|
this.updateItems(null, true);
|
890
911
|
});
|
891
912
|
}
|
892
|
-
this.addEventListener(input, 'showDropdown', () =>
|
913
|
+
this.addEventListener(input, 'showDropdown', () => {
|
914
|
+
this.update();
|
915
|
+
this.positionDropdown();
|
916
|
+
});
|
917
|
+
if (this.shouldPositionDropdown) {
|
918
|
+
this.addEventListener(input, 'highlightChoice', () => {
|
919
|
+
this.positionDropdown();
|
920
|
+
});
|
921
|
+
}
|
893
922
|
if (this.choices && choicesOptions.placeholderValue && this.choices._isSelectOneElement) {
|
894
923
|
this.addPlaceholderItem(choicesOptions.placeholderValue);
|
895
924
|
this.addEventListener(input, 'removeItem', () => {
|
@@ -926,6 +955,44 @@ export default class SelectComponent extends ListComponent {
|
|
926
955
|
this.triggerUpdate();
|
927
956
|
return superAttach;
|
928
957
|
}
|
958
|
+
setDropdownPosition() {
|
959
|
+
const dropdown = this.choices?.dropdown?.element;
|
960
|
+
const container = this.choices?.containerOuter?.element;
|
961
|
+
if (!dropdown || !container) {
|
962
|
+
return;
|
963
|
+
}
|
964
|
+
const containerPosition = container.getBoundingClientRect();
|
965
|
+
const isFlipped = container.classList.contains('is-flipped');
|
966
|
+
_.assign(dropdown.style, {
|
967
|
+
top: `${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px`,
|
968
|
+
left: `${containerPosition.left}px`,
|
969
|
+
width: `${containerPosition.width}px`,
|
970
|
+
position: 'fixed',
|
971
|
+
bottom: 'unset',
|
972
|
+
right: 'unset',
|
973
|
+
});
|
974
|
+
}
|
975
|
+
hasDataGridAncestor(comp) {
|
976
|
+
comp = comp || this;
|
977
|
+
if (comp.inDataGrid || comp.type === 'datagrid') {
|
978
|
+
return true;
|
979
|
+
}
|
980
|
+
else if (comp.parent) {
|
981
|
+
return this.hasDataGridAncestor(comp.parent);
|
982
|
+
}
|
983
|
+
else {
|
984
|
+
return false;
|
985
|
+
}
|
986
|
+
}
|
987
|
+
positionDropdown(scroll) {
|
988
|
+
if (!this.shouldPositionDropdown || !this.choices || (!this.choices.dropdown?.isActive && scroll)) {
|
989
|
+
return;
|
990
|
+
}
|
991
|
+
this.setDropdownPosition();
|
992
|
+
this.itemsLoaded.then(() => {
|
993
|
+
this.setDropdownPosition();
|
994
|
+
});
|
995
|
+
}
|
929
996
|
get isLoadingAvailable() {
|
930
997
|
return !this.isScrollLoading && this.additionalResourcesAvailable;
|
931
998
|
}
|
@@ -1043,10 +1110,10 @@ export default class SelectComponent extends ListComponent {
|
|
1043
1110
|
}
|
1044
1111
|
return added;
|
1045
1112
|
}
|
1046
|
-
getValueAsString(data) {
|
1113
|
+
getValueAsString(data, options) {
|
1047
1114
|
return (this.component.multiple && Array.isArray(data))
|
1048
|
-
? data.map(this.asString
|
1049
|
-
: this.asString(data);
|
1115
|
+
? data.map((v) => this.asString(v, options)).join(', ')
|
1116
|
+
: this.asString(data, options);
|
1050
1117
|
}
|
1051
1118
|
getValue() {
|
1052
1119
|
// If the widget isn't active.
|
@@ -1398,7 +1465,7 @@ export default class SelectComponent extends ListComponent {
|
|
1398
1465
|
}
|
1399
1466
|
return this.component.data.values.map(value => ({ label: value.label, value: String(this.normalizeSingleValue(value.value)) }));
|
1400
1467
|
}
|
1401
|
-
asString(value) {
|
1468
|
+
asString(value, options = {}) {
|
1402
1469
|
value = value ?? this.getValue();
|
1403
1470
|
//need to convert values to strings to be able to compare values with available options that are strings
|
1404
1471
|
const convertToString = (data, valueProperty) => {
|
@@ -1448,9 +1515,15 @@ export default class SelectComponent extends ListComponent {
|
|
1448
1515
|
if (_.isString(value)) {
|
1449
1516
|
return value;
|
1450
1517
|
}
|
1518
|
+
const getTemplateValue = (v) => {
|
1519
|
+
const itemTemplate = this.itemTemplate(v);
|
1520
|
+
return options.csv && itemTemplate
|
1521
|
+
? unescapeHTML(itemTemplate)
|
1522
|
+
: itemTemplate;
|
1523
|
+
};
|
1451
1524
|
if (Array.isArray(value)) {
|
1452
1525
|
const items = [];
|
1453
|
-
value.forEach(item => items.push(
|
1526
|
+
value.forEach(item => items.push(getTemplateValue(item)));
|
1454
1527
|
if (this.component.dataSrc === 'resource' && items.length > 0) {
|
1455
1528
|
return items.join(', ');
|
1456
1529
|
}
|
@@ -1465,7 +1538,7 @@ export default class SelectComponent extends ListComponent {
|
|
1465
1538
|
return JSON.stringify(value);
|
1466
1539
|
}
|
1467
1540
|
return !_.isNil(value)
|
1468
|
-
?
|
1541
|
+
? getTemplateValue(value)
|
1469
1542
|
: '-';
|
1470
1543
|
}
|
1471
1544
|
detach() {
|
@@ -148,6 +148,16 @@ export default class SurveyComponent extends Field {
|
|
148
148
|
result += '</tbody></table>';
|
149
149
|
return result;
|
150
150
|
}
|
151
|
+
if (_.isPlainObject(value)) {
|
152
|
+
const { values = [], questions = [] } = this.component;
|
153
|
+
return _.isEmpty(value)
|
154
|
+
? ''
|
155
|
+
: _.map(value, (v, q) => {
|
156
|
+
const valueLabel = _.get(_.find(values, val => _.isEqual(val.value, v)), 'label', v);
|
157
|
+
const questionLabel = _.get(_.find(questions, quest => _.isEqual(quest.value, q)), 'label', q);
|
158
|
+
return `${questionLabel}: ${valueLabel}`;
|
159
|
+
}).join('; ');
|
160
|
+
}
|
151
161
|
return super.getValueAsString(value, options);
|
152
162
|
}
|
153
163
|
}
|