Skip to content

Commit c1c7154

Browse files
authored
Merge pull request #7 from blocknotes/feature/js-refactoring-2
Major JS refactoring
2 parents 0c237dc + 3f6b09b commit c1c7154

File tree

9 files changed

+387
-165
lines changed

9 files changed

+387
-165
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
/Gemfile.lock
77

88
/_misc/
9-
/coverage/
109
/spec/dummy/db/*.sqlite3*
1110
/spec/dummy/log/
1211
/spec/dummy/storage/

activeadmin_dynamic_fields.gemspec

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,5 @@ Gem::Specification.new do |spec|
2727
spec.add_development_dependency 'rspec-rails', '~> 4.0.1'
2828
spec.add_development_dependency 'rubocop', '~> 0.90.0'
2929
spec.add_development_dependency 'selenium-webdriver', '~> 3.142.7'
30-
spec.add_development_dependency 'simplecov', '~> 0.19.0'
3130
spec.add_development_dependency 'sqlite3', '~> 1.4.2'
3231
end
Lines changed: 111 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,90 @@
11
(function () {
22
'use strict'
33

4-
// Evaluate a condition
5-
function dfEvalCondition(el, args, on_change) {
6-
if (args.fn) {
7-
if (args.fn && window[args.fn]) return !window[args.fn](el);
8-
else console.log('Warning - activeadmin_dynamic_fields: ' + args.fn + '() not available [1]');
9-
}
10-
else if (args.if == 'checked') {
11-
return el.is(':checked');
12-
}
13-
else if (args.if == 'not_checked') {
14-
return !el.is(':checked');
15-
}
16-
else if (args.if == 'blank') {
17-
return el.val().length === 0 || !el.val().trim();
18-
}
19-
else if (args.if == 'not_blank') {
20-
return el.val().length !== 0 && el.val().trim();
21-
}
22-
else if (args.if == 'changed') {
23-
return on_change;
24-
}
25-
else if (args.eq) {
26-
return el.val() == args.eq;
27-
}
28-
else if (args.not) {
29-
return el.val() != args.not;
30-
}
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) => dfSetValue(el, value),
16+
slide: el => el.slideUp()
17+
}
3118

32-
return undefined;
19+
const CONDITIONS = {
20+
blank: el => el.val().length === 0 || !el.val().trim(),
21+
changed: _el => true,
22+
checked: el => el.is(':checked'),
23+
eq: (el, value) => el.val() == value,
24+
not: (el, value) => el.val() != value,
25+
not_blank: el => el.val().trim(),
26+
not_checked: el => !el.is(':checked')
3327
}
3428

35-
// Prepare a field
36-
function dfSetupField(el) {
37-
let action = el.data('then') || el.data('action');
38-
let target, args = {};
39-
40-
args.if = el.data('if');
41-
args.eq = el.data('eq');
42-
args.not = el.data('not');
43-
args.fn = el.data('function');
44-
if (el.data('target')) target = el.closest('fieldset').find(el.data('target')); // closest find for has many associations
45-
else if (el.data('gtarget')) target = $(el.data('gtarget'));
46-
47-
if (action == 'hide') {
48-
if (dfEvalCondition(el, args, false)) target.hide();
49-
else target.show();
50-
el.on('change', function (event) {
51-
if (dfEvalCondition($(this), args, true)) target.hide();
52-
else target.show();
53-
});
54-
}
55-
else if (action == 'slide') {
56-
if (dfEvalCondition(el, args, false)) target.slideDown();
57-
else target.slideUp();
58-
el.on('change', function (event) {
59-
if (dfEvalCondition($(this), args, true)) target.slideDown();
60-
else target.slideUp();
61-
});
62-
}
63-
else if (action == 'fade') {
64-
if (dfEvalCondition(el, args, false)) target.fadeIn();
65-
else target.fadeOut();
66-
el.on('change', function (event) {
67-
if (dfEvalCondition($(this), args, true)) target.fadeIn();
68-
else target.fadeOut();
69-
});
70-
}
71-
else if (action.substr(0, 8) == 'setValue') {
72-
let val = action.substr(8).trim();
73-
if (dfEvalCondition(el, args, false)) dfSetValue(target, val);
74-
el.on('change', function (event) {
75-
if (dfEvalCondition($(this), args, true)) dfSetValue(target, val);
76-
});
77-
}
78-
else if (action.substr(0, 8) == 'callback') {
79-
let cb = action.substr(8).trim();
80-
if (cb && window[cb]) {
81-
if (dfEvalCondition(el, args, false)) window[cb](el.data('args'));
82-
el.on('change', function (event) {
83-
if (dfEvalCondition($(this), args, true)) window[cb](el.data('args'));
84-
});
29+
const REVERSE_ACTIONS = {
30+
addClass: (el, name) => el.removeClass(name),
31+
fade: el => el.fadeIn(),
32+
hide: el => el.show(),
33+
slide: el => el.slideDown()
34+
}
35+
36+
function dfEvalCondition(el) {
37+
let condition = CONDITIONS[el.data('if')]
38+
let condition_arg
39+
40+
if(!condition && el.data('eq')) {
41+
condition = CONDITIONS['eq']
42+
condition_arg = el.data('eq')
43+
}
44+
if(!condition && el.data('not')) {
45+
condition = CONDITIONS['not']
46+
condition_arg = el.data('not')
47+
}
48+
if(!condition && el.data('function')) {
49+
condition = window[el.data('function')]
50+
if(!condition) {
51+
el.attr('data-df-errors', 'custom function not found')
52+
console.warn(`activeadmin_dynamic_fields custom function not found: ${el.data('function')}`)
8553
}
86-
else console.log('Warning - activeadmin_dynamic_fields: ' + cb + '() not available [2]');
87-
}
88-
else if (action.substr(0, 8) == 'addClass') {
89-
let classes = action.substr(8).trim();
90-
if (dfEvalCondition(el, args, false)) target.removeClass(classes);
91-
else target.addClass(classes);
92-
el.on('change', function (event) {
93-
if (dfEvalCondition($(this), args, true)) target.removeClass(classes);
94-
else target.addClass(classes);
95-
});
96-
}
97-
else if (args.fn) { // function without action
98-
dfEvalCondition(el, args, false);
99-
el.on('change', function (event) {
100-
dfEvalCondition(el, args, true);
101-
});
10254
}
55+
56+
return [condition, condition_arg]
57+
}
58+
59+
function dfInitField(el) {
60+
const [condition, condition_arg] = dfEvalCondition(el)
61+
const action_name = (el.data('then') || el.data('action') || '').substr(0, 8)
62+
const action = ACTIONS[action_name]
63+
const arg = (el.data('then') || el.data('action') || '').substr(9)
64+
const reverse_action = REVERSE_ACTIONS[action_name]
65+
if (typeof condition === 'undefined') return
66+
if (typeof action === 'undefined' && !el.data('function')) return
67+
68+
// closest find for has many associations
69+
let target
70+
if (el.data('target')) target = el.closest('fieldset').find(el.data('target'))
71+
else if (el.data('gtarget')) target = $(el.data('gtarget'))
72+
if (action_name == 'callback') target = el
73+
74+
if (condition(el, condition_arg) && el.data('if') != 'changed') action(target, arg)
75+
else if (reverse_action) reverse_action(target, arg)
76+
77+
el.on('change', () => {
78+
if (condition(el, condition_arg)) action(target, arg)
79+
else if (reverse_action) reverse_action(target, arg)
80+
})
10381
}
10482

10583
// Set the value of an element
10684
function dfSetValue(el, val) {
107-
if (el.attr('type') != 'checkbox') el.val(val);
108-
else el.prop('checked', val == '1');
109-
el.trigger('change');
85+
if (el.attr('type') == 'checkbox') el.prop('checked', val == '1')
86+
else el.val(val)
87+
el.trigger('change')
11088
}
11189

11290
// Inline update - must be called binded on the editing element
@@ -164,54 +142,58 @@
164142
// Init
165143
$(document).ready(function () {
166144
// Setup dynamic fields
167-
$('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each(function () {
168-
dfSetupField($(this));
169-
});
170-
// Setup dynamic fields for has many associations
171-
$('.active_admin .has_many_container').on('has_many_add:after', function (e, fieldset, container) {
172-
$('.active_admin .input [data-if], .active_admin .input [data-function], .active_admin .input [data-eq], .active_admin .input [data-not]').each(function () {
173-
dfSetupField($(this));
174-
});
175-
});
145+
const selectors = '.active_admin .input [data-if], .active_admin .input [data-eq], .active_admin .input [data-not], .active_admin .input [data-function]'
146+
$(selectors).each(function () {
147+
dfInitField($(this))
148+
})
149+
150+
// Setup dynamic fields for associations
151+
$('.active_admin .has_many_container').on('has_many_add:after', () => {
152+
$(selectors).each(function () {
153+
dfInitField($(this))
154+
})
155+
})
156+
176157
// Set dialog icon link
177158
$('.active_admin [data-df-icon]').each(function () {
178-
$(this).append(' »'); // ' •'
179-
});
159+
$(this).append(' »')
160+
})
161+
180162
// Open content in dialog
181163
$('.active_admin [data-df-dialog]').on('click', function (event) {
182-
event.preventDefault();
183-
$(this).blur();
164+
event.preventDefault()
165+
$(this).blur()
184166
if ($('#df-dialog').data('loading') != '1') {
185-
$('#df-dialog').data('loading', '1');
186-
if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>');
187-
let title = $(this).attr('title');
167+
$('#df-dialog').data('loading', '1')
168+
if ($('#df-dialog').length == 0) $('body').append('<div id="df-dialog"></div>')
169+
let title = $(this).attr('title')
188170
$.ajax({
189171
url: $(this).attr('href'),
190172
complete: function (req, status) {
191-
$('#df-dialog').data('loading', '0');
173+
$('#df-dialog').data('loading', '0')
192174
},
193175
success: function (data, status, req) {
194-
if (title) $('#df-dialog').attr('title', title);
195-
$('#df-dialog').html(data);
196-
$('#df-dialog').dialog({ modal: true });
176+
if (title) $('#df-dialog').attr('title', title)
177+
$('#df-dialog').html(data)
178+
$('#df-dialog').dialog({ modal: true })
197179
},
198-
});
180+
})
199181
}
200-
});
182+
})
201183

202184
// Inline editing
203185
$('[data-field][data-field-type="boolean"][data-save-url]').each(function () {
204-
$(this).on('click', $.proxy(dfUpdateField, $(this)));
205-
});
186+
$(this).on('click', $.proxy(dfUpdateField, $(this)))
187+
})
206188
$('[data-field][data-field-type="string"][data-save-url]').each(function () {
207-
$(this).data('field-value', $(this).text());
208-
let fnUpdate = $.proxy(dfUpdateField, $(this));
189+
$(this).data('field-value', $(this).text())
190+
let fnUpdate = $.proxy(dfUpdateField, $(this))
209191
$(this).on('blur', function () {
210-
if ($(this).data('field-value') != $(this).text()) fnUpdate();
211-
});
212-
});
192+
if ($(this).data('field-value') != $(this).text()) fnUpdate()
193+
})
194+
})
213195
$('[data-field][data-field-type="select"][data-save-url]').each(function () {
214-
$(this).on('change', $.proxy(dfUpdateField, $(this)));
215-
});
216-
});
196+
$(this).on('change', $.proxy(dfUpdateField, $(this)))
197+
})
198+
})
217199
})()

spec/dummy/app/admin/authors.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
hint: (object.avatar.attached? ? "Current: #{object.avatar.filename}" : nil)
5454
end
5555
f.has_many :profile, allow_destroy: true do |ff|
56-
ff.input :description
56+
dyn_description = { if: 'not_blank', then: 'addClass red', gtarget: 'body' }
57+
ff.input :description, input_html: { data: dyn_description }
5758
end
5859
f.actions
5960
end

0 commit comments

Comments
 (0)