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