This repository has been archived by the owner on Feb 21, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is done on the backend (completely by hand, unfortunately), and handled by a dedicated js object handling the front-end part of the pagination. - API fixes page argument, if out of range. - comparison_id and page in URL control page behaviour.
- Loading branch information
1 parent
f68d049
commit db2d7d7
Showing
5 changed files
with
685 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
/* | ||
Pagination helper for splitting array-like data into pages | ||
and showing a page index to browse it. | ||
This implementation assumes that all the data is available at | ||
instantiation, and then split into pages. | ||
*/ | ||
function pagination_helper() { | ||
let _current_page = 1; | ||
let _data = {} | ||
let _num_total_items = 1; | ||
let _dom_element = ''; // The DOM element where the data will be put in | ||
let _items_per_page = 1; | ||
let _total_pages = 1; | ||
let _item_creation_callback; | ||
let _is_initialized = false; | ||
const NUM_PAGES_SHOW_ELLIPSIS = 2 | ||
|
||
|
||
function _calculate_num_pages() { | ||
_total_pages = Math.ceil(_num_total_items / _items_per_page); | ||
} | ||
|
||
/* Helper function for adding CSS classes to a paginator page button */ | ||
function _add_bootstrap_classes_to_page_li(el) { | ||
el.classList.add('page-item') | ||
el.firstChild.classList.add('page-link') | ||
return el | ||
} | ||
|
||
/* Callback to run when clicking the next page button */ | ||
function _goto_next_page() { | ||
if (_current_page + 1 > _total_pages) { | ||
_current_page = _total_pages; | ||
} | ||
else { | ||
_current_page += 1; | ||
} | ||
_goto_specific_page(_current_page) | ||
} | ||
|
||
/* Callback to run when clicking the previous page button */ | ||
function _goto_prev_page() { | ||
if (_current_page - 1 < 1) { | ||
_current_page = 1; | ||
} | ||
else { | ||
_current_page -= 1; | ||
} | ||
_goto_specific_page(_current_page) | ||
} | ||
|
||
/* To be run on any page change */ | ||
function _on_page_change() { | ||
_clear_paginator() | ||
_create_paginator_buttons() | ||
_clear_page_data() | ||
_fill_page_data() | ||
} | ||
/* Callback to run when clicking on a page button */ | ||
function _goto_specific_page(page_num) { | ||
if (page_num <= _total_pages && page_num >= 1) { | ||
_current_page = page_num; | ||
_on_page_change() | ||
} | ||
} | ||
|
||
/* | ||
Function that crates a paginator button for a single page. | ||
content: can be either a DOM Element (in which case it is appended directly | ||
into the button), or a text/number, in which case it replaces the innerHTML. | ||
additional classes: a string of CSS classes to be added to the button (e.g., "class1 class2") | ||
onclick: the function to run when the button is clicked. It will be passed the | ||
DOM Element as an argument. | ||
title: the string to display on button mouse hover. | ||
*/ | ||
function _create_page_el(content, additional_classes, onclick, title) { | ||
let li = document.createElement('li') | ||
if (additional_classes) { | ||
li.classList.add(additional_classes) | ||
} | ||
if (title) { | ||
li.setAttribute('title', title) | ||
} | ||
let a = document.createElement('a') | ||
a.setAttribute.href = '#' | ||
if (content instanceof Element) { | ||
a.appendChild(content) | ||
} else if (typeof content === 'string' || typeof content === 'number') { | ||
a.innerHTML = content | ||
if (!title) { | ||
li.setAttribute('title', `Go to page ${content}`) | ||
} | ||
} | ||
if (typeof onclick === 'function') { | ||
li.onclick = onclick | ||
} | ||
li.appendChild(a) | ||
return _add_bootstrap_classes_to_page_li(li) | ||
} | ||
|
||
function _create_first_page_el() { | ||
let i = document.createElement('i') | ||
i.classList.add('bi', 'bi-chevron-double-left') | ||
|
||
return _create_page_el(i, _current_page === 1 ? "disabled" : "", | ||
() => { _goto_specific_page(1) }, "First page") | ||
} | ||
|
||
function _create_previous_page_el() { | ||
let i = document.createElement('i') | ||
i.classList.add('bi', 'bi-chevron-left') | ||
return _create_page_el(i, _current_page === 1 ? "disabled" : "", | ||
_goto_prev_page, "Previous page") | ||
} | ||
|
||
function _create_last_page_el() { | ||
let i = document.createElement('i') | ||
i.classList.add('bi', 'bi-chevron-double-right') | ||
return _create_page_el(i, (_current_page === _total_pages || _total_pages === 0) ? | ||
"disabled" : "", | ||
() => { _goto_specific_page(_total_pages) }, | ||
'Last page') | ||
} | ||
|
||
function _create_next_page_el() { | ||
let i = document.createElement('i') | ||
i.classList.add('bi', 'bi-chevron-right') | ||
return _create_page_el(i, (_current_page === _total_pages || _total_pages === 0) ? | ||
"disabled" : "", | ||
_goto_next_page, | ||
"Next page") | ||
} | ||
|
||
/* Create a single page button for a specific page */ | ||
function _create_page_number_el(page_num) { | ||
return _create_page_el( | ||
String(page_num), | ||
_current_page === page_num ? "active" : "", | ||
() => { _goto_specific_page(page_num) } | ||
) | ||
} | ||
|
||
/* Get the DOM element id where page buttons will be added */ | ||
function _get_pagination_el_id() { | ||
return `${_dom_element}_pagination` | ||
} | ||
|
||
/* Clear the pagination buttons completely */ | ||
function _clear_paginator() { | ||
let pagination_el = document.getElementById(_get_pagination_el_id()) | ||
if (pagination_el) { | ||
pagination_el.innerHTML = '' | ||
} | ||
} | ||
|
||
/* | ||
Create the pagination element under the same parent as the | ||
_dom_element passed at initialization. | ||
*/ | ||
function _create_pagination_el() { | ||
let base_el = document.getElementById(_dom_element).parentElement; | ||
let pagination_el = document.createElement('nav'); | ||
let ul_el = document.createElement('ul'); | ||
ul_el.classList.add('pagination', 'mt-2') | ||
ul_el.setAttribute('id', _get_pagination_el_id()) | ||
pagination_el.appendChild(ul_el) | ||
base_el.appendChild(pagination_el); | ||
} | ||
|
||
/* | ||
Create the page links inside the pagination DOM element. | ||
*/ | ||
function _create_paginator_buttons() { | ||
let pagination_el = document.getElementById(_get_pagination_el_id()) | ||
pagination_el.appendChild(_create_first_page_el()) | ||
pagination_el.appendChild(_create_previous_page_el()) | ||
if (_total_pages < 3) { | ||
for (i = 1; i <= _total_pages; i++) { | ||
pagination_el.appendChild(_create_page_number_el(i)) | ||
} | ||
} | ||
else { | ||
if (_current_page - 1 <= NUM_PAGES_SHOW_ELLIPSIS) { | ||
for (let i = 1; i <= _current_page - 1; i++) { | ||
pagination_el.appendChild(_create_page_number_el(i)) | ||
} | ||
} | ||
else { | ||
pagination_el.appendChild(_create_page_el('...', 'disabled')) | ||
pagination_el.appendChild(_create_page_number_el(_current_page - 1)) | ||
} | ||
|
||
pagination_el.appendChild(_create_page_number_el(_current_page)) | ||
|
||
if (_total_pages - _current_page <= NUM_PAGES_SHOW_ELLIPSIS) { | ||
for (let i = _current_page + 1; i <= _total_pages; i++) { | ||
pagination_el.appendChild(_create_page_number_el(i)) | ||
} | ||
} | ||
else { | ||
pagination_el.appendChild(_create_page_number_el(_current_page + 1)) | ||
pagination_el.appendChild(_create_page_el('...', 'disabled')) | ||
|
||
} | ||
} | ||
pagination_el.appendChild(_create_next_page_el()) | ||
pagination_el.appendChild(_create_last_page_el()) | ||
} | ||
|
||
// Create a single li element for a single comparison report. | ||
function _create_result_list_element(data) { | ||
let li = document.createElement("li"); | ||
|
||
if (typeof _item_creation_callback === 'function') { | ||
li = _item_creation_callback(li, data) | ||
} | ||
return li; | ||
} | ||
|
||
function _clear_page_data() { | ||
let data_el = document.getElementById(_dom_element) | ||
data_el.innerHTML = '' | ||
} | ||
function _fill_page_data() { | ||
let data_el = document.getElementById(_dom_element) | ||
let index_start = (_current_page - 1) * _items_per_page | ||
let page_data = _data.slice(index_start, index_start + _items_per_page) | ||
page_data.forEach(element => { | ||
data_el.appendChild(_create_result_list_element(element)) | ||
}); | ||
} | ||
function _paginate(data, dom_element, item_creation_callback, items_per_page) { | ||
if (typeof data !== 'object' || !data.hasOwnProperty('length')) { | ||
throw Error(`data passed should be array-like, cannot initialize pagination`) | ||
} | ||
_data = data; | ||
_num_total_items = data.length; | ||
if (typeof dom_element !== 'string' || !document.getElementById(dom_element)) { | ||
throw Error(`Could not find DOM element "${dom_element}" to attach pagination to`) | ||
} | ||
_dom_element = dom_element; | ||
|
||
_items_per_page = Number(items_per_page) | ||
if (isNaN(_items_per_page)) { | ||
throw Error(`items_per_page should be a number, received ${typeof items_per_page}: ${items_per_page}`) | ||
} | ||
|
||
if (typeof item_creation_callback === 'function') { | ||
_item_creation_callback = item_creation_callback | ||
} | ||
_calculate_num_pages() | ||
_create_pagination_el() | ||
_on_page_change() | ||
} | ||
/* "Public" methods are returned as an object with functions */ | ||
return { | ||
paginate: (data, dom_element, item_creation_callback, items_per_page = _items_per_page) => { | ||
_paginate(data, dom_element, item_creation_callback, items_per_page) | ||
_is_initialized = true | ||
}, | ||
data: (data) => { | ||
if (!_is_initialized) { | ||
throw Error(`Paginator has not been initialized!`) | ||
} | ||
_paginate(data, _dom_element, _item_creation_callback, _items_per_page) | ||
if (_current_page > _total_pages) { | ||
_current_page = _total_pages | ||
} | ||
_on_page_change() | ||
} | ||
} | ||
}; |
Oops, something went wrong.