mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Improve product cards/pagination, Farsi locale fonts, and super-admin migrate UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
2e40d5eb4c
commit
f5b2193ba1
@@ -7,6 +7,12 @@
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.paginationInline {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.navBtn {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
@@ -49,11 +55,16 @@
|
||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.pageBtn:hover {
|
||||
.pageBtn:hover:not(:disabled) {
|
||||
background: rgba(var(--primary-rgb) / 0.08);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.pageBtn:disabled {
|
||||
opacity: 0.55;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pageBtn.active {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'
|
||||
import styles from './Pagination.module.css'
|
||||
|
||||
interface PaginationProps {
|
||||
export interface PaginationProps {
|
||||
currentPage: number
|
||||
totalPages: number
|
||||
onPageChange: (page: number) => void
|
||||
ariaLabel?: string
|
||||
/** Pages on each side of current. Default 3 → e.g. 1 2 3 [4] 5 6 7 */
|
||||
siblingCount?: number
|
||||
disabled?: boolean
|
||||
/** `inline` for table footers (no top margin). */
|
||||
variant?: 'default' | 'inline'
|
||||
className?: string
|
||||
}
|
||||
|
||||
function buildPageWindow(
|
||||
currentPage: number,
|
||||
totalPages: number,
|
||||
siblingCount: number,
|
||||
): number[] {
|
||||
const start = Math.max(1, currentPage - siblingCount)
|
||||
const end = Math.min(totalPages, currentPage + siblingCount)
|
||||
const pages: number[] = []
|
||||
for (let page = start; page <= end; page += 1) {
|
||||
pages.push(page)
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
export function Pagination({
|
||||
@@ -13,18 +33,39 @@ export function Pagination({
|
||||
totalPages,
|
||||
onPageChange,
|
||||
ariaLabel = 'Pagination',
|
||||
siblingCount = 3,
|
||||
disabled = false,
|
||||
variant = 'default',
|
||||
className,
|
||||
}: PaginationProps) {
|
||||
if (totalPages <= 1) return null
|
||||
|
||||
const pages = Array.from({ length: totalPages }, (_, i) => i + 1)
|
||||
const pages = buildPageWindow(currentPage, totalPages, siblingCount)
|
||||
const rootClass = [
|
||||
styles.pagination,
|
||||
variant === 'inline' ? styles.paginationInline : '',
|
||||
className ?? '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
return (
|
||||
<nav className={styles.pagination} aria-label={ariaLabel}>
|
||||
<nav className={rootClass} aria-label={ariaLabel}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.navBtn}
|
||||
onClick={() => onPageChange(1)}
|
||||
disabled={disabled || currentPage === 1}
|
||||
aria-label="First page"
|
||||
>
|
||||
<ChevronsLeft size={18} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={styles.navBtn}
|
||||
onClick={() => onPageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
disabled={disabled || currentPage === 1}
|
||||
aria-label="Previous page"
|
||||
>
|
||||
<ChevronLeft size={18} />
|
||||
@@ -37,6 +78,7 @@ export function Pagination({
|
||||
type="button"
|
||||
className={`${styles.pageBtn} ${page === currentPage ? styles.active : ''}`}
|
||||
onClick={() => onPageChange(page)}
|
||||
disabled={disabled}
|
||||
aria-label={`Page ${page}`}
|
||||
aria-current={page === currentPage ? 'page' : undefined}
|
||||
>
|
||||
@@ -49,11 +91,21 @@ export function Pagination({
|
||||
type="button"
|
||||
className={styles.navBtn}
|
||||
onClick={() => onPageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
disabled={disabled || currentPage === totalPages}
|
||||
aria-label="Next page"
|
||||
>
|
||||
<ChevronRight size={18} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={styles.navBtn}
|
||||
onClick={() => onPageChange(totalPages)}
|
||||
disabled={disabled || currentPage === totalPages}
|
||||
aria-label="Last page"
|
||||
>
|
||||
<ChevronsRight size={18} />
|
||||
</button>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, type FormEvent } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { X } from 'lucide-react'
|
||||
import styles from './PasswordResetModal.module.css'
|
||||
|
||||
@@ -153,7 +154,7 @@ export function PasswordResetModal({
|
||||
|
||||
if (!mounted) return null
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<div
|
||||
className={`${styles.overlay} ${closing ? styles.overlayOut : styles.overlayIn}`}
|
||||
onClick={onClose}
|
||||
@@ -278,6 +279,7 @@ export function PasswordResetModal({
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export {
|
||||
} from './components/AddressListEditor'
|
||||
export { Breadcrumbs, type BreadcrumbItem } from './components/Breadcrumbs'
|
||||
export { Pagination } from './components/Pagination'
|
||||
export type { PaginationProps } from './components/Pagination'
|
||||
export { SectionCard } from './components/SectionCard'
|
||||
export { RouteLoader } from './components/RouteLoader'
|
||||
export { createDomainGuard, type DomainGuardConfig } from './components/createDomainGuard'
|
||||
|
||||
Reference in New Issue
Block a user