mirror of
https://github.com/flarum/framework.git
synced 2024-11-22 12:31:41 +08:00
feat: improved page navigation for users list (#3741)
* feat: add first and last page buttons * feat: add textbox-based page navigation * feat: add query parameter page navigation
This commit is contained in:
parent
0da069ba9f
commit
408a92b4ea
|
@ -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';
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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">
|
||||
|
@ -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')}
|
||||
|
@ -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>
|
||||
|
@ -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>,
|
||||
];
|
||||
}
|
||||
|
@ -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 },
|
||||
|
@ -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();
|
||||
})
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user