@formio/js 5.0.0-dev.5898.ffba52a → 5.0.0-dev.5904.e376ad2

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 (31) hide show
  1. package/dist/formio.form.js +50 -28
  2. package/dist/formio.form.min.js +1 -1
  3. package/dist/formio.form.min.js.LICENSE.txt +2 -0
  4. package/dist/formio.full.js +50 -28
  5. package/dist/formio.full.min.js +1 -1
  6. package/dist/formio.full.min.js.LICENSE.txt +2 -0
  7. package/dist/formio.js +207 -4
  8. package/dist/formio.min.js +1 -1
  9. package/dist/formio.min.js.LICENSE.txt +2 -0
  10. package/dist/formio.utils.js +42 -20
  11. package/dist/formio.utils.min.js +1 -1
  12. package/dist/formio.utils.min.js.LICENSE.txt +2 -0
  13. package/lib/cjs/components/_classes/component/Component.d.ts +15 -0
  14. package/lib/cjs/components/_classes/component/Component.js +57 -23
  15. package/lib/cjs/components/_classes/component/editForm/Component.edit.data.js +2 -2
  16. package/lib/cjs/components/_classes/nested/NestedComponent.js +16 -7
  17. package/lib/cjs/components/datamap/DataMap.js +1 -1
  18. package/lib/cjs/components/editgrid/EditGrid.js +2 -5
  19. package/lib/cjs/components/form/Form.js +4 -4
  20. package/lib/cjs/components/html/HTML.js +15 -3
  21. package/lib/cjs/components/radio/Radio.js +2 -3
  22. package/lib/mjs/components/_classes/component/Component.d.ts +15 -0
  23. package/lib/mjs/components/_classes/component/Component.js +57 -23
  24. package/lib/mjs/components/_classes/component/editForm/Component.edit.data.js +2 -2
  25. package/lib/mjs/components/_classes/nested/NestedComponent.js +16 -7
  26. package/lib/mjs/components/datamap/DataMap.js +1 -1
  27. package/lib/mjs/components/editgrid/EditGrid.js +2 -5
  28. package/lib/mjs/components/form/Form.js +4 -4
  29. package/lib/mjs/components/html/HTML.js +15 -3
  30. package/lib/mjs/components/radio/Radio.js +2 -3
  31. package/package.json +2 -2
@@ -308,11 +308,18 @@ export default class Component extends Element {
308
308
  this._path = '';
309
309
  // Needs for Nextgen Rules Engine
310
310
  this.resetCaches();
311
+ /**
312
+ * Determines if this component is conditionally hidden. Should generally not be set outside of conditional logic pipeline.
313
+ * This is necessary because of clearOnHide behavior that only clears when conditionally hidden - we need to track
314
+ * conditionallyHidden separately from "regular" visibility.
315
+ */
316
+ this._parentConditionallyHidden = this.options.hasOwnProperty('parentConditionallyHidden') ? this.options.parentConditionallyHidden : false;
317
+ this._conditionallyHidden = this.checkConditionallyHidden(null, data) || this._parentConditionallyHidden;
311
318
  /**
312
319
  * Determines if this component is visible, or not.
313
320
  */
314
321
  this._parentVisible = this.options.hasOwnProperty('parentVisible') ? this.options.parentVisible : true;
315
- this._visible = this._parentVisible && this.conditionallyVisible(null, data);
322
+ this._visible = this._parentVisible && (this.hasCondition() ? !this._conditionallyHidden : !this.component.hidden);
316
323
  this._parentDisabled = false;
317
324
  /**
318
325
  * The reference attribute name for this component
@@ -381,7 +388,7 @@ export default class Component extends Element {
381
388
  if (this.allowData && this.key) {
382
389
  this.options.name += `[${this.key}]`;
383
390
  // If component is visible or not set to clear on hide, set the default value.
384
- if (this.visible || !this.component.clearOnHide) {
391
+ if (!(this.conditionallyHidden && this.component.clearOnHide)) {
385
392
  if (!this.hasValue()) {
386
393
  if (this.shouldAddDefaultValue) {
387
394
  this.dataValue = this.defaultValue;
@@ -455,7 +462,8 @@ export default class Component extends Element {
455
462
  }
456
463
  init() {
457
464
  this.disabled = this.shouldDisabled;
458
- this._visible = this.conditionallyVisible(null, null);
465
+ this._conditionallyHidden = this.checkConditionallyHidden();
466
+ this._visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden);
459
467
  if (this.component.addons?.length) {
460
468
  this.component.addons.forEach((addon) => this.createAddon(addon));
461
469
  }
@@ -573,7 +581,6 @@ export default class Component extends Element {
573
581
  return;
574
582
  }
575
583
  this._visible = value;
576
- this.clearOnHide();
577
584
  this.redraw();
578
585
  }
579
586
  }
@@ -594,6 +601,21 @@ export default class Component extends Element {
594
601
  }
595
602
  return this._visible && this._parentVisible;
596
603
  }
604
+ get conditionallyHidden() {
605
+ return this._conditionallyHidden || this._parentConditionallyHidden;
606
+ }
607
+ /**
608
+ * Evaluates whether the component is conditionally hidden (as opposed to intentionally hidden, e.g. via the `hidden` component schema property).
609
+ * @param {object} data - The data object to evaluate the condition against.
610
+ * @param {object} row - The row object to evaluate the condition against.
611
+ * @returns {boolean} - Whether the component is conditionally hidden.
612
+ */
613
+ checkConditionallyHidden(data = null, row = null) {
614
+ if (!this.hasCondition()) {
615
+ return false;
616
+ }
617
+ return !this.conditionallyVisible(data, row);
618
+ }
597
619
  get currentForm() {
598
620
  return this._currentForm;
599
621
  }
@@ -1764,7 +1786,7 @@ export default class Component extends Element {
1764
1786
  rebuild() {
1765
1787
  this.destroy();
1766
1788
  this.init();
1767
- this.visible = this.conditionallyVisible(null, null);
1789
+ this.visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden;
1768
1790
  return this.redraw();
1769
1791
  }
1770
1792
  /**
@@ -1831,8 +1853,8 @@ export default class Component extends Element {
1831
1853
  conditionallyVisible(data, row) {
1832
1854
  data = data || this.rootValue;
1833
1855
  row = row || this.data;
1834
- if (this.builderMode || this.previewMode || !this.hasCondition()) {
1835
- return !this.component.hidden;
1856
+ if (this.builderMode || this.previewMode) {
1857
+ return true;
1836
1858
  }
1837
1859
  data = data || (this.root ? this.root.data : {});
1838
1860
  return this.checkCondition(row, data);
@@ -1862,8 +1884,14 @@ export default class Component extends Element {
1862
1884
  if (!this.builderMode & !this.previewMode && this.fieldLogic(data, row)) {
1863
1885
  this.redraw();
1864
1886
  }
1865
- // Check advanced conditions
1866
- const visible = this.conditionallyVisible(data, row);
1887
+ // Check advanced conditions (and cache the result)
1888
+ const isConditionallyHidden = this.checkConditionallyHidden(data, row) || this._parentConditionallyHidden;
1889
+ if (isConditionallyHidden !== this._conditionallyHidden) {
1890
+ this._conditionallyHidden = isConditionallyHidden;
1891
+ this.clearOnHide();
1892
+ }
1893
+ // Check visibility
1894
+ const visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden);
1867
1895
  if (this.visible !== visible) {
1868
1896
  this.visible = visible;
1869
1897
  }
@@ -1973,6 +2001,12 @@ export default class Component extends Element {
1973
2001
  FormioUtils.setActionProperty(newComponent, action, result, row, data, this);
1974
2002
  const property = action.property.value;
1975
2003
  if (!_.isEqual(_.get(this.component, property), _.get(newComponent, property))) {
2004
+ // Advanced Logic can modify the component's hidden property; because we track conditionally hidden state
2005
+ // separately from the component's hidden property, and technically this Advanced Logic conditionally hides
2006
+ // a component, we need to set _conditionallyHidden to the new value
2007
+ if (property === 'hidden') {
2008
+ this._conditionallyHidden = newComponent.hidden;
2009
+ }
1976
2010
  changed = true;
1977
2011
  }
1978
2012
  break;
@@ -1986,7 +2020,7 @@ export default class Component extends Element {
1986
2020
  component: newComponent,
1987
2021
  result,
1988
2022
  });
1989
- if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) {
2023
+ if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) {
1990
2024
  this.setValue(newValue);
1991
2025
  if (this.viewOnly) {
1992
2026
  this.dataValue = newValue;
@@ -2019,7 +2053,7 @@ export default class Component extends Element {
2019
2053
  component: newComponent,
2020
2054
  result,
2021
2055
  }, 'value');
2022
- if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) {
2056
+ if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) {
2023
2057
  this.setValue(newValue);
2024
2058
  if (this.viewOnly) {
2025
2059
  this.dataValue = newValue;
@@ -2130,7 +2164,7 @@ export default class Component extends Element {
2130
2164
  this.component.clearOnHide !== false &&
2131
2165
  !this.options.readOnly &&
2132
2166
  !this.options.showHiddenFields) {
2133
- if (!this.visible) {
2167
+ if (this.conditionallyHidden) {
2134
2168
  this.deleteValue();
2135
2169
  }
2136
2170
  else if (!this.hasValue() && this.shouldAddDefaultValue) {
@@ -2392,7 +2426,7 @@ export default class Component extends Element {
2392
2426
  */
2393
2427
  get dataValue() {
2394
2428
  if (!this.key ||
2395
- (!this.visible && this.component.clearOnHide && !this.rootPristine)) {
2429
+ (this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine)) {
2396
2430
  return this.emptyValue;
2397
2431
  }
2398
2432
  if (!this.hasValue() && this.shouldAddDefaultValue) {
@@ -2411,7 +2445,7 @@ export default class Component extends Element {
2411
2445
  set dataValue(value) {
2412
2446
  if (!this.allowData ||
2413
2447
  !this.key ||
2414
- (!this.visible && this.component.clearOnHide && !this.rootPristine)) {
2448
+ (this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine)) {
2415
2449
  return;
2416
2450
  }
2417
2451
  if ((value !== null) && (value !== undefined)) {
@@ -2731,7 +2765,7 @@ export default class Component extends Element {
2731
2765
  // If no calculated value or
2732
2766
  // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden)
2733
2767
  const { clearOnHide } = this.component;
2734
- const shouldBeCleared = !this.visible && clearOnHide;
2768
+ const shouldBeCleared = this.conditionallyHidden && clearOnHide;
2735
2769
  const allowOverride = _.get(this.component, 'allowCalculateOverride', false);
2736
2770
  if (shouldBeCleared) {
2737
2771
  // remove calculated value so that the value is recalculated once component becomes visible
@@ -3214,12 +3248,6 @@ export default class Component extends Element {
3214
3248
  return (this.component.protected || !this.component.persistent || (this.component.persistent === 'client-only'));
3215
3249
  }
3216
3250
  shouldSkipValidation(data, row, flags = {}) {
3217
- const { validateWhenHidden = false } = this.component || {};
3218
- const forceValidOnHidden = (!this.visible || !this.checkCondition(row, data)) && !validateWhenHidden;
3219
- if (forceValidOnHidden) {
3220
- // If this component is forced valid when it is hidden, then we also need to reset the errors for this component.
3221
- this._errors = [];
3222
- }
3223
3251
  const rules = [
3224
3252
  // Do not validate if the flags say not too.
3225
3253
  () => flags.noValidate,
@@ -3230,7 +3258,13 @@ export default class Component extends Element {
3230
3258
  // Check to see if we are editing and if so, check component persistence.
3231
3259
  () => this.isValueHidden(),
3232
3260
  // Force valid if component is hidden.
3233
- () => forceValidOnHidden
3261
+ () => {
3262
+ if (!this.component.validateWhenHidden && (!this.visible || !this.checkCondition(row, data))) {
3263
+ this._errors = [];
3264
+ return true;
3265
+ }
3266
+ return false;
3267
+ }
3234
3268
  ];
3235
3269
  return rules.some(pred => pred());
3236
3270
  }
@@ -3367,7 +3401,7 @@ export default class Component extends Element {
3367
3401
  // If component definition changed, replace it.
3368
3402
  if (!_.isEqual(this.component, newComponent)) {
3369
3403
  this.component = newComponent;
3370
- const visible = this.conditionallyVisible(null, null);
3404
+ const visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden;
3371
3405
  const disabled = this.shouldDisabled;
3372
3406
  // Change states which won't be recalculated during redrawing
3373
3407
  if (this.visible !== visible) {
@@ -128,10 +128,10 @@ export default [
128
128
  {
129
129
  weight: 700,
130
130
  type: 'checkbox',
131
- label: 'Clear Value When Hidden',
131
+ label: 'Omit Value From Submission Data When Conditionally Hidden',
132
132
  key: 'clearOnHide',
133
133
  defaultValue: true,
134
- tooltip: 'When a field is hidden, clear the value.',
134
+ tooltip: 'When a field is conditionally hidden, omit the value from the submission data.',
135
135
  input: true
136
136
  },
137
137
  EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 1000, '<p><h4>Example:</h4><pre>value = data.firstName + " " + data.lastName;</pre></p>', '<p><h4>Example:</h4><pre>{"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}</pre>'),
@@ -80,17 +80,26 @@ export default class NestedComponent extends Field {
80
80
  const visibilityChanged = this._visible !== value;
81
81
  this._visible = value;
82
82
  const isVisible = this.visible;
83
+ const isConditionallyHidden = this.checkConditionallyHidden();
83
84
  const forceShow = this.shouldForceShow();
84
85
  const forceHide = this.shouldForceHide();
85
- this.components.forEach(component => {
86
+ this.components.forEach((component) => {
86
87
  // Set the parent visibility first since we may have nested components within nested components
87
88
  // and they need to be able to determine their visibility based on the parent visibility.
88
89
  component.parentVisible = isVisible;
89
- const conditionallyVisible = component.conditionallyVisible();
90
- if (forceShow || conditionallyVisible) {
90
+ component._parentConditionallyHidden = isConditionallyHidden;
91
+ let visible;
92
+ if (component.hasCondition()) {
93
+ component._conditionallyHidden = component.checkConditionallyHidden() || component._parentConditionallyHidden;
94
+ visible = !component.conditionallyHidden;
95
+ }
96
+ else {
97
+ visible = !component.component.hidden;
98
+ }
99
+ if (forceShow || visible) {
91
100
  component.visible = true;
92
101
  }
93
- else if (forceHide || !isVisible || !conditionallyVisible) {
102
+ else if (forceHide || !isVisible || !visible) {
94
103
  component.visible = false;
95
104
  }
96
105
  // If hiding a nested component, clear all errors below.
@@ -99,7 +108,6 @@ export default class NestedComponent extends Field {
99
108
  }
100
109
  });
101
110
  if (visibilityChanged) {
102
- this.clearOnHide();
103
111
  this.redraw();
104
112
  }
105
113
  }
@@ -380,6 +388,7 @@ export default class NestedComponent extends Field {
380
388
  data = data || this.data;
381
389
  options.parent = this;
382
390
  options.parentVisible = this.visible;
391
+ options.parentConditionallyHidden = this.conditionallyHidden;
383
392
  options.root = options?.root || this.root || this;
384
393
  options.localRoot = this.localRoot;
385
394
  options.skipInit = true;
@@ -638,7 +647,7 @@ export default class NestedComponent extends Field {
638
647
  clearOnHide(show) {
639
648
  super.clearOnHide(show);
640
649
  if (this.component.clearOnHide) {
641
- if (this.allowData && !this.hasValue() && !(this.options.server && !this.visible)) {
650
+ if (this.allowData && !this.hasValue() && !this.conditionallyHidden) {
642
651
  this.dataValue = this.defaultValue;
643
652
  }
644
653
  if (this.hasValue()) {
@@ -667,7 +676,7 @@ export default class NestedComponent extends Field {
667
676
  }
668
677
  calculateValue(data, flags, row) {
669
678
  // Do not iterate into children and calculateValues if this nested component is conditionally hidden.
670
- if (!this.conditionallyVisible()) {
679
+ if (this.conditionallyHidden) {
671
680
  return false;
672
681
  }
673
682
  return this.getComponents().reduce((changed, comp) => comp.calculateValue(data, flags, row) || changed, super.calculateValue(data, flags, row));
@@ -70,7 +70,7 @@ export default class DataMapComponent extends DataGridComponent {
70
70
  }
71
71
  get dataValue() {
72
72
  if (!this.key ||
73
- (!this.visible && this.component.clearOnHide)) {
73
+ (this.conditionallyHidden && this.component.clearOnHide)) {
74
74
  return this.emptyValue;
75
75
  }
76
76
  if (!this.hasValue() && this.shouldAddDefaultValue) {
@@ -1139,7 +1139,7 @@ export default class EditGridComponent extends NestedArrayComponent {
1139
1139
  }
1140
1140
  }
1141
1141
  const changed = this.hasChanged(value, this.dataValue);
1142
- if (this.parent && !this.options.server) {
1142
+ if (this.parent) {
1143
1143
  this.parent.checkComponentConditions();
1144
1144
  }
1145
1145
  this.dataValue = value;
@@ -1172,10 +1172,7 @@ export default class EditGridComponent extends NestedArrayComponent {
1172
1172
  this.editRows = this.editRows.slice(0, dataLength);
1173
1173
  this.openWhenEmpty();
1174
1174
  this.updateOnChange(flags, changed);
1175
- // do not call checkData with server option, it is called when change is triggered in updateOnChange
1176
- if (!this.options.server) {
1177
- this.checkData();
1178
- }
1175
+ this.checkData();
1179
1176
  this.changeState(changed, flags);
1180
1177
  return changed;
1181
1178
  }
@@ -417,10 +417,10 @@ export default class FormComponent extends Component {
417
417
  return this.subFormReady;
418
418
  }
419
419
  hideSubmitButton(component) {
420
- const isSubmitButton = (component.type === 'button') &&
421
- ((component.action === 'submit') || !component.action);
420
+ const isSubmitButton = component.type === 'button' && (component.action === 'submit' || !component.action);
422
421
  if (isSubmitButton) {
423
422
  component.hidden = true;
423
+ component.customConditional = 'show = false';
424
424
  }
425
425
  }
426
426
  /**
@@ -429,7 +429,7 @@ export default class FormComponent extends Component {
429
429
  * @returns {Promise} - The promise that resolves when the subform is loaded.
430
430
  */
431
431
  loadSubForm(fromAttach) {
432
- if (this.builderMode || this.isHidden() || (this.isSubFormLazyLoad() && !fromAttach)) {
432
+ if (this.builderMode || this.conditionallyHidden || (this.isSubFormLazyLoad() && !fromAttach)) {
433
433
  return Promise.resolve();
434
434
  }
435
435
  if (this.hasLoadedForm && !this.isRevisionChanged &&
@@ -512,7 +512,7 @@ export default class FormComponent extends Component {
512
512
  * @returns {*|boolean} - TRUE if the subform should be submitted, FALSE if it should not.
513
513
  */
514
514
  get shouldSubmit() {
515
- return this.subFormReady && (!this.component.hasOwnProperty('reference') || this.component.reference) && !this.isHidden();
515
+ return this.subFormReady && (!this.component.hasOwnProperty('reference') || this.component.reference) && !this.conditionallyHidden;
516
516
  }
517
517
  /**
518
518
  * Returns the data for the subform.
@@ -51,9 +51,21 @@ export default class HTMLComponent extends Component {
51
51
  }
52
52
  checkRefreshOn(changed) {
53
53
  super.checkRefreshOn(changed);
54
- if (!this.builderMode && this.component.refreshOnChange && this.element &&
55
- !_.isUndefined(changed) && ((_.isBoolean(changed) && changed) || !_.isEmpty(changed)) &&
56
- this.conditionallyVisible(this.data, this.row)) {
54
+ let visible;
55
+ if (this.hasCondition()) {
56
+ this._conditionallyHidden = this.checkConditionallyHidden();
57
+ visible = !this.conditionallyHidden;
58
+ }
59
+ else {
60
+ visible = !this.component.hidden;
61
+ }
62
+ const shouldSetContent = !this.builderMode
63
+ && this.component.refreshOnChange
64
+ && this.element
65
+ && !_.isUndefined(changed)
66
+ && ((_.isBoolean(changed) && changed) || !_.isEmpty(changed))
67
+ && visible;
68
+ if (shouldSetContent) {
57
69
  this.setContent(this.element, this.renderContent());
58
70
  }
59
71
  }
@@ -342,9 +342,8 @@ export default class RadioComponent extends ListComponent {
342
342
  label: this.component.valueProperty ? this.itemTemplate(item, valueAtProperty) : this.itemTemplate(item, item, i)
343
343
  };
344
344
  listData.push(this.templateData[this.component.valueProperty ? valueAtProperty : i]);
345
- if ((this.component.valueProperty || !this.isRadio) && (_.isUndefined(valueAtProperty) ||
346
- (!this.isRadio && _.isObject(valueAtProperty)) ||
347
- (!this.isRadio && _.isBoolean(valueAtProperty)))) {
345
+ const value = this.loadedOptions[i].value;
346
+ if (!this.isRadio && (_.isObject(value) || _.isBoolean(value) || _.isUndefined(value))) {
348
347
  this.loadedOptions[i].invalid = true;
349
348
  }
350
349
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formio/js",
3
- "version": "5.0.0-dev.5898.ffba52a",
3
+ "version": "5.0.0-dev.5904.e376ad2",
4
4
  "description": "JavaScript powered Forms with JSON Form Builder",
5
5
  "main": "lib/cjs/index.js",
6
6
  "exports": {
@@ -82,7 +82,7 @@
82
82
  "dependencies": {
83
83
  "@formio/bootstrap": "3.0.0-dev.98.17ba6ea",
84
84
  "@formio/choices.js": "^10.2.1",
85
- "@formio/core": "v2.1.0-dev.174.9a3c6ec",
85
+ "@formio/core": "2.1.0-dev.191.8c609ab",
86
86
  "@formio/text-mask-addons": "^3.8.0-formio.3",
87
87
  "@formio/vanilla-text-mask": "^5.1.1-formio.1",
88
88
  "abortcontroller-polyfill": "^1.7.5",