activeadmin_select_many 0.3.2 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![gem version](https://badge.fury.io/rb/activeadmin_select_many.svg)](https://badge.fury.io/rb/activeadmin_select_many)
|
3
|
+
[![gem downloads](https://badgen.net/rubygems/dt/activeadmin_select_many)](https://rubygems.org/gems/activeadmin_select_many)
|
4
|
+
[![specs](https://github.com/blocknotes/activeadmin_select_many/actions/workflows/specs.yml/badge.svg)](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
|
![screenshot](screenshot.png)
|
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
|