Skip to content

Instantly share code, notes, and snippets.

@chancyk
Last active May 23, 2022 19:16
Show Gist options
  • Save chancyk/a4b458613bc092282f7c6783eb435c28 to your computer and use it in GitHub Desktop.
Save chancyk/a4b458613bc092282f7c6783eb435c28 to your computer and use it in GitHub Desktop.
Simple Sortable Table in Elm
import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing ( onClick )
import Array exposing ( Array )
-- Copied from elm-community/list-extra to remove the 3rd party
-- dependency so that the code can be pasted to: http://elm-lang.org/try
-- Otherwise `elemIndex` can be imported with:
-- import List.Extra exposing ( elemIndex )
--------------------------------------------------------------------
elemIndex : a -> List a -> Maybe Int
elemIndex x = findIndex ((==)x)
findIndex : (a -> Bool) -> List a -> Maybe Int
findIndex p = List.head << findIndices p
findIndices : (a -> Bool) -> List a -> List Int
findIndices p =
List.map fst << List.filter (\(i,x) -> p x) << List.indexedMap (,)
--------------------------------------------------------------------
main =
Html.beginnerProgram
{ model = init
, view = view
, update = update
}
-- MODEL
type SortOrder
= NoSort
| Asc
| Desc
type alias Model =
{ sortOrders : Array SortOrder
, tableHeaders : List String
, tableData : List (Array String)
}
init : Model
init =
{ sortOrders = Array.fromList [ NoSort, NoSort, NoSort ]
, tableHeaders = [ "first", "second", "third" ]
, tableData =
[ Array.fromList [ "hello3", "world", "1 another column" ]
, Array.fromList [ "hello2", "second", "2 another column" ]
, Array.fromList [ "hello1", "third", "3 another column" ]
]
}
-- UPDATE
type Msg
= SortBy String
update : Msg -> Model -> Model
update msg model =
case msg of
SortBy columnName ->
sortBy model columnName
-- Insert the new sort order into `model.sortOrders` and sort the data.
sortTable model columnIndex sortOrder sortFunc =
{ sortOrders = Array.set columnIndex sortOrder model.sortOrders
, tableHeaders = model.tableHeaders
, tableData = List.sortWith sortFunc model.tableData
}
sortAscByIndex columnIndex rowOne rowTwo =
case (Array.get columnIndex rowOne, Array.get columnIndex rowTwo) of
(Nothing, Nothing) -> EQ
(Just _, Nothing) -> GT
(Nothing, Just _) -> LT
(Just o, Just t) -> compare o t
-- Flip the result of sortAscByIndex.
sortDescByIndex columnIndex rowOne rowTwo =
case sortAscByIndex columnIndex rowOne rowTwo of
GT -> LT
LT -> GT
EQ -> EQ
sortBy : Model -> String -> Model
sortBy model columnName =
let
-- Grab the array index of the provided column name.
columnIndex =
case elemIndex columnName model.tableHeaders of
Nothing -> 0
Just val -> val
-- Grab the current sort order for the specified column.
sortOrder =
case Array.get columnIndex model.sortOrders of
Nothing -> Asc
Just order ->
case order of
NoSort -> Asc
Desc -> Asc
Asc -> Desc
-- All columns start unsorted as NoSort, so the first click will switch
-- the column to an ascending sort order. We will curry the sort function
-- with the `columnIndex` of the clicked header.
sortFunc =
case sortOrder of
NoSort -> sortAscByIndex columnIndex
Asc -> sortAscByIndex columnIndex
Desc -> sortDescByIndex columnIndex
in
sortTable model columnIndex sortOrder sortFunc
-- VIEW
tableCellStyle =
style
[ ("width", "100px")
, ("margin", "1px")
, ("float", "left")
, ("text-align", "center")
, ("padding", "2px")
, ("text-overflow", "ellipsis")
, ("white-space", "nowrap")
, ("overflow", "hidden")
]
tableHeadStyle =
style
[ ("font-weight", "bold")
, ("text-decoration", "underline")
, ("cursor", "pointer")
]
tableStyle =
style
[ ("display", "inline-block")
, ("position", "absolute")
, ("left", "100px")
, ("top", "50px")
, ("border", "1px solid black")
, ("overflow", "auto")
]
rowStyle =
style
[ ("border-bottom", "1px solid #CCC")
, ("float", "left")
, ("clear", "both")
]
headerCell cellText =
div
[ tableCellStyle
, tableHeadStyle
, onClick (SortBy cellText)
]
[ text cellText ]
cell : String -> Html a
cell cellText =
div [ tableCellStyle ] [ text cellText ]
header : List String -> Html Msg
header row_values =
div [ rowStyle ] ( List.map headerCell row_values )
row : Array String -> Html a
row row_values =
div [ rowStyle ] (Array.toList ( Array.map cell row_values ))
table : List String -> List (Array String) -> Html Msg
table table_header table_data =
div
[ tableStyle ]
( header table_header :: List.map row table_data )
view : Model -> Html Msg
view model =
table model.tableHeaders model.tableData
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment