@formio/js 5.3.0 → 5.3.1
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.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 +602 -602
- package/dist/formio.form.min.js +1 -1
- package/dist/formio.form.min.js.LICENSE.txt +1 -1
- package/dist/formio.full.js +775 -775
- package/dist/formio.full.min.js +1 -1
- package/dist/formio.full.min.js.LICENSE.txt +1 -1
- package/dist/formio.js +514 -514
- package/dist/formio.min.js +1 -1
- package/dist/formio.min.js.LICENSE.txt +1 -1
- package/dist/formio.utils.js +485 -485
- package/dist/formio.utils.min.js +1 -1
- package/dist/formio.utils.min.js.LICENSE.txt +1 -1
- package/lib/cjs/Embed.js +1 -1
- package/lib/cjs/Formio.js +1 -1
- package/lib/cjs/PDF.js +9 -1
- package/lib/cjs/Webform.js +1 -1
- package/lib/cjs/components/_classes/component/Component.d.ts +1 -0
- package/lib/cjs/components/_classes/component/Component.js +14 -1
- package/lib/cjs/components/_classes/input/Input.js +17 -2
- package/lib/cjs/components/address/Address.js +2 -1
- package/lib/cjs/components/datagrid/DataGrid.js +12 -1
- package/lib/cjs/components/fieldset/Fieldset.d.ts +1 -0
- package/lib/cjs/components/fieldset/Fieldset.js +7 -0
- package/lib/cjs/components/select/Select.js +13 -8
- package/lib/cjs/components/tags/Tags.js +2 -1
- package/lib/cjs/package.json +1 -1
- package/lib/cjs/utils/conditionOperators/IsEqualTo.d.ts +1 -3
- package/lib/cjs/utils/conditionOperators/IsEqualTo.js +6 -12
- package/lib/cjs/utils/conditionOperators/index.d.ts +2 -1
- package/lib/cjs/utils/index.d.ts +2 -1
- package/lib/cjs/utils/utils.d.ts +9 -0
- package/lib/cjs/utils/utils.js +132 -2
- package/lib/cjs/widgets/CalendarWidget.js +2 -1
- package/lib/mjs/Embed.js +1 -1
- package/lib/mjs/Formio.js +1 -1
- package/lib/mjs/PDF.js +9 -1
- package/lib/mjs/Webform.js +1 -1
- package/lib/mjs/components/_classes/component/Component.d.ts +1 -0
- package/lib/mjs/components/_classes/component/Component.js +12 -1
- package/lib/mjs/components/_classes/input/Input.js +16 -3
- package/lib/mjs/components/address/Address.js +1 -1
- package/lib/mjs/components/datagrid/DataGrid.js +11 -1
- package/lib/mjs/components/fieldset/Fieldset.d.ts +1 -0
- package/lib/mjs/components/fieldset/Fieldset.js +7 -0
- package/lib/mjs/components/select/Select.js +11 -8
- package/lib/mjs/components/tags/Tags.js +1 -1
- package/lib/mjs/package.json +1 -1
- package/lib/mjs/utils/conditionOperators/IsEqualTo.d.ts +1 -3
- package/lib/mjs/utils/conditionOperators/IsEqualTo.js +6 -11
- package/lib/mjs/utils/conditionOperators/index.d.ts +2 -1
- package/lib/mjs/utils/index.d.ts +2 -1
- package/lib/mjs/utils/utils.d.ts +9 -0
- package/lib/mjs/utils/utils.js +130 -1
- package/lib/mjs/widgets/CalendarWidget.js +1 -1
- package/package.json +3 -3
|
@@ -503,6 +503,17 @@ export default class Component extends Element {
|
|
|
503
503
|
}
|
|
504
504
|
return label;
|
|
505
505
|
}
|
|
506
|
+
getFieldsetLegendIds() {
|
|
507
|
+
const legendIds = [];
|
|
508
|
+
let currentParent = this.parent;
|
|
509
|
+
while (currentParent) {
|
|
510
|
+
if (currentParent.component?.type === 'fieldset' && currentParent.component?.legend) {
|
|
511
|
+
legendIds.push(`l-${currentParent.id}-legend`);
|
|
512
|
+
}
|
|
513
|
+
currentParent = currentParent.parent;
|
|
514
|
+
}
|
|
515
|
+
return legendIds.reverse().join(' ');
|
|
516
|
+
}
|
|
506
517
|
init() {
|
|
507
518
|
this.disabled = this.shouldDisabled;
|
|
508
519
|
this._visible = this.hasCondition() ? !this.conditionallyHidden() : !this.component.hidden;
|
|
@@ -3672,7 +3683,7 @@ export default class Component extends Element {
|
|
|
3672
3683
|
class: 'form-control',
|
|
3673
3684
|
lang: this.options.language,
|
|
3674
3685
|
};
|
|
3675
|
-
if (this.component.placeholder) {
|
|
3686
|
+
if (this.component.placeholder && !this.options?.readOnly) {
|
|
3676
3687
|
attributes.placeholder = this.t(this.component.placeholder, { _userInput: true });
|
|
3677
3688
|
}
|
|
3678
3689
|
if (this.component.tabindex) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Multivalue from '../multivalue/Multivalue';
|
|
2
|
-
import { convertStringToHTMLElement } from '../../../utils/index';
|
|
2
|
+
import { announceScreenReaderMessage, convertStringToHTMLElement } from '../../../utils/index';
|
|
3
3
|
import Widgets from '../../../widgets/index';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
export default class Input extends Multivalue {
|
|
@@ -29,7 +29,7 @@ export default class Input extends Multivalue {
|
|
|
29
29
|
if (this.component.inputMode) {
|
|
30
30
|
attr.inputmode = this.component.inputMode;
|
|
31
31
|
}
|
|
32
|
-
if (this.component.placeholder) {
|
|
32
|
+
if (this.component.placeholder && !this.options?.readOnly) {
|
|
33
33
|
attr.placeholder = this.getFormattedAttribute(this.component.placeholder);
|
|
34
34
|
}
|
|
35
35
|
if (this.component.tabindex) {
|
|
@@ -161,6 +161,9 @@ export default class Input extends Multivalue {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
updateValueAt(value, flags, index) {
|
|
164
|
+
if (flags.modified) {
|
|
165
|
+
announceScreenReaderMessage(this, value, index);
|
|
166
|
+
}
|
|
164
167
|
flags = flags || {};
|
|
165
168
|
if (_.get(this.component, 'showWordCount', false)) {
|
|
166
169
|
if (this.refs.wordcount && this.refs.wordcount[index]) {
|
|
@@ -185,7 +188,7 @@ export default class Input extends Multivalue {
|
|
|
185
188
|
updateValue(value, flags, index) {
|
|
186
189
|
flags = flags || {};
|
|
187
190
|
const changed = super.updateValue(value, flags);
|
|
188
|
-
this.triggerUpdateValueAt(this.dataValue, flags, index);
|
|
191
|
+
this.triggerUpdateValueAt(this.dataValue, { ...flags }, index);
|
|
189
192
|
return changed;
|
|
190
193
|
}
|
|
191
194
|
parseValue(value) {
|
|
@@ -196,6 +199,7 @@ export default class Input extends Multivalue {
|
|
|
196
199
|
}
|
|
197
200
|
attach(element) {
|
|
198
201
|
this.loadRefs(element, {
|
|
202
|
+
announceMessage: 'multiple',
|
|
199
203
|
charcount: 'multiple',
|
|
200
204
|
wordcount: 'multiple',
|
|
201
205
|
prefix: 'multiple',
|
|
@@ -239,6 +243,15 @@ export default class Input extends Multivalue {
|
|
|
239
243
|
}
|
|
240
244
|
});
|
|
241
245
|
}
|
|
246
|
+
this.on('focus', (comp) => {
|
|
247
|
+
announceScreenReaderMessage(comp, comp.dataValue, 0, true);
|
|
248
|
+
});
|
|
249
|
+
this.on('blur', (comp) => {
|
|
250
|
+
const el = comp.refs?.["announceMessage"]?.[0];
|
|
251
|
+
if (el) {
|
|
252
|
+
el.textContent = "";
|
|
253
|
+
}
|
|
254
|
+
});
|
|
242
255
|
return promise;
|
|
243
256
|
}
|
|
244
257
|
/**
|
|
@@ -317,7 +317,7 @@ export default class AddressComponent extends ContainerComponent {
|
|
|
317
317
|
lang: this.options.language,
|
|
318
318
|
tabindex: this.component.tabindex || 0,
|
|
319
319
|
};
|
|
320
|
-
if (this.component.placeholder) {
|
|
320
|
+
if (this.component.placeholder && !this.options?.readOnly) {
|
|
321
321
|
((attr.placeholder = this.t(this.component.placeholder)), { _userInput: true });
|
|
322
322
|
}
|
|
323
323
|
if (this.disabled) {
|
|
@@ -108,6 +108,16 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
108
108
|
for (let dIndex = defaultValue.length; dIndex < this.minLength; dIndex++) {
|
|
109
109
|
defaultValue.push({});
|
|
110
110
|
}
|
|
111
|
+
if (this.component.customDefaultValue) {
|
|
112
|
+
return defaultValue;
|
|
113
|
+
}
|
|
114
|
+
if (defaultValue.length === 1 && this.columns) {
|
|
115
|
+
eachComponent(this.components, (comp, path) => {
|
|
116
|
+
if (comp.component?.input && comp.defaultValue) {
|
|
117
|
+
_.set(defaultValue[0], path, comp.defaultValue);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
111
121
|
return defaultValue;
|
|
112
122
|
}
|
|
113
123
|
set disabled(disabled) {
|
|
@@ -433,7 +443,7 @@ export default class DataGridComponent extends NestedArrayComponent {
|
|
|
433
443
|
});
|
|
434
444
|
screenReaderSpeech('Row has been added');
|
|
435
445
|
this.checkConditions();
|
|
436
|
-
this.triggerChange?.({ modified: true });
|
|
446
|
+
this.triggerChange?.({ modified: true, noPristineChangeOnModified: true });
|
|
437
447
|
this.redraw().then(() => {
|
|
438
448
|
this.focusOnNewRowElement(this.rows[index]);
|
|
439
449
|
});
|
|
@@ -10,6 +10,7 @@ export default class FieldsetComponent extends NestedComponent {
|
|
|
10
10
|
};
|
|
11
11
|
static savedValueTypes(): never[];
|
|
12
12
|
constructor(...args: any[]);
|
|
13
|
+
attach(element: any): Promise<void>;
|
|
13
14
|
noField: boolean;
|
|
14
15
|
}
|
|
15
16
|
import NestedComponent from '../_classes/nested/NestedComponent';
|
|
@@ -34,6 +34,13 @@ export default class FieldsetComponent extends NestedComponent {
|
|
|
34
34
|
get templateName() {
|
|
35
35
|
return 'fieldset';
|
|
36
36
|
}
|
|
37
|
+
attach(element) {
|
|
38
|
+
return super.attach(element).then(() => {
|
|
39
|
+
if (this.component.legend && this.refs.header) {
|
|
40
|
+
this.refs.header.setAttribute('id', `l-${this.id}-legend`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
37
44
|
constructor(...args) {
|
|
38
45
|
super(...args);
|
|
39
46
|
this.noField = true;
|
|
@@ -276,9 +276,9 @@ export default class SelectComponent extends ListComponent {
|
|
|
276
276
|
// Inside DataTable component won't have dataValue set
|
|
277
277
|
const shouldUseSelectData = (this.component.multiple && _.isArray(this.dataValue)
|
|
278
278
|
? this.dataValue.find((val) => this.normalizeSingleValue(value) === val)
|
|
279
|
-
: this.dataValue === this.normalizeSingleValue(value)) || this.inDataTable;
|
|
279
|
+
: this.dataValue === this.normalizeSingleValue(value)) || (this.inDataTable && !this.element);
|
|
280
280
|
if (shouldUseSelectData) {
|
|
281
|
-
const selectData = this.selectData;
|
|
281
|
+
const selectData = (this.inDataTable && !this.element) ? this.component.selectData : this.selectData;
|
|
282
282
|
if (selectData) {
|
|
283
283
|
const templateValue = this.component.reference && value?._id ? value._id.toString() : value;
|
|
284
284
|
if (!this.templateData || !this.templateData[templateValue]) {
|
|
@@ -768,7 +768,7 @@ export default class SelectComponent extends ListComponent {
|
|
|
768
768
|
}
|
|
769
769
|
}
|
|
770
770
|
addPlaceholder() {
|
|
771
|
-
if (!this.component.placeholder) {
|
|
771
|
+
if (!this.component.placeholder || this.options?.readOnly) {
|
|
772
772
|
return;
|
|
773
773
|
}
|
|
774
774
|
this.addOption('', this.component.placeholder, { placeholder: true });
|
|
@@ -835,7 +835,8 @@ export default class SelectComponent extends ListComponent {
|
|
|
835
835
|
const useSearch = this.component.hasOwnProperty('searchEnabled')
|
|
836
836
|
? this.component.searchEnabled
|
|
837
837
|
: true;
|
|
838
|
-
const
|
|
838
|
+
const hasPlaceholder = !!this.component.placeholder && !this.options?.readOnly;
|
|
839
|
+
const placeholderValue = hasPlaceholder ? this.t(this.component.placeholder, { _userInput: true }) : null;
|
|
839
840
|
let customOptions = this.component.customOptions || {};
|
|
840
841
|
if (typeof customOptions == 'string') {
|
|
841
842
|
try {
|
|
@@ -865,7 +866,7 @@ export default class SelectComponent extends ListComponent {
|
|
|
865
866
|
},
|
|
866
867
|
addItemText: false,
|
|
867
868
|
allowHTML: true,
|
|
868
|
-
placeholder:
|
|
869
|
+
placeholder: hasPlaceholder,
|
|
869
870
|
placeholderValue: placeholderValue,
|
|
870
871
|
noResultsText: this.t('No results found'),
|
|
871
872
|
noChoicesText: this.t('No choices to choose from'),
|
|
@@ -1325,7 +1326,8 @@ export default class SelectComponent extends ListComponent {
|
|
|
1325
1326
|
return super.normalizeValue(this.normalizeSingleValue(value));
|
|
1326
1327
|
}
|
|
1327
1328
|
setMetadata(value, flags = {}) {
|
|
1328
|
-
if (_.isNil(value)
|
|
1329
|
+
if (_.isNil(value) ||
|
|
1330
|
+
(this.inDataTable && this.component.dataSrc === 'values')) {
|
|
1329
1331
|
return;
|
|
1330
1332
|
}
|
|
1331
1333
|
const valueIsObject = _.isObject(value);
|
|
@@ -1335,7 +1337,7 @@ export default class SelectComponent extends ListComponent {
|
|
|
1335
1337
|
}
|
|
1336
1338
|
// Check to see if we need to save off the template data into our metadata.
|
|
1337
1339
|
const templateValue = this.component.reference && value?._id ? value._id.toString() : value;
|
|
1338
|
-
const shouldSaveData = (!valueIsObject || this.component.reference) && !this.inDataTable;
|
|
1340
|
+
const shouldSaveData = (!valueIsObject || this.component.reference) && !(this.inDataTable && this.row === '');
|
|
1339
1341
|
if (!_.isNil(templateValue) &&
|
|
1340
1342
|
shouldSaveData &&
|
|
1341
1343
|
this.templateData &&
|
|
@@ -1620,7 +1622,8 @@ export default class SelectComponent extends ListComponent {
|
|
|
1620
1622
|
}
|
|
1621
1623
|
asString(value, options = {}) {
|
|
1622
1624
|
value = value ?? this.getValue();
|
|
1623
|
-
if (options.modalPreview ||
|
|
1625
|
+
if (options.modalPreview ||
|
|
1626
|
+
((this.inDataTable || this.inEditGrid) && !['values', 'custom'].includes(this.component.dataSrc))) {
|
|
1624
1627
|
if (this.inDataTable) {
|
|
1625
1628
|
value = this.undoValueTyping(value);
|
|
1626
1629
|
}
|
|
@@ -74,7 +74,7 @@ export default class TagsComponent extends Input {
|
|
|
74
74
|
if (!Choices) {
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
const hasPlaceholder = !!this.component.placeholder;
|
|
77
|
+
const hasPlaceholder = !!this.component.placeholder && !this.options?.readOnly;
|
|
78
78
|
this.choices = new Choices(element, {
|
|
79
79
|
delimiter: this.delimiter,
|
|
80
80
|
editItems: true,
|
package/lib/mjs/package.json
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export default class IsEqualTo extends ConditionOperator {
|
|
2
|
-
execute({ value, comparedValue
|
|
2
|
+
execute({ value, comparedValue }: {
|
|
3
3
|
value: any;
|
|
4
4
|
comparedValue: any;
|
|
5
|
-
instance: any;
|
|
6
|
-
path: any;
|
|
7
5
|
}): any;
|
|
8
6
|
}
|
|
9
7
|
import ConditionOperator from './ConditionOperator';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import ConditionOperator from './ConditionOperator';
|
|
2
2
|
import _ from 'lodash';
|
|
3
|
-
import { compareSelectResourceWithObjectTypeValues, isSelectResourceWithObjectValue } from '../';
|
|
4
3
|
export default class IsEqualTo extends ConditionOperator {
|
|
5
4
|
static get operatorKey() {
|
|
6
5
|
return 'isEqual';
|
|
@@ -8,7 +7,7 @@ export default class IsEqualTo extends ConditionOperator {
|
|
|
8
7
|
static get displayedName() {
|
|
9
8
|
return 'Is Equal To';
|
|
10
9
|
}
|
|
11
|
-
execute({ value, comparedValue
|
|
10
|
+
execute({ value, comparedValue }) {
|
|
12
11
|
if ((value || value === false) &&
|
|
13
12
|
comparedValue &&
|
|
14
13
|
typeof value !== typeof comparedValue &&
|
|
@@ -20,18 +19,14 @@ export default class IsEqualTo extends ConditionOperator {
|
|
|
20
19
|
// ignore
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
|
-
if (instance?.root?.getComponent) {
|
|
24
|
-
const conditionTriggerComponent = instance.root.getComponent(path);
|
|
25
|
-
if (conditionTriggerComponent &&
|
|
26
|
-
isSelectResourceWithObjectValue(conditionTriggerComponent.component) &&
|
|
27
|
-
conditionTriggerComponent.component?.template) {
|
|
28
|
-
return compareSelectResourceWithObjectTypeValues(value, comparedValue, conditionTriggerComponent.component);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
22
|
//special check for select boxes
|
|
32
23
|
if (_.isObject(value) && comparedValue && _.isBoolean(value[comparedValue])) {
|
|
33
24
|
return value[comparedValue];
|
|
34
25
|
}
|
|
35
|
-
|
|
26
|
+
const valuesAreObjects = typeof comparedValue === 'object' &&
|
|
27
|
+
comparedValue !== null &&
|
|
28
|
+
typeof value === 'object' &&
|
|
29
|
+
value !== null;
|
|
30
|
+
return valuesAreObjects ? _.isMatch(value, comparedValue) : _.isEqual(comparedValue, value);
|
|
36
31
|
}
|
|
37
32
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default ConditionOperators;
|
|
2
2
|
declare const ConditionOperators: {
|
|
3
|
-
[x: string]: typeof IsEqualTo | typeof DateGreaterThan;
|
|
3
|
+
[x: string]: typeof IsEqualTo | typeof IsEmptyValue | typeof DateGreaterThan;
|
|
4
4
|
};
|
|
5
5
|
import IsEqualTo from './IsEqualTo';
|
|
6
|
+
import IsEmptyValue from './IsEmptyValue';
|
|
6
7
|
import DateGreaterThan from './DateGreaterThan';
|
package/lib/mjs/utils/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ declare const FormioUtils: {
|
|
|
5
5
|
Evaluator: DefaultEvaluator;
|
|
6
6
|
interpolate: typeof interpolate;
|
|
7
7
|
ConditionOperators: {
|
|
8
|
-
[x: string]: typeof import("./conditionOperators/IsEqualTo").default | typeof import("./conditionOperators/DateGreaterThan").default;
|
|
8
|
+
[x: string]: typeof import("./conditionOperators/IsEqualTo").default | typeof import("./conditionOperators/IsEmptyValue").default | typeof import("./conditionOperators/DateGreaterThan").default;
|
|
9
9
|
};
|
|
10
10
|
_: any;
|
|
11
11
|
moment: typeof moment;
|
|
@@ -150,6 +150,7 @@ declare const FormioUtils: {
|
|
|
150
150
|
getFocusableElements(element: HTMLElement): NodeList<HTMLElement>;
|
|
151
151
|
getComponentSavedTypes(fullSchema: import("@formio/core").Component): string[] | null;
|
|
152
152
|
hasEncodedTimezone(value: string): boolean;
|
|
153
|
+
announceScreenReaderMessage(component: any, value: any, index?: any, forFocus?: any): undefined;
|
|
153
154
|
screenReaderSpeech(text: string): void;
|
|
154
155
|
firstNonNil: any;
|
|
155
156
|
componentValueTypes: {
|
package/lib/mjs/utils/utils.d.ts
CHANGED
|
@@ -499,6 +499,15 @@ export function getComponentSavedTypes(fullSchema: import('@formio/core').Compon
|
|
|
499
499
|
* @returns {boolean} if value has encoded timezone
|
|
500
500
|
*/
|
|
501
501
|
export function hasEncodedTimezone(value: string): boolean;
|
|
502
|
+
/**
|
|
503
|
+
* The function for announcing messages via a screen reader
|
|
504
|
+
* @param {component} component - The component instance
|
|
505
|
+
* @param {value} value - The current component value
|
|
506
|
+
* @param {index} index - The component index
|
|
507
|
+
* @param {forFocus} forFocus - Whether the component is focused or not
|
|
508
|
+
* @returns {undefined}
|
|
509
|
+
*/
|
|
510
|
+
export function announceScreenReaderMessage(component: any, value: any, index?: any, forFocus?: any): undefined;
|
|
502
511
|
/**
|
|
503
512
|
* Outputs text to screen reader
|
|
504
513
|
* @param {string} text The text to output to screen readers
|
package/lib/mjs/utils/utils.js
CHANGED
|
@@ -135,7 +135,7 @@ function getConditionalPathsRecursive(conditionPaths, data) {
|
|
|
135
135
|
if (currentData.some((element) => typeof element !== 'object')) {
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
|
-
const hasInnerDataArray = currentData.find((x) => Array.isArray(x[conditionPaths[currentLocalIndex]]));
|
|
138
|
+
const hasInnerDataArray = currentData.find((x) => x && conditionPaths && Array.isArray(x[conditionPaths[currentLocalIndex]]));
|
|
139
139
|
if (hasInnerDataArray) {
|
|
140
140
|
currentData.forEach((_, indexOutside) => {
|
|
141
141
|
const innerCompDataPath = `${currentPath}[${indexOutside}].${conditionPaths[currentLocalIndex]}`;
|
|
@@ -1619,6 +1619,135 @@ export function hasEncodedTimezone(value) {
|
|
|
1619
1619
|
value.substring(value.length - 1) === 'Z' ||
|
|
1620
1620
|
value.match(/[+|-][0-9]{2}:[0-9]{2}$/));
|
|
1621
1621
|
}
|
|
1622
|
+
// Types for min max validation if value = string
|
|
1623
|
+
const TYPES = new Map([["char", "Length"], ["word", "Words"]]);
|
|
1624
|
+
// The number from which the remaining character(words) count message starts being read
|
|
1625
|
+
const REMAIN_COUNT = new Map([["char", 10], ["word", 5]]);
|
|
1626
|
+
function getWordOrCharacterLabel(isWordType, count) {
|
|
1627
|
+
const base = isWordType ? "word" : "character";
|
|
1628
|
+
return Math.abs(count) === 1 ? base : `${base}s`;
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* The function calculates the message values depending on the type and the current component settings.
|
|
1632
|
+
* @param {component} component - The component instance
|
|
1633
|
+
* @param {type} type - The type of validation max and min
|
|
1634
|
+
* @param {value} value - The current component value
|
|
1635
|
+
* @param {forFocus} forFocus - Whether the component is focused or not
|
|
1636
|
+
* @returns {string} - The messsage string
|
|
1637
|
+
*/
|
|
1638
|
+
function getScreenReaderMessage(component, type, value) {
|
|
1639
|
+
const isWordType = type === "word";
|
|
1640
|
+
const maxKey = typeof value === "string" && (type === "char" || isWordType)
|
|
1641
|
+
? `validate.max${TYPES.get(type)}`
|
|
1642
|
+
: "validate.max";
|
|
1643
|
+
const minKey = typeof value === "string" && (type === "char" || isWordType)
|
|
1644
|
+
? `validate.min${TYPES.get(type)}`
|
|
1645
|
+
: "validate.min";
|
|
1646
|
+
const max = _.parseInt(_.get(component.component, maxKey), 10);
|
|
1647
|
+
const min = _.parseInt(_.get(component.component, minKey), 10);
|
|
1648
|
+
let message = "";
|
|
1649
|
+
if (typeof value === "string") {
|
|
1650
|
+
const currentLength = isWordType ? component.getWordCount(value) : value.length;
|
|
1651
|
+
if (!isNaN(max)) {
|
|
1652
|
+
const remains = max - currentLength;
|
|
1653
|
+
if (value) {
|
|
1654
|
+
const threshold = REMAIN_COUNT.get(type) || max;
|
|
1655
|
+
if (remains > 0 && remains < threshold) {
|
|
1656
|
+
message += `${remains} ${getWordOrCharacterLabel(isWordType, remains)} remaining. `;
|
|
1657
|
+
}
|
|
1658
|
+
else if (remains < 0) {
|
|
1659
|
+
const removeCount = Math.abs(remains);
|
|
1660
|
+
message += `${removeCount} ${getWordOrCharacterLabel(isWordType, removeCount)} should be removed. `;
|
|
1661
|
+
}
|
|
1662
|
+
else if (remains === 0) {
|
|
1663
|
+
message += `No ${getWordOrCharacterLabel(isWordType, 0)} remaining.`;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
message += `Maximum ${max} ${getWordOrCharacterLabel(isWordType, max)}. `;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (!isNaN(min)) {
|
|
1671
|
+
if (value) {
|
|
1672
|
+
const remains = min - currentLength;
|
|
1673
|
+
if (remains > 0) {
|
|
1674
|
+
message += `${remains} ${getWordOrCharacterLabel(isWordType, remains)} should be added.`;
|
|
1675
|
+
}
|
|
1676
|
+
if (remains === 0) {
|
|
1677
|
+
message += ``;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
message += `Minimum ${min} ${getWordOrCharacterLabel(isWordType, min)}. `;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
else if (typeof value === "number" || value === null) {
|
|
1686
|
+
if (value != null && value !== "") {
|
|
1687
|
+
if (!isNaN(max) && value > max) {
|
|
1688
|
+
message += `Number cannot be greater than ${max}. `;
|
|
1689
|
+
}
|
|
1690
|
+
if (!isNaN(min) && value < min) {
|
|
1691
|
+
message += `Number cannot be less than ${min}.`;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
if (!isNaN(min))
|
|
1696
|
+
message += `Minimum value ${min}. `;
|
|
1697
|
+
if (!isNaN(max))
|
|
1698
|
+
message += `Maximum value ${max}. `;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return message.trim();
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* The function for announcing messages via a screen reader
|
|
1705
|
+
* @param {component} component - The component instance
|
|
1706
|
+
* @param {value} value - The current component value
|
|
1707
|
+
* @param {index} index - The component index
|
|
1708
|
+
* @param {forFocus} forFocus - Whether the component is focused or not
|
|
1709
|
+
* @returns {undefined}
|
|
1710
|
+
*/
|
|
1711
|
+
export function announceScreenReaderMessage(component, value, index = 0, forFocus = false) {
|
|
1712
|
+
if (typeof value !== "string" && typeof value !== "number" && value !== null) {
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
// The ref for announcing messages
|
|
1716
|
+
const messageSpan = "announceMessage";
|
|
1717
|
+
if (!component.refs[messageSpan])
|
|
1718
|
+
return;
|
|
1719
|
+
const el = component.refs[messageSpan][index];
|
|
1720
|
+
if (!el)
|
|
1721
|
+
return;
|
|
1722
|
+
// Define types for validation
|
|
1723
|
+
const typesToCheck = [];
|
|
1724
|
+
if (typeof value === "string")
|
|
1725
|
+
typesToCheck.push("char", "word");
|
|
1726
|
+
if (typeof value === "number" || value === null)
|
|
1727
|
+
typesToCheck.push("number");
|
|
1728
|
+
// Construct the combined message
|
|
1729
|
+
const combinedMessage = typesToCheck
|
|
1730
|
+
.map(type => getScreenReaderMessage(component, type, value))
|
|
1731
|
+
.filter(msg => msg)
|
|
1732
|
+
.join(" ")
|
|
1733
|
+
.trim();
|
|
1734
|
+
if (forFocus) {
|
|
1735
|
+
setTimeout(() => {
|
|
1736
|
+
el.textContent = "";
|
|
1737
|
+
requestAnimationFrame(() => {
|
|
1738
|
+
el.textContent = combinedMessage;
|
|
1739
|
+
});
|
|
1740
|
+
}, 150);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
clearTimeout(el._announceTimer);
|
|
1744
|
+
el._announceTimer = setTimeout(() => {
|
|
1745
|
+
el.textContent = "";
|
|
1746
|
+
requestAnimationFrame(() => {
|
|
1747
|
+
el.textContent = combinedMessage;
|
|
1748
|
+
});
|
|
1749
|
+
}, 500);
|
|
1750
|
+
}
|
|
1622
1751
|
/**
|
|
1623
1752
|
* Outputs text to screen reader
|
|
1624
1753
|
* @param {string} text The text to output to screen readers
|
|
@@ -442,7 +442,7 @@ export default class CalendarWidget extends InputWidget {
|
|
|
442
442
|
// If other fields are used to calculate disabled dates, we need to redraw calendar to refresh disabled dates
|
|
443
443
|
if (this.settings.disableFunction && this.componentInstance && this.componentInstance.root) {
|
|
444
444
|
this.changeHandler = (e) => {
|
|
445
|
-
if (e.changed && this.calendar) {
|
|
445
|
+
if (e.changed && this.calendar?.config) {
|
|
446
446
|
this.calendar.redraw();
|
|
447
447
|
}
|
|
448
448
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formio/js",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"description": "JavaScript powered Forms with JSON Form Builder",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"tippy.js": "^6.3.7",
|
|
89
89
|
"uuid": "^9.0.0",
|
|
90
90
|
"vanilla-picker": "^2.12.3",
|
|
91
|
-
"@formio/
|
|
92
|
-
"@formio/
|
|
91
|
+
"@formio/bootstrap": "^3.2.1",
|
|
92
|
+
"@formio/core": "^2.6.1"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/node": "^22.15.19",
|