@@ -9,9 +9,43 @@ import {
99 createResource ,
1010 createSignal ,
1111 onCleanup ,
12+ untrack ,
1213} from "solid-js" ;
1314import { isServer } from "solid-js/web" ;
1415
16+ /**
17+ * createSegment - create a reactive segment out of an array of items
18+ * @param {MaybeAccessor<any[]> } items - array of items
19+ * @param {MaybeAccessor<number> } limit - limit of items per segment
20+ * @param {Accessor<number> } page - segment number starting with 1
21+ *
22+ * ```ts
23+ * const [limit, setLimit] = createSignal(10);
24+ * const [paginationProps, page, setPage] = createPagination(() => ({
25+ * pages: Math.ceil(items().length / limit())
26+ * }));
27+ * const segment = createSegment(items, limit, page);
28+ * ```
29+ */
30+ export const createSegment = < T > (
31+ items : MaybeAccessor < T [ ] > ,
32+ limit : MaybeAccessor < number > ,
33+ page : Accessor < number >
34+ ) : Accessor < T [ ] > => {
35+ let previousStart = NaN , previousEnd = NaN ;
36+ return createMemo ( ( previous ) => {
37+ const currentItems = access ( items ) ;
38+ const start = ( page ( ) - 1 ) * access ( limit ) ;
39+ const end = Math . min ( start + access ( limit ) , currentItems . length ) ;
40+ if ( previous && ( previous . length === 0 && end <= start || start === previousStart && end === previousEnd ) ) {
41+ return previous ;
42+ }
43+ previousStart = start ;
44+ previousEnd = end ;
45+ return currentItems . slice ( start , end ) ;
46+ } ) ;
47+ }
48+
1549export type PaginationOptions = {
1650 /** the overall number of pages */
1751 pages : number ;
@@ -35,6 +69,8 @@ export type PaginationOptions = {
3569 nextContent ?: JSX . Element ;
3670 /** content for the last page element, e.g. an SVG icon, default is ">|" */
3771 lastContent ?: JSX . Element ;
72+ /** number of pages a large jump, if it should exist, should skip */
73+ jumpPages ?: number ;
3874} ;
3975
4076export type PaginationProps = {
@@ -98,22 +134,42 @@ export const createPagination = (
98134) : [ props : Accessor < PaginationProps > , page : Accessor < number > , setPage : Setter < number > ] => {
99135 const opts = createMemo ( ( ) => Object . assign ( { } , PAGINATION_DEFAULTS , access ( options ) ) ) ;
100136 const [ page , _setPage ] = createSignal ( opts ( ) . initialPage || 1 ) ;
137+
138+ // do not allow pages beyond the number of pages in case the latter changes
101139 const setPage = ( p : number | ( ( _p : number ) => number ) ) => {
102140 if ( typeof p === "function" ) {
103141 p = p ( page ( ) ) ;
104142 }
105- return p >= 1 && p <= opts ( ) . pages ? _setPage ( p ) : page ( ) ;
143+ if ( p < 1 ) {
144+ return _setPage ( 1 ) ;
145+ }
146+ const pages = opts ( ) . pages ;
147+ if ( p > pages ) {
148+ return _setPage ( pages ) ;
149+ }
150+ return _setPage ( p ) ;
106151 } ;
107152
153+ // normalize in case the number of pages changes, do not run the first time
154+ createComputed ( ( previous ) => {
155+ opts ( ) . pages ;
156+ return previous ? setPage ( untrack ( page ) ) : true ;
157+ } ) ;
158+
159+ const goPage = ( p : number | ( ( p : number ) => number ) , ev : KeyboardEvent ) => {
160+ setPage ( p ) ;
161+ ev . currentTarget ?. parentNode . querySelector ( '[aria-current="true"]' ) . focus ( ) ;
162+ }
163+
108164 const onKeyUp = ( pageNo : number , ev : KeyboardEvent ) =>
109165 (
110166 ( {
111- ArrowLeft : ( ) => setPage ( p => p - 1 ) ,
112- ArrowRight : ( ) => setPage ( p => p + 1 ) ,
113- Home : ( ) => setPage ( 1 ) ,
114- End : ( ) => setPage ( opts ( ) . pages ) ,
115- Space : ( ) => setPage ( pageNo ) ,
116- Return : ( ) => setPage ( pageNo ) ,
167+ ArrowLeft : ( ) => goPage ( p => p - 1 , ev ) ,
168+ ArrowRight : ( ) => goPage ( p => p + 1 , ev ) ,
169+ Home : ( ) => goPage ( 1 , ev ) ,
170+ End : ( ) => goPage ( opts ( ) . pages , ev ) ,
171+ Space : ( ) => goPage ( pageNo , ev ) ,
172+ Return : ( ) => goPage ( pageNo , ev ) ,
117173 } ) [ ev . key ] || noop
118174 ) ( ) ;
119175
@@ -197,6 +253,32 @@ export const createPagination = (
197253 page : { get : ( ) => opts ( ) . pages , enumerable : false } ,
198254 } ,
199255 ) ;
256+ const jumpBack = Object . defineProperties (
257+ isServer
258+ ? ( { } as PaginationProps [ number ] )
259+ : ( {
260+ onClick : ( ) => setPage ( Math . max ( 1 , page ( ) - ( opts ( ) . jumpPages || Infinity ) ) ) ,
261+ onKeyUp : ( ev : KeyboardEvent ) => onKeyUp ( page ( ) - ( opts ( ) . jumpPages || Infinity ) , ev ) ,
262+ } as unknown as PaginationProps [ number ] ) ,
263+ {
264+ disabled : { get : ( ) => page ( ) - ( opts ( ) . jumpPages || Infinity ) < 0 , set : noop , enumerable : true } ,
265+ children : { get : ( ) => `-${ opts ( ) . jumpPages } ` , set : noop , enumerable : true } ,
266+ page : { get : ( ) => Math . max ( 1 , page ( ) - ( opts ( ) . jumpPages || Infinity ) ) , enumerable : false } ,
267+ }
268+ ) ;
269+ const jumpForth = Object . defineProperties (
270+ isServer
271+ ? ( { } as PaginationProps [ number ] )
272+ : ( {
273+ onClick : ( ) => setPage ( Math . min ( opts ( ) . pages , page ( ) + ( opts ( ) . jumpPages || Infinity ) ) ) ,
274+ onKeyUp : ( ev : KeyboardEvent ) => onKeyUp ( Math . min ( opts ( ) . pages , page ( ) + ( opts ( ) . jumpPages || Infinity ) ) , ev ) ,
275+ } as unknown as PaginationProps [ number ] ) ,
276+ {
277+ disabled : { get : ( ) => page ( ) + ( opts ( ) . jumpPages || Infinity ) > opts ( ) . pages , set : noop , enumerable : true } ,
278+ children : { get : ( ) => `+${ opts ( ) . jumpPages } ` , set : noop , enumerable : true } ,
279+ page : { get : ( ) => Math . min ( opts ( ) . pages , page ( ) + ( opts ( ) . jumpPages || Infinity ) ) , enumerable : false } ,
280+ }
281+ )
200282
201283 const start = createMemo ( ( ) =>
202284 Math . min ( opts ( ) . pages - maxPages ( ) , Math . max ( 1 , page ( ) - ( maxPages ( ) >> 1 ) ) - 1 ) ,
@@ -219,13 +301,19 @@ export const createPagination = (
219301 if ( showFirst ( ) ) {
220302 props . push ( first ) ;
221303 }
304+ if ( opts ( ) . jumpPages ) {
305+ props . push ( jumpBack ) ;
306+ }
222307 if ( showPrev ( ) ) {
223308 props . push ( back ) ;
224309 }
225310 props . push ( ...pages ( ) . slice ( start ( ) , start ( ) + maxPages ( ) ) ) ;
226311 if ( showNext ( ) ) {
227312 props . push ( next ) ;
228313 }
314+ if ( opts ( ) . jumpPages ) {
315+ props . push ( jumpForth ) ;
316+ }
229317 if ( showLast ( ) ) {
230318 props . push ( last ) ;
231319 }
0 commit comments