Fixed #31523 -- Removed jQuery dependency from actions.js.

This commit is contained in:
Jon Dufresne 2020-06-24 11:13:51 +02:00 committed by Carlton Gibson
parent 074844e947
commit 30e59705fc
7 changed files with 178 additions and 158 deletions

View File

@ -211,6 +211,10 @@ p img, h1 img, h2 img, h3 img, h4 img, td img {
white-space: nowrap;
}
.hidden {
display: none;
}
/* TABLES */
table {

View File

@ -300,7 +300,6 @@
#changelist .actions span.question {
font-size: 13px;
margin: 0 0.5em;
display: none;
}
#changelist .actions:last-child {

View File

@ -22,10 +22,6 @@ form .form-row p {
padding-left: 0;
}
.hidden {
display: none;
}
/* FORM LABELS */
label {

View File

@ -1,154 +1,170 @@
/*global gettext, interpolate, ngettext*/
'use strict';
{
const $ = django.jQuery;
let lastChecked;
function show(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.classList.remove('hidden');
});
}
$.fn.actions = function(opts) {
const options = $.extend({}, $.fn.actions.defaults, opts);
const actionCheckboxes = $(this);
let list_editable_changed = false;
const showQuestion = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).show();
$(options.allContainer).hide();
},
showClear = function() {
$(options.acrossClears).show();
$(options.acrossQuestions).hide();
$(options.actionContainer).toggleClass(options.selectedClass);
$(options.allContainer).show();
$(options.counterContainer).hide();
},
reset = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).hide();
$(options.allContainer).hide();
$(options.counterContainer).show();
},
clearAcross = function() {
reset();
$(options.acrossInput).val(0);
$(options.actionContainer).removeClass(options.selectedClass);
},
checker = function(checked) {
if (checked) {
showQuestion();
} else {
reset();
}
$(actionCheckboxes).prop("checked", checked)
.parent().parent().toggleClass(options.selectedClass, checked);
},
updateCounter = function() {
const sel = $(actionCheckboxes).filter(":checked").length;
// data-actions-icnt is defined in the generated HTML
// and contains the total amount of objects in the queryset
const actions_icnt = $('.action-counter').data('actionsIcnt');
$(options.counterContainer).html(interpolate(
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
sel: sel,
cnt: actions_icnt
}, true));
$(options.allToggle).prop("checked", function() {
let value;
if (sel === actionCheckboxes.length) {
value = true;
showQuestion();
} else {
value = false;
clearAcross();
}
return value;
});
};
// Show counter by default
$(options.counterContainer).show();
// Check state of checkboxes and reinit state if needed
$(this).filter(":checked").each(function(i) {
$(this).parent().parent().toggleClass(options.selectedClass);
updateCounter();
if ($(options.acrossInput).val() === 1) {
showClear();
}
function hide(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.classList.add('hidden');
});
$(options.allToggle).show().on('click', function() {
checker($(this).prop("checked"));
updateCounter();
}
function showQuestion(options) {
hide(options.acrossClears);
show(options.acrossQuestions);
hide(options.allContainer);
}
function showClear(options) {
show(options.acrossClears);
hide(options.acrossQuestions);
document.querySelector(options.actionContainer).classList.remove(options.selectedClass);
show(options.allContainer);
hide(options.counterContainer);
}
function reset(options) {
hide(options.acrossClears);
hide(options.acrossQuestions);
hide(options.allContainer);
show(options.counterContainer);
}
function clearAcross(options) {
reset(options);
document.querySelector(options.acrossInput).value = 0;
document.querySelector(options.actionContainer).classList.remove(options.selectedClass);
}
function checker(actionCheckboxes, options, checked) {
if (checked) {
showQuestion(options);
} else {
reset(options);
}
actionCheckboxes.forEach(function(el) {
el.checked = checked;
el.closest('tr').classList.toggle(options.selectedClass, checked);
});
$("a", options.acrossQuestions).on('click', function(event) {
event.preventDefault();
$(options.acrossInput).val(1);
showClear();
});
$("a", options.acrossClears).on('click', function(event) {
event.preventDefault();
$(options.allToggle).prop("checked", false);
clearAcross();
checker(0);
updateCounter();
});
lastChecked = null;
$(actionCheckboxes).on('click', function(event) {
if (!event) { event = window.event; }
const target = event.target ? event.target : event.srcElement;
if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
let inrange = false;
$(lastChecked).prop("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
$(actionCheckboxes).each(function() {
if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
inrange = (inrange) ? false : true;
}
if (inrange) {
$(this).prop("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
}
});
}
$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
lastChecked = target;
updateCounter();
});
$('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() {
list_editable_changed = true;
});
$('form#changelist-form button[name="index"]').on('click', function(event) {
if (list_editable_changed) {
return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
}
});
$('form#changelist-form input[name="_save"]').on('click', function(event) {
let action_changed = false;
$('select option:selected', options.actionContainer).each(function() {
if ($(this).val()) {
action_changed = true;
}
});
if (action_changed) {
if (list_editable_changed) {
return confirm(gettext("You have selected an action, but you havent saved your changes to individual fields yet. Please click OK to save. Youll need to re-run the action."));
} else {
return confirm(gettext("You have selected an action, and you havent made any changes on individual fields. Youre probably looking for the Go button rather than the Save button."));
}
}
});
};
/* Setup plugin defaults */
$.fn.actions.defaults = {
}
function updateCounter(actionCheckboxes, options) {
const sel = Array.from(actionCheckboxes).filter(function(el) {
return el.checked;
}).length;
const counter = document.querySelector(options.counterContainer);
// data-actions-icnt is defined in the generated HTML
// and contains the total amount of objects in the queryset
const actions_icnt = Number(counter.dataset.actionsIcnt);
counter.textContent = interpolate(
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
sel: sel,
cnt: actions_icnt
}, true);
const allToggle = document.getElementById(options.allToggleId);
allToggle.checked = sel === actionCheckboxes.length;
if (allToggle.checked) {
showQuestion(options);
} else {
clearAcross(options);
}
}
const defaults = {
actionContainer: "div.actions",
counterContainer: "span.action-counter",
allContainer: "div.actions span.all",
acrossInput: "div.actions input.select-across",
acrossQuestions: "div.actions span.question",
acrossClears: "div.actions span.clear",
allToggle: "#action-toggle",
allToggleId: "action-toggle",
selectedClass: "selected"
};
$(document).ready(function() {
const $actionsEls = $('tr input.action-select');
if ($actionsEls.length > 0) {
$actionsEls.actions();
window.Actions = function(actionCheckboxes, options) {
options = Object.assign({}, defaults, options);
let list_editable_changed = false;
document.getElementById(options.allToggleId).addEventListener('click', function(event) {
checker(actionCheckboxes, options, this.checked);
updateCounter(actionCheckboxes, options);
});
document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) {
el.addEventListener('click', function(event) {
event.preventDefault();
const acrossInput = document.querySelector(options.acrossInput);
acrossInput.value = 1;
showClear(options);
});
});
document.querySelectorAll(options.acrossClears + " a").forEach(function(el) {
el.addEventListener('click', function(event) {
event.preventDefault();
document.getElementById(options.allToggleId).checked = false;
clearAcross(options);
checker(actionCheckboxes, options, false);
updateCounter(actionCheckboxes, options);
});
});
Array.from(document.getElementById('result_list').tBodies).forEach(function(el) {
el.addEventListener('change', function(event) {
const target = event.target;
if (target.classList.contains('action-select')) {
target.closest('tr').classList.toggle(options.selectedClass, target.checked);
updateCounter(actionCheckboxes, options);
} else {
list_editable_changed = true;
}
});
});
document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() {
if (list_editable_changed) {
const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
if (!confirmed) {
event.preventDefault();
}
}
});
const el = document.querySelector('#changelist-form input[name=_save]');
// The button does not exist if no fields are editable.
if (el) {
el.addEventListener('click', function(event) {
if (document.querySelector('[name=action]').value) {
const text = list_editable_changed
? gettext("You have selected an action, but you havent saved your changes to individual fields yet. Please click OK to save. Youll need to re-run the action.")
: gettext("You have selected an action, and you havent made any changes on individual fields. Youre probably looking for the Go button rather than the Save button.");
if (!confirm(text)) {
event.preventDefault();
}
}
});
}
};
// Call function fn when the DOM is loaded and ready. If it is already
// loaded, call the function now.
// http://youmightnotneedjquery.com/#ready
function ready(fn) {
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function() {
const actionsEls = document.querySelectorAll('tr input.action-select');
if (actionsEls.length > 0) {
Actions(actionsEls);
}
});
}

View File

@ -11,11 +11,11 @@
{% if actions_selection_counter %}
<span class="action-counter" data-actions-icnt="{{ cl.result_list|length }}">{{ selection_note }}</span>
{% if cl.result_count != cl.result_list|length %}
<span class="all">{{ selection_note_all }}</span>
<span class="question">
<span class="all hidden">{{ selection_note_all }}</span>
<span class="question hidden">
<a href="#" title="{% translate "Click here to select the objects across all pages" %}">{% blocktranslate with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktranslate %}</a>
</span>
<span class="clear"><a href="#">{% translate "Clear selection" %}</a></span>
<span class="clear hidden"><a href="#">{% translate "Clear selection" %}</a></span>
{% endif %}
{% endif %}
{% endblock %}

View File

@ -11,7 +11,7 @@ QUnit.module('admin.actions', {
const $ = django.jQuery;
$('#qunit-fixture').append($('#result-table').text());
$('tr input.action-select').actions();
Actions(document.querySelectorAll('tr input.action-select'));
}
});

View File

@ -11,18 +11,23 @@
<div id="qunit-fixture">
</div>
<script type="text/html" id="result-table">
<table id="result_list">
<tr>
<th>
<input type="checkbox" id="action-toggle">
</th>
</tr>
<tr>
<td class="action-checkbox">
<input class="action-select" type="checkbox" value="618">
</td>
</tr>
</table>
<form id="changelist-form">
<button type="submit" class="button" name="index" value="0">Go</button>
<span class="action-counter" data-actions-icnt="100"></span>
<table id="result_list">
<tr>
<th>
<input type="checkbox" id="action-toggle">
</th>
</tr>
<tr>
<td class="action-checkbox">
<input class="action-select" type="checkbox" value="618">
</td>
</tr>
</table>
<input type="submit" name="_save" value="Save">
</form>
</script>
<script type="text/html" id="tabular-formset">
<input id="id_first-TOTAL_FORMS" value="1">