From 136a47bf1018defd7947465dfde211782ef5fadb Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Thu, 11 Jun 2020 18:20:20 -0700 Subject: [PATCH] Add support for spinner groups, all buttons spin when one clicked. --- frontends/etiquette_flask/static/js/common.js | 97 ++++++++++++++++--- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/frontends/etiquette_flask/static/js/common.js b/frontends/etiquette_flask/static/js/common.js index 68603ff..ef21a31 100644 --- a/frontends/etiquette_flask/static/js/common.js +++ b/frontends/etiquette_flask/static/js/common.js @@ -262,13 +262,55 @@ function init_button_with_confirm() }); } -var spinner_button_index = 0; +common.spinner_button_index = 0; +common.button_spinner_groups = {}; +// When a group member is closing, it will call the closer on all other members +// in the group. Of course, this would recurse forever without some kind of +// flagging, so this dict will hold group_id:true if a close is in progress, +// and be empty otherwise. +common.spinner_group_closing = {}; + +common.add_to_spinner_group = +function add_to_spinner_group(group_id, button) +{ + if (!(group_id in common.button_spinner_groups)) + { + common.button_spinner_groups[group_id] = []; + } + common.button_spinner_groups[group_id].push(button); +} + +common.close_grouped_spinners = +function close_grouped_spinners(group_id) +{ + if (group_id && !(common.spinner_group_closing[group_id])) + { + common.spinner_group_closing[group_id] = true; + common.button_spinner_groups[group_id].forEach(function(button) + { + window[button.dataset.spinnerCloser](); + }); + delete common.spinner_group_closing[group_id]; + } +} + +common.open_grouped_spinners = +function open_grouped_spinners(group_id) +{ + common.button_spinner_groups[group_id].forEach(function(button) + { + window[button.dataset.spinnerOpener](); + }); +} + common.init_button_with_spinner = function init_button_with_spinner() { /* To create a button that has a spinner, and cannot be clicked again while the action is running, assign it the class "button_with_spinner". + When you're ready for the spinner to disappear, call + window[button.dataset.spinnerCloser](). Required: data-onclick: The string that would normally be the button's onclick. @@ -277,7 +319,17 @@ function init_button_with_spinner() data-spinner-id: If you want to use your own element as the spinner, give its ID here. Otherwise a new one will be created. + data-spinner-delay: The number of milliseconds to wait before the + spinner appears. For tasks that you expect to run very quickly, + this helps prevent a pointlessly short spinner. + data-holder-class: CSS class for the new span that holds the menu. + + data-spinner-group: An opaque string. All button_with_spinner that have + the same group will go into spinner mode when any of them is + clicked. Useful if you want to have two copies of a button on the + page, or two buttons which do opposite things and you only want one + to run at a time. */ var buttons = Array.from(document.getElementsByClassName("button_with_spinner")); buttons.forEach(function(button) @@ -305,26 +357,47 @@ function init_button_with_spinner() holder.appendChild(spinner_element); } + if (button.dataset.spinnerGroup) + { + common.add_to_spinner_group(button.dataset.spinnerGroup, button); + } + var spin = new spinner.Spinner(spinner_element); var spin_delay = parseFloat(button.dataset.spinnerDelay) || 0; + button.dataset.spinnerOpener = "spinner_opener_" + common.spinner_button_index; + window[button.dataset.spinnerOpener] = function spinner_opener() + { + spin.show(spin_delay); + button.disabled = true; + } + // It is expected that the function referenced by data-onclick will call + // window[button.dataset.spinnerCloser]() when appropriate, since from + // our perspective we cannot be sure when to close the spinner. + button.dataset.spinnerCloser = "spinner_closer_" + common.spinner_button_index; + window[button.dataset.spinnerCloser] = function spinner_closer() + { + common.close_grouped_spinners(button.dataset.spinnerGroup); + spin.hide(); + button.disabled = false; + } + var wrapped_onclick = Function(button.dataset.onclick); button.onclick = function() { - spin.show(spin_delay); - button.disabled = true; - wrapped_onclick(); + if (button.dataset.spinnerGroup) + { + common.open_grouped_spinners(button.dataset.spinnerGroup); + } + else + { + window[button.dataset.spinnerOpener](); + } + return wrapped_onclick(); } delete button.dataset.onclick; - var closer_id = "spinner_closer_" + spinner_button_index; - spinner_button_index += 1; - window[closer_id] = function spinner_closer() - { - spin.hide(); - button.disabled = false; - } - button.dataset.spinnerCloser = closer_id; + common.spinner_button_index += 1; }); }