activeadmin_dynamic_fields 0.2.2 → 0.4.2

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: a41854a61414d8eb8f8c35556fd0cbfdfa8364ed
4
- data.tar.gz: 85340ca45697226edb75f74a5189c812400e19cd
2
+ SHA256:
3
+ metadata.gz: 703a3fcf46aa777dda6ed22421da5a937027a19de733cc461da566d892bf207a
4
+ data.tar.gz: c5c389c17b60a395a77d0d9d92a0171af83d5f94fb102017c3b01adbf10f7857
5
5
  SHA512:
6
- metadata.gz: 72f93305d28ba3134437e19d062cc03a654113776d8aecfceb78f06d0939caeb8a3e01e4bca0f273db11be3b4358d2a7d72a57fbd838248c4af6108c54a44d42
7
- data.tar.gz: 45231b55baf2d328e05e83d843285858487067f7798859c8b0d5fbfee11663e282c9230a41a8a5efed1a71082f68e1892c31e06fbc09893f9d12f69f6986ddec
6
+ metadata.gz: 3f45524f227c1d73c64eea3f8660aee6e8054617c17f969ed85f3c131048678d8fc0988c24d5421b7a27b6a8787135a0957c8de1d5c7033e13fa92f25a504c6c
7
+ data.tar.gz: a77b5396c95012b32f2a682c7bb40974f9e60e9f65fc8a0a890595f968a9114ba3c883c3f19f1352b1f84ef8c9ea97a07024887c1209c6e63043a57c5d5cdf19
@@ -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,116 +1,138 @@
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
- + **checked**: check if a checkbox is checked
27
+ + **checked**: check if a checkbox is checked (ex. `"data-if": "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
30
31
  + **changed**: check if the value of an input is changed (dirty)
31
- - **data-eq**: check if a field has a specific value
32
- - **data-not**: check if a field hasn't a specific value
33
- - **data-target**: target css selector (from parent fieldset, look for the closest match)
34
- - **data-gtarget**: target css selector globally
35
- - **data-action**: the action to trigger, values:
36
- + **hide**: hides elements
32
+ - **data-eq**: check if a field has a specific value (ex. `"data-eq": "42"`)
33
+ - **data-not**: check if a field has not a specific value
34
+ - **data-function**: check the return value of a custom function (ex. `"data-function": "my_check"`)
35
+
36
+ Actions:
37
+ - **data-then**: action to trigger (alias **data-action**), values:
38
+ + **hide**: hides elements (ex. `"data-then": "hide", "data-target": ".errors"`)
37
39
  + **slide**: hides elements (using sliding)
38
40
  + **fade**: hides elements (using fading)
39
- + **addClass**: adds classes
40
- + **setValue**: set a value
41
- + **callback**: call a function
42
- - **data-function**: check the return value of a custom function
43
- - **data-arg**: argument passed to the custom set function (as array of strings)
41
+ + **addClass**: adds classes (ex. `"data-then": "addClass red"`)
42
+ + **addStyle**: adds some styles (ex. `"data-then": "addStyle color: #fb1; font-size: 12px"`)
43
+ + **setText**: set the text of an element (ex. `"data-then": "setText A sample text"`)
44
+ + **setValue**: set the value of an input element (ex. `"data-then": "setValue A sample value"`)
45
+ + **callback**: call a function (with arguments: **data-args**) (ex. `"data-then": "callback a_fun"`)
46
+ - **data-else**: action to trigger when the condition check is not true
47
+ - **data-args**: arguments passed to the callback function
48
+
49
+ Targets:
50
+ - **data-target**: target css selector (from parent fieldset, look for the closest match)
51
+ - **data-gtarget**: target css selector globally
52
+
53
+ 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).
44
54
 
45
55
  ## Examples
46
56
 
47
57
  ### Dynamic fields examples
48
-
49
- - A checkbox that hides other fields if false (ex. model *Article*):
58
+ - A checkbox that hides other fields if is checked (ex. model *Article*):
50
59
 
51
60
  ```rb
52
61
  form do |f|
53
62
  f.inputs 'Article' do
54
- f.input :published, input_html: { data: { if: 'not_checked', action: 'hide', target: '.grp1' } }
63
+ f.input :published, input_html: { data: { if: 'checked', then: 'hide', target: '.grp1' } }
55
64
  f.input :online_date, wrapper_html: { class: 'grp1' }
56
- f.input :position, wrapper_html: { class: 'grp1' }
65
+ f.input :draft_notes, wrapper_html: { class: 'grp1' }
57
66
  end
58
67
  f.actions
59
68
  end
60
69
  ```
61
70
 
62
- - Add 3 classes (*first*, *second*, *third*) if a checkbox is true:
71
+ - Add 3 classes (*first*, *second*, *third*) if a checkbox is not checked, else add "forth" class:
63
72
 
64
- `f.input :published, input_html: { data: { if: 'checked', action: 'addClass first second third', target: '.grp1' } }`
73
+ ```rb
74
+ data = { if: 'not_checked', then: 'addClass first second third', target: '.grp1', else: 'addClass forth' }
75
+ f.input :published, input_html: { data: data }
76
+ ```
65
77
 
66
78
  - Set another field value if a string field is blank:
67
79
 
68
- `f.input :title, input_html: { data: { if: 'blank', action: 'setValue 10', target: '#article_position' } }`
80
+ ```rb
81
+ f.input :title, input_html: { data: { if: 'blank', then: 'setValue 10', target: '#article_position' } }
82
+ ```
69
83
 
70
84
  - Use a custom function for conditional check (*title_not_empty()* must be available on global scope) (with alternative syntax for data attributes):
71
85
 
72
- `f.input :title, input_html: { 'data-function': 'title_empty', 'data-action': 'slide', 'data-target': '#article_description_input' }`
86
+ ```rb
87
+ attrs = { 'data-function': 'title_empty', 'data-then': 'slide', 'data-target': '#article_description_input' }
88
+ f.input :title, input_html: attrs
89
+ ```
73
90
 
74
91
  ```js
75
- function title_empty( el ) {
76
- return ( $('#article_title').val().trim() === '' );
92
+ function title_empty(el) {
93
+ return ($('#article_title').val().trim() === '');
77
94
  }
78
95
  ```
79
96
 
80
97
  - Call a callback function as action:
81
98
 
82
- `f.input :published, input_html: { data: { if: 'checked', action: 'callback set_title', args: '["Unpublished !"]' } }`
99
+ ```rb
100
+ data = { if: 'checked', then: 'callback set_title', args: '["Unpublished !"]' }
101
+ f.input :published, input_html: { data: data }
102
+ ```
83
103
 
84
104
  ```js
85
- function set_title( args ) {
86
- if( $('#article_title').val().trim() === '' ) {
87
- $('#article_title').val( args[0] );
88
- $('#article_title').trigger( 'change' );
105
+ function set_title(args) {
106
+ if($('#article_title').val().trim() === '') {
107
+ $('#article_title').val(args[0]);
108
+ $('#article_title').trigger('change');
89
109
  }
90
110
  }
91
111
  ```
92
112
 
93
113
  - Custom function without action:
94
114
 
95
- `f2.input :category, as: :select, collection: [ [ 'Cat 1', 'cat1' ], [ 'Cat 2', 'cat2' ], [ 'Cat 3', 'cat3' ] ], input_html: { 'data-function': 'on_change_category' }`
115
+ ```rb
116
+ collection = [['Cat 1', 'cat1'], ['Cat 2', 'cat2'], ['Cat 3', 'cat3']]
117
+ f2.input :category, as: :select, collection: collection, input_html: { 'data-function': 'on_change_category' }
118
+ ```
96
119
 
97
120
  ```js
98
- function on_change_category( el ) {
99
- var target = el.closest( 'fieldset' ).find( '.pub' );
100
- target.prop( 'checked', ( el.val() == 'cat2' ) );
101
- target.trigger( 'change' );
121
+ function on_change_category(el) {
122
+ var target = el.closest('fieldset').find('.pub');
123
+ target.prop('checked', (el.val() == 'cat2');
124
+ target.trigger('change');
102
125
  }
103
126
  ```
104
127
 
105
128
  ### Inline editing examples
106
-
107
129
  - Prepare a custom member action to save data, an *update* helper function is available (third parameter is optional, allow to filter using strong parameters):
108
130
 
109
131
  ```rb
110
132
  member_action :save, method: [:post] do
111
- render ActiveAdmin::DynamicFields::update( resource, params )
112
- # render ActiveAdmin::DynamicFields::update( resource, params, [:published] )
113
- # render ActiveAdmin::DynamicFields::update( resource, params, Article::permit_params )
133
+ render ActiveAdmin::DynamicFields.update(resource, params)
134
+ # render ActiveAdmin::DynamicFields.update(resource, params, [:published])
135
+ # render ActiveAdmin::DynamicFields.update(resource, params, Article::permit_params)
114
136
  end
115
137
  ```
116
138
 
@@ -119,29 +141,28 @@ end
119
141
  ```rb
120
142
  # Edit a string:
121
143
  column :title do |row|
122
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
144
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
123
145
  end
124
146
  # Edit a boolean:
125
147
  column :published do |row|
126
- status_tag row.published, ActiveAdmin::DynamicFields::edit_boolean( :published, save_admin_article_path( row.id ), row.published )
148
+ status_tag row.published, ActiveAdmin::DynamicFields.edit_boolean(:published, save_admin_article_path(row.id), row.published)
127
149
  end
128
150
  # Edit a select ([''] allow to have a blank value):
129
151
  column :author do |row|
130
- select ActiveAdmin::DynamicFields::edit_select( :author_id, save_admin_article_path( row.id ) ) do
131
- options_for_select( [''] + Author.pluck( :name, :id ), row.author_id )
152
+ select ActiveAdmin::DynamicFields.edit_select(:author_id, save_admin_article_path(row.id)) do
153
+ options_for_select([''] + Author.pluck(:name, :id), row.author_id)
132
154
  end
133
155
  end
134
156
  ```
135
157
 
136
- - In *show* config (less useful):
158
+ - In *show* config (inside `attributes_table` block):
137
159
  ```rb
138
160
  row :title do |row|
139
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
161
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
140
162
  end
141
163
  ```
142
164
 
143
165
  ### Dialog example
144
-
145
166
  Example with 2 models: *Author* and *Article*
146
167
 
147
168
  Prepare the content dialog - in Active Admin Author config:
@@ -150,12 +171,16 @@ Prepare the content dialog - in Active Admin Author config:
150
171
  ActiveAdmin.register Author do
151
172
  # ...
152
173
  member_action :dialog do
153
- content = '<dl style="margin: 12px">'
154
- [:name, :age, :created_at].each do |field|
155
- content += "<dt>#{Author.human_attribute_name(field)}:</dt><dd>#{resource[field]}</dd>"
174
+ record = resource
175
+ context = Arbre::Context.new do
176
+ dl do
177
+ %i[name age created_at].each do |field|
178
+ dt "#{Author.human_attribute_name(field)}:"
179
+ dd record[field]
180
+ end
181
+ end
156
182
  end
157
- content += '</dl>'
158
- render plain: content
183
+ render plain: context
159
184
  end
160
185
  # ...
161
186
  end
@@ -170,7 +195,7 @@ ActiveAdmin.register Article do
170
195
  attributes_table do
171
196
  # ...
172
197
  row :author do
173
- link_to object.author.name, dialog_admin_author_path( object.author ), title: object.author.name, 'data-df-dialog': true, 'data-df-icon': true
198
+ link_to object.author.name, dialog_admin_author_path(object.author), title: object.author.name, 'data-df-dialog': true, 'data-df-icon': true
174
199
  end
175
200
  end
176
201
  end
@@ -181,13 +206,13 @@ end
181
206
  The link url is loaded via AJAX before opening the dialog.
182
207
 
183
208
  ## Do you like it? Star it!
184
-
185
209
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
186
210
 
187
- ## Contributors
211
+ 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.
188
212
 
189
- - [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
213
+ ## Contributors
214
+ - [Mattia Roccoberton](http://blocknot.es): author
215
+ - The good guys that opened issues and pull requests from time to time
190
216
 
191
217
  ## License
192
-
193
- [MIT](LICENSE.txt)
218
+ 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,212 +1,221 @@
1
- // Evaluate a condition
2
- function dfEvalCondition( el, args, on_change ) {
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.if == 'changed' ) {
20
- return on_change;
21
- }
22
- else if( args.eq ) {
23
- return el.val() == args.eq;
24
- }
25
- else if( args.not ) {
26
- return el.val() != args.not;
27
- }
28
- return undefined;
29
- }
30
-
31
- // Prepare a field
32
- function dfSetupField( el ) {
33
- var action = el.data( 'action' );
34
- var target, args = {};
35
- args.if = el.data( 'if' );
36
- args.eq = el.data( 'eq' );
37
- args.not = el.data( 'not' );
38
- args.fn = el.data( 'function' );
39
- if( el.data( 'target' ) ) target = el.closest( 'fieldset' ).find( el.data( 'target' ) ); // closest find for has many associations
40
- else if( el.data( 'gtarget' ) ) target = $( el.data( 'gtarget' ) );
41
- if( action == 'hide' ) {
42
- if( dfEvalCondition( el, args, false ) ) target.hide();
43
- else target.show();
44
- el.on( 'change', function( event ) {
45
- if( dfEvalCondition( $(this), args, true ) ) target.hide();
46
- else target.show();
47
- });
48
- }
49
- else if( action == 'slide' ) {
50
- if( dfEvalCondition( el, args, false ) ) target.slideDown();
51
- else target.slideUp();
52
- el.on( 'change', function( event ) {
53
- if( dfEvalCondition( $(this), args, true ) ) target.slideDown();
54
- else target.slideUp();
55
- });
1
+ (function () {
2
+ 'use strict'
3
+
4
+ const ACTIONS = {
5
+ addClass: (el, name) => el.addClass(name),
6
+ addStyle: (el, extra_style) => {
7
+ let style = (el.attr('style') || '').trim()
8
+ if (!style.includes(extra_style)) {
9
+ if (style) style = style.replace(/;$/, '') + '; ' // ensure style ends with ;
10
+ el.attr('style', `${style}${extra_style}`)
11
+ }
12
+ },
13
+ callback: (el, name) => {
14
+ if (window[name]) window[name](el.data('args'))
15
+ else {
16
+ el.attr('data-df-errors', 'callback function not found')
17
+ console.warn(`activeadmin_dynamic_fields callback function not found: ${name}`)
18
+ }
19
+ },
20
+ fade: el => el.fadeOut(),
21
+ hide: el => el.hide(),
22
+ setText: (el, text) => el.text(text),
23
+ setValue: (el, value) => {
24
+ if (el.attr('type') == 'checkbox') el.prop('checked', value == '1')
25
+ else el.val(value)
26
+ el.trigger('change')
27
+ },
28
+ slide: el => el.slideUp()
56
29
  }
57
- else if( action == 'fade' ) {
58
- if( dfEvalCondition( el, args, false ) ) target.fadeIn();
59
- else target.fadeOut();
60
- el.on( 'change', function( event ) {
61
- if( dfEvalCondition( $(this), args, true ) ) target.fadeIn();
62
- else target.fadeOut();
63
- });
30
+
31
+ const CONDITIONS = {
32
+ blank: el => el.val().length === 0 || !el.val().trim(),
33
+ changed: _el => true,
34
+ checked: el => el.is(':checked'),
35
+ eq: (el, value) => el.val() == value,
36
+ not: (el, value) => el.val() != value,
37
+ not_blank: el => el.val().trim(),
38
+ not_checked: el => !el.is(':checked')
64
39
  }
65
- else if( action.substr( 0, 8 ) == 'setValue' ) {
66
- var val = action.substr( 8 ).trim();
67
- if( dfEvalCondition( el, args, false ) ) dfSetValue( target, val );
68
- el.on( 'change', function( event ) {
69
- if( dfEvalCondition( $(this), args, true ) ) dfSetValue( target, val );
70
- });
40
+
41
+ const REVERSE_ACTIONS = {
42
+ addClass: (el, name) => el.removeClass(name),
43
+ addStyle: (el, extra_style) => {
44
+ if(el.attr('style')) el.attr('style', el.attr('style').replace(extra_style, ''))
45
+ },
46
+ fade: el => el.fadeIn(),
47
+ hide: el => el.show(),
48
+ slide: el => el.slideDown()
71
49
  }
72
- else if( action.substr( 0, 8 ) == 'callback' ) {
73
- var cb = action.substr( 8 ).trim();
74
- if( cb && window[cb] ) {
75
- if( dfEvalCondition( el, args, false ) ) window[cb]( el.data( 'args' ) );
76
- el.on( 'change', function( event ) {
77
- if( dfEvalCondition( $(this), args, true ) ) window[cb]( el.data( 'args' ) );
78
- });
50
+
51
+ class Field {
52
+ constructor(el) {
53
+ const action = el.data('then') || el.data('action') || ''
54
+ const action_name = action.split(' ', 1)[0]
55
+ const else_action = el.data('else') || ''
56
+ const else_action_name = else_action.split(' ', 1)[0]
57
+
58
+ this.el = el
59
+ this.action = ACTIONS[action_name]
60
+ this.action_arg = action.substring(action.indexOf(' ') + 1)
61
+ this.reverse_action = REVERSE_ACTIONS[action_name]
62
+ this.else_action = ACTIONS[else_action_name]
63
+ this.else_action_arg = else_action.substring(else_action.indexOf(' ') + 1)
64
+ this.else_reverse_action = REVERSE_ACTIONS[else_action_name]
65
+ this.condition = CONDITIONS[el.data('if')]
66
+ if (!this.condition && el.data('eq')) {
67
+ [this.condition, this.condition_arg] = [CONDITIONS['eq'], el.data('eq')]
68
+ }
69
+ if (!this.condition && el.data('not')) {
70
+ [this.condition, this.condition_arg] = [CONDITIONS['not'], el.data('not')]
71
+ }
72
+ this.custom_function = el.data('function')
73
+ if (!this.condition && this.custom_function) {
74
+ this.condition = window[this.custom_function]
75
+ if (!this.condition) {
76
+ el.attr('data-df-errors', 'custom function not found')
77
+ console.warn(`activeadmin_dynamic_fields custom function not found: ${this.custom_function}`)
78
+ }
79
+ }
80
+
81
+ // closest find for has many associations
82
+ if (el.data('target')) this.target = el.closest('fieldset').find(el.data('target'))
83
+ else if (el.data('gtarget')) this.target = $(el.data('gtarget'))
84
+ if (action_name == 'callback') this.target = el
85
+ }
86
+
87
+ apply(el) {
88
+ if (this.condition(el, this.condition_arg)) {
89
+ if (this.else_reverse_action) this.else_reverse_action(this.target, this.else_action_arg)
90
+ this.action(this.target, this.action_arg)
91
+ }
92
+ else {
93
+ if (this.reverse_action) this.reverse_action(this.target, this.action_arg)
94
+ if (this.else_action) this.else_action(this.target, this.else_action_arg)
95
+ }
96
+ }
97
+
98
+ is_valid() {
99
+ if (!this.condition) return false
100
+ if (!this.action && !this.custom_function) return false
101
+
102
+ return true
103
+ }
104
+
105
+ setup() {
106
+ if (!this.is_valid()) return
107
+ if (this.el.data('if') != 'changed') this.apply(this.el)
108
+ this.el.on('change', () => this.apply(this.el))
79
109
  }
80
- else console.log( 'Warning - activeadmin_dynamic_fields: ' + cb + '() not available [2]' );
81
- }
82
- else if( action.substr( 0, 8 ) == 'addClass' ) {
83
- var classes = action.substr( 8 ).trim();
84
- if( dfEvalCondition( el, args, false ) ) target.removeClass( classes );
85
- else target.addClass( classes );
86
- el.on( 'change', function( event ) {
87
- if( dfEvalCondition( $(this), args, true ) ) target.removeClass( classes );
88
- else target.addClass( classes );
89
- });
90
- }
91
- else if( args.fn ) { // function without action
92
- dfEvalCondition( el, args, false );
93
- el.on( 'change', function( event ) {
94
- dfEvalCondition( el, args, true );
95
- });
96
110
  }
97
- }
98
-
99
- // Set the value of an element
100
- function dfSetValue( el, val ) {
101
- if( el.attr('type') != 'checkbox' ) el.val( val );
102
- else el.prop('checked', val == '1');
103
- el.trigger( 'change' );
104
- }
105
-
106
- // Inline update - must be called binded on the editing element
107
- function dfUpdateField() {
108
- if( $(this).data( 'loading' ) != '1' ) {
109
- $(this).data( 'loading', '1' );
110
- var _this = $(this);
111
- var type = $(this).data( 'field-type' );
112
- var new_value;
113
- if( type == 'boolean' ) new_value = !$(this).data( 'field-value' );
114
- else if( type == 'select' ) new_value = $(this).val();
115
- else new_value = $(this).text();
116
- var data = {};
117
- data[$(this).data('field')] = new_value;
118
- $.ajax({
119
- context: _this,
120
- data: { data: data },
121
- method: 'POST',
122
- url: $(this).data( 'save-url' ),
123
- complete: function( req, status ) {
124
- $(this).data( 'loading', '0' );
125
- },
126
- success: function( data, status, req ) {
127
- if( data.status == 'error' ) {
128
- if( $(this).data( 'show-errors' ) ) {
129
- var result = '';
130
- var message = data.message;
131
- for( var key in message ) {
132
- if( typeof( message[key] ) === 'object' ) {
133
- if( result ) result += ' - ';
134
- result += key + ': ' + message[key].join( '; ' );
111
+
112
+ // Inline update - must be called binded on the editing element
113
+ function dfUpdateField() {
114
+ if ($(this).data('loading') != '1') {
115
+ $(this).data('loading', '1');
116
+ let _this = $(this);
117
+ let type = $(this).data('field-type');
118
+ let new_value;
119
+ if (type == 'boolean') new_value = !$(this).data('field-value');
120
+ else if (type == 'select') new_value = $(this).val();
121
+ else new_value = $(this).text();
122
+ let data = {};
123
+ data[$(this).data('field')] = new_value;
124
+ $.ajax({
125
+ context: _this,
126
+ data: { data: data },
127
+ method: 'POST',
128
+ url: $(this).data('save-url'),
129
+ complete: function (req, status) {
130
+ $(this).data('loading', '0');
131
+ },
132
+ success: function (data, status, req) {
133
+ if (data.status == 'error') {
134
+ if ($(this).data('show-errors')) {
135
+ let result = '';
136
+ let message = data.message;
137
+ for (let key in message) {
138
+ if (typeof (message[key]) === 'object') {
139
+ if (result) result += ' - ';
140
+ result += key + ': ' + message[key].join('; ');
141
+ }
135
142
  }
143
+ if (result) alert(result);
136
144
  }
137
- if( result ) alert( result );
138
145
  }
139
- }
140
- else {
141
- $(this).data( 'field-value', new_value );
142
- if( $(this).data('content') ) {
143
- var old_text = $(this).text();
144
- var old_class = $(this).attr( 'class' );
145
- var content = $($(this).data('content'));
146
- $(this).text( content.text() );
147
- $(this).attr( 'class', content.attr( 'class' ) );
148
- content.text( old_text );
149
- content.attr( 'class', old_class );
150
- $(this).data( 'content', content );
146
+ else {
147
+ $(this).data('field-value', new_value);
148
+ if ($(this).data('content')) {
149
+ let old_text = $(this).text();
150
+ let old_class = $(this).attr('class');
151
+ let content = $($(this).data('content'));
152
+ $(this).text(content.text());
153
+ $(this).attr('class', content.attr('class'));
154
+ content.text(old_text);
155
+ content.attr('class', old_class);
156
+ $(this).data('content', content);
157
+ }
151
158
  }
152
159
  }
153
- },
154
- // error: function( req, status, error ) {
155
- // // if( $(this).data( 'show-errors' ) && req.responseJSON.message ) { }
156
- // },
157
- });
158
- }
159
- }
160
-
161
- // Init
162
- $(document).ready( function() {
163
- // Setup dynamic fields
164
- $('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each( function() {
165
- dfSetupField( $(this) );
166
- });
167
- // Setup dynamic fields for has many associations
168
- $('.active_admin .has_many_container').on( 'has_many_add:after', function( e, fieldset, container ) {
169
- $('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each( function() {
170
- dfSetupField( $(this) );
171
- });
172
- });
173
- // Set dialog icon link
174
- $('.active_admin [data-df-icon]').each( function() {
175
- $(this).append( ' &raquo;' ); // ' &bullet;'
176
- });
177
- // Open content in dialog
178
- $('.active_admin [data-df-dialog]').on( 'click', function( event ) {
179
- event.preventDefault();
180
- $(this).blur();
181
- if( $('#df-dialog').data( 'loading' ) != '1' ) {
182
- $('#df-dialog').data( 'loading', '1' );
183
- if( $('#df-dialog').length == 0 ) $('body').append( '<div id="df-dialog"></div>' );
184
- var title = $(this).attr( 'title' );
185
- $.ajax({
186
- url: $(this).attr( 'href' ),
187
- complete: function( req, status ) {
188
- $('#df-dialog').data( 'loading', '0' );
189
- },
190
- success: function( data, status, req ) {
191
- if( title ) $('#df-dialog').attr( 'title', title );
192
- $('#df-dialog').html( data );
193
- $('#df-dialog').dialog({ modal: true });
194
- },
195
160
  });
196
161
  }
197
- });
198
- // Inline editing
199
- $('[data-field][data-field-type="boolean"][data-save-url]').each( function() {
200
- $(this).on( 'click', $.proxy( dfUpdateField, $(this) ) );
201
- });
202
- $('[data-field][data-field-type="string"][data-save-url]' ).each( function() {
203
- $(this).data( 'field-value', $(this).text() );
204
- var fnUpdate = $.proxy( dfUpdateField, $(this) );
205
- $(this).on( 'blur', function() {
206
- if( $(this).data( 'field-value' ) != $(this).text() ) fnUpdate();
207
- });
208
- });
209
- $('[data-field][data-field-type="select"][data-save-url]').each( function() {
210
- $(this).on( 'change', $.proxy( dfUpdateField, $(this) ) );
211
- });
212
- });
162
+ }
163
+
164
+ // Init
165
+ $(document).ready(function () {
166
+ // Setup dynamic fields
167
+ const selectors = '.active_admin .input [data-if], .active_admin .input [data-eq], .active_admin .input [data-not], .active_admin .input [data-function]'
168
+ $(selectors).each(function () {
169
+ new Field($(this)).setup()
170
+ })
171
+
172
+ // Setup dynamic fields for associations
173
+ $('.active_admin .has_many_container').on('has_many_add:after', () => {
174
+ $(selectors).each(function () {
175
+ new Field($(this)).setup()
176
+ })
177
+ })
178
+
179
+ // Set dialog icon link
180
+ $('.active_admin [data-df-icon]').each(function () {
181
+ $(this).append(' &raquo;')
182
+ })
183
+
184
+ // Open content in dialog
185
+ $('.active_admin [data-df-dialog]').on('click', function (event) {
186
+ event.preventDefault()
187
+ $(this).blur()
188
+ if ($('#df-dialog').data('loading') != '1') {
189
+ $('#df-dialog').data('loading', '1')
190
+ if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>')
191
+ let title = $(this).attr('title')
192
+ $.ajax({
193
+ url: $(this).attr('href'),
194
+ complete: function (req, status) {
195
+ $('#df-dialog').data('loading', '0')
196
+ },
197
+ success: function (data, status, req) {
198
+ if (title) $('#df-dialog').attr('title', title)
199
+ $('#df-dialog').html(data)
200
+ $('#df-dialog').dialog({ modal: true })
201
+ },
202
+ })
203
+ }
204
+ })
205
+
206
+ // Inline editing
207
+ $('[data-field][data-field-type="boolean"][data-save-url]').each(function () {
208
+ $(this).on('click', $.proxy(dfUpdateField, $(this)))
209
+ })
210
+ $('[data-field][data-field-type="string"][data-save-url]').each(function () {
211
+ $(this).data('field-value', $(this).text())
212
+ let fnUpdate = $.proxy(dfUpdateField, $(this))
213
+ $(this).on('blur', function () {
214
+ if ($(this).data('field-value') != $(this).text()) fnUpdate()
215
+ })
216
+ })
217
+ $('[data-field][data-field-type="select"][data-save-url]').each(function () {
218
+ $(this).on('change', $.proxy(dfUpdateField, $(this)))
219
+ })
220
+ })
221
+ })()
@@ -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.2'
5
+ VERSION = '0.4.2'
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.2
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: 2018-02-24 00:00:00.000000000 Z
11
+ date: 2020-09-24 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