Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved page navigation for users list #3741

Merged
merged 3 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 99 additions & 6 deletions framework/core/js/src/admin/components/UserListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import icon from '../../common/helpers/icon';
import listItems from '../../common/helpers/listItems';

import type User from '../../common/models/User';
import type { IPageAttrs } from '../../common/components/Page';

import ItemList from '../../common/utils/ItemList';
import classList from '../../common/utils/classList';
Expand Down Expand Up @@ -45,6 +46,11 @@ export default class UserListPage extends AdminPage {
*/
private pageNumber: number = 0;

/**
* Page number being loaded. Zero-indexed.
*/
private loadingPageNumber: number = 0;

/**
* Total number of forum users.
*
Expand Down Expand Up @@ -77,12 +83,28 @@ export default class UserListPage extends AdminPage {

private isLoadingPage: boolean = false;

oninit(vnode: Mithril.Vnode<IPageAttrs, this>) {
super.oninit(vnode);

// Get page query value from URL
const page = parseInt(m.route.param('page'));

if (isNaN(page) || page < 1) {
this.setPageNumberInUrl(1);
this.pageNumber = 0;
} else {
this.pageNumber = page - 1;
}

this.loadingPageNumber = this.pageNumber;
}

/**
* Component to render.
*/
content() {
if (typeof this.pageData === 'undefined') {
this.loadPage(0);
this.loadPage(this.pageNumber);

return [
<section class="UserListPage-grid UserListPage-grid--loading">
Expand Down Expand Up @@ -149,6 +171,13 @@ export default class UserListPage extends AdminPage {
{this.isLoadingPage && <LoadingIndicator size="large" />}
</section>,
<nav class="UserListPage-gridPagination">
<Button
disabled={this.pageNumber === 0}
title={app.translator.trans('core.admin.users.pagination.first_page_button')}
onclick={this.goToPage.bind(this, 1)}
icon="fas fa-step-backward"
className="Button Button--icon UserListPage-firstPageBtn"
/>
<Button
disabled={this.pageNumber === 0}
title={app.translator.trans('core.admin.users.pagination.back_button')}
Expand All @@ -158,7 +187,38 @@ export default class UserListPage extends AdminPage {
/>
<span class="UserListPage-pageNumber">
{app.translator.trans('core.admin.users.pagination.page_counter', {
current: this.pageNumber + 1,
current: (
<input
type="text"
value={this.loadingPageNumber + 1}
aria-label={extractText(app.translator.trans('core.admin.users.pagination.go_to_page_textbox_a11y_label'))}
autocomplete="off"
className="FormControl UserListPage-pageNumberInput"
onchange={(e: InputEvent) => {
const target = e.target as HTMLInputElement;
let pageNumber = parseInt(target.value);

if (isNaN(pageNumber)) {
// Invalid value, reset to current page
target.value = (this.pageNumber + 1).toString();
return;
}

if (pageNumber < 1) {
// Lower constraint
pageNumber = 1;
} else if (pageNumber > this.getTotalPageCount()) {
// Upper constraint
pageNumber = this.getTotalPageCount();
}

target.value = pageNumber.toString();

this.goToPage(pageNumber);
}}
/>
),
currentNum: this.pageNumber + 1,
total: this.getTotalPageCount(),
})}
</span>
Expand All @@ -169,6 +229,13 @@ export default class UserListPage extends AdminPage {
icon="fas fa-chevron-right"
className="Button Button--icon UserListPage-nextBtn"
/>
<Button
disabled={!this.moreData}
title={app.translator.trans('core.admin.users.pagination.last_page_button')}
onclick={this.goToPage.bind(this, this.getTotalPageCount())}
icon="fas fa-step-forward"
className="Button Button--icon UserListPage-lastPageBtn"
/>
</nav>,
];
}
Expand Down Expand Up @@ -347,11 +414,14 @@ export default class UserListPage extends AdminPage {
*
* Uses the `this.numPerPage` as the response limit, and automatically calculates the offset required from `pageNumber`.
*
* @param pageNumber The page number to load and display
* @param pageNumber The **zero-based** page number to load and display
*/
async loadPage(pageNumber: number) {
if (pageNumber < 0) pageNumber = 0;

this.loadingPageNumber = pageNumber;
this.setPageNumberInUrl(pageNumber + 1);

app.store
.find<User[]>('users', {
filter: { q: this.query },
Expand All @@ -369,9 +439,16 @@ export default class UserListPage extends AdminPage {
// @ts-ignore
delete data.payload;

this.pageData = data;
this.pageNumber = pageNumber;
this.isLoadingPage = false;
const lastPage = this.getTotalPageCount();

if (pageNumber > lastPage) {
this.loadPage(lastPage - 1);
} else {
this.pageData = data;
this.pageNumber = pageNumber;
this.loadingPageNumber = pageNumber;
this.isLoadingPage = false;
}

m.redraw();
})
Expand All @@ -390,4 +467,20 @@ export default class UserListPage extends AdminPage {
this.isLoadingPage = true;
this.loadPage(this.pageNumber - 1);
}

/**
* @param page The **1-based** page number
*/
goToPage(page: number) {
this.isLoadingPage = true;
this.loadPage(page - 1);
}

private setPageNumberInUrl(pageNumber: number) {
const search = window.location.hash.split('?', 2);
const params = new URLSearchParams(search?.[1] ?? '');

params.set('page', `${pageNumber}`);
window.location.hash = search?.[0] + '?' + params.toString();
}
}
17 changes: 15 additions & 2 deletions framework/core/less/admin/UsersListPage.less
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,24 @@
}

&-gridPagination {
display: flex;
display: grid;
grid-template-columns: auto auto 1fr auto auto;
gap: 8px;
align-items: center;
justify-content: space-between;
justify-content: center;
margin-top: 16px;
}

&-pageNumber {
text-align: center;
}

&-pageNumberInput {
display: inline-block;
margin: 0 8px;
width: auto;
max-width: 80px;
}
}

// Handles styling of default UserList columns
Expand Down
3 changes: 3 additions & 0 deletions framework/core/locale/core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ core:

pagination:
back_button: Previous page
first_button: Go to first page
go_to_page_textbox_a11y_label: Go directly to page number
last_button: Go to last page
next_button: Next page
page_counter: Page {current} of {total}

Expand Down