activeadmin_select_many 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45fd55dbb9b440057b38aeef68aca5494f89247b
4
- data.tar.gz: afa5035de499e0997a1fb2e8c81c3b21dd343e07
3
+ metadata.gz: dc928a64768e531eb353bfb5aecc59b92151a4ff
4
+ data.tar.gz: 8b4ff87b759f176fa8f1d0a0806e5bff7be878be
5
5
  SHA512:
6
- metadata.gz: b71c96131788ee4b9b883ed73186abad17ff7fc85d05b985f674f5561ff1f89681b7395e64636aca366c2f38a9829f639f24b0ba9aa677ff86d35aab278a3c53
7
- data.tar.gz: 5d9f79c90856223aa9cd45810bc1679a6fde32cc39b8b21b93205f081fe5b38c3a22fcc202a2a29e75f0fc4e2f732ab5c8c94dd4a5caf195e0d4502701eaf24d
6
+ metadata.gz: 7b754c3cf2c094ea2eec2d805b8cf99b760680d74471fadcaa35ea8fe4437d0e05f75bded2ac68e817722b9005e093db3e84f44102d0abd8eacfbf8846c7f1fc
7
+ data.tar.gz: 6244aa2b14352c08cc696319dc93f49d79c44a49f59d8176d572cfc8c71f5aa12e9786a912bc180e98012367be57ee5b6cfb08bf6dd00ca0203e518bead4991c
data/README.md CHANGED
@@ -1,17 +1,25 @@
1
1
  # ActiveAdmin Select Many [![Gem Version](https://badge.fury.io/rb/activeadmin_select_many.svg)](https://badge.fury.io/rb/activeadmin_select_many)
2
2
 
3
- An Active Admin plugin which improves one-to-many and many-to-many associations selection (jQuery required).
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
- - select on the left with available items
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
  ![screenshot](screenshot.png)
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
- ## Examples
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
- ## Options
83
+ ## Example with select_one
63
84
 
64
- - **collection**: local collection
65
- - **placeholder**: placeholder string for search box
66
- - **remote_collection**: JSON path
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)
70
- - **text_key**: key to use as text for select options
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
- // // WORK IN PROGRESS
107
- // var onRemoteSelectOne = smDebounce( function( event ) {
108
- // if( $(this).data( 'searching' ) != '1' ) {
109
- // $(this).data( 'searching', '1' );
110
- // var _this = $(this);
111
- // var data = {}
112
- // var search_key = $(this).data('search') ? $(this).data('search') : 'name_contains';
113
- // var value_key = $(this).data('value') ? $(this).data('value') : 'id';
114
- // var text_key = $(this).data('text') ? $(this).data('text') : 'name';
115
- // data['q['+search_key+']'] = event.key;
116
- // $.ajax({
117
- // context: _this,
118
- // data: data,
119
- // url: $(this).data( 'remote' ),
120
- // complete: function( req, status ) {
121
- // $(this).data( 'searching', '' );
122
- // },
123
- // success: function( data, status, req ) {
124
- // var select = $(this);
125
- // select.empty();
126
- // data.forEach( function( item ) {
127
- // select.append( $('<option>', { value: item[value_key], text: item[text_key] }) );
128
- // });
129
- // },
130
- // });
131
- // }
132
- // }, 400 );
133
- // $('.select_one.input select').on( 'keyup', onRemoteSelectOne );
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
- .search-select
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-left: 30px
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
@@ -3,3 +3,5 @@ en:
3
3
  select_many:
4
4
  available: Available
5
5
  selected: Selected
6
+ select_one:
7
+ status: ''
@@ -1,5 +1,5 @@
1
1
  module ActiveAdmin
2
2
  module SelectMany
3
- VERSION = '0.1.4'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -1,4 +1,4 @@
1
1
  require 'activeadmin/select_many'
2
2
 
3
3
  require 'formtastic/inputs/select_many_input'
4
- require 'formtastic/inputs/select_one_input' # WORK IN PROGRESS
4
+ require 'formtastic/inputs/select_one_input'
@@ -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
- opts = { class: 'selects' }
10
- opts[:sortable] = options[:sortable] if options[:sortable]
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
- search_box_html +
16
- template.content_tag( :span, '' ) +
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' ) +
19
- select_src_html +
20
- buttons_html +
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[: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'}
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[:remote_collection]
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
- # def input_html_options
9
- # super.merge( class: 'select-one' )
10
- # end
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
- search_box <<
17
- select_html
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.1.4
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-26 00:00:00.000000000 Z
11
+ date: 2017-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeadmin