@formio/js 5.1.0-dev.5892.54c1dc7 → 5.1.0-dev.5936.81a1533
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.form.js +3 -3
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.full.js +4 -4
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.utils.js +1 -1
- package/dist/formio.utils.min.js +1 -1
- package/lib/cjs/WebformBuilder.js +0 -7
- package/lib/cjs/components/radio/Radio.d.ts +4 -8
- package/lib/cjs/components/radio/Radio.js +72 -36
- package/lib/cjs/components/selectboxes/SelectBoxes.d.ts +0 -6
- package/lib/cjs/translations/en.d.ts +0 -1
- package/lib/cjs/translations/en.js +1 -1
- package/lib/cjs/utils/utils.js +3 -10
- package/lib/mjs/WebformBuilder.js +0 -7
- package/lib/mjs/components/radio/Radio.d.ts +4 -8
- package/lib/mjs/components/radio/Radio.js +71 -36
- package/lib/mjs/components/selectboxes/SelectBoxes.d.ts +0 -6
- package/lib/mjs/translations/en.d.ts +0 -1
- package/lib/mjs/translations/en.js +0 -1
- package/lib/mjs/utils/utils.js +2 -10
- package/package.json +1 -1
@@ -1144,19 +1144,12 @@ class WebformBuilder extends Component_1.default {
|
|
1144
1144
|
highlightInvalidComponents() {
|
1145
1145
|
const repeatablePaths = this.findRepeatablePaths();
|
1146
1146
|
let hasInvalidComponents = false;
|
1147
|
-
// Matches anything expect letters and '_' at the beginning of the key and anything except of letters, numbers,
|
1148
|
-
// '-', '.' and '_' in the rest of the key
|
1149
|
-
const badCharacters = /^[^A-Za-z_]+|[^A-Za-z0-9\-._]+/g;
|
1150
1147
|
this.webform.everyComponent((comp) => {
|
1151
1148
|
const path = comp.path;
|
1152
1149
|
if (repeatablePaths.includes(path)) {
|
1153
1150
|
comp.setCustomValidity(this.t('apiKey', { key: comp.key }));
|
1154
1151
|
hasInvalidComponents = true;
|
1155
1152
|
}
|
1156
|
-
else if (comp.key.replace(badCharacters, '') === '') {
|
1157
|
-
comp.setCustomValidity(this.t('apiKeyNotValid', { key: comp.key }));
|
1158
|
-
hasInvalidComponents = true;
|
1159
|
-
}
|
1160
1153
|
else {
|
1161
1154
|
comp.setCustomValidity();
|
1162
1155
|
}
|
@@ -27,8 +27,8 @@ export default class RadioComponent extends ListComponent {
|
|
27
27
|
itemsLoadedResolve: ((value: any) => void) | undefined;
|
28
28
|
optionsLoaded: boolean | undefined;
|
29
29
|
loadedOptions: any[] | undefined;
|
30
|
+
valuesMap: Map<any, any> | undefined;
|
30
31
|
beforeSubmit(): Promise<any>;
|
31
|
-
convertValues(values: any): any;
|
32
32
|
render(): string;
|
33
33
|
attach(element: any): Promise<void>;
|
34
34
|
detach(element: any): void;
|
@@ -36,18 +36,14 @@ export default class RadioComponent extends ListComponent {
|
|
36
36
|
validateValueAvailability(setting: any, value: any): boolean;
|
37
37
|
getValueAsString(value: any, options?: {}): any;
|
38
38
|
setValueAt(index: any, value: any): void;
|
39
|
+
prepareValue(item: any, options?: {}): any;
|
40
|
+
getValueByInput(input: any): any;
|
39
41
|
loadItems(url: any, search: any, headers: any, options: any, method: any, body: any): void;
|
40
42
|
loadItemsFromMetadata(): void;
|
41
43
|
setItems(items: any): void;
|
42
44
|
setSelectedClasses(): void;
|
45
|
+
setMetadata(value: any): void;
|
43
46
|
updateValue(value: any, flags: any): boolean;
|
44
47
|
currentValue: any;
|
45
|
-
/**
|
46
|
-
* Normalize values coming into updateValue. For example, depending on the configuration, string value `"true"` will be normalized to boolean `true`.
|
47
|
-
* @param {*} value - The value to normalize
|
48
|
-
* @returns {*} - Returns the normalized value
|
49
|
-
*/
|
50
|
-
convertByDataType(value: any): any;
|
51
|
-
normalizeValue(value: any): any;
|
52
48
|
}
|
53
49
|
import ListComponent from '../_classes/list/ListComponent';
|
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const lodash_1 = __importDefault(require("lodash"));
|
7
7
|
const ListComponent_1 = __importDefault(require("../_classes/list/ListComponent"));
|
8
8
|
const Formio_1 = require("../../Formio");
|
9
|
-
const utils_1 = require("../../utils");
|
9
|
+
const utils_1 = require("../../utils/utils");
|
10
|
+
const uuid_1 = require("uuid");
|
10
11
|
class RadioComponent extends ListComponent_1.default {
|
11
12
|
static schema(...extend) {
|
12
13
|
return ListComponent_1.default.schema({
|
@@ -142,6 +143,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
142
143
|
});
|
143
144
|
this.optionsLoaded = !this.component.dataSrc || this.component.dataSrc === 'values';
|
144
145
|
this.loadedOptions = [];
|
146
|
+
this.valuesMap = new Map();
|
145
147
|
if (!this.visible) {
|
146
148
|
this.itemsLoadedResolve();
|
147
149
|
}
|
@@ -153,12 +155,6 @@ class RadioComponent extends ListComponent_1.default {
|
|
153
155
|
this.dataReady.then(() => res(true));
|
154
156
|
});
|
155
157
|
}
|
156
|
-
convertValues(values) {
|
157
|
-
if (this.options.renderMode === 'html' && this.type === 'radio') {
|
158
|
-
return values.map(x => (Object.assign(Object.assign({}, x), { value: this.convertByDataType(x.value) })));
|
159
|
-
}
|
160
|
-
return values;
|
161
|
-
}
|
162
158
|
render() {
|
163
159
|
if (!this.optionsLoaded) {
|
164
160
|
return super.render(this.renderTemplate('loader'));
|
@@ -166,7 +162,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
166
162
|
return super.render(this.renderTemplate('radio', {
|
167
163
|
input: this.inputInfo,
|
168
164
|
inline: this.component.inline,
|
169
|
-
values: this.component.dataSrc === 'values' ? this.
|
165
|
+
values: this.component.dataSrc === 'values' ? this.component.values : this.loadedOptions,
|
170
166
|
value: this.dataValue,
|
171
167
|
row: this.row,
|
172
168
|
}));
|
@@ -187,9 +183,12 @@ class RadioComponent extends ListComponent_1.default {
|
|
187
183
|
if (!lodash_1.default.isString(this.dataValue)) {
|
188
184
|
dataValue = lodash_1.default.toString(this.dataValue);
|
189
185
|
}
|
190
|
-
if (this.isSelectURL
|
191
|
-
const
|
192
|
-
|
186
|
+
if (this.isSelectURL) {
|
187
|
+
const valueKey = this.loadedOptions[index].value;
|
188
|
+
const optionValue = this.valuesMap.has(valueKey)
|
189
|
+
? this.valuesMap.get(valueKey)
|
190
|
+
: valueKey;
|
191
|
+
input.checked = lodash_1.default.isEqual(this.normalizeValue(optionValue), this.dataValue);
|
193
192
|
}
|
194
193
|
else {
|
195
194
|
input.checked = (dataValue === input.value && (input.value || this.component.dataSrc !== 'url'));
|
@@ -226,9 +225,14 @@ class RadioComponent extends ListComponent_1.default {
|
|
226
225
|
let value = this.component.inputType === 'checkbox' ? '' : this.dataValue;
|
227
226
|
this.refs.input.forEach((input, index) => {
|
228
227
|
if (input.checked) {
|
229
|
-
|
230
|
-
|
231
|
-
|
228
|
+
if (!this.isSelectURL) {
|
229
|
+
value = input.value;
|
230
|
+
return;
|
231
|
+
}
|
232
|
+
const optionValue = this.loadedOptions[index].value;
|
233
|
+
value = this.valuesMap.has(optionValue)
|
234
|
+
? this.valuesMap.get(optionValue)
|
235
|
+
: optionValue;
|
232
236
|
}
|
233
237
|
});
|
234
238
|
return value;
|
@@ -272,8 +276,8 @@ class RadioComponent extends ListComponent_1.default {
|
|
272
276
|
}
|
273
277
|
setValueAt(index, value) {
|
274
278
|
if (this.refs.input && this.refs.input[index] && value !== null && value !== undefined) {
|
275
|
-
const inputValue = this.refs.input[index]
|
276
|
-
this.refs.input[index].checked = (inputValue
|
279
|
+
const inputValue = this.getValueByInput(this.refs.input[index]);
|
280
|
+
this.refs.input[index].checked = lodash_1.default.isEqual(inputValue, value);
|
277
281
|
}
|
278
282
|
}
|
279
283
|
get shouldLoad() {
|
@@ -283,6 +287,23 @@ class RadioComponent extends ListComponent_1.default {
|
|
283
287
|
}
|
284
288
|
return super.shouldLoad;
|
285
289
|
}
|
290
|
+
prepareValue(item, options = {}) {
|
291
|
+
const value = this.component.valueProperty && !options.skipValueProperty
|
292
|
+
? lodash_1.default.get(item, this.component.valueProperty)
|
293
|
+
: item;
|
294
|
+
if (this.component.type === 'radio' && typeof value !== 'string') {
|
295
|
+
const uuid = (0, uuid_1.v4)();
|
296
|
+
this.valuesMap.set(uuid, value);
|
297
|
+
return uuid;
|
298
|
+
}
|
299
|
+
return value;
|
300
|
+
}
|
301
|
+
getValueByInput(input) {
|
302
|
+
const inputValue = input.value;
|
303
|
+
return this.valuesMap.has(inputValue)
|
304
|
+
? this.valuesMap.get(inputValue)
|
305
|
+
: inputValue;
|
306
|
+
}
|
286
307
|
loadItems(url, search, headers, options, method, body) {
|
287
308
|
if (this.optionsLoaded) {
|
288
309
|
this.itemsLoadedResolve();
|
@@ -333,7 +354,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
333
354
|
label: this.itemTemplate(item)
|
334
355
|
};
|
335
356
|
if (lodash_1.default.isEqual(item, this.selectData || lodash_1.default.pick(this.dataValue, lodash_1.default.keys(item)))) {
|
336
|
-
this.loadedOptions[i].value = this.dataValue;
|
357
|
+
this.loadedOptions[i].value = this.prepareValue(this.dataValue, { skipValueProperty: true });
|
337
358
|
}
|
338
359
|
});
|
339
360
|
this.optionsLoaded = true;
|
@@ -343,12 +364,15 @@ class RadioComponent extends ListComponent_1.default {
|
|
343
364
|
const listData = [];
|
344
365
|
items === null || items === void 0 ? void 0 : items.forEach((item, i) => {
|
345
366
|
const valueAtProperty = lodash_1.default.get(item, this.component.valueProperty);
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
367
|
+
const value = this.prepareValue(item);
|
368
|
+
const label = this.component.valueProperty
|
369
|
+
? this.itemTemplate(item, valueAtProperty, i)
|
370
|
+
: this.itemTemplate(item, item, i);
|
371
|
+
this.loadedOptions[i] = { label, value };
|
372
|
+
listData.push(this.templateData[i]);
|
373
|
+
if (this.valuesMap.has(value)) {
|
374
|
+
this.templateData[value] = this.templateData[i];
|
375
|
+
}
|
352
376
|
if (!this.isRadio && (lodash_1.default.isObject(value) || lodash_1.default.isBoolean(value) || lodash_1.default.isUndefined(value))) {
|
353
377
|
this.loadedOptions[i].invalid = true;
|
354
378
|
}
|
@@ -371,7 +395,11 @@ class RadioComponent extends ListComponent_1.default {
|
|
371
395
|
const value = this.dataValue;
|
372
396
|
this.refs.wrapper.forEach((wrapper, index) => {
|
373
397
|
const input = this.refs.input[index];
|
374
|
-
const checked = (value === undefined || value === null)
|
398
|
+
const checked = (value === undefined || value === null)
|
399
|
+
? false
|
400
|
+
: (input.type === 'checkbox')
|
401
|
+
? value[input.value] || input.checked
|
402
|
+
: lodash_1.default.isEqual(this.normalizeValue(this.getValueByInput(input)), value);
|
375
403
|
if (checked) {
|
376
404
|
//add class to container when selected
|
377
405
|
this.addClass(wrapper, this.optionSelectedClass);
|
@@ -385,10 +413,26 @@ class RadioComponent extends ListComponent_1.default {
|
|
385
413
|
});
|
386
414
|
}
|
387
415
|
}
|
416
|
+
setMetadata(value) {
|
417
|
+
var _a;
|
418
|
+
let key = value;
|
419
|
+
if (typeof value !== 'string') {
|
420
|
+
const checkedInput = Array.prototype.find.call((_a = this.refs.input) !== null && _a !== void 0 ? _a : [], (input => input.type === 'radio' && input.getAttribute('checked')));
|
421
|
+
key = (checkedInput === null || checkedInput === void 0 ? void 0 : checkedInput.value) || key;
|
422
|
+
}
|
423
|
+
if (this.isSelectURL && this.templateData && this.templateData[key]) {
|
424
|
+
const submission = this.root.submission;
|
425
|
+
if (!submission.metadata.selectData) {
|
426
|
+
submission.metadata.selectData = {};
|
427
|
+
}
|
428
|
+
lodash_1.default.set(submission.metadata.selectData, this.path, this.templateData[key]);
|
429
|
+
}
|
430
|
+
}
|
388
431
|
updateValue(value, flags) {
|
389
432
|
const changed = super.updateValue(value, flags);
|
390
433
|
if (changed) {
|
391
434
|
this.setSelectedClasses();
|
435
|
+
this.setMetadata(this.dataValue);
|
392
436
|
}
|
393
437
|
if (!flags || !flags.modified || !this.isRadio) {
|
394
438
|
if (changed) {
|
@@ -412,7 +456,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
412
456
|
* @param {*} value - The value to normalize
|
413
457
|
* @returns {*} - Returns the normalized value
|
414
458
|
*/
|
415
|
-
|
459
|
+
normalizeValue(value) {
|
416
460
|
const dataType = this.component.dataType || 'auto';
|
417
461
|
if (value === this.emptyValue) {
|
418
462
|
return value;
|
@@ -444,18 +488,10 @@ class RadioComponent extends ListComponent_1.default {
|
|
444
488
|
value = !(!value || value.toString() === 'false');
|
445
489
|
break;
|
446
490
|
}
|
447
|
-
return value;
|
491
|
+
return super.normalizeValue(value);
|
448
492
|
}
|
449
|
-
|
450
|
-
|
451
|
-
if (this.isSelectURL && this.templateData && this.templateData[valueConverted]) {
|
452
|
-
const submission = this.root.submission;
|
453
|
-
if (!submission.metadata.selectData) {
|
454
|
-
submission.metadata.selectData = {};
|
455
|
-
}
|
456
|
-
lodash_1.default.set(submission.metadata.selectData, this.path, this.templateData[valueConverted]);
|
457
|
-
}
|
458
|
-
return super.normalizeValue(valueConverted);
|
493
|
+
isSingleInputValue() {
|
494
|
+
return true;
|
459
495
|
}
|
460
496
|
}
|
461
497
|
exports.default = RadioComponent;
|
@@ -8,12 +8,6 @@ export default class SelectBoxesComponent extends RadioComponent {
|
|
8
8
|
* @returns {boolean} - If the value is empty.
|
9
9
|
*/
|
10
10
|
isEmpty(value?: any): boolean;
|
11
|
-
/**
|
12
|
-
* Normalize values coming into updateValue.
|
13
|
-
* @param {any} value - The value to normalize.
|
14
|
-
* @returns {*} - The normalized value
|
15
|
-
*/
|
16
|
-
normalizeValue(value: any): any;
|
17
11
|
setInputsDisabled(value: any, onlyUnchecked: any): void;
|
18
12
|
checkComponentValidity(data: any, dirty: any, rowData: any, options: any, errors?: any[]): boolean;
|
19
13
|
}
|
@@ -5,6 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
var _a;
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
7
|
const bootstrap_1 = __importDefault(require("@formio/bootstrap"));
|
8
|
-
exports.default = Object.assign(Object.assign({}, (((_a = bootstrap_1.default === null || bootstrap_1.default === void 0 ? void 0 : bootstrap_1.default.translations) === null || _a === void 0 ? void 0 : _a.en) || {})), { unsavedRowsError: 'Please save all rows before proceeding.', invalidRowsError: 'Please correct invalid rows before proceeding.', invalidRowError: 'Invalid row. Please correct it or delete.', invalidOption: '{{field}} is an invalid value.', invalidDay: '{{field}} is not a valid day.', complete: 'Submission Complete', error: 'Please fix the following errors before submitting.', errorListHotkey: 'Press Ctrl + Alt + X to go back to the error list.', errorsListNavigationMessage: 'Click to navigate to the field with following error.', submitError: 'Please check the form and correct all errors before submitting.', required: '{{field}} is required', unique: '{{field}} must be unique', array: '{{field}} must be an array', array_nonempty: '{{field}} must be a non-empty array', nonarray: '{{field}} must not be an array', select: '{{field}} contains an invalid selection', pattern: '{{field}} does not match the pattern {{pattern}}', minLength: '{{field}} must have at least {{length}} characters.', maxLength: '{{field}} must have no more than {{length}} characters.', minWords: '{{field}} must have at least {{length}} words.', maxWords: '{{field}} must have no more than {{length}} words.', min: '{{field}} cannot be less than {{min}}.', max: '{{field}} cannot be greater than {{max}}.', maxDate: '{{field}} should not contain date after {{maxDate}}', minDate: '{{field}} should not contain date before {{minDate}}', maxYear: '{{field}} should not contain year greater than {{maxYear}}', minYear: '{{field}} should not contain year less than {{minYear}}', minSelectedCount: 'You must select at least {{minCount}} items', maxSelectedCount: 'You may only select up to {{maxCount}} items', invalid_email: '{{field}} must be a valid email.', invalid_url: '{{field}} must be a valid url.', invalid_regex: '{{field}} does not match the pattern {{regex}}.', invalid_date: '{{field}} is not a valid date.', invalid_day: '{{field}} is not a valid day.', invalidValueProperty: 'Invalid Value Property', mask: '{{field}} does not match the mask.', valueIsNotAvailable: '{{ field }} is an invalid value.', stripe: '{{stripe}}', month: 'Month', day: 'Day', year: 'Year', january: 'January', february: 'February', march: 'March', april: 'April', may: 'May', june: 'June', july: 'July', august: 'August', september: 'September', october: 'October', november: 'November', december: 'December', next: 'Next', previous: 'Previous', cancel: 'Cancel', submit: 'Submit Form', confirmCancel: 'Are you sure you want to cancel?', saveDraftInstanceError: 'Cannot save draft because there is no formio instance.', saveDraftAuthError: 'Cannot save draft unless a user is authenticated.', restoreDraftInstanceError: 'Cannot restore draft because there is no formio instance.', saveDraftError: 'Unable to save draft.', restoreDraftError: 'Unable to restore draft.', time: 'Invalid time', cancelButtonAriaLabel: 'Cancel button. Click to reset the form', previousButtonAriaLabel: 'Previous button. Click to go back to the previous tab', nextButtonAriaLabel: 'Next button. Click to go to the next tab', submitButtonAriaLabel: 'Submit Form button. Click to submit the form', reCaptchaTokenValidationError: 'ReCAPTCHA: Token validation error', reCaptchaTokenNotSpecifiedError: 'ReCAPTCHA: Token is not specified in submission', apiKey: 'API Key is not unique: {{key}}',
|
8
|
+
exports.default = Object.assign(Object.assign({}, (((_a = bootstrap_1.default === null || bootstrap_1.default === void 0 ? void 0 : bootstrap_1.default.translations) === null || _a === void 0 ? void 0 : _a.en) || {})), { unsavedRowsError: 'Please save all rows before proceeding.', invalidRowsError: 'Please correct invalid rows before proceeding.', invalidRowError: 'Invalid row. Please correct it or delete.', invalidOption: '{{field}} is an invalid value.', invalidDay: '{{field}} is not a valid day.', complete: 'Submission Complete', error: 'Please fix the following errors before submitting.', errorListHotkey: 'Press Ctrl + Alt + X to go back to the error list.', errorsListNavigationMessage: 'Click to navigate to the field with following error.', submitError: 'Please check the form and correct all errors before submitting.', required: '{{field}} is required', unique: '{{field}} must be unique', array: '{{field}} must be an array', array_nonempty: '{{field}} must be a non-empty array', nonarray: '{{field}} must not be an array', select: '{{field}} contains an invalid selection', pattern: '{{field}} does not match the pattern {{pattern}}', minLength: '{{field}} must have at least {{length}} characters.', maxLength: '{{field}} must have no more than {{length}} characters.', minWords: '{{field}} must have at least {{length}} words.', maxWords: '{{field}} must have no more than {{length}} words.', min: '{{field}} cannot be less than {{min}}.', max: '{{field}} cannot be greater than {{max}}.', maxDate: '{{field}} should not contain date after {{maxDate}}', minDate: '{{field}} should not contain date before {{minDate}}', maxYear: '{{field}} should not contain year greater than {{maxYear}}', minYear: '{{field}} should not contain year less than {{minYear}}', minSelectedCount: 'You must select at least {{minCount}} items', maxSelectedCount: 'You may only select up to {{maxCount}} items', invalid_email: '{{field}} must be a valid email.', invalid_url: '{{field}} must be a valid url.', invalid_regex: '{{field}} does not match the pattern {{regex}}.', invalid_date: '{{field}} is not a valid date.', invalid_day: '{{field}} is not a valid day.', invalidValueProperty: 'Invalid Value Property', mask: '{{field}} does not match the mask.', valueIsNotAvailable: '{{ field }} is an invalid value.', stripe: '{{stripe}}', month: 'Month', day: 'Day', year: 'Year', january: 'January', february: 'February', march: 'March', april: 'April', may: 'May', june: 'June', july: 'July', august: 'August', september: 'September', october: 'October', november: 'November', december: 'December', next: 'Next', previous: 'Previous', cancel: 'Cancel', submit: 'Submit Form', confirmCancel: 'Are you sure you want to cancel?', saveDraftInstanceError: 'Cannot save draft because there is no formio instance.', saveDraftAuthError: 'Cannot save draft unless a user is authenticated.', restoreDraftInstanceError: 'Cannot restore draft because there is no formio instance.', saveDraftError: 'Unable to save draft.', restoreDraftError: 'Unable to restore draft.', time: 'Invalid time', cancelButtonAriaLabel: 'Cancel button. Click to reset the form', previousButtonAriaLabel: 'Previous button. Click to go back to the previous tab', nextButtonAriaLabel: 'Next button. Click to go to the next tab', submitButtonAriaLabel: 'Submit Form button. Click to submit the form', reCaptchaTokenValidationError: 'ReCAPTCHA: Token validation error', reCaptchaTokenNotSpecifiedError: 'ReCAPTCHA: Token is not specified in submission', apiKey: 'API Key is not unique: {{key}}', typeRemaining: '{{ remaining }} {{ type }} remaining.', typeCount: '{{ count }} {{ type }}', requiredDayField: '{{ field }} is required', requiredDayEmpty: '{{ field }} is required', requiredMonthField: '{{ field }} is required', requiredYearField: '{{ field }} is required', formNotReady: 'Form not ready. Use form.ready promise', noFormElement: 'No DOM element for form.', notUniqueKey: 'API Key is not unique', newFormSchema: 'Form schema is for a newer version, please upgrade your renderer. Some functionality may not work.', missingUrl: 'Missing URL argument', urlNotAttachedToBtn: 'You should add a URL to this button.', loadingProjectSettingsError: 'Could not load project settings', sessionStorageSupportError: 'Session storage is not supported in this browser.', builderUniqueError: `You cannot add more than one {{componentKeyOrTitle}} component to one page.`, pageNotFound: 'Page not found', noDragInfoError: 'There is no Drag Info available for either dragged or sibling element', addonSupportTypeError: 'Addon {{label}} does not support component of type {{type}}', setPathError: 'Should not be setting the path of a component.', calculatedPathDeprecation: 'component.calculatedPath was deprecated, use component.path instead.', unknownTemplate: 'Unknown template: {{name}}', unknownComponent: 'Unknown component: {{type}}', renderTemplateFunctionDeprecation: `Form.io 'render' template function is deprecated.
|
9
9
|
If you need to render template (template A) inside of another template (template B),
|
10
10
|
pass pre-compiled template A (use this.renderTemplate('template_A_name') as template context variable for template B`, whenReadyDeprecation: 'The whenReady() method has been deprecated. Please use the dataReady property instead.', loadResourcesError: 'Unable to load resources for {{componentKey}}', noSelectDataConfiguration: 'Select component {{componentKey}} does not have data configuration.', indexedDBSupportError: "Your browser doesn't support current version of indexedDB", caretPositionSavingError: 'An error occurred while trying to save caret position', iteratableRowsError: 'Getter #iteratableRows() is not implemented', checkRowDeprecation: 'Deprecation Warning: checkRow method has been replaced with processRow', noOAuthBtn: 'You must add the OAuth button to a form for it to function properly', noOAuthConfiguration: 'OAuth not configured. You must configure oauth for your project before it will work.', oAuthErrorsTitle: 'The Following Error Has Occured', noOAuthFormUrl: 'You must attach a Form API url to your form in order to use OAuth buttons.', oAuthStateError: 'OAuth state does not match. Please try logging in again.', componentInvalidRowValidation: 'Invalid row validation for {{componentKey}}', videoPlayerNotFound: 'Video player not found in template.', synchronizationFailed: 'Synchronization is failed', fileWithDuplicatedNameInProgress: 'File with the same name is already being uploaded', fileWithDuplicatedNameLoaded: 'File with the same name is already uploaded', nestedForm: 'Nested form', noDataProvided: 'No data provided', subformSubmissionLoadingError: 'Unable to load subform submission {{submissionId}}:', noDelimiterSet: 'In order for thousands separator to work properly, you must set the delimiter to true in the component json', noSiteKey: 'There is no Site Key specified in settings in form JSON', failedToNormalize: 'Failed to normalize value', failedToCompareItems: 'Error while comparing items', editorFocusError: 'An editor did not initialize properly when trying to focus:', quillImageUploadFailed: 'Quill image upload failed', noFilesSelected: 'No files selected', needConfigurationForQuill: 'The WYSIWYG settings are configured for CKEditor. For this renderer, you will need to use configurations for the Quill Editor. See https://quilljs.com/docs/configuration for more information.', waitPdfConverting: 'Converting PDF. Please wait.', uploading: 'Uploading', pasteBelow: 'Paste below', copy: 'Copy', move: 'Move', edit: 'Edit', editJson: 'Edit JSON', remove: 'Remove', clickToSetValue: 'Click to set value', words: 'words', characters: 'characters', addAnother: 'Add Another', yes: 'Yes', no: 'No', wantToClearData: 'Do you want to clear data?', yesDelete: 'Yes, delete it', waitFileProcessing: 'Processing file. Please wait...', wrongFileType: 'File is the wrong type; it must be {{ pattern }}', fileTooSmall: 'File is too small; it must be at least {{ size }}', fileTooBig: 'File is too big; it must be at most {{ size }}', noFileService: 'File Service not provided.', fileProcessingFailed: 'File processing has been failed.', readyForUpload: 'Ready to be uploaded into storage', readyForRemovingFromStorage: 'Ready to be removed from storage', preparingFileToRemove: 'Preparing file to remove', succefullyRemoved: 'Succefully removed', succefullyUploaded: 'Succefully uploaded', maxSelectItems: 'You may only select up to {{maxCount}} items', minSelectItems: 'You must select at least {{minCount}} items', clickToSign: 'Click to Sign', surveyQuestion: 'Question', surveyQuestionValue: 'Value', success: 'Success', noResultsFound: 'No results found', noChoices: 'No choices to choose from', typeToSearch: 'Type to search', loading: 'Loading' });
|
package/lib/cjs/utils/utils.js
CHANGED
@@ -209,20 +209,13 @@ function checkSimpleConditional(component, condition, row, data, instance) {
|
|
209
209
|
return true;
|
210
210
|
}
|
211
211
|
const conditionsResult = lodash_1.default.map(conditions, (cond) => {
|
212
|
+
var _a, _b;
|
212
213
|
const { value: comparedValue, operator, component: conditionComponentPath } = cond;
|
213
214
|
if (!conditionComponentPath) {
|
214
215
|
return true;
|
215
216
|
}
|
216
217
|
const splittedConditionPath = conditionComponentPath.split('.');
|
217
|
-
const
|
218
|
-
if (!(instance === null || instance === void 0 ? void 0 : instance.parent)) {
|
219
|
-
return false;
|
220
|
-
}
|
221
|
-
return (instance === null || instance === void 0 ? void 0 : instance.parent.type) === componentType || checkParentTypeInTree(instance.parent, componentType);
|
222
|
-
};
|
223
|
-
const conditionalPaths = checkParentTypeInTree(instance, 'datagrid') || checkParentTypeInTree(instance, 'editgrid')
|
224
|
-
? []
|
225
|
-
: getConditionalPathsRecursive(splittedConditionPath, data);
|
218
|
+
const conditionalPaths = ((_a = instance === null || instance === void 0 ? void 0 : instance.parent) === null || _a === void 0 ? void 0 : _a.type) === 'datagrid' || ((_b = instance === null || instance === void 0 ? void 0 : instance.parent) === null || _b === void 0 ? void 0 : _b.type) === 'editgrid' ? [] : getConditionalPathsRecursive(splittedConditionPath, data);
|
226
219
|
if (conditionalPaths.length > 0) {
|
227
220
|
return conditionalPaths.map((path) => {
|
228
221
|
const value = getComponentActualValue(path, data, row);
|
@@ -341,7 +334,7 @@ function getRow(component, row, instance, conditional) {
|
|
341
334
|
}
|
342
335
|
const dataParent = getDataParentComponent(instance);
|
343
336
|
if (dataParent) {
|
344
|
-
const parentPath = (_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.
|
337
|
+
const parentPath = (_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.localDataPath;
|
345
338
|
const isTriggerCondtionComponentPath = condition.when || !condition.conditions
|
346
339
|
? (_b = condition.when) === null || _b === void 0 ? void 0 : _b.startsWith((_c = dataParent.paths) === null || _c === void 0 ? void 0 : _c.localPath)
|
347
340
|
: lodash_1.default.some(condition.conditions, cond => { var _a; return cond.component.startsWith((_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.localPath); });
|
@@ -1127,19 +1127,12 @@ export default class WebformBuilder extends Component {
|
|
1127
1127
|
highlightInvalidComponents() {
|
1128
1128
|
const repeatablePaths = this.findRepeatablePaths();
|
1129
1129
|
let hasInvalidComponents = false;
|
1130
|
-
// Matches anything expect letters and '_' at the beginning of the key and anything except of letters, numbers,
|
1131
|
-
// '-', '.' and '_' in the rest of the key
|
1132
|
-
const badCharacters = /^[^A-Za-z_]+|[^A-Za-z0-9\-._]+/g;
|
1133
1130
|
this.webform.everyComponent((comp) => {
|
1134
1131
|
const path = comp.path;
|
1135
1132
|
if (repeatablePaths.includes(path)) {
|
1136
1133
|
comp.setCustomValidity(this.t('apiKey', { key: comp.key }));
|
1137
1134
|
hasInvalidComponents = true;
|
1138
1135
|
}
|
1139
|
-
else if (comp.key.replace(badCharacters, '') === '') {
|
1140
|
-
comp.setCustomValidity(this.t('apiKeyNotValid', { key: comp.key }));
|
1141
|
-
hasInvalidComponents = true;
|
1142
|
-
}
|
1143
1136
|
else {
|
1144
1137
|
comp.setCustomValidity();
|
1145
1138
|
}
|
@@ -27,8 +27,8 @@ export default class RadioComponent extends ListComponent {
|
|
27
27
|
itemsLoadedResolve: ((value: any) => void) | undefined;
|
28
28
|
optionsLoaded: boolean | undefined;
|
29
29
|
loadedOptions: any[] | undefined;
|
30
|
+
valuesMap: Map<any, any> | undefined;
|
30
31
|
beforeSubmit(): Promise<any>;
|
31
|
-
convertValues(values: any): any;
|
32
32
|
render(): string;
|
33
33
|
attach(element: any): Promise<void>;
|
34
34
|
detach(element: any): void;
|
@@ -36,18 +36,14 @@ export default class RadioComponent extends ListComponent {
|
|
36
36
|
validateValueAvailability(setting: any, value: any): boolean;
|
37
37
|
getValueAsString(value: any, options?: {}): any;
|
38
38
|
setValueAt(index: any, value: any): void;
|
39
|
+
prepareValue(item: any, options?: {}): any;
|
40
|
+
getValueByInput(input: any): any;
|
39
41
|
loadItems(url: any, search: any, headers: any, options: any, method: any, body: any): void;
|
40
42
|
loadItemsFromMetadata(): void;
|
41
43
|
setItems(items: any): void;
|
42
44
|
setSelectedClasses(): void;
|
45
|
+
setMetadata(value: any): void;
|
43
46
|
updateValue(value: any, flags: any): boolean;
|
44
47
|
currentValue: any;
|
45
|
-
/**
|
46
|
-
* Normalize values coming into updateValue. For example, depending on the configuration, string value `"true"` will be normalized to boolean `true`.
|
47
|
-
* @param {*} value - The value to normalize
|
48
|
-
* @returns {*} - Returns the normalized value
|
49
|
-
*/
|
50
|
-
convertByDataType(value: any): any;
|
51
|
-
normalizeValue(value: any): any;
|
52
48
|
}
|
53
49
|
import ListComponent from '../_classes/list/ListComponent';
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import ListComponent from '../_classes/list/ListComponent';
|
3
3
|
import { Formio } from '../../Formio';
|
4
|
-
import { boolValue, componentValueTypes, getComponentSavedTypes } from '../../utils';
|
4
|
+
import { boolValue, componentValueTypes, getComponentSavedTypes } from '../../utils/utils';
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
5
6
|
export default class RadioComponent extends ListComponent {
|
6
7
|
static schema(...extend) {
|
7
8
|
return ListComponent.schema({
|
@@ -142,6 +143,7 @@ export default class RadioComponent extends ListComponent {
|
|
142
143
|
});
|
143
144
|
this.optionsLoaded = !this.component.dataSrc || this.component.dataSrc === 'values';
|
144
145
|
this.loadedOptions = [];
|
146
|
+
this.valuesMap = new Map();
|
145
147
|
if (!this.visible) {
|
146
148
|
this.itemsLoadedResolve();
|
147
149
|
}
|
@@ -153,12 +155,6 @@ export default class RadioComponent extends ListComponent {
|
|
153
155
|
this.dataReady.then(() => res(true));
|
154
156
|
});
|
155
157
|
}
|
156
|
-
convertValues(values) {
|
157
|
-
if (this.options.renderMode === 'html' && this.type === 'radio') {
|
158
|
-
return values.map(x => ({ ...x, value: this.convertByDataType(x.value) }));
|
159
|
-
}
|
160
|
-
return values;
|
161
|
-
}
|
162
158
|
render() {
|
163
159
|
if (!this.optionsLoaded) {
|
164
160
|
return super.render(this.renderTemplate('loader'));
|
@@ -166,7 +162,7 @@ export default class RadioComponent extends ListComponent {
|
|
166
162
|
return super.render(this.renderTemplate('radio', {
|
167
163
|
input: this.inputInfo,
|
168
164
|
inline: this.component.inline,
|
169
|
-
values: this.component.dataSrc === 'values' ? this.
|
165
|
+
values: this.component.dataSrc === 'values' ? this.component.values : this.loadedOptions,
|
170
166
|
value: this.dataValue,
|
171
167
|
row: this.row,
|
172
168
|
}));
|
@@ -187,9 +183,12 @@ export default class RadioComponent extends ListComponent {
|
|
187
183
|
if (!_.isString(this.dataValue)) {
|
188
184
|
dataValue = _.toString(this.dataValue);
|
189
185
|
}
|
190
|
-
if (this.isSelectURL
|
191
|
-
const
|
192
|
-
|
186
|
+
if (this.isSelectURL) {
|
187
|
+
const valueKey = this.loadedOptions[index].value;
|
188
|
+
const optionValue = this.valuesMap.has(valueKey)
|
189
|
+
? this.valuesMap.get(valueKey)
|
190
|
+
: valueKey;
|
191
|
+
input.checked = _.isEqual(this.normalizeValue(optionValue), this.dataValue);
|
193
192
|
}
|
194
193
|
else {
|
195
194
|
input.checked = (dataValue === input.value && (input.value || this.component.dataSrc !== 'url'));
|
@@ -226,9 +225,14 @@ export default class RadioComponent extends ListComponent {
|
|
226
225
|
let value = this.component.inputType === 'checkbox' ? '' : this.dataValue;
|
227
226
|
this.refs.input.forEach((input, index) => {
|
228
227
|
if (input.checked) {
|
229
|
-
|
230
|
-
|
231
|
-
|
228
|
+
if (!this.isSelectURL) {
|
229
|
+
value = input.value;
|
230
|
+
return;
|
231
|
+
}
|
232
|
+
const optionValue = this.loadedOptions[index].value;
|
233
|
+
value = this.valuesMap.has(optionValue)
|
234
|
+
? this.valuesMap.get(optionValue)
|
235
|
+
: optionValue;
|
232
236
|
}
|
233
237
|
});
|
234
238
|
return value;
|
@@ -272,8 +276,8 @@ export default class RadioComponent extends ListComponent {
|
|
272
276
|
}
|
273
277
|
setValueAt(index, value) {
|
274
278
|
if (this.refs.input && this.refs.input[index] && value !== null && value !== undefined) {
|
275
|
-
const inputValue = this.refs.input[index]
|
276
|
-
this.refs.input[index].checked = (inputValue
|
279
|
+
const inputValue = this.getValueByInput(this.refs.input[index]);
|
280
|
+
this.refs.input[index].checked = _.isEqual(inputValue, value);
|
277
281
|
}
|
278
282
|
}
|
279
283
|
get shouldLoad() {
|
@@ -283,6 +287,23 @@ export default class RadioComponent extends ListComponent {
|
|
283
287
|
}
|
284
288
|
return super.shouldLoad;
|
285
289
|
}
|
290
|
+
prepareValue(item, options = {}) {
|
291
|
+
const value = this.component.valueProperty && !options.skipValueProperty
|
292
|
+
? _.get(item, this.component.valueProperty)
|
293
|
+
: item;
|
294
|
+
if (this.component.type === 'radio' && typeof value !== 'string') {
|
295
|
+
const uuid = uuidv4();
|
296
|
+
this.valuesMap.set(uuid, value);
|
297
|
+
return uuid;
|
298
|
+
}
|
299
|
+
return value;
|
300
|
+
}
|
301
|
+
getValueByInput(input) {
|
302
|
+
const inputValue = input.value;
|
303
|
+
return this.valuesMap.has(inputValue)
|
304
|
+
? this.valuesMap.get(inputValue)
|
305
|
+
: inputValue;
|
306
|
+
}
|
286
307
|
loadItems(url, search, headers, options, method, body) {
|
287
308
|
if (this.optionsLoaded) {
|
288
309
|
this.itemsLoadedResolve();
|
@@ -333,7 +354,7 @@ export default class RadioComponent extends ListComponent {
|
|
333
354
|
label: this.itemTemplate(item)
|
334
355
|
};
|
335
356
|
if (_.isEqual(item, this.selectData || _.pick(this.dataValue, _.keys(item)))) {
|
336
|
-
this.loadedOptions[i].value = this.dataValue;
|
357
|
+
this.loadedOptions[i].value = this.prepareValue(this.dataValue, { skipValueProperty: true });
|
337
358
|
}
|
338
359
|
});
|
339
360
|
this.optionsLoaded = true;
|
@@ -343,12 +364,15 @@ export default class RadioComponent extends ListComponent {
|
|
343
364
|
const listData = [];
|
344
365
|
items?.forEach((item, i) => {
|
345
366
|
const valueAtProperty = _.get(item, this.component.valueProperty);
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
367
|
+
const value = this.prepareValue(item);
|
368
|
+
const label = this.component.valueProperty
|
369
|
+
? this.itemTemplate(item, valueAtProperty, i)
|
370
|
+
: this.itemTemplate(item, item, i);
|
371
|
+
this.loadedOptions[i] = { label, value };
|
372
|
+
listData.push(this.templateData[i]);
|
373
|
+
if (this.valuesMap.has(value)) {
|
374
|
+
this.templateData[value] = this.templateData[i];
|
375
|
+
}
|
352
376
|
if (!this.isRadio && (_.isObject(value) || _.isBoolean(value) || _.isUndefined(value))) {
|
353
377
|
this.loadedOptions[i].invalid = true;
|
354
378
|
}
|
@@ -371,7 +395,11 @@ export default class RadioComponent extends ListComponent {
|
|
371
395
|
const value = this.dataValue;
|
372
396
|
this.refs.wrapper.forEach((wrapper, index) => {
|
373
397
|
const input = this.refs.input[index];
|
374
|
-
const checked = (value === undefined || value === null)
|
398
|
+
const checked = (value === undefined || value === null)
|
399
|
+
? false
|
400
|
+
: (input.type === 'checkbox')
|
401
|
+
? value[input.value] || input.checked
|
402
|
+
: _.isEqual(this.normalizeValue(this.getValueByInput(input)), value);
|
375
403
|
if (checked) {
|
376
404
|
//add class to container when selected
|
377
405
|
this.addClass(wrapper, this.optionSelectedClass);
|
@@ -385,10 +413,25 @@ export default class RadioComponent extends ListComponent {
|
|
385
413
|
});
|
386
414
|
}
|
387
415
|
}
|
416
|
+
setMetadata(value) {
|
417
|
+
let key = value;
|
418
|
+
if (typeof value !== 'string') {
|
419
|
+
const checkedInput = Array.prototype.find.call(this.refs.input ?? [], (input => input.type === 'radio' && input.getAttribute('checked')));
|
420
|
+
key = checkedInput?.value || key;
|
421
|
+
}
|
422
|
+
if (this.isSelectURL && this.templateData && this.templateData[key]) {
|
423
|
+
const submission = this.root.submission;
|
424
|
+
if (!submission.metadata.selectData) {
|
425
|
+
submission.metadata.selectData = {};
|
426
|
+
}
|
427
|
+
_.set(submission.metadata.selectData, this.path, this.templateData[key]);
|
428
|
+
}
|
429
|
+
}
|
388
430
|
updateValue(value, flags) {
|
389
431
|
const changed = super.updateValue(value, flags);
|
390
432
|
if (changed) {
|
391
433
|
this.setSelectedClasses();
|
434
|
+
this.setMetadata(this.dataValue);
|
392
435
|
}
|
393
436
|
if (!flags || !flags.modified || !this.isRadio) {
|
394
437
|
if (changed) {
|
@@ -412,7 +455,7 @@ export default class RadioComponent extends ListComponent {
|
|
412
455
|
* @param {*} value - The value to normalize
|
413
456
|
* @returns {*} - Returns the normalized value
|
414
457
|
*/
|
415
|
-
|
458
|
+
normalizeValue(value) {
|
416
459
|
const dataType = this.component.dataType || 'auto';
|
417
460
|
if (value === this.emptyValue) {
|
418
461
|
return value;
|
@@ -444,17 +487,9 @@ export default class RadioComponent extends ListComponent {
|
|
444
487
|
value = !(!value || value.toString() === 'false');
|
445
488
|
break;
|
446
489
|
}
|
447
|
-
return value;
|
490
|
+
return super.normalizeValue(value);
|
448
491
|
}
|
449
|
-
|
450
|
-
|
451
|
-
if (this.isSelectURL && this.templateData && this.templateData[valueConverted]) {
|
452
|
-
const submission = this.root.submission;
|
453
|
-
if (!submission.metadata.selectData) {
|
454
|
-
submission.metadata.selectData = {};
|
455
|
-
}
|
456
|
-
_.set(submission.metadata.selectData, this.path, this.templateData[valueConverted]);
|
457
|
-
}
|
458
|
-
return super.normalizeValue(valueConverted);
|
492
|
+
isSingleInputValue() {
|
493
|
+
return true;
|
459
494
|
}
|
460
495
|
}
|
@@ -8,12 +8,6 @@ export default class SelectBoxesComponent extends RadioComponent {
|
|
8
8
|
* @returns {boolean} - If the value is empty.
|
9
9
|
*/
|
10
10
|
isEmpty(value?: any): boolean;
|
11
|
-
/**
|
12
|
-
* Normalize values coming into updateValue.
|
13
|
-
* @param {any} value - The value to normalize.
|
14
|
-
* @returns {*} - The normalized value
|
15
|
-
*/
|
16
|
-
normalizeValue(value: any): any;
|
17
11
|
setInputsDisabled(value: any, onlyUnchecked: any): void;
|
18
12
|
checkComponentValidity(data: any, dirty: any, rowData: any, options: any, errors?: any[]): boolean;
|
19
13
|
}
|
@@ -72,7 +72,6 @@ export default {
|
|
72
72
|
reCaptchaTokenValidationError: 'ReCAPTCHA: Token validation error',
|
73
73
|
reCaptchaTokenNotSpecifiedError: 'ReCAPTCHA: Token is not specified in submission',
|
74
74
|
apiKey: 'API Key is not unique: {{key}}',
|
75
|
-
apiKeyNotValid: 'API Key is not valid: {{key}}',
|
76
75
|
typeRemaining: '{{ remaining }} {{ type }} remaining.',
|
77
76
|
typeCount: '{{ count }} {{ type }}',
|
78
77
|
requiredDayField: '{{ field }} is required',
|