document.addEventListener('DOMContentLoaded', function () { function label(element, value) { if (element.hasAttribute('value')) { if (value !== undefined) { element.setAttribute('value', value) } else { return element.getAttribute('value') } } else { if (value !== undefined) { element.textContent = value } else { return element.textContent } } } function countdownDeleteButton(event) { const btn = event.target const countdown = btn.getAttribute('data-countdown') if (countdown !== '1') { event.preventDefault() btn.classList.add('__on-countdown') const hasCountdown = countdown !== null const newCountdown = hasCountdown ? countdown - 1 : 3 if (!hasCountdown) { btn.setAttribute('data-label', label(btn)) } label(btn, newCountdown) btn.setAttribute('data-countdown', newCountdown) setTimeout(countdownDeleteButton, 1000, event) } else { label(btn, btn.getAttribute('data-label')) } } document.querySelectorAll('.delete-btn').forEach(function (btn) { btn.addEventListener('click', countdownDeleteButton) }) function dynamicTables() { const pageSize = 40 const range = (x, y) => x > y ? [] : [x, ...range(x + 1, y)]; const visibleRows = page => range(page * pageSize, (page + 1) * pageSize - 1) const tblRows = tbl => [...tbl.tBodies[0].rows] const partial11 = (fn, arg1) => function (arg2) { fn(arg1, arg2) } const partial21 = (fn, arg1, arg2) => function(arg3) { fn(arg1, arg2, arg3) } function toggleVisibilities(tbl, ixsToShow) { tblRows(tbl).forEach(function (row, ix) { row.style.display = ixsToShow.includes(ix) ? 'table-row' : 'none'; }) } function setCurrPage(ctx, currPageIx) { const rows = tblRows(ctx.tbl).length const pages = Math.ceil(rows / pageSize) ctx.rowsLbls.forEach(function (elem) { elem.innerText = rows }) ctx.pageLbls.forEach(function (elem) { elem.innerText = (currPageIx + 1) + ' / ' + pages }) ctx.tbl.setAttribute('data-page', currPageIx) } function changePage(ctx, ix) { toggleVisibilities(ctx.tbl, visibleRows(ix)) setCurrPage(ctx, ix) } function enablePagination(ctx, on) { [...ctx.nextBtns, ...ctx.prevBtns].forEach(function (btn) { btn.disabled = !on }) ctx.pageLbls.forEach(function (elem) { elem.style.display = on ? 'inline' : 'none' }) } function prevButton() { const btn = document.createElement('button') btn.classList.add('prev') btn.classList.add('pg-btn') btn.classList.add('btn') btn.classList.add('btn-light') return btn; } function nextButton() { const btn = document.createElement('button') btn.classList.add('next') btn.classList.add('pg-btn') btn.classList.add('btn') btn.classList.add('btn-light') return btn } function currPage() { const rowInfo = document.createElement('span') rowInfo.classList.add('row-info') const rows = document.createElement('span') rows.classList.add('rows') rows.innerText = '0' const page = document.createElement('span') page.classList.add('page') page.innerText = '1' rowInfo.insertAdjacentElement('afterbegin', rows) rowInfo.insertAdjacentElement('afterbegin', page) return rowInfo } function searchBox() { const input = document.createElement('input') input.classList.add('search') input.classList.add('form-control') input.type = 'search' input.placeholder = '🔍...' return input } function onSearch(ctx, evt) { const term = evt.target.value.toLocaleLowerCase() if (term.length === 0) { changePage(ctx, 0) enablePagination(ctx, true) return } enablePagination(ctx, false) const contents = tblRows(ctx.tbl).map(function (row) { return Array.from(row.cells).reduce(function (carr, cell) { return carr + cell.textContent.toLocaleLowerCase() }, "") }) const ixs = [] contents.forEach(function (rowContent, ix) { if (rowContent.match(term)) { ixs.push(ix) } }) ctx.rowsLbls.forEach(function (elem) { elem.innerText = ixs.length }) toggleVisibilities(ctx.tbl, ixs) } function onPrevClick(ctx) { const page = ctx.tbl.getAttribute('data-page') * 1 if (page > 0) { changePage(ctx, page - 1) } } function onNextClick(ctx) { const page = ctx.tbl.getAttribute('data-page') * 1 const lastRowNum = (page + 1) * pageSize if (lastRowNum < tblRows(ctx.tbl).length) { changePage(ctx, page + 1) } } function buildContext(tbl) { const currPage1 = currPage() const currPage2 = currPage() const ctx = { tbl: tbl, currPages: [currPage1, currPage2], pageLbls: [currPage1.children[0], currPage2.children[0]], rowsLbls: [currPage1.children[1], currPage2.children[1]], searchBox: searchBox(), prevBtns: [prevButton(), prevButton()], nextBtns: [nextButton(), nextButton()] } ctx.searchBox.addEventListener('input', partial11(onSearch, ctx)) ctx.prevBtns.forEach(function (btn) { btn.addEventListener('click', partial11(onPrevClick, ctx)) }) ctx.nextBtns.forEach(function (btn) { btn.addEventListener('click', partial11(onNextClick, ctx)) }) return ctx } function addToolbar(tbl) { const ctx = buildContext(tbl) const toolbarTop = document.createElement('section') toolbarTop.classList.add('tbl-toolbar') toolbarTop.insertAdjacentElement('beforeend', ctx.currPages[0]) toolbarTop.insertAdjacentElement('beforeend', ctx.searchBox) toolbarTop.insertAdjacentElement('beforeend', ctx.prevBtns[0]) toolbarTop.insertAdjacentElement('beforeend', ctx.nextBtns[0]) const toolbarBottom = document.createElement('section') toolbarBottom.classList.add('tbl-toolbar') toolbarBottom.insertAdjacentElement('beforeend', ctx.currPages[1]) toolbarBottom.insertAdjacentElement('beforeend', ctx.prevBtns[1]) toolbarBottom.insertAdjacentElement('beforeend', ctx.nextBtns[1]) tbl.insertAdjacentElement('afterend', toolbarBottom) tbl.insertAdjacentElement('beforebegin', toolbarTop) return ctx } function flipSortDir(dir) { return (dir || 'asc') === 'asc' ? 'desc' : 'asc' } function clearSortAttributes(tbl) { [...tbl.tHead.rows[0].cells] .forEach(th => th.removeAttribute('data-sort')) } function onSort(ctx, ix, event) { const tbl = ctx.tbl const sortDir = event.target.getAttribute('data-sort') || 'asc' const sortMod = sortDir === 'asc' ? -1 : 1; const rows = tblRows(tbl) const newTbody = document.createElement('tbody') rows.sort((row1, row2) => { const val1 = row1.cells[ix].textContent const val2 = row2.cells[ix].textContent return sortMod * (val1 > val2 ? 1 : val2 > val1 ? -1 : 0); }) rows.forEach(row => newTbody.appendChild(row)) tbl.replaceChild(newTbody, tbl.tBodies[0]) clearSortAttributes(tbl) event.target.setAttribute('data-sort', flipSortDir(sortDir)) changePage(ctx, 0) } function enableSorting(ctx) { const ths = ctx.tbl.querySelectorAll('thead th') ths.forEach((th, ix) => { th.addEventListener('click', partial21(onSort, ctx, ix)) }) } document.querySelectorAll('table').forEach(function (tbl) { if (tbl.classList.contains('no-sort')) { return } const ctx = addToolbar(tbl) changePage(ctx, 0) enableSorting(ctx) }) } dynamicTables() })