@projectcaluma/ember-form 11.0.0-beta.1 → 11.0.0-beta.10
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/CHANGELOG.md +1181 -0
- package/addon/components/cf-content.hbs +35 -37
- package/addon/components/cf-content.js +1 -9
- package/addon/components/cf-field/info.hbs +2 -2
- package/addon/components/cf-field/info.js +0 -15
- package/addon/components/cf-field/input/action-button.hbs +18 -19
- package/addon/components/cf-field/input/checkbox.hbs +5 -1
- package/addon/components/cf-field/input/date.hbs +8 -5
- package/addon/components/cf-field/input/date.js +28 -10
- package/addon/components/cf-field/input/file.hbs +2 -2
- package/addon/components/cf-field/input/file.js +2 -2
- package/addon/components/cf-field/input/radio.hbs +4 -1
- package/addon/components/cf-field/input/static.hbs +1 -1
- package/addon/components/cf-field/input/table.hbs +24 -24
- package/addon/components/cf-field/input/textarea.hbs +1 -0
- package/addon/components/cf-field/input.hbs +10 -1
- package/addon/components/cf-field-value.hbs +22 -7
- package/addon/components/cf-field-value.js +8 -33
- package/addon/components/cf-field.hbs +12 -4
- package/addon/components/cf-field.js +20 -5
- package/addon/components/cf-navigation.hbs +4 -1
- package/addon/components/document-validity.js +16 -1
- package/addon/gql/fragments/field.graphql +18 -0
- package/addon/lib/field.js +47 -13
- package/addon/lib/navigation.js +28 -15
- package/blueprints/@projectcaluma/ember-form/index.js +1 -0
- package/package.json +19 -15
- package/addon/components/cf-navigation.js +0 -9
- package/addon/instance-initializers/setup-pikaday-i18n.js +0 -35
@@ -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
|
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
|
}
|
@@ -19,6 +19,15 @@ fragment SimpleQuestion on Question {
|
|
19
19
|
value
|
20
20
|
}
|
21
21
|
placeholder
|
22
|
+
formatValidators {
|
23
|
+
edges {
|
24
|
+
node {
|
25
|
+
slug
|
26
|
+
regex
|
27
|
+
errorMsg
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
22
31
|
}
|
23
32
|
... on TextareaQuestion {
|
24
33
|
textareaMinLength: minLength
|
@@ -28,6 +37,15 @@ fragment SimpleQuestion on Question {
|
|
28
37
|
value
|
29
38
|
}
|
30
39
|
placeholder
|
40
|
+
formatValidators {
|
41
|
+
edges {
|
42
|
+
node {
|
43
|
+
slug
|
44
|
+
regex
|
45
|
+
errorMsg
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
31
49
|
}
|
32
50
|
... on IntegerQuestion {
|
33
51
|
integerMinValue: minValue
|
package/addon/lib/field.js
CHANGED
@@ -3,6 +3,7 @@ import { assert } from "@ember/debug";
|
|
3
3
|
import { associateDestroyableChild } from "@ember/destroyable";
|
4
4
|
import { inject as service } from "@ember/service";
|
5
5
|
import { camelize } from "@ember/string";
|
6
|
+
import { isEmpty } from "@ember/utils";
|
6
7
|
import { tracked } from "@glimmer/tracking";
|
7
8
|
import { queryManager } from "ember-apollo-client";
|
8
9
|
import { restartableTask, lastValue, dropTask } from "ember-concurrency";
|
@@ -63,7 +64,6 @@ const fieldIsHiddenOrEmpty = (field) => {
|
|
63
64
|
*/
|
64
65
|
export default class Field extends Base {
|
65
66
|
@service intl;
|
66
|
-
@service validator;
|
67
67
|
|
68
68
|
@queryManager apollo;
|
69
69
|
|
@@ -113,6 +113,7 @@ export default class Field extends Base {
|
|
113
113
|
|
114
114
|
answer = new Answer({
|
115
115
|
raw: {
|
116
|
+
id: null,
|
116
117
|
__typename: answerType,
|
117
118
|
question: { slug: this.raw.question.slug },
|
118
119
|
[camelize(answerType.replace(/Answer$/, "Value"))]: null,
|
@@ -152,6 +153,16 @@ export default class Field extends Base {
|
|
152
153
|
*/
|
153
154
|
@tracked _errors = [];
|
154
155
|
|
156
|
+
/**
|
157
|
+
* Currently rendered field components that use this field. This is used in
|
158
|
+
* the document validity component to await all current save tasks before
|
159
|
+
* validating.
|
160
|
+
*
|
161
|
+
* @property {Set} _components
|
162
|
+
* @private
|
163
|
+
*/
|
164
|
+
_components = new Set();
|
165
|
+
|
155
166
|
/**
|
156
167
|
* The primary key of the field. Consists of the document and question primary
|
157
168
|
* keys.
|
@@ -618,6 +629,35 @@ export default class Field extends Base {
|
|
618
629
|
this._errors = errors;
|
619
630
|
}
|
620
631
|
|
632
|
+
/**
|
633
|
+
* Validate the value against the regexes of the given format validators.
|
634
|
+
*
|
635
|
+
* @method _validateFormatValidators
|
636
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
637
|
+
* @private
|
638
|
+
*/
|
639
|
+
_validateFormatValidators() {
|
640
|
+
const validators =
|
641
|
+
this.question.raw.formatValidators?.edges.map((edge) => edge.node) ?? [];
|
642
|
+
const value = this.answer.value;
|
643
|
+
|
644
|
+
if (isEmpty(value)) {
|
645
|
+
// empty values should not be validated since they are handled by the
|
646
|
+
// requiredness validation
|
647
|
+
return validators.map(() => true);
|
648
|
+
}
|
649
|
+
|
650
|
+
return validators.map((validator) => {
|
651
|
+
return (
|
652
|
+
new RegExp(validator.regex).test(value) || {
|
653
|
+
type: "format",
|
654
|
+
context: { errorMsg: validator.errorMsg },
|
655
|
+
value,
|
656
|
+
}
|
657
|
+
);
|
658
|
+
});
|
659
|
+
}
|
660
|
+
|
621
661
|
/**
|
622
662
|
* Method to validate if a question is required or not.
|
623
663
|
*
|
@@ -637,15 +677,12 @@ export default class Field extends Base {
|
|
637
677
|
* predefined by the question.
|
638
678
|
*
|
639
679
|
* @method _validateTextQuestion
|
640
|
-
* @return {
|
680
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
641
681
|
* @private
|
642
682
|
*/
|
643
|
-
|
683
|
+
_validateTextQuestion() {
|
644
684
|
return [
|
645
|
-
...
|
646
|
-
this.answer.value,
|
647
|
-
this.question.raw.meta.formatValidators ?? []
|
648
|
-
)),
|
685
|
+
...this._validateFormatValidators(),
|
649
686
|
validate("length", this.answer.value, {
|
650
687
|
min: this.question.raw.textMinLength || 0,
|
651
688
|
max: this.question.raw.textMaxLength || Number.POSITIVE_INFINITY,
|
@@ -658,15 +695,12 @@ export default class Field extends Base {
|
|
658
695
|
* than predefined by the question.
|
659
696
|
*
|
660
697
|
* @method _validateTextareaQuestion
|
661
|
-
* @return {
|
698
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
662
699
|
* @private
|
663
700
|
*/
|
664
|
-
|
701
|
+
_validateTextareaQuestion() {
|
665
702
|
return [
|
666
|
-
...
|
667
|
-
this.answer.value,
|
668
|
-
this.question.raw.meta.formatValidators ?? []
|
669
|
-
)),
|
703
|
+
...this._validateFormatValidators(),
|
670
704
|
validate("length", this.answer.value, {
|
671
705
|
min: this.question.raw.textareaMinLength || 0,
|
672
706
|
max: this.question.raw.textareaMaxLength || Number.POSITIVE_INFINITY,
|
package/addon/lib/navigation.js
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
import { getOwner } from "@ember/application";
|
2
2
|
import { assert } from "@ember/debug";
|
3
|
-
import {
|
4
|
-
|
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
|
-
|
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
|
-
*
|
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 (
|
425
|
+
if (this.currentItem?.navigable) {
|
409
426
|
return;
|
410
427
|
}
|
411
428
|
|
412
|
-
|
429
|
+
once(this, "_transitionToNextItem");
|
413
430
|
}
|
414
431
|
|
415
432
|
/**
|
416
|
-
*
|
433
|
+
* Transition to next item or start (empty displayed form).
|
417
434
|
*
|
418
|
-
* @method
|
435
|
+
* @method _transitionToNextItem
|
419
436
|
*/
|
420
|
-
|
421
|
-
if (!this.nextItem?.slug || this.currentItem?.navigable) {
|
422
|
-
return;
|
423
|
-
}
|
424
|
-
|
437
|
+
_transitionToNextItem() {
|
425
438
|
this.router.replaceWith({
|
426
|
-
queryParams: { displayedForm: this.nextItem
|
439
|
+
queryParams: { displayedForm: this.nextItem?.slug ?? "" },
|
427
440
|
});
|
428
441
|
}
|
429
442
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@projectcaluma/ember-form",
|
3
|
-
"version": "11.0.0-beta.
|
3
|
+
"version": "11.0.0-beta.10",
|
4
4
|
"description": "Ember addon for rendering Caluma forms.",
|
5
5
|
"keywords": [
|
6
6
|
"ember-addon"
|
@@ -16,39 +16,43 @@
|
|
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.
|
19
|
+
"@projectcaluma/ember-core": "^11.0.0-beta.4",
|
20
20
|
"ember-apollo-client": "^3.2.0",
|
21
|
-
"ember-auto-import": "^2.
|
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-
|
27
|
+
"ember-concurrency": "^2.2.1",
|
28
|
+
"ember-fetch": "^8.1.1",
|
27
29
|
"ember-in-viewport": "^4.0.0",
|
28
30
|
"ember-intl": "^5.7.2",
|
29
|
-
"ember-math-helpers": "^2.18.
|
30
|
-
"ember-pikaday": "^
|
31
|
+
"ember-math-helpers": "^2.18.1",
|
32
|
+
"ember-pikaday": "^4.0.0",
|
31
33
|
"ember-power-select": "^5.0.3",
|
32
|
-
"ember-resources": "^4.1
|
33
|
-
"ember-uikit": "^
|
34
|
+
"ember-resources": "^4.3.1",
|
35
|
+
"ember-uikit": "^5.0.0",
|
36
|
+
"ember-validators": "^4.1.2",
|
34
37
|
"graphql": "^15.8.0",
|
35
38
|
"jexl": "^2.3.0",
|
36
39
|
"lodash.isequal": "^4.5.0",
|
37
|
-
"
|
40
|
+
"luxon": "^2.3.0",
|
38
41
|
"tracked-toolbox": "^1.2.3"
|
39
42
|
},
|
40
43
|
"devDependencies": {
|
41
44
|
"@ember/optional-features": "2.0.0",
|
42
45
|
"@ember/test-helpers": "2.6.0",
|
43
|
-
"@embroider/test-setup": "
|
44
|
-
"@
|
45
|
-
"@projectcaluma/ember-
|
46
|
+
"@embroider/test-setup": "1.1.0",
|
47
|
+
"@faker-js/faker": "6.0.0-alpha.5",
|
48
|
+
"@projectcaluma/ember-testing": "11.0.0-beta.2",
|
49
|
+
"@projectcaluma/ember-workflow": "11.0.0-beta.4",
|
46
50
|
"broccoli-asset-rev": "3.0.0",
|
47
51
|
"ember-cli": "3.28.5",
|
48
52
|
"ember-cli-code-coverage": "1.0.3",
|
49
53
|
"ember-cli-dependency-checker": "3.2.0",
|
50
54
|
"ember-cli-inject-live-reload": "2.1.0",
|
51
|
-
"ember-cli-mirage": "2.
|
55
|
+
"ember-cli-mirage": "2.4.0",
|
52
56
|
"ember-cli-sri": "2.1.1",
|
53
57
|
"ember-cli-terser": "4.0.2",
|
54
58
|
"ember-disable-prototype-extensions": "1.1.3",
|
@@ -60,13 +64,13 @@
|
|
60
64
|
"ember-source": "3.28.8",
|
61
65
|
"ember-source-channel-url": "3.0.0",
|
62
66
|
"ember-try": "2.0.0",
|
63
|
-
"faker": "5.5.3",
|
64
67
|
"loader.js": "4.7.0",
|
68
|
+
"miragejs": "0.1.43",
|
65
69
|
"npm-run-all": "4.1.5",
|
66
70
|
"qunit": "2.17.2",
|
67
71
|
"qunit-dom": "2.0.0",
|
68
72
|
"uuid": "8.3.2",
|
69
|
-
"webpack": "5.
|
73
|
+
"webpack": "5.68.0"
|
70
74
|
},
|
71
75
|
"engines": {
|
72
76
|
"node": "12.* || 14.* || >= 16"
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import EmberObject from "@ember/object";
|
2
|
-
import { inject as service } from "@ember/service";
|
3
|
-
import moment from "moment";
|
4
|
-
|
5
|
-
class Translations extends EmberObject {
|
6
|
-
@service intl;
|
7
|
-
|
8
|
-
get previousMonth() {
|
9
|
-
return this.intl.t("caluma.form.pikaday.month-previous");
|
10
|
-
}
|
11
|
-
|
12
|
-
get nextMonth() {
|
13
|
-
return this.intl.t("caluma.form.pikaday.month-next");
|
14
|
-
}
|
15
|
-
|
16
|
-
months = moment.localeData().months();
|
17
|
-
weekdays = moment.localeData().weekdays();
|
18
|
-
weekdaysShort = moment.localeData().weekdaysShort();
|
19
|
-
}
|
20
|
-
|
21
|
-
export function initialize(applicationInstance) {
|
22
|
-
applicationInstance.register("pikaday-i18n:main", Translations, {
|
23
|
-
singleton: true,
|
24
|
-
});
|
25
|
-
applicationInstance.inject(
|
26
|
-
"component:pikaday-input",
|
27
|
-
"i18n",
|
28
|
-
"pikaday-i18n:main"
|
29
|
-
);
|
30
|
-
}
|
31
|
-
|
32
|
-
export default {
|
33
|
-
name: "setup-pikaday-i18n",
|
34
|
-
initialize,
|
35
|
-
};
|