activeadmin_select_many 0.1.0 → 0.1.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 +4 -4
- data/README.md +29 -1
- data/app/assets/javascripts/activeadmin/select_many.js +57 -11
- data/app/assets/stylesheets/activeadmin/_select_many.sass +19 -2
- data/lib/activeadmin/select_many/version.rb +1 -1
- data/lib/activeadmin_select_many.rb +1 -0
- data/lib/formtastic/inputs/select_many_input.rb +27 -21
- data/lib/formtastic/inputs/select_one_input.rb +81 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71d37813e5fbcb67285b3c1e8413e4880e79e69d
|
4
|
+
data.tar.gz: 8cb5b71339e21a10c4e84cc059f0d5ede4e2a220
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8eabd05b1cf8e08453b0e5ae2bf169c11840f61bbc7066787c15725dcc943df14cdaea09e63de3577a8f4efe550f09f31790bdfc94066db862808b035421714
|
7
|
+
data.tar.gz: a4c4220404ecc844dade9e6b2656c15792315bb1cc2e639a6d4515ad722d27223f45621c4dc69e7baf72876dd7d8103fdfe4f0f2a4ef4593214709c934f4f9f8
|
data/README.md
CHANGED
@@ -8,6 +8,7 @@ Features:
|
|
8
8
|
- select on the right with selected items
|
9
9
|
- local/remote collections
|
10
10
|
- double click to add/remove items
|
11
|
+
- sortable
|
11
12
|
|
12
13
|

|
13
14
|
|
@@ -30,8 +31,33 @@ Add to ActiveAdmin model config, in *form* block.
|
|
30
31
|
`f.input :sections, as: :select_many`
|
31
32
|
- Remote collection (using AJAX):
|
32
33
|
`f.input :tags, as: :select_many, remote_collection: admin_tags_path( format: :json )`
|
33
|
-
- Changing search param and text key:
|
34
|
+
- Changing search param and text key (default: *name*):
|
34
35
|
`f.input :tags, as: :select_many, remote_collection: admin_tags_path( format: :json ), search_param: 'category_contains', text_key: 'category', placeholder: 'Type something...'`
|
36
|
+
- Sortable (items position must be saved manually):
|
37
|
+
`f.input :tags, as: :select_many, remote_collection: admin_tags_path( format: :json ), sortable: true`
|
38
|
+
```rb
|
39
|
+
# Manually update position field
|
40
|
+
after_save :on_after_save
|
41
|
+
controller do
|
42
|
+
def on_after_save( object )
|
43
|
+
if params[:article][:section_ids]
|
44
|
+
order = {}
|
45
|
+
params[:article][:section_ids].each_with_index { |id, i| order[id.to_i] = i }
|
46
|
+
object.sections.each { |item| item.update_column( :position, order[item.id].to_i ) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
Example to enable JSON response on an ActiveAdmin model:
|
53
|
+
|
54
|
+
```rb
|
55
|
+
ActiveAdmin.register Tag do
|
56
|
+
config.per_page = 30
|
57
|
+
config.sort_order = 'name_asc'
|
58
|
+
index download_links: [:json]
|
59
|
+
end
|
60
|
+
```
|
35
61
|
|
36
62
|
## Options
|
37
63
|
|
@@ -39,6 +65,8 @@ Add to ActiveAdmin model config, in *form* block.
|
|
39
65
|
- **placeholder**: placeholder string for search box
|
40
66
|
- **remote_collection**: JSON path
|
41
67
|
- **search_param**: parameter to use as search key (ransack style)
|
68
|
+
- **sortable**: set to true to enable sortable buttons (default: not set)
|
69
|
+
- **size**: number of rows of both the selects (default: 4)
|
42
70
|
- **text_key**: key to use as text for select options
|
43
71
|
|
44
72
|
## Do you like it? Star it!
|
@@ -1,4 +1,4 @@
|
|
1
|
-
function
|
1
|
+
function smDebounce( func, wait, immediate ) {
|
2
2
|
var timeout;
|
3
3
|
return function() {
|
4
4
|
var context = this, args = arguments;
|
@@ -13,6 +13,14 @@ function debounce( func, wait, immediate ) {
|
|
13
13
|
};
|
14
14
|
};
|
15
15
|
|
16
|
+
function smUpdateValues( parent ) {
|
17
|
+
var values = parent.find( '.values' );
|
18
|
+
values.empty();
|
19
|
+
parent.find( '[data-select="dst"] option' ).each( function() {
|
20
|
+
values.append( $('<input>', { type: 'hidden', name: values.data( 'name' ), value: $(this).val() }) );
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
16
24
|
$(document).ready( function() {
|
17
25
|
$('.select_many.input select').on( 'dblclick', function( event ) {
|
18
26
|
if( event.target.tagName.toLowerCase() == 'option' ) {
|
@@ -21,22 +29,17 @@ $(document).ready( function() {
|
|
21
29
|
var dst = parent.find( $(this).data( 'select' ) == 'src' ? '[data-select="dst"]' : '[data-select="src"]' );
|
22
30
|
dst.append( $('<option>', { value: opt.val(), text: opt.text() }) );
|
23
31
|
opt.remove();
|
24
|
-
|
25
|
-
var values = parent.find( '.values' );
|
26
|
-
values.empty();
|
27
|
-
parent.find( '[data-select="dst"] option' ).each( function() {
|
28
|
-
values.append( $('<input>', { type: 'hidden', name: values.data( 'name' ), value: $(this).val() }) );
|
29
|
-
});
|
32
|
+
smUpdateValues( parent );
|
30
33
|
}
|
31
34
|
});
|
32
35
|
|
33
|
-
var onLocalSelect =
|
36
|
+
var onLocalSelect = smDebounce( function() {
|
34
37
|
var search = $(this).val().toLowerCase();
|
35
38
|
$(this).closest( '.select_many' ).find( '[data-select="src"] option' ).each( function() {
|
36
39
|
$(this).toggle( $(this).text().toLowerCase().indexOf( search ) >= 0 );
|
37
40
|
});
|
38
41
|
}, 250 );
|
39
|
-
var onRemoteSelect =
|
42
|
+
var onRemoteSelect = smDebounce( function() {
|
40
43
|
var search = $(this).val().trim();
|
41
44
|
if( search != '' && $(this).data( 'searching' ) != '1' ) {
|
42
45
|
$(this).data( 'searching', '1' );
|
@@ -64,9 +67,52 @@ $(document).ready( function() {
|
|
64
67
|
}
|
65
68
|
}, 400 );
|
66
69
|
|
67
|
-
// $('.select_many.input .search-select').on( 'keyup', onKeyup );
|
68
|
-
|
69
70
|
$('.select_many.input .search-select').each( function() {
|
70
71
|
$(this).on( 'keyup', $(this).data( 'remote' ) ? onRemoteSelect : onLocalSelect );
|
71
72
|
});
|
73
|
+
$('.select_many [sortable] .move_up').on( 'click', function() {
|
74
|
+
var select = $(this).parent().next();
|
75
|
+
var current = select.find( 'option:selected' )[0];
|
76
|
+
if( current ) {
|
77
|
+
$(current).prev().before( current );
|
78
|
+
smUpdateValues( $(this).closest( '.select_many' ) );
|
79
|
+
}
|
80
|
+
});
|
81
|
+
$('.select_many [sortable] .move_down').on( 'click', function() {
|
82
|
+
var select = $(this).parent().next();
|
83
|
+
var current = select.find( 'option:selected' )[0];
|
84
|
+
if( current ) {
|
85
|
+
$(current).next().after( current );
|
86
|
+
smUpdateValues( $(this).closest( '.select_many' ) );
|
87
|
+
}
|
88
|
+
});
|
89
|
+
|
90
|
+
// // WORK IN PROGRESS
|
91
|
+
// var onRemoteSelectOne = smDebounce( function( event ) {
|
92
|
+
// if( $(this).data( 'searching' ) != '1' ) {
|
93
|
+
// $(this).data( 'searching', '1' );
|
94
|
+
// var _this = $(this);
|
95
|
+
// var data = {}
|
96
|
+
// var search_key = $(this).data('search') ? $(this).data('search') : 'name_contains';
|
97
|
+
// var value_key = $(this).data('value') ? $(this).data('value') : 'id';
|
98
|
+
// var text_key = $(this).data('text') ? $(this).data('text') : 'name';
|
99
|
+
// data['q['+search_key+']'] = event.key;
|
100
|
+
// $.ajax({
|
101
|
+
// context: _this,
|
102
|
+
// data: data,
|
103
|
+
// url: $(this).data( 'remote' ),
|
104
|
+
// complete: function( req, status ) {
|
105
|
+
// $(this).data( 'searching', '' );
|
106
|
+
// },
|
107
|
+
// success: function( data, status, req ) {
|
108
|
+
// var select = $(this);
|
109
|
+
// select.empty();
|
110
|
+
// data.forEach( function( item ) {
|
111
|
+
// select.append( $('<option>', { value: item[value_key], text: item[text_key] }) );
|
112
|
+
// });
|
113
|
+
// },
|
114
|
+
// });
|
115
|
+
// }
|
116
|
+
// }, 400 );
|
117
|
+
// $('.select_one.input select').on( 'keyup', onRemoteSelectOne );
|
72
118
|
});
|
@@ -1,13 +1,30 @@
|
|
1
1
|
body.active_admin
|
2
2
|
.select_many
|
3
|
+
.buttons
|
4
|
+
margin-top: 0
|
5
|
+
text-align: center
|
6
|
+
width: 22px
|
7
|
+
a
|
8
|
+
background: #ddd
|
9
|
+
border: 1px solid #eee
|
10
|
+
display: block
|
11
|
+
font-weight: bold
|
12
|
+
line-height: 16px
|
13
|
+
text-decoration: none
|
14
|
+
&.move_up, &.move_down
|
15
|
+
display: none
|
3
16
|
.search-select
|
4
17
|
margin-bottom: 1px
|
5
18
|
.selects
|
6
19
|
display: flex
|
7
20
|
flex-wrap: wrap
|
8
21
|
> input[type="text"], > select, > span
|
9
|
-
margin-right: 1%
|
10
22
|
min-width: auto
|
11
|
-
width:
|
23
|
+
width: calc(50% - 12px);
|
12
24
|
> span
|
13
25
|
padding: 3px 6px 1px 6px
|
26
|
+
.selected
|
27
|
+
padding-left: 30px
|
28
|
+
[sortable] >.buttons
|
29
|
+
a.move_up, a.move_down
|
30
|
+
display: block
|
@@ -6,15 +6,18 @@ module Formtastic
|
|
6
6
|
# end
|
7
7
|
|
8
8
|
def to_html
|
9
|
+
opts = { class: 'selects' }
|
10
|
+
opts[:sortable] = options[:sortable] if options[:sortable]
|
9
11
|
input_wrapping do
|
10
12
|
label_html <<
|
11
13
|
hidden_input <<
|
12
|
-
template.content_tag( :div,
|
13
|
-
|
14
|
+
template.content_tag( :div, opts ) do
|
15
|
+
search_box_html +
|
14
16
|
template.content_tag( :span, '' ) +
|
15
|
-
template.content_tag( :span, template.t( 'inputs.select_many.available' )
|
16
|
-
template.content_tag( :span, template.t( 'inputs.select_many.selected' )
|
17
|
+
template.content_tag( :span, template.t( 'inputs.select_many.available' ), class: 'available' ) +
|
18
|
+
template.content_tag( :span, template.t( 'inputs.select_many.selected' ), class: 'selected' ) +
|
17
19
|
select_src_html +
|
20
|
+
buttons_html +
|
18
21
|
select_dst_html
|
19
22
|
end
|
20
23
|
end
|
@@ -22,13 +25,24 @@ module Formtastic
|
|
22
25
|
|
23
26
|
def hidden_input
|
24
27
|
template.content_tag( :div, class: 'values', 'data-name': input_html_options[:name] ) do
|
25
|
-
object.send( input_name )
|
28
|
+
values = object.send( input_name )
|
29
|
+
values = [values] if values.is_a? Fixnum
|
30
|
+
values.each do |value|
|
26
31
|
template.concat template.hidden_field_tag( input_html_options[:name], value, {id: nil} )
|
27
|
-
end
|
32
|
+
end if values
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def buttons_html
|
37
|
+
template.content_tag( :div, class: 'buttons' ) do
|
38
|
+
# template.link_to( '→'.html_safe, 'Javascript:void(0)', class: 'add' ) +
|
39
|
+
# template.link_to( '←'.html_safe, 'Javascript:void(0)', class: 'remove' ) +
|
40
|
+
template.link_to( '↑'.html_safe, 'Javascript:void(0)', class: 'move_up' ) +
|
41
|
+
template.link_to( '↓'.html_safe, 'Javascript:void(0)', class: 'move_down' )
|
28
42
|
end
|
29
43
|
end
|
30
44
|
|
31
|
-
def
|
45
|
+
def search_box_html
|
32
46
|
@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'}
|
33
47
|
template.text_field_tag( nil, '', @opts )
|
34
48
|
end
|
@@ -39,28 +53,20 @@ module Formtastic
|
|
39
53
|
else
|
40
54
|
# TODO: add option unique ?
|
41
55
|
selected = object.send( input_name )
|
42
|
-
|
56
|
+
selected = [selected] if selected.is_a? Fixnum
|
57
|
+
selected ? collection.select { |option| !selected.include?( option[1] ) } : collection
|
43
58
|
end
|
44
|
-
opts = input_options.
|
59
|
+
opts = input_options.merge( name: nil, id: nil, multiple: true, 'data-select': 'src', size: options[:size] ? options[:size] : 4 )
|
45
60
|
template.select_tag nil, template.options_for_select( coll ), opts
|
46
61
|
end
|
47
62
|
|
48
63
|
def select_dst_html
|
49
64
|
selected = object.send( input_name )
|
50
|
-
|
51
|
-
|
65
|
+
selected = [selected] if selected.is_a? Fixnum
|
66
|
+
coll = selected ? collection.select { |option| selected.include?( option[1] ) } : collection
|
67
|
+
opts = input_options.merge( name: nil, id: nil, multiple: true, 'data-select': 'dst', size: options[:size] ? options[:size] : 4 )
|
52
68
|
template.select_tag nil, template.options_for_select( coll ), opts
|
53
69
|
end
|
54
|
-
|
55
|
-
# def select_html
|
56
|
-
# selected = object.send( input_name )
|
57
|
-
# coll = collection.select { |option| selected.include?( option[1] ) }
|
58
|
-
|
59
|
-
# opts = input_options.dup.merge( name: nil, id: nil, multiple: true )
|
60
|
-
# template.select_tag nil, template.options_for_select( coll ), opts
|
61
|
-
|
62
|
-
# # builder.select(input_name, coll, input_options, input_html_options)
|
63
|
-
# end
|
64
70
|
end
|
65
71
|
end
|
66
72
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Formtastic
|
2
|
+
module Inputs
|
3
|
+
class SelectOneInput < SelectInput
|
4
|
+
def input_options
|
5
|
+
super.merge include_blank: false
|
6
|
+
end
|
7
|
+
|
8
|
+
# def input_html_options
|
9
|
+
# super.merge( class: 'select-one' )
|
10
|
+
# end
|
11
|
+
|
12
|
+
|
13
|
+
def to_html
|
14
|
+
input_wrapping do
|
15
|
+
label_html <<
|
16
|
+
search_box <<
|
17
|
+
select_html
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
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
|
+
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'}
|
33
|
+
template.text_field_tag( nil, '', @opts )
|
34
|
+
end
|
35
|
+
|
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
|
+
def select_html
|
69
|
+
selected = object.send( input_name )
|
70
|
+
sel = ''
|
71
|
+
collection.each do |item|
|
72
|
+
if item[1] == selected
|
73
|
+
sel = template.options_for_select( [item], selected ) if item[1] == selected
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
builder.select(input_name, sel, input_options, input_html_options.merge( 'data-select': 'src' ) )
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
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.1.
|
4
|
+
version: 0.1.2
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeadmin
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/activeadmin/select_many/version.rb
|
46
46
|
- lib/activeadmin_select_many.rb
|
47
47
|
- lib/formtastic/inputs/select_many_input.rb
|
48
|
+
- lib/formtastic/inputs/select_one_input.rb
|
48
49
|
- screenshot.png
|
49
50
|
homepage: https://github.com/blocknotes/activeadmin_select_many
|
50
51
|
licenses:
|