add paginations
This commit is contained in:
parent
f3c1990af4
commit
3c1574a393
411
dist/index.js
vendored
411
dist/index.js
vendored
@ -1,6 +1,407 @@
|
|||||||
const e = (r) => new Intl.NumberFormat("fa-IR").format(Number(r)), i = (r) => String(r).replace(/\d/g, (t) => "۰۱۲۳۴۵۶۷۸۹"[Number(t)]), o = (r) => String(r).replace(/[۰-۹]/g, (n) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(n)));
|
import { jsxs as I, jsx as o } from "react/jsx-runtime";
|
||||||
export {
|
import { useState as y, useRef as M, useEffect as A, useCallback as _ } from "react";
|
||||||
o as toEnDigits,
|
import { HStack as B, IconButton as N, Button as S, Input as E, Text as v } from "@chakra-ui/react";
|
||||||
i as toFaDigits,
|
import { IoChevronForwardOutline as z, IoChevronBackOutline as V } from "react-icons/io5";
|
||||||
e as toFaNumber
|
const K = (i) => new Intl.NumberFormat("fa-IR").format(Number(i)), t = (i) => String(i).replace(/\d/g, (n) => "۰۱۲۳۴۵۶۷۸۹"[Number(n)]), D = (i) => String(i).replace(/[۰-۹]/g, (C) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(C))), O = ({
|
||||||
|
currentPage: i,
|
||||||
|
totalPages: n,
|
||||||
|
totalCount: C,
|
||||||
|
onPageChange: d
|
||||||
|
}) => {
|
||||||
|
const [c, l] = y(t(i)), [a, f] = y(!1), [b, s] = y(null), x = M(null), w = [.../* @__PURE__ */ new Set([1, i, n])].filter(
|
||||||
|
(e) => e >= 1 && e <= n
|
||||||
|
);
|
||||||
|
A(() => {
|
||||||
|
if (b !== null) {
|
||||||
|
i === b && (s(null), f(!1), l(t(i)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a || l(t(i));
|
||||||
|
}, [i, a, b]), A(() => {
|
||||||
|
if (!a || !c || b !== null) return;
|
||||||
|
const e = parseInt(D(c), 10);
|
||||||
|
if (isNaN(e)) return;
|
||||||
|
const u = setTimeout(() => {
|
||||||
|
var m;
|
||||||
|
const r = Math.min(Math.max(e, 1), n);
|
||||||
|
r !== i ? (s(r), l(t(r)), d(r)) : (f(!1), l(t(i))), (m = x.current) == null || m.blur();
|
||||||
|
}, 1e3);
|
||||||
|
return () => clearTimeout(u);
|
||||||
|
}, [
|
||||||
|
c,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
n,
|
||||||
|
i,
|
||||||
|
d
|
||||||
|
]);
|
||||||
|
const k = () => {
|
||||||
|
var r, m;
|
||||||
|
const e = parseInt(D(c), 10);
|
||||||
|
if (isNaN(e)) {
|
||||||
|
l(t(i)), f(!1), s(null), (r = x.current) == null || r.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const u = Math.min(Math.max(e, 1), n);
|
||||||
|
u !== i ? (s(u), l(t(u)), d(u)) : (l(t(i)), f(!1), s(null)), (m = x.current) == null || m.blur();
|
||||||
|
};
|
||||||
|
return n === 0 || !C ? null : /* @__PURE__ */ I(
|
||||||
|
B,
|
||||||
|
{
|
||||||
|
justifyContent: "space-around",
|
||||||
|
backgroundColor: "white",
|
||||||
|
py: "10px",
|
||||||
|
px: "16px",
|
||||||
|
rounded: "lg",
|
||||||
|
border: "1px",
|
||||||
|
borderColor: "blue.200",
|
||||||
|
w: "100%",
|
||||||
|
children: [
|
||||||
|
/* @__PURE__ */ I(B, { spacing: 2, alignItems: "center", justifyContent: "center", children: [
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => d(i - 1),
|
||||||
|
isDisabled: i === 1,
|
||||||
|
icon: /* @__PURE__ */ o(z, {}),
|
||||||
|
"aria-label": "صفحه قبلی"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
w.map((e) => /* @__PURE__ */ o(
|
||||||
|
S,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
px: 2,
|
||||||
|
bg: i === e ? "#4B49AC" : "#e6e6f1",
|
||||||
|
color: i === e ? "#e6e6f1" : "#4B49AC",
|
||||||
|
_hover: { bg: i === e ? "#4B49AC" : "#cbcbe8" },
|
||||||
|
onClick: () => d(e),
|
||||||
|
variant: "soft",
|
||||||
|
children: t(e)
|
||||||
|
},
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => d(i + 1),
|
||||||
|
isDisabled: i === n,
|
||||||
|
icon: /* @__PURE__ */ o(V, {}),
|
||||||
|
"aria-label": "صفحه بعدی"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
E,
|
||||||
|
{
|
||||||
|
ref: x,
|
||||||
|
type: "text",
|
||||||
|
inputMode: "numeric",
|
||||||
|
placeholder: "صفحه",
|
||||||
|
w: "56px",
|
||||||
|
value: c,
|
||||||
|
onFocus: () => {
|
||||||
|
f(!0), s(null), l("");
|
||||||
|
},
|
||||||
|
onChange: (e) => {
|
||||||
|
const u = e.target.value, r = D(u).replace(/\D/g, "");
|
||||||
|
l(t(r));
|
||||||
|
},
|
||||||
|
onBlur: () => {
|
||||||
|
c || (l(t(i)), f(!1), s(null));
|
||||||
|
},
|
||||||
|
onKeyDown: (e) => {
|
||||||
|
e.key === "Enter" && k();
|
||||||
|
},
|
||||||
|
textAlign: "center",
|
||||||
|
color: "#1D2939",
|
||||||
|
px: 1,
|
||||||
|
minW: 0,
|
||||||
|
dir: "ltr"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
] }),
|
||||||
|
/* @__PURE__ */ I(B, { spacing: 2, children: [
|
||||||
|
/* @__PURE__ */ o(v, { w: "auto", fontWeight: "bold", whiteSpace: "nowrap", children: "تعداد کل:" }),
|
||||||
|
/* @__PURE__ */ o(v, { children: t(C) })
|
||||||
|
] })
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, L = ({
|
||||||
|
currentPage: i,
|
||||||
|
totalPages: n,
|
||||||
|
totalCount: C,
|
||||||
|
onPageChange: d
|
||||||
|
}) => {
|
||||||
|
const [c, l] = y(t(i)), [a, f] = y(!1), [b, s] = y(null), x = M(null), w = Array.from(
|
||||||
|
/* @__PURE__ */ new Set([1, i, i + 1, n])
|
||||||
|
).filter((e) => e >= 1 && e <= n).sort((e, u) => e - u);
|
||||||
|
A(() => {
|
||||||
|
if (b !== null) {
|
||||||
|
i === b && (s(null), f(!1), l(t(i)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a || l(t(i));
|
||||||
|
}, [i, a, b]), A(() => {
|
||||||
|
if (!a || !c || b !== null) return;
|
||||||
|
const e = parseInt(D(c), 10);
|
||||||
|
if (isNaN(e)) return;
|
||||||
|
const u = setTimeout(() => {
|
||||||
|
var m;
|
||||||
|
const r = Math.min(Math.max(e, 1), n);
|
||||||
|
r !== i ? (s(r), l(t(r)), d(r)) : (f(!1), l(t(i))), (m = x.current) == null || m.blur();
|
||||||
|
}, 1e3);
|
||||||
|
return () => clearTimeout(u);
|
||||||
|
}, [
|
||||||
|
c,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
n,
|
||||||
|
i,
|
||||||
|
d
|
||||||
|
]);
|
||||||
|
const k = () => {
|
||||||
|
var r, m;
|
||||||
|
const e = parseInt(D(c), 10);
|
||||||
|
if (isNaN(e)) {
|
||||||
|
l(t(i)), f(!1), s(null), (r = x.current) == null || r.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const u = Math.min(Math.max(e, 1), n);
|
||||||
|
u !== i ? (s(u), l(t(u)), d(u)) : (l(t(i)), f(!1), s(null)), (m = x.current) == null || m.blur();
|
||||||
|
};
|
||||||
|
return n === 0 || !C ? null : /* @__PURE__ */ I(
|
||||||
|
B,
|
||||||
|
{
|
||||||
|
justifyContent: "space-around",
|
||||||
|
backgroundColor: "white",
|
||||||
|
py: "10px",
|
||||||
|
px: "16px",
|
||||||
|
rounded: "lg",
|
||||||
|
border: "1px",
|
||||||
|
borderColor: "blue.200",
|
||||||
|
w: "100%",
|
||||||
|
children: [
|
||||||
|
/* @__PURE__ */ I(B, { spacing: 2, whiteSpace: "nowrap", children: [
|
||||||
|
/* @__PURE__ */ o(v, { fontWeight: "bold", children: "صفحه" }),
|
||||||
|
/* @__PURE__ */ o(v, { children: t(i) }),
|
||||||
|
/* @__PURE__ */ o(v, { fontWeight: "bold", children: "از" }),
|
||||||
|
/* @__PURE__ */ o(v, { children: t(n) })
|
||||||
|
] }),
|
||||||
|
/* @__PURE__ */ I(B, { spacing: 2, alignItems: "center", justifyContent: "center", children: [
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => d(i - 1),
|
||||||
|
isDisabled: i === 1,
|
||||||
|
icon: /* @__PURE__ */ o(z, {}),
|
||||||
|
"aria-label": "صفحه قبلی"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
w.map((e) => /* @__PURE__ */ o(
|
||||||
|
S,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: i === e ? "#4B49AC" : "#e6e6f1",
|
||||||
|
color: i === e ? "#e6e6f1" : "#4B49AC",
|
||||||
|
_hover: { bg: i === e ? "#4B49AC" : "#cbcbe8" },
|
||||||
|
onClick: () => d(e),
|
||||||
|
variant: "soft",
|
||||||
|
children: t(e)
|
||||||
|
},
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => d(i + 1),
|
||||||
|
isDisabled: i === n,
|
||||||
|
icon: /* @__PURE__ */ o(V, {}),
|
||||||
|
"aria-label": "صفحه بعدی"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
E,
|
||||||
|
{
|
||||||
|
ref: x,
|
||||||
|
type: "text",
|
||||||
|
inputMode: "numeric",
|
||||||
|
placeholder: "صفحه",
|
||||||
|
w: "100px",
|
||||||
|
value: c,
|
||||||
|
onFocus: () => {
|
||||||
|
f(!0), s(null), l("");
|
||||||
|
},
|
||||||
|
onChange: (e) => {
|
||||||
|
const u = e.target.value, r = D(u).replace(/\D/g, "");
|
||||||
|
l(t(r));
|
||||||
|
},
|
||||||
|
onBlur: () => {
|
||||||
|
c || (l(t(i)), f(!1), s(null));
|
||||||
|
},
|
||||||
|
onKeyDown: (e) => {
|
||||||
|
e.key === "Enter" && k();
|
||||||
|
},
|
||||||
|
textAlign: "center",
|
||||||
|
color: "#1D2939",
|
||||||
|
px: 1,
|
||||||
|
minW: 0,
|
||||||
|
dir: "ltr"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
] }),
|
||||||
|
/* @__PURE__ */ I(B, { spacing: 2, whiteSpace: "nowrap", children: [
|
||||||
|
/* @__PURE__ */ o(v, { fontWeight: "bold", children: "تعداد کل:" }),
|
||||||
|
/* @__PURE__ */ o(v, { children: t(C) })
|
||||||
|
] })
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, H = ({
|
||||||
|
currentPage: i,
|
||||||
|
totalPages: n,
|
||||||
|
totalCount: C,
|
||||||
|
onPageChange: d
|
||||||
|
}) => {
|
||||||
|
const [c, l] = y(""), [a, f] = y(!1), [b, s] = y(null), x = M(null), w = b ?? i, k = _(
|
||||||
|
(p) => {
|
||||||
|
const h = Math.min(Math.max(p, 1), n);
|
||||||
|
f(!1), l(""), s(null), h !== i && d(h);
|
||||||
|
},
|
||||||
|
[i, n, d]
|
||||||
|
), e = _(() => {
|
||||||
|
if (!c) {
|
||||||
|
f(!1), l(""), s(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const p = parseInt(D(c), 10);
|
||||||
|
if (isNaN(p)) {
|
||||||
|
f(!1), l(""), s(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const h = Math.min(Math.max(p, 1), n);
|
||||||
|
f(!1), l(""), h !== i ? (s(h), d(h)) : s(null);
|
||||||
|
}, [c, i, n, d]);
|
||||||
|
A(() => {
|
||||||
|
var p;
|
||||||
|
a && ((p = x.current) == null || p.focus());
|
||||||
|
}, [a]), A(() => {
|
||||||
|
b !== null && i === b && s(null);
|
||||||
|
}, [i, b]), A(() => {
|
||||||
|
if (!a || !c) return;
|
||||||
|
const p = setTimeout(() => {
|
||||||
|
e();
|
||||||
|
}, 1e3);
|
||||||
|
return () => clearTimeout(p);
|
||||||
|
}, [c, a, e]);
|
||||||
|
const u = () => {
|
||||||
|
s(null), l(""), f(!0);
|
||||||
|
}, r = (p, h = !1) => /* @__PURE__ */ o(
|
||||||
|
S,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: h ? "#4B49AC" : "#e6e6f1",
|
||||||
|
color: h ? "#e6e6f1" : "#4B49AC",
|
||||||
|
_hover: { bg: h ? "#4B49AC" : "#cbcbe8" },
|
||||||
|
onClick: h ? u : () => k(p),
|
||||||
|
variant: "soft",
|
||||||
|
children: t(p)
|
||||||
|
},
|
||||||
|
p
|
||||||
|
);
|
||||||
|
if (n === 0 || !C) return null;
|
||||||
|
const m = n > 1 && w !== 1, j = n > 1 && w !== n;
|
||||||
|
return /* @__PURE__ */ o(
|
||||||
|
B,
|
||||||
|
{
|
||||||
|
justifyContent: "center",
|
||||||
|
backgroundColor: "white",
|
||||||
|
py: "10px",
|
||||||
|
px: "16px",
|
||||||
|
rounded: "lg",
|
||||||
|
border: "1px",
|
||||||
|
borderColor: "blue.200",
|
||||||
|
w: "100%",
|
||||||
|
children: /* @__PURE__ */ I(B, { spacing: 2, alignItems: "center", justifyContent: "center", children: [
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => k(w - 1),
|
||||||
|
isDisabled: w === 1,
|
||||||
|
icon: /* @__PURE__ */ o(z, {}),
|
||||||
|
"aria-label": "صفحه قبلی"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
m && r(1),
|
||||||
|
a ? /* @__PURE__ */ o(
|
||||||
|
E,
|
||||||
|
{
|
||||||
|
ref: x,
|
||||||
|
type: "text",
|
||||||
|
inputMode: "numeric",
|
||||||
|
placeholder: "صفحه",
|
||||||
|
w: "48px",
|
||||||
|
value: c,
|
||||||
|
onChange: (p) => {
|
||||||
|
const h = p.target.value, T = D(h).replace(/\D/g, "");
|
||||||
|
l(t(T));
|
||||||
|
},
|
||||||
|
onBlur: () => {
|
||||||
|
c ? e() : (l(""), f(!1), s(null));
|
||||||
|
},
|
||||||
|
onKeyDown: (p) => {
|
||||||
|
p.key === "Enter" && e(), p.key === "Escape" && (l(""), f(!1), s(null));
|
||||||
|
},
|
||||||
|
textAlign: "center",
|
||||||
|
color: "#1D2939",
|
||||||
|
px: 1,
|
||||||
|
minW: 0,
|
||||||
|
dir: "ltr"
|
||||||
|
}
|
||||||
|
) : r(w, !0),
|
||||||
|
j && r(n),
|
||||||
|
/* @__PURE__ */ o(
|
||||||
|
N,
|
||||||
|
{
|
||||||
|
size: "md",
|
||||||
|
bg: "#e6e6f1",
|
||||||
|
color: "#4B49AC",
|
||||||
|
_hover: { bg: "#cbcbe8" },
|
||||||
|
onClick: () => k(w + 1),
|
||||||
|
isDisabled: w === n,
|
||||||
|
icon: /* @__PURE__ */ o(V, {}),
|
||||||
|
"aria-label": "صفحه بعدی"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
] })
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
O as LightPagination,
|
||||||
|
L as Pagination,
|
||||||
|
H as SimplePagination,
|
||||||
|
D as toEnDigits,
|
||||||
|
t as toFaDigits,
|
||||||
|
K as toFaNumber
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,9 +7,6 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js"
|
".": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
|
|||||||
191
src/components/pagination/LightPagination.jsx
Normal file
191
src/components/pagination/LightPagination.jsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { Button, HStack, IconButton, Input, Text } from "@chakra-ui/react";
|
||||||
|
import { IoChevronBackOutline, IoChevronForwardOutline } from "react-icons/io5";
|
||||||
|
import { toEnDigits, toFaDigits } from "../../utils/numbers.js";
|
||||||
|
|
||||||
|
export const LightPagination = ({
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
totalCount,
|
||||||
|
onPageChange,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState(toFaDigits(currentPage));
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [pendingPage, setPendingPage] = useState(null);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
const pageNumbers = [...new Set([1, currentPage, totalPages])].filter(
|
||||||
|
(page) => page >= 1 && page <= totalPages,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingPage !== null) {
|
||||||
|
if (currentPage === pendingPage) {
|
||||||
|
setPendingPage(null);
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEditing) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
}, [currentPage, isEditing, pendingPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEditing) return;
|
||||||
|
if (!inputValue) return;
|
||||||
|
if (pendingPage !== null) return;
|
||||||
|
|
||||||
|
const page = parseInt(toEnDigits(inputValue), 10);
|
||||||
|
if (isNaN(page)) return;
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
setPendingPage(safePage);
|
||||||
|
setInputValue(toFaDigits(safePage));
|
||||||
|
onPageChange(safePage);
|
||||||
|
} else {
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRef.current?.blur();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [
|
||||||
|
inputValue,
|
||||||
|
isEditing,
|
||||||
|
pendingPage,
|
||||||
|
totalPages,
|
||||||
|
currentPage,
|
||||||
|
onPageChange,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const page = parseInt(toEnDigits(inputValue), 10);
|
||||||
|
|
||||||
|
if (isNaN(page)) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
inputRef.current?.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
setPendingPage(safePage);
|
||||||
|
setInputValue(toFaDigits(safePage));
|
||||||
|
onPageChange(safePage);
|
||||||
|
} else {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRef.current?.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (totalPages === 0 || !totalCount) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent={"space-around"}
|
||||||
|
backgroundColor={"white"}
|
||||||
|
py={"10px"}
|
||||||
|
px={"16px"}
|
||||||
|
rounded={"lg"}
|
||||||
|
border={"1px"}
|
||||||
|
borderColor={"blue.200"}
|
||||||
|
w={"100%"}
|
||||||
|
>
|
||||||
|
<HStack spacing={2} alignItems={"center"} justifyContent={"center"}>
|
||||||
|
<IconButton
|
||||||
|
size={"md"}
|
||||||
|
bg={"#e6e6f1"}
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => onPageChange(currentPage - 1)}
|
||||||
|
isDisabled={currentPage === 1}
|
||||||
|
icon={<IoChevronForwardOutline />}
|
||||||
|
aria-label="صفحه قبلی"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{pageNumbers.map((number) => (
|
||||||
|
<Button
|
||||||
|
size={"md"}
|
||||||
|
px={2}
|
||||||
|
bg={currentPage === number ? "#4B49AC" : "#e6e6f1"}
|
||||||
|
color={currentPage === number ? "#e6e6f1" : "#4B49AC"}
|
||||||
|
_hover={{ bg: currentPage === number ? "#4B49AC" : "#cbcbe8" }}
|
||||||
|
key={number}
|
||||||
|
onClick={() => onPageChange(number)}
|
||||||
|
variant={"soft"}
|
||||||
|
>
|
||||||
|
{toFaDigits(number)}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size={"md"}
|
||||||
|
bg={"#e6e6f1"}
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => onPageChange(currentPage + 1)}
|
||||||
|
isDisabled={currentPage === totalPages}
|
||||||
|
icon={<IoChevronBackOutline />}
|
||||||
|
aria-label="صفحه بعدی"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="صفحه"
|
||||||
|
w="56px"
|
||||||
|
value={inputValue}
|
||||||
|
onFocus={() => {
|
||||||
|
setIsEditing(true);
|
||||||
|
setPendingPage(null);
|
||||||
|
setInputValue("");
|
||||||
|
}}
|
||||||
|
onChange={(e) => {
|
||||||
|
const rawValue = e.target.value;
|
||||||
|
const onlyDigits = toEnDigits(rawValue).replace(/\D/g, "");
|
||||||
|
setInputValue(toFaDigits(onlyDigits));
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (!inputValue) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handleSubmit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
textAlign="center"
|
||||||
|
color="#1D2939"
|
||||||
|
px={1}
|
||||||
|
minW={0}
|
||||||
|
dir="ltr"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
<HStack spacing={2}>
|
||||||
|
<Text w={"auto"} fontWeight={"bold"} whiteSpace={"nowrap"}>
|
||||||
|
تعداد کل:
|
||||||
|
</Text>
|
||||||
|
<Text>{toFaDigits(totalCount)}</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
197
src/components/pagination/Pagination.jsx
Normal file
197
src/components/pagination/Pagination.jsx
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { Button, HStack, IconButton, Input, Text } from "@chakra-ui/react";
|
||||||
|
import { IoChevronBackOutline, IoChevronForwardOutline } from "react-icons/io5";
|
||||||
|
import { toEnDigits, toFaDigits } from "../../utils/numbers.js";
|
||||||
|
|
||||||
|
export const Pagination = ({
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
totalCount,
|
||||||
|
onPageChange,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState(toFaDigits(currentPage));
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [pendingPage, setPendingPage] = useState(null);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
const pageNumbers = Array.from(
|
||||||
|
new Set([1, currentPage, currentPage + 1, totalPages]),
|
||||||
|
)
|
||||||
|
.filter((page) => page >= 1 && page <= totalPages)
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingPage !== null) {
|
||||||
|
if (currentPage === pendingPage) {
|
||||||
|
setPendingPage(null);
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEditing) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
}, [currentPage, isEditing, pendingPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEditing) return;
|
||||||
|
if (!inputValue) return;
|
||||||
|
if (pendingPage !== null) return;
|
||||||
|
|
||||||
|
const page = parseInt(toEnDigits(inputValue), 10);
|
||||||
|
if (isNaN(page)) return;
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
setPendingPage(safePage);
|
||||||
|
setInputValue(toFaDigits(safePage));
|
||||||
|
onPageChange(safePage);
|
||||||
|
} else {
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRef.current?.blur();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [
|
||||||
|
inputValue,
|
||||||
|
isEditing,
|
||||||
|
pendingPage,
|
||||||
|
totalPages,
|
||||||
|
currentPage,
|
||||||
|
onPageChange,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const page = parseInt(toEnDigits(inputValue), 10);
|
||||||
|
|
||||||
|
if (isNaN(page)) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
inputRef.current?.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
setPendingPage(safePage);
|
||||||
|
setInputValue(toFaDigits(safePage));
|
||||||
|
onPageChange(safePage);
|
||||||
|
} else {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRef.current?.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (totalPages === 0 || !totalCount) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent={"space-around"}
|
||||||
|
backgroundColor={"white"}
|
||||||
|
py={"10px"}
|
||||||
|
px={"16px"}
|
||||||
|
rounded={"lg"}
|
||||||
|
border={"1px"}
|
||||||
|
borderColor={"blue.200"}
|
||||||
|
w={"100%"}
|
||||||
|
>
|
||||||
|
<HStack spacing={2} whiteSpace={"nowrap"}>
|
||||||
|
<Text fontWeight={"bold"}>صفحه</Text>
|
||||||
|
<Text>{toFaDigits(currentPage)}</Text>
|
||||||
|
<Text fontWeight={"bold"}>از</Text>
|
||||||
|
<Text>{toFaDigits(totalPages)}</Text>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
<HStack spacing={2} alignItems={"center"} justifyContent={"center"}>
|
||||||
|
<IconButton
|
||||||
|
size={"md"}
|
||||||
|
bg={"#e6e6f1"}
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => onPageChange(currentPage - 1)}
|
||||||
|
isDisabled={currentPage === 1}
|
||||||
|
icon={<IoChevronForwardOutline />}
|
||||||
|
aria-label="صفحه قبلی"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{pageNumbers.map((number) => (
|
||||||
|
<Button
|
||||||
|
size={"md"}
|
||||||
|
bg={currentPage === number ? "#4B49AC" : "#e6e6f1"}
|
||||||
|
color={currentPage === number ? "#e6e6f1" : "#4B49AC"}
|
||||||
|
_hover={{ bg: currentPage === number ? "#4B49AC" : "#cbcbe8" }}
|
||||||
|
key={number}
|
||||||
|
onClick={() => onPageChange(number)}
|
||||||
|
variant={"soft"}
|
||||||
|
>
|
||||||
|
{toFaDigits(number)}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size={"md"}
|
||||||
|
bg={"#e6e6f1"}
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => onPageChange(currentPage + 1)}
|
||||||
|
isDisabled={currentPage === totalPages}
|
||||||
|
icon={<IoChevronBackOutline />}
|
||||||
|
aria-label="صفحه بعدی"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="صفحه"
|
||||||
|
w="100px"
|
||||||
|
value={inputValue}
|
||||||
|
onFocus={() => {
|
||||||
|
setIsEditing(true);
|
||||||
|
setPendingPage(null);
|
||||||
|
setInputValue("");
|
||||||
|
}}
|
||||||
|
onChange={(e) => {
|
||||||
|
const rawValue = e.target.value;
|
||||||
|
const onlyDigits = toEnDigits(rawValue).replace(/\D/g, "");
|
||||||
|
setInputValue(toFaDigits(onlyDigits));
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (!inputValue) {
|
||||||
|
setInputValue(toFaDigits(currentPage));
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handleSubmit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
textAlign="center"
|
||||||
|
color="#1D2939"
|
||||||
|
px={1}
|
||||||
|
minW={0}
|
||||||
|
dir="ltr"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
<HStack spacing={2} whiteSpace={"nowrap"}>
|
||||||
|
<Text fontWeight={"bold"}>تعداد کل:</Text>
|
||||||
|
<Text>{toFaDigits(totalCount)}</Text>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
195
src/components/pagination/SimplePagination.jsx
Normal file
195
src/components/pagination/SimplePagination.jsx
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import { Button, HStack, IconButton, Input } from "@chakra-ui/react";
|
||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { IoChevronBackOutline, IoChevronForwardOutline } from "react-icons/io5";
|
||||||
|
import { toEnDigits, toFaDigits } from "../../utils/numbers.js";
|
||||||
|
|
||||||
|
export const SimplePagination = ({
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
totalCount,
|
||||||
|
onPageChange,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [pendingPage, setPendingPage] = useState(null);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
const visibleCurrentPage = pendingPage ?? currentPage;
|
||||||
|
|
||||||
|
const goToPage = useCallback(
|
||||||
|
(page) => {
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue("");
|
||||||
|
setPendingPage(null);
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
onPageChange(safePage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentPage, totalPages, onPageChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
const commitInput = useCallback(() => {
|
||||||
|
if (!inputValue) {
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue("");
|
||||||
|
setPendingPage(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = parseInt(toEnDigits(inputValue), 10);
|
||||||
|
|
||||||
|
if (isNaN(page)) {
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue("");
|
||||||
|
setPendingPage(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const safePage = Math.min(Math.max(page, 1), totalPages);
|
||||||
|
|
||||||
|
setIsEditing(false);
|
||||||
|
setInputValue("");
|
||||||
|
|
||||||
|
if (safePage !== currentPage) {
|
||||||
|
setPendingPage(safePage);
|
||||||
|
onPageChange(safePage);
|
||||||
|
} else {
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}, [inputValue, currentPage, totalPages, onPageChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEditing) {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}
|
||||||
|
}, [isEditing]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingPage !== null && currentPage === pendingPage) {
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}, [currentPage, pendingPage]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEditing) return;
|
||||||
|
if (!inputValue) return;
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
commitInput();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [inputValue, isEditing, commitInput]);
|
||||||
|
|
||||||
|
const startEditing = () => {
|
||||||
|
setPendingPage(null);
|
||||||
|
setInputValue("");
|
||||||
|
setIsEditing(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderPageButton = (number, isCurrent = false) => (
|
||||||
|
<Button
|
||||||
|
size="md"
|
||||||
|
bg={isCurrent ? "#4B49AC" : "#e6e6f1"}
|
||||||
|
color={isCurrent ? "#e6e6f1" : "#4B49AC"}
|
||||||
|
_hover={{ bg: isCurrent ? "#4B49AC" : "#cbcbe8" }}
|
||||||
|
key={number}
|
||||||
|
onClick={isCurrent ? startEditing : () => goToPage(number)}
|
||||||
|
variant="soft"
|
||||||
|
>
|
||||||
|
{toFaDigits(number)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (totalPages === 0 || !totalCount) return null;
|
||||||
|
|
||||||
|
const showFirstButton = totalPages > 1 && visibleCurrentPage !== 1;
|
||||||
|
const showLastButton = totalPages > 1 && visibleCurrentPage !== totalPages;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="center"
|
||||||
|
backgroundColor="white"
|
||||||
|
py="10px"
|
||||||
|
px="16px"
|
||||||
|
rounded="lg"
|
||||||
|
border="1px"
|
||||||
|
borderColor="blue.200"
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
<HStack spacing={2} alignItems="center" justifyContent="center">
|
||||||
|
<IconButton
|
||||||
|
size="md"
|
||||||
|
bg="#e6e6f1"
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => goToPage(visibleCurrentPage - 1)}
|
||||||
|
isDisabled={visibleCurrentPage === 1}
|
||||||
|
icon={<IoChevronForwardOutline />}
|
||||||
|
aria-label="صفحه قبلی"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{showFirstButton && renderPageButton(1)}
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="صفحه"
|
||||||
|
w="48px"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
const rawValue = e.target.value;
|
||||||
|
const onlyDigits = toEnDigits(rawValue).replace(/\D/g, "");
|
||||||
|
setInputValue(toFaDigits(onlyDigits));
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (inputValue) {
|
||||||
|
commitInput();
|
||||||
|
} else {
|
||||||
|
setInputValue("");
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
commitInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
setInputValue("");
|
||||||
|
setIsEditing(false);
|
||||||
|
setPendingPage(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
textAlign="center"
|
||||||
|
color="#1D2939"
|
||||||
|
px={1}
|
||||||
|
minW={0}
|
||||||
|
dir="ltr"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
renderPageButton(visibleCurrentPage, true)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showLastButton && renderPageButton(totalPages)}
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="md"
|
||||||
|
bg="#e6e6f1"
|
||||||
|
color="#4B49AC"
|
||||||
|
_hover={{ bg: "#cbcbe8" }}
|
||||||
|
onClick={() => goToPage(visibleCurrentPage + 1)}
|
||||||
|
isDisabled={visibleCurrentPage === totalPages}
|
||||||
|
icon={<IoChevronBackOutline />}
|
||||||
|
aria-label="صفحه بعدی"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1 +1,5 @@
|
|||||||
export { toFaNumber, toFaDigits, toEnDigits } from "./utils/numbers.js";
|
export { toFaNumber, toFaDigits, toEnDigits } from "./utils/numbers.js";
|
||||||
|
|
||||||
|
export { LightPagination } from "./components/pagination/LightPagination.jsx";
|
||||||
|
export { Pagination } from "./components/pagination/Pagination.jsx";
|
||||||
|
export { SimplePagination } from "./components/pagination/SimplePagination.jsx";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user