@formio/js 5.0.0-rc.37 → 5.0.0-rc.38
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|