@projectcaluma/ember-form 10.0.3 → 11.0.0-beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/CHANGELOG.md +1119 -0
  2. package/addon/components/cf-content.hbs +36 -39
  3. package/addon/components/cf-content.js +46 -27
  4. package/addon/components/cf-field/info.hbs +2 -2
  5. package/addon/components/cf-field/info.js +0 -15
  6. package/addon/components/cf-field/input/action-button.hbs +1 -1
  7. package/addon/components/cf-field/input/action-button.js +9 -7
  8. package/addon/components/cf-field/input/checkbox.hbs +6 -2
  9. package/addon/components/cf-field/input/checkbox.js +9 -29
  10. package/addon/components/cf-field/input/file.hbs +1 -1
  11. package/addon/components/cf-field/input/file.js +7 -8
  12. package/addon/components/cf-field/input/float.hbs +2 -2
  13. package/addon/components/cf-field/input/integer.hbs +3 -3
  14. package/addon/components/cf-field/input/radio.hbs +4 -1
  15. package/addon/components/cf-field/input/table.hbs +7 -8
  16. package/addon/components/cf-field/input/table.js +12 -10
  17. package/addon/components/cf-field/input/text.hbs +3 -3
  18. package/addon/components/cf-field/input/textarea.hbs +4 -3
  19. package/addon/components/cf-field/input.hbs +10 -1
  20. package/addon/components/cf-field/input.js +1 -1
  21. package/addon/components/cf-field/label.hbs +1 -1
  22. package/addon/components/cf-field-value.hbs +1 -1
  23. package/addon/components/cf-field-value.js +7 -12
  24. package/addon/components/cf-field.hbs +2 -2
  25. package/addon/components/cf-field.js +2 -3
  26. package/addon/components/cf-navigation.hbs +4 -1
  27. package/addon/components/document-validity.js +1 -1
  28. package/addon/helpers/get-widget.js +50 -0
  29. package/addon/lib/answer.js +108 -72
  30. package/addon/lib/base.js +32 -23
  31. package/addon/lib/dependencies.js +36 -71
  32. package/addon/lib/document.js +92 -96
  33. package/addon/lib/field.js +333 -400
  34. package/addon/lib/fieldset.js +46 -47
  35. package/addon/lib/form.js +27 -15
  36. package/addon/lib/navigation.js +211 -192
  37. package/addon/lib/question.js +102 -93
  38. package/addon/services/caluma-store.js +10 -6
  39. package/app/helpers/get-widget.js +4 -0
  40. package/blueprints/@projectcaluma/ember-form/index.js +1 -0
  41. package/package.json +24 -20
  42. package/addon/components/cf-navigation.js +0 -9
@@ -1,8 +1,8 @@
1
1
  import { getOwner } from "@ember/application";
2
2
  import { assert } from "@ember/debug";
3
- import { computed, defineProperty } from "@ember/object";
4
- import { inject as service } from "@ember/service";
3
+ import { associateDestroyableChild } from "@ember/destroyable";
5
4
  import jexl from "jexl";
5
+ import { cached } from "tracked-toolbox";
6
6
 
7
7
  import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
8
8
  import { intersects, mapby } from "@projectcaluma/ember-core/utils/jexl";
@@ -17,124 +17,123 @@ const sum = (nums) => nums.reduce((num, base) => base + num, 0);
17
17
  *
18
18
  * @class Document
19
19
  */
20
- export default Base.extend({
21
- calumaStore: service(),
22
-
23
- init(...args) {
20
+ export default class Document extends Base {
21
+ constructor({ raw, parentDocument, ...args }) {
24
22
  assert(
25
23
  "A graphql document `raw` must be passed",
26
- this.raw && this.raw.__typename === "Document"
24
+ raw?.__typename === "Document"
27
25
  );
28
26
 
29
- defineProperty(this, "pk", {
30
- writable: false,
31
- value: `Document:${decodeId(this.raw.id)}`,
32
- });
27
+ super({ raw, ...args });
33
28
 
34
- this._super(...args);
29
+ this.parentDocument = parentDocument;
35
30
 
36
- this.set("fieldsets", []);
31
+ this.pushIntoStore();
37
32
 
38
33
  this._createRootForm();
39
34
  this._createFieldsets();
40
- },
35
+ }
41
36
 
42
37
  _createRootForm() {
43
- const rootForm =
44
- this.calumaStore.find(`Form:${this.raw.rootForm.slug}`) ||
45
- getOwner(this)
46
- .factoryFor("caluma-model:form")
47
- .create({ raw: this.raw.rootForm });
38
+ const owner = getOwner(this);
48
39
 
49
- this.set("rootForm", rootForm);
50
- },
40
+ this.rootForm =
41
+ this.calumaStore.find(`Form:${this.raw.rootForm.slug}`) ||
42
+ new (owner.factoryFor("caluma-model:form").class)({
43
+ raw: this.raw.rootForm,
44
+ owner,
45
+ });
46
+ }
51
47
 
52
48
  _createFieldsets() {
53
- const fieldsets = this.raw.forms.map((form) => {
54
- return (
49
+ const owner = getOwner(this);
50
+
51
+ this.fieldsets = this.raw.forms.map((form) => {
52
+ return associateDestroyableChild(
53
+ this,
55
54
  this.calumaStore.find(`${this.pk}:Form:${form.slug}`) ||
56
- getOwner(this)
57
- .factoryFor("caluma-model:fieldset")
58
- .create({
55
+ new (owner.factoryFor("caluma-model:fieldset").class)({
59
56
  raw: { form, answers: this.raw.answers },
60
57
  document: this,
58
+ owner,
61
59
  })
62
60
  );
63
61
  });
64
-
65
- this.set("fieldsets", fieldsets);
66
- },
67
-
68
- willDestroy(...args) {
69
- this._super(...args);
70
-
71
- const fieldsets = this.fieldsets;
72
- this.set("fieldsets", []);
73
- fieldsets.forEach((fieldset) => fieldset.destroy());
74
- },
62
+ }
75
63
 
76
64
  /**
77
- * The uuid of the document
65
+ * The parent document of this document. If this is set, the document is most
66
+ * likely a table row.
78
67
  *
79
- * @property {String} uuid
80
- * @accessor
68
+ * @property {Document} parentDocument
81
69
  */
82
- uuid: computed("raw.id", function () {
83
- return decodeId(this.raw.id);
84
- }),
85
-
86
- workItemUuid: computed(
87
- "raw.{workItem.id,case.workItems.edges.[]}",
88
- function () {
89
- // The document is either directly attached to a work item (via
90
- // CompleteTaskFormTask) or it's the case document and therefore
91
- // indirectly attached to a work item (via CompleteWorkflowFormTask)
92
- const rawId =
93
- this.raw.workItem?.id ||
94
- this.raw.case?.workItems.edges.find(
95
- (edge) => edge.node.task.__typename === "CompleteWorkflowFormTask"
96
- )?.node.id;
97
-
98
- return rawId ? decodeId(rawId) : null;
99
- }
100
- ),
70
+ parentDocument = null;
101
71
 
102
72
  /**
103
73
  * The root form of this document
104
74
  *
105
75
  * @property {Form} rootForm
106
- * @accessor
107
76
  */
108
- rootForm: null,
77
+ rootForm = null;
109
78
 
110
79
  /**
111
80
  * The fieldsets of this document
112
81
  *
113
82
  * @property {Fieldset[]} fieldsets
114
- * @accessor
115
83
  */
116
- fieldsets: null,
84
+ fieldsets = [];
85
+
86
+ /**
87
+ * The primary key of the document.
88
+ *
89
+ * @property {String} pk
90
+ */
91
+ @cached
92
+ get pk() {
93
+ return `Document:${this.uuid}`;
94
+ }
95
+
96
+ /**
97
+ * The uuid of the document
98
+ *
99
+ * @property {String} uuid
100
+ */
101
+ @cached
102
+ get uuid() {
103
+ return decodeId(this.raw.id);
104
+ }
105
+
106
+ @cached
107
+ get workItemUuid() {
108
+ // The document is either directly attached to a work item (via
109
+ // CompleteTaskFormTask) or it's the case document and therefore
110
+ // indirectly attached to a work item (via CompleteWorkflowFormTask)
111
+ const rawId =
112
+ this.raw.workItem?.id ||
113
+ this.raw.case?.workItems.edges.find(
114
+ (edge) => edge.node.task.__typename === "CompleteWorkflowFormTask"
115
+ )?.node.id;
116
+
117
+ return rawId ? decodeId(rawId) : null;
118
+ }
117
119
 
118
120
  /**
119
121
  * All fields of all fieldsets of this document
120
122
  *
121
123
  * @property {Field[]} fields
122
- * @accessor
123
124
  */
124
- fields: computed("fieldsets.@each.fields", function () {
125
- return this.fieldsets.reduce(
126
- (fields, fieldset) => [...fields, ...fieldset.fields],
127
- []
128
- );
129
- }),
125
+ @cached
126
+ get fields() {
127
+ return this.fieldsets.flatMap((fieldset) => fieldset.fields);
128
+ }
130
129
 
131
130
  /**
132
131
  * The JEXL object for evaluating jexl expressions on this document
133
132
  *
134
133
  * @property {JEXL} jexl
135
- * @accessor
136
134
  */
137
- jexl: computed(function () {
135
+ @cached
136
+ get jexl() {
138
137
  const documentJexl = new jexl.Jexl();
139
138
 
140
139
  documentJexl.addTransform("answer", (slug, defaultValue) =>
@@ -174,31 +173,29 @@ export default Base.extend({
174
173
  documentJexl.addTransform("stringify", (input) => JSON.stringify(input));
175
174
 
176
175
  return documentJexl;
177
- }),
176
+ }
178
177
 
179
178
  /**
180
179
  * The JEXL context object for passing to the evaluation of jexl expessions
181
180
  *
182
181
  * @property {Object} jexlContext
183
- * @accessor
184
182
  */
185
- jexlContext: computed(
186
- "rootForm.{slug,meta}",
187
- "parentDocument.jexlContext",
188
- function () {
189
- if (this.parentDocument) return this.parentDocument.jexlContext;
190
-
191
- return {
183
+ get jexlContext() {
184
+ return (
185
+ this.parentDocument?.jexlContext ?? {
192
186
  // JEXL interprets null in an expression as variable instead of a
193
187
  // primitive. This resolves that issue.
194
188
  null: null,
195
189
  form: this.rootForm.slug,
196
190
  info: {
197
- root: { form: this.rootForm.slug, formMeta: this.rootForm.meta },
191
+ root: {
192
+ form: this.rootForm.slug,
193
+ formMeta: this.rootForm.raw.meta,
194
+ },
198
195
  },
199
- };
200
- }
201
- ),
196
+ }
197
+ );
198
+ }
202
199
 
203
200
  /**
204
201
  * Object representation of a document. The question slug as key and the
@@ -215,9 +212,9 @@ export default Base.extend({
215
212
  * answer.
216
213
  *
217
214
  * @property {Object} flatAnswerMap
218
- * @accessor
219
215
  */
220
- flatAnswerMap: computed("fields.@each.{question,value}", function () {
216
+ @cached
217
+ get flatAnswerMap() {
221
218
  return this.fields.reduce(
222
219
  (answerMap, field) => ({
223
220
  ...answerMap,
@@ -225,7 +222,7 @@ export default Base.extend({
225
222
  }),
226
223
  {}
227
224
  );
228
- }),
225
+ }
229
226
 
230
227
  /**
231
228
  * Find an answer for a given question slug
@@ -249,7 +246,7 @@ export default Base.extend({
249
246
  return defaultValue ?? field.question.isMultipleChoice ? [] : null;
250
247
  }
251
248
 
252
- if (field.question.__typename === "TableQuestion") {
249
+ if (field.question.isTable) {
253
250
  return field.value.map((doc) =>
254
251
  doc.fields
255
252
  .filter((field) => !field.hidden)
@@ -263,7 +260,7 @@ export default Base.extend({
263
260
  }
264
261
 
265
262
  return field.value;
266
- },
263
+ }
267
264
 
268
265
  /**
269
266
  * Find a field in the document by a given question slug
@@ -272,9 +269,8 @@ export default Base.extend({
272
269
  * @return {Field} The wanted field
273
270
  */
274
271
  findField(slug) {
275
- return [
276
- ...this.fields,
277
- ...(this.parentDocument ? this.parentDocument.fields : []),
278
- ].find((field) => field.question.slug === slug);
279
- },
280
- });
272
+ return [...this.fields, ...(this.parentDocument?.fields ?? [])].find(
273
+ (field) => field.question.slug === slug
274
+ );
275
+ }
276
+ }