@formio/js 5.3.0 → 5.4.0-api98.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.
Files changed (112) hide show
  1. package/dist/formio.builder.css +16 -5
  2. package/dist/formio.builder.min.css +1 -1
  3. package/dist/formio.embed.css +1 -1
  4. package/dist/formio.embed.js +1 -1
  5. package/dist/formio.embed.min.css +1 -1
  6. package/dist/formio.embed.min.js +1 -1
  7. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  8. package/dist/formio.form.css +16 -5
  9. package/dist/formio.form.js +3234 -3226
  10. package/dist/formio.form.min.css +1 -1
  11. package/dist/formio.form.min.js +1 -1
  12. package/dist/formio.form.min.js.LICENSE.txt +1 -3
  13. package/dist/formio.full.css +16 -5
  14. package/dist/formio.full.js +3961 -3933
  15. package/dist/formio.full.min.css +1 -1
  16. package/dist/formio.full.min.js +1 -1
  17. package/dist/formio.full.min.js.LICENSE.txt +1 -3
  18. package/dist/formio.js +1485 -1487
  19. package/dist/formio.min.js +1 -1
  20. package/dist/formio.min.js.LICENSE.txt +1 -1
  21. package/dist/formio.utils.js +1402 -1415
  22. package/dist/formio.utils.min.js +1 -1
  23. package/dist/formio.utils.min.js.LICENSE.txt +1 -3
  24. package/lib/cjs/Embed.js +35 -1
  25. package/lib/cjs/Form.d.ts +3 -1
  26. package/lib/cjs/Form.js +2 -2
  27. package/lib/cjs/FormBuilder.d.ts +2 -2
  28. package/lib/cjs/FormBuilder.js +1 -1
  29. package/lib/cjs/Formio.js +1 -1
  30. package/lib/cjs/PDF.js +11 -1
  31. package/lib/cjs/Webform.d.ts +2 -1
  32. package/lib/cjs/Webform.js +77 -2
  33. package/lib/cjs/WebformBuilder.js +15 -0
  34. package/lib/cjs/Wizard.d.ts +1 -0
  35. package/lib/cjs/Wizard.js +11 -0
  36. package/lib/cjs/components/_classes/component/Component.d.ts +14 -3
  37. package/lib/cjs/components/_classes/component/Component.js +91 -24
  38. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
  39. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +1 -0
  40. package/lib/cjs/components/_classes/input/Input.js +17 -2
  41. package/lib/cjs/components/_classes/nested/NestedComponent.js +5 -0
  42. package/lib/cjs/components/address/Address.js +18 -1
  43. package/lib/cjs/components/datagrid/DataGrid.js +14 -3
  44. package/lib/cjs/components/datamap/DataMap.d.ts +4 -0
  45. package/lib/cjs/components/datamap/DataMap.js +6 -5
  46. package/lib/cjs/components/fieldset/Fieldset.d.ts +1 -0
  47. package/lib/cjs/components/fieldset/Fieldset.js +8 -0
  48. package/lib/cjs/components/file/File.d.ts +3 -1
  49. package/lib/cjs/components/file/File.js +62 -17
  50. package/lib/cjs/components/form/Form.js +2 -1
  51. package/lib/cjs/components/select/Select.js +14 -9
  52. package/lib/cjs/components/table/editForm/Table.edit.display.d.ts +27 -0
  53. package/lib/cjs/components/table/editForm/Table.edit.display.js +10 -0
  54. package/lib/cjs/components/tags/Tags.js +2 -1
  55. package/lib/cjs/package.json +1 -1
  56. package/lib/cjs/templates/index.d.ts +3 -0
  57. package/lib/cjs/translations/en.d.ts +2 -0
  58. package/lib/cjs/translations/en.js +2 -0
  59. package/lib/cjs/utils/conditionOperators/IsEqualTo.d.ts +1 -3
  60. package/lib/cjs/utils/conditionOperators/IsEqualTo.js +6 -12
  61. package/lib/cjs/utils/conditionOperators/index.d.ts +2 -1
  62. package/lib/cjs/utils/i18n.d.ts +1 -0
  63. package/lib/cjs/utils/i18n.js +2 -0
  64. package/lib/cjs/utils/index.d.ts +2 -1
  65. package/lib/cjs/utils/utils.d.ts +9 -0
  66. package/lib/cjs/utils/utils.js +132 -2
  67. package/lib/cjs/widgets/CalendarWidget.js +2 -1
  68. package/lib/mjs/Embed.js +35 -1
  69. package/lib/mjs/Form.d.ts +3 -1
  70. package/lib/mjs/Form.js +2 -2
  71. package/lib/mjs/FormBuilder.d.ts +2 -2
  72. package/lib/mjs/FormBuilder.js +1 -1
  73. package/lib/mjs/Formio.js +1 -1
  74. package/lib/mjs/PDF.js +11 -1
  75. package/lib/mjs/Webform.d.ts +2 -1
  76. package/lib/mjs/Webform.js +76 -2
  77. package/lib/mjs/WebformBuilder.js +15 -0
  78. package/lib/mjs/Wizard.d.ts +1 -0
  79. package/lib/mjs/Wizard.js +12 -1
  80. package/lib/mjs/components/_classes/component/Component.d.ts +14 -3
  81. package/lib/mjs/components/_classes/component/Component.js +87 -24
  82. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.d.ts +7 -0
  83. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +1 -0
  84. package/lib/mjs/components/_classes/input/Input.js +16 -3
  85. package/lib/mjs/components/_classes/nested/NestedComponent.js +5 -0
  86. package/lib/mjs/components/address/Address.js +16 -1
  87. package/lib/mjs/components/datagrid/DataGrid.js +12 -2
  88. package/lib/mjs/components/datamap/DataMap.d.ts +4 -0
  89. package/lib/mjs/components/datamap/DataMap.js +6 -5
  90. package/lib/mjs/components/fieldset/Fieldset.d.ts +1 -0
  91. package/lib/mjs/components/fieldset/Fieldset.js +8 -0
  92. package/lib/mjs/components/file/File.d.ts +3 -1
  93. package/lib/mjs/components/file/File.js +60 -15
  94. package/lib/mjs/components/form/Form.js +2 -1
  95. package/lib/mjs/components/select/Select.js +12 -9
  96. package/lib/mjs/components/table/editForm/Table.edit.display.d.ts +27 -0
  97. package/lib/mjs/components/table/editForm/Table.edit.display.js +10 -0
  98. package/lib/mjs/components/tags/Tags.js +1 -1
  99. package/lib/mjs/package.json +1 -1
  100. package/lib/mjs/templates/index.d.ts +3 -0
  101. package/lib/mjs/translations/en.d.ts +2 -0
  102. package/lib/mjs/translations/en.js +2 -0
  103. package/lib/mjs/utils/conditionOperators/IsEqualTo.d.ts +1 -3
  104. package/lib/mjs/utils/conditionOperators/IsEqualTo.js +6 -11
  105. package/lib/mjs/utils/conditionOperators/index.d.ts +2 -1
  106. package/lib/mjs/utils/i18n.d.ts +1 -0
  107. package/lib/mjs/utils/i18n.js +2 -0
  108. package/lib/mjs/utils/index.d.ts +2 -1
  109. package/lib/mjs/utils/utils.d.ts +9 -0
  110. package/lib/mjs/utils/utils.js +130 -1
  111. package/lib/mjs/widgets/CalendarWidget.js +1 -1
  112. package/package.json +5 -4
@@ -96,6 +96,8 @@ export default class FileComponent extends Field {
96
96
  };
97
97
  this.isSyncing = false;
98
98
  this.abortUploads = [];
99
+ this.pendingfiles = [];
100
+ this.resolvedFiles = [];
99
101
  }
100
102
  get dataReady() {
101
103
  return this.filesReady || Promise.resolve();
@@ -695,22 +697,42 @@ export default class FileComponent extends Field {
695
697
  };
696
698
  }
697
699
  // Check file minimum size
698
- if (this.component.fileMinSize && !this.validateMinSize(file, this.component.fileMinSize)) {
699
- return {
700
- status: 'error',
701
- message: this.t('File is too small; it must be at least {{ size }}', {
702
- size: this.component.fileMinSize,
703
- }),
704
- };
700
+ if (this.component.fileMinSize) {
701
+ const interpolatedMinSize = this.interpolate(this.component.fileMinSize, this.evalContext());
702
+ // This case is when the user entered fileMinSize expression, but did not enter or made a typo when
703
+ // setting the fileMinSize variable in the config of the project
704
+ if (!interpolatedMinSize) {
705
+ return {
706
+ status: 'error',
707
+ message: 'Please, check the entered parameters',
708
+ };
709
+ }
710
+ if (!this.validateMinSize(file, interpolatedMinSize)) {
711
+ return {
712
+ status: 'error',
713
+ message: this.t('fileTooSmall', {
714
+ size: interpolatedMinSize,
715
+ }),
716
+ };
717
+ }
705
718
  }
706
719
  // Check file maximum size
707
- if (this.component.fileMaxSize && !this.validateMaxSize(file, this.component.fileMaxSize)) {
708
- return {
709
- status: 'error',
710
- message: this.t('File is too big; it must be at most {{ size }}', {
711
- size: this.component.fileMaxSize,
712
- }),
713
- };
720
+ if (this.component.fileMaxSize) {
721
+ const interpolatedMaxSize = this.interpolate(this.component.fileMaxSize, this.evalContext());
722
+ if (!interpolatedMaxSize) {
723
+ return {
724
+ status: 'error',
725
+ message: 'Please, check the entered parameters',
726
+ };
727
+ }
728
+ if (!this.validateMaxSize(file, interpolatedMaxSize)) {
729
+ return {
730
+ status: 'error',
731
+ message: this.t('fileTooBig', {
732
+ size: interpolatedMaxSize,
733
+ }),
734
+ };
735
+ }
714
736
  }
715
737
  return {};
716
738
  }
@@ -965,7 +987,23 @@ export default class FileComponent extends Field {
965
987
  fileInfo,
966
988
  };
967
989
  }
968
- fileInfo = await this.uploadFile(fileToSync);
990
+ if (fileToSync.status === "success") {
991
+ const uploadedFile = this.resolvedFiles.find(x => x.fileToSync.originalName === fileToSync.originalName);
992
+ return {
993
+ fileToSync: uploadedFile.fileToSync,
994
+ fileInfo: uploadedFile.fileInfo,
995
+ };
996
+ }
997
+ const pendingFile = this.pendingfiles.find(x => x.name === fileToSync.name);
998
+ if (pendingFile) {
999
+ fileInfo = await pendingFile.fileInfoProm;
1000
+ }
1001
+ else {
1002
+ const promInfo = this.uploadFile(fileToSync);
1003
+ this.pendingfiles.push({ name: fileToSync.name, fileInfoProm: promInfo });
1004
+ fileInfo = await promInfo;
1005
+ }
1006
+ this.pendingfiles = this.pendingfiles.filter(x => x.name !== fileToSync.name);
969
1007
  fileToSync.status = 'success';
970
1008
  fileToSync.message = this.t('Succefully uploaded');
971
1009
  fileInfo.originalName = fileToSync.originalName;
@@ -990,6 +1028,10 @@ export default class FileComponent extends Field {
990
1028
  finally {
991
1029
  delete fileToSync.progress;
992
1030
  this.redraw();
1031
+ const fileExists = this.resolvedFiles.find(x => x.fileInfo.originalName === fileToSync.originalName);
1032
+ if (!fileExists) {
1033
+ this.resolvedFiles.push({ fileToSync, fileInfo });
1034
+ }
993
1035
  }
994
1036
  return {
995
1037
  fileToSync,
@@ -1006,6 +1048,9 @@ export default class FileComponent extends Field {
1006
1048
  this.delete(),
1007
1049
  this.upload(),
1008
1050
  ]);
1051
+ if (filesToUpload.length !== this.filesToSync?.filesToUpload?.length) {
1052
+ return;
1053
+ }
1009
1054
  this.filesToSync.filesToDelete = filesToDelete
1010
1055
  .filter((file) => file.fileToSync?.status === 'error')
1011
1056
  .map((file) => file.fileToSync);
@@ -554,7 +554,8 @@ export default class FormComponent extends Component {
554
554
  get shouldSubmit() {
555
555
  return (this.subFormReady &&
556
556
  (!this.component.hasOwnProperty('reference') || this.component.reference) &&
557
- !this.shouldConditionallyClear());
557
+ !this.shouldConditionallyClear() &&
558
+ !(this.component.hidden && this.component.clearOnHide));
558
559
  }
559
560
  /**
560
561
  * Returns the data for the subform.
@@ -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]) {
@@ -522,7 +522,7 @@ export default class SelectComponent extends ListComponent {
522
522
  noUpdateEvent: true,
523
523
  });
524
524
  }
525
- else if (this.shouldAddDefaultValue && !this.options.readOnly) {
525
+ else if (this.shouldAddDefaultValue && !this.options.readOnly && this.root && !this.root.submissionSet) {
526
526
  // If a default value is provided then select it.
527
527
  const defaultValue = this.defaultValue;
528
528
  if (!this.isEmpty(defaultValue)) {
@@ -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 placeholderValue = this.t(this.component.placeholder, { _userInput: true });
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: !!this.component.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 || this.inDataTable) {
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
  }
@@ -10,6 +10,8 @@ declare const _default: ({
10
10
  validate?: undefined;
11
11
  autofocus?: undefined;
12
12
  overrideEditForm?: undefined;
13
+ multiple?: undefined;
14
+ customConditional?: undefined;
13
15
  dataSrc?: undefined;
14
16
  data?: undefined;
15
17
  defaultValue?: undefined;
@@ -27,6 +29,8 @@ declare const _default: ({
27
29
  autofocus: boolean;
28
30
  overrideEditForm: boolean;
29
31
  ignore?: undefined;
32
+ multiple?: undefined;
33
+ customConditional?: undefined;
30
34
  dataSrc?: undefined;
31
35
  data?: undefined;
32
36
  defaultValue?: undefined;
@@ -42,6 +46,25 @@ declare const _default: ({
42
46
  validate?: undefined;
43
47
  autofocus?: undefined;
44
48
  overrideEditForm?: undefined;
49
+ multiple?: undefined;
50
+ customConditional?: undefined;
51
+ dataSrc?: undefined;
52
+ data?: undefined;
53
+ defaultValue?: undefined;
54
+ } | {
55
+ label: string;
56
+ multiple: boolean;
57
+ key: string;
58
+ type: string;
59
+ input: boolean;
60
+ tooltip: string;
61
+ customConditional: string;
62
+ weight: number;
63
+ ignore?: undefined;
64
+ placeholder?: undefined;
65
+ validate?: undefined;
66
+ autofocus?: undefined;
67
+ overrideEditForm?: undefined;
45
68
  dataSrc?: undefined;
46
69
  data?: undefined;
47
70
  defaultValue?: undefined;
@@ -57,6 +80,8 @@ declare const _default: ({
57
80
  validate?: undefined;
58
81
  autofocus?: undefined;
59
82
  overrideEditForm?: undefined;
83
+ multiple?: undefined;
84
+ customConditional?: undefined;
60
85
  dataSrc?: undefined;
61
86
  data?: undefined;
62
87
  defaultValue?: undefined;
@@ -80,5 +105,7 @@ declare const _default: ({
80
105
  validate?: undefined;
81
106
  autofocus?: undefined;
82
107
  overrideEditForm?: undefined;
108
+ multiple?: undefined;
109
+ customConditional?: undefined;
83
110
  })[];
84
111
  export default _default;
@@ -67,6 +67,16 @@ export default [
67
67
  placeholder: 'Number of Columns',
68
68
  tooltip: 'Enter the number or columns that should be displayed by this table.',
69
69
  },
70
+ {
71
+ label: "Column Headers",
72
+ multiple: true,
73
+ key: "header",
74
+ type: "textfield",
75
+ input: true,
76
+ tooltip: 'Enter header names for the columns',
77
+ customConditional: "if(instance && instance.dataValue && Array.isArray(instance.dataValue)) {\r\n const isAllowToAddHeader = instance.dataValue.length < data.numCols;\r\n const addBtn = _.get(instance, 'refs.addButton[0]', null);\r\n if(addBtn) {\r\n isAllowToAddHeader? addBtn.removeAttribute('disabled'): addBtn.setAttribute('disabled', 'disabled');\r\n }\r\n} ",
78
+ weight: 3,
79
+ },
70
80
  {
71
81
  type: 'checkbox',
72
82
  label: 'Clone Row Components',
@@ -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,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "type": "module",
3
- "version": "5.3.0"
3
+ "version": "5.4.0-api98.1"
4
4
 
5
5
  }
@@ -20,6 +20,9 @@ declare namespace _default {
20
20
  wizardHeaderVertical: {
21
21
  form: string;
22
22
  };
23
+ wizardHeaderStepIndicator: {
24
+ form: string;
25
+ };
23
26
  wizardNav: {
24
27
  form: string;
25
28
  };
@@ -76,5 +76,7 @@ declare namespace _default {
76
76
  let requiredDayEmpty: string;
77
77
  let requiredMonthField: string;
78
78
  let requiredYearField: string;
79
+ let fileTooSmall: string;
80
+ let fileTooBig: string;
79
81
  }
80
82
  export default _default;
@@ -76,4 +76,6 @@ export default {
76
76
  requiredDayEmpty: '{{ field }} is required',
77
77
  requiredMonthField: '{{ field }} is required',
78
78
  requiredYearField: '{{ field }} is required',
79
+ fileTooSmall: 'File is too small; it must be at least {{ size }}',
80
+ fileTooBig: 'File is too big; it must be at most {{ size }}'
79
81
  };
@@ -1,9 +1,7 @@
1
1
  export default class IsEqualTo extends ConditionOperator {
2
- execute({ value, comparedValue, instance, path }: {
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, instance, path }) {
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
- return _.isEqual(value, comparedValue);
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';
@@ -7,6 +7,7 @@ export class I18n {
7
7
  constructor(languages?: {});
8
8
  languages: {};
9
9
  language: string;
10
+ originalLanguage: string;
10
11
  currentLanguage: any;
11
12
  setLanguages(languages: any): void;
12
13
  dir(lang?: string): "rtl" | "ltr";
@@ -12,6 +12,7 @@ for (const lang in i18n.resources) {
12
12
  export class I18n {
13
13
  languages = i18Defaults;
14
14
  language = 'en';
15
+ originalLanguage = 'en';
15
16
  currentLanguage = i18Defaults.en;
16
17
  constructor(languages = {}) {
17
18
  this.setLanguages(languages);
@@ -66,6 +67,7 @@ export class I18n {
66
67
  return new I18n();
67
68
  }
68
69
  changeLanguage(language, ready = null) {
70
+ this.originalLanguage = language;
69
71
  if (!this.languages[language]) {
70
72
  language = 'en';
71
73
  }
@@ -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: {
@@ -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
@@ -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.0",
3
+ "version": "5.4.0-api98.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/core": "^2.6.0",
92
- "@formio/bootstrap": "^3.2.0"
91
+ "@formio/bootstrap": "^4.0.0-api98.1",
92
+ "@formio/core": "^2.7.0-api98.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/node": "^22.15.19",
@@ -100,6 +100,7 @@
100
100
  "browser-env": "^3.3.0",
101
101
  "chai": "^4.3.7",
102
102
  "chance": "^1.1.9",
103
+ "cross-env": "^10.1.0",
103
104
  "ejs-loader": "^0.5.0",
104
105
  "escape-string-regexp": "^5.0.0",
105
106
  "eslint": "^9.38.0",
@@ -170,7 +171,7 @@
170
171
  "lint": "eslint . --fix",
171
172
  "serve": "jekyll serve --config _config.yml,_config.dev.yml",
172
173
  "test": "mocha --config .mocharc.json test/unit/*.unit.js",
173
- "test:updateRenders": "npm run lib && cross-env TZ=UTC node --require jsdom-global/register test/updateRenders.js",
174
+ "test:updateRenders": "npm run lib && cross-env TZ=UTC node --require jsdom-global/register test/updateRenders.cjs",
174
175
  "show-coverage": "open coverage/lcov-report/index.html"
175
176
  }
176
177
  }