mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
feat(ui): Add quick page navigation to the email list pagination #85
This commit is contained in:
@@ -20,6 +20,8 @@
|
|||||||
import {
|
import {
|
||||||
ChevronLeftIcon,
|
ChevronLeftIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
|
DoubleArrowLeftIcon,
|
||||||
|
DoubleArrowRightIcon,
|
||||||
} from '@radix-ui/react-icons'
|
} from '@radix-ui/react-icons'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +32,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { showNumbers } from '@/lib/utils'
|
||||||
|
|
||||||
interface PaginationProps {
|
interface PaginationProps {
|
||||||
totalItems: number
|
totalItems: number
|
||||||
@@ -66,6 +69,9 @@ export function EnvelopeListPagination({
|
|||||||
setPageIndex(newPageIndex)
|
setPageIndex(newPageIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentPage = pageIndex + 1;
|
||||||
|
const pageNumbers = showNumbers(currentPage, pageCount)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-between space-x-2 overflow-auto px-2'>
|
<div className='flex items-center justify-between space-x-2 overflow-auto px-2'>
|
||||||
<div className='hidden flex-1 text-sm text-muted-foreground sm:block'>
|
<div className='hidden flex-1 text-sm text-muted-foreground sm:block'>
|
||||||
@@ -94,6 +100,14 @@ export function EnvelopeListPagination({
|
|||||||
{t("table.page")} {pageIndex + 1} {t("table.of")} {pageCount}
|
{t("table.page")} {pageIndex + 1} {t("table.of")} {pageCount}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center space-x-2'>
|
<div className='flex items-center space-x-2'>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
className='size-8 p-0 @max-md/content:hidden'
|
||||||
|
onClick={() => setPageIndex(0)}
|
||||||
|
disabled={pageIndex === 0}
|
||||||
|
>
|
||||||
|
<DoubleArrowLeftIcon className='h-4 w-4' />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
className='h-8 w-8 p-0'
|
className='h-8 w-8 p-0'
|
||||||
@@ -103,6 +117,22 @@ export function EnvelopeListPagination({
|
|||||||
<span className='sr-only'>{t("table.prevPage")}</span>
|
<span className='sr-only'>{t("table.prevPage")}</span>
|
||||||
<ChevronLeftIcon className='h-4 w-4' />
|
<ChevronLeftIcon className='h-4 w-4' />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{pageNumbers.map((pageNumber, index) => (
|
||||||
|
<div key={`${pageNumber}-${index}`} className='flex items-center'>
|
||||||
|
{pageNumber === '...' ? (
|
||||||
|
<span className='px-1 text-sm text-muted-foreground'>...</span>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant={currentPage === pageNumber ? 'default' : 'outline'}
|
||||||
|
className='h-8 min-w-8 px-2'
|
||||||
|
onClick={() => setPageIndex((pageNumber as number) - 1)}
|
||||||
|
>
|
||||||
|
{pageNumber}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
className='h-8 w-8 p-0'
|
className='h-8 w-8 p-0'
|
||||||
@@ -112,6 +142,14 @@ export function EnvelopeListPagination({
|
|||||||
<span className='sr-only'>{t("table.nextPage")}</span>
|
<span className='sr-only'>{t("table.nextPage")}</span>
|
||||||
<ChevronRightIcon className='h-4 w-4' />
|
<ChevronRightIcon className='h-4 w-4' />
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
className='size-8 p-0 @max-md/content:hidden'
|
||||||
|
onClick={() => setPageIndex(pageCount - 1)}
|
||||||
|
disabled={!hasNextPage()}
|
||||||
|
>
|
||||||
|
<DoubleArrowRightIcon className='h-4 w-4' />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ export function MailList({
|
|||||||
/>
|
/>
|
||||||
<MailIcon className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
<MailIcon className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
||||||
<div className="flex-1 min-w-0 grid grid-cols-1 sm:grid-cols-12 gap-1 sm:gap-0">
|
<div className="flex-1 min-w-0 grid grid-cols-1 sm:grid-cols-12 gap-1 sm:gap-0">
|
||||||
{/* LEFT AREA: From + Subject + Tags */}
|
|
||||||
<div className="col-span-1 sm:col-span-8 flex flex-col min-w-0 gap-1">
|
<div className="col-span-1 sm:col-span-8 flex flex-col min-w-0 gap-1">
|
||||||
<div className="flex items-center gap-1 min-w-0">
|
<div className="flex items-center gap-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate">{item.from}</p>
|
<p className="text-sm font-medium truncate">{item.from}</p>
|
||||||
@@ -154,7 +153,6 @@ export function MailList({
|
|||||||
{item.subject}
|
{item.subject}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* TAGS BELOW SUBJECT */}
|
|
||||||
<div className="flex flex-wrap gap-1 mt-0.25">
|
<div className="flex flex-wrap gap-1 mt-0.25">
|
||||||
{item.tags?.map((tag, i) => (
|
{item.tags?.map((tag, i) => (
|
||||||
<Badge
|
<Badge
|
||||||
@@ -167,7 +165,6 @@ export function MailList({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* RIGHT AREA – actions & meta */}
|
|
||||||
<div className="col-span-1 sm:col-span-4 flex items-center justify-end gap-1 text-xs text-muted-foreground">
|
<div className="col-span-1 sm:col-span-4 flex items-center justify-end gap-1 text-xs text-muted-foreground">
|
||||||
{hasAttachments && (
|
{hasAttachments && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
|||||||
@@ -172,3 +172,35 @@ export const dateFnsLocaleMap: Record<string, Locale> = {
|
|||||||
no: nb,
|
no: nb,
|
||||||
'no-no': nb,
|
'no-no': nb,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function showNumbers(current: number, total: number) {
|
||||||
|
const max = 5
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
if (total <= max) {
|
||||||
|
for (let i = 1; i <= total; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.push(1)
|
||||||
|
if (current <= 3) {
|
||||||
|
for (let i = 2; i <= 4; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
result.push('...', total)
|
||||||
|
} else if (current >= total - 2) {
|
||||||
|
result.push('...')
|
||||||
|
for (let i = total - 3; i <= total; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.push('...')
|
||||||
|
for (let i = current - 1; i <= current + 1; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
result.push('...', total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user