@formio/js 5.1.0-dev.6156.9cd5779 → 5.1.0-dev.6159.811e953
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formio.builder.css +1 -0
- package/dist/formio.builder.min.css +1 -1
- package/dist/formio.form.css +1 -0
- package/dist/formio.form.js +10 -10
- package/dist/formio.form.min.css +1 -1
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.full.css +1 -0
- package/dist/formio.full.js +11 -11
- package/dist/formio.full.min.css +1 -1
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.js +4 -4
- package/dist/formio.min.js +1 -1
- package/dist/formio.utils.js +5 -5
- package/dist/formio.utils.min.js +1 -1
- package/lib/cjs/WebformBuilder.js +18 -5
- package/lib/cjs/components/_classes/nested/NestedComponent.js +1 -1
- package/lib/cjs/components/datamap/DataMap.js +2 -7
- package/lib/cjs/components/radio/Radio.d.ts +8 -0
- package/lib/cjs/components/radio/Radio.js +15 -5
- package/lib/cjs/components/select/Select.d.ts +1 -0
- package/lib/cjs/components/select/Select.js +18 -2
- package/lib/cjs/components/selectboxes/SelectBoxes.d.ts +6 -0
- package/lib/cjs/translations/en.d.ts +1 -0
- package/lib/cjs/translations/en.js +1 -1
- package/lib/cjs/utils/utils.js +10 -3
- package/lib/mjs/WebformBuilder.js +19 -6
- package/lib/mjs/components/_classes/nested/NestedComponent.js +1 -1
- package/lib/mjs/components/datamap/DataMap.js +2 -7
- package/lib/mjs/components/radio/Radio.d.ts +8 -0
- package/lib/mjs/components/radio/Radio.js +15 -5
- package/lib/mjs/components/select/Select.d.ts +1 -0
- package/lib/mjs/components/select/Select.js +18 -2
- package/lib/mjs/components/selectboxes/SelectBoxes.d.ts +6 -0
- package/lib/mjs/translations/en.d.ts +1 -0
- package/lib/mjs/translations/en.js +1 -0
- package/lib/mjs/utils/utils.js +10 -2
- package/package.json +2 -2
@@ -1129,11 +1129,14 @@ class WebformBuilder extends Component_1.default {
|
|
1129
1129
|
const repeatablePaths = [];
|
1130
1130
|
const keys = new Map();
|
1131
1131
|
(0, utils_1.eachComponent)(this.form.components, (comp, path, components, parent, paths) => {
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1132
|
+
const isLayout = (0, utils_1.componentInfo)(comp).layout;
|
1133
|
+
if (!isLayout) {
|
1134
|
+
if (keys.has(paths.dataPath)) {
|
1135
|
+
repeatablePaths.push(paths.dataPath);
|
1136
|
+
}
|
1137
|
+
else {
|
1138
|
+
keys.set(paths.dataPath, true);
|
1139
|
+
}
|
1137
1140
|
}
|
1138
1141
|
}, true);
|
1139
1142
|
return repeatablePaths;
|
@@ -1141,12 +1144,22 @@ class WebformBuilder extends Component_1.default {
|
|
1141
1144
|
highlightInvalidComponents() {
|
1142
1145
|
const repeatablePaths = this.findRepeatablePaths();
|
1143
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;
|
1144
1150
|
this.webform.everyComponent((comp) => {
|
1145
1151
|
const path = comp.path;
|
1146
1152
|
if (repeatablePaths.includes(path)) {
|
1147
1153
|
comp.setCustomValidity(this.t('apiKey', { key: comp.key }));
|
1148
1154
|
hasInvalidComponents = true;
|
1149
1155
|
}
|
1156
|
+
else if (comp.key.replace(badCharacters, '') === '') {
|
1157
|
+
comp.setCustomValidity(this.t('apiKeyNotValid', { key: comp.key }));
|
1158
|
+
hasInvalidComponents = true;
|
1159
|
+
}
|
1160
|
+
else {
|
1161
|
+
comp.setCustomValidity();
|
1162
|
+
}
|
1150
1163
|
});
|
1151
1164
|
this.emit('builderFormValidityChange', hasInvalidComponents);
|
1152
1165
|
}
|
@@ -664,7 +664,7 @@ class NestedComponent extends Field_1.default {
|
|
664
664
|
validationProcessor({ scope, data, row, instance, paths }, flags) {
|
665
665
|
var _a;
|
666
666
|
const { dirty } = flags;
|
667
|
-
if (this.root.hasSubWizards && this.page !== this.root.page) {
|
667
|
+
if (this.root && this.root.hasSubWizards && this.page !== this.root.page) {
|
668
668
|
instance = ((_a = this.componentsMap) === null || _a === void 0 ? void 0 : _a.hasOwnProperty(paths.dataPath))
|
669
669
|
? this.componentsMap[paths.dataPath]
|
670
670
|
: this.getComponent(paths.dataPath);
|
@@ -228,9 +228,6 @@ class DataMapComponent extends DataGrid_1.default {
|
|
228
228
|
options.name += `[${rowIndex}]`;
|
229
229
|
options.row = `${rowIndex}`;
|
230
230
|
options.rowIndex = rowIndex;
|
231
|
-
options.onChange = (flags, changed, modified) => {
|
232
|
-
this.triggerChange({ modified });
|
233
|
-
};
|
234
231
|
const components = {};
|
235
232
|
components['__key'] = this.createComponent(this.keySchema, options, { __key: this.builderMode ? this.defaultRowKey : key });
|
236
233
|
components['__key'].on('componentChange', (event) => {
|
@@ -246,9 +243,7 @@ class DataMapComponent extends DataGrid_1.default {
|
|
246
243
|
valueComponent.key = key;
|
247
244
|
const componentOptions = this.options;
|
248
245
|
componentOptions.row = options.row;
|
249
|
-
|
250
|
-
componentOptionsCloned.onChange = options.onChange;
|
251
|
-
components[this.valueKey] = this.createComponent(valueComponent, componentOptionsCloned, this.dataValue);
|
246
|
+
components[this.valueKey] = this.createComponent(valueComponent, componentOptions, this.dataValue);
|
252
247
|
return components;
|
253
248
|
}
|
254
249
|
get canAddColumn() {
|
@@ -271,7 +266,7 @@ class DataMapComponent extends DataGrid_1.default {
|
|
271
266
|
const index = this.rows.length;
|
272
267
|
this.rows[index] = this.createRowComponents(this.dataValue, index);
|
273
268
|
this.redraw();
|
274
|
-
this.triggerChange(
|
269
|
+
this.triggerChange();
|
275
270
|
}
|
276
271
|
removeRow(index) {
|
277
272
|
const keys = Object.keys(this.dataValue);
|
@@ -28,6 +28,7 @@ export default class RadioComponent extends ListComponent {
|
|
28
28
|
optionsLoaded: boolean | undefined;
|
29
29
|
loadedOptions: any[] | undefined;
|
30
30
|
beforeSubmit(): Promise<any>;
|
31
|
+
convertValues(values: any): any;
|
31
32
|
render(): string;
|
32
33
|
attach(element: any): Promise<void>;
|
33
34
|
detach(element: any): void;
|
@@ -41,5 +42,12 @@ export default class RadioComponent extends ListComponent {
|
|
41
42
|
setSelectedClasses(): void;
|
42
43
|
updateValue(value: any, flags: any): boolean;
|
43
44
|
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;
|
44
52
|
}
|
45
53
|
import ListComponent from '../_classes/list/ListComponent';
|
@@ -153,6 +153,12 @@ class RadioComponent extends ListComponent_1.default {
|
|
153
153
|
this.dataReady.then(() => res(true));
|
154
154
|
});
|
155
155
|
}
|
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
|
+
}
|
156
162
|
render() {
|
157
163
|
if (!this.optionsLoaded) {
|
158
164
|
return super.render(this.renderTemplate('loader'));
|
@@ -160,7 +166,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
160
166
|
return super.render(this.renderTemplate('radio', {
|
161
167
|
input: this.inputInfo,
|
162
168
|
inline: this.component.inline,
|
163
|
-
values: this.component.dataSrc === 'values' ? this.component.values : this.loadedOptions,
|
169
|
+
values: this.component.dataSrc === 'values' ? this.convertValues(this.component.values) : this.loadedOptions,
|
164
170
|
value: this.dataValue,
|
165
171
|
row: this.row,
|
166
172
|
}));
|
@@ -406,7 +412,7 @@ class RadioComponent extends ListComponent_1.default {
|
|
406
412
|
* @param {*} value - The value to normalize
|
407
413
|
* @returns {*} - Returns the normalized value
|
408
414
|
*/
|
409
|
-
|
415
|
+
convertByDataType(value) {
|
410
416
|
const dataType = this.component.dataType || 'auto';
|
411
417
|
if (value === this.emptyValue) {
|
412
418
|
return value;
|
@@ -438,14 +444,18 @@ class RadioComponent extends ListComponent_1.default {
|
|
438
444
|
value = !(!value || value.toString() === 'false');
|
439
445
|
break;
|
440
446
|
}
|
441
|
-
|
447
|
+
return value;
|
448
|
+
}
|
449
|
+
normalizeValue(value) {
|
450
|
+
const valueConverted = this.convertByDataType(value);
|
451
|
+
if (this.isSelectURL && this.templateData && this.templateData[valueConverted]) {
|
442
452
|
const submission = this.root.submission;
|
443
453
|
if (!submission.metadata.selectData) {
|
444
454
|
submission.metadata.selectData = {};
|
445
455
|
}
|
446
|
-
lodash_1.default.set(submission.metadata.selectData, this.path, this.templateData[
|
456
|
+
lodash_1.default.set(submission.metadata.selectData, this.path, this.templateData[valueConverted]);
|
447
457
|
}
|
448
|
-
return super.normalizeValue(
|
458
|
+
return super.normalizeValue(valueConverted);
|
449
459
|
}
|
450
460
|
}
|
451
461
|
exports.default = RadioComponent;
|
@@ -89,6 +89,7 @@ export default class SelectComponent extends ListComponent {
|
|
89
89
|
disableInfiniteScroll(): void;
|
90
90
|
set serverCount(value: any);
|
91
91
|
get serverCount(): any;
|
92
|
+
shouldResetChoicesItems(items: any): boolean;
|
92
93
|
setItems(items: any, fromSearch: any): void;
|
93
94
|
selectItems: any;
|
94
95
|
set downloadedResources(value: any);
|
@@ -355,6 +355,18 @@ class SelectComponent extends ListComponent_1.default {
|
|
355
355
|
this.downloadedResources.serverCount = this.downloadedResources.length;
|
356
356
|
this.serverCount = this.downloadedResources.length;
|
357
357
|
}
|
358
|
+
shouldResetChoicesItems(items) {
|
359
|
+
if (this.choices._store.choices.length !== items.length) {
|
360
|
+
return true;
|
361
|
+
}
|
362
|
+
for (let item of items) {
|
363
|
+
const choicesItem = this.choices._store.choices.find((i) => i.label === item.label);
|
364
|
+
if (!choicesItem) {
|
365
|
+
return true;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
return false;
|
369
|
+
}
|
358
370
|
/* eslint-disable max-statements */
|
359
371
|
setItems(items, fromSearch) {
|
360
372
|
var _a, _b;
|
@@ -441,7 +453,7 @@ class SelectComponent extends ListComponent_1.default {
|
|
441
453
|
this.addOption(itemValueAndLabel.value, itemValueAndLabel.label, {}, lodash_1.default.get(item, this.component.idPath, String(index)));
|
442
454
|
});
|
443
455
|
if (this.choices) {
|
444
|
-
this.choices.setChoices(this.selectOptions, 'value', 'label', true);
|
456
|
+
this.choices.setChoices(this.selectOptions, 'value', 'label', true, true, !fromSearch && this.shouldResetChoicesItems(this.selectOptions));
|
445
457
|
}
|
446
458
|
else if (this.loading) {
|
447
459
|
// Re-attach select input.
|
@@ -903,8 +915,9 @@ class SelectComponent extends ListComponent_1.default {
|
|
903
915
|
});
|
904
916
|
}
|
905
917
|
// Add value options.
|
918
|
+
const value = this.undoValueTyping(this.dataValue);
|
906
919
|
this.addValueOptions();
|
907
|
-
this.setChoicesValue(
|
920
|
+
this.setChoicesValue(value);
|
908
921
|
if (this.isSelectResource && this.refs.addResource) {
|
909
922
|
this.addEventListener(this.refs.addResource, 'click', (event) => {
|
910
923
|
event.preventDefault();
|
@@ -1283,6 +1296,9 @@ class SelectComponent extends ListComponent_1.default {
|
|
1283
1296
|
this.lazyLoadInit = true;
|
1284
1297
|
const searchProperty = this.component.searchField || this.component.valueProperty;
|
1285
1298
|
this.triggerUpdate(lodash_1.default.get(value.data || value, searchProperty, value), true);
|
1299
|
+
this.itemsLoaded.then(() => {
|
1300
|
+
this.setChoicesValue(value, hasPreviousValue, flags);
|
1301
|
+
});
|
1286
1302
|
return changed;
|
1287
1303
|
}
|
1288
1304
|
// Add the value options.
|
@@ -8,6 +8,12 @@ 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;
|
11
17
|
setInputsDisabled(value: any, onlyUnchecked: any): void;
|
12
18
|
checkComponentValidity(data: any, dirty: any, rowData: any, options: any, errors?: any[]): boolean;
|
13
19
|
}
|
@@ -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}}', 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.
|
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}}', apiKeyNotValid: 'API Key is not valid: {{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,13 +209,20 @@ 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;
|
213
212
|
const { value: comparedValue, operator, component: conditionComponentPath } = cond;
|
214
213
|
if (!conditionComponentPath) {
|
215
214
|
return true;
|
216
215
|
}
|
217
216
|
const splittedConditionPath = conditionComponentPath.split('.');
|
218
|
-
const
|
217
|
+
const checkParentTypeInTree = (instance, componentType) => {
|
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);
|
219
226
|
if (conditionalPaths.length > 0) {
|
220
227
|
return conditionalPaths.map((path) => {
|
221
228
|
const value = getComponentActualValue(path, data, row);
|
@@ -334,7 +341,7 @@ function getRow(component, row, instance, conditional) {
|
|
334
341
|
}
|
335
342
|
const dataParent = getDataParentComponent(instance);
|
336
343
|
if (dataParent) {
|
337
|
-
const parentPath = (_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.
|
344
|
+
const parentPath = (_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.localPath;
|
338
345
|
const isTriggerCondtionComponentPath = condition.when || !condition.conditions
|
339
346
|
? (_b = condition.when) === null || _b === void 0 ? void 0 : _b.startsWith((_c = dataParent.paths) === null || _c === void 0 ? void 0 : _c.localPath)
|
340
347
|
: lodash_1.default.some(condition.conditions, cond => { var _a; return cond.component.startsWith((_a = dataParent.paths) === null || _a === void 0 ? void 0 : _a.localPath); });
|
@@ -3,7 +3,7 @@ import Component from './components/_classes/component/Component';
|
|
3
3
|
import tippy from 'tippy.js';
|
4
4
|
import Components from './components/Components';
|
5
5
|
import { Formio } from './Formio';
|
6
|
-
import { fastCloneDeep, bootstrapVersion, getArrayFromComponentPath, getStringFromComponentPath, eachComponent, getComponent, } from './utils';
|
6
|
+
import { fastCloneDeep, bootstrapVersion, getArrayFromComponentPath, getStringFromComponentPath, eachComponent, getComponent, componentInfo } from './utils';
|
7
7
|
import BuilderUtils from './utils/builder';
|
8
8
|
import _ from 'lodash';
|
9
9
|
import autoScroll from 'dom-autoscroller';
|
@@ -1112,11 +1112,14 @@ export default class WebformBuilder extends Component {
|
|
1112
1112
|
const repeatablePaths = [];
|
1113
1113
|
const keys = new Map();
|
1114
1114
|
eachComponent(this.form.components, (comp, path, components, parent, paths) => {
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1115
|
+
const isLayout = componentInfo(comp).layout;
|
1116
|
+
if (!isLayout) {
|
1117
|
+
if (keys.has(paths.dataPath)) {
|
1118
|
+
repeatablePaths.push(paths.dataPath);
|
1119
|
+
}
|
1120
|
+
else {
|
1121
|
+
keys.set(paths.dataPath, true);
|
1122
|
+
}
|
1120
1123
|
}
|
1121
1124
|
}, true);
|
1122
1125
|
return repeatablePaths;
|
@@ -1124,12 +1127,22 @@ export default class WebformBuilder extends Component {
|
|
1124
1127
|
highlightInvalidComponents() {
|
1125
1128
|
const repeatablePaths = this.findRepeatablePaths();
|
1126
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;
|
1127
1133
|
this.webform.everyComponent((comp) => {
|
1128
1134
|
const path = comp.path;
|
1129
1135
|
if (repeatablePaths.includes(path)) {
|
1130
1136
|
comp.setCustomValidity(this.t('apiKey', { key: comp.key }));
|
1131
1137
|
hasInvalidComponents = true;
|
1132
1138
|
}
|
1139
|
+
else if (comp.key.replace(badCharacters, '') === '') {
|
1140
|
+
comp.setCustomValidity(this.t('apiKeyNotValid', { key: comp.key }));
|
1141
|
+
hasInvalidComponents = true;
|
1142
|
+
}
|
1143
|
+
else {
|
1144
|
+
comp.setCustomValidity();
|
1145
|
+
}
|
1133
1146
|
});
|
1134
1147
|
this.emit('builderFormValidityChange', hasInvalidComponents);
|
1135
1148
|
}
|
@@ -659,7 +659,7 @@ export default class NestedComponent extends Field {
|
|
659
659
|
}
|
660
660
|
validationProcessor({ scope, data, row, instance, paths }, flags) {
|
661
661
|
const { dirty } = flags;
|
662
|
-
if (this.root.hasSubWizards && this.page !== this.root.page) {
|
662
|
+
if (this.root && this.root.hasSubWizards && this.page !== this.root.page) {
|
663
663
|
instance = this.componentsMap?.hasOwnProperty(paths.dataPath)
|
664
664
|
? this.componentsMap[paths.dataPath]
|
665
665
|
: this.getComponent(paths.dataPath);
|
@@ -223,9 +223,6 @@ export default class DataMapComponent extends DataGridComponent {
|
|
223
223
|
options.name += `[${rowIndex}]`;
|
224
224
|
options.row = `${rowIndex}`;
|
225
225
|
options.rowIndex = rowIndex;
|
226
|
-
options.onChange = (flags, changed, modified) => {
|
227
|
-
this.triggerChange({ modified });
|
228
|
-
};
|
229
226
|
const components = {};
|
230
227
|
components['__key'] = this.createComponent(this.keySchema, options, { __key: this.builderMode ? this.defaultRowKey : key });
|
231
228
|
components['__key'].on('componentChange', (event) => {
|
@@ -241,9 +238,7 @@ export default class DataMapComponent extends DataGridComponent {
|
|
241
238
|
valueComponent.key = key;
|
242
239
|
const componentOptions = this.options;
|
243
240
|
componentOptions.row = options.row;
|
244
|
-
|
245
|
-
componentOptionsCloned.onChange = options.onChange;
|
246
|
-
components[this.valueKey] = this.createComponent(valueComponent, componentOptionsCloned, this.dataValue);
|
241
|
+
components[this.valueKey] = this.createComponent(valueComponent, componentOptions, this.dataValue);
|
247
242
|
return components;
|
248
243
|
}
|
249
244
|
get canAddColumn() {
|
@@ -266,7 +261,7 @@ export default class DataMapComponent extends DataGridComponent {
|
|
266
261
|
const index = this.rows.length;
|
267
262
|
this.rows[index] = this.createRowComponents(this.dataValue, index);
|
268
263
|
this.redraw();
|
269
|
-
this.triggerChange(
|
264
|
+
this.triggerChange();
|
270
265
|
}
|
271
266
|
removeRow(index) {
|
272
267
|
const keys = Object.keys(this.dataValue);
|
@@ -28,6 +28,7 @@ export default class RadioComponent extends ListComponent {
|
|
28
28
|
optionsLoaded: boolean | undefined;
|
29
29
|
loadedOptions: any[] | undefined;
|
30
30
|
beforeSubmit(): Promise<any>;
|
31
|
+
convertValues(values: any): any;
|
31
32
|
render(): string;
|
32
33
|
attach(element: any): Promise<void>;
|
33
34
|
detach(element: any): void;
|
@@ -41,5 +42,12 @@ export default class RadioComponent extends ListComponent {
|
|
41
42
|
setSelectedClasses(): void;
|
42
43
|
updateValue(value: any, flags: any): boolean;
|
43
44
|
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;
|
44
52
|
}
|
45
53
|
import ListComponent from '../_classes/list/ListComponent';
|
@@ -153,6 +153,12 @@ export default class RadioComponent extends ListComponent {
|
|
153
153
|
this.dataReady.then(() => res(true));
|
154
154
|
});
|
155
155
|
}
|
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
|
+
}
|
156
162
|
render() {
|
157
163
|
if (!this.optionsLoaded) {
|
158
164
|
return super.render(this.renderTemplate('loader'));
|
@@ -160,7 +166,7 @@ export default class RadioComponent extends ListComponent {
|
|
160
166
|
return super.render(this.renderTemplate('radio', {
|
161
167
|
input: this.inputInfo,
|
162
168
|
inline: this.component.inline,
|
163
|
-
values: this.component.dataSrc === 'values' ? this.component.values : this.loadedOptions,
|
169
|
+
values: this.component.dataSrc === 'values' ? this.convertValues(this.component.values) : this.loadedOptions,
|
164
170
|
value: this.dataValue,
|
165
171
|
row: this.row,
|
166
172
|
}));
|
@@ -406,7 +412,7 @@ export default class RadioComponent extends ListComponent {
|
|
406
412
|
* @param {*} value - The value to normalize
|
407
413
|
* @returns {*} - Returns the normalized value
|
408
414
|
*/
|
409
|
-
|
415
|
+
convertByDataType(value) {
|
410
416
|
const dataType = this.component.dataType || 'auto';
|
411
417
|
if (value === this.emptyValue) {
|
412
418
|
return value;
|
@@ -438,13 +444,17 @@ export default class RadioComponent extends ListComponent {
|
|
438
444
|
value = !(!value || value.toString() === 'false');
|
439
445
|
break;
|
440
446
|
}
|
441
|
-
|
447
|
+
return value;
|
448
|
+
}
|
449
|
+
normalizeValue(value) {
|
450
|
+
const valueConverted = this.convertByDataType(value);
|
451
|
+
if (this.isSelectURL && this.templateData && this.templateData[valueConverted]) {
|
442
452
|
const submission = this.root.submission;
|
443
453
|
if (!submission.metadata.selectData) {
|
444
454
|
submission.metadata.selectData = {};
|
445
455
|
}
|
446
|
-
_.set(submission.metadata.selectData, this.path, this.templateData[
|
456
|
+
_.set(submission.metadata.selectData, this.path, this.templateData[valueConverted]);
|
447
457
|
}
|
448
|
-
return super.normalizeValue(
|
458
|
+
return super.normalizeValue(valueConverted);
|
449
459
|
}
|
450
460
|
}
|
@@ -89,6 +89,7 @@ export default class SelectComponent extends ListComponent {
|
|
89
89
|
disableInfiniteScroll(): void;
|
90
90
|
set serverCount(value: any);
|
91
91
|
get serverCount(): any;
|
92
|
+
shouldResetChoicesItems(items: any): boolean;
|
92
93
|
setItems(items: any, fromSearch: any): void;
|
93
94
|
selectItems: any;
|
94
95
|
set downloadedResources(value: any);
|
@@ -359,6 +359,18 @@ export default class SelectComponent extends ListComponent {
|
|
359
359
|
this.downloadedResources.serverCount = this.downloadedResources.length;
|
360
360
|
this.serverCount = this.downloadedResources.length;
|
361
361
|
}
|
362
|
+
shouldResetChoicesItems(items) {
|
363
|
+
if (this.choices._store.choices.length !== items.length) {
|
364
|
+
return true;
|
365
|
+
}
|
366
|
+
for (let item of items) {
|
367
|
+
const choicesItem = this.choices._store.choices.find((i) => i.label === item.label);
|
368
|
+
if (!choicesItem) {
|
369
|
+
return true;
|
370
|
+
}
|
371
|
+
}
|
372
|
+
return false;
|
373
|
+
}
|
362
374
|
/* eslint-disable max-statements */
|
363
375
|
setItems(items, fromSearch) {
|
364
376
|
this.selectItems = items;
|
@@ -444,7 +456,7 @@ export default class SelectComponent extends ListComponent {
|
|
444
456
|
this.addOption(itemValueAndLabel.value, itemValueAndLabel.label, {}, _.get(item, this.component.idPath, String(index)));
|
445
457
|
});
|
446
458
|
if (this.choices) {
|
447
|
-
this.choices.setChoices(this.selectOptions, 'value', 'label', true);
|
459
|
+
this.choices.setChoices(this.selectOptions, 'value', 'label', true, true, !fromSearch && this.shouldResetChoicesItems(this.selectOptions));
|
448
460
|
}
|
449
461
|
else if (this.loading) {
|
450
462
|
// Re-attach select input.
|
@@ -935,8 +947,9 @@ export default class SelectComponent extends ListComponent {
|
|
935
947
|
});
|
936
948
|
}
|
937
949
|
// Add value options.
|
950
|
+
const value = this.undoValueTyping(this.dataValue);
|
938
951
|
this.addValueOptions();
|
939
|
-
this.setChoicesValue(
|
952
|
+
this.setChoicesValue(value);
|
940
953
|
if (this.isSelectResource && this.refs.addResource) {
|
941
954
|
this.addEventListener(this.refs.addResource, 'click', (event) => {
|
942
955
|
event.preventDefault();
|
@@ -1312,6 +1325,9 @@ export default class SelectComponent extends ListComponent {
|
|
1312
1325
|
this.lazyLoadInit = true;
|
1313
1326
|
const searchProperty = this.component.searchField || this.component.valueProperty;
|
1314
1327
|
this.triggerUpdate(_.get(value.data || value, searchProperty, value), true);
|
1328
|
+
this.itemsLoaded.then(() => {
|
1329
|
+
this.setChoicesValue(value, hasPreviousValue, flags);
|
1330
|
+
});
|
1315
1331
|
return changed;
|
1316
1332
|
}
|
1317
1333
|
// Add the value options.
|
@@ -8,6 +8,12 @@ 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;
|
11
17
|
setInputsDisabled(value: any, onlyUnchecked: any): void;
|
12
18
|
checkComponentValidity(data: any, dirty: any, rowData: any, options: any, errors?: any[]): boolean;
|
13
19
|
}
|
@@ -72,6 +72,7 @@ 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}}',
|
75
76
|
typeRemaining: '{{ remaining }} {{ type }} remaining.',
|
76
77
|
typeCount: '{{ count }} {{ type }}',
|
77
78
|
requiredDayField: '{{ field }} is required',
|
package/lib/mjs/utils/utils.js
CHANGED
@@ -199,7 +199,15 @@ export function checkSimpleConditional(component, condition, row, data, instance
|
|
199
199
|
return true;
|
200
200
|
}
|
201
201
|
const splittedConditionPath = conditionComponentPath.split('.');
|
202
|
-
const
|
202
|
+
const checkParentTypeInTree = (instance, componentType) => {
|
203
|
+
if (!instance?.parent) {
|
204
|
+
return false;
|
205
|
+
}
|
206
|
+
return instance?.parent.type === componentType || checkParentTypeInTree(instance.parent, componentType);
|
207
|
+
};
|
208
|
+
const conditionalPaths = checkParentTypeInTree(instance, 'datagrid') || checkParentTypeInTree(instance, 'editgrid')
|
209
|
+
? []
|
210
|
+
: getConditionalPathsRecursive(splittedConditionPath, data);
|
203
211
|
if (conditionalPaths.length > 0) {
|
204
212
|
return conditionalPaths.map((path) => {
|
205
213
|
const value = getComponentActualValue(path, data, row);
|
@@ -313,7 +321,7 @@ function getRow(component, row, instance, conditional) {
|
|
313
321
|
}
|
314
322
|
const dataParent = getDataParentComponent(instance);
|
315
323
|
if (dataParent) {
|
316
|
-
const parentPath = dataParent.paths?.
|
324
|
+
const parentPath = dataParent.paths?.localPath;
|
317
325
|
const isTriggerCondtionComponentPath = condition.when || !condition.conditions
|
318
326
|
? condition.when?.startsWith(dataParent.paths?.localPath)
|
319
327
|
: _.some(condition.conditions, cond => cond.component.startsWith(dataParent.paths?.localPath));
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@formio/js",
|
3
|
-
"version": "5.1.0-dev.
|
3
|
+
"version": "5.1.0-dev.6159.811e953",
|
4
4
|
"description": "JavaScript powered Forms with JSON Form Builder",
|
5
5
|
"main": "lib/cjs/index.js",
|
6
6
|
"exports": {
|
@@ -81,7 +81,7 @@
|
|
81
81
|
"homepage": "https://github.com/formio/formio.js#readme",
|
82
82
|
"dependencies": {
|
83
83
|
"@formio/bootstrap": "v3.0.0-dev.121.085d187",
|
84
|
-
"@formio/core": "
|
84
|
+
"@formio/core": "v2.4.0-dev.255.7fab6ff",
|
85
85
|
"@formio/text-mask-addons": "3.8.0-formio.4",
|
86
86
|
"@formio/vanilla-text-mask": "^5.1.1-formio.1",
|
87
87
|
"abortcontroller-polyfill": "^1.7.5",
|