@boxcustodia/library 2.0.0-alpha.31 → 2.0.0-alpha.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),o=require("../internal/use-latest-ref.cjs.js"),O=10,U=1;function d(e,r){return e<=0||Number.isNaN(e)?(process.env.NODE_ENV!=="production"&&(!r||!r.current)&&(r&&(r.current=!0),console.warn(`[usePagination] pageSize must be a positive number, received ${e}. Coercing to 1.`)),1):Math.floor(e)}function j(e,r){return e<=0?0:Math.ceil(e/r)}function h(e,r){return r<=0||Number.isNaN(e)?1:Math.min(Math.max(1,Math.floor(e)),r)}function V({totalItems:e,pageSize:r,defaultPageSize:z=O,onPageSizeChange:b,page:C,defaultPage:A=U,onPageChange:T,onPaginationChange:_}){const[P,S]=n.useState(A),[l,L]=n.useState(z),x=n.useRef(!1),s=d(r??l,x),i=j(e,s),c=h(C??P,i),f=o.useLatestRef(c),y=o.useLatestRef(P),D=o.useLatestRef(s),a=o.useLatestRef(i),E=o.useLatestRef(T),N=o.useLatestRef(b),F=o.useLatestRef(_),R=n.useRef(!1),p=n.useRef(null),m=n.useRef(null);n.useEffect(()=>{if(!R.current)return;const t=h(P,a.current);p.current!==t&&(p.current=t,E.current?.(t))},[P]),n.useEffect(()=>{if(!R.current)return;const t=d(l);m.current!==t&&(m.current=t,N.current?.(t))},[l]),n.useEffect(()=>{R.current&&F.current?.({page:c,pageSize:s})},[c,s]),n.useEffect(()=>{R.current=!0},[]);const q=n.useMemo(()=>{const t=g=>{const u=h(g,a.current);u!==f.current&&(y.current===u&&(p.current=u,E.current?.(u)),S(u))};return{goTo:g=>{a.current<=0||t(g)},next:()=>{f.current>=a.current||t(f.current+1)},prev:()=>{f.current<=1||t(f.current-1)},firstPage:()=>{a.current<=0||t(1)},lastPage:()=>{a.current<=0||t(a.current)},setPageSize:g=>{const u=d(g);u!==D.current&&(m.current=u,L(u),N.current?.(u),f.current!==1&&(p.current=1,E.current?.(1)),S(1))}}},[S,L]),v=i<=0?!0:c<=1,M=i<=0?!0:c>=i,G=e<=0?{start:0,end:0}:{start:(c-1)*s+1,end:Math.min(c*s,e)};return{page:c,pageSize:s,pageCount:i,isFirstPage:v,isLastPage:M,hasPrevPage:!v,hasNextPage:!M,range:G,...q}}exports.usePagination=V;
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import { useState as b, useRef as
|
|
2
|
-
import { useLatestRef as
|
|
3
|
-
const
|
|
1
|
+
import { useState as b, useRef as S, useEffect as l, useMemo as Z } from "react";
|
|
2
|
+
import { useLatestRef as o } from "../internal/use-latest-ref.es.js";
|
|
3
|
+
const $ = 10, j = 1;
|
|
4
4
|
function d(e, t) {
|
|
5
5
|
return e <= 0 || Number.isNaN(e) ? (process.env.NODE_ENV !== "production" && (!t || !t.current) && (t && (t.current = !0), console.warn(
|
|
6
6
|
`[usePagination] pageSize must be a positive number, received ${e}. Coercing to 1.`
|
|
7
7
|
)), 1) : Math.floor(e);
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function k(e, t) {
|
|
10
10
|
return e <= 0 ? 0 : Math.ceil(e / t);
|
|
11
11
|
}
|
|
12
12
|
function N(e, t) {
|
|
13
13
|
return t <= 0 || Number.isNaN(e) ? 1 : Math.min(Math.max(1, Math.floor(e)), t);
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function H({
|
|
16
16
|
totalItems: e,
|
|
17
17
|
pageSize: t,
|
|
18
|
-
defaultPageSize: x =
|
|
18
|
+
defaultPageSize: x = $,
|
|
19
19
|
onPageSizeChange: A,
|
|
20
20
|
page: L,
|
|
21
|
-
defaultPage: _ =
|
|
21
|
+
defaultPage: _ = j,
|
|
22
22
|
onPageChange: D,
|
|
23
23
|
onPaginationChange: F
|
|
24
24
|
}) {
|
|
25
|
-
const [
|
|
26
|
-
|
|
27
|
-
if (!
|
|
28
|
-
const r = N(
|
|
29
|
-
|
|
30
|
-
}, [
|
|
31
|
-
if (!
|
|
32
|
-
const r = d(
|
|
33
|
-
|
|
34
|
-
}, [
|
|
35
|
-
|
|
36
|
-
}, [
|
|
37
|
-
|
|
25
|
+
const [g, m] = b(_), [h, v] = b(x), T = S(!1), c = d(t ?? h, T), i = k(e, c), u = N(L ?? g, i), s = o(u), G = o(g), U = o(c), a = o(i), E = o(D), z = o(A), y = o(F), P = S(!1), p = S(null), R = S(null);
|
|
26
|
+
l(() => {
|
|
27
|
+
if (!P.current) return;
|
|
28
|
+
const r = N(g, a.current);
|
|
29
|
+
p.current !== r && (p.current = r, E.current?.(r));
|
|
30
|
+
}, [g]), l(() => {
|
|
31
|
+
if (!P.current) return;
|
|
32
|
+
const r = d(h);
|
|
33
|
+
R.current !== r && (R.current = r, z.current?.(r));
|
|
34
|
+
}, [h]), l(() => {
|
|
35
|
+
P.current && y.current?.({ page: u, pageSize: c });
|
|
36
|
+
}, [u, c]), l(() => {
|
|
37
|
+
P.current = !0;
|
|
38
38
|
}, []);
|
|
39
|
-
const
|
|
39
|
+
const O = Z(() => {
|
|
40
40
|
const r = (f) => {
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
const n = N(f, a.current);
|
|
42
|
+
n !== s.current && (G.current === n && (p.current = n, E.current?.(n)), m(n));
|
|
43
43
|
};
|
|
44
44
|
return {
|
|
45
45
|
goTo: (f) => {
|
|
46
|
-
|
|
46
|
+
a.current <= 0 || r(f);
|
|
47
47
|
},
|
|
48
48
|
next: () => {
|
|
49
|
-
|
|
49
|
+
s.current >= a.current || r(s.current + 1);
|
|
50
50
|
},
|
|
51
51
|
prev: () => {
|
|
52
|
-
|
|
52
|
+
s.current <= 1 || r(s.current - 1);
|
|
53
53
|
},
|
|
54
54
|
firstPage: () => {
|
|
55
|
-
|
|
55
|
+
a.current <= 0 || r(1);
|
|
56
56
|
},
|
|
57
57
|
lastPage: () => {
|
|
58
|
-
|
|
58
|
+
a.current <= 0 || r(a.current);
|
|
59
59
|
},
|
|
60
60
|
setPageSize: (f) => {
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const n = d(f);
|
|
62
|
+
n !== U.current && (R.current = n, v(n), z.current?.(n), s.current !== 1 && (p.current = 1, E.current?.(1)), m(1));
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
|
-
}, [
|
|
66
|
-
start: (
|
|
67
|
-
end: Math.min(
|
|
65
|
+
}, [m, v]), M = i <= 0 ? !0 : u <= 1, C = i <= 0 ? !0 : u >= i, V = e <= 0 ? { start: 0, end: 0 } : {
|
|
66
|
+
start: (u - 1) * c + 1,
|
|
67
|
+
end: Math.min(u * c, e)
|
|
68
68
|
};
|
|
69
69
|
return {
|
|
70
|
-
page:
|
|
71
|
-
pageSize:
|
|
72
|
-
pageCount:
|
|
70
|
+
page: u,
|
|
71
|
+
pageSize: c,
|
|
72
|
+
pageCount: i,
|
|
73
73
|
isFirstPage: M,
|
|
74
74
|
isLastPage: C,
|
|
75
75
|
hasPrevPage: !M,
|
|
76
76
|
hasNextPage: !C,
|
|
77
|
-
range:
|
|
78
|
-
...
|
|
77
|
+
range: V,
|
|
78
|
+
...O
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
export {
|
|
82
|
-
|
|
82
|
+
H as usePagination
|
|
83
83
|
};
|
package/package.json
CHANGED
|
@@ -324,6 +324,32 @@ describe("usePagination — Controllable Page", () => {
|
|
|
324
324
|
expect(onPageChange).toHaveBeenCalledWith(3);
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
it("Scenario 21b — onPageChange fires when controlled prop diverges from internal state then user navigates back to page 1", () => {
|
|
328
|
+
// Regression: if the page prop is updated externally (no internal navigation),
|
|
329
|
+
// pageState stays at its default (1). A subsequent goTo(1) calls setPageState(1)
|
|
330
|
+
// which is a React no-op → useEffect never fires → onPageChange silently skipped.
|
|
331
|
+
const onPageChange = vi.fn();
|
|
332
|
+
let controlledPage = 1;
|
|
333
|
+
const { result, rerender } = renderHook(() =>
|
|
334
|
+
usePagination({
|
|
335
|
+
totalItems: 100,
|
|
336
|
+
defaultPageSize: 10,
|
|
337
|
+
page: controlledPage,
|
|
338
|
+
onPageChange,
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Parent externally jumps to page 5 — no internal navigation, pageState stays 1
|
|
343
|
+
controlledPage = 5;
|
|
344
|
+
rerender();
|
|
345
|
+
expect(result.current.page).toBe(5);
|
|
346
|
+
onPageChange.mockClear();
|
|
347
|
+
|
|
348
|
+
// User clicks "First" — should fire onPageChange(1)
|
|
349
|
+
act(() => result.current.goTo(1));
|
|
350
|
+
expect(onPageChange).toHaveBeenCalledWith(1);
|
|
351
|
+
});
|
|
352
|
+
|
|
327
353
|
it("Scenario 22 — onPageChange NOT fired on mount", () => {
|
|
328
354
|
const onPageChange = vi.fn();
|
|
329
355
|
renderHook(() =>
|
|
@@ -136,6 +136,7 @@ export function usePagination({
|
|
|
136
136
|
|
|
137
137
|
// Refs so memoized actions read live values without stale closures
|
|
138
138
|
const pageRef = useLatestRef(page);
|
|
139
|
+
const pageStateRef = useLatestRef(pageState);
|
|
139
140
|
const pageSizeRef = useLatestRef(pageSize);
|
|
140
141
|
const pageCountRef = useLatestRef(pageCount);
|
|
141
142
|
const onPageChangeRef = useLatestRef(onPageChange);
|
|
@@ -186,6 +187,14 @@ export function usePagination({
|
|
|
186
187
|
const applyPage = (target: number) => {
|
|
187
188
|
const clamped = clampPage(target, pageCountRef.current);
|
|
188
189
|
if (clamped === pageRef.current) return; // no-op for same page
|
|
190
|
+
// In controlled mode, pageState may already equal `clamped` (diverged from
|
|
191
|
+
// pageProp), so setPageState would be a no-op and the useEffect that fires
|
|
192
|
+
// onPageChange would never run. Mirror the same direct-fire pattern used in
|
|
193
|
+
// setPageSize to guarantee the callback reaches the consumer.
|
|
194
|
+
if (pageStateRef.current === clamped) {
|
|
195
|
+
lastEmittedPageRef.current = clamped;
|
|
196
|
+
onPageChangeRef.current?.(clamped);
|
|
197
|
+
}
|
|
189
198
|
setPageState(clamped);
|
|
190
199
|
};
|
|
191
200
|
|