terry-bit-io/elm-paginate

Simple and robust pagination in elm


License
BSD-3-Clause
Install
elm-package install terry-bit-io/elm-paginate 1.0.0

Documentation

elm-pagination

Simple and robust pagination in elm.

Encapsulate your pagination concerns from the rest of your app.

Example usage

Below is a fully featured example (demo | source).

module Example exposing (main)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Paginate exposing (..)


type alias Model =
    { things : PaginatedList String
    , reversed : Bool
    , query : String
    , globalId : Int
    }


type Msg
    = Next
    | Prev
    | First
    | Last
    | GoTo Int
    | ChangePageSize String
    | DeleteItem String
    | AddItem
    | Reverse
    | Find String


main : Program () Model Msg
main =
    Browser.sandbox
        { init = init
        , view = filterAndSortThings >> view
        , update = update
        }


init : Model
init =
    let
        things =
            List.map (String.fromInt >> (++) "item ") <| List.range 1 37
    in
    { things = Paginate.fromList 10 things
    , reversed = False
    , query = ""
    , globalId = List.length things
    }


update : Msg -> Model -> Model
update msg model =
    case msg of
        GoTo index ->
            { model | things = Paginate.goTo index model.things }

        Next ->
            { model | things = Paginate.next model.things }

        Prev ->
            { model | things = Paginate.prev model.things }

        First ->
            { model | things = Paginate.first model.things }

        Last ->
            { model | things = Paginate.last model.things }

        ChangePageSize size ->
            let
                sizeAsInt =
                    Maybe.withDefault 10 <| String.toInt size
            in
            { model | things = Paginate.changeItemsPerPage sizeAsInt model.things }

        DeleteItem item ->
            let
                removeItem =
                    List.filter ((/=) item)
            in
            { model | things = Paginate.map removeItem model.things }

        AddItem ->
            let
                newId =
                    model.globalId + 1

                addItem existing =
                    existing ++ [ "new item " ++ String.fromInt newId ]
            in
            { model
                | things = Paginate.map addItem model.things
                , globalId = newId
            }

        Reverse ->
            { model | reversed = not model.reversed }

        Find query ->
            { model | query = query }


filterAndSortThings : Model -> PaginatedList String
filterAndSortThings model =
    let
        sort =
            if model.reversed then
                List.reverse

            else
                identity

        filter =
            if model.query == "" then
                identity

            else
                List.filter (\thing -> String.contains model.query thing)
    in
    Paginate.map (filter >> sort) model.things


view : PaginatedList String -> Html Msg
view filteredSortedThings =
    let
        displayInfoView =
            div []
                [ div []
                    [ text <|
                        String.join " " <|
                            [ "showing"
                            , String.fromInt <| List.length <| Paginate.page filteredSortedThings
                            , "of"
                            , String.fromInt <| Paginate.length filteredSortedThings
                            , "items"
                            ]
                    , u [ onClick <| AddItem, style "cursor" "pointer" ] [ text " (add more!)" ]
                    ]
                , text <|
                    String.join " "
                        [ "page"
                        , String.fromInt <| Paginate.currentPage filteredSortedThings
                        , "of"
                        , String.fromInt <| Paginate.totalPages filteredSortedThings
                        ]
                , div []
                    [ text <|
                        String.join " "
                            [ "including"
                            , Paginate.foldMap
                                (List.filter (String.contains "new item") >> List.length >> String.fromInt)
                                filteredSortedThings
                            , "new items"
                            ]
                    ]
                ]

        itemView item =
            li []
                [ span [] [ text item ]
                , u [ onClick <| DeleteItem item, style "cursor" "pointer" ] [ text " (delete)" ]
                ]

        itemsPerPageSelector =
            div []
                [ text "show"
                , select [ onInput ChangePageSize ]
                    [ option [ value "3" ] [ text "3" ]
                    , option [ value "10", selected True ] [ text "10" ]
                    , option [ value "20" ] [ text "20" ]
                    , option [ value "30" ] [ text "30" ]
                    ]
                , text "items per page"
                ]

        prevButtons =
            [ button [ onClick First, disabled <| Paginate.isFirst filteredSortedThings ] [ text "<<" ]
            , button [ onClick Prev, disabled <| Paginate.isFirst filteredSortedThings ] [ text "<" ]
            ]

        nextButtons =
            [ button [ onClick Next, disabled <| Paginate.isLast filteredSortedThings ] [ text ">" ]
            , button [ onClick Last, disabled <| Paginate.isLast filteredSortedThings ] [ text ">>" ]
            ]

        pagerButtonView index isActive =
            button
                [ style "font-weight"
                    (if isActive then
                        "bold"

                     else
                        "normal"
                    )
                , onClick <| GoTo index
                ]
                [ text <| String.fromInt index ]

        pagerOptions =
            { innerWindow = 1
            , outerWindow = 1
            , pageNumberView = pagerButtonView
            , gapView = text "..."
            }
    in
    div [] <|
        [ displayInfoView
        , itemsPerPageSelector
        , button [ onClick Reverse ] [ text "Reverse list" ]
        , input [ placeholder "Search...", onInput Find ] []
        , ul [] (List.map itemView <| Paginate.page filteredSortedThings)
        ]
            ++ prevButtons
            ++ [ span [] <| Paginate.pager pagerButtonView filteredSortedThings ]
            ++ nextButtons
            ++ [ p [] [ text "Elidied pager (set items per page to 3 to see it elide)" ]
               , span [] <| Paginate.elidedPager pagerOptions filteredSortedThings
               ]