{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Yesod.Paginator.Pages
(
PageNumber
, PerPage
, ItemsCount
, pageOffset
, Page
, pageItems
, pageNumber
, toPage
, Pages
, pagesCurrent
, pagesLast
, toPages
, takePreviousPages
, takeNextPages
, getPreviousPage
, getNextPage
)
where
import Yesod.Paginator.Prelude
import Text.Blaze (ToMarkup)
import Web.PathPieces
newtype PageNumber = PageNumber Natural
deriving (Enum, Eq, Integral, Num, Ord, Real)
deriving newtype (Show, ToMarkup)
newtype PerPage = PerPage Natural
deriving (Enum, Eq, Integral, Num, Ord, Real)
deriving newtype (Read, Show, PathPiece)
newtype ItemsCount = ItemsCount Natural
deriving (Enum, Eq, Integral, Num, Ord, Real)
deriving newtype (Read, Show, PathPiece)
data Page a = Page
{ pageItems :: [a]
, pageNumber :: PageNumber
}
deriving (Eq, Show)
toPage :: [a] -> PageNumber -> Page a
toPage = Page
data Pages a = Pages
{ pagesCurrent :: Page a
, pagesPrevious :: [PageNumber]
, pagesNext :: [PageNumber]
, pagesLast :: PageNumber
}
deriving (Eq, Show)
takePreviousPages :: Natural -> Pages a -> [PageNumber]
takePreviousPages n = reverse . genericTake n . reverse . pagesPrevious
takeNextPages :: Natural -> Pages a -> [PageNumber]
takeNextPages n = genericTake n . pagesNext
getPreviousPage :: Pages a -> Maybe PageNumber
getPreviousPage pages = do
let prevPage = pageNumber (pagesCurrent pages) - 1
firstPage <- headMay $ pagesPrevious pages
prevPage <$ guard (prevPage >= firstPage)
getNextPage :: Pages a -> Maybe PageNumber
getNextPage pages = do
let nextPage = pageNumber (pagesCurrent pages) + 1
lastPage <- lastMay $ pagesNext pages
nextPage <$ guard (nextPage <= lastPage)
toPages :: PageNumber -> PerPage -> ItemsCount -> [a] -> Pages a
toPages number per total items = Pages
{ pagesCurrent = toPage items number
, pagesPrevious = [1 .. (number - 1)]
, pagesNext = [(number + 1) .. lastPage]
, pagesLast = lastPage
}
where lastPage = getLastPage total per
getLastPage :: ItemsCount -> PerPage -> PageNumber
getLastPage total = fromIntegral . carry . (total `divMod`) . fromIntegral
where
carry (q, 0) = q
carry (q, _) = q + 1
pageOffset :: PageNumber -> PerPage -> ItemsCount
pageOffset p per = fromIntegral $ (fromIntegral p - 1) * per