From 8b1fae28561480a1675da11359ff5f18c99ffbbc Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Mon, 8 Apr 2019 21:06:09 +0200 Subject: [PATCH] async table js utility now with explicit selector --- static/js/utils/asyncTable.js | 187 +++++++++++--------------- templates/table/layout-wrapper.hamlet | 2 +- 2 files changed, 82 insertions(+), 107 deletions(-) diff --git a/static/js/utils/asyncTable.js b/static/js/utils/asyncTable.js index 1f2ba3c36..cbb48b2b9 100644 --- a/static/js/utils/asyncTable.js +++ b/static/js/utils/asyncTable.js @@ -6,15 +6,14 @@ * Async Table Utility * makes table filters, sorting and pagination behave asynchronously via AJAX calls * - * Attribute: [none] - * (will be set up automatically on tables) + * Attribute: uw-async-table * * Example usage: * (regular table) */ var ASYNC_TABLE_UTIL_NAME = 'asyncTable'; - var ASYNC_TABLE_UTIL_SELECTOR = 'table'; + var ASYNC_TABLE_UTIL_SELECTOR = '[uw-async-table]'; var ASYNC_TABLE_LOCAL_STORAGE_KEY = 'ASYNC_TABLE'; @@ -33,16 +32,12 @@ var asyncTableUtil = function(element) { var asyncTableHeader; var asyncTableId; - var lastHorizontalPosition; - var currentTableUrl; var ths = []; var pageLinks = []; var pagesizeForm; var scrollTable; - var utilInstances = []; - function init() { if (!element) { throw new Error('Async Table utility cannot be setup without an element!'); @@ -53,11 +48,20 @@ asyncTableHeader = element.dataset.asyncTableDbHeader; } - asyncTableId = element.id; + asyncTableId = element.querySelector('table').id; - console.log('asyncTable.init()', { asyncTableId }); + // find scrolltable wrapper + scrollTable = element.querySelector('.scrolltable'); + if (!scrollTable) { + throw new Error('Async Table cannot be set up without a .scrolltable element!'); + } - setup(); + setupSortableHeaders(); + setupPagination(); + setupPageSizeSelect(); + setupTableFilter(); + + processLocalStorage(); // mark initialized element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS); @@ -69,44 +73,67 @@ }; } - function setup() { - scrollTable = element.closest('.scrolltable'); - if (!scrollTable) { - return false; - } - - // sortable element headers - ths = Array.from(element.querySelectorAll('th.sortable')).map(function(th) { + function setupSortableHeaders() { + ths = Array.from(scrollTable.querySelectorAll('th.sortable')).map(function(th) { return { element: th }; }); - // pagination links + ths.forEach(function(th) { + th.clickHandler = function(event) { + var horizPos = (scrollTable || {}).scrollLeft; + setLocalStorageParameter('horizPos', horizPos); + clickHandler(event); + }; + th.element.addEventListener('click', th.clickHandler); + }); + } + + function setupPagination() { var pagination = element.querySelector('#' + asyncTableId + '-pagination'); if (pagination) { pageLinks = Array.from(pagination.querySelectorAll('.page-link')).map(function(link) { return { element: link }; }); - } + pageLinks.forEach(function(link) { + link.clickHandler = function(event) { + var tableBoundingRect = scrollTable.getBoundingClientRect(); + if (tableBoundingRect.top < HEADER_HEIGHT) { + var scrollTo = { + top: (scrollTable.offsetTop || 0) - HEADER_HEIGHT, + left: scrollTable.offsetLeft || 0, + behavior: 'smooth', + }; + setLocalStorageParameter('scrollTo', scrollTo); + } + clickHandler(event); + } + link.element.addEventListener('click', link.clickHandler); + }); + } + } + + function setupPageSizeSelect() { // pagesize form pagesizeForm = element.querySelector('#' + asyncTableId + '-pagesize-form'); + if (pagesizeForm) { + var pagesizeSelect = pagesizeForm.querySelector('[name=' + asyncTableId + '-pagesize]'); + pagesizeSelect.addEventListener('change', changePagesizeHandler); + } + } + + function setupTableFilter() { // // filter // var filterForm = element.querySelector('.' + TABLE_FILTER_FORM_CLASS); // if (filterForm) { // options.updateTableFrom = updateTableFrom; // utilInstances.push(window.utils.setup('asyncTableFilter', filterForm, options)); // } - - processLocalStorage(); - - setupListeners(); - element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS); } function reset() { removeListeners(); - element.classList.remove(ASYNC_TABLE_INITIALIZED_CLASS); } @@ -114,48 +141,14 @@ var scrollTo = getLocalStorageParameter('scrollTo'); if (scrollTo && scrollTable) { window.scrollTo(scrollTo); - scrollTo = null; } + setLocalStorageParameter('scrollTo', null); var horizPos = getLocalStorageParameter('horizPos'); if (horizPos && scrollTable) { - scrollTable.scrollLeft = lastHorizontalPosition; - lastHorizontalPosition = null; - } - } - - function setupListeners() { - ths.forEach(function(th) { - th.clickHandler = function(event) { - var boundClickHandler = clickHandler.bind(this); - var horizPos = (scrollTable || {}).scrollLeft; - lastHorizontalPosition = (scrollTable || {}).scrollLeft; - boundClickHandler(event, { horizPos }); - }; - th.element.addEventListener('click', th.clickHandler); - }); - - pageLinks.forEach(function(link) { - link.clickHandler = function(event) { - var boundClickHandler = clickHandler.bind(this); - var tableBoundingRect = scrollTable.getBoundingClientRect(); - if (tableBoundingRect.top < HEADER_HEIGHT) { - var scrollTo = { - top: (scrollTable.offsetTop || 0) - HEADER_HEIGHT, - left: scrollTable.offsetLeft || 0, - behavior: 'smooth', - }; - setLocalStorageParameter('scrollTo', JSON.stringify(scrollTo)); - } - boundClickHandler(event, tableOptions); - } - link.element.addEventListener('click', link.clickHandler); - }); - - if (pagesizeForm) { - var pagesizeSelect = pagesizeForm.querySelector('[name=' + asyncTableId + '-pagesize]'); - pagesizeSelect.addEventListener('change', changePagesizeHandler); + scrollTable.scrollLeft = horizPos; } + setLocalStorageParameter('horizPos', null); } function removeListeners() { @@ -173,40 +166,41 @@ } } - function clickHandler(event, tableOptions) { + function clickHandler(event) { event.preventDefault(); - var url = getClickDestination(this); + var url = getClickDestination(event.target); if (!url.match(/^http/)) { - url = new URL(window.location.origin + window.location.pathname + getClickDestination(this)); + url = window.location.origin + window.location.pathname + url; } - updateTableFrom(url, tableOptions); + updateTableFrom(url); } function getClickDestination(el) { - if (!el.querySelector('a')) { + if (!el.matches('a') && !el.querySelector('a')) { return ''; } - return el.querySelector('a').getAttribute('href'); + return el.getAttribute('href') || el.querySelector('a').getAttribute('href'); } function changePagesizeHandler(event) { var pagesizeParamKey = asyncTableId + '-pagesize'; var pageParamKey = asyncTableId + '-page'; - var url = new URL(currentTableUrl || window.location.href); + var url = new URL(getLocalStorageParameter('currentTableUrl') || window.location.href); url.searchParams.set(pagesizeParamKey, event.target.value); url.searchParams.set(pageParamKey, 0); - updateTableFrom(url); + updateTableFrom(url.href); } // fetches new sorted element from url with params and replaces contents of current element - function updateTableFrom(url, tableOptions, callback) { + function updateTableFrom(url) { if (!HttpClient) { throw new Error('HttpClient not found!'); } + console.log('updating from ', { url }); + element.classList.add(ASYNC_TABLE_LOADING_CLASS); - tableOptions = tableOptions || {}; var headers = { 'Accept': 'text/html', [asyncTableHeader]: asyncTableId @@ -218,12 +212,11 @@ } return response.text(); }).then(function(data) { - currentTableUrl = url.href; + setLocalStorageParameter('currentTableUrl', url.href); + // reset table reset(); - updateWrapperContents(data, tableOptions); - if (callback && typeof callback === 'function') { - callback(element); - } + // update table with new + updateWrapperContents(data); element.classList.remove(ASYNC_TABLE_LOADING_CLASS); }).catch(function(err) { @@ -231,33 +224,11 @@ }); } - function updateWrapperContents(newHtml, tableOptions) { - tableOptions = tableOptions || {}; - element.innerHTML = newHtml; - - - // merge global options and element specific options - // var resetOptions = {}; - // Object.keys(options) - // .filter(function(key) { - // return !RESET_OPTIONS.includes(key); - // }) - // .forEach(function(key) { - // resetOptions[key] = options[key]; - // }); - // var combinedOptions = {}; - // combinedOptions = Object.keys(tableOptions) - // .filter(function(key) { - // return tableOptions.hasOwnProperty(key); - // }) - // .map(function(key) { - // return { key, value: tableOptions[key] } - // }) - // .reduce(function(cumulatedOpts, opt) { - // cumulatedOpts[opt.key] = opt.value; - // return cumulatedOpts; - // }, resetOptions); - + function updateWrapperContents(newHtml) { + var newPage = document.createElement('div'); + newPage.innerHTML = newHtml; + var newWrapperContents = newPage.querySelector('#' + element.id); + element.innerHTML = newWrapperContents.innerHTML; if (UtilRegistry) { UtilRegistry.setupAll(); @@ -269,7 +240,11 @@ function setLocalStorageParameter(key, value) { var currentLSState = JSON.parse(window.localStorage.getItem(ASYNC_TABLE_LOCAL_STORAGE_KEY)) || {}; - currentLSState[key] = value; + if (value !== null) { + currentLSState[key] = value; + } else { + delete currentLSState[key]; + } window.localStorage.setItem(ASYNC_TABLE_LOCAL_STORAGE_KEY, JSON.stringify(currentLSState)); } diff --git a/templates/table/layout-wrapper.hamlet b/templates/table/layout-wrapper.hamlet index bfa000765..fc144c1ed 100644 --- a/templates/table/layout-wrapper.hamlet +++ b/templates/table/layout-wrapper.hamlet @@ -1,3 +1,3 @@ $newline never -
+
^{table}