decidim-forms 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +65 -0
- data/Rakefile +3 -0
- data/app/assets/config/admin/decidim_forms_manifest.js +1 -0
- data/app/assets/config/decidim_forms_manifest.js +1 -0
- data/app/assets/images/decidim/surveys/icon.svg +19 -0
- data/app/assets/javascripts/decidim/forms/admin/auto_buttons_by_min_items.component.js.es6 +25 -0
- data/app/assets/javascripts/decidim/forms/admin/auto_select_options_by_total_items.component.js.es6 +23 -0
- data/app/assets/javascripts/decidim/forms/admin/forms.js.es6 +188 -0
- data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +65 -0
- data/app/assets/javascripts/decidim/forms/forms.js.es6 +20 -0
- data/app/assets/javascripts/decidim/forms/option_attached_inputs.component.js.es6 +32 -0
- data/app/commands/decidim/forms/admin/update_questionnaire.rb +86 -0
- data/app/commands/decidim/forms/answer_questionnaire.rb +54 -0
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +95 -0
- data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +84 -0
- data/app/forms/decidim/forms/admin/answer_option_form.rb +23 -0
- data/app/forms/decidim/forms/admin/question_form.rb +35 -0
- data/app/forms/decidim/forms/admin/questionnaire_form.rb +27 -0
- data/app/forms/decidim/forms/answer_choice_form.rb +15 -0
- data/app/forms/decidim/forms/answer_form.rb +69 -0
- data/app/forms/decidim/forms/questionnaire_form.rb +22 -0
- data/app/helpers/decidim/forms/admin/application_helper.rb +19 -0
- data/app/models/concerns/decidim/forms/has_questionnaire.rb +20 -0
- data/app/models/decidim/forms/answer.rb +45 -0
- data/app/models/decidim/forms/answer_choice.rb +15 -0
- data/app/models/decidim/forms/answer_option.rb +11 -0
- data/app/models/decidim/forms/application_record.rb +10 -0
- data/app/models/decidim/forms/question.rb +36 -0
- data/app/models/decidim/forms/questionnaire.rb +23 -0
- data/app/queries/decidim/forms/questionnaire_user_answers.rb +28 -0
- data/app/views/decidim/forms/admin/questionnaires/_answer_option.html.erb +44 -0
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +51 -0
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +115 -0
- data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +7 -0
- data/app/views/decidim/forms/questionnaires/_answer.html.erb +94 -0
- data/app/views/decidim/forms/questionnaires/show.html.erb +84 -0
- data/config/locales/ca.yml +79 -0
- data/config/locales/de.yml +79 -0
- data/config/locales/en.yml +79 -0
- data/config/locales/es-PY.yml +79 -0
- data/config/locales/es.yml +79 -0
- data/config/locales/eu.yml +79 -0
- data/config/locales/fi-pl.yml +79 -0
- data/config/locales/fi.yml +79 -0
- data/config/locales/fr.yml +79 -0
- data/config/locales/gl.yml +79 -0
- data/config/locales/hu.yml +79 -0
- data/config/locales/id-ID.yml +1 -0
- data/config/locales/it.yml +79 -0
- data/config/locales/nl.yml +79 -0
- data/config/locales/pl.yml +79 -0
- data/config/locales/pt-BR.yml +79 -0
- data/config/locales/pt.yml +79 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sv.yml +79 -0
- data/config/locales/tr-TR.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/db/migrate/20170511092231_create_decidim_forms_questionnaires.rb +15 -0
- data/db/migrate/20170515090916_create_decidim_forms_questions.rb +17 -0
- data/db/migrate/20170515144119_create_decidim_forms_answers.rb +15 -0
- data/db/migrate/20180405015012_create_decidim_forms_answer_options.rb +11 -0
- data/db/migrate/20180405015147_create_decidim_forms_answer_choices.rb +13 -0
- data/lib/decidim/forms.rb +13 -0
- data/lib/decidim/forms/admin.rb +9 -0
- data/lib/decidim/forms/admin_engine.rb +21 -0
- data/lib/decidim/forms/data_portability_user_answers_serializer.rb +37 -0
- data/lib/decidim/forms/engine.rb +14 -0
- data/lib/decidim/forms/test.rb +4 -0
- data/lib/decidim/forms/test/factories.rb +55 -0
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +524 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +626 -0
- data/lib/decidim/forms/user_answers_serializer.rb +29 -0
- data/lib/decidim/forms/version.rb +10 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ddc9adf1fa3236f89e1a63a9f08ec53daa5c9dca46be2de0ed5855334d9dc81a
|
4
|
+
data.tar.gz: 86eb4e54e761e29f03ba5d859bc7303bbaf9770c9f12bb6dda991bfd986e537a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19cd51ee2b4d5db3bf9857551e840e9a1f5fbfcc839f89e9c6fbbab3af2342d314f7c11baa2da05b8429e58d1cc2e1fb4bcb9ec4ba8e4c7a773a7918064ee1d7
|
7
|
+
data.tar.gz: c8da685c765fff9c6d7757a25efc573b3bed4bdb2d441f2c63cd7648224f5560729e6e42a2943740b7cd374689e851a2c0a6b5a613b660c6c9d621ba13c51d7c
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Decidim::Forms
|
2
|
+
|
3
|
+
This gem encapsulates the logic to create and manage forms, so it can be reused in other modules, like surveys and meetings.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your module's gemspec:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
s.add_dependency "decidim-forms", Decidim::YourModule.version
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
## Contributing
|
20
|
+
|
21
|
+
See [Decidim](https://github.com/decidim/decidim).
|
22
|
+
|
23
|
+
## License
|
24
|
+
|
25
|
+
See [Decidim](https://github.com/decidim/decidim).
|
26
|
+
|
27
|
+
## Seeds
|
28
|
+
|
29
|
+
Since questionnaires cannot exist without a real model we are not including specific seeds for this engine.
|
30
|
+
|
31
|
+
Other engines are free to include questionnaires on their seeds like this:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
Decidim::Forms::Questionnaire.new(
|
35
|
+
title: Decidim::Faker::Localized.paragraph,
|
36
|
+
description: Decidim::Faker::Localized.wrapped("<p>", "</p>") do
|
37
|
+
Decidim::Faker::Localized.paragraph(3)
|
38
|
+
end,
|
39
|
+
tos: Decidim::Faker::Localized.wrapped("<p>", "</p>") do
|
40
|
+
Decidim::Faker::Localized.paragraph(2)
|
41
|
+
end,
|
42
|
+
)
|
43
|
+
|
44
|
+
Decidim::Surveys::Survey.create!(component: component, questionnaire: questionnaire)
|
45
|
+
|
46
|
+
%w(short_answer long_answer).each do |text_question_type|
|
47
|
+
Decidim::Forms::Question.create!(
|
48
|
+
questionnaire: questionnaire,
|
49
|
+
body: Decidim::Faker::Localized.paragraph,
|
50
|
+
question_type: text_question_type
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
%w(single_option multiple_option).each do |multiple_choice_question_type|
|
55
|
+
question = Decidim::Forms::Question.create!(
|
56
|
+
questionnaire: questionnaire,
|
57
|
+
body: Decidim::Faker::Localized.paragraph,
|
58
|
+
question_type: multiple_choice_question_type
|
59
|
+
)
|
60
|
+
|
61
|
+
3.times do
|
62
|
+
question.answer_options.create!(body: Decidim::Faker::Localized.sentence)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
//= link decidim/forms/admin/forms.js
|
@@ -0,0 +1 @@
|
|
1
|
+
// = link decidim/forms/forms.js
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
3
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
4
|
+
viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
|
5
|
+
<path d="M27,28H9c-0.6,0-1-0.4-1-1V11c0-0.6,0.4-1,1-1h3.1c0.6,0,1,0.4,1,1v1.9h9.7V11c0-0.6,0.4-1,1-1H27c0.6,0,1,0.4,1,1v16
|
6
|
+
C28,27.6,27.6,28,27,28z M10,26h16V12h-1.2v1.9c0,0.6-0.4,1-1,1H12.1c-0.6,0-1-0.4-1-1V12H10V26z"/>
|
7
|
+
<g>
|
8
|
+
<g>
|
9
|
+
<path d="M21,11h-6c-0.6,0-1-0.4-1-1s0.4-1,1-1h6c0.6,0,1,0.4,1,1S21.6,11,21,11z"/>
|
10
|
+
</g>
|
11
|
+
<g>
|
12
|
+
<path d="M18.7,11h-1.5c-0.6,0-1-0.4-1-1V8c0-0.6,0.4-1,1-1h1.5c0.6,0,1,0.4,1,1v2C19.7,10.5,19.3,11,18.7,11z"/>
|
13
|
+
</g>
|
14
|
+
</g>
|
15
|
+
<g>
|
16
|
+
<path d="M18,36.1c-9.9,0-18-8.1-18-18s8.1-18,18-18s18,8.1,18,18S27.9,36.1,18,36.1z M18,2.1c-8.8,0-16,7.2-16,16s7.2,16,16,16
|
17
|
+
s16-7.2,16-16S26.8,2.1,18,2.1z"/>
|
18
|
+
</g>
|
19
|
+
</svg>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
((exports) => {
|
2
|
+
class AutoButtonsByMinItemsComponent {
|
3
|
+
constructor(options = {}) {
|
4
|
+
this.listSelector = options.listSelector;
|
5
|
+
this.minItems = options.minItems;
|
6
|
+
this.hideOnMinItemsOrLessSelector = options.hideOnMinItemsOrLessSelector;
|
7
|
+
|
8
|
+
this.run();
|
9
|
+
}
|
10
|
+
|
11
|
+
run() {
|
12
|
+
const $list = $(this.listSelector);
|
13
|
+
const $items = $list.find(this.hideOnMinItemsOrLessSelector);
|
14
|
+
|
15
|
+
if ($list.length <= this.minItems) {
|
16
|
+
$items.hide();
|
17
|
+
} else {
|
18
|
+
$items.show();
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
exports.DecidimAdmin = exports.DecidimAdmin || {};
|
24
|
+
exports.DecidimAdmin.AutoButtonsByMinItemsComponent = AutoButtonsByMinItemsComponent;
|
25
|
+
})(window);
|
data/app/assets/javascripts/decidim/forms/admin/auto_select_options_by_total_items.component.js.es6
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
((exports) => {
|
2
|
+
class AutoSelectOptionsByTotalItemsComponent {
|
3
|
+
constructor(options = {}) {
|
4
|
+
this.wrapperSelector = options.wrapperSelector;
|
5
|
+
this.selectSelector = options.selectSelector;
|
6
|
+
this.listSelector = options.listSelector;
|
7
|
+
}
|
8
|
+
|
9
|
+
run() {
|
10
|
+
const $list = $(this.listSelector);
|
11
|
+
const $selectField = $list.parents(this.wrapperSelector).find(this.selectSelector);
|
12
|
+
|
13
|
+
$selectField.find("option").slice(1).remove();
|
14
|
+
|
15
|
+
for (let idx = 2; idx <= $list.length; idx += 1) {
|
16
|
+
$(`<option value="${idx}">${idx}</option>`).appendTo($selectField);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
exports.DecidimAdmin = exports.DecidimAdmin || {};
|
22
|
+
exports.DecidimAdmin.AutoSelectOptionsByTotalItemsComponent = AutoSelectOptionsByTotalItemsComponent;
|
23
|
+
})(window);
|
@@ -0,0 +1,188 @@
|
|
1
|
+
// = require ./auto_buttons_by_min_items.component
|
2
|
+
// = require ./auto_select_options_by_total_items.component
|
3
|
+
|
4
|
+
((exports) => {
|
5
|
+
const { AutoLabelByPositionComponent, AutoButtonsByPositionComponent, AutoButtonsByMinItemsComponent, AutoSelectOptionsByTotalItemsComponent, createFieldDependentInputs, createDynamicFields, createSortList } = exports.DecidimAdmin;
|
6
|
+
const { createQuillEditor } = exports.Decidim;
|
7
|
+
|
8
|
+
const wrapperSelector = ".questionnaire-questions";
|
9
|
+
const fieldSelector = ".questionnaire-question";
|
10
|
+
const questionTypeSelector = "select[name$=\\[question_type\\]]";
|
11
|
+
const answerOptionFieldSelector = ".questionnaire-question-answer-option";
|
12
|
+
const answerOptionsWrapperSelector = ".questionnaire-question-answer-options";
|
13
|
+
const answerOptionRemoveFieldButtonSelector = ".remove-answer-option";
|
14
|
+
const maxChoicesWrapperSelector = ".questionnaire-question-max-choices";
|
15
|
+
|
16
|
+
const autoLabelByPosition = new AutoLabelByPositionComponent({
|
17
|
+
listSelector: ".questionnaire-question:not(.hidden)",
|
18
|
+
labelSelector: ".card-title span:first",
|
19
|
+
onPositionComputed: (el, idx) => {
|
20
|
+
$(el).find("input[name$=\\[position\\]]").val(idx);
|
21
|
+
}
|
22
|
+
});
|
23
|
+
|
24
|
+
const autoButtonsByPosition = new AutoButtonsByPositionComponent({
|
25
|
+
listSelector: ".questionnaire-question:not(.hidden)",
|
26
|
+
hideOnFirstSelector: ".move-up-question",
|
27
|
+
hideOnLastSelector: ".move-down-question"
|
28
|
+
});
|
29
|
+
|
30
|
+
const createAutoMaxChoicesByNumberOfAnswerOptions = (fieldId) => {
|
31
|
+
return new AutoSelectOptionsByTotalItemsComponent({
|
32
|
+
wrapperSelector: fieldSelector,
|
33
|
+
selectSelector: `${maxChoicesWrapperSelector} select`,
|
34
|
+
listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .questionnaire-question-answer-option:not(.hidden)`
|
35
|
+
})
|
36
|
+
};
|
37
|
+
|
38
|
+
const createAutoButtonsByMinItemsForAnswerOptions = (fieldId) => {
|
39
|
+
return new AutoButtonsByMinItemsComponent({
|
40
|
+
wrapperSelector: fieldSelector,
|
41
|
+
listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .questionnaire-question-answer-option:not(.hidden)`,
|
42
|
+
minItems: 2,
|
43
|
+
hideOnMinItemsOrLessSelector: answerOptionRemoveFieldButtonSelector
|
44
|
+
})
|
45
|
+
};
|
46
|
+
|
47
|
+
const createSortableList = () => {
|
48
|
+
createSortList(".questionnaire-questions-list:not(.published)", {
|
49
|
+
handle: ".question-divider",
|
50
|
+
placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
|
51
|
+
forcePlaceholderSize: true,
|
52
|
+
onSortUpdate: () => { autoLabelByPosition.run() }
|
53
|
+
});
|
54
|
+
};
|
55
|
+
|
56
|
+
const createDynamicFieldsForAnswerOptions = (fieldId) => {
|
57
|
+
const autoButtons = createAutoButtonsByMinItemsForAnswerOptions(fieldId);
|
58
|
+
const autoSelectOptions = createAutoMaxChoicesByNumberOfAnswerOptions(fieldId);
|
59
|
+
|
60
|
+
return createDynamicFields({
|
61
|
+
placeholderId: "questionnaire-question-answer-option-id",
|
62
|
+
wrapperSelector: `#${fieldId} ${answerOptionsWrapperSelector}`,
|
63
|
+
containerSelector: ".questionnaire-question-answer-options-list",
|
64
|
+
fieldSelector: answerOptionFieldSelector,
|
65
|
+
addFieldButtonSelector: ".add-answer-option",
|
66
|
+
removeFieldButtonSelector: answerOptionRemoveFieldButtonSelector,
|
67
|
+
onAddField: () => {
|
68
|
+
autoButtons.run();
|
69
|
+
autoSelectOptions.run();
|
70
|
+
},
|
71
|
+
onRemoveField: () => {
|
72
|
+
autoButtons.run();
|
73
|
+
autoSelectOptions.run();
|
74
|
+
}
|
75
|
+
});
|
76
|
+
};
|
77
|
+
|
78
|
+
const dynamicFieldsForAnswerOptions = {};
|
79
|
+
|
80
|
+
const isMultipleChoiceOption = ($selectField) => {
|
81
|
+
const value = $selectField.val();
|
82
|
+
|
83
|
+
return value === "single_option" || value === "multiple_option" || value === "sorting"
|
84
|
+
}
|
85
|
+
|
86
|
+
const setupInitialQuestionAttributes = ($target) => {
|
87
|
+
const fieldId = $target.attr("id");
|
88
|
+
const $fieldQuestionTypeSelect = $target.find(questionTypeSelector);
|
89
|
+
|
90
|
+
createFieldDependentInputs({
|
91
|
+
controllerField: $fieldQuestionTypeSelect,
|
92
|
+
wrapperSelector: fieldSelector,
|
93
|
+
dependentFieldsSelector: answerOptionsWrapperSelector,
|
94
|
+
dependentInputSelector: `${answerOptionFieldSelector} input`,
|
95
|
+
enablingCondition: ($field) => {
|
96
|
+
return isMultipleChoiceOption($field);
|
97
|
+
}
|
98
|
+
});
|
99
|
+
|
100
|
+
createFieldDependentInputs({
|
101
|
+
controllerField: $fieldQuestionTypeSelect,
|
102
|
+
wrapperSelector: fieldSelector,
|
103
|
+
dependentFieldsSelector: maxChoicesWrapperSelector,
|
104
|
+
dependentInputSelector: "select",
|
105
|
+
enablingCondition: ($field) => {
|
106
|
+
return $field.val() === "multiple_option"
|
107
|
+
}
|
108
|
+
});
|
109
|
+
|
110
|
+
dynamicFieldsForAnswerOptions[fieldId] = createDynamicFieldsForAnswerOptions(fieldId);
|
111
|
+
|
112
|
+
const dynamicFields = dynamicFieldsForAnswerOptions[fieldId];
|
113
|
+
|
114
|
+
const onQuestionTypeChange = () => {
|
115
|
+
if (isMultipleChoiceOption($fieldQuestionTypeSelect)) {
|
116
|
+
const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(answerOptionFieldSelector).length;
|
117
|
+
|
118
|
+
if (nOptions === 0) {
|
119
|
+
dynamicFields._addField();
|
120
|
+
dynamicFields._addField();
|
121
|
+
}
|
122
|
+
}
|
123
|
+
};
|
124
|
+
|
125
|
+
$fieldQuestionTypeSelect.on("change", onQuestionTypeChange);
|
126
|
+
|
127
|
+
onQuestionTypeChange();
|
128
|
+
}
|
129
|
+
|
130
|
+
const hideDeletedQuestion = ($target) => {
|
131
|
+
const inputDeleted = $target.find("input[name$=\\[deleted\\]]").val();
|
132
|
+
|
133
|
+
if (inputDeleted === "true") {
|
134
|
+
$target.addClass("hidden");
|
135
|
+
$target.hide();
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
createDynamicFields({
|
140
|
+
placeholderId: "questionnaire-question-id",
|
141
|
+
wrapperSelector: wrapperSelector,
|
142
|
+
containerSelector: ".questionnaire-questions-list",
|
143
|
+
fieldSelector: fieldSelector,
|
144
|
+
addFieldButtonSelector: ".add-question",
|
145
|
+
removeFieldButtonSelector: ".remove-question",
|
146
|
+
moveUpFieldButtonSelector: ".move-up-question",
|
147
|
+
moveDownFieldButtonSelector: ".move-down-question",
|
148
|
+
onAddField: ($field) => {
|
149
|
+
setupInitialQuestionAttributes($field);
|
150
|
+
createSortableList();
|
151
|
+
|
152
|
+
$field.find(".editor-container").each((idx, el) => {
|
153
|
+
createQuillEditor(el);
|
154
|
+
});
|
155
|
+
|
156
|
+
autoLabelByPosition.run();
|
157
|
+
autoButtonsByPosition.run();
|
158
|
+
},
|
159
|
+
onRemoveField: ($field) => {
|
160
|
+
autoLabelByPosition.run();
|
161
|
+
autoButtonsByPosition.run();
|
162
|
+
|
163
|
+
$field.find(answerOptionRemoveFieldButtonSelector).each((idx, el) => {
|
164
|
+
dynamicFieldsForAnswerOptions[$field.attr("id")]._removeField(el);
|
165
|
+
});
|
166
|
+
},
|
167
|
+
onMoveUpField: () => {
|
168
|
+
autoLabelByPosition.run();
|
169
|
+
autoButtonsByPosition.run();
|
170
|
+
},
|
171
|
+
onMoveDownField: () => {
|
172
|
+
autoLabelByPosition.run();
|
173
|
+
autoButtonsByPosition.run();
|
174
|
+
}
|
175
|
+
});
|
176
|
+
|
177
|
+
createSortableList();
|
178
|
+
|
179
|
+
$(fieldSelector).each((idx, el) => {
|
180
|
+
const $target = $(el);
|
181
|
+
|
182
|
+
hideDeletedQuestion($target);
|
183
|
+
setupInitialQuestionAttributes($target);
|
184
|
+
});
|
185
|
+
|
186
|
+
autoLabelByPosition.run();
|
187
|
+
autoButtonsByPosition.run();
|
188
|
+
})(window);
|
@@ -0,0 +1,65 @@
|
|
1
|
+
((exports) => {
|
2
|
+
class AutosortableCheckboxesComponent {
|
3
|
+
constructor(options = {}) {
|
4
|
+
this.wrapperField = options.wrapperField;
|
5
|
+
this._bindEvent();
|
6
|
+
this._run();
|
7
|
+
}
|
8
|
+
|
9
|
+
_run() {
|
10
|
+
$(this.wrapperField).find("input[type=checkbox]").each((idx, el) => {
|
11
|
+
const $parentLabel = $(el).parents("label");
|
12
|
+
|
13
|
+
if ($(el).is(":checked")) {
|
14
|
+
const $lastSorted = this.wrapperField.find("label.sorted").last();
|
15
|
+
|
16
|
+
if ($lastSorted.length > 0) {
|
17
|
+
$lastSorted.removeClass("last-sorted");
|
18
|
+
$parentLabel.insertAfter($lastSorted);
|
19
|
+
} else {
|
20
|
+
$parentLabel.insertBefore(this.wrapperField.find("label:first-child"));
|
21
|
+
}
|
22
|
+
|
23
|
+
$parentLabel.addClass("sorted");
|
24
|
+
$parentLabel.addClass("last-sorted");
|
25
|
+
} else {
|
26
|
+
const $lastUnsorted = this.wrapperField.find("label:not(.sorted)").last();
|
27
|
+
|
28
|
+
if ($lastUnsorted.length > 0) {
|
29
|
+
$parentLabel.insertBefore($lastUnsorted);
|
30
|
+
} else {
|
31
|
+
$parentLabel.insertAfter(this.wrapperField.find("label:last-child"));
|
32
|
+
}
|
33
|
+
|
34
|
+
$parentLabel.removeClass("sorted");
|
35
|
+
}
|
36
|
+
});
|
37
|
+
|
38
|
+
$(this.wrapperField).find("label").each((idx, el) => {
|
39
|
+
const $positionSelector = $(el).find(".position");
|
40
|
+
const $positionField = $(el).find("input[name$=\\[position\\]]");
|
41
|
+
|
42
|
+
if ($(el).hasClass("sorted")) {
|
43
|
+
$positionField.val(idx);
|
44
|
+
$positionField.prop("disabled", false);
|
45
|
+
$positionSelector.html(`${idx + 1}. `);
|
46
|
+
} else {
|
47
|
+
$positionField.val("");
|
48
|
+
$positionField.prop("disabled", true);
|
49
|
+
$positionSelector.html("");
|
50
|
+
}
|
51
|
+
});
|
52
|
+
}
|
53
|
+
|
54
|
+
_bindEvent() {
|
55
|
+
$(this.wrapperField).find("input[type=checkbox]").on("change", () => {
|
56
|
+
this._run();
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
exports.Decidim = exports.Decidim || {};
|
62
|
+
exports.Decidim.createAutosortableCheckboxes = (options) => {
|
63
|
+
return new AutosortableCheckboxesComponent(options);
|
64
|
+
};
|
65
|
+
})(window);
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// = require ./option_attached_inputs.component
|
2
|
+
// = require ./autosortable_checkboxes.component
|
3
|
+
|
4
|
+
((exports) => {
|
5
|
+
const { createOptionAttachedInputs, createAutosortableCheckboxes } = exports.Decidim;
|
6
|
+
|
7
|
+
$(".radio-button-collection, .check-box-collection").each((idx, el) => {
|
8
|
+
createOptionAttachedInputs({
|
9
|
+
wrapperField: $(el),
|
10
|
+
controllerFieldSelector: "input[type=radio], input[type=checkbox]",
|
11
|
+
dependentInputSelector: "input[type=text], input[type=hidden]"
|
12
|
+
});
|
13
|
+
});
|
14
|
+
|
15
|
+
$(".sortable-check-box-collection").each((idx, el) => {
|
16
|
+
createAutosortableCheckboxes({
|
17
|
+
wrapperField: $(el)
|
18
|
+
})
|
19
|
+
});
|
20
|
+
})(window);
|
@@ -0,0 +1,32 @@
|
|
1
|
+
((exports) => {
|
2
|
+
class OptionAttachedInputsComponent {
|
3
|
+
constructor(options = {}) {
|
4
|
+
this.wrapperField = options.wrapperField;
|
5
|
+
this.controllerFieldSelector = options.controllerFieldSelector;
|
6
|
+
this.dependentInputSelector = options.dependentInputSelector;
|
7
|
+
this.controllerSelector = this.wrapperField.find(this.controllerFieldSelector);
|
8
|
+
this._bindEvent();
|
9
|
+
this._run();
|
10
|
+
}
|
11
|
+
|
12
|
+
_run() {
|
13
|
+
this.controllerSelector.each((idx, el) => {
|
14
|
+
const $field = $(el);
|
15
|
+
const enabled = $field.is(":checked");
|
16
|
+
|
17
|
+
$field.parents("label").find(this.dependentInputSelector).prop("disabled", !enabled);
|
18
|
+
});
|
19
|
+
}
|
20
|
+
|
21
|
+
_bindEvent() {
|
22
|
+
this.controllerSelector.on("change", () => {
|
23
|
+
this._run();
|
24
|
+
});
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
exports.Decidim = exports.Decidim || {};
|
29
|
+
exports.Decidim.createOptionAttachedInputs = (options) => {
|
30
|
+
return new OptionAttachedInputsComponent(options);
|
31
|
+
};
|
32
|
+
})(window);
|