diff --git a/src/pretix/control/templates/pretixcontrol/subevents/index.html b/src/pretix/control/templates/pretixcontrol/subevents/index.html index bdec4ae96..89ed7f4be 100644 --- a/src/pretix/control/templates/pretixcontrol/subevents/index.html +++ b/src/pretix/control/templates/pretixcontrol/subevents/index.html @@ -67,7 +67,7 @@ {% if "can_change_event_settings" in request.eventpermset %} - + {% endif %} @@ -108,7 +108,7 @@ {% if "can_change_event_settings" in request.eventpermset %} - + {% endif %} diff --git a/src/pretix/static/pretixcontrol/js/ui/main.js b/src/pretix/static/pretixcontrol/js/ui/main.js index 40ea6acfd..73b6d3bce 100644 --- a/src/pretix/static/pretixcontrol/js/ui/main.js +++ b/src/pretix/static/pretixcontrol/js/ui/main.js @@ -690,47 +690,104 @@ $(function () { // Tables with bulk selection, e.g. subevent list $("input[data-toggle-table]").each(function (ev) { var $toggle = $(this); - - var update = function () { - var all_true = true; - var all_false = true; - $toggle.closest("table").find("tbody td:first-child input[type=checkbox]").each(function () { - if ($(this).prop("checked")) { - all_false = false; - } else { - all_true = false; - } - }); - if (all_true) { - $toggle.prop("checked", true).prop("indeterminate", false); - } else if (all_false) { - $toggle.prop("checked", false).prop("indeterminate", false); - } else { - $toggle.prop("checked", false).prop("indeterminate", true); + var $table = $toggle.closest("table"); + var $selectAll = $table.find(".table-select-all"); + var $rows = $table.find("tbody tr"); + var $checkboxes = $rows.find("td:first-child input[type=checkbox]"); + var firstIndex, lastIndex, selectionChecked, onChangeSelectionHappened = false; + var updateSelection = function(a, b, checked) { + if (a > b) { + //[a, b] = [b, a];// ES6 not ready yet for pretix + var tmp = a; + a = b; + b = tmp; } - var is_incomplete = $toggle.prop("indeterminate") || !$toggle.prop("checked") - $toggle.closest("table").find(".table-select-all").toggleClass( - "hidden", is_incomplete - ) - if (is_incomplete) { - $toggle.closest("table").find(".table-select-all input").prop( - "checked", false - ) + for (var i = a; i <= b; i++) { + var checkbox = $checkboxes.get(i); + if (!checkbox.hasAttribute("data-inital")) checkbox.setAttribute("data-inital", checkbox.checked); + if (checked === undefined || checked === null) checkbox.checked = checkbox.getAttribute("data-inital") === "true"; + else checkbox.checked = checked; } }; + var onChangeSelection = function(ev) { + onChangeSelectionHappened = true; - $(this).closest("table").find("td:first-child input[type=checkbox]").change(update); - $(this).change(function (ev) { - $(this).closest("table").find("tbody td:first-child input[type=checkbox]").prop("checked", $(this).prop("checked")); - var is_incomplete = $(this).prop("indeterminate") || !$(this).prop("checked") - $(this).closest("table").find(".table-select-all").toggleClass( - "hidden", is_incomplete - ) - if (is_incomplete) { - $(this).closest("table").find(".table-select-all input").prop( - "checked", false - ) + var row = ev.target.closest("tr"); + var currentIndex = 0; + while(row = row.previousSibling) { + if (row.tagName) currentIndex++; } + var dCurrent = currentIndex - firstIndex; + var dLast = lastIndex - firstIndex; + if (dCurrent*dLast < 0) { + // direction of selection changed => reset all previously selected + updateSelection(lastIndex, firstIndex); + } + else if (Math.abs(dCurrent) < Math.abs(dLast)) { + // selection distance decreased => reset unselected + updateSelection(currentIndex, lastIndex); + } + lastIndex = currentIndex; + updateSelection(firstIndex, currentIndex, selectionChecked); + + ev.preventDefault(); + }; + $table.on("pointerdown", function(ev) { + if (!ev.target.closest("td:first-child")) return; + var row = ev.target.closest("tr"); + selectionChecked = !row.querySelector("td:first-child input").checked; + + firstIndex = 0; + while(row = row.previousSibling) { + if (row.tagName) firstIndex++; + } + lastIndex = firstIndex; + + ev.preventDefault(); + $rows.on("pointerenter", onChangeSelection); + + $(document).one("pointerup", function(ev) { + if (onChangeSelectionHappened) { + ev.preventDefault(); + onChangeSelectionHappened = false; + $checkboxes.removeAttr("data-inital"); + + update(); + } + $rows.off("pointerenter", onChangeSelection); + }); + }); + + + var update = function () { + var all_same; + var checkboxes = $checkboxes.toArray(); + var i = checkboxes.length; + while (i--) { + if (all_same === undefined) { + all_same = checkboxes[i].checked; + continue; + } + if (all_same != checkboxes[i].checked) { + $toggle.prop("checked", false).prop("indeterminate", true).trigger("change"); + return; + } + } + $toggle.prop("checked", all_same).prop("indeterminate", false).trigger("change"); + }; + + var debounceUpdate; + $checkboxes.change(function() { + //$(this).closest("tr").toggleClass("warning", this.checked); + // when changing the $toggle’s checked-property, lots of change events + // get triggered => debounce + if (debounceUpdate) window.clearTimeout(debounceUpdate); + debounceUpdate = window.setTimeout(update, 10); + }); + $toggle.change(function (ev) { + if (!this.indeterminate) $checkboxes.prop("checked", this.checked);//.trigger("change"); + $selectAll.toggleClass("hidden", !this.checked).prop("hidden", !this.checked); + if (!this.checked) $selectAll.find("input").prop("checked", false); }); }); diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index d45a76118..d79fe15a7 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -614,6 +614,14 @@ table td > .checkbox input[type="checkbox"] { padding-bottom: 5px; } + +.batch-select-label { + display: block; + width: 100%; + height: 1.5em; + cursor: pointer; +} + .bulk-edit-field-group { .field-toggle { font-weight: normal;