Last active
May 23, 2022 19:16
-
-
Save chancyk/a4b458613bc092282f7c6783eb435c28 to your computer and use it in GitHub Desktop.
Simple Sortable Table in Elm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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