activeadmin_dynamic_fields 0.2.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b1a35cc8fb49264ee130c088dd680bde833cecdcb1fd5f2544b7111369c4f74
4
- data.tar.gz: 5d72305f05e98728adcb05d03e2d3ffea6fc3c5707544de12c3a045691d45467
3
+ metadata.gz: '019f2b81ed1552f13f57b73141860957f4869b10de1f018a289aa8724cc23ec1'
4
+ data.tar.gz: 4ed1bb60f5b1ddef1a3d870296d8548faa18cc36db5adadb8271f0c655c769f6
5
5
  SHA512:
6
- metadata.gz: a334732f34e9636c60dd6ba376fe0331fc7dbfddb50e5e7c78b7b294758381dacd85ca51a6a61b85b936a937e4d140a467b2f6fd6baf4952599878cbc7ffbfac
7
- data.tar.gz: c9dd461761fff9e2194368ff22404fca87c688399798ea67ab17bdd292c5dcd2aff7eedd7f16fe44b30a17bc3eb4d76d43ada098ba4b25f57557844d9807a6e6
6
+ metadata.gz: b07cc4e73b801ef79ee52b52a6c82117d7e966a2f1eadb0e415e00c608456c02f2c14694c2fc41037ad381c9c0c3bfbf1c0ed96c0c6d13bd0ce3335c38dc1e11
7
+ data.tar.gz: c0aa33df12ef6ed16927a49bb9b93d0fcffeffc6f20b661f11edb350544f12e550f90fd5fa5870a43154c0097995bbb02f2f80f24e1ff821eddc2d0fa60e613d
@@ -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,51 +70,64 @@ 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
@@ -141,7 +165,6 @@ 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:
@@ -185,15 +208,13 @@ end
185
208
  The link url is loaded via AJAX before opening the dialog.
186
209
 
187
210
  ## Do you like it? Star it!
188
-
189
211
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
190
212
 
191
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.
192
214
 
193
215
  ## Contributors
194
-
195
216
  - [Mattia Roccoberton](http://blocknot.es): author
217
+ - The good guys that opened issues and pull requests from time to time
196
218
 
197
219
  ## License
198
-
199
- [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,241 @@
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
+ this.el = el
56
+ const action_name = this.evaluateAction()
57
+ const result = this.evaluateCondition()
58
+ this.condition = result.condition
59
+ this.condition_arg = result.condition_arg
60
+ this.evaluateTarget(action_name)
79
61
  }
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
- }
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('; ');
135
- }
136
- }
137
- if(result) alert(result);
138
- }
139
- }
62
+
63
+ apply() {
64
+ if (this.condition(this.el, this.condition_arg)) {
65
+ if (this.else_reverse_action) this.else_reverse_action(this.target, this.else_action_arg)
66
+ this.action(this.target, this.action_arg)
67
+ }
68
+ else {
69
+ if (this.reverse_action) this.reverse_action(this.target, this.action_arg)
70
+ if (this.else_action) this.else_action(this.target, this.else_action_arg)
71
+ }
72
+ }
73
+
74
+ evaluateAction() {
75
+ const action = this.el.data('then') || this.el.data('action') || ''
76
+ const action_name = action.split(' ', 1)[0]
77
+ const else_action = this.el.data('else') || ''
78
+ const else_action_name = else_action.split(' ', 1)[0]
79
+
80
+ this.action = ACTIONS[action_name]
81
+ this.action_arg = action.substring(action.indexOf(' ') + 1)
82
+ this.reverse_action = REVERSE_ACTIONS[action_name]
83
+ this.else_action = ACTIONS[else_action_name]
84
+ this.else_action_arg = else_action.substring(else_action.indexOf(' ') + 1)
85
+ this.else_reverse_action = REVERSE_ACTIONS[else_action_name]
86
+
87
+ return action_name
88
+ }
89
+
90
+ evaluateCondition() {
91
+ let value = CONDITIONS[this.el.data('if')?.trim()]
92
+ if (value) return { condition: value }
93
+ if (value = this.el.data('eq')) return { condition: CONDITIONS['eq'], condition_arg: value }
94
+ if (value = this.el.data('not')) return { condition: CONDITIONS['not'], condition_arg: value }
95
+ if (value = this.el.data('match')) return { condition: CONDITIONS['match'], condition_arg: new RegExp(value) }
96
+ if (value = this.el.data('mismatch')) return { condition: CONDITIONS['mismatch'], condition_arg: new RegExp(value) }
97
+
98
+ this.custom_function = this.el.data('function')
99
+ if (this.custom_function) {
100
+ value = window[this.custom_function]
101
+ if (value) return { condition: value }
140
102
  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);
151
- }
103
+ this.el.attr('data-df-errors', 'custom function not found')
104
+ console.warn(`activeadmin_dynamic_fields custom function not found: ${this.custom_function}`)
152
105
  }
153
106
  }
154
- });
107
+
108
+ return {}
109
+ }
110
+
111
+ evaluateTarget(action_name) {
112
+ // closest find for has many associations
113
+ if (this.el.data('target')) this.target = this.el.closest('fieldset').find(this.el.data('target'))
114
+ else if (this.el.data('gtarget')) this.target = $(this.el.data('gtarget'))
115
+ if (action_name == 'callback') this.target = this.el
116
+ }
117
+
118
+ isValid() {
119
+ if (!this.condition) return false
120
+ if (!this.action && !this.custom_function) return false
121
+
122
+ return true
123
+ }
124
+
125
+ setup() {
126
+ if (!this.isValid()) return
127
+ if (this.el.data('if') != 'changed') this.apply()
128
+ this.el.on('change', () => this.apply())
129
+ }
155
130
  }
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(' »'); // ' •'
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');
131
+
132
+ // Inline update - must be called binded on the editing element
133
+ function dfUpdateField() {
134
+ if ($(this).data('loading') != '1') {
135
+ $(this).data('loading', '1');
136
+ let _this = $(this);
137
+ let type = $(this).data('field-type');
138
+ let new_value;
139
+ if (type == 'boolean') new_value = !$(this).data('field-value');
140
+ else if (type == 'select') new_value = $(this).val();
141
+ else new_value = $(this).text();
142
+ let data = {};
143
+ data[$(this).data('field')] = new_value;
182
144
  $.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 });
145
+ context: _this,
146
+ data: { data: data },
147
+ method: 'POST',
148
+ url: $(this).data('save-url'),
149
+ complete: function (req, status) {
150
+ $(this).data('loading', '0');
191
151
  },
152
+ success: function (data, status, req) {
153
+ if (data.status == 'error') {
154
+ if ($(this).data('show-errors')) {
155
+ let result = '';
156
+ let message = data.message;
157
+ for (let key in message) {
158
+ if (typeof (message[key]) === 'object') {
159
+ if (result) result += ' - ';
160
+ result += key + ': ' + message[key].join('; ');
161
+ }
162
+ }
163
+ if (result) alert(result);
164
+ }
165
+ }
166
+ else {
167
+ $(this).data('field-value', new_value);
168
+ if ($(this).data('content')) {
169
+ let old_text = $(this).text();
170
+ let old_class = $(this).attr('class');
171
+ let content = $($(this).data('content'));
172
+ $(this).text(content.text());
173
+ $(this).attr('class', content.attr('class'));
174
+ content.text(old_text);
175
+ content.attr('class', old_class);
176
+ $(this).data('content', content);
177
+ }
178
+ }
179
+ }
192
180
  });
193
181
  }
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
- });
182
+ }
183
+
184
+ // Init
185
+ $(document).ready(function () {
186
+ // Setup dynamic fields
187
+ 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]'
188
+ $(selectors).each(function () {
189
+ new Field($(this)).setup()
190
+ })
191
+
192
+ // Setup dynamic fields for associations
193
+ $('.active_admin .has_many_container').on('has_many_add:after', () => {
194
+ $(selectors).each(function () {
195
+ new Field($(this)).setup()
196
+ })
197
+ })
198
+
199
+ // Set dialog icon link
200
+ $('.active_admin [data-df-icon]').each(function () {
201
+ $(this).append(' &raquo;')
202
+ })
203
+
204
+ // Open content in dialog
205
+ $('.active_admin [data-df-dialog]').on('click', function (event) {
206
+ event.preventDefault()
207
+ $(this).blur()
208
+ if ($('#df-dialog').data('loading') != '1') {
209
+ $('#df-dialog').data('loading', '1')
210
+ if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>')
211
+ let title = $(this).attr('title')
212
+ $.ajax({
213
+ url: $(this).attr('href'),
214
+ complete: function (req, status) {
215
+ $('#df-dialog').data('loading', '0')
216
+ },
217
+ success: function (data, status, req) {
218
+ if (title) $('#df-dialog').attr('title', title)
219
+ $('#df-dialog').html(data)
220
+ $('#df-dialog').dialog({ modal: true })
221
+ },
222
+ })
223
+ }
224
+ })
225
+
226
+ // Inline editing
227
+ $('[data-field][data-field-type="boolean"][data-save-url]').each(function () {
228
+ $(this).on('click', $.proxy(dfUpdateField, $(this)))
229
+ })
230
+ $('[data-field][data-field-type="string"][data-save-url]').each(function () {
231
+ $(this).data('field-value', $(this).text())
232
+ let fnUpdate = $.proxy(dfUpdateField, $(this))
233
+ $(this).on('blur', function () {
234
+ if ($(this).data('field-value') != $(this).text()) fnUpdate()
235
+ })
236
+ })
237
+ $('[data-field][data-field-type="select"][data-save-url]').each(function () {
238
+ $(this).on('change', $.proxy(dfUpdateField, $(this)))
239
+ })
240
+ })
241
+ })()
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module DynamicFields
5
- VERSION = '0.2.8'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
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.8
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-28 00:00:00.000000000 Z
11
+ date: 2020-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeadmin
@@ -109,33 +109,33 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: 4.0.1
111
111
  - !ruby/object:Gem::Dependency
112
- name: selenium-webdriver
112
+ name: rubocop
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 3.142.7
117
+ version: 0.90.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 3.142.7
124
+ version: 0.90.0
125
125
  - !ruby/object:Gem::Dependency
126
- name: simplecov
126
+ name: selenium-webdriver
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.19.0
131
+ version: 3.142.7
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.19.0
138
+ version: 3.142.7
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: sqlite3
141
141
  requirement: !ruby/object:Gem::Requirement