activeadmin_dynamic_fields 0.2.1 → 0.4.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
- SHA1:
3
- metadata.gz: 1c9721fc5faeb42090d36f2f0200317ad41a4903
4
- data.tar.gz: c4b4e0049e7b20bf127b2559f641c4255b67cd2b
2
+ SHA256:
3
+ metadata.gz: bf72bdd2f498f04dd656b044b1b711cd1e4962ad98d9190807ac9b4bfa423ba0
4
+ data.tar.gz: 036c3c43e6b00ed99b900f21216b824b00dccbf3a3d871cdfa331080f30eceb3
5
5
  SHA512:
6
- metadata.gz: 21956e124e0fce3acd285680693af28885ed0c6bb8a5582a32401d27d4640d97e56c7ba66baed5dabe9ed18eec6b1c67e54a88d4c0e3d580bc4169591614345d
7
- data.tar.gz: ace8f765a7fa6d9c6717dc0c95365abb7ddf63a51f354fd967706e1ffc7a28b1f9384120c15b7235975de944cbfb35437ef0ace30375bb0f4b9db092329788e8
6
+ metadata.gz: 94007fc44295a8e251f011ef78884c31a03904e53d264bc9b5aa716645c268a0c3a7343f75b0831f790a36b14f1dcdb03ea6a87fb8e819accb36b871c5617f14
7
+ data.tar.gz: bc6beec26ce12eb2f40c70cc8b9fd12f3cdd416ecf735380fd77343d395e7e7b3f2665cfc2fed8a8339651dcbc12db1eaaae3ce7c6019b5cf6bdf133c4390e31
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017 Mattia Roccoberton
1
+ Copyright (c) 2017-2020 Mattia Roccoberton
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/README.md CHANGED
@@ -1,115 +1,136 @@
1
- # ActiveAdmin Dynamic Fields [![Gem Version](https://badge.fury.io/rb/activeadmin_dynamic_fields.svg)](https://badge.fury.io/rb/activeadmin_dynamic_fields)
1
+ # ActiveAdmin Dynamic Fields [![Gem Version](https://badge.fury.io/rb/activeadmin_dynamic_fields.svg)](https://badge.fury.io/rb/activeadmin_dynamic_fields) [![CircleCI](https://circleci.com/gh/blocknotes/activeadmin_dynamic_fields.svg?style=svg)](https://circleci.com/gh/blocknotes/activeadmin_dynamic_fields)
2
2
 
3
- An Active Admin plugin to add dynamic behaviors to fields.
3
+ An Active Admin plugin to add dynamic behaviors to some fields.
4
4
 
5
5
  Features:
6
-
7
6
  - set conditional checks on fields
8
- - trigger some actions on other fields
7
+ - trigger actions on target elements
9
8
  - inline field editing
10
9
  - create links to load some content in a dialog
11
10
 
12
- The easiest way to show how this plugin works is looking the examples [below](#examples-of-dynamic-fields).
11
+ The easiest way to show how this plugin works is looking the examples [below](#examples).
13
12
 
14
13
  ## Install
15
-
16
14
  - Add to your Gemfile: `gem 'activeadmin_dynamic_fields'`
17
15
  - Execute bundle
18
16
  - Add at the end of your ActiveAdmin javascripts (_app/assets/javascripts/active_admin.js_):
19
- `//= require activeadmin/dynamic_fields`
20
17
 
21
- ## Options
18
+ ```js
19
+ //= require activeadmin/dynamic_fields
20
+ ```
22
21
 
23
- Options are passed to fields using *input_html* parameter as *data* attributes:
22
+ ## Options
23
+ Options are passed to fields using *input_html* parameter as *data* attributes.
24
24
 
25
+ Conditions:
25
26
  - **data-if**: check a condition, values:
26
27
  + **checked**: check if a checkbox is checked
27
28
  + **not_checked**: check if a checkbox is not checked
28
29
  + **blank**: check if a field is blank
29
30
  + **not_blank**: check if a field is not blank
31
+ + **changed**: check if the value of an input is changed (dirty)
30
32
  - **data-eq**: check if a field has a specific value
31
33
  - **data-not**: check if a field hasn't a specific value
32
- - **data-target**: target css selector (from parent fieldset, look for the closest match)
33
- - **data-gtarget**: target css selector globally
34
- - **data-action**: the action to trigger, values:
34
+ - **data-function**: check the return value of a custom function
35
+
36
+ Actions:
37
+ - **data-then**: action to trigger (alias **data-action**), values:
35
38
  + **hide**: hides elements
36
39
  + **slide**: hides elements (using sliding)
37
40
  + **fade**: hides elements (using fading)
38
41
  + **addClass**: adds classes
39
42
  + **setValue**: set a value
40
- + **callback**: call a function
41
- - **data-function**: check the return value of a custom function
42
- - **data-arg**: argument passed to the custom set function (as array of strings)
43
+ + **callback**: call a function (with arguments: **data-args**)
44
+ - **data-else**: action to trigger when the condition check is not true
45
+ - **data-args**: arguments passed to the callback function
46
+
47
+ Targets:
48
+ - **data-target**: target css selector (from parent fieldset, look for the closest match)
49
+ - **data-gtarget**: target css selector globally
50
+
51
+ A check condition or a custom check function are required. A trigger action is required too, unless you are using a custom function (in that case it is optional).
43
52
 
44
53
  ## Examples
45
54
 
46
55
  ### Dynamic fields examples
47
-
48
- - A checkbox that hides other fields if false (ex. model *Article*):
56
+ - A checkbox that hides other fields if is checked (ex. model *Article*):
49
57
 
50
58
  ```rb
51
59
  form do |f|
52
60
  f.inputs 'Article' do
53
- f.input :published, input_html: { data: { if: 'not_checked', action: 'hide', target: '.grp1' } }
61
+ f.input :published, input_html: { data: { if: 'checked', then: 'hide', target: '.grp1' } }
54
62
  f.input :online_date, wrapper_html: { class: 'grp1' }
55
- f.input :position, wrapper_html: { class: 'grp1' }
63
+ f.input :draft_notes, wrapper_html: { class: 'grp1' }
56
64
  end
57
65
  f.actions
58
66
  end
59
67
  ```
60
68
 
61
- - Add 3 classes (*first*, *second*, *third*) if a checkbox is true:
69
+ - Add 3 classes (*first*, *second*, *third*) if a checkbox is not checked, else add "forth" class:
62
70
 
63
- `f.input :published, input_html: { data: { if: 'checked', action: 'addClass first second third', target: '.grp1' } }`
71
+ ```rb
72
+ data = { if: 'not_checked', then: 'addClass first second third', target: '.grp1', else: 'addClass forth' }
73
+ f.input :published, input_html: { data: data }
74
+ ```
64
75
 
65
76
  - Set another field value if a string field is blank:
66
77
 
67
- `f.input :title, input_html: { data: { if: 'blank', action: 'setValue 10', target: '#article_position' } }`
78
+ ```rb
79
+ f.input :title, input_html: { data: { if: 'blank', then: 'setValue 10', target: '#article_position' } }
80
+ ```
68
81
 
69
82
  - Use a custom function for conditional check (*title_not_empty()* must be available on global scope) (with alternative syntax for data attributes):
70
83
 
71
- `f.input :title, input_html: { 'data-function': 'title_empty', 'data-action': 'slide', 'data-target': '#article_description_input' }`
84
+ ```rb
85
+ attrs = { 'data-function': 'title_empty', 'data-then': 'slide', 'data-target': '#article_description_input' }
86
+ f.input :title, input_html: attrs
87
+ ```
72
88
 
73
89
  ```js
74
- function title_empty( el ) {
75
- return ( $('#article_title').val().trim() === '' );
90
+ function title_empty(el) {
91
+ return ($('#article_title').val().trim() === '');
76
92
  }
77
93
  ```
78
94
 
79
95
  - Call a callback function as action:
80
96
 
81
- `f.input :published, input_html: { data: { if: 'checked', action: 'callback set_title', args: '["Unpublished !"]' } }`
97
+ ```rb
98
+ data = { if: 'checked', then: 'callback set_title', args: '["Unpublished !"]' }
99
+ f.input :published, input_html: { data: data }
100
+ ```
82
101
 
83
102
  ```js
84
- function set_title( args ) {
85
- if( $('#article_title').val().trim() === '' ) {
86
- $('#article_title').val( args[0] );
87
- $('#article_title').trigger( 'change' );
103
+ function set_title(args) {
104
+ if($('#article_title').val().trim() === '') {
105
+ $('#article_title').val(args[0]);
106
+ $('#article_title').trigger('change');
88
107
  }
89
108
  }
90
109
  ```
91
110
 
92
111
  - Custom function without action:
93
112
 
94
- `f2.input :category, as: :select, collection: [ [ 'Cat 1', 'cat1' ], [ 'Cat 2', 'cat2' ], [ 'Cat 3', 'cat3' ] ], input_html: { 'data-function': 'on_change_category' }`
113
+ ```rb
114
+ collection = [['Cat 1', 'cat1'], ['Cat 2', 'cat2'], ['Cat 3', 'cat3']]
115
+ f2.input :category, as: :select, collection: collection, input_html: { 'data-function': 'on_change_category' }
116
+ ```
95
117
 
96
118
  ```js
97
- function on_change_category( el ) {
98
- var target = el.closest( 'fieldset' ).find( '.pub' );
99
- target.prop( 'checked', ( el.val() == 'cat2' ) );
100
- target.trigger( 'change' );
119
+ function on_change_category(el) {
120
+ var target = el.closest('fieldset').find('.pub');
121
+ target.prop('checked', (el.val() == 'cat2');
122
+ target.trigger('change');
101
123
  }
102
124
  ```
103
125
 
104
126
  ### Inline editing examples
105
-
106
127
  - Prepare a custom member action to save data, an *update* helper function is available (third parameter is optional, allow to filter using strong parameters):
107
128
 
108
129
  ```rb
109
130
  member_action :save, method: [:post] do
110
- render ActiveAdmin::DynamicFields::update( resource, params )
111
- # render ActiveAdmin::DynamicFields::update( resource, params, [:published] )
112
- # render ActiveAdmin::DynamicFields::update( resource, params, Article::permit_params )
131
+ render ActiveAdmin::DynamicFields.update(resource, params)
132
+ # render ActiveAdmin::DynamicFields.update(resource, params, [:published])
133
+ # render ActiveAdmin::DynamicFields.update(resource, params, Article::permit_params)
113
134
  end
114
135
  ```
115
136
 
@@ -118,29 +139,28 @@ end
118
139
  ```rb
119
140
  # Edit a string:
120
141
  column :title do |row|
121
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
142
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
122
143
  end
123
144
  # Edit a boolean:
124
145
  column :published do |row|
125
- status_tag row.published, ActiveAdmin::DynamicFields::edit_boolean( :published, save_admin_article_path( row.id ), row.published )
146
+ status_tag row.published, ActiveAdmin::DynamicFields.edit_boolean(:published, save_admin_article_path(row.id), row.published)
126
147
  end
127
148
  # Edit a select ([''] allow to have a blank value):
128
149
  column :author do |row|
129
- select ActiveAdmin::DynamicFields::edit_select( :author_id, save_admin_article_path( row.id ) ) do
130
- options_for_select( [''] + Author.pluck( :name, :id ), row.author_id )
150
+ select ActiveAdmin::DynamicFields.edit_select(:author_id, save_admin_article_path(row.id)) do
151
+ options_for_select([''] + Author.pluck(:name, :id), row.author_id)
131
152
  end
132
153
  end
133
154
  ```
134
155
 
135
- - In *show* config (less useful):
156
+ - In *show* config (inside `attributes_table` block):
136
157
  ```rb
137
158
  row :title do |row|
138
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
159
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
139
160
  end
140
161
  ```
141
162
 
142
163
  ### Dialog example
143
-
144
164
  Example with 2 models: *Author* and *Article*
145
165
 
146
166
  Prepare the content dialog - in Active Admin Author config:
@@ -149,12 +169,16 @@ Prepare the content dialog - in Active Admin Author config:
149
169
  ActiveAdmin.register Author do
150
170
  # ...
151
171
  member_action :dialog do
152
- content = '<dl style="margin: 12px">'
153
- [:name, :age, :created_at].each do |field|
154
- content += "<dt>#{Author.human_attribute_name(field)}:</dt><dd>#{resource[field]}</dd>"
172
+ record = resource
173
+ context = Arbre::Context.new do
174
+ dl do
175
+ %i[name age created_at].each do |field|
176
+ dt "#{Author.human_attribute_name(field)}:"
177
+ dd record[field]
178
+ end
179
+ end
155
180
  end
156
- content += '</dl>'
157
- render plain: content
181
+ render plain: context
158
182
  end
159
183
  # ...
160
184
  end
@@ -169,7 +193,7 @@ ActiveAdmin.register Article do
169
193
  attributes_table do
170
194
  # ...
171
195
  row :author do
172
- link_to object.author.name, dialog_admin_author_path( object.author ), title: object.author.name, 'data-df-dialog': true, 'data-df-icon': true
196
+ link_to object.author.name, dialog_admin_author_path(object.author), title: object.author.name, 'data-df-dialog': true, 'data-df-icon': true
173
197
  end
174
198
  end
175
199
  end
@@ -180,13 +204,13 @@ end
180
204
  The link url is loaded via AJAX before opening the dialog.
181
205
 
182
206
  ## Do you like it? Star it!
183
-
184
207
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
185
208
 
186
- ## Contributors
209
+ 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.
187
210
 
188
- - [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
211
+ ## Contributors
212
+ - [Mattia Roccoberton](http://blocknot.es): author
213
+ - The good guys that opened issues and pull requests from time to time
189
214
 
190
215
  ## License
191
-
192
- [MIT](LICENSE.txt)
216
+ The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
@@ -1,209 +1,210 @@
1
- // Evaluate a condition
2
- function dfEvalCondition( el, args ) {
3
- if( args.fn ) {
4
- if( args.fn && window[args.fn] ) return !window[args.fn]( el );
5
- else console.log( 'Warning - activeadmin_dynamic_fields: ' + args.fn + '() not available [1]' );
6
- }
7
- else if( args.if == 'checked' ) {
8
- return el.is(':checked');
9
- }
10
- else if( args.if == 'not_checked' ) {
11
- return !el.is(':checked');
12
- }
13
- else if( args.if == 'blank' ) {
14
- return el.val().length === 0 || !el.val().trim();
15
- }
16
- else if( args.if == 'not_blank' ) {
17
- return el.val().length !== 0 && el.val().trim();
18
- }
19
- else if( args.eq ) {
20
- return el.val() == args.eq;
21
- }
22
- else if( args.not ) {
23
- return el.val() != args.not;
24
- }
25
- return undefined;
26
- }
27
-
28
- // Prepare a field
29
- function dfSetupField( el ) {
30
- var action = el.data( 'action' );
31
- var target, args = {};
32
- args.if = el.data( 'if' );
33
- args.eq = el.data( 'eq' );
34
- args.not = el.data( 'not' );
35
- args.fn = el.data( 'function' );
36
- if( el.data( 'target' ) ) target = el.closest( 'fieldset' ).find( el.data( 'target' ) ); // closest find for has many associations
37
- else if( el.data( 'gtarget' ) ) target = $( el.data( 'gtarget' ) );
38
- if( action == 'hide' ) {
39
- if( dfEvalCondition( el, args ) ) target.hide();
40
- else target.show();
41
- el.on( 'change', function( event ) {
42
- if( dfEvalCondition( $(this), args ) ) target.hide();
43
- else target.show();
44
- });
45
- }
46
- else if( action == 'slide' ) {
47
- if( dfEvalCondition( el, args ) ) target.slideDown();
48
- else target.slideUp();
49
- el.on( 'change', function( event ) {
50
- if( dfEvalCondition( $(this), args ) ) target.slideDown();
51
- else target.slideUp();
52
- });
1
+ (function () {
2
+ 'use strict'
3
+
4
+ const ACTIONS = {
5
+ addClass: (el, name) => el.addClass(name),
6
+ callback: (el, name) => {
7
+ if (window[name]) window[name](el.data('args'))
8
+ else {
9
+ el.attr('data-df-errors', 'callback function not found')
10
+ console.warn(`activeadmin_dynamic_fields callback function not found: ${name}`)
11
+ }
12
+ },
13
+ fade: el => el.fadeOut(),
14
+ hide: el => el.hide(),
15
+ setValue: (el, value) => {
16
+ if (el.attr('type') == 'checkbox') el.prop('checked', value == '1')
17
+ else el.val(value)
18
+ el.trigger('change')
19
+ },
20
+ slide: el => el.slideUp()
53
21
  }
54
- else if( action == 'fade' ) {
55
- if( dfEvalCondition( el, args ) ) target.fadeIn();
56
- else target.fadeOut();
57
- el.on( 'change', function( event ) {
58
- if( dfEvalCondition( $(this), args ) ) target.fadeIn();
59
- else target.fadeOut();
60
- });
22
+
23
+ const CONDITIONS = {
24
+ blank: el => el.val().length === 0 || !el.val().trim(),
25
+ changed: _el => true,
26
+ checked: el => el.is(':checked'),
27
+ eq: (el, value) => el.val() == value,
28
+ not: (el, value) => el.val() != value,
29
+ not_blank: el => el.val().trim(),
30
+ not_checked: el => !el.is(':checked')
61
31
  }
62
- else if( action.substr( 0, 8 ) == 'setValue' ) {
63
- var val = action.substr( 8 ).trim();
64
- if( dfEvalCondition( el, args ) ) dfSetValue( target, val );
65
- el.on( 'change', function( event ) {
66
- if( dfEvalCondition( $(this), args ) ) dfSetValue( target, val );
67
- });
32
+
33
+ const REVERSE_ACTIONS = {
34
+ addClass: (el, name) => el.removeClass(name),
35
+ fade: el => el.fadeIn(),
36
+ hide: el => el.show(),
37
+ slide: el => el.slideDown()
68
38
  }
69
- else if( action.substr( 0, 8 ) == 'callback' ) {
70
- var cb = action.substr( 8 ).trim();
71
- if( cb && window[cb] ) {
72
- if( dfEvalCondition( el, args ) ) window[cb]( el.data( 'args' ) );
73
- el.on( 'change', function( event ) {
74
- if( dfEvalCondition( $(this), args ) ) window[cb]( el.data( 'args' ) );
75
- });
39
+
40
+ class Field {
41
+ constructor(el) {
42
+ const action = el.data('then') || el.data('action') || ''
43
+ const action_name = action.split(' ', 1)[0]
44
+ const else_action = el.data('else') || ''
45
+ const else_action_name = else_action.split(' ', 1)[0]
46
+
47
+ this.el = el
48
+ this.action = ACTIONS[action_name]
49
+ this.action_arg = action.substring(action.indexOf(' ') + 1)
50
+ this.reverse_action = REVERSE_ACTIONS[action_name]
51
+ this.else_action = ACTIONS[else_action_name]
52
+ this.else_action_arg = else_action.substring(else_action.indexOf(' ') + 1)
53
+ this.else_reverse_action = REVERSE_ACTIONS[else_action_name]
54
+ this.condition = CONDITIONS[el.data('if')]
55
+ if (!this.condition && el.data('eq')) {
56
+ [this.condition, this.condition_arg] = [CONDITIONS['eq'], el.data('eq')]
57
+ }
58
+ if (!this.condition && el.data('not')) {
59
+ [this.condition, this.condition_arg] = [CONDITIONS['not'], el.data('not')]
60
+ }
61
+ this.custom_function = el.data('function')
62
+ if (!this.condition && this.custom_function) {
63
+ this.condition = window[this.custom_function]
64
+ if (!this.condition) {
65
+ el.attr('data-df-errors', 'custom function not found')
66
+ console.warn(`activeadmin_dynamic_fields custom function not found: ${this.custom_function}`)
67
+ }
68
+ }
69
+
70
+ // closest find for has many associations
71
+ if (el.data('target')) this.target = el.closest('fieldset').find(el.data('target'))
72
+ else if (el.data('gtarget')) this.target = $(el.data('gtarget'))
73
+ if (action_name == 'callback') this.target = el
74
+ }
75
+
76
+ apply(el) {
77
+ if (this.condition(el, this.condition_arg)) {
78
+ if (this.else_reverse_action) this.else_reverse_action(this.target, this.else_action_arg)
79
+ this.action(this.target, this.action_arg)
80
+ }
81
+ else {
82
+ if (this.reverse_action) this.reverse_action(this.target, this.action_arg)
83
+ if (this.else_action) this.else_action(this.target, this.else_action_arg)
84
+ }
85
+ }
86
+
87
+ is_valid() {
88
+ if (!this.condition) return false
89
+ if (!this.action && !this.custom_function) return false
90
+
91
+ return true
92
+ }
93
+
94
+ setup() {
95
+ if (!this.is_valid()) return
96
+ if (this.el.data('if') != 'changed') this.apply(this.el)
97
+ this.el.on('change', () => this.apply(this.el))
76
98
  }
77
- else console.log( 'Warning - activeadmin_dynamic_fields: ' + cb + '() not available [2]' );
78
- }
79
- else if( action.substr( 0, 8 ) == 'addClass' ) {
80
- var classes = action.substr( 8 ).trim();
81
- if( dfEvalCondition( el, args ) ) target.removeClass( classes );
82
- else target.addClass( classes );
83
- el.on( 'change', function( event ) {
84
- if( dfEvalCondition( $(this), args ) ) target.removeClass( classes );
85
- else target.addClass( classes );
86
- });
87
- }
88
- else if( args.fn ) { // function without action
89
- dfEvalCondition( el, args );
90
- el.on( 'change', function( event ) {
91
- dfEvalCondition( el, args );
92
- });
93
99
  }
94
- }
95
-
96
- // Set the value of an element
97
- function dfSetValue( el, val ) {
98
- if( el.attr('type') != 'checkbox' ) el.val( val );
99
- else el.prop('checked', val == '1');
100
- el.trigger( 'change' );
101
- }
102
-
103
- // Inline update - must be called binded on the editing element
104
- function dfUpdateField() {
105
- if( $(this).data( 'loading' ) != '1' ) {
106
- $(this).data( 'loading', '1' );
107
- var _this = $(this);
108
- var type = $(this).data( 'field-type' );
109
- var new_value;
110
- if( type == 'boolean' ) new_value = !$(this).data( 'field-value' );
111
- else if( type == 'select' ) new_value = $(this).val();
112
- else new_value = $(this).text();
113
- var data = {};
114
- data[$(this).data('field')] = new_value;
115
- $.ajax({
116
- context: _this,
117
- data: { data: data },
118
- method: 'POST',
119
- url: $(this).data( 'save-url' ),
120
- complete: function( req, status ) {
121
- $(this).data( 'loading', '0' );
122
- },
123
- success: function( data, status, req ) {
124
- if( data.status == 'error' ) {
125
- if( $(this).data( 'show-errors' ) ) {
126
- var result = '';
127
- var message = data.message;
128
- for( var key in message ) {
129
- if( typeof( message[key] ) === 'object' ) {
130
- if( result ) result += ' - ';
131
- result += key + ': ' + message[key].join( '; ' );
100
+
101
+ // Inline update - must be called binded on the editing element
102
+ function dfUpdateField() {
103
+ if ($(this).data('loading') != '1') {
104
+ $(this).data('loading', '1');
105
+ let _this = $(this);
106
+ let type = $(this).data('field-type');
107
+ let new_value;
108
+ if (type == 'boolean') new_value = !$(this).data('field-value');
109
+ else if (type == 'select') new_value = $(this).val();
110
+ else new_value = $(this).text();
111
+ let data = {};
112
+ data[$(this).data('field')] = new_value;
113
+ $.ajax({
114
+ context: _this,
115
+ data: { data: data },
116
+ method: 'POST',
117
+ url: $(this).data('save-url'),
118
+ complete: function (req, status) {
119
+ $(this).data('loading', '0');
120
+ },
121
+ success: function (data, status, req) {
122
+ if (data.status == 'error') {
123
+ if ($(this).data('show-errors')) {
124
+ let result = '';
125
+ let message = data.message;
126
+ for (let key in message) {
127
+ if (typeof (message[key]) === 'object') {
128
+ if (result) result += ' - ';
129
+ result += key + ': ' + message[key].join('; ');
130
+ }
132
131
  }
132
+ if (result) alert(result);
133
133
  }
134
- if( result ) alert( result );
135
134
  }
136
- }
137
- else {
138
- $(this).data( 'field-value', new_value );
139
- if( $(this).data('content') ) {
140
- var old_text = $(this).text();
141
- var old_class = $(this).attr( 'class' );
142
- var content = $($(this).data('content'));
143
- $(this).text( content.text() );
144
- $(this).attr( 'class', content.attr( 'class' ) );
145
- content.text( old_text );
146
- content.attr( 'class', old_class );
147
- $(this).data( 'content', content );
135
+ else {
136
+ $(this).data('field-value', new_value);
137
+ if ($(this).data('content')) {
138
+ let old_text = $(this).text();
139
+ let old_class = $(this).attr('class');
140
+ let content = $($(this).data('content'));
141
+ $(this).text(content.text());
142
+ $(this).attr('class', content.attr('class'));
143
+ content.text(old_text);
144
+ content.attr('class', old_class);
145
+ $(this).data('content', content);
146
+ }
148
147
  }
149
148
  }
150
- },
151
- // error: function( req, status, error ) {
152
- // // if( $(this).data( 'show-errors' ) && req.responseJSON.message ) { }
153
- // },
154
- });
155
- }
156
- }
157
-
158
- // Init
159
- $(document).ready( function() {
160
- // Setup dynamic fields
161
- $('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each( function() {
162
- dfSetupField( $(this) );
163
- });
164
- // Setup dynamic fields for has many associations
165
- $('.active_admin .has_many_container').on( 'has_many_add:after', function( e, fieldset, container ) {
166
- $('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each( function() {
167
- dfSetupField( $(this) );
168
- });
169
- });
170
- // Set dialog icon link
171
- $('.active_admin [data-df-icon]').each( function() {
172
- $(this).append( ' &raquo;' ); // ' &bullet;'
173
- });
174
- // Open content in dialog
175
- $('.active_admin [data-df-dialog]').on( 'click', function( event ) {
176
- event.preventDefault();
177
- $(this).blur();
178
- if( $('#df-dialog').data( 'loading' ) != '1' ) {
179
- $('#df-dialog').data( 'loading', '1' );
180
- if( $('#df-dialog').length == 0 ) $('body').append( '<div id="df-dialog"></div>' );
181
- var title = $(this).attr( 'title' );
182
- $.ajax({
183
- url: $(this).attr( 'href' ),
184
- complete: function( req, status ) {
185
- $('#df-dialog').data( 'loading', '0' );
186
- },
187
- success: function( data, status, req ) {
188
- if( title ) $('#df-dialog').attr( 'title', title );
189
- $('#df-dialog').html( data );
190
- $('#df-dialog').dialog({ modal: true });
191
- },
192
149
  });
193
150
  }
194
- });
195
- // Inline editing
196
- $('[data-field][data-field-type="boolean"][data-save-url]').each( function() {
197
- $(this).on( 'click', $.proxy( dfUpdateField, $(this) ) );
198
- });
199
- $('[data-field][data-field-type="string"][data-save-url]' ).each( function() {
200
- $(this).data( 'field-value', $(this).text() );
201
- var fnUpdate = $.proxy( dfUpdateField, $(this) );
202
- $(this).on( 'blur', function() {
203
- if( $(this).data( 'field-value' ) != $(this).text() ) fnUpdate();
204
- });
205
- });
206
- $('[data-field][data-field-type="select"][data-save-url]').each( function() {
207
- $(this).on( 'change', $.proxy( dfUpdateField, $(this) ) );
208
- });
209
- });
151
+ }
152
+
153
+ // Init
154
+ $(document).ready(function () {
155
+ // Setup dynamic fields
156
+ const selectors = '.active_admin .input [data-if], .active_admin .input [data-eq], .active_admin .input [data-not], .active_admin .input [data-function]'
157
+ $(selectors).each(function () {
158
+ new Field($(this)).setup()
159
+ })
160
+
161
+ // Setup dynamic fields for associations
162
+ $('.active_admin .has_many_container').on('has_many_add:after', () => {
163
+ $(selectors).each(function () {
164
+ new Field($(this)).setup()
165
+ })
166
+ })
167
+
168
+ // Set dialog icon link
169
+ $('.active_admin [data-df-icon]').each(function () {
170
+ $(this).append(' &raquo;')
171
+ })
172
+
173
+ // Open content in dialog
174
+ $('.active_admin [data-df-dialog]').on('click', function (event) {
175
+ event.preventDefault()
176
+ $(this).blur()
177
+ if ($('#df-dialog').data('loading') != '1') {
178
+ $('#df-dialog').data('loading', '1')
179
+ if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>')
180
+ let title = $(this).attr('title')
181
+ $.ajax({
182
+ url: $(this).attr('href'),
183
+ complete: function (req, status) {
184
+ $('#df-dialog').data('loading', '0')
185
+ },
186
+ success: function (data, status, req) {
187
+ if (title) $('#df-dialog').attr('title', title)
188
+ $('#df-dialog').html(data)
189
+ $('#df-dialog').dialog({ modal: true })
190
+ },
191
+ })
192
+ }
193
+ })
194
+
195
+ // Inline editing
196
+ $('[data-field][data-field-type="boolean"][data-save-url]').each(function () {
197
+ $(this).on('click', $.proxy(dfUpdateField, $(this)))
198
+ })
199
+ $('[data-field][data-field-type="string"][data-save-url]').each(function () {
200
+ $(this).data('field-value', $(this).text())
201
+ let fnUpdate = $.proxy(dfUpdateField, $(this))
202
+ $(this).on('blur', function () {
203
+ if ($(this).data('field-value') != $(this).text()) fnUpdate()
204
+ })
205
+ })
206
+ $('[data-field][data-field-type="select"][data-save-url]').each(function () {
207
+ $(this).on('change', $.proxy(dfUpdateField, $(this)))
208
+ })
209
+ })
210
+ })()
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'activeadmin/dynamic_fields/engine'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_admin'
2
4
 
3
5
  module ActiveAdmin
@@ -6,21 +8,39 @@ module ActiveAdmin
6
8
  engine_name 'activeadmin_dynamic_fields'
7
9
  end
8
10
 
9
- def self.edit_boolean( field, url, value )
10
- { 'data-field': field, 'data-field-type': 'boolean', 'data-field-value': value, 'data-content': "<span class=\"status_tag changed\">#{value ? 'no' : 'yes'}</span>", 'data-save-url': url, 'data-show-errors': '1' }
11
+ def self.edit_boolean(field, url, value)
12
+ {
13
+ 'data-field': field,
14
+ 'data-field-type': 'boolean',
15
+ 'data-field-value': value,
16
+ 'data-content': "<span class=\"status_tag changed\">#{value ? 'no' : 'yes'}</span>",
17
+ 'data-save-url': url,
18
+ 'data-show-errors': '1'
19
+ }
11
20
  end
12
21
 
13
- def self.edit_select( field, url )
14
- { 'data-field': field, 'data-field-type': 'select', 'data-save-url': url, 'data-show-errors': '1' }
22
+ def self.edit_select(field, url)
23
+ {
24
+ 'data-field': field,
25
+ 'data-field-type': 'select',
26
+ 'data-save-url': url,
27
+ 'data-show-errors': '1'
28
+ }
15
29
  end
16
30
 
17
- def self.edit_string( field, url )
18
- { contenteditable: true, 'data-field': field, 'data-field-type': 'string', 'data-save-url': url, 'data-show-errors': '1' }
31
+ def self.edit_string(field, url)
32
+ {
33
+ contenteditable: true,
34
+ 'data-field': field,
35
+ 'data-field-type': 'string',
36
+ 'data-save-url': url,
37
+ 'data-show-errors': '1'
38
+ }
19
39
  end
20
40
 
21
- def self.update( resource, params, permit_params = nil )
41
+ def self.update(resource, params, permit_params = nil)
22
42
  if params[:data]
23
- if resource.update( permit_params ? params[:data].permit( permit_params ) : params[:data].permit! )
43
+ if resource.update(permit_params ? params[:data].permit(permit_params) : params[:data].permit!)
24
44
  { json: { status: 'ok' } }
25
45
  else
26
46
  { json: { status: 'error', message: resource.errors } }
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveAdmin
2
4
  module DynamicFields
3
- VERSION = '0.2.1'
5
+ VERSION = '0.4.0'
4
6
  end
5
7
  end
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'activeadmin/dynamic_fields'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin_dynamic_fields
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
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: 2018-02-24 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeadmin
@@ -16,26 +16,149 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
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: '1.0'
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activestorage
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.3.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.3.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: capybara
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.33.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.33.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.13.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.13.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: puma
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.3.5
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.3.5
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec_junit_formatter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.4.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.4.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 4.0.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.90.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.90.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: selenium-webdriver
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 3.142.7
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 3.142.7
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 1.4.2
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 1.4.2
27
153
  description: An Active Admin plugin to add dynamic behaviors to fields
28
154
  email: mat@blocknot.es
29
155
  executables: []
30
156
  extensions: []
31
157
  extra_rdoc_files: []
32
158
  files:
33
- - ".gitignore"
34
- - Gemfile
35
159
  - LICENSE.txt
36
160
  - README.md
37
161
  - Rakefile
38
- - activeadmin_dynamic_fields.gemspec
39
162
  - app/assets/javascripts/activeadmin/dynamic_fields.js
40
163
  - lib/activeadmin/dynamic_fields.rb
41
164
  - lib/activeadmin/dynamic_fields/engine.rb
@@ -45,7 +168,7 @@ homepage: https://github.com/blocknotes/activeadmin_dynamic_fields
45
168
  licenses:
46
169
  - MIT
47
170
  metadata: {}
48
- post_install_message:
171
+ post_install_message:
49
172
  rdoc_options: []
50
173
  require_paths:
51
174
  - lib
@@ -60,9 +183,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
183
  - !ruby/object:Gem::Version
61
184
  version: '0'
62
185
  requirements: []
63
- rubyforge_project:
64
- rubygems_version: 2.6.14
65
- signing_key:
186
+ rubygems_version: 3.0.3
187
+ signing_key:
66
188
  specification_version: 4
67
189
  summary: Dynamic fields for ActiveAdmin
68
190
  test_files: []
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- _misc/
2
- Gemfile.lock
3
- *.orig
4
-
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
@@ -1,19 +0,0 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'activeadmin/dynamic_fields/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'activeadmin_dynamic_fields'
7
- spec.version = ActiveAdmin::DynamicFields::VERSION
8
- spec.summary = 'Dynamic fields for ActiveAdmin'
9
- spec.description = 'An Active Admin plugin to add dynamic behaviors to fields'
10
- spec.license = 'MIT'
11
- spec.authors = ['Mattia Roccoberton']
12
- spec.email = 'mat@blocknot.es'
13
- spec.homepage = 'https://github.com/blocknotes/activeadmin_dynamic_fields'
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