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