diff --git a/src/Foundation.hs b/src/Foundation.hs index 84766c47e..98bdd1c5a 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1020,6 +1020,7 @@ siteLayout' headingOverride widget = do addScript $ StaticR js_utils_alerts_js addScript $ StaticR js_utils_asidenav_js addScript $ StaticR js_utils_asyncTable_js + addScript $ StaticR js_utils_checkAll_js addScript $ StaticR js_utils_form_js addScript $ StaticR js_utils_inputs_js addScript $ StaticR js_utils_setup_js diff --git a/static/js/utils/asyncTable.js b/static/js/utils/asyncTable.js index fe164d3b2..d11f6298e 100644 --- a/static/js/utils/asyncTable.js +++ b/static/js/utils/asyncTable.js @@ -18,7 +18,7 @@ var scrollTable; function init() { - var table = wrapper.querySelector('#' + tableIdent); + var table = wrapper.querySelector('#' + tableIdent); if (!table) { return; diff --git a/static/js/utils/checkAll.js b/static/js/utils/checkAll.js new file mode 100644 index 000000000..5decca2de --- /dev/null +++ b/static/js/utils/checkAll.js @@ -0,0 +1,117 @@ +(function() { + 'use strict'; + + window.utils = window.utils || {}; + + var JS_INITIALIZED_CLASS = 'js-check-all-initialized'; + var CHECKBOX_SELECTOR = '[type="checkbox"]'; + + function getCheckboxId() { + return 'check-all-checkbox-' + Math.floor(Math.random() * 100000); + } + + window.utils.checkAll = function(wrapper, options) { + + if (!wrapper || wrapper.classList.contains(JS_INITIALIZED_CLASS)) { + return false; + } + options = options || {}; + + var columns = []; + var checkboxColumn = []; + var checkAllCheckbox = null; + + function init() { + + columns = gatherColumns(wrapper); + + setupCheckAllCheckbox(findCheckboxColumn(columns)); + + wrapper.classList.add(JS_INITIALIZED_CLASS); + } + + function gatherColumns(table) { + var rows = Array.from(table.querySelectorAll('tr')); + var cols = []; + rows.forEach(function(tr) { + var cells = Array.from(tr.querySelectorAll('td')); + cells.forEach(function(cell, cellIndex) { + if (!cols[cellIndex]) { + cols[cellIndex] = []; + } + cols[cellIndex].push(cell); + }); + }); + return cols; + } + + function findCheckboxColumn(columns) { + var checkboxColumnId = null; + columns.forEach(function(col, i) { + if (isCheckboxColumn(col)) { + checkboxColumnId = i; + } + }); + return checkboxColumnId; + } + + function isCheckboxColumn(col) { + var onlyCheckboxes = true; + col.forEach(function(cell) { + if (onlyCheckboxes && !cell.querySelector(CHECKBOX_SELECTOR)) { + onlyCheckboxes = false; + } + }); + return onlyCheckboxes; + } + + function setupCheckAllCheckbox(columnId) { + if (columnId === null) { + return; + } + + checkboxColumn = columns[columnId]; + var firstRow = wrapper.querySelector('tr'); + var th = Array.from(firstRow.querySelectorAll('th, td'))[columnId]; + th.innerHTML = 'test'; + checkAllCheckbox = document.createElement('input'); + checkAllCheckbox.setAttribute('type', 'checkbox'); + checkAllCheckbox.setAttribute('id', getCheckboxId()); + th.innerHTML = ''; + th.insertBefore(checkAllCheckbox, null); + window.utils.setup('checkboxRadio', checkAllCheckbox); + + checkAllCheckbox.addEventListener('input', onCheckAllCheckboxInput); + setupCheckboxListeners(); + } + + function onCheckAllCheckboxInput() { + toggleAll(checkAllCheckbox.checked); + } + + function setupCheckboxListeners() { + checkboxColumn + .map(function(cell) { + return cell.querySelector(CHECKBOX_SELECTOR); + }) + .forEach(function(checkbox) { + checkbox.addEventListener('input', updateCheckAllCheckboxState); + }); + } + + function updateCheckAllCheckboxState() { + var allChecked = checkboxColumn.reduce(function(acc, cell) { + return acc && cell.querySelector(CHECKBOX_SELECTOR).checked; + }, true); + checkAllCheckbox.checked = allChecked; + } + + function toggleAll(checked) { + checkboxColumn.forEach(function(cell) { + cell.querySelector(CHECKBOX_SELECTOR).checked = checked; + }); + } + + init(); + }; +})(); diff --git a/templates/table/layout.julius b/templates/table/layout.julius index 3bc39312d..d0f02a7d0 100644 --- a/templates/table/layout.julius +++ b/templates/table/layout.julius @@ -1,10 +1,11 @@ document.addEventListener('DOMContentLoaded', function() { var dbtIdent = #{String $ dbtIdent}; var headerDBTableShortcircuit = #{String (toPathPiece HeaderDBTableShortcircuit)}; - var selector = '#' + dbtIdent + '-table-wrapper:not(.js-initialized)'; + var selector = '#' + dbtIdent + '-table-wrapper'; var wrapper = document.querySelector(selector); if (wrapper) { window.utils.setup('asyncTable', wrapper, { headerDBTableShortcircuit, dbtIdent }); + window.utils.setup('checkAll', wrapper); } });