activeadmin_dynamic_fields 0.2.6 → 0.4.4

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
  SHA256:
3
- metadata.gz: a0ed5077c7c98c62def46fd92bc4e832c97239af6c5549feb26c9c0190dd9f4c
4
- data.tar.gz: 6c874a1265046e090b1e1528de0f030a06fc4cf916d1f1625b174323c90be756
3
+ metadata.gz: e095e90e8a67093d377cd4ba64763adead3eaf7f100e7e27a3a6ab60c2851763
4
+ data.tar.gz: ee22043cb52eea432818add1114b5e08f8b91deb450845c480ee289794f72809
5
5
  SHA512:
6
- metadata.gz: abb89d40dd388777bad8ec10264358993e3d70c07d45b5ab4720a7ac4bab0caa9ff0032bc312eb9a744e84cb0923dead550a9e9e61dc822846dce5a998ae3c62
7
- data.tar.gz: adfea763fa7d6ab978a3048d55e0f6a9ab14c5fc2ac743603176512f477fc822524126ca936d816aa3285a87c3c1ec46aae0076f7513e669552bf51cbd2a4a98
6
+ metadata.gz: 116395a999d86f0f976a04cd7be8ba1ee994ce6bcb71c21d735e621ed037881aa24b73a57fa75e7be8062acfb6ea178e935da0e03b9d0c82f29ec8f73b7afff5
7
+ data.tar.gz: 6d7659ba3297a2f218829678bd24ea10a62703ed294822dc94ac5591671f92073d496e9d7432b6c7b80da25bd70238490e4285ef31e42c21e5a60d991c5a8448
@@ -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,57 +1,68 @@
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
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-match**: check if a field match a regexp
35
+ - **data-mismatch**: check if a field doesn't match a regexp (ex. `"data-mismatch": "^\d+$"`)
36
+ - **data-function**: check the return value of a custom function (ex. `"data-function": "my_check"`)
37
+
38
+ Actions:
39
+ - **data-then**: action to trigger (alias **data-action**), values:
40
+ + **hide**: hides elements (ex. `"data-then": "hide", "data-target": ".errors"`)
37
41
  + **slide**: hides elements (using sliding)
38
42
  + **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)
43
+ + **addClass**: adds classes (ex. `"data-then": "addClass red"`)
44
+ + **addStyle**: adds some styles (ex. `"data-then": "addStyle color: #fb1; font-size: 12px"`)
45
+ + **setText**: set the text of an element (ex. `"data-then": "setText A sample text"`)
46
+ + **setValue**: set the value of an input element (ex. `"data-then": "setValue A sample value"`)
47
+ + **callback**: call a function (with arguments: **data-args**) (ex. `"data-then": "callback a_fun"`)
48
+ - **data-else**: action to trigger when the condition check is not true
49
+ - **data-args**: arguments passed to the callback function
50
+
51
+ Targets:
52
+ - **data-target**: target css selector (from parent fieldset, look for the closest match)
53
+ - **data-gtarget**: target css selector globally
54
+
55
+ 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
56
 
45
57
  ## Examples
46
58
 
47
59
  ### Dynamic fields examples
48
-
49
60
  - A checkbox that hides other fields if is checked (ex. model *Article*):
50
61
 
51
62
  ```rb
52
63
  form do |f|
53
64
  f.inputs 'Article' do
54
- f.input :published, input_html: { data: { if: 'checked', action: 'hide', target: '.grp1' } }
65
+ f.input :published, input_html: { data: { if: 'checked', then: 'hide', target: '.grp1' } }
55
66
  f.input :online_date, wrapper_html: { class: 'grp1' }
56
67
  f.input :draft_notes, wrapper_html: { class: 'grp1' }
57
68
  end
@@ -59,58 +70,71 @@ form do |f|
59
70
  end
60
71
  ```
61
72
 
62
- - Add 3 classes (*first*, *second*, *third*) if a checkbox is not checked:
73
+ - Add 3 classes (*first*, *second*, *third*) if a checkbox is not checked, else add "forth" class:
63
74
 
64
- `f.input :published, input_html: { data: { if: 'not_checked', action: 'addClass first second third', target: '.grp1' } }`
75
+ ```rb
76
+ data = { if: 'not_checked', then: 'addClass first second third', target: '.grp1', else: 'addClass forth' }
77
+ f.input :published, input_html: { data: data }
78
+ ```
65
79
 
66
80
  - Set another field value if a string field is blank:
67
81
 
68
- `f.input :title, input_html: { data: { if: 'blank', action: 'setValue 10', target: '#article_position' } }`
82
+ ```rb
83
+ f.input :title, input_html: { data: { if: 'blank', then: 'setValue 10', target: '#article_position' } }
84
+ ```
69
85
 
70
86
  - Use a custom function for conditional check (*title_not_empty()* must be available on global scope) (with alternative syntax for data attributes):
71
87
 
72
- `f.input :title, input_html: { 'data-function': 'title_empty', 'data-action': 'slide', 'data-target': '#article_description_input' }`
88
+ ```rb
89
+ attrs = { 'data-function': 'title_empty', 'data-then': 'slide', 'data-target': '#article_description_input' }
90
+ f.input :title, input_html: attrs
91
+ ```
73
92
 
74
93
  ```js
75
- function title_empty( el ) {
76
- return ( $('#article_title').val().trim() === '' );
94
+ function title_empty(el) {
95
+ return ($('#article_title').val().trim() === '');
77
96
  }
78
97
  ```
79
98
 
80
99
  - Call a callback function as action:
81
100
 
82
- `f.input :published, input_html: { data: { if: 'checked', action: 'callback set_title', args: '["Unpublished !"]' } }`
101
+ ```rb
102
+ data = { if: 'checked', then: 'callback set_title', args: '["Unpublished !"]' }
103
+ f.input :published, input_html: { data: data }
104
+ ```
83
105
 
84
106
  ```js
85
- function set_title( args ) {
86
- if( $('#article_title').val().trim() === '' ) {
87
- $('#article_title').val( args[0] );
88
- $('#article_title').trigger( 'change' );
107
+ function set_title(args) {
108
+ if($('#article_title').val().trim() === '') {
109
+ $('#article_title').val(args[0]);
110
+ $('#article_title').trigger('change');
89
111
  }
90
112
  }
91
113
  ```
92
114
 
93
115
  - Custom function without action:
94
116
 
95
- `f2.input :category, as: :select, collection: [ [ 'Cat 1', 'cat1' ], [ 'Cat 2', 'cat2' ], [ 'Cat 3', 'cat3' ] ], input_html: { 'data-function': 'on_change_category' }`
117
+ ```rb
118
+ collection = [['Cat 1', 'cat1'], ['Cat 2', 'cat2'], ['Cat 3', 'cat3']]
119
+ f2.input :category, as: :select, collection: collection, input_html: { 'data-function': 'on_change_category' }
120
+ ```
96
121
 
97
122
  ```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' );
123
+ function on_change_category(el) {
124
+ var target = el.closest('fieldset').find('.pub');
125
+ target.prop('checked', (el.val() == 'cat2');
126
+ target.trigger('change');
102
127
  }
103
128
  ```
104
129
 
105
130
  ### Inline editing examples
106
-
107
131
  - 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
132
 
109
133
  ```rb
110
134
  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 )
135
+ render ActiveAdmin::DynamicFields.update(resource, params)
136
+ # render ActiveAdmin::DynamicFields.update(resource, params, [:published])
137
+ # render ActiveAdmin::DynamicFields.update(resource, params, Article::permit_params)
114
138
  end
115
139
  ```
116
140
 
@@ -119,29 +143,28 @@ end
119
143
  ```rb
120
144
  # Edit a string:
121
145
  column :title do |row|
122
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
146
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
123
147
  end
124
148
  # Edit a boolean:
125
149
  column :published do |row|
126
- status_tag row.published, ActiveAdmin::DynamicFields::edit_boolean( :published, save_admin_article_path( row.id ), row.published )
150
+ status_tag row.published, ActiveAdmin::DynamicFields.edit_boolean(:published, save_admin_article_path(row.id), row.published)
127
151
  end
128
152
  # Edit a select ([''] allow to have a blank value):
129
153
  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 )
154
+ select ActiveAdmin::DynamicFields.edit_select(:author_id, save_admin_article_path(row.id)) do
155
+ options_for_select([''] + Author.pluck(:name, :id), row.author_id)
132
156
  end
133
157
  end
134
158
  ```
135
159
 
136
- - In *show* config (less useful):
160
+ - In *show* config (inside `attributes_table` block):
137
161
  ```rb
138
162
  row :title do |row|
139
- div row.title, ActiveAdmin::DynamicFields::edit_string( :title, save_admin_article_path( row.id ) )
163
+ div row.title, ActiveAdmin::DynamicFields.edit_string(:title, save_admin_article_path(row.id))
140
164
  end
141
165
  ```
142
166
 
143
167
  ### Dialog example
144
-
145
168
  Example with 2 models: *Author* and *Article*
146
169
 
147
170
  Prepare the content dialog - in Active Admin Author config:
@@ -150,12 +173,16 @@ Prepare the content dialog - in Active Admin Author config:
150
173
  ActiveAdmin.register Author do
151
174
  # ...
152
175
  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>"
176
+ record = resource
177
+ context = Arbre::Context.new do
178
+ dl do
179
+ %i[name age created_at].each do |field|
180
+ dt "#{Author.human_attribute_name(field)}:"
181
+ dd record[field]
182
+ end
183
+ end
156
184
  end
157
- content += '</dl>'
158
- render plain: content
185
+ render plain: context
159
186
  end
160
187
  # ...
161
188
  end
@@ -170,7 +197,7 @@ ActiveAdmin.register Article do
170
197
  attributes_table do
171
198
  # ...
172
199
  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
200
+ link_to object.author.name, dialog_admin_author_path(object.author), title: object.author.name, 'data-df-dialog': true, 'data-df-icon': true
174
201
  end
175
202
  end
176
203
  end
@@ -181,15 +208,13 @@ end
181
208
  The link url is loaded via AJAX before opening the dialog.
182
209
 
183
210
  ## Do you like it? Star it!
184
-
185
211
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
186
212
 
187
213
  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
214
 
189
215
  ## Contributors
190
-
191
- - [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
216
+ - [Mattia Roccoberton](http://blocknot.es): author
217
+ - The good guys that opened issues and pull requests from time to time
192
218
 
193
219
  ## License
194
-
195
- [MIT](LICENSE.txt)
220
+ The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
3
+ require "bundler/gem_tasks"
@@ -1,209 +1,229 @@
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
+ match: (el, regexp) => regexp.test(el.val()),
37
+ mismatch: (el, regexp) => !regexp.test(el.val()),
38
+ not: (el, value) => el.val() != value,
39
+ not_blank: el => el.val().trim(),
40
+ not_checked: el => !el.is(':checked')
64
41
  }
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
- });
42
+
43
+ const REVERSE_ACTIONS = {
44
+ addClass: (el, name) => el.removeClass(name),
45
+ addStyle: (el, extra_style) => {
46
+ if(el.attr('style')) el.attr('style', el.attr('style').replace(extra_style, ''))
47
+ },
48
+ fade: el => el.fadeIn(),
49
+ hide: el => el.show(),
50
+ slide: el => el.slideDown()
71
51
  }
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
- });
52
+
53
+ class Field {
54
+ constructor(el) {
55
+ const action = el.data('then') || el.data('action') || ''
56
+ const action_name = action.split(' ', 1)[0]
57
+ const else_action = el.data('else') || ''
58
+ const else_action_name = else_action.split(' ', 1)[0]
59
+
60
+ this.el = el
61
+ this.action = ACTIONS[action_name]
62
+ this.action_arg = action.substring(action.indexOf(' ') + 1)
63
+ this.reverse_action = REVERSE_ACTIONS[action_name]
64
+ this.else_action = ACTIONS[else_action_name]
65
+ this.else_action_arg = else_action.substring(else_action.indexOf(' ') + 1)
66
+ this.else_reverse_action = REVERSE_ACTIONS[else_action_name]
67
+ this.condition = CONDITIONS[el.data('if')]
68
+ if (!this.condition && el.data('eq')) {
69
+ [this.condition, this.condition_arg] = [CONDITIONS['eq'], el.data('eq')]
70
+ }
71
+ if (!this.condition && el.data('not')) {
72
+ [this.condition, this.condition_arg] = [CONDITIONS['not'], el.data('not')]
73
+ }
74
+ if (!this.condition && el.data('match')) {
75
+ [this.condition, this.condition_arg] = [CONDITIONS['match'], new RegExp(el.data('match'))]
76
+ }
77
+ if (!this.condition && el.data('mismatch')) {
78
+ [this.condition, this.condition_arg] = [CONDITIONS['mismatch'], new RegExp(el.data('mismatch'))]
79
+ }
80
+ this.custom_function = el.data('function')
81
+ if (!this.condition && this.custom_function) {
82
+ this.condition = window[this.custom_function]
83
+ if (!this.condition) {
84
+ el.attr('data-df-errors', 'custom function not found')
85
+ console.warn(`activeadmin_dynamic_fields custom function not found: ${this.custom_function}`)
86
+ }
87
+ }
88
+
89
+ // closest find for has many associations
90
+ if (el.data('target')) this.target = el.closest('fieldset').find(el.data('target'))
91
+ else if (el.data('gtarget')) this.target = $(el.data('gtarget'))
92
+ if (action_name == 'callback') this.target = el
93
+ }
94
+
95
+ apply(el) {
96
+ if (this.condition(el, this.condition_arg)) {
97
+ if (this.else_reverse_action) this.else_reverse_action(this.target, this.else_action_arg)
98
+ this.action(this.target, this.action_arg)
99
+ }
100
+ else {
101
+ if (this.reverse_action) this.reverse_action(this.target, this.action_arg)
102
+ if (this.else_action) this.else_action(this.target, this.else_action_arg)
103
+ }
104
+ }
105
+
106
+ is_valid() {
107
+ if (!this.condition) return false
108
+ if (!this.action && !this.custom_function) return false
109
+
110
+ return true
111
+ }
112
+
113
+ setup() {
114
+ if (!this.is_valid()) return
115
+ if (this.el.data('if') != 'changed') this.apply(this.el)
116
+ this.el.on('change', () => this.apply(this.el))
79
117
  }
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
118
  }
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('; ');
119
+
120
+ // Inline update - must be called binded on the editing element
121
+ function dfUpdateField() {
122
+ if ($(this).data('loading') != '1') {
123
+ $(this).data('loading', '1');
124
+ let _this = $(this);
125
+ let type = $(this).data('field-type');
126
+ let new_value;
127
+ if (type == 'boolean') new_value = !$(this).data('field-value');
128
+ else if (type == 'select') new_value = $(this).val();
129
+ else new_value = $(this).text();
130
+ let data = {};
131
+ data[$(this).data('field')] = new_value;
132
+ $.ajax({
133
+ context: _this,
134
+ data: { data: data },
135
+ method: 'POST',
136
+ url: $(this).data('save-url'),
137
+ complete: function (req, status) {
138
+ $(this).data('loading', '0');
139
+ },
140
+ success: function (data, status, req) {
141
+ if (data.status == 'error') {
142
+ if ($(this).data('show-errors')) {
143
+ let result = '';
144
+ let message = data.message;
145
+ for (let key in message) {
146
+ if (typeof (message[key]) === 'object') {
147
+ if (result) result += ' - ';
148
+ result += key + ': ' + message[key].join('; ');
149
+ }
135
150
  }
151
+ if (result) alert(result);
136
152
  }
137
- if(result) alert(result);
138
153
  }
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);
154
+ else {
155
+ $(this).data('field-value', new_value);
156
+ if ($(this).data('content')) {
157
+ let old_text = $(this).text();
158
+ let old_class = $(this).attr('class');
159
+ let content = $($(this).data('content'));
160
+ $(this).text(content.text());
161
+ $(this).attr('class', content.attr('class'));
162
+ content.text(old_text);
163
+ content.attr('class', old_class);
164
+ $(this).data('content', content);
165
+ }
151
166
  }
152
167
  }
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
168
  });
193
169
  }
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
- });
170
+ }
171
+
172
+ // Init
173
+ $(document).ready(function () {
174
+ // Setup dynamic fields
175
+ const selectors = '.active_admin .input [data-if], .active_admin .input [data-eq], .active_admin .input [data-not], .active_admin .input [data-match], .active_admin .input [data-mismatch], .active_admin .input [data-function]'
176
+ $(selectors).each(function () {
177
+ new Field($(this)).setup()
178
+ })
179
+
180
+ // Setup dynamic fields for associations
181
+ $('.active_admin .has_many_container').on('has_many_add:after', () => {
182
+ $(selectors).each(function () {
183
+ new Field($(this)).setup()
184
+ })
185
+ })
186
+
187
+ // Set dialog icon link
188
+ $('.active_admin [data-df-icon]').each(function () {
189
+ $(this).append(' &raquo;')
190
+ })
191
+
192
+ // Open content in dialog
193
+ $('.active_admin [data-df-dialog]').on('click', function (event) {
194
+ event.preventDefault()
195
+ $(this).blur()
196
+ if ($('#df-dialog').data('loading') != '1') {
197
+ $('#df-dialog').data('loading', '1')
198
+ if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>')
199
+ let title = $(this).attr('title')
200
+ $.ajax({
201
+ url: $(this).attr('href'),
202
+ complete: function (req, status) {
203
+ $('#df-dialog').data('loading', '0')
204
+ },
205
+ success: function (data, status, req) {
206
+ if (title) $('#df-dialog').attr('title', title)
207
+ $('#df-dialog').html(data)
208
+ $('#df-dialog').dialog({ modal: true })
209
+ },
210
+ })
211
+ }
212
+ })
213
+
214
+ // Inline editing
215
+ $('[data-field][data-field-type="boolean"][data-save-url]').each(function () {
216
+ $(this).on('click', $.proxy(dfUpdateField, $(this)))
217
+ })
218
+ $('[data-field][data-field-type="string"][data-save-url]').each(function () {
219
+ $(this).data('field-value', $(this).text())
220
+ let fnUpdate = $.proxy(dfUpdateField, $(this))
221
+ $(this).on('blur', function () {
222
+ if ($(this).data('field-value') != $(this).text()) fnUpdate()
223
+ })
224
+ })
225
+ $('[data-field][data-field-type="select"][data-save-url]').each(function () {
226
+ $(this).on('change', $.proxy(dfUpdateField, $(this)))
227
+ })
228
+ })
229
+ })()
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module DynamicFields
5
- VERSION = '0.2.6'
5
+ VERSION = '0.4.4'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,42 +1,164 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin_dynamic_fields
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.4.4
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: 2019-06-15 00:00:00.000000000 Z
11
+ date: 2020-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeadmin
15
15
  requirement: !ruby/object:Gem::Requirement
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
- - ".rubocop.yml"
35
- - Gemfile
36
159
  - LICENSE.txt
37
160
  - README.md
38
161
  - Rakefile
39
- - activeadmin_dynamic_fields.gemspec
40
162
  - app/assets/javascripts/activeadmin/dynamic_fields.js
41
163
  - lib/activeadmin/dynamic_fields.rb
42
164
  - lib/activeadmin/dynamic_fields/engine.rb
@@ -46,7 +168,7 @@ homepage: https://github.com/blocknotes/activeadmin_dynamic_fields
46
168
  licenses:
47
169
  - MIT
48
170
  metadata: {}
49
- post_install_message:
171
+ post_install_message:
50
172
  rdoc_options: []
51
173
  require_paths:
52
174
  - lib
@@ -61,8 +183,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
183
  - !ruby/object:Gem::Version
62
184
  version: '0'
63
185
  requirements: []
64
- rubygems_version: 3.0.2
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
-
@@ -1,73 +0,0 @@
1
- require:
2
- - rubocop-rspec
3
-
4
- Rails:
5
- Enabled: true
6
-
7
- AllCops:
8
- TargetRubyVersion: 2.3.8
9
- TargetRailsVersion: 5.2
10
- Exclude:
11
- - db/schema.rb
12
- - bin/*
13
- - node_modules/**/*
14
- # Temporary files
15
- - tmp/**/*
16
-
17
- Rails/InverseOf:
18
- Enabled: false
19
-
20
- Style/Documentation:
21
- Enabled: false
22
-
23
- Metrics/ClassLength:
24
- # Default value is 100
25
- Max: 150
26
-
27
- Metrics/LineLength:
28
- # Default is 80
29
- Max: 120
30
-
31
- Metrics/ModuleLength:
32
- # Default is 100
33
- Max: 150
34
-
35
- Metrics/ParameterLists:
36
- # Default is 5
37
- Max: 6
38
-
39
- RSpec/ExampleLength:
40
- # Default is 10
41
- Max: 20
42
-
43
- Style/FrozenStringLiteralComment:
44
- # Deface DOES edit strings in place
45
- Exclude:
46
- - 'app/overrides/**/*'
47
-
48
- RSpec/MultipleExpectations:
49
- # Default is 3
50
- Max: 5
51
-
52
- RSpec/NestedGroups:
53
- # Default is 3
54
- Max: 6
55
-
56
- Metrics/AbcSize:
57
- Max: 25
58
-
59
- Metrics/BlockLength:
60
- # This value double the rubocop default
61
- Max: 50
62
-
63
- Metrics/CyclomaticComplexity:
64
- # This value double the rubocop default
65
- Max: 12
66
-
67
- Metrics/MethodLength:
68
- # This value double the rubocop default
69
- Max: 20
70
-
71
- Metrics/PerceivedComplexity:
72
- # Default is 7
73
- Max: 10
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'activeadmin/dynamic_fields/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'activeadmin_dynamic_fields'
9
- spec.version = ActiveAdmin::DynamicFields::VERSION
10
- spec.summary = 'Dynamic fields for ActiveAdmin'
11
- spec.description = 'An Active Admin plugin to add dynamic behaviors to fields'
12
- spec.license = 'MIT'
13
- spec.authors = ['Mattia Roccoberton']
14
- spec.email = 'mat@blocknot.es'
15
- spec.homepage = 'https://github.com/blocknotes/activeadmin_dynamic_fields'
16
-
17
- spec.files = `git ls-files -z`.split("\x0")
18
- spec.require_paths = ['lib']
19
-
20
- spec.add_runtime_dependency 'activeadmin', '>= 1.0'
21
- end