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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +65 -44
- data/Rakefile +1 -1
- data/app/assets/javascripts/activeadmin/dynamic_fields.js +229 -197
- data/lib/activeadmin/dynamic_fields/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '019f2b81ed1552f13f57b73141860957f4869b10de1f018a289aa8724cc23ec1'
|
4
|
+
data.tar.gz: 4ed1bb60f5b1ddef1a3d870296d8548faa18cc36db5adadb8271f0c655c769f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b07cc4e73b801ef79ee52b52a6c82117d7e966a2f1eadb0e415e00c608456c02f2c14694c2fc41037ad381c9c0c3bfbf1c0ed96c0c6d13bd0ce3335c38dc1e11
|
7
|
+
data.tar.gz: c0aa33df12ef6ed16927a49bb9b93d0fcffeffc6f20b661f11edb350544f12e550f90fd5fa5870a43154c0097995bbb02f2f80f24e1ff821eddc2d0fa60e613d
|
data/LICENSE.txt
CHANGED
@@ -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
|
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
|
-
|
18
|
+
```js
|
19
|
+
//= require activeadmin/dynamic_fields
|
20
|
+
```
|
22
21
|
|
23
|
-
Options
|
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
|
33
|
-
- **data-
|
34
|
-
- **data-
|
35
|
-
- **data-
|
36
|
-
|
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
|
-
+ **
|
41
|
-
+ **
|
42
|
-
|
43
|
-
|
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',
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
76
|
-
return (
|
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
|
-
|
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(
|
86
|
-
if(
|
87
|
-
$('#article_title').val(
|
88
|
-
$('#article_title').trigger(
|
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
|
-
|
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(
|
99
|
-
var target = el.closest(
|
100
|
-
target.prop(
|
101
|
-
target.trigger(
|
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,209 +1,241 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
el
|
69
|
-
if(
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
142
|
-
|
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
|
-
|
159
|
-
$(
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
$(
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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(' »')
|
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
|
+
})()
|
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.
|
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-
|
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:
|
112
|
+
name: rubocop
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
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:
|
124
|
+
version: 0.90.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: selenium-webdriver
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
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:
|
138
|
+
version: 3.142.7
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: sqlite3
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|