@formio/js 5.0.0-rc.37 → 5.0.0-rc.39

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/dist/fonts/bootstrap-icons.woff +0 -0
  2. package/dist/fonts/bootstrap-icons.woff2 +0 -0
  3. package/dist/formio.embed.js +1 -1
  4. package/dist/formio.embed.min.js +1 -1
  5. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  6. package/dist/formio.form.js +853 -132
  7. package/dist/formio.form.min.js +1 -1
  8. package/dist/formio.form.min.js.LICENSE.txt +5 -3
  9. package/dist/formio.full.css +4 -4
  10. package/dist/formio.full.js +827 -136
  11. package/dist/formio.full.min.css +3 -3
  12. package/dist/formio.full.min.js +1 -1
  13. package/dist/formio.full.min.js.LICENSE.txt +5 -3
  14. package/dist/formio.js +4 -4
  15. package/dist/formio.min.js +1 -1
  16. package/dist/formio.min.js.LICENSE.txt +1 -1
  17. package/dist/formio.utils.js +25 -14
  18. package/dist/formio.utils.min.js +1 -1
  19. package/dist/formio.utils.min.js.LICENSE.txt +5 -3
  20. package/lib/cjs/Element.js +2 -2
  21. package/lib/cjs/Embed.js +9 -6
  22. package/lib/cjs/components/_classes/component/Component.js +21 -7
  23. package/lib/cjs/components/_classes/component/fixtures/comp5.js +2 -2
  24. package/lib/cjs/components/_classes/list/ListComponent.js +5 -12
  25. package/lib/cjs/components/_classes/nested/NestedComponent.js +17 -9
  26. package/lib/cjs/components/columns/editForm/Columns.edit.display.js +1 -1
  27. package/lib/cjs/components/editgrid/EditGrid.js +11 -4
  28. package/lib/cjs/components/form/Form.js +3 -1
  29. package/lib/cjs/components/html/HTML.js +2 -2
  30. package/lib/cjs/components/html/fixtures/comp3.js +31 -0
  31. package/lib/cjs/components/html/fixtures/index.js +3 -1
  32. package/lib/cjs/components/radio/Radio.js +2 -1
  33. package/lib/cjs/components/radio/fixtures/comp10.js +23 -0
  34. package/lib/cjs/components/radio/fixtures/index.js +3 -1
  35. package/lib/cjs/components/recaptcha/ReCaptcha.js +3 -0
  36. package/lib/cjs/components/select/Select.js +84 -9
  37. package/lib/cjs/components/survey/Survey.js +10 -0
  38. package/lib/cjs/utils/conditionOperators/IsEqualTo.js +19 -1
  39. package/lib/cjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
  40. package/lib/cjs/utils/utils.js +40 -2
  41. package/lib/mjs/Element.js +2 -2
  42. package/lib/mjs/Embed.js +9 -6
  43. package/lib/mjs/components/_classes/component/Component.js +24 -8
  44. package/lib/mjs/components/_classes/component/fixtures/comp5.js +2 -2
  45. package/lib/mjs/components/_classes/list/ListComponent.js +5 -12
  46. package/lib/mjs/components/_classes/nested/NestedComponent.js +17 -9
  47. package/lib/mjs/components/columns/editForm/Columns.edit.display.js +1 -1
  48. package/lib/mjs/components/editgrid/EditGrid.js +11 -4
  49. package/lib/mjs/components/form/Form.js +3 -1
  50. package/lib/mjs/components/html/HTML.js +2 -2
  51. package/lib/mjs/components/html/fixtures/comp3.js +29 -0
  52. package/lib/mjs/components/html/fixtures/index.js +2 -1
  53. package/lib/mjs/components/radio/Radio.js +2 -1
  54. package/lib/mjs/components/radio/fixtures/comp10.js +21 -0
  55. package/lib/mjs/components/radio/fixtures/index.js +2 -1
  56. package/lib/mjs/components/recaptcha/ReCaptcha.js +3 -0
  57. package/lib/mjs/components/select/Select.js +83 -10
  58. package/lib/mjs/components/survey/Survey.js +10 -0
  59. package/lib/mjs/utils/conditionOperators/IsEqualTo.js +18 -1
  60. package/lib/mjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
  61. package/lib/mjs/utils/utils.js +35 -0
  62. package/package.json +2 -2
  63. package/types/formio.d.ts +4 -0
@@ -3,7 +3,7 @@ import { Formio } from '../../Formio';
3
3
  import ListComponent from '../_classes/list/ListComponent';
4
4
  import Input from '../_classes/input/Input';
5
5
  import Form from '../../Form';
6
- import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes } from '../../utils/utils';
6
+ import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes, unescapeHTML, isSelectResourceWithObjectValue } from '../../utils/utils';
7
7
  import Choices from '../../utils/ChoicesWrapper';
8
8
  export default class SelectComponent extends ListComponent {
9
9
  static schema(...extend) {
@@ -61,7 +61,19 @@ export default class SelectComponent extends ListComponent {
61
61
  return {
62
62
  ...super.conditionOperatorsSettings,
63
63
  valueComponent(classComp) {
64
- return { ...classComp, type: 'select' };
64
+ const valueComp = { ...classComp, type: 'select' };
65
+ if (isSelectResourceWithObjectValue(classComp)) {
66
+ valueComp.reference = false;
67
+ valueComp.onSetItems = `
68
+ var templateKeys = utils.getItemTemplateKeys(component.template) || [];
69
+ items = _.map(items || [], i => {
70
+ var item = {};
71
+ _.each(templateKeys, k => _.set(item, k, _.get(i, k)));
72
+ return item;
73
+ })
74
+ `;
75
+ }
76
+ return valueComp;
65
77
  }
66
78
  };
67
79
  }
@@ -120,6 +132,7 @@ export default class SelectComponent extends ListComponent {
120
132
  this.itemsLoaded = new Promise((resolve) => {
121
133
  this.itemsLoadedResolve = resolve;
122
134
  });
135
+ this.shouldPositionDropdown = this.hasDataGridAncestor();
123
136
  if (this.isHtmlRenderMode()) {
124
137
  this.activate();
125
138
  }
@@ -851,6 +864,11 @@ export default class SelectComponent extends ListComponent {
851
864
  });
852
865
  }
853
866
  }
867
+ if (window && this.choices && this.shouldPositionDropdown) {
868
+ this.addEventListener(window.document, 'scroll', () => {
869
+ this.positionDropdown(true);
870
+ }, false, true);
871
+ }
854
872
  this.focusableElement.setAttribute('tabIndex', tabIndex);
855
873
  // If a search field is provided, then add an event listener to update items on search.
856
874
  if (this.component.searchField) {
@@ -880,7 +898,10 @@ export default class SelectComponent extends ListComponent {
880
898
  const updateComponent = (evt) => {
881
899
  this.triggerUpdate(evt.detail.value);
882
900
  };
883
- this.addEventListener(input, 'search', _.debounce(updateComponent, debounceTimeout));
901
+ this.addEventListener(input, 'search', _.debounce((e) => {
902
+ updateComponent(e);
903
+ this.positionDropdown();
904
+ }, debounceTimeout));
884
905
  this.addEventListener(input, 'stopSearch', () => this.triggerUpdate());
885
906
  this.addEventListener(input, 'hideDropdown', () => {
886
907
  if (this.choices && this.choices.input && this.choices.input.element) {
@@ -889,7 +910,15 @@ export default class SelectComponent extends ListComponent {
889
910
  this.updateItems(null, true);
890
911
  });
891
912
  }
892
- this.addEventListener(input, 'showDropdown', () => this.update());
913
+ this.addEventListener(input, 'showDropdown', () => {
914
+ this.update();
915
+ this.positionDropdown();
916
+ });
917
+ if (this.shouldPositionDropdown) {
918
+ this.addEventListener(input, 'highlightChoice', () => {
919
+ this.positionDropdown();
920
+ });
921
+ }
893
922
  if (this.choices && choicesOptions.placeholderValue && this.choices._isSelectOneElement) {
894
923
  this.addPlaceholderItem(choicesOptions.placeholderValue);
895
924
  this.addEventListener(input, 'removeItem', () => {
@@ -926,6 +955,44 @@ export default class SelectComponent extends ListComponent {
926
955
  this.triggerUpdate();
927
956
  return superAttach;
928
957
  }
958
+ setDropdownPosition() {
959
+ const dropdown = this.choices?.dropdown?.element;
960
+ const container = this.choices?.containerOuter?.element;
961
+ if (!dropdown || !container) {
962
+ return;
963
+ }
964
+ const containerPosition = container.getBoundingClientRect();
965
+ const isFlipped = container.classList.contains('is-flipped');
966
+ _.assign(dropdown.style, {
967
+ top: `${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px`,
968
+ left: `${containerPosition.left}px`,
969
+ width: `${containerPosition.width}px`,
970
+ position: 'fixed',
971
+ bottom: 'unset',
972
+ right: 'unset',
973
+ });
974
+ }
975
+ hasDataGridAncestor(comp) {
976
+ comp = comp || this;
977
+ if (comp.inDataGrid || comp.type === 'datagrid') {
978
+ return true;
979
+ }
980
+ else if (comp.parent) {
981
+ return this.hasDataGridAncestor(comp.parent);
982
+ }
983
+ else {
984
+ return false;
985
+ }
986
+ }
987
+ positionDropdown(scroll) {
988
+ if (!this.shouldPositionDropdown || !this.choices || (!this.choices.dropdown?.isActive && scroll)) {
989
+ return;
990
+ }
991
+ this.setDropdownPosition();
992
+ this.itemsLoaded.then(() => {
993
+ this.setDropdownPosition();
994
+ });
995
+ }
929
996
  get isLoadingAvailable() {
930
997
  return !this.isScrollLoading && this.additionalResourcesAvailable;
931
998
  }
@@ -1043,10 +1110,10 @@ export default class SelectComponent extends ListComponent {
1043
1110
  }
1044
1111
  return added;
1045
1112
  }
1046
- getValueAsString(data) {
1113
+ getValueAsString(data, options) {
1047
1114
  return (this.component.multiple && Array.isArray(data))
1048
- ? data.map(this.asString.bind(this)).join(', ')
1049
- : this.asString(data);
1115
+ ? data.map((v) => this.asString(v, options)).join(', ')
1116
+ : this.asString(data, options);
1050
1117
  }
1051
1118
  getValue() {
1052
1119
  // If the widget isn't active.
@@ -1398,7 +1465,7 @@ export default class SelectComponent extends ListComponent {
1398
1465
  }
1399
1466
  return this.component.data.values.map(value => ({ label: value.label, value: String(this.normalizeSingleValue(value.value)) }));
1400
1467
  }
1401
- asString(value) {
1468
+ asString(value, options = {}) {
1402
1469
  value = value ?? this.getValue();
1403
1470
  //need to convert values to strings to be able to compare values with available options that are strings
1404
1471
  const convertToString = (data, valueProperty) => {
@@ -1448,9 +1515,15 @@ export default class SelectComponent extends ListComponent {
1448
1515
  if (_.isString(value)) {
1449
1516
  return value;
1450
1517
  }
1518
+ const getTemplateValue = (v) => {
1519
+ const itemTemplate = this.itemTemplate(v);
1520
+ return options.csv && itemTemplate
1521
+ ? unescapeHTML(itemTemplate)
1522
+ : itemTemplate;
1523
+ };
1451
1524
  if (Array.isArray(value)) {
1452
1525
  const items = [];
1453
- value.forEach(item => items.push(this.itemTemplate(item)));
1526
+ value.forEach(item => items.push(getTemplateValue(item)));
1454
1527
  if (this.component.dataSrc === 'resource' && items.length > 0) {
1455
1528
  return items.join(', ');
1456
1529
  }
@@ -1465,7 +1538,7 @@ export default class SelectComponent extends ListComponent {
1465
1538
  return JSON.stringify(value);
1466
1539
  }
1467
1540
  return !_.isNil(value)
1468
- ? this.itemTemplate(value)
1541
+ ? getTemplateValue(value)
1469
1542
  : '-';
1470
1543
  }
1471
1544
  detach() {
@@ -148,6 +148,16 @@ export default class SurveyComponent extends Field {
148
148
  result += '</tbody></table>';
149
149
  return result;
150
150
  }
151
+ if (_.isPlainObject(value)) {
152
+ const { values = [], questions = [] } = this.component;
153
+ return _.isEmpty(value)
154
+ ? ''
155
+ : _.map(value, (v, q) => {
156
+ const valueLabel = _.get(_.find(values, val => _.isEqual(val.value, v)), 'label', v);
157
+ const questionLabel = _.get(_.find(questions, quest => _.isEqual(quest.value, q)), 'label', q);
158
+ return `${questionLabel}: ${valueLabel}`;
159
+ }).join('; ');
160
+ }
151
161
  return super.getValueAsString(value, options);
152
162
  }
153
163
  }
@@ -1,5 +1,6 @@
1
1
  import ConditionOperator from './ConditionOperator';
2
2
  import _ from 'lodash';
3
+ import { getItemTemplateKeys, isSelectResourceWithObjectValue } from '../utils';
3
4
  export default class IsEqualTo extends ConditionOperator {
4
5
  static get operatorKey() {
5
6
  return 'isEqual';
@@ -7,7 +8,7 @@ export default class IsEqualTo extends ConditionOperator {
7
8
  static get displayedName() {
8
9
  return 'Is Equal To';
9
10
  }
10
- execute({ value, comparedValue }) {
11
+ execute({ value, comparedValue, instance, conditionComponentPath }) {
11
12
  if (value && comparedValue && typeof value !== typeof comparedValue && _.isString(comparedValue)) {
12
13
  try {
13
14
  comparedValue = JSON.parse(comparedValue);
@@ -15,6 +16,22 @@ export default class IsEqualTo extends ConditionOperator {
15
16
  // eslint-disable-next-line no-empty
16
17
  catch (e) { }
17
18
  }
19
+ if (instance && instance.root) {
20
+ const conditionTriggerComponent = instance.root.getComponent(conditionComponentPath);
21
+ if (conditionTriggerComponent
22
+ && isSelectResourceWithObjectValue(conditionTriggerComponent.component)
23
+ && conditionTriggerComponent.component?.template) {
24
+ if (!value || !_.isPlainObject(value)) {
25
+ return false;
26
+ }
27
+ const { template, valueProperty } = conditionTriggerComponent.component;
28
+ if (valueProperty === 'data') {
29
+ value = { data: value };
30
+ comparedValue = { data: comparedValue };
31
+ }
32
+ return _.every(getItemTemplateKeys(template) || [], k => _.isEqual(_.get(value, k), _.get(comparedValue, k)));
33
+ }
34
+ }
18
35
  //special check for select boxes
19
36
  if (_.isObject(value) && comparedValue && _.isString(comparedValue)) {
20
37
  return value[comparedValue];
@@ -1,13 +1,12 @@
1
- import ConditionOperator from './ConditionOperator';
2
- import _ from 'lodash';
3
- export default class IsNotEqualTo extends ConditionOperator {
1
+ import IsEqualTo from './IsEqualTo';
2
+ export default class IsNotEqualTo extends IsEqualTo {
4
3
  static get operatorKey() {
5
4
  return 'isNotEqual';
6
5
  }
7
6
  static get displayedName() {
8
7
  return 'Is Not Equal To';
9
8
  }
10
- execute({ value, comparedValue }) {
11
- return !_.isEqual(value, comparedValue);
9
+ execute(options) {
10
+ return !super.execute(options);
12
11
  }
13
12
  }
@@ -394,6 +394,21 @@ export function unescapeHTML(str) {
394
394
  const doc = new window.DOMParser().parseFromString(str, 'text/html');
395
395
  return doc.documentElement.textContent;
396
396
  }
397
+ /**
398
+ * Escape HTML characters like <, >, & and etc.
399
+ * @param str
400
+ * @returns {string}
401
+ */
402
+ export function escapeHTML(html) {
403
+ if (html) {
404
+ return html.replace(/&/g, '&amp;')
405
+ .replace(/</g, '&lt;')
406
+ .replace(/>/g, '&gt;')
407
+ .replace(/"/g, '&quot;')
408
+ .replace(/'/g, '&#39;');
409
+ }
410
+ return '';
411
+ }
397
412
  /**
398
413
  * Make HTML element from string
399
414
  * @param str
@@ -1407,3 +1422,23 @@ export const interpolateErrors = (component, errors, interpolateFn) => {
1407
1422
  return { ...error, message: unescapeHTML(interpolateFn(toInterpolate, context)), context: { ...context } };
1408
1423
  });
1409
1424
  };
1425
+ export function getItemTemplateKeys(template) {
1426
+ const templateKeys = [];
1427
+ if (!template) {
1428
+ return templateKeys;
1429
+ }
1430
+ const keys = template.match(/({{\s*(.*?)\s*}})/g);
1431
+ if (keys) {
1432
+ keys.forEach((key) => {
1433
+ const propKey = key.match(/{{\s*item\.(.*?)\s*}}/);
1434
+ if (propKey && propKey.length > 1) {
1435
+ templateKeys.push(propKey[1]);
1436
+ }
1437
+ });
1438
+ }
1439
+ return templateKeys;
1440
+ }
1441
+ export function isSelectResourceWithObjectValue(comp = {}) {
1442
+ const { reference, dataSrc, valueProperty } = comp;
1443
+ return reference || (dataSrc === 'resource' && (!valueProperty || valueProperty === 'data'));
1444
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formio/js",
3
- "version": "5.0.0-rc.37",
3
+ "version": "5.0.0-rc.39",
4
4
  "description": "JavaScript powered Forms with JSON Form Builder",
5
5
  "main": "lib/cjs/index.js",
6
6
  "exports": {
@@ -86,7 +86,7 @@
86
86
  "dependencies": {
87
87
  "@formio/bootstrap": "^3.0.0-rc.20",
88
88
  "@formio/choices.js": "^10.2.0",
89
- "@formio/core": "^2.0.0-rc.2",
89
+ "@formio/core": "^2.0.0-rc.6",
90
90
  "@formio/text-mask-addons": "^3.8.0-formio.2",
91
91
  "@formio/vanilla-text-mask": "^5.1.1-formio.1",
92
92
  "abortcontroller-polyfill": "^1.7.5",
package/types/formio.d.ts CHANGED
@@ -159,6 +159,10 @@ export declare class Formio {
159
159
  * Stores all of the libraries lazy loaded with ```Formio.requireLibrary``` method.
160
160
  */
161
161
  static libraries: any;
162
+ /**
163
+ * The Library license for this application.
164
+ */
165
+ static license: string;
162
166
  /**
163
167
  * A direct interface to the Form.io fetch polyfill.
164
168
  */