From 37701cbc751863ea01fbf57a4fa63db6160b9f97 Mon Sep 17 00:00:00 2001 From: K-RT-Dev Date: Fri, 20 Sep 2024 15:49:36 -0300 Subject: [PATCH 1/2] feat: pagination --- conf/default.php | 4 ++ conf/metadata.php | 4 ++ helper.php | 38 ++++++++++++++- script.js | 122 ++++++++++++++++++++++++++++++++++++++++++++++ style.css | 9 ++++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 script.js diff --git a/conf/default.php b/conf/default.php index e3899d0..16bcb21 100644 --- a/conf/default.php +++ b/conf/default.php @@ -16,3 +16,7 @@ $conf['sort'] = 0; $conf['rsort'] = 0; $conf['sortby'] = ''; +$conf['pagination'] = 0; +$conf['rowsperpage'] = 10; +$conf['buttonsposition'] = 'bottom'; +$conf['buttonswindow'] = 3; \ No newline at end of file diff --git a/conf/metadata.php b/conf/metadata.php index 6203011..f9eb705 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -21,3 +21,7 @@ $meta['sort'] = array('onoff'); $meta['rsort'] = array('onoff'); $meta['sortby'] = array('string', '_pattern' => '/^([^&=]*)$/'); +$meta['pagination'] = array('onoff'); +$meta['rowsperpage'] = array('string', '_pattern' => '/^\d+$/'); +$meta['buttonsposition'] = array('multichoice', '_choices' => array('top', 'bottom')); +$meta['buttonswindow'] = array('string', '_pattern' => '/^-?\d+$/'); \ No newline at end of file diff --git a/helper.php b/helper.php index d7105cd..3987a9d 100644 --- a/helper.php +++ b/helper.php @@ -110,6 +110,11 @@ public function __construct() if($this->sortKey) { $this->sort = true; } + //Pagination flags + $this->pagination = $this->getConf('pagination'); //on-off + $this->rowsPerPage = $this->getConf('rowsperpage'); //string + $this->buttonsPosition = $this->getConf('buttonsposition'); //string (top, bottom) + $this->buttonsWindow = $this->getConf('buttonswindow'); //string $this->plugins = [ 'discussion' => ['comments'], @@ -291,6 +296,8 @@ public function setFlags($flags) case 'showdiff': $flag = 'diff'; break; + case 'pagination': + $this->pagination = true; } // it is not required to set the sort flag, rsort flag will reverse. @@ -315,6 +322,25 @@ public function setFlags($flags) $value = true; } + /**Pagination params. Rows per page */ + if (substr($flag, 0, 12) == 'rowsperpage=') { + $this->rowsPerPage = (int) substr($flag, 12); + $this->pagination = true; + } + + /**Pagination params. Navigation buttons position (top/bottom) */ + if (substr($flag, 0, 16) == 'buttonsposition=') { + $this->buttonsPosition = substr($flag, 16); + if ($this->buttonsPosition != 'top' && $this->buttonsPosition != 'bottom') { + $this->buttonsPosition = 'bottom'; + } + } + + //**Pagination params. Regulate the max number of navigation show at the same time. Min possible value 2. Use -1 to disable*/ + if (substr($flag, 0, 14) == 'buttonswindow=') { + $this->buttonsWindow = (int) substr($flag, 14); + } + if (isset($this->column[$flag]) && $flag !== 'page') { $this->column[$flag] = $value; } @@ -354,7 +380,12 @@ public function startList($callerClass = null) if ($callerClass) { $class .= ' ' . $callerClass; } - $this->doc = '
'; + $this->doc = '
'; } else { // Simplelist is enabled; Skip header and firsthl $this->showheader = false; @@ -549,6 +580,11 @@ protected function renderPageRow($page) $class .= $this->page['class']; } + //If pagination is in use, all the rows start hidden (for fix 'flickering' onload) + if ($this->pagination) { + $class .= 'hidden '; + } + if (!empty($class)) { $class = ' class="' . $class . '"'; } diff --git a/script.js b/script.js new file mode 100644 index 0000000..5da1ed9 --- /dev/null +++ b/script.js @@ -0,0 +1,122 @@ +document.addEventListener('DOMContentLoaded', function () { + //Get all tables with pagination class included + const tables = document.querySelectorAll('.table-with-pagination'); + + //Foreach table found in the wiki page + tables.forEach(table => { + const tableState = { + tableRef: table, + rowsRef: Array.from(table.getElementsByTagName('tr')).slice(1), + currentPage: 0, + rowsPerPage: Number(table.getAttribute('data-rowsperpage')) || 10, //Number of rows per page + buttonsPosition: table.getAttribute('data-buttonsposition') || 'bottom', //Position of buttons navigator + buttonsWindow: Number(table.getAttribute('data-buttonwindow')) || 3, //Number of max buttons in screen = buttonsWindow * 2 + 1 //Use -1 to disable + }; + tableState.totalPages = Math.ceil(tableState.rowsRef.length / tableState.rowsPerPage); + + createPageButtons(tableState); // Create the navigation buttons initially + showPage(tableState); // Set initial page state + }); + + function createPageButtons(tableState) { + const { totalPages, buttonsPosition, tableRef } = tableState; + const paginationTdContainer = document.createElement('td'); + + //Create buttons nodes + const buttonsRef = []; + for (let i = 0; i < totalPages; i++) { + buttonsRef.push(document.createElement('button')); + } + + //Set style and evento for each button + for (let i = 0; i < totalPages; i++) { + buttonsRef[i].textContent = i + 1; + buttonsRef[i].classList.add('pagination-button'); + //Event on click button + buttonsRef[i].addEventListener('click', () => { + tableState.currentPage = i; + showPage(tableState); //Set page content + setButtons(buttonsRef, tableState); //Set buttons state + }); + paginationTdContainer.appendChild(buttonsRef[i]); + } + + setButtons(buttonsRef, tableState); //Set buttons state + + paginationTdContainer.classList.add('pagination-buttons-container'); + paginationTdContainer.setAttribute('colspan', '99'); //to use all the width of the table + const paginationTrContainer = document.createElement('tr'); + paginationTrContainer.appendChild(paginationTdContainer); + + // Append the navigation buttons in the table + if (buttonsPosition == 'top') { + tableRef.insertBefore(paginationTrContainer, tableRef.firstChild); + } else { + tableRef.appendChild(paginationTrContainer); + } + } + + //Set de buttons style and others elements of the navigation buttons + function setButtons(buttonsRef, tableState) { + const { currentPage, totalPages, buttonsWindow } = tableState; + + for (let i = 0; i < totalPages; i++) { + + //Set active class to the current page button + buttonsRef[i].classList.toggle('active', i == currentPage); + + //Hidden extra buttons if buttonsWindow if set. Min value for buttonsWindow is 2. + if (buttonsWindow >= 2 && i >= 1 && i < totalPages - 1) { + const leftMargin = currentPage - buttonsWindow; + const rightMargin = currentPage + buttonsWindow; + const leftCompensation = leftMargin < 0 ? leftMargin : 0; + const rightCompensation = rightMargin - totalPages >= 0 ? rightMargin - totalPages + 1 : 0; + + //Hide or reveal buttons base in current page + if ( leftMargin - rightCompensation < i && rightMargin - leftCompensation > i) { + buttonsRef[i].classList.toggle('hidden', false); + } else { + buttonsRef[i].classList.toggle('hidden', true); + } + + //Hide or reveal space dots to limit the total number of buttons in screen + if ((currentPage - rightCompensation == i + buttonsWindow - 1) && i >= buttonsWindow - 1) { + deleteSpaceDots('left'); + drawSpaceDots(buttonsRef, i, 'left') + } else if (currentPage - 1 < buttonsWindow) { + deleteSpaceDots('left'); + } + if ((currentPage - leftCompensation + buttonsWindow - 1 == i) && i <= totalPages - buttonsWindow) { + deleteSpaceDots('right'); + drawSpaceDots(buttonsRef, i, 'right') + } else if (currentPage + 1 >= totalPages - buttonsWindow) { + deleteSpaceDots('right'); + } + } + } + } + + //Draw space dots after or before a certain button + function drawSpaceDots(buttonsRef, i, side) { + const dotsRef = document.createElement('a'); + dotsRef.textContent = " ..... "; + dotsRef.id = side == 'right' ? 'pagination_right_dots' : 'pagination_left_dots'; + buttonsRef[i].insertAdjacentElement(side == 'right' ? 'afterend' : 'beforebegin', dotsRef); + } + + //Delete left or right space dots + function deleteSpaceDots(side) { + const spaceDotsRef = document.getElementById( side == 'right' ? 'pagination_right_dots' : 'pagination_left_dots'); + if (spaceDotsRef) spaceDotsRef.parentNode.removeChild(spaceDotsRef); + } + + //Show and hide elements (rows) of a page + function showPage(tableState) { + const { currentPage, rowsPerPage, rowsRef } = tableState; + const startIndex = currentPage * rowsPerPage; + const endIndex = startIndex + rowsPerPage; + rowsRef.forEach((rowRef, index) => { + rowRef.classList.toggle('hidden', index < startIndex || index >= endIndex); + }); + } +}); \ No newline at end of file diff --git a/style.css b/style.css index 7cb7679..1e821bd 100644 --- a/style.css +++ b/style.css @@ -55,3 +55,12 @@ div.dokuwiki table.pagelist td.page, div.dokuwiki table.ul td.page { font-size: 100%; } + +div.dokuwiki button.pagination-button.active { + font-weight: bold; +} + +td.pagination-buttons-container { + text-align: center; + background: none !important; +} \ No newline at end of file From b58b0ec6309ddc57ae25d102d06c54fe587713e1 Mon Sep 17 00:00:00 2001 From: K-RT-Dev Date: Fri, 20 Sep 2024 18:48:45 -0300 Subject: [PATCH 2/2] default activate --- helper.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helper.php b/helper.php index 3987a9d..4e19c7f 100644 --- a/helper.php +++ b/helper.php @@ -334,11 +334,13 @@ public function setFlags($flags) if ($this->buttonsPosition != 'top' && $this->buttonsPosition != 'bottom') { $this->buttonsPosition = 'bottom'; } + $this->pagination = true; } //**Pagination params. Regulate the max number of navigation show at the same time. Min possible value 2. Use -1 to disable*/ if (substr($flag, 0, 14) == 'buttonswindow=') { $this->buttonsWindow = (int) substr($flag, 14); + $this->pagination = true; } if (isset($this->column[$flag]) && $flag !== 'page') {