add table filter js utility

This commit is contained in:
Felix Hamann 2019-03-02 22:27:03 +01:00
parent f20f2cb005
commit 7756862aeb
5 changed files with 186 additions and 32 deletions

View File

@ -1021,6 +1021,7 @@ siteLayout' headingOverride widget = do
addScript $ StaticR js_utils_asidenav_js
addScript $ StaticR js_utils_asyncForm_js
addScript $ StaticR js_utils_asyncTable_js
addScript $ StaticR js_utils_asyncTableFilter_js
addScript $ StaticR js_utils_checkAll_js
addScript $ StaticR js_utils_httpClient_js
addScript $ StaticR js_utils_form_js

View File

@ -5,6 +5,8 @@
var HEADER_HEIGHT = 80;
var RESET_OPTIONS = [ 'scrollTo' ];
var TABLE_FILTER_FORM_CLASS = 'table-filter-form';
var JS_INITIALIZED_CLASS = 'js-async-table-initialized';
window.utils.asyncTable = function(wrapper, options) {
@ -42,17 +44,24 @@
// pagesize form
pagesizeForm = wrapper.querySelector('#' + tableIdent + '-pagesize-form');
// filter
var filterForm = wrapper.querySelector('.' + TABLE_FILTER_FORM_CLASS);
if (filterForm) {
options.updateTableFrom = updateTableFrom;
window.utils.setup('asyncTableFilter', filterForm, options);
}
// take options into account
if (options && options.scrollTo) {
if (options.scrollTo) {
window.scrollTo(options.scrollTo);
}
if (options && options.horizPos && scrollTable) {
if (options.horizPos && scrollTable) {
scrollTable.scrollLeft = options.horizPos;
}
setupListeners();
wrapper.classList.add('js-initialized');
wrapper.classList.add(JS_INITIALIZED_CLASS);
}
function setupListeners() {
@ -117,28 +126,16 @@
}
function changePagesizeHandler(event) {
var currentTableUrl = options.currentUrl || window.location.href;
var url = getUrlWithUpdatedPagesize(currentTableUrl, event.target.value);
url = new URL(getUrlWithResetPagenumber(url));
var pagesizeParamKey = tableIdent + '-pagesize';
var pageParamKey = tableIdent + '-page';
var url = new URL(options.currentUrl || window.location.href);
url.searchParams.set(pagesizeParamKey, event.target.value);
url.searchParams.set(pageParamKey, 0);
updateTableFrom(url);
}
function getUrlWithUpdatedPagesize(url, pagesize) {
if (url.indexOf('pagesize') >= 0) {
return url.replace(/pagesize=(\d+|all)/, 'pagesize=' + pagesize);
} else if (url.indexOf('?') >= 0) {
return url += '&' + tableIdent + '-pagesize=' + pagesize;
}
return url += '?' + tableIdent + '-pagesize=' + pagesize;
}
function getUrlWithResetPagenumber(url) {
return url.replace(/-page=\d+/, '-page=0');
}
// fetches new sorted table from url with params and replaces contents of current table
function updateTableFrom(url, tableOptions) {
function updateTableFrom(url, tableOptions, callback) {
if (!window.utils.httpClient) {
throw new Error('httpClient not found!');
}
@ -157,6 +154,9 @@
tableOptions.currentUrl = url.href;
removeListeners();
updateWrapperContents(data, tableOptions);
if (callback && typeof callback === 'function') {
callback(wrapper);
}
}).catch(function(err) {
console.error(err);
});
@ -165,7 +165,7 @@
function updateWrapperContents(newHtml, tableOptions) {
tableOptions = tableOptions || {};
wrapper.innerHTML = newHtml;
wrapper.classList.remove("js-initialized");
wrapper.classList.remove(JS_INITIALIZED_CLASS);
// setup the wrapper and its components to behave async again
window.utils.teardown('asyncTable');

View File

@ -0,0 +1,154 @@
(function () {
'use strict';
window.utils = window.utils || {};
var JS_INITIALIZED_CLASS = 'js-async-table-filter-initialized';
// debounce function, taken from Underscore.js
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
window.utils.asyncTableFilter = function(formElement, options) {
if (!options || !options.updateTableFrom) {
return false;
}
if (formElement.matches('.' + JS_INITIALIZED_CLASS)) {
return false;
}
var formId = formElement.querySelector('[name="_formid"]').value;
var inputs = {
search: [],
input: [],
change: [],
select: [],
}
function setup() {
gatherInputs();
addEventListeners();
}
function gatherInputs() {
Array.from(formElement.querySelectorAll('input[type="search"]')).forEach(function(input) {
inputs.search.push(input);
});
Array.from(formElement.querySelectorAll('input[type="text"]')).forEach(function(input) {
inputs.input.push(input);
});
Array.from(formElement.querySelectorAll('input:not([type="text"]):not([type="search"])')).forEach(function(input) {
inputs.change.push(input);
});
Array.from(formElement.querySelectorAll('select')).forEach(function(input) {
inputs.select.push(input);
});
}
function addEventListeners() {
inputs.search.forEach(function(input) {
var debouncedInput = debounce(function() {
if (input.value.length === 0 || input.value.length > 2) {
updateTable();
}
}, 400);
input.addEventListener('input', debouncedInput);
});
inputs.input.forEach(function(input) {
var debouncedInput = debounce(function() {
if (input.value.length === 0 || input.value.length > 2) {
updateTable();
}
}, 400);
input.addEventListener('input', debouncedInput);
});
inputs.change.forEach(function(input) {
input.addEventListener('change', function() {
updateTable();
});
});
inputs.select.forEach(function(input) {
input.addEventListener('change', function() {
updateTable();
});
});
formElement.addEventListener('submit', function(event) {
event.preventDefault();
updateTable();
});
}
function updateTable() {
var url = serializeFormToURL();
var callback = null;
var focusedSearch = inputs.search.reduce(function(acc, input) {
return acc || (input.matches(':focus') && input);
}, null);
// focus search input
if (focusedSearch) {
var selectionStart = focusedSearch.selectionStart;
callback = function(wrapper) {
var search = wrapper.querySelector('input[type="search"]');
if (search) {
search.focus();
search.selectionStart = selectionStart;
}
};
}
options.updateTableFrom(url, options, callback);
}
function serializeFormToURL() {
var url = new URL(options.currentUrl || window.location.href);
url.searchParams.set('_formid', formId);
url.searchParams.set('_hasdata', 'true');
inputs.search.forEach(function(input) {
url.searchParams.set(input.name, input.value);
});
inputs.input.forEach(function(input) {
url.searchParams.set(input.name, input.value);
});
inputs.change.forEach(function(input) {
if (input.checked) {
url.searchParams.set(input.name, input.value);
}
});
inputs.select.forEach(function(select) {
var options = Array.from(select.querySelectorAll('option'));
var selected = options.find(function(option) { return option.selected });
if (selected) {
url.searchParams.set(select.name, selected.value);
}
});
return url;
}
setup();
}
})();

View File

@ -26,10 +26,7 @@
}
// reactive buttons
var submitBtn = form.querySelector(SUBMIT_BUTTON_SELECTOR);
if (submitBtn) {
window.utils.setup('reactiveButton', form, { button: submitBtn });
}
window.utils.setup('reactiveButton', form);
// conditonal fieldsets
var fieldSets = Array.from(form.querySelectorAll('fieldset[data-conditional-id][data-conditional-value]'));
@ -43,18 +40,20 @@
window.utils.setup('asyncForm', form, options);
}
// inputs
window.utils.setup('inputs', form, options);
form.classList.add(JS_INITIALIZED);
};
// registers input-listener for each element in <inputs> (array) and
// enables <button> if <formValidator> for these inputs returns true
window.utils.reactiveButton = function(form, options) {
options = options || {};
var button = options.button;
var button = form.querySelector(SUBMIT_BUTTON_SELECTOR);
var requireds = Array.from(form.querySelectorAll('[required]'));
if (!button) {
throw new Error('Please provide both a button to reactiveButton');
if (!button || button.matches(AUTOSUBMIT_BUTTON_SELECTOR)) {
return false;
}
if (requireds.length == 0) {

View File

@ -1,8 +1,8 @@
$newline never
<section>
<form method=GET action=#{filterAction} enctype=#{filterEnctype}>
<form .table-filter-form method=GET action=#{filterAction} enctype=#{filterEnctype}>
^{filterWgdt}
<button>
<button type=submit data-autosubmit>
^{btnLabel BtnSubmit}
<section>
^{scrolltable}