decidim-forms 0.24.3 → 0.25.0.rc1
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.
- checksums.yaml +4 -4
- data/app/cells/decidim/forms/step_navigation/show.erb +1 -1
- data/app/commands/decidim/forms/admin/update_questionnaire.rb +3 -2
- data/app/forms/decidim/forms/admin/question_form.rb +14 -0
- data/app/forms/decidim/forms/admin/question_matrix_row_form.rb +2 -2
- data/app/forms/decidim/forms/questionnaire_form.rb +1 -0
- data/app/helpers/decidim/forms/application_helper.rb +1 -0
- data/app/jobs/decidim/forms/export_questionnaire_answers_job.rb +1 -1
- data/app/models/decidim/forms/question.rb +4 -0
- data/app/models/decidim/forms/question_matrix_row.rb +3 -0
- data/app/packs/entrypoints/decidim_forms.js +4 -0
- data/app/packs/entrypoints/decidim_forms_admin.js +4 -0
- data/app/packs/entrypoints/decidim_questionnaire_answers_pdf.js +1 -0
- data/app/packs/entrypoints/decidim_questionnaire_answers_pdf.scss +1 -0
- data/app/{assets/images/decidim/surveys/icon.svg → packs/images/decidim/surveys/decidim_surveys.svg} +0 -0
- data/app/packs/src/decidim/forms/admin/auto_buttons_by_min_items.component.js +20 -0
- data/app/packs/src/decidim/forms/admin/auto_select_options_by_total_items.component.js +18 -0
- data/app/packs/src/decidim/forms/admin/auto_select_options_from_url.component.js +35 -0
- data/app/{assets/javascripts/decidim/forms/admin/collapsible_questions.js.es6 → packs/src/decidim/forms/admin/collapsible_questions.js} +0 -0
- data/app/{assets/javascripts/decidim/forms/admin/forms.js.es6 → packs/src/decidim/forms/admin/forms.js} +16 -12
- data/app/packs/src/decidim/forms/admin/live_text_update.component.js +49 -0
- data/app/packs/src/decidim/forms/autosortable_checkboxes.component.js +83 -0
- data/app/packs/src/decidim/forms/display_conditions.component.js +200 -0
- data/app/{assets/javascripts/decidim/forms/forms.js.es6 → packs/src/decidim/forms/forms.js} +7 -7
- data/app/packs/src/decidim/forms/max_choices_alert.component.js +43 -0
- data/app/packs/src/decidim/forms/option_attached_inputs.component.js +31 -0
- data/app/{assets → packs}/stylesheets/decidim/forms/forms.scss +5 -0
- data/app/{assets → packs}/stylesheets/decidim/forms/questionnaire-answers-pdf.scss +0 -0
- data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +2 -2
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +5 -3
- data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +1 -0
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +1 -1
- data/app/views/decidim/forms/questionnaires/answers/_matrix_multiple.html.erb +1 -1
- data/app/views/decidim/forms/questionnaires/answers/_matrix_single.html.erb +1 -1
- data/app/views/decidim/forms/questionnaires/show.html.erb +7 -1
- data/app/views/layouts/decidim/forms/admin/questionnaires/questionnaire_answers.html.erb +1 -1
- data/config/assets.rb +10 -0
- data/config/locales/fr-LU.yml +178 -0
- data/config/locales/hu.yml +2 -0
- data/config/locales/it.yml +7 -0
- data/config/locales/ja.yml +8 -0
- data/config/locales/lb-LU.yml +1 -0
- data/config/locales/pl.yml +2 -2
- data/config/locales/pt-BR.yml +93 -0
- data/db/migrate/20170515144119_create_decidim_forms_answers.rb +1 -2
- data/db/migrate/20210616153042_set_position_to_question_matrix_rows.rb +11 -0
- data/lib/decidim/forms/admin_engine.rb +0 -4
- data/lib/decidim/forms/engine.rb +2 -4
- data/lib/decidim/forms/test/factories.rb +39 -2
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +2 -0
- data/lib/decidim/forms/user_answers_serializer.rb +3 -0
- data/lib/decidim/forms/version.rb +1 -1
- metadata +35 -32
- data/app/assets/config/admin/decidim_forms_manifest.css +0 -3
- data/app/assets/config/admin/decidim_forms_manifest.js +0 -2
- data/app/assets/config/decidim_forms_manifest.css +0 -4
- data/app/assets/config/decidim_forms_manifest.js +0 -1
- data/app/assets/javascripts/decidim/forms/admin/auto_buttons_by_min_items.component.js.es6 +0 -25
- data/app/assets/javascripts/decidim/forms/admin/auto_select_options_by_total_items.component.js.es6 +0 -23
- data/app/assets/javascripts/decidim/forms/admin/auto_select_options_from_url.component.js.es6 +0 -40
- data/app/assets/javascripts/decidim/forms/admin/live_text_update.component.js.es6 +0 -52
- data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +0 -85
- data/app/assets/javascripts/decidim/forms/display_conditions.component.js.es6 +0 -203
- data/app/assets/javascripts/decidim/forms/max_choices_alert.component.js.es6 +0 -44
- data/app/assets/javascripts/decidim/forms/option_attached_inputs.component.js.es6 +0 -32
- data/config/locales/ja-JP.yml +0 -170
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d6a89d9130f70f34752048e73c5326d2da80e71f44e3da9c70115ec4cb9bbc0
|
|
4
|
+
data.tar.gz: be2fcaae153df09c162d1b9878c1163042f4802968bd4626ce1a279a67830b20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f58f802c1bb233ef62fd86cb08fd382c4e67f95417759371f0685ae5ad92bdf00576d3b57854e3da73665822ea13918ef522aa980af1ea8fc918717df66b4a2
|
|
7
|
+
data.tar.gz: c6b772ae8123a55dc7f36ea3297984a91f7f81ba19fcb1fce27d1378e007c279a3edeec7970f984ebac720ae637f81a542ead1fb5bcfdbab4e8392fc5da4de11
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<a></a>
|
|
4
4
|
<% else %>
|
|
5
5
|
<%= link_to(
|
|
6
|
-
icon("caret-left", class: "icon--small", role: "img") + " " + t("decidim.forms.step_navigation.show.back"),
|
|
6
|
+
icon("caret-left", class: "icon--small", role: "img", "aria-hidden": true) + " " + t("decidim.forms.step_navigation.show.back"),
|
|
7
7
|
"#",
|
|
8
8
|
class: "hollow secondary",
|
|
9
9
|
data: {
|
|
@@ -78,9 +78,10 @@ module Decidim
|
|
|
78
78
|
update_nested_model(form_display_condition, display_condition_attributes, question.display_conditions)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
form_question.
|
|
81
|
+
form_question.matrix_rows_by_position.each_with_index do |form_matrix_row, idx|
|
|
82
82
|
matrix_row_attributes = {
|
|
83
|
-
body: form_matrix_row.body
|
|
83
|
+
body: form_matrix_row.body,
|
|
84
|
+
position: form_matrix_row.position || idx
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
update_nested_model(form_matrix_row, matrix_row_attributes, question.matrix_rows)
|
|
@@ -42,6 +42,20 @@ module Decidim
|
|
|
42
42
|
question_type == Decidim::Forms::Question::SEPARATOR_TYPE
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def matrix_rows_by_position
|
|
46
|
+
matrix_rows.sort do |a, b|
|
|
47
|
+
if a.position && b.position
|
|
48
|
+
a.position <=> b.position
|
|
49
|
+
elsif a.position
|
|
50
|
+
-1
|
|
51
|
+
elsif b.position
|
|
52
|
+
1
|
|
53
|
+
else
|
|
54
|
+
0
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
45
59
|
private
|
|
46
60
|
|
|
47
61
|
def matrix?
|
|
@@ -7,12 +7,12 @@ module Decidim
|
|
|
7
7
|
class QuestionMatrixRowForm < Decidim::Form
|
|
8
8
|
include TranslatableAttributes
|
|
9
9
|
|
|
10
|
-
attribute :position, Integer
|
|
10
|
+
attribute :position, Integer
|
|
11
11
|
attribute :deleted, Boolean, default: false
|
|
12
12
|
|
|
13
13
|
translatable_attribute :body, String
|
|
14
14
|
|
|
15
|
-
validates :position, numericality: { greater_than_or_equal_to: 0 }
|
|
15
|
+
validates :position, numericality: { greater_than_or_equal_to: 0 }, if: -> { position.present? }
|
|
16
16
|
validates :body, translatable_presence: true, unless: :deleted
|
|
17
17
|
|
|
18
18
|
def to_param
|
|
@@ -8,6 +8,7 @@ module Decidim
|
|
|
8
8
|
# important not to use the same word here to avoid querying all the entries, resulting in a high performance penalty
|
|
9
9
|
attribute :responses, Array[AnswerForm]
|
|
10
10
|
attribute :user_group_id, Integer
|
|
11
|
+
attribute :public_participation, Boolean, default: false
|
|
11
12
|
|
|
12
13
|
attribute :tos_agreement, Boolean
|
|
13
14
|
|
|
@@ -10,6 +10,9 @@ module Decidim
|
|
|
10
10
|
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
|
|
11
11
|
|
|
12
12
|
delegate :answer_options, :mandatory, :max_choices, to: :question
|
|
13
|
+
|
|
14
|
+
scope :by_position, -> { order(:position) }
|
|
15
|
+
default_scope { by_position }
|
|
13
16
|
end
|
|
14
17
|
end
|
|
15
18
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "entrypoints/decidim_questionnaire_answers_pdf.scss"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "stylesheets/decidim/forms/questionnaire-answers-pdf.scss";
|
data/app/{assets/images/decidim/surveys/icon.svg → packs/images/decidim/surveys/decidim_surveys.svg}
RENAMED
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default class AutoButtonsByMinItemsComponent {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this.listSelector = options.listSelector;
|
|
4
|
+
this.minItems = options.minItems;
|
|
5
|
+
this.hideOnMinItemsOrLessSelector = options.hideOnMinItemsOrLessSelector;
|
|
6
|
+
|
|
7
|
+
this.run();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
run() {
|
|
11
|
+
const $list = $(this.listSelector);
|
|
12
|
+
const $items = $list.find(this.hideOnMinItemsOrLessSelector);
|
|
13
|
+
|
|
14
|
+
if ($list.length <= this.minItems) {
|
|
15
|
+
$items.hide();
|
|
16
|
+
} else {
|
|
17
|
+
$items.show();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default class AutoSelectOptionsByTotalItemsComponent {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this.wrapperSelector = options.wrapperSelector;
|
|
4
|
+
this.selectSelector = options.selectSelector;
|
|
5
|
+
this.listSelector = options.listSelector;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
run() {
|
|
9
|
+
const $list = $(this.listSelector);
|
|
10
|
+
const $selectField = $list.parents(this.wrapperSelector).find(this.selectSelector);
|
|
11
|
+
|
|
12
|
+
$selectField.find("option").slice(1).remove();
|
|
13
|
+
|
|
14
|
+
for (let idx = 2; idx <= $list.length; idx += 1) {
|
|
15
|
+
$(`<option value="${idx}">${idx}</option>`).appendTo($selectField);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export default class AutoSelectOptionsFromUrl {
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this.$source = options.source;
|
|
4
|
+
this.$select = options.select;
|
|
5
|
+
this.sourceToParams = options.sourceToParams;
|
|
6
|
+
this.run();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
run() {
|
|
10
|
+
this.$source.on("change", this._onSourceChange.bind(this));
|
|
11
|
+
this._onSourceChange();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
_onSourceChange() {
|
|
15
|
+
const select = this.$select;
|
|
16
|
+
const params = this.sourceToParams(this.$source);
|
|
17
|
+
const url = this.$source.data("url");
|
|
18
|
+
|
|
19
|
+
$.getJSON(url, params, function (data) {
|
|
20
|
+
select.find("option:not([value=''])").remove();
|
|
21
|
+
const selectedValue = select.data("selected");
|
|
22
|
+
|
|
23
|
+
data.forEach((option) => {
|
|
24
|
+
let optionElement = $(`<option value="${option.id}">${option.body}</option>`).appendTo(select);
|
|
25
|
+
if (option.id === selectedValue) {
|
|
26
|
+
optionElement.attr("selected", true);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (selectedValue) {
|
|
31
|
+
select.val(selectedValue);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
File without changes
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
/* eslint-disable require-jsdoc */
|
|
3
|
+
|
|
4
|
+
import AutoButtonsByMinItemsComponent from "src/decidim/forms/admin/auto_buttons_by_min_items.component"
|
|
5
|
+
import AutoSelectOptionsByTotalItemsComponent from "src/decidim/forms/admin/auto_select_options_by_total_items.component"
|
|
6
|
+
import AutoSelectOptionsFromUrl from "src/decidim/forms/admin/auto_select_options_from_url.component"
|
|
7
|
+
import createLiveTextUpdateComponent from "src/decidim/forms/admin/live_text_update.component"
|
|
8
|
+
import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_position.component"
|
|
9
|
+
import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
|
|
10
|
+
import createSortList from "src/decidim/admin/sort_list.component"
|
|
11
|
+
import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
|
|
12
|
+
import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
|
|
13
|
+
import createQuillEditor from "src/decidim/editor"
|
|
14
|
+
|
|
15
|
+
export default function createEditableForm() {
|
|
12
16
|
const wrapperSelector = ".questionnaire-questions";
|
|
13
17
|
const fieldSelector = ".questionnaire-question";
|
|
14
18
|
const questionTypeSelector = "select[name$=\\[question_type\\]]";
|
|
@@ -59,7 +63,7 @@
|
|
|
59
63
|
listSelector: ".questionnaire-question:not(.hidden)",
|
|
60
64
|
labelSelector: ".card-title span:first",
|
|
61
65
|
onPositionComputed: (el, idx) => {
|
|
62
|
-
$(el).find("input[name$=\\[position\\]]").val(idx);
|
|
66
|
+
$(el).find("input[name$=\\[position\\]]:not([name*=\\[matrix_rows\\]])").val(idx);
|
|
63
67
|
|
|
64
68
|
autoButtonsByPosition.run();
|
|
65
69
|
|
|
@@ -429,4 +433,4 @@
|
|
|
429
433
|
|
|
430
434
|
autoLabelByPosition.run();
|
|
431
435
|
autoButtonsByPosition.run();
|
|
432
|
-
}
|
|
436
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable require-jsdoc */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This component allows for an element's text value to be updated with the value
|
|
5
|
+
* of an input whenever this input's value is changed.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} options
|
|
8
|
+
*
|
|
9
|
+
* Available options:
|
|
10
|
+
* {string} `inputSelector`: The query selector to locate the input element
|
|
11
|
+
* {string} `targetSelector`: The query selector to locate the target element
|
|
12
|
+
* {number} `maxLength`: The maximum characters from the input value to be displayed in the target
|
|
13
|
+
* {string} `omission`: The string used to shorten the value to the given maxLength (e.g. "...")
|
|
14
|
+
* {string} `placeholder`: The string to be displayed in the target element when the input has no value
|
|
15
|
+
*/
|
|
16
|
+
class LiveTextUpdateComponent {
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.inputSelector = options.inputSelector;
|
|
19
|
+
this.targetSelector = options.targetSelector;
|
|
20
|
+
this.maxLength = options.maxLength;
|
|
21
|
+
this.omission = options.omission;
|
|
22
|
+
this.placeholder = options.placeholder;
|
|
23
|
+
this._bindEvent();
|
|
24
|
+
this._run();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_run() {
|
|
28
|
+
const $input = $(this.inputSelector);
|
|
29
|
+
const $target = $(this.targetSelector);
|
|
30
|
+
|
|
31
|
+
let text = $input.val() || this.placeholder;
|
|
32
|
+
|
|
33
|
+
// truncate string
|
|
34
|
+
if (text.length > this.maxLength) {
|
|
35
|
+
text = text.substring(0, this.maxLength - this.omission.length) + this.omission;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
$target.text(text);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_bindEvent() {
|
|
42
|
+
const $input = $(this.inputSelector);
|
|
43
|
+
$input.on("change", this._run.bind(this));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function createLiveTextUpdateComponent(options) {
|
|
48
|
+
return new LiveTextUpdateComponent(options);
|
|
49
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* eslint-disable require-jsdoc */
|
|
2
|
+
/* eslint-disable no-ternary */
|
|
3
|
+
|
|
4
|
+
class AutosortableCheckboxesComponent {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.wrapperField = options.wrapperField;
|
|
7
|
+
this._bindEvent();
|
|
8
|
+
this._order();
|
|
9
|
+
this._normalize();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Order by position
|
|
13
|
+
_order() {
|
|
14
|
+
const max = $(this.wrapperField).find(".collection-input").length;
|
|
15
|
+
$(this.wrapperField).find(".collection-input").each((idx, el) => {
|
|
16
|
+
const $positionField = $(el).find("input[name$=\\[position\\]]");
|
|
17
|
+
const position = $positionField.val()
|
|
18
|
+
? parseInt($positionField.val(), 10)
|
|
19
|
+
: max;
|
|
20
|
+
|
|
21
|
+
let $next = $(el).next();
|
|
22
|
+
while ($next.length > 0) {
|
|
23
|
+
const $nextPositionField = $next.find("input[name$=\\[position\\]]");
|
|
24
|
+
const nextPosition = $nextPositionField.val()
|
|
25
|
+
? parseInt($nextPositionField.val(), 10)
|
|
26
|
+
: max;
|
|
27
|
+
|
|
28
|
+
if (position > nextPosition) {
|
|
29
|
+
$next.insertBefore($(el));
|
|
30
|
+
}
|
|
31
|
+
$next = $next.next();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_findLastPosition() {
|
|
37
|
+
let lastPosition = 0;
|
|
38
|
+
$(this.wrapperField).find(".collection-input").each((idx, el) => {
|
|
39
|
+
const $positionField = $(el).find("input[name$=\\[position\\]]");
|
|
40
|
+
const position = parseInt($positionField.val(), 10);
|
|
41
|
+
if (position > lastPosition) {
|
|
42
|
+
lastPosition = position;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return lastPosition;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_normalize() {
|
|
49
|
+
$(this.wrapperField).find(".collection-input .position").each((idx, el) => {
|
|
50
|
+
const $positionField = $(el).parent().find("input[name$=\\[position\\]]");
|
|
51
|
+
if ($positionField.val()) {
|
|
52
|
+
$positionField.val(idx);
|
|
53
|
+
$positionField.prop("disabled", false);
|
|
54
|
+
$(el).html(`${idx + 1}. `);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_bindEvent() {
|
|
60
|
+
$(this.wrapperField).find("input[type=checkbox]").on("change", (el) => {
|
|
61
|
+
const $parentLabel = $(el.target).parents("label");
|
|
62
|
+
const $positionSelector = $parentLabel.find(".position");
|
|
63
|
+
const $positionField = $parentLabel.find("input[name$=\\[position\\]]");
|
|
64
|
+
const lastPosition = this._findLastPosition();
|
|
65
|
+
|
|
66
|
+
if (el.target.checked) {
|
|
67
|
+
$positionField.val(lastPosition + 1);
|
|
68
|
+
$positionField.prop("disabled", false);
|
|
69
|
+
$positionSelector.html(lastPosition + 1);
|
|
70
|
+
} else {
|
|
71
|
+
$positionField.val("");
|
|
72
|
+
$positionField.prop("disabled", true);
|
|
73
|
+
$positionSelector.html("");
|
|
74
|
+
}
|
|
75
|
+
this._order();
|
|
76
|
+
this._normalize();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default function createAutosortableCheckboxes(options) {
|
|
82
|
+
return new AutosortableCheckboxesComponent(options);
|
|
83
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/* eslint-disable no-ternary, no-plusplus, require-jsdoc */
|
|
2
|
+
|
|
3
|
+
class DisplayCondition {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.wrapperField = options.wrapperField;
|
|
6
|
+
this.type = options.type;
|
|
7
|
+
this.conditionQuestion = options.conditionQuestion;
|
|
8
|
+
this.answerOption = options.answerOption;
|
|
9
|
+
this.mandatory = options.mandatory;
|
|
10
|
+
this.value = options.value;
|
|
11
|
+
this.onFulfilled = options.onFulfilled;
|
|
12
|
+
this.bindEvent();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
bindEvent() {
|
|
16
|
+
this.checkCondition();
|
|
17
|
+
this.getInputsToListen().on("change", this.checkCondition.bind(this));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getInputValue() {
|
|
21
|
+
const $conditionWrapperField = $(`.question[data-question-id='${this.conditionQuestion}']`);
|
|
22
|
+
const $textInput = $conditionWrapperField.find("textarea, input[type='text']:not([name$=\\[custom_body\\]])");
|
|
23
|
+
|
|
24
|
+
if ($textInput.length) {
|
|
25
|
+
return $textInput.val();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let multipleInput = [];
|
|
29
|
+
|
|
30
|
+
$conditionWrapperField.find(".radio-button-collection, .check-box-collection").find(".collection-input").each((idx, el) => {
|
|
31
|
+
const $input = $(el).find("input[name$=\\[body\\]]");
|
|
32
|
+
const checked = $input.is(":checked");
|
|
33
|
+
|
|
34
|
+
if (checked) {
|
|
35
|
+
const text = $(el).find("input[name$=\\[custom_body\\]]").val();
|
|
36
|
+
const value = $input.val();
|
|
37
|
+
const id = $(el).find("input[name$=\\[answer_option_id\\]]").val();
|
|
38
|
+
|
|
39
|
+
multipleInput.push({ id, value, text });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return multipleInput;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getInputsToListen() {
|
|
47
|
+
const $conditionWrapperField = $(`.question[data-question-id='${this.conditionQuestion}']`);
|
|
48
|
+
const $textInput = $conditionWrapperField.find("textarea, input[type='text']:not([name$=\\[custom_body\\]])");
|
|
49
|
+
|
|
50
|
+
if ($textInput.length) {
|
|
51
|
+
return $textInput;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return $conditionWrapperField.find(".collection-input").find("input:not([type='hidden'])");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
checkAnsweredCondition(value) {
|
|
58
|
+
if (typeof (value) !== "object") {
|
|
59
|
+
return Boolean(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Boolean(value.some((it) => it.value));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
checkNotAnsweredCondition(value) {
|
|
66
|
+
return !this.checkAnsweredCondition(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
checkEqualCondition(value) {
|
|
70
|
+
if (value.length) {
|
|
71
|
+
return value.some((it) => it.id === this.answerOption.toString());
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
checkNotEqualCondition(value) {
|
|
77
|
+
if (value.length) {
|
|
78
|
+
return value.every((it) => it.id !== this.answerOption.toString());
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
checkMatchCondition(value) {
|
|
84
|
+
let regexp = new RegExp(this.value, "i");
|
|
85
|
+
|
|
86
|
+
if (typeof (value) !== "object") {
|
|
87
|
+
return Boolean(value.match(regexp));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return value.some(function (it) {
|
|
91
|
+
return it.text
|
|
92
|
+
? it.text.match(regexp)
|
|
93
|
+
: it.value.match(regexp)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
checkCondition() {
|
|
98
|
+
const value = this.getInputValue();
|
|
99
|
+
let fulfilled = false;
|
|
100
|
+
|
|
101
|
+
switch (this.type) {
|
|
102
|
+
case "answered":
|
|
103
|
+
fulfilled = this.checkAnsweredCondition(value);
|
|
104
|
+
break;
|
|
105
|
+
case "not_answered":
|
|
106
|
+
fulfilled = this.checkNotAnsweredCondition(value);
|
|
107
|
+
break;
|
|
108
|
+
case "equal":
|
|
109
|
+
fulfilled = this.checkEqualCondition(value);
|
|
110
|
+
break;
|
|
111
|
+
case "not_equal":
|
|
112
|
+
fulfilled = this.checkNotEqualCondition(value);
|
|
113
|
+
break;
|
|
114
|
+
case "match":
|
|
115
|
+
fulfilled = this.checkMatchCondition(value);
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
fulfilled = false;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.onFulfilled(fulfilled);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class DisplayConditionsComponent {
|
|
127
|
+
constructor(options = {}) {
|
|
128
|
+
this.wrapperField = options.wrapperField;
|
|
129
|
+
this.conditions = {};
|
|
130
|
+
this.showCount = 0;
|
|
131
|
+
this.initializeConditions();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
initializeConditions() {
|
|
135
|
+
const $conditionElements = this.wrapperField.find(".display-condition");
|
|
136
|
+
|
|
137
|
+
$conditionElements.each((idx, el) => {
|
|
138
|
+
const $condition = $(el);
|
|
139
|
+
const id = $condition.data("id");
|
|
140
|
+
this.conditions[id] = {};
|
|
141
|
+
|
|
142
|
+
this.conditions[id] = new DisplayCondition({
|
|
143
|
+
wrapperField: this.wrapperField,
|
|
144
|
+
type: $condition.data("type"),
|
|
145
|
+
conditionQuestion: $condition.data("condition"),
|
|
146
|
+
answerOption: $condition.data("option"),
|
|
147
|
+
mandatory: $condition.data("mandatory"),
|
|
148
|
+
value: $condition.data("value"),
|
|
149
|
+
onFulfilled: (fulfilled) => {
|
|
150
|
+
this.onFulfilled(id, fulfilled);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
mustShow() {
|
|
157
|
+
const conditions = Object.values(this.conditions);
|
|
158
|
+
const mandatoryConditions = conditions.filter((condition) => condition.mandatory);
|
|
159
|
+
const nonMandatoryConditions = conditions.filter((condition) => !condition.mandatory);
|
|
160
|
+
|
|
161
|
+
if (mandatoryConditions.length) {
|
|
162
|
+
return mandatoryConditions.every((condition) => condition.fulfilled);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return nonMandatoryConditions.some((condition) => condition.fulfilled);
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
onFulfilled(id, fulfilled) {
|
|
170
|
+
this.conditions[id].fulfilled = fulfilled;
|
|
171
|
+
|
|
172
|
+
if (this.mustShow()) {
|
|
173
|
+
this.showQuestion();
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
this.hideQuestion();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
showQuestion() {
|
|
181
|
+
this.wrapperField.fadeIn();
|
|
182
|
+
this.wrapperField.find("input, textarea").prop("disabled", null);
|
|
183
|
+
this.showCount++;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
hideQuestion() {
|
|
187
|
+
if (this.showCount) {
|
|
188
|
+
this.wrapperField.fadeOut();
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.wrapperField.hide();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.wrapperField.find("input, textarea").prop("disabled", "disabled");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default function createDisplayConditions(options) {
|
|
199
|
+
return new DisplayConditionsComponent(options);
|
|
200
|
+
}
|