activeadmin_select_many 0.1.4 → 0.2.0
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/README.md +39 -16
- data/app/assets/javascripts/activeadmin/select_many.js +30 -30
- data/app/assets/stylesheets/activeadmin/_select_many.sass +23 -5
- data/config/locales/en.yml +2 -0
- data/lib/activeadmin/select_many/version.rb +1 -1
- data/lib/activeadmin_select_many.rb +1 -1
- data/lib/formtastic/inputs/select_many_input.rb +12 -15
- data/lib/formtastic/inputs/select_one_input.rb +14 -49
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc928a64768e531eb353bfb5aecc59b92151a4ff
|
4
|
+
data.tar.gz: 8b4ff87b759f176fa8f1d0a0806e5bff7be878be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b754c3cf2c094ea2eec2d805b8cf99b760680d74471fadcaa35ea8fe4437d0e05f75bded2ac68e817722b9005e093db3e84f44102d0abd8eacfbf8846c7f1fc
|
7
|
+
data.tar.gz: 6244aa2b14352c08cc696319dc93f49d79c44a49f59d8176d572cfc8c71f5aa12e9786a912bc180e98012367be57ee5b6cfb08bf6dd00ca0203e518bead4991c
|
data/README.md
CHANGED
@@ -1,17 +1,25 @@
|
|
1
1
|
# ActiveAdmin Select Many [](https://badge.fury.io/rb/activeadmin_select_many)
|
2
2
|
|
3
|
-
An Active Admin plugin which improves one-to-many
|
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)
|
4
4
|
|
5
|
-
Features
|
5
|
+
Features for *select_many*:
|
6
6
|
- search box
|
7
|
-
-
|
8
|
-
- select on the right with selected items
|
7
|
+
- available items on the left, selected items on the right
|
9
8
|
- local/remote collections
|
10
9
|
- double click to add/remove items
|
11
|
-
- sortable
|
10
|
+
- sortable (with up/down buttons)
|
11
|
+
|
12
|
+
Features for *select_one*:
|
13
|
+
- search box
|
14
|
+
- selected items on the right
|
15
|
+
- remote collections
|
16
|
+
- counter of items found
|
17
|
+
- can be used as filter
|
12
18
|
|
13
19
|

|
14
20
|
|
21
|
+
*(inspired by RailsAdmin associations selector)*
|
22
|
+
|
15
23
|
## Install
|
16
24
|
|
17
25
|
- Add to your Gemfile:
|
@@ -23,7 +31,18 @@ Features:
|
|
23
31
|
`//= require activeadmin/select_many`
|
24
32
|
- Use the input with `as: :select_many` in Active Admin model conf
|
25
33
|
|
26
|
-
##
|
34
|
+
## Options
|
35
|
+
|
36
|
+
- **collection**: local collection
|
37
|
+
- **filter_form**: for *select_one* only, allow to use it as filter
|
38
|
+
- **placeholder**: placeholder string for search box
|
39
|
+
- **remote_collection**: JSON path
|
40
|
+
- **search_param**: parameter to use as search key (ransack format)
|
41
|
+
- **sortable**: set to true to enable sortable buttons (default: not set)
|
42
|
+
- **size**: number of rows of both the selects (default: 4)
|
43
|
+
- **text_key**: key to use as text for select options
|
44
|
+
|
45
|
+
## Example with select_many
|
27
46
|
|
28
47
|
Add to ActiveAdmin model config, in *form* block.
|
29
48
|
|
@@ -35,8 +54,10 @@ Add to ActiveAdmin model config, in *form* block.
|
|
35
54
|
`f.input :tags, as: :select_many, remote_collection: admin_tags_path( format: :json ), search_param: 'category_contains', text_key: 'category', placeholder: 'Type something...'`
|
36
55
|
- Sortable (items position must be saved manually):
|
37
56
|
`f.input :tags, as: :select_many, remote_collection: admin_tags_path( format: :json ), sortable: true`
|
57
|
+
|
58
|
+
Example to update *position* field:
|
59
|
+
|
38
60
|
```rb
|
39
|
-
# Manually update position field
|
40
61
|
after_save :on_after_save
|
41
62
|
controller do
|
42
63
|
def on_after_save( object )
|
@@ -53,26 +74,28 @@ Example to enable JSON response on an ActiveAdmin model:
|
|
53
74
|
|
54
75
|
```rb
|
55
76
|
ActiveAdmin.register Tag do
|
56
|
-
config.per_page = 30
|
77
|
+
config.per_page = 30 # to limit served items
|
57
78
|
config.sort_order = 'name_asc'
|
58
79
|
index download_links: [:json]
|
59
80
|
end
|
60
81
|
```
|
61
82
|
|
62
|
-
##
|
83
|
+
## Example with select_one
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
85
|
+
In a form:
|
86
|
+
|
87
|
+
`f.input :article, as: :select_one, placeholder: 'Search...', remote_collection: admin_articles_path( format: :json ), search_param: 'title_contains', text_key: 'title'`
|
88
|
+
|
89
|
+
As filter:
|
90
|
+
|
91
|
+
`filter :article_id_eq, as: :select_one, filter_form: true, placeholder: 'Search...', search_param: 'title_contains', text_key: 'title', remote_collection: '/admin/articles.json'`
|
71
92
|
|
72
93
|
## Do you like it? Star it!
|
73
94
|
|
74
95
|
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
75
96
|
|
97
|
+
Take a look at [other ActiveAdmin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source) that I made if you are curious.
|
98
|
+
|
76
99
|
## Contributors
|
77
100
|
|
78
101
|
- [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
|
@@ -57,7 +57,7 @@ $(document).ready( function() {
|
|
57
57
|
$.ajax({
|
58
58
|
context: _this,
|
59
59
|
data: data,
|
60
|
-
url: $(this).data( 'remote' ),
|
60
|
+
url: $(this).data( 'remote-collection' ),
|
61
61
|
complete: function( req, status ) {
|
62
62
|
$(this).data( 'searching', '' );
|
63
63
|
},
|
@@ -74,7 +74,7 @@ $(document).ready( function() {
|
|
74
74
|
}, 400 );
|
75
75
|
|
76
76
|
$('.select_many.input .search-select').each( function() {
|
77
|
-
$(this).on( 'keyup', $(this).data( 'remote' ) ? onRemoteSelect : onLocalSelect );
|
77
|
+
$(this).on( 'keyup', $(this).data( 'remote-collection' ) ? onRemoteSelect : onLocalSelect );
|
78
78
|
});
|
79
79
|
$('.select_many .add').on( 'click', function() {
|
80
80
|
var select = $(this).parent().prev();
|
@@ -103,32 +103,32 @@ $(document).ready( function() {
|
|
103
103
|
}
|
104
104
|
});
|
105
105
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
106
|
+
var onRemoteSelectOne = smDebounce( function( event ) {
|
107
|
+
var select = $(this).next();
|
108
|
+
if( select.data( 'searching' ) != '1' ) {
|
109
|
+
select.data( 'searching', '1' );
|
110
|
+
var data = {}
|
111
|
+
var search_key = $(this).data('search') ? $(this).data('search') : 'name_contains';
|
112
|
+
var value_key = $(this).data('value') ? $(this).data('value') : 'id';
|
113
|
+
var text_key = $(this).data('text') ? $(this).data('text') : 'name';
|
114
|
+
data['q['+search_key+']'] = $(this).val();
|
115
|
+
$.ajax({
|
116
|
+
context: select,
|
117
|
+
data: data,
|
118
|
+
url: $(this).data( 'remote-collection' ),
|
119
|
+
complete: function( req, status ) {
|
120
|
+
$(this).data( 'searching', '' );
|
121
|
+
},
|
122
|
+
success: function( data, status, req ) {
|
123
|
+
var sel = $(this);
|
124
|
+
sel.empty();
|
125
|
+
data.forEach( function( item ) {
|
126
|
+
sel.append( $('<option>', { value: item[value_key], text: item[text_key] }) );
|
127
|
+
});
|
128
|
+
sel.parent().find( '.status' ).text( '[' + data.length + ']' );
|
129
|
+
},
|
130
|
+
});
|
131
|
+
}
|
132
|
+
}, 500 );
|
133
|
+
$('.select-one-inputs > .search-select').on( 'keyup', onRemoteSelectOne );
|
134
134
|
});
|
@@ -16,21 +16,39 @@ body.active_admin
|
|
16
16
|
display: none
|
17
17
|
&:hover
|
18
18
|
background: #e0e0e0
|
19
|
-
.
|
20
|
-
margin-bottom: 1px
|
21
|
-
.selects
|
19
|
+
.select-many-inputs
|
22
20
|
display: flex
|
23
21
|
flex-wrap: wrap
|
24
22
|
> input[type="text"], > select, > span
|
25
23
|
min-width: auto
|
26
|
-
width: calc(50% - 12px)
|
24
|
+
width: calc(50% - 12px)
|
27
25
|
> span
|
28
26
|
padding: 3px 6px 1px 6px
|
27
|
+
.empty
|
28
|
+
width: 20%
|
29
29
|
.available
|
30
30
|
font-style: italic
|
31
|
+
padding: 0 6px 0 6px
|
31
32
|
.selected
|
32
33
|
font-style: italic
|
33
|
-
padding
|
34
|
+
padding: 0 0 0 12px
|
35
|
+
.search-select
|
36
|
+
margin-bottom: 1px
|
37
|
+
width: 50%
|
34
38
|
[sortable] >.buttons
|
35
39
|
a.move_up, a.move_down
|
36
40
|
display: block
|
41
|
+
|
42
|
+
.formtastic > .inputs .select_one
|
43
|
+
.select-one-inputs
|
44
|
+
display: flex
|
45
|
+
> input[type="text"], > select, > span
|
46
|
+
display: inline-block
|
47
|
+
width: calc(50% - 12px)
|
48
|
+
.status
|
49
|
+
line-height: 26px
|
50
|
+
margin-left: 5px
|
51
|
+
|
52
|
+
.filter_form .select-one-inputs
|
53
|
+
.status
|
54
|
+
display: none
|
data/config/locales/en.yml
CHANGED
@@ -1,23 +1,20 @@
|
|
1
1
|
module Formtastic
|
2
2
|
module Inputs
|
3
3
|
class SelectManyInput < SelectInput
|
4
|
-
# def input_html_options
|
5
|
-
# super.merge( class: 'select-many' )
|
6
|
-
# end
|
7
|
-
|
8
4
|
def to_html
|
9
|
-
|
10
|
-
opts
|
5
|
+
options[:'data-remote-collection'] = options.delete( :remote_collection )
|
6
|
+
opts = { class: 'select-many-inputs' }
|
7
|
+
opts[:sortable] = options.delete( :sortable ) if options[:sortable]
|
11
8
|
input_wrapping do
|
12
9
|
label_html <<
|
13
|
-
hidden_input <<
|
14
10
|
template.content_tag( :div, opts ) do
|
15
|
-
|
16
|
-
|
17
|
-
template.content_tag( :span,
|
18
|
-
template.content_tag( :span, template.t( 'inputs.select_many.
|
19
|
-
|
20
|
-
|
11
|
+
hidden_input <<
|
12
|
+
search_box_html <<
|
13
|
+
template.content_tag( :span, '', class: 'empty' ) <<
|
14
|
+
template.content_tag( :span, template.t( 'inputs.select_many.available' ), class: 'available' ) <<
|
15
|
+
template.content_tag( :span, template.t( 'inputs.select_many.selected' ), class: 'selected' ) <<
|
16
|
+
select_src_html <<
|
17
|
+
buttons_html <<
|
21
18
|
select_dst_html
|
22
19
|
end
|
23
20
|
end
|
@@ -43,12 +40,12 @@ module Formtastic
|
|
43
40
|
end
|
44
41
|
|
45
42
|
def search_box_html
|
46
|
-
@opts ||= {id: nil, class: 'search-select', placeholder: options
|
43
|
+
@opts ||= {id: nil, class: 'search-select', placeholder: options.delete( :placeholder ), 'data-remote-collection': options[:'data-remote-collection'], 'data-search': options[:search_param] ? options[:search_param] : 'name_contains', 'data-text': options[:text_key] ? options[:text_key] : 'name', 'data-value': options[:value_key] ? options[:value_key] : 'id'}
|
47
44
|
template.text_field_tag( nil, '', @opts )
|
48
45
|
end
|
49
46
|
|
50
47
|
def select_src_html
|
51
|
-
coll = if options[:
|
48
|
+
coll = if options[:'data-remote-collection']
|
52
49
|
[]
|
53
50
|
else
|
54
51
|
# TODO: add option unique ?
|
@@ -5,66 +5,31 @@ module Formtastic
|
|
5
5
|
super.merge include_blank: false
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def input_wrapping(&block)
|
9
|
+
template.content_tag(options[:filter_form] ? :div : :li,
|
10
|
+
[template.capture(&block), error_html, hint_html].join("\n").html_safe,
|
11
|
+
wrapper_html_options
|
12
|
+
)
|
13
|
+
end
|
12
14
|
|
13
15
|
def to_html
|
16
|
+
opts = { class: 'select-one-inputs' }
|
14
17
|
input_wrapping do
|
15
18
|
label_html <<
|
16
|
-
|
17
|
-
|
19
|
+
template.content_tag( :div, opts ) do
|
20
|
+
search_box <<
|
21
|
+
select_html <<
|
22
|
+
# template.content_tag( :span, '' ) <<
|
23
|
+
template.content_tag( :span, template.t( 'inputs.select_one.status' ), class: 'status' )
|
24
|
+
end
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
# def hidden_input
|
22
|
-
# template.content_tag( :div, class: 'values', 'data-name': input_html_options[:name] ) do
|
23
|
-
# values = object.send( input_name )
|
24
|
-
# values = [values] if values.is_a? Fixnum
|
25
|
-
# values.each do |value|
|
26
|
-
# template.concat template.hidden_field_tag( input_html_options[:name], value, {id: nil} )
|
27
|
-
# end if values
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
|
31
28
|
def search_box
|
32
|
-
@opts ||= {id: nil, class: 'search-select', placeholder: options[:placeholder], 'data-remote': options[:remote_collection], 'data-search': options[:search_param] ? options[:search_param] : 'name_contains', 'data-text': options[:text_key] ? options[:text_key] : 'name', 'data-value': options[:value_key] ? options[:value_key] : 'id'}
|
29
|
+
@opts ||= {id: nil, class: 'search-select', placeholder: options[:placeholder], 'data-remote-collection': options[:remote_collection], 'data-search': options[:search_param] ? options[:search_param] : 'name_contains', 'data-text': options[:text_key] ? options[:text_key] : 'name', 'data-value': options[:value_key] ? options[:value_key] : 'id', 'data-msg': options[:msg_items]}
|
33
30
|
template.text_field_tag( nil, '', @opts )
|
34
31
|
end
|
35
32
|
|
36
|
-
# def select_src_html
|
37
|
-
# coll = if options[:remote_collection]
|
38
|
-
# []
|
39
|
-
# else
|
40
|
-
# # TODO: add option unique ?
|
41
|
-
# selected = object.send( input_name )
|
42
|
-
# selected = [selected] if selected.is_a? Fixnum
|
43
|
-
# selected ? collection.select { |option| !selected.include?( option[1] ) } : collection
|
44
|
-
# end
|
45
|
-
# opts = input_options.dup.merge( name: nil, id: nil, multiple: true, 'data-select': 'src' )
|
46
|
-
# template.select_tag nil, template.options_for_select( coll ), opts
|
47
|
-
# end
|
48
|
-
|
49
|
-
|
50
|
-
# def select_dst_html
|
51
|
-
# selected = object.send( input_name )
|
52
|
-
# selected = [selected] if selected.is_a? Fixnum
|
53
|
-
# coll = selected ? collection.select { |option| selected.include?( option[1] ) } : collection
|
54
|
-
# opts = input_options.dup.merge( name: nil, id: nil, multiple: true, 'data-select': 'dst' )
|
55
|
-
# template.select_tag nil, template.options_for_select( coll ), opts
|
56
|
-
# end
|
57
|
-
|
58
|
-
# # def select_html
|
59
|
-
# # selected = object.send( input_name )
|
60
|
-
# # coll = collection.select { |option| selected.include?( option[1] ) }
|
61
|
-
|
62
|
-
# # opts = input_options.dup.merge( name: nil, id: nil, multiple: true )
|
63
|
-
# # template.select_tag nil, template.options_for_select( coll ), opts
|
64
|
-
|
65
|
-
# # # builder.select(input_name, coll, input_options, input_html_options)
|
66
|
-
# # end
|
67
|
-
|
68
33
|
def select_html
|
69
34
|
selected = object.send( input_name )
|
70
35
|
sel = ''
|
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.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Roccoberton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeadmin
|