Variant of Table for paginated remote data that is sorted before being given to the view. The Sorter type is only used for visually representing the sort state of the table data that is sorted elsewhere (e.g. sorted paginated data from a server).
This library helps you create sortable tables. The crucial feature is that it lets you own your data separately and keep it in whatever format is best for you. This way you are free to change your data without worrying about the table “getting out of sync” with the data. Having a single source of truth is pretty great!
We recommend checking out the examples to get a feel for how it works.
view : Config data msg -> State -> List data -> Html msg
Take a list of data and turn it into a table. The Config
argument is the
configuration for the table. It describes the columns that we want to show. The
State
argument describes which column we are sorting by at the moment.
Note: The State
and List data
should live in your Model
. The Config
for the table belongs in your view
code. I very strongly recommend against
putting Config
in your model. Describe any potential table configurations
statically, and look for a different library if you need something crazier than
that.
config : { toId : data -> String, toMsg : State -> msg, columns : List (Column data msg) } -> Config data msg
Create the Config
for your view
function. Everything you need to
render your columns efficiently and handle selection of columns.
Say we have a List Person
that we want to show as a table. The table should
have a column for name and age. We would create a Config
like this:
import Table
type Msg = NewTableState State | ...
config : Table.Config Person Msg
config =
Table.config
{ toId = .name
, toMsg = NewTableState
, columns =
[ Table.stringColumn "Name" .name
, Table.intColumn "Age" .age
]
}
You provide the following information in your table configuration:
toId
— turn a Person
into a unique ID. This lets us use
Html.Keyed
under the hood to make resorts faster.columns
— specify some columns to show.toMsg
— a way to send new table states to your app as messages.See the examples to get a better feel for this!
stringColumn : String -> String -> (data -> String) -> Column data msg
intColumn : String -> String -> (data -> Basics.Int) -> Column data msg
floatColumn : String -> String -> (data -> Basics.Float) -> Column data msg
dateColumn : String -> DateColumnConfig data -> Column data msg
posixColumn : String -> PosixColumnConfig data -> Column data msg
Tracks pagination state of table.
initialState : String -> Basics.Int -> State
Create a table state. By providing a column name, you determine which column should be used for sorting, defaulting to an ascending sort. So if you want your table of yachts to be sorted by length by default, you might say:
import Table
Table.initialState "Length" pageSize
initialStateDirected : String -> Basics.Bool -> Basics.Int -> State
Create a table state with the ability to specify the direction of sort on the specified column in the form of an isReversed Bool. For a descending sort on your table of yachts by length, use
import Table
Table.initialStateDirected "Length" True pageSize
getSortColumn : State -> String
Get the column currently sorted by.
getCurrentPage : State -> Basics.Int
Get the current page number.
setCurrentPage : Basics.Int -> State -> State
Set the current page number.
Useful for setting the page number base on URL query parameters.
getPageSize : State -> Basics.Int
Get the page size (i.e. the max number of items shown in the table at one time).
setPageSize : Basics.Int -> State -> State
Set the page size.
setTotal : Basics.Int -> State -> State
Set the total number of items that can be viewed in the table.
You will probably find this out when you do the first request for data and this value maybe change on subsequent requests.
E.g. if there are 20 rows of data and the page size is 5, the total parameter is 20 (while the number of pages will be 4)
getPageCount : State -> Basics.Int
Get the page count (i.e. the total number of pages that can be displayed).
getIsReversed : State -> Basics.Bool
Get whether the current sortable column is set to be sorted in ascending or descending order.
nextPage : State -> State
Move to the next page. Do nothing if the currentPage is the last page.
previousPage : State -> State
Move to the previous page. Do nothing if the currentPage is the first page.
Sort Order data type corresponding to SQL order by.
sortOrder : Config data msg -> State -> Maybe SortOrder
Get the sort order for the table. Used to request sorting of remote data.
If you are new to this library, you can probably stop reading here. After this point are a bunch of ways to customize your table further. If it does not provide what you need, you may just want to write a custom table yourself. It is not that crazy.
Describes how to turn data
into a column in your table.
customColumn : { id : String, name : String, viewData : data -> String, sorter : Sorter } -> Column data msg
Perhaps the basic columns are not quite what you want. Maybe you want to
display monetary values in thousands of dollars, and floatColumn
does not
quite cut it. You could define a custom column like this:
import Table
dollarColumn : String -> (data -> Float) -> Column data msg
dollarColumn name toDollars =
Table.customColumn
{ name = name
, viewData = \data -> viewDollars (toDollars data)
, sorter = Table.decreasingBy toDollars
}
viewDollars : Float -> String
viewDollars dollars =
"$" ++ String.fromInt (round (dollars / 1000)) ++ "k"
The viewData
field means we will displays the number 12345.67
as $12k
.
The sorter
field specifies how the column can be sorted. In dollarColumn
we
are saying that it can only be shown from highest-to-lowest monetary value.
More about sorters soon!
veryCustomColumn : { id : String, name : String, viewData : data -> HtmlDetails msg, sorter : Sorter } -> Column data msg
It is possible that you want something crazier than customColumn
. In
that unlikely scenario, this function lets you have full control over the
attributes and children of each <td>
cell in this column.
So maybe you want to a dollars column, and the dollar signs should be green.
import Html exposing (Attribute, Html, span, text)
import Html.Attributes exposing (style)
import Table
dollarColumn : String -> (data -> Float) -> Column data msg
dollarColumn name toDollars =
Table.veryCustomColumn
{ name = name
, viewData = \data -> viewDollars (toDollars data)
, sorter = Table.decreasingBy toDollars
}
viewDollars : Float -> Table.HtmlDetails msg
viewDollars dollars =
Table.HtmlDetails []
[ span [ style "color" "green" ] [ text "$" ]
, text (String.fromInt (round (dollars / 1000)) ++ "k")
]
{ name : String
, toIsoDate : data -> String
, default : String
, formatString : String
}
{ name : String
, toPosix : data -> Maybe Time.Posix
, default : String
, timeZone : Time.Zone
, formatString : String
}
Specifies a particular way of sorting data.
unsortable : Sorter
A sorter for columns that are unsortable. Maybe you have a column in your table for delete buttons that delete the row. It would not make any sense to sort based on that column.
increasingBy : Sorter
Create a sorter that can only display the data in increasing order. If we want a table of people, sorted alphabetically by name, we would say this:
decreasingBy : Sorter
Create a sorter that can only display the data in decreasing order. If we want a table of countries, sorted by population from highest to lowest, we would say this:
increasingOrDecreasingBy : Sorter
Sometimes you want to be able to sort data in increasing or decreasing order. Maybe you have race times for the 100 meter sprint. This function lets sort by best time by default, but also see the other order.
decreasingOrIncreasingBy : Sorter
Sometimes you want to be able to sort data in increasing or decreasing order. Maybe you have a bunch of data about orange juice, and you want to know both which has the most sugar, and which has the least sugar. Both interesting! This function lets you see both, starting with decreasing order.
Configuration for your table, describing your columns.
Note: Your Config
should never be held in your model.
It should only appear in view
code.
customConfig : { toId : data -> String, toMsg : State -> msg, columns : List (Column data msg), customizations : Customizations data msg } -> Config data msg
Just like config
but you can specify a bunch of table customizations.
{ tableAttrs : List (Html.Attribute msg)
, caption : Maybe (HtmlDetails msg)
, thead : List ( String
, Status
, Html.Attribute msg ) -> HtmlDetails msg
, tfoot : Maybe (HtmlDetails msg)
, tbodyAttrs : List (Html.Attribute msg)
, rowAttrs : data -> List (Html.Attribute msg)
, pagination : (State -> msg) -> State -> Html msg
}
There are quite a lot of ways to customize the <table>
tag. You can add
a <caption>
which can be styled via CSS. You can do crazy stuff with
<thead>
to group columns in weird ways. You can have a <tfoot>
tag for
summaries of various columns. And maybe you want to put attributes on <tbody>
or on particular rows in the body. All these customizations are available to you.
Note: The level of craziness possible in <thead>
and <tfoot>
are so
high that I could not see how to provide the full functionality and make it
impossible to do bad stuff. So just be aware of that, and share any stories
you have. Stories make it possible to design better!
{ attributes : List (Html.Attribute msg)
, children : List (Html msg)
}
Sometimes you must use a <td>
tag, but the attributes and children are up
to you. This type lets you specify all the details of an HTML node except the
tag name.
htmlDetails : Html msg -> HtmlDetails msg
The status of a particular column, for use in the thead
field of your
Customizations
.
Unsortable
.Sortable
.
The associated boolean represents whether this column is selected. So it is
True
if the table is currently sorted by this column, and False
otherwise.Reversible
.
The associated maybe tells you whether this column is selected. It is
Just isReversed
if the table is currently sorted by this column, and
Nothing
otherwise. The isReversed
boolean lets you know which way it
is sorted.This information lets you do custom header decorations for each scenario.
defaultCustomizations : Customizations data msg
The customizations used in config
by default.
Pagination controls are not included as there is not a standard way to layout these components. See Table.Bulma for an configuration of pagination controls using the bulma UI framework.
{ windowSize : Basics.Int
, minPages : Basics.Int
}
Render settings.
renderPageButton : RenderSettings -> State -> Basics.Int -> Basics.Bool
Check if a page button should rendered.