@projectcaluma/ember-form 10.0.0 → 11.0.0-beta.1
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.
- package/addon/components/cf-content.hbs +36 -39
- package/addon/components/cf-content.js +47 -20
- package/addon/components/cf-field/input/action-button.hbs +1 -1
- package/addon/components/cf-field/input/action-button.js +9 -7
- package/addon/components/cf-field/input/checkbox.hbs +2 -2
- package/addon/components/cf-field/input/checkbox.js +9 -29
- package/addon/components/cf-field/input/file.js +8 -9
- package/addon/components/cf-field/input/float.hbs +4 -4
- package/addon/components/cf-field/input/integer.hbs +5 -5
- package/addon/components/cf-field/input/table.js +12 -10
- package/addon/components/cf-field/input/text.hbs +5 -5
- package/addon/components/cf-field/input/textarea.hbs +5 -5
- package/addon/components/cf-field/input.js +1 -1
- package/addon/components/cf-field/label.hbs +1 -1
- package/addon/components/cf-field-value.js +8 -13
- package/addon/components/cf-field.hbs +2 -2
- package/addon/components/cf-field.js +2 -3
- package/addon/components/cf-navigation-item.hbs +2 -2
- package/addon/components/document-validity.js +1 -1
- package/addon/gql/fragments/field.graphql +27 -0
- package/addon/gql/mutations/save-document-table-answer.graphql +1 -1
- package/addon/gql/mutations/save-document.graphql +1 -0
- package/addon/gql/queries/{get-document-answers.graphql → document-answers.graphql} +2 -1
- package/addon/gql/queries/{get-document-forms.graphql → document-forms.graphql} +2 -1
- package/addon/gql/queries/{get-document-used-dynamic-options.graphql → document-used-dynamic-options.graphql} +2 -1
- package/addon/gql/queries/{get-dynamic-options.graphql → dynamic-options.graphql} +2 -1
- package/addon/gql/queries/{get-fileanswer-info.graphql → fileanswer-info.graphql} +2 -1
- package/addon/helpers/get-widget.js +50 -0
- package/addon/lib/answer.js +108 -72
- package/addon/lib/base.js +32 -23
- package/addon/lib/dependencies.js +36 -71
- package/addon/lib/document.js +92 -96
- package/addon/lib/field.js +334 -401
- package/addon/lib/fieldset.js +46 -47
- package/addon/lib/form.js +27 -15
- package/addon/lib/navigation.js +187 -181
- package/addon/lib/question.js +103 -94
- package/addon/services/caluma-store.js +10 -6
- package/app/helpers/get-widget.js +4 -0
- package/package.json +19 -18
- package/CHANGELOG.md +0 -21
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { get } from "@ember/object";
|
2
|
+
import { cached } from "tracked-toolbox";
|
2
3
|
|
3
4
|
import { getAST, getTransforms } from "@projectcaluma/ember-core/utils/jexl";
|
4
5
|
|
@@ -47,79 +48,43 @@ export function getDependenciesFromJexl(jexl, expression) {
|
|
47
48
|
}
|
48
49
|
|
49
50
|
/**
|
50
|
-
*
|
51
|
-
* nested dependency parent would be a table field that is used with a mapby
|
52
|
-
* transform in the JEXL expression.
|
53
|
-
*
|
54
|
-
* E.g: 'foo'|answer in 'bar'|answer|mapby('column') where 'bar' would be a
|
55
|
-
* nested dependency parent.
|
56
|
-
*
|
57
|
-
* Those need to be extracted seperately since the overall dependencies need to
|
58
|
-
* depend on the values of the nested dependency parents to recompute
|
59
|
-
* correctly.
|
51
|
+
* Getter to extract all fields used in an expression.
|
60
52
|
*
|
61
53
|
* @param {String} expressionPath The path of the expression
|
62
|
-
* @return {Field[]}
|
54
|
+
* @return {Field[]} An array of all dependency fields
|
63
55
|
*/
|
64
|
-
export function
|
65
|
-
return
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
) {
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
const slugs = getDependenciesFromJexl(this.document.jexl, expression);
|
97
|
-
|
98
|
-
return slugs
|
99
|
-
.flatMap((slug) => {
|
100
|
-
const [fieldSlug, nestedSlug = null] = slug.split(".");
|
101
|
-
|
102
|
-
if (onlyNestedParents && !nestedSlug) {
|
103
|
-
return null;
|
104
|
-
}
|
105
|
-
|
106
|
-
const field = this.document.findField(fieldSlug);
|
107
|
-
|
108
|
-
if (!onlyNestedParents && nestedSlug && field?.value) {
|
109
|
-
// Get the nested fields from the parents value (rows)
|
110
|
-
const childFields =
|
111
|
-
nestedSlug === "__all__"
|
112
|
-
? field.value.flatMap((row) => row.fields)
|
113
|
-
: field.value.map((row) => row.findField(nestedSlug));
|
114
|
-
|
115
|
-
return [field, ...childFields];
|
116
|
-
}
|
117
|
-
|
118
|
-
return [field];
|
119
|
-
})
|
120
|
-
.filter(Boolean);
|
121
|
-
}
|
122
|
-
);
|
56
|
+
export function dependencies(expressionPath) {
|
57
|
+
return function (target, key) {
|
58
|
+
return cached(target, key, {
|
59
|
+
get() {
|
60
|
+
const expression = get(this, expressionPath);
|
61
|
+
|
62
|
+
if (!expression) return [];
|
63
|
+
|
64
|
+
const slugs = getDependenciesFromJexl(this.document.jexl, expression);
|
65
|
+
|
66
|
+
return slugs
|
67
|
+
.flatMap((slug) => {
|
68
|
+
const [fieldSlug, nestedSlug = null] = slug.split(".");
|
69
|
+
|
70
|
+
const field = this.document.findField(fieldSlug);
|
71
|
+
|
72
|
+
if (nestedSlug && field?.value) {
|
73
|
+
// Get the nested fields from the parents value (rows)
|
74
|
+
const childFields =
|
75
|
+
nestedSlug === "__all__"
|
76
|
+
? field.value.flatMap((row) => row.fields)
|
77
|
+
: field.value.map((row) => row.findField(nestedSlug));
|
78
|
+
|
79
|
+
return [field, ...childFields];
|
80
|
+
}
|
81
|
+
|
82
|
+
return [field];
|
83
|
+
})
|
84
|
+
.filter(Boolean);
|
85
|
+
},
|
86
|
+
});
|
87
|
+
};
|
123
88
|
}
|
124
89
|
|
125
90
|
export default dependencies;
|
package/addon/lib/document.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { getOwner } from "@ember/application";
|
2
2
|
import { assert } from "@ember/debug";
|
3
|
-
import {
|
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
|
21
|
-
|
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
|
-
|
24
|
+
raw?.__typename === "Document"
|
27
25
|
);
|
28
26
|
|
29
|
-
|
30
|
-
writable: false,
|
31
|
-
value: `Document:${decodeId(this.raw.id)}`,
|
32
|
-
});
|
27
|
+
super({ raw, ...args });
|
33
28
|
|
34
|
-
this.
|
29
|
+
this.parentDocument = parentDocument;
|
35
30
|
|
36
|
-
this.
|
31
|
+
this.pushIntoStore();
|
37
32
|
|
38
33
|
this._createRootForm();
|
39
34
|
this._createFieldsets();
|
40
|
-
}
|
35
|
+
}
|
41
36
|
|
42
37
|
_createRootForm() {
|
43
|
-
const
|
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.
|
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
|
54
|
-
|
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
|
-
|
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
|
65
|
+
* The parent document of this document. If this is set, the document is most
|
66
|
+
* likely a table row.
|
78
67
|
*
|
79
|
-
* @property {
|
80
|
-
* @accessor
|
68
|
+
* @property {Document} parentDocument
|
81
69
|
*/
|
82
|
-
|
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
|
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
|
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
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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
|
186
|
-
|
187
|
-
|
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: {
|
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
|
-
|
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.
|
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
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
});
|
272
|
+
return [...this.fields, ...(this.parentDocument?.fields ?? [])].find(
|
273
|
+
(field) => field.question.slug === slug
|
274
|
+
);
|
275
|
+
}
|
276
|
+
}
|