module Yesod.Goodies.Paginate
( PageOptions(..)
, paginate
) where
import Yesod
import qualified Data.Text as T
data PageOptions a s m = PageOptions
{ itemsPerPage :: Int
, showItems :: [a] -> GWidget s m ()
}
data Page a = Page
{ thisPage :: (Int, [a])
, prevPages :: [Int]
, nextPages :: [Int]
}
paginate :: PageOptions a s m -> [a] -> GWidget s m ()
paginate opts xs = do
mp <- lift $ lookupGetParam "p"
let page = case mp of
Nothing -> determinePage 1 (itemsPerPage opts) xs
Just "" -> determinePage 1 (itemsPerPage opts) xs
Just p -> case readIntegral $ T.unpack p of
Just i -> determinePage i (itemsPerPage opts) xs
_ -> determinePage 1 (itemsPerPage opts) xs
displayPage (showItems opts) page
determinePage :: Int -> Int -> [a] -> Page a
determinePage p per xs = go $ length xs `divPlus` per
where
go pages
| pages <= 1 = Page (1, xs) [] []
| pages < p = determinePage pages per xs
| otherwise = let items = take per $ drop ((p 1) * per) xs
in Page (p, items) [1..p1] [p+1..pages]
divPlus :: Int -> Int -> Int
x `divPlus` y = (\(n, r) -> if r == 0 then n else n + 1) $ x `divMod` y
displayPage :: ([a] -> GWidget s m ()) -> Page a -> GWidget s m ()
displayPage doShow (Page (this, items) prev next) = do
addCassius [cassius|
ul.pagination
margin: 5px 0px;
padding: 0px;
textalign: center
.pagination li
display: inline
liststyletype: none
margin: 0px;
padding: 0px 3px
textalign: center
.pagination li.this_page
padding: 0px 5px
|]
let prev' = if length prev > 10 then drop (length prev 10) prev else prev
let next' = if length next > 10 then take 10 next else next
[hamlet|
^{doShow items}
<ul .pagination>
$if (/=) prev prev'
<li .previous_pages_more>...
$forall p <- prev'
<li .previous_pages>
<a href="?p=#{show p}">#{show p}
<li .this_page>#{show this}
$forall n <- next'
<li .next_pages>
<a href="?p=#{show n}">#{show n}
$if (/=) next next'
<li .next_pages_more>...
|]