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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- }