@projectcaluma/ember-form-builder 10.0.3 → 11.0.0-beta.11

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +1159 -0
  2. package/addon/components/cfb-code-editor.hbs +2 -1
  3. package/addon/components/cfb-code-editor.js +53 -8
  4. package/addon/components/cfb-form-editor/general.hbs +81 -90
  5. package/addon/components/cfb-form-editor/general.js +15 -11
  6. package/addon/components/cfb-form-editor/question/default.js +11 -12
  7. package/addon/components/cfb-form-editor/question/options.hbs +1 -1
  8. package/addon/components/cfb-form-editor/question/validation.hbs +3 -2
  9. package/addon/components/cfb-form-editor/question/validation.js +17 -13
  10. package/addon/components/cfb-form-editor/question-list/item.hbs +4 -4
  11. package/addon/components/cfb-form-editor/question-list/item.js +2 -2
  12. package/addon/components/cfb-form-editor/question-list.hbs +6 -6
  13. package/addon/components/cfb-form-editor/question-list.js +13 -8
  14. package/addon/components/cfb-form-editor/question.hbs +24 -16
  15. package/addon/components/cfb-form-editor/question.js +22 -20
  16. package/addon/components/cfb-form-editor.hbs +5 -5
  17. package/addon/components/cfb-form-list/item.hbs +1 -1
  18. package/addon/components/cfb-form-list.hbs +69 -72
  19. package/addon/components/cfb-form-list.js +49 -39
  20. package/addon/components/cfb-toggle-switch.hbs +1 -1
  21. package/addon/controllers/index.js +6 -0
  22. package/addon/engine.js +8 -11
  23. package/addon/gql/queries/all-format-validators.graphql +10 -0
  24. package/addon/gql/queries/form-editor-question.graphql +14 -0
  25. package/addon/instance-initializers/form-builder-widget-overrides.js +15 -0
  26. package/addon/modifiers/pikaday.js +2 -0
  27. package/addon/routes/edit/questions/edit.js +1 -1
  28. package/addon/routes/edit.js +1 -1
  29. package/addon/templates/edit/questions/edit.hbs +1 -1
  30. package/addon/templates/edit/questions/new.hbs +1 -4
  31. package/addon/templates/edit.hbs +5 -5
  32. package/addon/templates/index.hbs +8 -1
  33. package/addon/templates/new.hbs +1 -1
  34. package/addon/utils/and.js +47 -0
  35. package/addon/utils/or.js +40 -0
  36. package/addon/validations/option.js +1 -1
  37. package/addon/validations/question.js +2 -2
  38. package/addon/validators/slug.js +1 -1
  39. package/app/instance-initializers/form-builder-widget-overrides.js +4 -0
  40. package/app/styles/@projectcaluma/ember-form-builder.scss +1 -1
  41. package/app/utils/and.js +1 -0
  42. package/app/utils/or.js +1 -0
  43. package/index.js +6 -4
  44. package/package.json +37 -29
  45. package/translations/de.yaml +5 -5
  46. package/translations/en.yaml +2 -2
  47. package/translations/fr.yaml +154 -1
@@ -2,9 +2,10 @@
2
2
 
3
3
  <div
4
4
  name={{@name}}
5
- class={{concat "uk-textarea cfb-code-editor language-" @language}}
5
+ class="uk-textarea cfb-code-editor {{concat 'language-' @language}}"
6
6
  {{did-insert this.didInsertNode}}
7
7
  {{will-destroy this.willDestroyNode}}
8
+ {{autoresize mode="height"}}
8
9
  ></div>
9
10
 
10
11
  {{component @hintComponent}}
@@ -1,25 +1,70 @@
1
1
  import { action } from "@ember/object";
2
+ import { addObserver } from "@ember/object/observers";
2
3
  import Component from "@glimmer/component";
3
- import { tracked } from "@glimmer/tracking";
4
4
  import { CodeJar } from "codejar";
5
5
  import Prism from "prismjs";
6
+
7
+ import "prismjs/components/prism-json.js";
6
8
  import "prismjs/components/prism-jexl.js";
7
9
  import "prismjs/components/prism-markdown.js";
8
10
 
9
11
  export default class CfbCodeEditorComponent extends Component {
10
- editor;
11
- @tracked show = false;
12
+ _editor = null;
13
+ _cursor = null;
14
+ _lastValue = null;
15
+
16
+ get value() {
17
+ const value = this.args.value;
18
+
19
+ if (this.args.language === "json" && typeof value === "object") {
20
+ return JSON.stringify(value?.unwrap?.() ?? value, null, 2);
21
+ }
22
+
23
+ return this.args.value;
24
+ }
25
+
26
+ @action
27
+ onUpdate(value) {
28
+ if (this._lastValue === value) return;
29
+
30
+ this._cursor = this._editor.save();
31
+
32
+ if (this.args.language === "json") {
33
+ try {
34
+ value = JSON.parse(value);
35
+ } catch {
36
+ // update value directly
37
+ }
38
+ }
39
+
40
+ this._lastValue = value;
41
+ this.args.update(value);
42
+ this.args.setDirty();
43
+ }
44
+
45
+ @action
46
+ updateCode() {
47
+ this._editor.updateCode(this.value);
48
+
49
+ if (this._cursor) {
50
+ this._editor.restore(this._cursor);
51
+ this._cursor = null;
52
+ }
53
+ }
12
54
 
13
55
  @action
14
56
  didInsertNode(element) {
15
- const highlight = (editor) => Prism.highlightElement(editor);
16
- this.editor = CodeJar(element, highlight);
17
- this.editor.updateCode(this.args.value);
18
- this.editor.onUpdate(this.args.update);
57
+ this._editor = CodeJar(element, (editor) => Prism.highlightElement(editor));
58
+ this._editor.onUpdate(this.onUpdate);
59
+
60
+ this.updateCode();
61
+
62
+ // eslint-disable-next-line ember/no-observers
63
+ addObserver(this.args, "value", this, "updateCode");
19
64
  }
20
65
 
21
66
  @action
22
67
  willDestroyNode() {
23
- this.editor.destroy();
68
+ this._editor.destroy();
24
69
  }
25
70
  }
@@ -1,98 +1,89 @@
1
- <div
2
- {{did-insert (perform this.data)}}
3
- {{did-update (perform this.data) @slug}}
4
- >
5
- {{#if this.data.isRunning}}
6
- <div class="uk-flex uk-flex-center uk-flex-middle uk-height-small">
7
- <UkSpinner ratio="2" />
8
- </div>
9
- {{else}}
10
- {{#with this.data.lastSuccessful.value.firstObject.node as |model|}}
11
- <ValidatedForm
12
- @model={{changeset model this.Validations}}
13
- @on-submit={{perform this.submit}}
14
- as |f|
15
- >
16
- <f.input
17
- @type="text"
18
- @label={{t "caluma.form-builder.form.name"}}
19
- @name="name"
20
- @required={{true}}
21
- @on-update={{this.updateName}}
22
- />
1
+ {{#if this._data.isRunning}}
2
+ <div class="uk-flex uk-flex-center uk-flex-middle uk-height-small">
3
+ <UkSpinner ratio="2" />
4
+ </div>
5
+ {{else if this.data}}
6
+ <ValidatedForm
7
+ @model={{changeset this.data this.Validations}}
8
+ @on-submit={{perform this.submit}}
9
+ as |f|
10
+ >
11
+ <f.input
12
+ @type="text"
13
+ @label={{t "caluma.form-builder.form.name"}}
14
+ @name="name"
15
+ @required={{true}}
16
+ @on-update={{this.updateName}}
17
+ />
23
18
 
24
- {{#if (or @slug (not this.prefix))}}
19
+ {{#if (or @slug (not this.prefix))}}
20
+ <f.input
21
+ @type="text"
22
+ @name="slug"
23
+ @label={{t "caluma.form-builder.form.slug"}}
24
+ @required={{true}}
25
+ @disabled={{@slug}}
26
+ @on-update={{this.updateSlug}}
27
+ />
28
+ {{else}}
29
+ <f.input
30
+ @name="slug"
31
+ @required={{true}}
32
+ @disabled={{@slug}}
33
+ @label={{t "caluma.form-builder.question.slug"}}
34
+ @on-update={{this.updateSlug value="target.value"}}
35
+ as |fi|
36
+ >
37
+ <div class="cfb-prefixed">
38
+ <span class="cfb-prefixed-slug">{{this.prefix}}</span>
25
39
  <f.input
26
40
  @type="text"
27
- @name="slug"
28
- @label={{t "caluma.form-builder.form.slug"}}
29
- @required={{true}}
30
- @disabled={{@slug}}
31
- @on-update={{this.updateSlug}}
41
+ @model={{fi.model}}
42
+ @name={{fi.name}}
43
+ @value={{fi.value}}
44
+ @update={{fi.update}}
45
+ @setDirty={{fi.setDirty}}
46
+ @inputId={{fi.inputId}}
47
+ @isValid={{fi.isValid}}
48
+ @isInvalid={{fi.isInvalid}}
32
49
  />
33
- {{else}}
34
- <f.input
35
- @name="slug"
36
- @required={{true}}
37
- @disabled={{@slug}}
38
- @label={{t "caluma.form-builder.question.slug"}}
39
- @on-update={{this.updateSlug value="target.value"}}
40
- as |fi|
41
- >
42
- <div class="cfb-prefixed">
43
- <span class="cfb-prefixed-slug">{{this.prefix}}</span>
44
- <f.input
45
- @type="text"
46
- @model={{fi.model}}
47
- @name={{fi.name}}
48
- @value={{fi.value}}
49
- @update={{fi.update}}
50
- @setDirty={{fi.setDirty}}
51
- @inputId={{fi.inputId}}
52
- @isValid={{fi.isValid}}
53
- @isInvalid={{fi.isInvalid}}
54
- />
55
- </div>
56
- </f.input>
57
- {{/if}}
50
+ </div>
51
+ </f.input>
52
+ {{/if}}
58
53
 
59
- <f.input
60
- @name="description"
61
- @type="textarea"
62
- @label={{t "caluma.form-builder.form.description"}}
63
- @rows="4"
64
- />
54
+ <f.input
55
+ @name="description"
56
+ @type="textarea"
57
+ @label={{t "caluma.form-builder.form.description"}}
58
+ {{autoresize mode="height"}}
59
+ />
65
60
 
66
- <f.input
67
- class="uk-flex uk-flex-between uk-flex-column"
68
- @name="isArchived"
69
- @label={{t "caluma.form-builder.form.isArchived"}}
70
- @required={{true}}
71
- @renderComponent={{component "cfb-toggle-switch" size="small"}}
72
- />
61
+ <f.input
62
+ @name="isArchived"
63
+ @label={{t "caluma.form-builder.form.isArchived"}}
64
+ @required={{true}}
65
+ @renderComponent={{component "cfb-toggle-switch" size="small"}}
66
+ />
73
67
 
74
- <f.input
75
- class="uk-flex uk-flex-between uk-flex-column"
76
- @name="isPublished"
77
- @label={{t "caluma.form-builder.form.isPublished"}}
78
- @required={{true}}
79
- @renderComponent={{component "cfb-toggle-switch" size="small"}}
80
- />
68
+ <f.input
69
+ @name="isPublished"
70
+ @label={{t "caluma.form-builder.form.isPublished"}}
71
+ @required={{true}}
72
+ @renderComponent={{component "cfb-toggle-switch" size="small"}}
73
+ />
81
74
 
82
- <div class="uk-text-right">
83
- <f.submit
84
- @disabled={{or f.loading f.model.isInvalid}}
85
- @label={{t "caluma.form-builder.global.save"}}
86
- />
87
- </div>
88
- </ValidatedForm>
89
- {{else}}
90
- <div
91
- class="uk-text-center uk-text-muted uk-padding uk-padding-remove-horizontal"
92
- >
93
- {{uk-icon "bolt" ratio=5}}
94
- <p>{{t "caluma.form-builder.form.not-found" slug=@slug}}</p>
95
- </div>
96
- {{/with}}
97
- {{/if}}
98
- </div>
75
+ <div class="uk-text-right">
76
+ <f.submit
77
+ @disabled={{or f.loading f.model.isInvalid}}
78
+ @label={{t "caluma.form-builder.global.save"}}
79
+ />
80
+ </div>
81
+ </ValidatedForm>
82
+ {{else}}
83
+ <div
84
+ class="uk-text-center uk-text-muted uk-padding uk-padding-remove-horizontal"
85
+ >
86
+ <UkIcon @icon="bolt" @ratio={{5}} />
87
+ <p>{{t "caluma.form-builder.form.not-found" slug=@slug}}</p>
88
+ </div>
89
+ {{/if}}
@@ -1,11 +1,10 @@
1
- import { getOwner } from "@ember/application";
2
- import { A } from "@ember/array";
3
1
  import { action } from "@ember/object";
4
2
  import { inject as service } from "@ember/service";
3
+ import { macroCondition, isTesting } from "@embroider/macros";
5
4
  import Component from "@glimmer/component";
6
5
  import { queryManager } from "ember-apollo-client";
7
- import { optional } from "ember-composable-helpers/helpers/optional";
8
6
  import { timeout, restartableTask, dropTask } from "ember-concurrency";
7
+ import { useTask } from "ember-resources";
9
8
 
10
9
  import FormValidations from "../../validations/form";
11
10
 
@@ -22,10 +21,16 @@ export default class CfbFormEditorGeneral extends Component {
22
21
 
23
22
  Validations = FormValidations;
24
23
 
24
+ get data() {
25
+ return this._data.value?.[0]?.node;
26
+ }
27
+
28
+ _data = useTask(this, this.fetchData, () => [this.args.slug]);
29
+
25
30
  @restartableTask
26
- *data() {
31
+ *fetchData() {
27
32
  if (!this.args.slug) {
28
- return A([
33
+ return [
29
34
  {
30
35
  node: {
31
36
  name: "",
@@ -34,7 +39,7 @@ export default class CfbFormEditorGeneral extends Component {
34
39
  isPublished: true,
35
40
  },
36
41
  },
37
- ]);
42
+ ];
38
43
  }
39
44
 
40
45
  return yield this.apollo.watchQuery(
@@ -83,7 +88,7 @@ export default class CfbFormEditorGeneral extends Component {
83
88
  )
84
89
  );
85
90
 
86
- optional([this.args["on-after-submit"]])(form);
91
+ this.args.onAfterSubmit?.(form);
87
92
  } catch (e) {
88
93
  this.notification.danger(
89
94
  this.intl.t(
@@ -98,10 +103,9 @@ export default class CfbFormEditorGeneral extends Component {
98
103
  @restartableTask
99
104
  *validateSlug(slug, changeset) {
100
105
  /* istanbul ignore next */
101
- if (
102
- getOwner(this).resolveRegistration("config:environment").environment !==
103
- "test"
104
- ) {
106
+ if (macroCondition(isTesting())) {
107
+ // no timeout
108
+ } else {
105
109
  yield timeout(500);
106
110
  }
107
111
 
@@ -92,18 +92,17 @@ export default class CfbFormEditorQuestionDefault extends Component {
92
92
  : null,
93
93
  }));
94
94
  }
95
-
96
- const document = getOwner(this)
97
- .factoryFor("caluma-model:document")
98
- .create({
99
- raw: {
100
- id: btoa(`Document:dv-document-${this.args.model.slug}`),
101
- rootForm,
102
- forms: [rootForm],
103
- answers: [answer],
104
- __typename: "Document",
105
- },
106
- });
95
+ const owner = getOwner(this);
96
+ const document = new (owner.factoryFor("caluma-model:document").class)({
97
+ raw: {
98
+ id: btoa(`Document:dv-document-${this.args.model.slug}`),
99
+ rootForm,
100
+ forms: [rootForm],
101
+ answers: [answer],
102
+ __typename: "Document",
103
+ },
104
+ owner,
105
+ });
107
106
 
108
107
  return document.findField(this.question.slug);
109
108
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  <UkSortable
5
5
  @handle=".uk-sortable-handle"
6
- @on-moved={{this._handleMoved}}
6
+ @onMoved={{this._handleMoved}}
7
7
  @tagName="ul"
8
8
  class="uk-list uk-list-divider uk-margin-remove-bottom uk-margin-small-top"
9
9
  >
@@ -1,11 +1,12 @@
1
- <div class="uk-margin" {{did-insert (perform this.availableFormatValidators)}}>
1
+ <div class="uk-margin">
2
2
  {{component @labelComponent}}
3
3
 
4
4
  <PowerSelectMultiple
5
5
  @selected={{this.selected}}
6
- @placeholder={{this.placeholder}}
6
+ @placeholder={{@placeholder}}
7
7
  @options={{this.validators}}
8
8
  @onChange={{this.updateValidators}}
9
+ @renderInPlace={{true}}
9
10
  as |item|
10
11
  >
11
12
  {{item.name}}
@@ -2,34 +2,38 @@ import { action } from "@ember/object";
2
2
  import Component from "@glimmer/component";
3
3
  import { queryManager } from "ember-apollo-client";
4
4
  import { dropTask } from "ember-concurrency";
5
+ import { useTask } from "ember-resources";
5
6
 
6
- import allFormatValidatorsQuery from "@projectcaluma/ember-core/gql/queries/all-format-validators.graphql";
7
+ import allFormatValidatorsQuery from "@projectcaluma/ember-form-builder/gql/queries/all-format-validators.graphql";
7
8
 
8
9
  export default class CfbFormEditorQuestionValidation extends Component {
9
10
  @queryManager apollo;
10
11
 
11
- @dropTask
12
- *availableFormatValidators() {
13
- const formatValidators = yield this.apollo.watchQuery(
14
- { query: allFormatValidatorsQuery, fetchPolicy: "cache-and-network" },
15
- "allFormatValidators.edges"
16
- );
17
- return formatValidators.map((edge) => edge.node);
18
- }
19
-
20
12
  get validators() {
21
- return this.availableFormatValidators?.lastSuccessful?.value || [];
13
+ return this._validators.value?.map((edge) => edge.node) ?? [];
22
14
  }
23
15
 
24
16
  get selected() {
25
17
  return this.validators.filter((validator) =>
26
- (this.args.value || []).includes(validator.slug)
18
+ (this.args.value?.edges.map((edge) => edge.node.slug) || []).includes(
19
+ validator.slug
20
+ )
21
+ );
22
+ }
23
+
24
+ _validators = useTask(this, this.fetchFormatValidators, () => []);
25
+
26
+ @dropTask
27
+ *fetchFormatValidators() {
28
+ return yield this.apollo.watchQuery(
29
+ { query: allFormatValidatorsQuery, fetchPolicy: "cache-and-network" },
30
+ "allFormatValidators.edges"
27
31
  );
28
32
  }
29
33
 
30
34
  @action
31
35
  updateValidators(value) {
32
- this.args.update(value.map((validator) => validator.slug));
36
+ this.args.update({ edges: value.map(({ slug }) => ({ node: { slug } })) });
33
37
  this.args.setDirty();
34
38
  }
35
39
  }
@@ -4,8 +4,8 @@
4
4
  @question.isArchived
5
5
  "cfb-form-editor__question-list__item__archived"
6
6
  }}
7
- {{did-insert (fn (optional @on-register) this.elementId @question.slug)}}
8
- {{will-destroy (fn (optional @on-unregister) this.elementId @question.slug)}}
7
+ {{did-insert (fn (optional @onRegister) this.elementId @question.slug)}}
8
+ {{will-destroy (fn (optional @onUnregister) this.elementId @question.slug)}}
9
9
  ...attributes
10
10
  >
11
11
  <div class="uk-flex uk-flex-middle">
@@ -24,7 +24,7 @@
24
24
  data-test-remove-item
25
25
  uk-icon="minus"
26
26
  class="cfb-pointer uk-text-danger"
27
- {{on "click" (fn (optional @on-remove-question) @question)}}
27
+ {{on "click" (fn (optional @onRemoveQuestion) @question)}}
28
28
  >
29
29
  </i>
30
30
  {{else if (eq @mode "add")}}
@@ -33,7 +33,7 @@
33
33
  data-test-add-item
34
34
  uk-icon="plus"
35
35
  class="cfb-pointer uk-text-success"
36
- {{on "click" (fn (optional @on-add-question) @question)}}
36
+ {{on "click" (fn (optional @onAddQuestion) @question)}}
37
37
  >
38
38
  </i>
39
39
  {{/if}}
@@ -51,12 +51,12 @@ export default class CfbFormEditorQuestionListItem extends Component {
51
51
  @action
52
52
  editQuestion(question, e) {
53
53
  e.preventDefault();
54
- this.args["on-edit-question"]?.(question);
54
+ this.args.onEditQuestion?.(question);
55
55
  }
56
56
 
57
57
  @action
58
58
  clickForm(form, e) {
59
59
  e.preventDefault();
60
- this.args["on-click-form"]?.(form);
60
+ this.args.onClickForm?.(form);
61
61
  }
62
62
  }
@@ -76,12 +76,12 @@
76
76
  data-test-question-list-item={{item.node.slug}}
77
77
  @mode={{this.mode}}
78
78
  @question={{item.node}}
79
- @on-edit-question={{@on-edit-question}}
80
- @on-remove-question={{perform this.removeQuestion}}
81
- @on-add-question={{perform this.addQuestion}}
82
- @on-click-form={{@on-click-form}}
83
- @on-register={{this.registerChild}}
84
- @on-unregister={{this.unregisterChild}}
79
+ @onEditQuestion={{@onEditQuestion}}
80
+ @onRemoveQuestion={{perform this.removeQuestion}}
81
+ @onAddQuestion={{perform this.addQuestion}}
82
+ @onClickForm={{@onClickForm}}
83
+ @onRegister={{this.registerChild}}
84
+ @onUnregister={{this.unregisterChild}}
85
85
  />
86
86
  {{else}}
87
87
  <li
@@ -1,16 +1,16 @@
1
1
  import { action } from "@ember/object";
2
2
  import { run } from "@ember/runloop";
3
3
  import { inject as service } from "@ember/service";
4
+ import { macroCondition, isTesting } from "@embroider/macros";
4
5
  import Component from "@glimmer/component";
5
6
  import { tracked } from "@glimmer/tracking";
6
7
  import { queryManager } from "ember-apollo-client";
7
- import { optional } from "ember-composable-helpers/helpers/optional";
8
- import { timeout } from "ember-concurrency";
9
8
  import {
9
+ timeout,
10
10
  enqueueTask,
11
11
  lastValue,
12
12
  restartableTask,
13
- } from "ember-concurrency-decorators";
13
+ } from "ember-concurrency";
14
14
  import UIkit from "uikit";
15
15
 
16
16
  import addFormQuestionMutation from "@projectcaluma/ember-form-builder/gql/mutations/add-form-question.graphql";
@@ -56,8 +56,13 @@ export default class ComponentsCfbFormEditorQuestionList extends Component {
56
56
  const mode = this.mode;
57
57
  const search = mode !== "reorder" ? this.search : "";
58
58
 
59
- if (search) {
60
- yield timeout(500);
59
+ /* istanbul ignore next */
60
+ if (macroCondition(isTesting())) {
61
+ // no timeout
62
+ } else {
63
+ if (search) {
64
+ yield timeout(500);
65
+ }
61
66
  }
62
67
 
63
68
  if (mode === "add" && this.hasNextPage) {
@@ -148,7 +153,7 @@ export default class ComponentsCfbFormEditorQuestionList extends Component {
148
153
 
149
154
  this.questionTask.perform();
150
155
 
151
- optional([this.args["on-after-add-question"]])(question);
156
+ this.args.onAfterAddQuestion?.(question);
152
157
  } catch (e) {
153
158
  this.notification.danger(
154
159
  this.intl.t("caluma.form-builder.notification.form.add-question.error")
@@ -176,7 +181,7 @@ export default class ComponentsCfbFormEditorQuestionList extends Component {
176
181
  )
177
182
  );
178
183
 
179
- optional([this.args["on-after-remove-question"]])(question);
184
+ this.args.onAfterRemoveQuestion?.(question);
180
185
  } catch (e) {
181
186
  this.notification.danger(
182
187
  this.intl.t(
@@ -238,7 +243,7 @@ export default class ComponentsCfbFormEditorQuestionList extends Component {
238
243
  createNewQuestion(e) {
239
244
  e.preventDefault();
240
245
 
241
- this.args["on-create-question"]?.();
246
+ this.args.onCreateQuestion?.();
242
247
  this.setMode("reorder");
243
248
  }
244
249
  }