mirror of
https://github.com/zadam/trilium.git
synced 2026-02-19 20:24:28 +01:00
134 lines
4.7 KiB
TypeScript
134 lines
4.7 KiB
TypeScript
import { ComponentChildren } from "preact";
|
|
import { Dispatch, StateUpdater, useEffect, useState } from "preact/hooks";
|
|
import FNote from "../../entities/fnote";
|
|
import froca from "../../services/froca";
|
|
import { useNoteLabelInt } from "../react/hooks";
|
|
import { t } from "../../services/i18n";
|
|
import ActionButton from "../react/ActionButton";
|
|
import Button from "../react/Button";
|
|
import "./Pagination.css";
|
|
import clsx from "clsx";
|
|
|
|
interface PaginationContext {
|
|
page: number;
|
|
setPage: Dispatch<StateUpdater<number>>;
|
|
pageNotes?: FNote[];
|
|
pageCount: number;
|
|
pageSize: number;
|
|
totalNotes: number;
|
|
}
|
|
|
|
export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit<PaginationContext, "pageNotes">) {
|
|
if (pageCount < 2) return;
|
|
|
|
return (
|
|
<div class="note-list-pager">
|
|
<ActionButton
|
|
icon="bx bx-chevron-left"
|
|
disabled={(page === 1)}
|
|
text={t("pagination.prev_page")}
|
|
onClick={() => {setPage(page - 1)}}
|
|
/>
|
|
|
|
<PageButtons page={page} setPage={setPage} pageCount={pageCount} />
|
|
|
|
<ActionButton
|
|
icon="bx bx-chevron-right"
|
|
disabled={(page === pageCount)}
|
|
text={t("pagination.next_page")}
|
|
onClick={() => {setPage(page + 1)}}
|
|
/>
|
|
<span className="note-list-pager-total-count">({t("pagination.total_notes", { count: totalNotes })})</span>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
interface PageButtonsProps {
|
|
page: number;
|
|
setPage: Dispatch<StateUpdater<number>>;
|
|
pageCount: number;
|
|
}
|
|
|
|
function PageButtons(props: PageButtonsProps) {
|
|
const maxButtonCount = 9;
|
|
const maxLeftRightSegmentLength = 2;
|
|
|
|
// The left-side segment
|
|
const leftLength = Math.min(props.pageCount, maxLeftRightSegmentLength);
|
|
const leftStart = 1;
|
|
|
|
// The middle segment
|
|
const middleMaxLength = maxButtonCount - maxLeftRightSegmentLength * 2;
|
|
const middleLength = Math.min(props.pageCount - leftLength, middleMaxLength);
|
|
let middleStart = props.page - Math.floor(middleLength / 2);
|
|
middleStart = Math.max(middleStart, leftLength + 1);
|
|
|
|
// The right-side segment
|
|
const rightLength = Math.min(props.pageCount - (middleLength + leftLength), maxLeftRightSegmentLength);
|
|
const rightStart = props.pageCount - rightLength + 1;
|
|
middleStart = Math.min(middleStart, rightStart - middleLength);
|
|
|
|
return <div class="note-list-pager-page-button-container"
|
|
style={{"--note-list-pager-page-button-count": leftLength + middleLength + rightLength}}>
|
|
{[
|
|
...createSegment(leftStart, leftLength, props.page, props.setPage, false),
|
|
...createSegment(middleStart, middleLength, props.page, props.setPage, (middleStart - leftLength > 1)),
|
|
...createSegment(rightStart, rightLength, props.page, props.setPage, (rightStart - (middleStart + middleLength - 1) > 1)),
|
|
]}
|
|
</div>;
|
|
}
|
|
|
|
function createSegment(start: number, length: number, currentPage: number, setPage: Dispatch<StateUpdater<number>>, prependEllipsis: boolean): ComponentChildren[] {
|
|
const children: ComponentChildren[] = [];
|
|
|
|
if (prependEllipsis) {
|
|
children.push(<span class="note-list-pager-ellipsis">...</span>);
|
|
}
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
const pageNum = start + i;
|
|
const isCurrent = (pageNum === currentPage);
|
|
children.push((
|
|
<Button
|
|
text={pageNum.toString()}
|
|
kind="lowProfile"
|
|
className={clsx(
|
|
"note-list-pager-page-button",
|
|
{"note-list-pager-page-button-current": isCurrent}
|
|
)}
|
|
disabled={isCurrent}
|
|
onClick={() => setPage(pageNum)}
|
|
/>
|
|
));
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
export function usePagination(note: FNote, noteIds: string[]): PaginationContext {
|
|
const [ page, setPage ] = useState(1);
|
|
const [ pageNotes, setPageNotes ] = useState<FNote[]>();
|
|
|
|
// Parse page size.
|
|
const [ pageSize ] = useNoteLabelInt(note, "pageSize");
|
|
const normalizedPageSize = (pageSize && pageSize > 0 ? pageSize : 20);
|
|
|
|
// Calculate start/end index.
|
|
const startIdx = (page - 1) * normalizedPageSize;
|
|
const endIdx = startIdx + normalizedPageSize;
|
|
const pageCount = Math.ceil(noteIds.length / normalizedPageSize);
|
|
|
|
// Obtain notes within the range.
|
|
const pageNoteIds = noteIds.slice(startIdx, Math.min(endIdx, noteIds.length));
|
|
|
|
useEffect(() => {
|
|
froca.getNotes(pageNoteIds).then(setPageNotes);
|
|
}, [ note, noteIds, page, pageSize ]);
|
|
|
|
return {
|
|
page, setPage, pageNotes, pageCount,
|
|
pageSize: normalizedPageSize,
|
|
totalNotes: noteIds.length
|
|
};
|
|
}
|