module IHP.Pagination.ViewFunctions (
module IHP.Pagination.Types,
renderPagination,
renderFilter,
) where
import IHP.Prelude
import IHP.Pagination.Types
import IHP.Pagination.Helpers
import IHP.ControllerSupport
import Text.Blaze.Html (Html)
import IHP.HSX.QQ (hsx)
import IHP.Controller.Param (paramOrNothing)
import qualified Network.Wai as Wai
import qualified Network.HTTP.Types.URI as Query
import IHP.ViewSupport (theRequest, theCSSFramework)
import qualified Data.Containers.ListUtils as List
import IHP.View.Types (PaginationView(..), styledPagination, styledPaginationPageLink, styledPaginationDotDot, styledPaginationItemsPerPageSelector, styledPaginationLinkPrevious, styledPaginationLinkNext)
renderPagination :: (?context::ControllerContext) => Pagination -> Html
pagination :: Pagination
pagination@Pagination {Int
currentPage :: Int
currentPage :: Pagination -> Int
currentPage, Int
window :: Int
window :: Pagination -> Int
window, Int
pageSize :: Int
pageSize :: Pagination -> Int
pageSize} =
Bool -> Html -> Html
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Pagination -> Bool
showPagination Pagination
pagination) (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ CSSFramework -> CSSFramework -> PaginationView -> Html
styledPagination CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework PaginationView
paginationView
where
paginationView :: PaginationView
paginationView = PaginationView
{ cssFramework :: CSSFramework
cssFramework = CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework
, pagination :: Pagination
pagination = Pagination
pagination
, pageUrl :: Int -> ByteString
pageUrl = Int -> ByteString
forall {a}.
(Show a, ?context::ControllerContext) =>
a -> ByteString
pageUrl
, linkPrevious :: Html
linkPrevious = Html
linkPrevious
, linkNext :: Html
linkNext = Html
linkNext
, pageDotDotItems :: Html
pageDotDotItems = Html
pageDotDotItems
, itemsPerPageSelector :: Html
itemsPerPageSelector = Html
itemsPerPageSelector
}
linkPrevious :: Html
linkPrevious =
CSSFramework -> CSSFramework -> Pagination -> ByteString -> Html
styledPaginationLinkPrevious CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework Pagination
pagination (Int -> ByteString
forall {a}.
(Show a, ?context::ControllerContext) =>
a -> ByteString
pageUrl (Int -> ByteString) -> Int -> ByteString
forall a b. (a -> b) -> a -> b
$ Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
linkNext :: Html
linkNext =
CSSFramework -> CSSFramework -> Pagination -> ByteString -> Html
styledPaginationLinkNext CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework Pagination
pagination (Int -> ByteString
forall {a}.
(Show a, ?context::ControllerContext) =>
a -> ByteString
pageUrl (Int -> ByteString) -> Int -> ByteString
forall a b. (a -> b) -> a -> b
$ Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
itemsPerPageSelector :: Html
itemsPerPageSelector =
CSSFramework
-> CSSFramework -> Pagination -> (Int -> ByteString) -> Html
styledPaginationItemsPerPageSelector CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework Pagination
pagination Int -> ByteString
forall {a}.
(Show a, ?context::ControllerContext) =>
a -> ByteString
itemsPerPageUrl
pageDotDotItems :: Html
pageDotDotItems = [hsx|{forEach (processedPages pages) pageDotDotItem}|]
pageDotDotItem :: PageDotDot -> Html
pageDotDotItem PageDotDot
pg =
case PageDotDot
pg of
Page Int
n ->
CSSFramework
-> CSSFramework -> Pagination -> ByteString -> Int -> Html
styledPaginationPageLink CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework Pagination
pagination (Int -> ByteString
forall {a}.
(Show a, ?context::ControllerContext) =>
a -> ByteString
pageUrl Int
n) Int
n
DotDot Int
n ->
CSSFramework -> CSSFramework -> Pagination -> Html
styledPaginationDotDot CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework CSSFramework
(?context::ControllerContext) => CSSFramework
theCSSFramework Pagination
pagination
pageUrl :: a -> ByteString
pageUrl a
n = ByteString
path ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Bool -> Query -> ByteString
Query.renderQuery Bool
True Query
newQueryString
where
path :: ByteString
path = Request -> ByteString
Wai.rawPathInfo Request
(?context::ControllerContext) => Request
theRequest
queryString :: Query
queryString = Request -> Query
Wai.queryString Request
(?context::ControllerContext) => Request
theRequest
newQueryString :: Query
newQueryString = Query
queryString
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
"page" (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ a -> Text
forall a. Show a => a -> Text
show a
n)
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (?context::ControllerContext) => Query -> Query
Query -> Query
maybeFilter
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (?context::ControllerContext) => Query -> Query
Query -> Query
maybeMaxItems
itemsPerPageUrl :: a -> ByteString
itemsPerPageUrl a
n = ByteString
path ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Bool -> Query -> ByteString
Query.renderQuery Bool
True Query
newQueryString
where
path :: ByteString
path = Request -> ByteString
Wai.rawPathInfo Request
(?context::ControllerContext) => Request
theRequest
queryString :: Query
queryString = Request -> Query
Wai.queryString Request
(?context::ControllerContext) => Request
theRequest
newQueryString :: Query
newQueryString = Query
queryString
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
"maxItems" (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ a -> Text
forall a. Show a => a -> Text
tshow a
n)
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
"page" (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Integer -> Text
forall a. Show a => a -> Text
show Integer
1)
maybeFilter :: Query -> Query
maybeFilter Query
queryString =
case forall paramType.
(?context::ControllerContext, ParamReader (Maybe paramType)) =>
ByteString -> Maybe paramType
paramOrNothing @Text ByteString
"filter" of
Maybe Text
Nothing -> Query
queryString
Just Text
"" -> Query
queryString
Just Text
filterValue -> Query
queryString Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
"filter" (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs Text
filterValue)
maybeMaxItems :: Query -> Query
maybeMaxItems Query
queryString =
case forall paramType.
(?context::ControllerContext, ParamReader (Maybe paramType)) =>
ByteString -> Maybe paramType
paramOrNothing @Int ByteString
"maxItems" of
Maybe Int
Nothing -> Query
queryString
Just Int
m -> Query
queryString Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
"maxItems" (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> Text
forall a. Show a => a -> Text
tshow Int
m)
processedPages :: [Int] -> [PageDotDot]
processedPages (Int
pg0:Int
pg1:[Int]
rest) =
if Int
pg1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
pg0 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 then
Int -> PageDotDot
Page Int
pg0 PageDotDot -> [PageDotDot] -> [PageDotDot]
forall a. a -> [a] -> [a]
: [Int] -> [PageDotDot]
processedPages (Int
pg1Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
:[Int]
rest)
else
Int -> PageDotDot
Page Int
pg0 PageDotDot -> [PageDotDot] -> [PageDotDot]
forall a. a -> [a] -> [a]
: Int -> PageDotDot
DotDot ((Int
pg1Int -> Int -> Int
forall a. Num a => a -> a -> a
+Int
pg0) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2) PageDotDot -> [PageDotDot] -> [PageDotDot]
forall a. a -> [a] -> [a]
: [Int] -> [PageDotDot]
processedPages (Int
pg1Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
:[Int]
rest)
processedPages [Int
pg] =
[Int -> PageDotDot
Page Int
pg]
processedPages [] = []
pages :: [Int]
pages =
let
totalPages :: Int
totalPages = Pagination -> Int
getLastPage Pagination
pagination
lowerBound :: Int
lowerBound
| Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
window Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 =
Int
1
| Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
window Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
totalPages =
Int
totalPages Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
window Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
| Bool
otherwise =
Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
window
upperBound :: Int
upperBound
| Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
window Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
totalPages =
Int
totalPages
| Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
window Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 =
Int
window Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2
| Bool
otherwise =
Int
currentPage Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
window
in
if Int
window Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
totalPages then
[Int
1..Pagination -> Int
getLastPage Pagination
pagination]
else
[Int] -> [Int]
List.nubInt ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ Int
1 Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: [Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
lowerBound..Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (Pagination -> Int
getLastPage Pagination
pagination) Int
upperBound] [Int] -> [Int] -> [Int]
forall a. Semigroup a => a -> a -> a
++ [Int
totalPages]
renderFilter :: (?context::ControllerContext) =>
Text
-> Html
renderFilter :: (?context::ControllerContext) => Text -> Html
renderFilter Text
placeholder =
[hsx|
<form method="GET" action="" class="mt-2 float-right">
<div class="form-row">
<div class="col-auto">
<label class="sr-only" for="inlineFormInput">Name</label>
<input type="hidden" name="page" value="1"/>
<input name="filter" type="text" class="form-control mb-2" id="inlineFormInput" placeholder={placeholder} value={boxValue}>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2 mr-2">Filter</button>
<a class="btn btn-primary mb-2" href={clearFilterUrl}>Clear</a>
</div>
</div>
</form>
|]
where
boxValue :: Text
boxValue = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" (ByteString -> Maybe Text
forall paramType.
(?context::ControllerContext, ParamReader (Maybe paramType)) =>
ByteString -> Maybe paramType
paramOrNothing ByteString
"filter") :: Text
clearFilterUrl :: ByteString
clearFilterUrl = ByteString
path ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Bool -> Query -> ByteString
Query.renderQuery Bool
True Query
newQueryString
where
path :: ByteString
path = Request -> ByteString
Wai.rawPathInfo Request
(?context::ControllerContext) => Request
theRequest
queryString :: Query
queryString = Request -> Query
Wai.queryString Request
(?context::ControllerContext) => Request
theRequest
newQueryString :: Query
newQueryString = Query
queryString
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ByteString -> Query -> Query
removeQueryItem ByteString
"filter"
setQueryValue :: ByteString -> ByteString -> Query.Query -> Query.Query
setQueryValue :: ByteString -> ByteString -> Query -> Query
setQueryValue ByteString
name ByteString
value Query
queryString =
case ByteString -> Query -> Maybe (Maybe ByteString)
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup ByteString
name Query
queryString of
Just Maybe ByteString
existingPage -> Query
queryString
Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (QueryItem -> QueryItem) -> Query -> Query
forall a b. (a -> b) -> [a] -> [b]
map (\(queryItem :: QueryItem
queryItem@(ByteString
queryItemName, Maybe ByteString
_)) -> if ByteString
queryItemName ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
name
then (ByteString
name, ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
value)
else QueryItem
queryItem
)
Maybe (Maybe ByteString)
Nothing -> Query
queryString Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> [(ByteString
name, ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
value)]
removeQueryItem :: ByteString -> Query.Query -> Query.Query
removeQueryItem :: ByteString -> Query -> Query
removeQueryItem ByteString
name Query
queryString = Query
queryString Query -> (Query -> Query) -> Query
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (QueryItem -> Bool) -> Query -> Query
forall a. (a -> Bool) -> [a] -> [a]
filter (\(ByteString
queryItemName, Maybe ByteString
_) -> ByteString
queryItemName ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= ByteString
name)
showPagination :: Pagination -> Bool
pagination :: Pagination
pagination@Pagination {Int
currentPage :: Pagination -> Int
currentPage :: Int
currentPage} =
Int
currentPage Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
1 Bool -> Bool -> Bool
|| Pagination -> Bool
hasNextPage Pagination
pagination Bool -> Bool -> Bool
|| Pagination -> Bool
hasPreviousPage Pagination
pagination