@projectcaluma/ember-form 11.0.0-beta.1 → 11.0.0-beta.5

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.
@@ -120,17 +120,9 @@ export default class CfContentComponent extends Component {
120
120
  this.router.currentRoute?.queryParams.displayedForm ||
121
121
  this.document?.raw.form.slug;
122
122
 
123
- const fieldset = this.document.fieldsets.find(
123
+ return this.document.fieldsets.find(
124
124
  (fieldset) => fieldset.form.slug === slug
125
125
  );
126
-
127
- if (!fieldset) {
128
- this.router.replaceWith({
129
- queryParams: { displayedForm: "" },
130
- });
131
- }
132
-
133
- return fieldset;
134
126
  }
135
127
 
136
128
  data = useTask(this, this.fetchData, () => [this.args.documentId]);
@@ -4,7 +4,7 @@
4
4
  class="uk-icon-button"
5
5
  uk-icon="info"
6
6
  title={{t "caluma.form.info"}}
7
- {{on "click" this.showModal}}
7
+ {{on "click" (fn (mut this.modalVisible) true)}}
8
8
  >
9
9
  <span class="uk-hidden">{{t "caluma.form.info"}}</span>
10
10
  </button>
@@ -12,7 +12,7 @@
12
12
  <UkModal
13
13
  @visible={{this.modalVisible}}
14
14
  @stack={{true}}
15
- @on-hide={{this.hideModal}}
15
+ @onHide={{fn (mut this.modalVisible) false}}
16
16
  as |modal|
17
17
  >
18
18
  <modal.body>
@@ -1,21 +1,6 @@
1
- import { action } from "@ember/object";
2
1
  import Component from "@glimmer/component";
3
2
  import { tracked } from "@glimmer/tracking";
4
3
 
5
4
  export default class CfFieldInfoComponent extends Component {
6
5
  @tracked modalVisible = false;
7
-
8
- @action
9
- showModal(e) {
10
- e.preventDefault();
11
-
12
- this.modalVisible = true;
13
- }
14
-
15
- @action
16
- hideModal(e) {
17
- e.preventDefault();
18
-
19
- this.modalVisible = false;
20
- }
21
6
  }
@@ -1,6 +1,10 @@
1
1
  {{#each @field.options as |option i|}}
2
2
  {{#if (gt i 0)}}<br />{{/if}}
3
- <label class="cf-checkbox_label {{if @field.isInvalid 'uk-form-danger'}}">
3
+ <label
4
+ class="cf-checkbox_label
5
+ {{if @field.isInvalid 'uk-form-danger'}}
6
+ {{if @field.raw.question.meta.vertical 'uk-margin-large-right'}}"
7
+ >
4
8
  <input
5
9
  class="uk-checkbox uk-margin-small-right"
6
10
  type="checkbox"
@@ -17,7 +17,7 @@
17
17
  <UkButton
18
18
  data-test-download-link
19
19
  @color="link"
20
- @on-click={{this.download}}
20
+ @onClick={{this.download}}
21
21
  >
22
22
  {{this.downloadName}}
23
23
  </UkButton>
@@ -1,6 +1,9 @@
1
1
  {{#each @field.options as |option i|}}
2
2
  {{#if (gt i 0)}}<br />{{/if}}
3
- <label class={{if @field.isInvalid "uk-form-danger"}}>
3
+ <label
4
+ class="{{if @field.isInvalid 'uk-form-danger'}}
5
+ {{if @field.raw.question.meta.vertical 'uk-margin-large-right'}}"
6
+ >
4
7
  <input
5
8
  class="uk-radio uk-margin-small-right"
6
9
  type="radio"
@@ -28,7 +28,7 @@
28
28
  <button
29
29
  data-test-edit-row
30
30
  type="button"
31
- class="uk-button-link uk-flex-inline uk-margin-small-left"
31
+ class="uk-button uk-button-link uk-flex-inline uk-margin-small-left"
32
32
  title={{t "caluma.form.edit"}}
33
33
  {{on "click" (fn this.edit document)}}
34
34
  >
@@ -39,7 +39,7 @@
39
39
  <button
40
40
  data-test-delete-row
41
41
  type="button"
42
- class="uk-button-link uk-flex-inline uk-margin-small-left"
42
+ class="uk-button uk-button-link uk-flex-inline uk-margin-small-left"
43
43
  title={{t "caluma.form.delete"}}
44
44
  {{on "click" (fn (perform this.delete) document)}}
45
45
  >
@@ -58,8 +58,7 @@
58
58
  <td colspan={{add this.columns.length 1}} class="uk-text-center">
59
59
  <UkButton
60
60
  @size="small"
61
- @color="default"
62
- @on-click={{perform this.add}}
61
+ @onClick={{perform this.add}}
63
62
  data-test-add-row
64
63
  >
65
64
  <UkIcon @icon="plus" />
@@ -74,7 +73,7 @@
74
73
  {{#if this.documentToEdit}}
75
74
  <UkModal
76
75
  @visible={{this.showAddModal}}
77
- @on-hide={{perform this.closeModal}}
76
+ @onHide={{perform this.close}}
78
77
  @bgClose={{false}}
79
78
  as |modal|
80
79
  >
@@ -91,7 +90,7 @@
91
90
  <UkButton
92
91
  @label={{t "caluma.form.close"}}
93
92
  @color="primary"
94
- @on-click={{perform this.close}}
93
+ @onClick={{perform this.close}}
95
94
  @disabled={{this.close.isRunning}}
96
95
  @loading={{this.close.isRunning}}
97
96
  data-test-close
@@ -99,7 +98,7 @@
99
98
  {{else}}
100
99
  <UkButton
101
100
  @label={{t "caluma.form.cancel"}}
102
- @on-click={{perform this.close}}
101
+ @onClick={{perform this.close}}
103
102
  @disabled={{this.close.isRunning}}
104
103
  @loading={{this.close.isRunning}}
105
104
  data-test-cancel
@@ -114,7 +113,7 @@
114
113
  @type="submit"
115
114
  @disabled={{or this.save.isRunning (not isValid)}}
116
115
  @loading={{this.save.isRunning}}
117
- @on-click={{fn (perform this.save) validate}}
116
+ @onClick={{fn (perform this.save) validate}}
118
117
  data-test-save
119
118
  />
120
119
  </DocumentValidity>
@@ -9,4 +9,5 @@
9
9
  maxlength={{@field.question.raw.textareaMaxLength}}
10
10
  readonly={{@disabled}}
11
11
  {{on "input" this.input}}
12
+ {{autoresize mode="height"}}
12
13
  >{{@field.answer.value}}</textarea>
@@ -1,6 +1,15 @@
1
1
  {{#if this.type}}
2
2
  {{#let (component (concat "cf-field/input/" this.type)) as |InputComponent|}}
3
- <div class="uk-form-controls">
3
+ <div
4
+ class="uk-form-controls
5
+ {{if
6
+ (and
7
+ (has-question-type @field.question 'multiple-choice' 'choice')
8
+ @field.question.raw.meta.vertical
9
+ )
10
+ 'uk-flex'
11
+ }}"
12
+ >
4
13
  <InputComponent
5
14
  @field={{@field}}
6
15
  @disabled={{@disabled}}
@@ -2,7 +2,7 @@
2
2
  <UkButton
3
3
  @color="link"
4
4
  @label={{this.value.label}}
5
- @on-click={{fn this.download this.value.fileAnswerId}}
5
+ @onClick={{fn this.download this.value.fileAnswerId}}
6
6
  />
7
7
  {{else}}
8
8
  {{this.value.label}}
@@ -1,5 +1,9 @@
1
1
  {{#if this.visible}}
2
- <div class="uk-margin">
2
+ <div
3
+ class="uk-margin"
4
+ {{did-insert this.registerComponent}}
5
+ {{will-destroy this.unregisterComponent}}
6
+ >
3
7
  {{#if this.labelVisible}}
4
8
  <CfField::label @field={{@field}} />
5
9
  {{/if}}
@@ -24,11 +28,11 @@
24
28
  <div
25
29
  class="cf-field__icon uk-padding-remove-vertical uk-flex uk-flex-middle uk-flex-center"
26
30
  >
27
- {{#if @field.save.isRunning}}
31
+ {{#if this.save.isRunning}}
28
32
  <UkSpinner class="uk-animation-fade" />
29
- {{else if (or @field.save.last.isError @field.isInvalid)}}
33
+ {{else if (or this.save.last.isError @field.isInvalid)}}
30
34
  <UkIcon @icon="warning" class="uk-animation-fade uk-text-danger" />
31
- {{else if @field.save.last.isSuccessful}}
35
+ {{else if this.save.last.isSuccessful}}
32
36
  <UkIcon @icon="check" class="uk-animation-fade uk-text-success" />
33
37
  {{/if}}
34
38
  </div>
@@ -1,5 +1,5 @@
1
1
  import { getOwner } from "@ember/application";
2
- import { set } from "@ember/object";
2
+ import { action } from "@ember/object";
3
3
  import Component from "@glimmer/component";
4
4
  import { timeout, restartableTask } from "ember-concurrency";
5
5
 
@@ -18,6 +18,16 @@ import { hasQuestionType } from "@projectcaluma/ember-core/helpers/has-question-
18
18
  * @argument {Field} field The field data model to render
19
19
  */
20
20
  export default class CfFieldComponent extends Component {
21
+ @action
22
+ registerComponent() {
23
+ this.args.field._components.add(this);
24
+ }
25
+
26
+ @action
27
+ unregisterComponent() {
28
+ this.args.field._components.delete(this);
29
+ }
30
+
21
31
  get visible() {
22
32
  return (
23
33
  !this.args.field?.hidden &&
@@ -41,8 +51,9 @@ export default class CfFieldComponent extends Component {
41
51
  }
42
52
 
43
53
  /**
44
- * Task to save a field. This will set the passed value to the answer and
45
- * save the field to the API after a timeout off 500 milliseconds.
54
+ * Task to save a field. This will set the passed value to the answer and save
55
+ * the field to the API after a timeout of 500 milliseconds which intends to
56
+ * reduce the amount of saved values when changed rapidly.
46
57
  *
47
58
  * @method save
48
59
  * @param {String|Number|String[]} value
@@ -57,14 +68,18 @@ export default class CfFieldComponent extends Component {
57
68
  yield timeout(500);
58
69
  }
59
70
 
60
- set(this.args.field.answer, "value", value);
71
+ if (this.args.field.answer) {
72
+ this.args.field.answer.value = value;
73
+ }
61
74
 
62
75
  yield this.args.field.validate.perform();
63
76
 
64
77
  try {
78
+ // Save the new field value unlinked so the fields save task is not
79
+ // aborted when this component is destroyed
65
80
  return yield this.args.field.save.unlinked().perform();
66
81
  } catch (e) {
67
- // that's ok
82
+ // The component was destroyed before the fields save task was finished
68
83
  }
69
84
  }
70
85
  }
@@ -1,4 +1,7 @@
1
- <ul class="uk-tab uk-tab-left uk-width-auto" {{did-insert this.goToNextItem}}>
1
+ <ul
2
+ class="uk-tab uk-tab-left uk-width-auto"
3
+ {{did-insert @navigation.goToNextItemIfNonNavigable}}
4
+ >
2
5
  {{#each @navigation.rootItems as |item|}}
3
6
  <CfNavigationItem
4
7
  @item={{item}}
@@ -1,6 +1,7 @@
1
1
  import { action } from "@ember/object";
2
2
  import Component from "@glimmer/component";
3
3
  import { restartableTask } from "ember-concurrency";
4
+ import { cached } from "tracked-toolbox";
4
5
 
5
6
  /**
6
7
  * Component to check the validity of a document
@@ -31,12 +32,26 @@ export default class DocumentValidity extends Component {
31
32
  * @argument {Boolean} validateOnEnter
32
33
  */
33
34
 
35
+ @cached
34
36
  get isValid() {
35
- return this.args.document.fields.every((f) => f.isValid);
37
+ return this.args.document.fields
38
+ .filter((f) => !f.hidden)
39
+ .every((f) => f.isValid);
36
40
  }
37
41
 
38
42
  @restartableTask
39
43
  *_validate() {
44
+ const saveTasks = this.args.document.fields
45
+ .flatMap((field) => [
46
+ ...[...(field._components ?? [])].map((c) => c.save.last),
47
+ field.save?.last,
48
+ ])
49
+ .filter(Boolean);
50
+
51
+ // Wait until all currently running save tasks in the UI and in the field
52
+ // itself are finished
53
+ yield Promise.all(saveTasks);
54
+
40
55
  for (const field of this.args.document.fields) {
41
56
  yield field.validate.linked().perform();
42
57
  }
@@ -152,6 +152,16 @@ export default class Field extends Base {
152
152
  */
153
153
  @tracked _errors = [];
154
154
 
155
+ /**
156
+ * Currently rendered field components that use this field. This is used in
157
+ * the document validity component to await all current save tasks before
158
+ * validating.
159
+ *
160
+ * @property {Set} _components
161
+ * @private
162
+ */
163
+ _components = new Set();
164
+
155
165
  /**
156
166
  * The primary key of the field. Consists of the document and question primary
157
167
  * keys.
@@ -1,7 +1,11 @@
1
1
  import { getOwner } from "@ember/application";
2
2
  import { assert } from "@ember/debug";
3
- import { associateDestroyableChild } from "@ember/destroyable";
4
- import { later, once } from "@ember/runloop";
3
+ import {
4
+ associateDestroyableChild,
5
+ registerDestructor,
6
+ } from "@ember/destroyable";
7
+ import { action } from "@ember/object";
8
+ import { next, cancel, once } from "@ember/runloop";
5
9
  import { inject as service } from "@ember/service";
6
10
  import { cached } from "tracked-toolbox";
7
11
 
@@ -300,7 +304,18 @@ export class Navigation extends Base {
300
304
 
301
305
  this._createItems();
302
306
 
303
- this.router.on("routeDidChange", this, "goToNextItemIfNonNavigable");
307
+ const transitionHandler = () => {
308
+ this._timer = next(this, "goToNextItemIfNonNavigable");
309
+ };
310
+
311
+ // go to next item in next run loop, this is necessary when the user clicks
312
+ // on a non navigable item in the navigation
313
+ this.router.on("routeDidChange", this, transitionHandler);
314
+
315
+ registerDestructor(this, () => {
316
+ cancel(this._timer);
317
+ this.router.off("routeDidChange", this, transitionHandler);
318
+ });
304
319
  }
305
320
 
306
321
  /**
@@ -399,31 +414,29 @@ export class Navigation extends Base {
399
414
  }
400
415
 
401
416
  /**
402
- * Observer which transitions to the next navigable item if the current item
403
- * is not navigable.
417
+ * Replace the current item with the next navigable item if the current item
418
+ * is not navigable. This makes sure that only one transition per runloop
419
+ * happens.
404
420
  *
405
421
  * @method goToNextItemIfNonNavigable
406
422
  */
423
+ @action
407
424
  goToNextItemIfNonNavigable() {
408
- if (!this.nextItem?.slug || this.currentItem?.navigable) {
425
+ if (this.currentItem?.navigable) {
409
426
  return;
410
427
  }
411
428
 
412
- later(this, () => once(this, "goToNextItem"));
429
+ once(this, "_transitionToNextItem");
413
430
  }
414
431
 
415
432
  /**
416
- * Replace the current item with the next navigable item
433
+ * Transition to next item or start (empty displayed form).
417
434
  *
418
- * @method goToNextItem
435
+ * @method _transitionToNextItem
419
436
  */
420
- goToNextItem() {
421
- if (!this.nextItem?.slug || this.currentItem?.navigable) {
422
- return;
423
- }
424
-
437
+ _transitionToNextItem() {
425
438
  this.router.replaceWith({
426
- queryParams: { displayedForm: this.nextItem.slug },
439
+ queryParams: { displayedForm: this.nextItem?.slug ?? "" },
427
440
  });
428
441
  }
429
442
  }
@@ -12,6 +12,7 @@ module.exports = {
12
12
  { name: "ember-math-helpers" },
13
13
  { name: "ember-pikaday" },
14
14
  { name: "ember-power-select" },
15
+ { name: "ember-autoresize-modifier" },
15
16
  ],
16
17
  });
17
18
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectcaluma/ember-form",
3
- "version": "11.0.0-beta.1",
3
+ "version": "11.0.0-beta.5",
4
4
  "description": "Ember addon for rendering Caluma forms.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -16,21 +16,23 @@
16
16
  "dependencies": {
17
17
  "@glimmer/component": "^1.0.4",
18
18
  "@glimmer/tracking": "^1.0.4",
19
- "@projectcaluma/ember-core": "^11.0.0-beta.1",
19
+ "@projectcaluma/ember-core": "^11.0.0-beta.2",
20
20
  "ember-apollo-client": "^3.2.0",
21
- "ember-auto-import": "^2.2.3",
21
+ "ember-auto-import": "^2.4.0",
22
+ "ember-autoresize-modifier": "^0.5.0",
22
23
  "ember-cli-babel": "^7.26.11",
23
24
  "ember-cli-htmlbars": "^6.0.1",
24
25
  "ember-cli-showdown": "^6.0.1",
25
26
  "ember-composable-helpers": "^5.0.0",
26
- "ember-fetch": "^8.0.4",
27
+ "ember-fetch": "^8.1.1",
27
28
  "ember-in-viewport": "^4.0.0",
28
29
  "ember-intl": "^5.7.2",
29
- "ember-math-helpers": "^2.18.0",
30
+ "ember-math-helpers": "^2.18.1",
30
31
  "ember-pikaday": "^3.0.0",
31
32
  "ember-power-select": "^5.0.3",
32
- "ember-resources": "^4.1.3",
33
- "ember-uikit": "^4.0.0",
33
+ "ember-resources": "^4.2.0",
34
+ "ember-uikit": "^5.0.0-beta.9",
35
+ "ember-validators": "^4.1.0",
34
36
  "graphql": "^15.8.0",
35
37
  "jexl": "^2.3.0",
36
38
  "lodash.isequal": "^4.5.0",
@@ -40,15 +42,16 @@
40
42
  "devDependencies": {
41
43
  "@ember/optional-features": "2.0.0",
42
44
  "@ember/test-helpers": "2.6.0",
43
- "@embroider/test-setup": "0.49.0",
44
- "@projectcaluma/ember-testing": "10.1.0",
45
- "@projectcaluma/ember-workflow": "10.0.3",
45
+ "@embroider/test-setup": "1.0.0",
46
+ "@faker-js/faker": "6.0.0-alpha.5",
47
+ "@projectcaluma/ember-testing": "10.2.0-beta.2",
48
+ "@projectcaluma/ember-workflow": "11.0.0-beta.3",
46
49
  "broccoli-asset-rev": "3.0.0",
47
50
  "ember-cli": "3.28.5",
48
51
  "ember-cli-code-coverage": "1.0.3",
49
52
  "ember-cli-dependency-checker": "3.2.0",
50
53
  "ember-cli-inject-live-reload": "2.1.0",
51
- "ember-cli-mirage": "2.2.0",
54
+ "ember-cli-mirage": "2.4.0",
52
55
  "ember-cli-sri": "2.1.1",
53
56
  "ember-cli-terser": "4.0.2",
54
57
  "ember-disable-prototype-extensions": "1.1.3",
@@ -60,13 +63,13 @@
60
63
  "ember-source": "3.28.8",
61
64
  "ember-source-channel-url": "3.0.0",
62
65
  "ember-try": "2.0.0",
63
- "faker": "5.5.3",
64
66
  "loader.js": "4.7.0",
67
+ "miragejs": "0.1.43",
65
68
  "npm-run-all": "4.1.5",
66
69
  "qunit": "2.17.2",
67
70
  "qunit-dom": "2.0.0",
68
71
  "uuid": "8.3.2",
69
- "webpack": "5.65.0"
72
+ "webpack": "5.68.0"
70
73
  },
71
74
  "engines": {
72
75
  "node": "12.* || 14.* || >= 16"
@@ -1,9 +0,0 @@
1
- import { action } from "@ember/object";
2
- import Component from "@glimmer/component";
3
-
4
- export default class CfNavigationComponent extends Component {
5
- @action
6
- goToNextItem() {
7
- this.args.navigation.goToNextItem();
8
- }
9
- }