2026-02-16 20:39:33 +02:00

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
};
}