PaackEng / paack-ui / UI.Tables.Stateful

Tables are a matrixial data disposition with rows, columns, headers, and cells.

UI.Tables are type-safe, which means that every row needs to have the same number of columns (including the headers). Otherwise, compilation fails.

Stateful.table
    { toExternalMsg = Msg.ForTable
    , columns = Book.tableColumns
    , toRow = Book.toTableRow
    , state = model.tableState
    }
    |> Stateful.withResponsive
        { toDetails = Book.toTableDetails
        , toCover = Book.toTableCover
        }
    |> Stateful.withWidth (Element.fill |> Element.maximum 640)
    |> Stateful.withItems
        [ Book "Dan Brown" "Angels & Demons" "2000"
        , Book "Dan Brown" "The Da Vinci Code" "2003"
        , Book "Dan Brown" "The Lost Symbol" "2009"
        , Book "Dan Brown" "Inferno" "2013"
        , Book "Dan Brown" "Origin" "2017"
        ]
    |> Stateful.renderElement renderConfig

Where Book is:

type alias Book =
    { author : String, title : String, year : String }

tableColumns =
    columnsEmpty
        |> column "Title" (columnWidthPixels 320)
        |> column "Author" (columnWidthPixels 240)
        |> column "Year" (columnWidthPixels 120)

toTableRow =
    { toKey = .title, toTableRowView}

toTableRowView { author, title, year } =
    rowEmpty
        |> rowCellText (Text.body1 title)
        |> rowCellText (Text.body2 author)
        |> rowCellText (Text.caption year)

toTableDetails { author, title } =
    detailsEmpty
        |> detailHidden
        |> detailShown { label = "Author", content = cellFromText <| Text.body2 author }
        |> detailHidden

toTableCover { title, year } =
    { title = title, caption = Just year }

someFilters =
    filtersEmpty
        |> localSingleTextFilter Nothing .title
        |> localSingleTextFilter (Just "Dan") .author
        |> localSingleTextFilter Nothing .year

And on model:

{ -...
, tableState : Stateful.Table Msg.Msg TypeNumbers.Three
}

{ -...
, tableState = Stateful.stateWithFilters Book.someFilters Stateful.init
}

Stateful


type StatefulTable msg item columns

The StatefulTable msg item columns type is used for describing the component for later rendering.

This is type that constrains type-safe sized-arrays. See TypeNumbers for how to compose its phantom type.


type alias StatefulConfig msg item columns =
{ columns : UI.Tables.Common.Columns columns
, toRow : UI.Tables.Common.ToRow msg item columns
, toExternalMsg : Msg item -> msg
, state : State msg item columns 
}

Record with parameters for the creation of a StatefulTable.

This is record that constrains type-safe sized-arrays. See TypeNumbers for how to compose its phantom type.

{ toExternalMsg = Msg.ForTable
, columns = Book.tableColumns
, toRow = Book.toTableRow
, state = model.tableState
}

table : StatefulConfig msg item columns -> StatefulTable msg item columns

Constructs a stateful table from its columns and rows. Also defines the handling function for messages, and the current table's state.

table
    { columns = Book.tableColumns
    , toRow = Book.toTableRow
    , toExternalMsg = Msg.ForTable
    , state = model.tableState
    }

withItems : List item -> StatefulTable msg item columns -> StatefulTable msg item columns

DEPRECATED: Use stateWithItems instead. Otherwise, by using this you'll be discarding sorting and fitlering.

Each of these items will become a row in this table.

withItems
    [ Book "Dan Brown" "Angels & Demons" "2000"
    , Book "Dan Brown" "The Da Vinci Code" "2003"
    , Book "Dan Brown" "The Lost Symbol" "2009"
    , Book "Dan Brown" "Inferno" "2013"
    , Book "Dan Brown" "Origin" "2017"
    ]
    someTable

Mobile


type alias Responsive msg item columns =
{ toDetails : item -> Details msg columns
, toCover : item -> Cover 
}

Required information for displaying the mobile's layout.

This is record that constrains type-safe sized-arrays. See TypeNumbers for how to compose its phantom type.

{ toDetails = Book.toTableDetails
, toCover = Book.toTableCover
}


type alias Cover =
{ title : String
, caption : Maybe String 
}

What is displayed in a collapsed mobile's row.

{ title = "Foo Fighters - Everlong"
, caption = Just "Morumbi - São Paulo 2015-01-23"
}


type alias Details msg columns =
UI.Internal.NArray.NArray (Maybe (Detail msg)) columns

A set of Detail msg. Must have the same amount of elements as cells do in the table's row.


type alias Detail msg =
{ label : String
, content : UI.Tables.Common.Cell msg 
}

Used to render a cell in the mobile's layout.

withResponsive : Responsive msg item columns -> StatefulTable msg item columns -> StatefulTable msg item columns

Allows a table to have a responsive layout when on mobile.

withResponsive
    { toDetails = Book.toTableDetails
    , toCover = Book.toTableCover
    }
    someTable

detailsEmpty : Details msg UI.Utils.TypeNumbers.Zero

An empty Details set.

toTableDetails { author, title } =
    detailsEmpty
        |> detailHidden
        |> detailShown
            { label = "Author"
            , content = cellFromText (Text.body2 author)
            }
        |> detailHidden

detailShown : Detail msg -> Details msg columns -> Details msg (UI.Utils.TypeNumbers.Increase columns)

Defines that a cell will be shown in the mobile's layout.

detailShown
    { label = "Edit"
    , content = cellFromButton editButton
    }
    detailsSet

detailHidden : Details msg columns -> Details msg (UI.Utils.TypeNumbers.Increase columns)

Defines that a cell will be hidden in the mobile's layout.

detailsEmpty
    |> detailHidden
    |> detailHidden
    |> detailHidden

State


type State msg item columns

Keep this one in your Model, it holds the table's current state.


type Msg item

The Stateful.Msg handles stateful table's related messages.

init : State msg item columns

The correct way of instantiating a Table.State.

{ -- ...
, tableState = Stateful.init
-- ...
}

update : Msg item -> State msg item columns -> ( State msg item columns, UI.Effects.Effects msg )

Given a message, apply an update to the Table.State. Do not ignore the returned Cmd, it may include remote filter's messages.

( newModel, newCmd ) =
    Table.update subMsg oldModel.tableState

stateWithItems : List item -> State msg item columns -> State msg item columns

Each of these items will become a row in this table.

stateWithItems
    [ Book "Dan Brown" "Angels & Demons" "2000"
    , Book "Dan Brown" "The Da Vinci Code" "2003"
    , Book "Dan Brown" "The Lost Symbol" "2009"
    , Book "Dan Brown" "Inferno" "2013"
    , Book "Dan Brown" "Origin" "2017"
    ]
    someTableState

stateWithPaginator : State msg item columns -> State msg item columns

Displays an paginator at the bottom of the table.

withPaginator someTable

Filters


type alias Filters msg item columns =
UI.Internal.Tables.Filters.Filters msg item columns

Array with all the columns' filters and their initial state.

This is a type-safe sized-array. See TypeNumbers for how to compose its phantom type.

filtersEmpty : Filters msg item UI.Utils.TypeNumbers.Zero

An empty Filters set.

toTableDetails { author, title } =
    filtersEmpty
        |> localSingleTextFilter Nothing .title
        |> localSingleTextFilter (Just "Dan") .author
        |> localSingleTextFilter Nothing .year

stateWithFilters : Filters msg item columns -> State msg item columns -> State msg item columns

Apply filters defintion to a table's State.

model =
    stateWithFilters Book.filtersInit init

Single Text

localSingleTextFilter : Maybe String -> (item -> String) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter with one single text field. Only part of the content must match the filter's input. Filtering logic is applied internally by the component.

localSingleTextFilter
    maybeInitialValue
    mapItemToString

remoteSingleTextFilter : Maybe String -> (Maybe String -> msg) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter with one single text field. Only part of the content must match the filter's input. Filtering logic is applied through an external message. When Nothing is applied to the message, it means to clear the current filter.

remoteSingleTextFilter
    maybeInitialValue
    Msg.ApplyFilter

Multi Text

localMultiTextFilter : List String -> (item -> String) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter with multiple text field. The content must match at least one of those fields otherwise it's filtered out. Only part of the content must match the filter's input. Filtering logic is applied internally by the component.

localMultiTextFilter
    []
    mapItemToString

For having an initial filter applied:

localMultiTextFilter
    [ "initial", "fields" ]
    mapItemToString

remoteMultiTextFilter : List String -> (List String -> msg) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter with multiple text field. The content must match at least one of those fields otherwise it's filtered out. Only part of the content must match the filter's input.

Filtering logic is applied through an external message. When an empty list is applied to the message, it means to clear the current filter.

remoteMultiTextFilter
    [ "initial", "fields" ]
    Msg.ApplyFilter

Single DateInput

localSingleDateFilter : Time.Zone -> Maybe Time.Posix -> (item -> Time.Posix) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for dates with one single field. Filtering logic is applied internally by the component.

localSingleDateFilter timeZone
    (Just somePosixEpoch)
    mapItemToPosixEpoch

remoteSingleDateFilter : Time.Zone -> Maybe Time.Posix -> (Maybe UI.Internal.DateInput.DateInput -> msg) -> UI.Internal.Tables.Filters.Filters msg item columns -> UI.Internal.Tables.Filters.Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for dates with one single field. Filtering logic is applied through an external message. When Nothing is applied to the message, it means to clear the current filter.

remoteSingleDateFilter
    maybeInitialPosix
    Msg.ApplyFilter

Range Dates

localRangeDateFilter : Time.Zone -> Maybe Time.Posix -> Maybe Time.Posix -> (item -> Time.Posix) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for dates in an expected range. The range is defined using two date fields. Filtering logic is applied internally by the component.

localRangeDateFilter timeZone
    (Just datesAfterThis)
    (Just datesBeforeThis)
    mapItemToPosixEpoch

NOTE: Hours, minutes and seconds are discarded from the range limits.

remoteRangeDateFilter : Time.Zone -> Maybe Time.Posix -> Maybe Time.Posix -> (Maybe UI.Internal.DateInput.RangeDate -> msg) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for dates in an expected range. The range is defined using two date fields.

Filtering logic is applied through an external message. When Nothing is applied to the message, it means to clear the current filter.

remoteRangeDateFilter timeZone
    (Just datesAfterThis)
    (Just datesBeforeThis)
    Msg.ApplyFilter

NOTE: Hours, minutes and seconds are discarded from the range limits.

Period Dates

periodSingle : PeriodComparison

When comparing if dates are the same.

pariodAfter : PeriodComparison

When comparing if some date is after another.

periodBefore : PeriodComparison

When comparing if some date is before another.

localPeriodDateFilter : Time.Zone -> Maybe Time.Posix -> Maybe PeriodComparison -> (item -> Time.Posix) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for a single date, dates before specified date, or dates after specified date. The filter-case is defined using radio buttons. Filtering logic is applied internally by the component.

localPeriodDateFilter timeZone
    (Just somePosixEpoch)
    (Just periodAfter)
    mapItemToPosixEpoch

NOTE: Hours, minutes and seconds are discarded from the range limits.

remotePeriodDateFilter : Time.Zone -> Maybe Time.Posix -> Maybe PeriodComparison -> (Maybe UI.Internal.DateInput.PeriodDate -> msg) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for a single date, dates before specified date, or dates after specified date. The filter-case is defined using radio buttons.

Filtering logic is applied through an external message. When Nothing is applied to the message, it means to clear the current filter.

remotePeriodDateFilter timeZone
    (Just somePosixEpoch)
    (Just periodAfter)
    Msg.ApplyFilter

NOTE: Hours, minutes and seconds are discarded from the range limits.

Select (Radio Buttons)

localSelectFilter : List String -> Maybe Basics.Int -> (item -> Basics.Int -> Basics.Bool) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for custom radio buttons. Filtering logic is applied internally by the component.

localSelectFilter
    [ "Option 1"
    , "Option 2"
    ]
    (Just 1)
    mapItemEachOptionToBool

remoteSelectFilter : List String -> Maybe Basics.Int -> (Maybe Basics.Int -> msg) -> Filters msg item columns -> Filters msg item (UI.Utils.TypeNumbers.Increase columns)

A filter for custom radio buttons.

Filtering logic is applied through an external message. When Nothing is applied to the message, it means to clear the current filter.

remoteSelectFilter
    [ "Option 1"
    , "Option 2"
    ]
    (Just 1)
    Msg.ApplyFilter

Sorting


type alias Sorters item columns =
UI.Internal.Tables.Sorters.Sorters item columns

Array with all the columns' sorting definitions.

This is a type-safe sized-array. See TypeNumbers for how to compose its phantom type.

stateWithSorters : Sorters item columns -> State msg item columns -> State msg item columns

Apply sortings defintion to a table's State.

model =
    stateWithSorters Book.sortersInit init

sortersEmpty : Sorters item UI.Utils.TypeNumbers.Zero

An empty Sorters set.

sortersInit =
    sortersEmpty
        |> sortBy .title
        |> sortBy .author
        |> unsortable

sortBy : (item -> String) -> Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Allow sorting a column alphabetically.

sortersInit =
    sortersEmpty
        |> sortBy .title
        |> sortBy .author
        |> unsortable

sortByFloat : (item -> Basics.Float) -> Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Allow sorting a column using a Float value.

sortersInit =
    sortersEmpty
        |> unsortable
        |> sortByFloat .value
        |> sortByFloat .timestamp
        |> sortByFloat .average

sortByInt : (item -> Basics.Int) -> Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Allow sorting a column using an Integer value.

sortersInit =
    sortersEmpty
        |> unsortable
        |> sortByInt .count
        |> sortByInt .areaCode
        |> sortByInt .hour

sortByChar : (item -> Char) -> Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Allow sorting a column using a Char value.

sortersInit =
    sortersEmpty
        |> unsortable
        |> sortByChar .firstLetter

sortWith : (item -> item -> Basics.Order) -> Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Allow sorting a column with a custom function. Check List.sortWith

sortersInit =
    sortersEmpty
        |> unsortable
        |> sortWith flippedComparison

unsortable : Sorters item columns -> Sorters item (UI.Utils.TypeNumbers.Increase columns)

Describes that some column is not sortable.

sortersInit =
    sortersEmpty
        |> sortBy .title
        |> sortBy .author
        |> unsortable

sortDecreasing : Basics.Int -> Sorters item columns -> Sorters item columns

Changes the initial sorting to some columns as descreasing.

model =
    stateWithSorters
        (Book.sortersInit |> sortDecreasing 1)
        init

sortIncreasing : Basics.Int -> Sorters item columns -> Sorters item columns

Changes the initial sorting to some columns as increasing.

model =
    stateWithSorters
        (Book.sortersInit |> sortIncreasing 1)
        init

Size

withWidth : Element.Length -> StatefulTable msg item columns -> StatefulTable msg item columns

Applies Element.width to the component.

Table.withWidth
    (Element.fill |> Element.minimum 220)
    someTable

withContentWidth : Element.Length -> StatefulTable msg item columns -> StatefulTable msg item columns

Allows expanding the content width when using a scrollbar.

Table.withContentWidth
    (Element.fill |> Element.minimum 220)
    someTable

withHeight : Element.Length -> StatefulTable msg item columns -> StatefulTable msg item columns

Applies Element.height to the component.

Table.withHeight
    (Element.fill |> Element.minimum 220)
    someTable

Selection

Local

stateWithSelection : (item -> String) -> Basics.Bool -> State msg item columns -> State msg item columns

Apply selection defintion to a table's State.

model =
    stateWithSelection Book.getISBN init

stateIsSelected : item -> State msg item columns -> Basics.Bool

Resolves if one item's row is or not selected.

isHungerGamesSelected =
    Table.stateIsSelected hungerGamesBook tableState

Remote

TODO: withRemoteSelection

Rendering

renderElement : UI.RenderConfig.RenderConfig -> StatefulTable msg item columns -> Element msg

End of the builder's life. The result of this function is a ready-to-insert Elm UI's Element.