{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} ------------------------------------------------------------------------------- -- | -- Module : Yesod.Goodies.Paginate -- Copyright : (c) Patrick Brisbin 2010 -- License : as-is -- -- Maintainer : pbrisbin@gmail.com -- Stability : unstable -- Portability : unportable -- -- orignal concept by ajdunlap: -- -- -- this version does not use the subsite approach. -- ------------------------------------------------------------------------------- module Yesod.Goodies.Paginate ( PageOptions(..) , paginate ) where import Yesod import qualified Data.Text as T data PageOptions a s m = PageOptions { itemsPerPage :: Int -- | How to show a single page's item listing , 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..p-1] [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 -- make the page listing a bit more apprope addCassius [cassius| ul.pagination margin: 5px 0px; padding: 0px; text-align: center .pagination li display: inline list-style-type: none margin: 0px; padding: 0px 3px text-align: center .pagination li.this_page padding: 0px 5px |] -- limit how many page links are shown 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}