Pagination
Monday, June 2, 2014
Most of you have used the pagination on various websites, usually in the context of search results or forum posts. I thought it would be fun to build a simple “paginator”, using Factor.
For example, if you are on page 23 of 28 total pages, you might see something like this, where you show the selected page and other pages that you can quickly link to:
<< 1 2 ... 21 22 [23] 24 25 ... 27 28 >
Creating a specification from this, our goal will be to show:
- the first two pages
- the selected page (with two pages before and after)
- the last two pages
Using the
output>array
smart combinator (and lexical
variables), we
can generate a sequence of page numbers, filtered to make sure we only
allow valid page numbers between 1
and #pages
:
:: pages-to-show ( page #pages -- seq )
[
1 2 page {
[ 2 - ]
[ 1 - ]
[ ]
[ 1 + ]
[ 2 + ]
} cleave #pages [ 1 - ] keep
] output>array members
[ 1 #pages between? ] filter ;
Some unit tests demonstrate that this works for our “spec” pretty well:
{ { 1 2 3 99 100 } } [ 1 100 pages-to-show ] unit-test
{ { 1 2 21 22 23 24 25 27 28 } } [ 23 28 pages-to-show ] unit-test
{ { 1 2 3 } } [ 1 3 pages-to-show ] unit-test
Lastly, we can split the page numbers to display ellipsis on gaps, and print something like our original goal above:
:: pages-to-show. ( page #pages -- )
page #pages pages-to-show
[ swap - 1 = ] monotonic-split { f } join
[
[
[ number>string ]
[ page = [ "[" "]" surround ] when ] bi
] [ "..." ] if*
] map " " join "<< " " >>" surround print ;
See, it works!
IN: scratchpad 1 100 pages-to-show.
<< [1] 2 3 ... 99 100 >
IN: scratchpad 23 28 pages-to-show.
<< 1 2 ... 21 22 [23] 24 25 ... 27 28 >
IN: scratchpad 1 3 pages-to-show.
<< [1] 2 3 >
Using this in a web application is left as an exercise for the reader,
although it might be nice to create a furnace.pagination
vocabulary
that automatically handles this in our web
framework.
You can find this code on my GitHub.