activeadmin_select_many 0.3.2 → 0.4.2
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 +5 -5
- data/README.md +28 -19
- data/Rakefile +15 -2
- data/app/assets/javascripts/activeadmin/select_many.js +113 -112
- data/lib/activeadmin/select_many.rb +2 -0
- data/lib/activeadmin/select_many/engine.rb +2 -0
- data/lib/activeadmin/select_many/version.rb +3 -1
- data/lib/activeadmin_select_many.rb +2 -0
- data/lib/formtastic/inputs/select_many_input.rb +50 -33
- data/lib/formtastic/inputs/select_one_input.rb +11 -16
- metadata +22 -13
- data/.gitignore +0 -3
- data/Gemfile +0 -4
- data/activeadmin_select_many.gemspec +0 -19
- data/screenshot.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c6ef718f33d654cc19c1cdff127cc0843e8e28b3e7b47a1e4a79ac12f479c7cd
|
4
|
+
data.tar.gz: 1bc05acfaf8138a18b3bb5abff9a9144dee8742d2db5389ca33f69f2097a4e44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7090832972fec99b68d357353ea4523eb12a4d6d3c0a861466425b74c6f43f8e93b2164bf27e274940ca041f0d9a07c320bc21362933c500329a801e2910a864
|
7
|
+
data.tar.gz: b6c837d30c6befb48fb487fc9ad5a1ae6fe9a02442141272076e256c42b9bc6ca2ee58a0d0aa8b7aeabde37eec4702faf91e0d1492cc9a4fd43ef1eaf18f18d7
|
data/README.md
CHANGED
@@ -1,22 +1,27 @@
|
|
1
|
-
# ActiveAdmin Select Many
|
1
|
+
# ActiveAdmin Select Many
|
2
|
+
[](https://badge.fury.io/rb/activeadmin_select_many)
|
3
|
+
[](https://rubygems.org/gems/activeadmin_select_many)
|
4
|
+
[](https://github.com/blocknotes/activeadmin_select_many/actions/workflows/specs.yml)
|
2
5
|
|
3
|
-
An Active Admin plugin which improves one-to-many / many-to-many / many-to-one associations selection using 2 new inputs: **select_many** and **select_one** (jQuery required)
|
6
|
+
An Active Admin plugin which improves one-to-many / many-to-many / many-to-one associations selection using 2 new inputs: **select_many** and **select_one** (jQuery required).
|
4
7
|
|
5
8
|
Features for *select_many*:
|
6
|
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
9
|
+
|
10
|
+
- search box;
|
11
|
+
- available items on the left, selected items on the right;
|
12
|
+
- local/remote collections;
|
13
|
+
- double click to add/remove items;
|
14
|
+
- sortable (with up/down buttons);
|
15
|
+
- key bindings to improve accessibility.
|
12
16
|
|
13
17
|
Features for *select_one*:
|
14
|
-
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
18
|
+
|
19
|
+
- search box;
|
20
|
+
- selected items on the right;
|
21
|
+
- remote collections;
|
22
|
+
- counter of items found;
|
23
|
+
- can be used as filter;
|
24
|
+
- key bindings to improve accessibility.
|
20
25
|
|
21
26
|

|
22
27
|
|
@@ -47,7 +52,7 @@ Features for *select_one*:
|
|
47
52
|
- **size**: number of rows of both the selects (default: 4)
|
48
53
|
- **sortable**: set to true to enable sortable buttons (default: not set)
|
49
54
|
|
50
|
-
##
|
55
|
+
## Examples with select_many
|
51
56
|
|
52
57
|
Add to ActiveAdmin model config, in *form* block.
|
53
58
|
|
@@ -85,7 +90,7 @@ ActiveAdmin.register Tag do
|
|
85
90
|
end
|
86
91
|
```
|
87
92
|
|
88
|
-
##
|
93
|
+
## Examples with select_one
|
89
94
|
|
90
95
|
In a form:
|
91
96
|
|
@@ -95,15 +100,19 @@ As filter:
|
|
95
100
|
|
96
101
|
`filter :article_id_eq, as: :select_one, filter_form: true, placeholder: 'Search...', search_param: 'title_contains', member_label: 'title', remote_collection: '/admin/articles.json'`
|
97
102
|
|
103
|
+
## Notes
|
104
|
+
|
105
|
+
- To use this plugins with ActiveAdmin 1.x please use the version 0.3.4
|
106
|
+
|
98
107
|
## Do you like it? Star it!
|
99
108
|
|
100
|
-
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
109
|
+
If you use this component just star it. A developer is more motivated to improve a project when there is some interest. My other [Active Admin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source).
|
101
110
|
|
102
|
-
|
111
|
+
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
|
103
112
|
|
104
113
|
## Contributors
|
105
114
|
|
106
|
-
- [Mattia Roccoberton](http://blocknot.es)
|
115
|
+
- [Mattia Roccoberton](http://blocknot.es): author
|
107
116
|
|
108
117
|
## License
|
109
118
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
# t.ruby_opts = %w[-w]
|
10
|
+
t.rspec_opts = ['--color', '--format documentation']
|
11
|
+
end
|
12
|
+
|
13
|
+
task default: :spec
|
14
|
+
rescue LoadError
|
15
|
+
puts '! LoadError: no RSpec available'
|
16
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
function smActivate(
|
2
|
-
if(
|
3
|
-
var parent = $(this).closest(
|
1
|
+
function smActivate(target) {
|
2
|
+
if(target.tagName.toLowerCase() == 'option') {
|
3
|
+
var parent = $(this).closest('.select_many');
|
4
4
|
var opt = $(target);
|
5
|
-
var dst = parent.find(
|
6
|
-
dst.append(
|
5
|
+
var dst = parent.find($(this).data('select') == 'src' ? '[data-select="dst"]' : '[data-select="src"]');
|
6
|
+
dst.append($('<option>', { value: opt.val(), text: opt.text() }));
|
7
7
|
opt.remove();
|
8
|
-
smUpdateValues(
|
8
|
+
smUpdateValues(parent);
|
9
9
|
}
|
10
10
|
}
|
11
11
|
|
12
|
-
function smDebounce(
|
12
|
+
function smDebounce(func, wait, immediate) {
|
13
13
|
var timeout;
|
14
14
|
return function() {
|
15
15
|
var context = this, args = arguments;
|
@@ -24,197 +24,198 @@ function smDebounce( func, wait, immediate ) {
|
|
24
24
|
};
|
25
25
|
};
|
26
26
|
|
27
|
-
function smUpdateValues(
|
28
|
-
var cnt = 0, values = parent.find(
|
27
|
+
function smUpdateValues(parent) {
|
28
|
+
var cnt = 0, values = parent.find('.values');
|
29
29
|
values.empty();
|
30
|
-
parent.find(
|
31
|
-
values.append(
|
30
|
+
parent.find('[data-select="dst"] option').each(function() {
|
31
|
+
values.append($('<input>', { type: 'hidden', name: values.data('name'), value: $(this).val() }));
|
32
32
|
cnt++;
|
33
33
|
});
|
34
|
-
if(
|
35
|
-
parent.find(
|
34
|
+
if(cnt == 0) values.append($('<input>', { type: 'hidden', name: values.data('name') }));
|
35
|
+
parent.find('.selected span').text(' [' + cnt + ']');
|
36
|
+
parent.find('.available span').text(' [' + parent.find('[data-select="src"] option').length + ']');
|
36
37
|
}
|
37
38
|
|
38
|
-
$(document).ready(
|
39
|
-
$('.select_many.input select').on(
|
40
|
-
$.proxy(
|
39
|
+
$(document).ready(function() {
|
40
|
+
$('.select_many.input select').on('dblclick', function(event) {
|
41
|
+
$.proxy(smActivate, $(this))(event.target);
|
41
42
|
});
|
42
43
|
|
43
44
|
// --- select_many ----------------------------------------------------------
|
44
|
-
var onLocalSelect = smDebounce(
|
45
|
+
var onLocalSelect = smDebounce(function() {
|
45
46
|
var cnt = 0, search = $(this).val().toLowerCase();
|
46
|
-
$(this).closest(
|
47
|
-
var found = $(this).text().toLowerCase().indexOf(
|
48
|
-
$(this).toggle(
|
49
|
-
if(
|
47
|
+
$(this).closest('.select_many').find('[data-select="src"] option').each(function() {
|
48
|
+
var found = $(this).text().toLowerCase().indexOf(search) >= 0;
|
49
|
+
$(this).toggle(found);
|
50
|
+
if(found) cnt++;
|
50
51
|
});
|
51
|
-
$(this).parent().find(
|
52
|
-
}, 250
|
52
|
+
$(this).parent().find('.available span').text(' [' + cnt + ']');
|
53
|
+
}, 250);
|
53
54
|
|
54
|
-
var onRemoteSelect = smDebounce(
|
55
|
+
var onRemoteSelect = smDebounce(function(event) {
|
55
56
|
var search = $(this).val().trim();
|
56
|
-
if(
|
57
|
-
$(this).data(
|
58
|
-
$(this).data(
|
57
|
+
if($(this).data('searching') != '1' && search && $(this).data('last-search') != search) {
|
58
|
+
$(this).data('searching', '1');
|
59
|
+
$(this).data('last-search', search);
|
59
60
|
var _this = $(this);
|
60
61
|
var data = {}
|
61
62
|
var text_key = $(this).data('text');
|
62
63
|
var value_key = $(this).data('value');
|
63
|
-
var counter_limit = $(this).data('counter-limit') ? Number(
|
64
|
+
var counter_limit = $(this).data('counter-limit') ? Number($(this).data('counter-limit')) : 0;
|
64
65
|
data['q['+$(this).data('search')+']'] = search;
|
65
66
|
$.ajax({
|
66
67
|
context: _this,
|
67
68
|
data: data,
|
68
|
-
url: $(this).data(
|
69
|
-
complete: function(
|
70
|
-
$(this).data(
|
69
|
+
url: $(this).data('remote-collection'),
|
70
|
+
complete: function(req, status) {
|
71
|
+
$(this).data('searching', '');
|
71
72
|
},
|
72
|
-
success: function(
|
73
|
-
var select = $(this).closest(
|
73
|
+
success: function(data, status, req) {
|
74
|
+
var select = $(this).closest('.select_many').find('[data-select="src"]');
|
74
75
|
select.empty();
|
75
|
-
data.forEach(
|
76
|
-
select.append(
|
76
|
+
data.forEach(function(item) {
|
77
|
+
select.append($('<option>', { value: item[value_key], text: item[text_key] }));
|
77
78
|
});
|
78
|
-
$(this).parent().find(
|
79
|
+
$(this).parent().find('.available span').text(' [' + ((counter_limit > 0 && data.length >= counter_limit) ? (counter_limit + '+') : data.length) + ']');
|
79
80
|
},
|
80
81
|
});
|
81
82
|
}
|
82
|
-
}, 400
|
83
|
+
}, 400);
|
83
84
|
|
84
|
-
$('.select_many.input .search-select').each(
|
85
|
+
$('.select_many.input .search-select').each(function() {
|
85
86
|
var parent = $(this).parent();
|
86
|
-
parent.find(
|
87
|
-
parent.find(
|
88
|
-
$(this).on(
|
89
|
-
if(
|
87
|
+
parent.find('.available').append('<span> [' + parent.find('[data-select="src"] option').length + ']</span>');
|
88
|
+
parent.find('.selected').append('<span> [' + parent.find('[data-select="dst"] option').length + ']</span>');
|
89
|
+
$(this).on('keydown', function(event) {
|
90
|
+
if(event.which == 13 || event.which == 40) { // enter or arrow down
|
90
91
|
event.preventDefault();
|
91
|
-
$(this).closest(
|
92
|
+
$(this).closest('.select_many').find('[data-select="src"]').focus();
|
92
93
|
}
|
93
|
-
else $.proxy(
|
94
|
+
else $.proxy($(this).data('remote-collection') ? onRemoteSelect : onLocalSelect, $(this))(event);
|
94
95
|
});
|
95
96
|
|
96
97
|
// --- key bindings -------------------------------------------------------
|
97
|
-
parent.find(
|
98
|
-
if(
|
98
|
+
parent.find('[data-select="src"]').on('keydown', function(event) {
|
99
|
+
if(event.which == 13) { // enter
|
99
100
|
event.preventDefault();
|
100
|
-
var opts = $(this).find(
|
101
|
-
if(
|
102
|
-
var next = $(
|
103
|
-
$.proxy(
|
104
|
-
if(
|
101
|
+
var opts = $(this).find(':selected');
|
102
|
+
if(opts.length > 0) {
|
103
|
+
var next = $(opts[0]).next();
|
104
|
+
$.proxy(smActivate, $(this))(opts[0]);
|
105
|
+
if(next.length > 0) $(this).val(next.val());
|
105
106
|
}
|
106
107
|
}
|
107
|
-
else if(
|
108
|
+
else if(event.which == 9 || event.which == 39) { // tab or right arrow
|
108
109
|
event.preventDefault();
|
109
|
-
parent.find(
|
110
|
+
parent.find('[data-select="dst"]').focus();
|
110
111
|
}
|
111
|
-
else if(
|
112
|
-
if(
|
112
|
+
else if(event.which == 38) { // up arrow
|
113
|
+
if($(this).find('option')[0] == $(this).find(':selected')[0]) {
|
113
114
|
event.preventDefault();
|
114
|
-
parent.find(
|
115
|
+
parent.find('.search-select').focus();
|
115
116
|
}
|
116
117
|
}
|
117
118
|
});
|
118
|
-
parent.find(
|
119
|
-
if(
|
119
|
+
parent.find('[data-select="dst"]').on('keydown', function(event) {
|
120
|
+
if(event.which == 13) { // enter
|
120
121
|
event.preventDefault();
|
121
|
-
var opts = $(this).find(
|
122
|
-
if(
|
123
|
-
var next = $(
|
124
|
-
$.proxy(
|
125
|
-
if(
|
122
|
+
var opts = $(this).find(':selected');
|
123
|
+
if(opts.length > 0) {
|
124
|
+
var next = $(opts[0]).next();
|
125
|
+
$.proxy(smActivate, $(this))(opts[0]);
|
126
|
+
if(next.length > 0) $(this).val(next.val());
|
126
127
|
}
|
127
128
|
}
|
128
|
-
else if(
|
129
|
+
else if(event.which == 37) { // left arrow
|
129
130
|
event.preventDefault();
|
130
|
-
parent.find(
|
131
|
+
parent.find('[data-select="src"]').focus();
|
131
132
|
}
|
132
133
|
});
|
133
134
|
});
|
134
135
|
|
135
136
|
// --- buttons --------------------------------------------------------------
|
136
|
-
$('.select_many .add').on(
|
137
|
+
$('.select_many .add').on('click', function() {
|
137
138
|
var select = $(this).parent().prev();
|
138
|
-
var current = select.find(
|
139
|
-
if(
|
139
|
+
var current = select.find('option:selected')[0];
|
140
|
+
if(current) $.proxy(smActivate, select)(current);
|
140
141
|
});
|
141
|
-
$('.select_many .remove').on(
|
142
|
+
$('.select_many .remove').on('click', function() {
|
142
143
|
var select = $(this).parent().next();
|
143
|
-
var current = select.find(
|
144
|
-
if(
|
144
|
+
var current = select.find('option:selected')[0];
|
145
|
+
if(current) $.proxy(smActivate, select)(current);
|
145
146
|
});
|
146
|
-
$('.select_many [sortable] .move_up').on(
|
147
|
+
$('.select_many [sortable] .move_up').on('click', function() {
|
147
148
|
var select = $(this).parent().next();
|
148
|
-
var current = select.find(
|
149
|
-
if(
|
150
|
-
$(current).prev().before(
|
151
|
-
smUpdateValues(
|
149
|
+
var current = select.find('option:selected')[0];
|
150
|
+
if(current) {
|
151
|
+
$(current).prev().before(current);
|
152
|
+
smUpdateValues($(this).closest('.select_many'));
|
152
153
|
}
|
153
154
|
});
|
154
|
-
$('.select_many [sortable] .move_down').on(
|
155
|
+
$('.select_many [sortable] .move_down').on('click', function() {
|
155
156
|
var select = $(this).parent().next();
|
156
|
-
var current = select.find(
|
157
|
-
if(
|
158
|
-
$(current).next().after(
|
159
|
-
smUpdateValues(
|
157
|
+
var current = select.find('option:selected')[0];
|
158
|
+
if(current) {
|
159
|
+
$(current).next().after(current);
|
160
|
+
smUpdateValues($(this).closest('.select_many'));
|
160
161
|
}
|
161
162
|
});
|
162
163
|
|
163
164
|
// --- select one -----------------------------------------------------------
|
164
|
-
var onRemoteSelectOne = smDebounce(
|
165
|
+
var onRemoteSelectOne = smDebounce(function(event) {
|
165
166
|
var search = $(this).val().trim();
|
166
|
-
var select = $(this).
|
167
|
-
if(
|
168
|
-
select.data(
|
169
|
-
select.data(
|
167
|
+
var select = $(this).closest('.select_one').find('[data-select="src"]');;
|
168
|
+
if(select.data('searching') != '1' && search && select.data('last-search') != search) {
|
169
|
+
select.data('searching', '1');
|
170
|
+
select.data('last-search', search);
|
170
171
|
var data = {}
|
171
172
|
var search_key = $(this).data('search') ? $(this).data('search') : 'name_contains';
|
172
173
|
var value_key = $(this).data('value') ? $(this).data('value') : 'id';
|
173
174
|
var text_key = $(this).data('text') ? $(this).data('text') : 'name';
|
174
|
-
var counter_limit = $(this).data('counter-limit') ? Number(
|
175
|
+
var counter_limit = $(this).data('counter-limit') ? Number($(this).data('counter-limit')) : 0;
|
175
176
|
data['q['+search_key+']'] = search;
|
176
177
|
$.ajax({
|
177
178
|
context: select,
|
178
179
|
data: data,
|
179
|
-
url: $(this).data(
|
180
|
-
complete: function(
|
181
|
-
$(this).data(
|
180
|
+
url: $(this).data('remote-collection'),
|
181
|
+
complete: function(req, status) {
|
182
|
+
$(this).data('searching', '');
|
182
183
|
},
|
183
|
-
success: function(
|
184
|
+
success: function(data, status, req) {
|
184
185
|
var first = false, sel = $(this);
|
185
186
|
sel.empty();
|
186
|
-
if(
|
187
|
-
data.forEach(
|
188
|
-
sel.append(
|
189
|
-
if(
|
187
|
+
if(sel.data('include-blank')) sel.append($('<option>', { value: '', text: '' }));
|
188
|
+
data.forEach(function(item) {
|
189
|
+
sel.append($('<option>', { value: item[value_key], text: item[text_key] }));
|
190
|
+
if(!first) first = item[value_key];
|
190
191
|
});
|
191
|
-
sel.parent().find(
|
192
|
-
if(
|
192
|
+
sel.parent().find('.status').text('[' + ((counter_limit > 0 && data.length >= counter_limit) ? (counter_limit + '+') : data.length) + ']');
|
193
|
+
if(first) sel.val(first);
|
193
194
|
},
|
194
195
|
});
|
195
196
|
}
|
196
|
-
}, 500
|
197
|
+
}, 500);
|
197
198
|
|
198
|
-
$('.select-one-inputs').each(
|
199
|
-
$(this).find(
|
200
|
-
if(
|
199
|
+
$('.select-one-inputs').each(function() {
|
200
|
+
$(this).find('.search-select').on('keydown', function(event) {
|
201
|
+
if(event.which == 38) { // up arrow
|
201
202
|
event.preventDefault();
|
202
|
-
var select = $(this).closest(
|
203
|
-
var prev = select.find(
|
204
|
-
if(
|
203
|
+
var select = $(this).closest('.select-one-inputs').find('[data-select="src"]');
|
204
|
+
var prev = select.find(':selected').prev();
|
205
|
+
if(prev.length) select.val(prev.val());
|
205
206
|
}
|
206
|
-
else if(
|
207
|
+
else if(event.which == 40) { // down arrow
|
207
208
|
event.preventDefault();
|
208
|
-
var select = $(this).closest(
|
209
|
-
var next = select.find(
|
210
|
-
if(
|
209
|
+
var select = $(this).closest('.select-one-inputs').find('[data-select="src"]');
|
210
|
+
var next = select.find(':selected').next();
|
211
|
+
if(next.length) select.val(next.val());
|
211
212
|
}
|
212
|
-
else $.proxy(
|
213
|
+
else if($(this).data('remote-collection')) $.proxy(onRemoteSelectOne, $(this))(event);
|
213
214
|
});
|
214
|
-
$(this).find(
|
215
|
-
if(
|
215
|
+
$(this).find('[data-select="src"]').on('keydown', function(event) {
|
216
|
+
if(event.which == 37) { // left arrow
|
216
217
|
event.preventDefault();
|
217
|
-
$(this).closest(
|
218
|
+
$(this).closest('.select-one-inputs').find('.search-select').focus();
|
218
219
|
}
|
219
220
|
});
|
220
221
|
});
|
@@ -1,18 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Formtastic
|
2
4
|
module Inputs
|
3
5
|
class SelectManyInput < SelectInput
|
4
6
|
def to_html
|
5
|
-
options[:'data-remote-collection'] = options.delete(
|
7
|
+
options[:'data-remote-collection'] = options.delete(:remote_collection)
|
6
8
|
opts = { class: 'select-many-inputs' }
|
7
|
-
opts[:sortable] = options.delete(
|
9
|
+
opts[:sortable] = options.delete(:sortable) if options[:sortable]
|
8
10
|
input_wrapping do
|
9
11
|
label_html <<
|
10
|
-
template.content_tag(
|
12
|
+
template.content_tag(:div, opts) do
|
11
13
|
hidden_input <<
|
12
14
|
search_box_html <<
|
13
|
-
template.content_tag(
|
14
|
-
template.content_tag(
|
15
|
-
template.content_tag(
|
15
|
+
template.content_tag(:span, '', class: 'empty') <<
|
16
|
+
template.content_tag(:span, ::I18n.t('inputs.select_many.available'), class: 'available') <<
|
17
|
+
template.content_tag(:span, ::I18n.t('inputs.select_many.selected'), class: 'selected') <<
|
16
18
|
select_src_html <<
|
17
19
|
buttons_html <<
|
18
20
|
select_dst_html
|
@@ -21,21 +23,21 @@ module Formtastic
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def hidden_input
|
24
|
-
template.content_tag(
|
25
|
-
values = object.send(
|
26
|
-
values = [values] if values.is_a?
|
26
|
+
template.content_tag(:div, class: 'values', 'data-name': input_html_options[:name]) do
|
27
|
+
values = object.send(input_name)
|
28
|
+
values = [values] if values.is_a? Integer
|
27
29
|
values.each do |value|
|
28
|
-
template.concat template.hidden_field_tag(
|
30
|
+
template.concat template.hidden_field_tag(input_html_options[:name], value, {id: nil})
|
29
31
|
end if values
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def buttons_html
|
34
|
-
template.content_tag(
|
35
|
-
template.link_to(
|
36
|
-
template.link_to(
|
37
|
-
template.link_to(
|
38
|
-
template.link_to(
|
36
|
+
template.content_tag(:div, class: 'buttons') do
|
37
|
+
template.link_to('→'.html_safe, 'Javascript:void(0)', class: 'add') +
|
38
|
+
template.link_to('←'.html_safe, 'Javascript:void(0)', class: 'remove') +
|
39
|
+
template.link_to('↑'.html_safe, 'Javascript:void(0)', class: 'move_up') +
|
40
|
+
template.link_to('↓'.html_safe, 'Javascript:void(0)', class: 'move_down')
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -43,35 +45,50 @@ module Formtastic
|
|
43
45
|
opts = {
|
44
46
|
id: nil,
|
45
47
|
class: 'search-select',
|
46
|
-
placeholder: options.delete(
|
48
|
+
placeholder: options.delete(:placeholder),
|
47
49
|
'data-counter-limit': options[:counter_limit].to_i,
|
48
50
|
'data-remote-collection': options[:'data-remote-collection'],
|
49
51
|
'data-search': options[:search_param] ? options[:search_param] : 'name_contains',
|
50
|
-
'data-text': options[:member_label] ? options[:member_label] : (
|
52
|
+
'data-text': options[:member_label] ? options[:member_label] : (options[:text_key] ? options[:text_key] : 'name'),
|
51
53
|
'data-value': options[:value_key] ? options[:value_key] : 'id',
|
52
54
|
}
|
53
|
-
template.text_field_tag(
|
55
|
+
template.text_field_tag(nil, '', opts)
|
54
56
|
end
|
55
57
|
|
56
58
|
def select_src_html
|
57
|
-
coll =
|
58
|
-
[]
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
coll =
|
60
|
+
if options[:'data-remote-collection']
|
61
|
+
[]
|
62
|
+
else
|
63
|
+
# TODO: add option unique ?
|
64
|
+
selected = object.send(input_name)
|
65
|
+
selected = [selected] if selected.is_a? Integer
|
66
|
+
selected ? collection.select { |option| !selected.include?(option[1]) } : collection
|
67
|
+
end
|
68
|
+
opts = {
|
69
|
+
id: nil,
|
70
|
+
include_blank: false,
|
71
|
+
multiple: true,
|
72
|
+
name: nil,
|
73
|
+
size: options[:size] || 4,
|
74
|
+
'data-select': 'src'
|
75
|
+
}
|
76
|
+
template.select_tag "#{input_name}_src", template.options_for_select(coll), input_options.merge(opts)
|
67
77
|
end
|
68
78
|
|
69
79
|
def select_dst_html
|
70
|
-
selected = options[:selected]
|
71
|
-
selected = [selected] if selected.is_a?
|
72
|
-
coll = selected ? collection.select { |option| selected.include?(
|
73
|
-
opts =
|
74
|
-
|
80
|
+
selected = options[:selected] || object.send(input_name)
|
81
|
+
selected = [selected] if selected.is_a? Integer
|
82
|
+
coll = selected ? collection.select { |option| selected.include?(option[1]) } : []
|
83
|
+
opts = {
|
84
|
+
id: nil,
|
85
|
+
include_blank: false,
|
86
|
+
multiple: true,
|
87
|
+
name: nil,
|
88
|
+
size: options[:size] || 4,
|
89
|
+
'data-select': 'dst'
|
90
|
+
}
|
91
|
+
template.select_tag "#{input_name}_dst", template.options_for_select(coll), input_options.merge(opts)
|
75
92
|
end
|
76
93
|
end
|
77
94
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Formtastic
|
2
4
|
module Inputs
|
3
5
|
class SelectOneInput < SelectInput
|
@@ -16,10 +18,10 @@ module Formtastic
|
|
16
18
|
opts = { class: 'select-one-inputs' }
|
17
19
|
input_wrapping do
|
18
20
|
label_html <<
|
19
|
-
template.content_tag(
|
21
|
+
template.content_tag(:div, opts) do
|
20
22
|
search_box <<
|
21
23
|
select_html <<
|
22
|
-
template.content_tag(
|
24
|
+
template.content_tag(:span, '', class: 'status')
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
@@ -33,25 +35,18 @@ module Formtastic
|
|
33
35
|
'data-counter-limit': options[:counter_limit].to_i,
|
34
36
|
'data-msg': options[:msg_items],
|
35
37
|
'data-remote-collection': options[:remote_collection],
|
36
|
-
'data-search': options[:search_param]
|
37
|
-
'data-text': options[:member_label]
|
38
|
-
'data-value': options[:value_key]
|
38
|
+
'data-search': options[:search_param] || 'name_contains',
|
39
|
+
'data-text': options[:member_label] || options[:text_key] || 'name',
|
40
|
+
'data-value': options[:value_key] || 'id'
|
39
41
|
}
|
40
|
-
template.text_field_tag(
|
42
|
+
template.text_field_tag(nil, '', opts)
|
41
43
|
end
|
42
44
|
|
43
45
|
def select_html
|
44
|
-
selected = options[:selected]
|
45
|
-
|
46
|
-
collection.each do |item|
|
47
|
-
if item[1] == selected
|
48
|
-
sel = template.options_for_select( [item], selected ) if item[1] == selected
|
49
|
-
break
|
50
|
-
end
|
51
|
-
end
|
52
|
-
opts = input_html_options.merge( 'data-select': 'src' )
|
46
|
+
selected = options[:selected] || object.send(input_name)
|
47
|
+
opts = input_html_options.merge('data-select': 'src')
|
53
48
|
opts['data-include-blank'] = '1' if input_options[:include_blank]
|
54
|
-
|
49
|
+
template.select_tag input_name, template.options_for_select(collection, selected), input_options.merge(opts)
|
55
50
|
end
|
56
51
|
end
|
57
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeadmin_select_many
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Roccoberton
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeadmin
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sassc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
27
41
|
description: An Active Admin plugin which improves one-to-many and many-to-many associations
|
28
42
|
selection (jQuery required)
|
29
43
|
email: mat@blocknot.es
|
@@ -31,12 +45,9 @@ executables: []
|
|
31
45
|
extensions: []
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
34
|
-
- ".gitignore"
|
35
|
-
- Gemfile
|
36
48
|
- LICENSE.txt
|
37
49
|
- README.md
|
38
50
|
- Rakefile
|
39
|
-
- activeadmin_select_many.gemspec
|
40
51
|
- app/assets/javascripts/activeadmin/select_many.js
|
41
52
|
- app/assets/stylesheets/activeadmin/_select_many.sass
|
42
53
|
- config/locales/en.yml
|
@@ -46,12 +57,11 @@ files:
|
|
46
57
|
- lib/activeadmin_select_many.rb
|
47
58
|
- lib/formtastic/inputs/select_many_input.rb
|
48
59
|
- lib/formtastic/inputs/select_one_input.rb
|
49
|
-
- screenshot.png
|
50
60
|
homepage: https://github.com/blocknotes/activeadmin_select_many
|
51
61
|
licenses:
|
52
62
|
- MIT
|
53
63
|
metadata: {}
|
54
|
-
post_install_message:
|
64
|
+
post_install_message:
|
55
65
|
rdoc_options: []
|
56
66
|
require_paths:
|
57
67
|
- lib
|
@@ -66,9 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
76
|
- !ruby/object:Gem::Version
|
67
77
|
version: '0'
|
68
78
|
requirements: []
|
69
|
-
|
70
|
-
|
71
|
-
signing_key:
|
79
|
+
rubygems_version: 3.1.4
|
80
|
+
signing_key:
|
72
81
|
specification_version: 4
|
73
82
|
summary: SelectMany plugin for ActiveAdmin
|
74
83
|
test_files: []
|
data/.gitignore
DELETED
data/Gemfile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
lib = File.expand_path('../lib', __FILE__)
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require 'activeadmin/select_many/version'
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = 'activeadmin_select_many'
|
7
|
-
spec.version = ActiveAdmin::SelectMany::VERSION
|
8
|
-
spec.summary = 'SelectMany plugin for ActiveAdmin'
|
9
|
-
spec.description = 'An Active Admin plugin which improves one-to-many and many-to-many associations selection (jQuery required)'
|
10
|
-
spec.license = 'MIT'
|
11
|
-
spec.authors = ['Mattia Roccoberton']
|
12
|
-
spec.email = 'mat@blocknot.es'
|
13
|
-
spec.homepage = 'https://github.com/blocknotes/activeadmin_select_many'
|
14
|
-
|
15
|
-
spec.files = `git ls-files -z`.split("\x0")
|
16
|
-
spec.require_paths = ['lib']
|
17
|
-
|
18
|
-
spec.add_runtime_dependency 'activeadmin', '~> 1.0'
|
19
|
-
end
|
data/screenshot.png
DELETED
Binary file
|