getto-systems / elm-html-table / Getto.Html.Table

construct table structure for html

import Html as H
import Html.Attributes as A

config =
  { attr =
    { table = \data ->
      [ [ "div-auto-size: none"
        , "rows: " ++ (data.thead |> String.fromInt)
        ]
        |> String.join "; "
        |> A.attribute "_fixedhead"
      ]
    , summary = [ "border-top-single" |> A.class ]
    , border = \(left,right) ->
      [ case left of
        Table.None -> []
        Table.Single -> ["border-left"]
        Table.Double -> ["border-left-double"]
      , case right of
        Table.None -> []
        Table.Single -> ["border-right"]
        Table.Double -> ["border-right-double"]
      ]
      |> List.concat |> List.map A.class
    }
  , emptyContent = Table.td []
    [ H.p [ "alert" |> A.class ]
      [ "empty!" |> H.text ]
    ]
  }

data =
  { sum = 18
  , roleLength = 2
  , genders =
    [ { value = "male"
      , sum = 2
      }
    , { value = "female"
      , sum = 3
      }
    , { value = "other"
      , sum = 1
      }
    ]
  }

rows =
  [ { id     = 2
    , name   = "John"
    , gender = "male"
    , roles = [ "admin" ]
    , comments =
      [ { user = "guest"
        , text = "looks goot to me"
        , likes =
          [ { user = "master"
            , text = "looks great to me!"
            }
          ]
        }
      ]
    }
  ]

rows |> Table.render config
  [ Table.column ( Table.none, Table.none )
    { header  = Table.th [] [ "id" |> H.text ]
    , summary = Table.empty
    , content = \row -> Table.td [ "is-center" |> A.class ]
      [ H.p [] [ row.id |> String.fromInt |> H.text ] ]
    }
  , Table.group ( Table.th [ "is-center" |> A.class ] [ "info" |> H.text ] )
    [ Table.column ( Table.none, Table.none )
      { header  = Table.th [] [ "name" |> H.text ]
      , summary = Table.th [ "is-right" |> A.class ] [ "sum" |> H.text ]
      , content = \row -> Table.td []
        [ H.p [] [ row.name |> H.text ] ]
      }
    , Table.column ( Table.none, Table.single )
      { header  = Table.th [] [ "gender" |> H.text ]
      , summary = Table.td [] [ H.p [] [ data.sum |> String.fromInt |> H.text ] ]
      , content = \row -> Table.td []
        [ H.p [] [ row.gender |> H.text ] ]
      }
    , Table.union ( Table.none, Table.none )
      { header  = Table.th [] [ "roles" |> H.text ]
      , summary = Table.empty
      , colspan = data.roleLength
      , data    = \row -> row.roles |> List.map (\role -> ( row, role ))
      , content = \(row,role) -> Table.td []
        [ H.p [] [ role |> H.text ] ]
      }
    , Table.parts data.genders
      (\gender ->
        [ Table.column ( Table.none, Table.none )
          { header  = Table.th [] [ gender.value |> H.text ]
          , summary = Table.td [ "is-center" |> A.class ]
            [ H.p [] [ gender.sum |> String.fromInt |> H.text ] ]
          , content = \row -> Table.td [ "is-center" |> A.class ]
            [ H.p []
              [ if row.gender == gender.value
                then "v" |> H.text
                else ""  |> H.text
              ]
            ]
          }
        , Table.column ( Table.none, Table.none )
          { header  = Table.th [] [ gender.value |> H.text ]
          , summary = Table.td [ "is-center" |> A.class ]
            [ H.p [] [ gender.sum |> String.fromInt |> H.text ] ]
          , content = \row -> Table.td [ "is-center" |> A.class ]
            [ H.p []
              [ if row.gender /= gender.value
                then "o" |> H.text
                else ""  |> H.text
              ]
            ]
          }
        ]
      )
    , Table.rows ( \row -> row.roles |> List.map (\role -> ( row, role )) )
      [ Table.column ( Table.none, Table.none )
        { header  = Table.th [] [ "roles" |> H.text ]
        , summary = Table.empty
        , content = \(row,role) -> Table.td []
          [ H.p [] [ role |> H.text ] ]
        }
      , Table.column ( Table.none, Table.none )
        { header  = Table.th [] [ "roles" |> H.text ]
        , summary = Table.empty
        , content = \(row,role) -> Table.td []
          [ H.p [] [ role |> H.text ] ]
        }
      ]
    , Table.group ( Table.th [ "is-center" |> A.class ] [ "comment" |> H.text ] )
      [ Table.rows ( \row -> row.comments |> List.map (\comment -> ( row, comment )) )
        [ Table.column ( Table.single, Table.none )
          { header  = Table.th [] [ "user" |> H.text ]
          , summary = Table.empty
          , content = \(row,comment) -> Table.td []
            [ H.p [] [ comment.user |> H.text ] ]
          }
        , Table.column ( Table.none, Table.none )
          { header  = Table.th [] [ "text" |> H.text ]
          , summary = Table.empty
          , content = \(row,comment) -> Table.td []
            [ H.p [] [ comment.text |> H.text ] ]
          }
        , Table.group ( Table.th [ "is-center" |> A.class ] [ "like" |> H.text ] )
          [ Table.rows
            ( \(row,comment) -> comment.likes |> List.map (\like -> ( row, comment, like )) )
            [ Table.column ( Table.single, Table.none )
              { header  = Table.th [] [ "user" |> H.text ]
              , summary = Table.empty
              , content = \(row,comment,like) -> Table.td []
                [ H.p [] [ like.user |> H.text ] ]
              }
            , Table.column ( Table.none, Table.none )
              { header  = Table.th [] [ "text" |> H.text ]
              , summary = Table.empty
              , content = \(row,comment,like) -> Table.td []
                [ H.p [] [ like.text |> H.text ] ]
              }
            ]
          ]
        ]
      ]
    ]
  ]

Definition


type alias Config msg =
{ attr : { table : { thead : Basics.Int } -> List (Html.Attribute msg)
, summary : List (Html.Attribute msg)
, border : BorderAttribute msg }
, emptyContent : Struct.Cell (Cell msg) 
}

configuration for rendering

  { attr =
      -- attribute of <table> tag
    { table = \data ->
      [ [ "div-auto-size: none"
        , "rows: " ++ (data.thead |> String.fromInt)
        ]
        |> String.join "; "
        |> A.attribute "_fixedhead"
      ]
      -- attribute of summary <tr> tag
    , summary = [ "border-top-single" |> A.class ]
      -- border attribute of <td> or <th> tag
    , border = \(left,right) ->
      [ case left of
        Table.None -> []
        Table.Single -> ["border-left"]
        Table.Double -> ["border-left-double"]
      , case right of
        Table.None -> []
        Table.Single -> ["border-right"]
        Table.Double -> ["border-right-double"]
      ]
      |> List.concat |> List.map A.class
    }
    -- <td> content when empty rows
  , emptyContent = Table.td []
    [ H.p [ "alert" |> A.class ]
      [ "empty!" |> H.text ]
    ]
  }


type alias Border =
Struct.Border

left border and right border

( BorderStyle, BorderStyle ) -- left, right


type BorderStyle
    = None
    | Single
    | Double

border style


type alias Column row msg =
Struct.Column row (Cell msg)

column definition


type alias ColumnModel row msg =
Struct.ColumnModel row (Cell msg)

config for column

{ header  : Cell msg        -- header cell
, summary : Cell msg        -- summary cell
, content : row -> Cell msg -- content cell
}


type alias UnionModel row data msg =
Struct.UnionModel row data (Cell msg)

config for union

{ header  : Cell msg         -- header cell
, summary : Cell msg         -- summary cell
, colspan : Int              -- colspan
, data    : row -> List data -- list of sub data
, content : data -> Cell msg -- content cell
}

Render

render : Config msg -> List (Column row msg) -> List row -> Html msg

render html by Config, Columns, data

Column Construction

column : Border -> ColumnModel row msg -> Column row msg

define column

Table.column
  ( Table.none, Table.none )       -- border ( left, right )
  { header  =
    Table.th [] [ "id" |> H.text ] -- header cell
  , summary = Table.empty          -- summary cell
  , content =                      -- content cell
    \row ->
      Table.td [ "is-center" |> A.class ]
        [ H.p [] [ row.id |> String.fromInt |> H.text ] ]
  }

group : Struct.Cell (Cell msg) -> List (Column row msg) -> Column row msg

define group

Table.group
  ( Table.th [ "is-center" |> A.class ] -- header cell
    [ "info" |> H.text ]
  )
  [ Table.column                        -- group columns
    ( Table.none, Table.none )
    { header  = Table.th [] [ "name" |> H.text ]
    , summary = Table.th [ "is-right" |> A.class ] [ "sum" |> H.text ]
    , content = \row -> Table.td []
      [ H.p [] [ row.name |> H.text ] ]
    }
  ]

union : Border -> UnionModel row data msg -> Column row msg

define union

Table.union
  ( Table.none, Table.none )          -- border ( left, right )
  { header  =
    Table.th [] [ "roles" |> H.text ] -- header cell
  , summary = Table.empty             -- summary cell
  , colspan = data.roleLength         -- colspan
  , data    =                         -- list of sub data
    \row -> row.roles |> List.map (\role -> ( row, role ))
  , content =                         -- content cell
    \(row,role) ->
      Table.td []
        [ H.p [] [ role |> H.text ] ]
  }

parts : List data -> (data -> List (Column row msg)) -> Column row msg

define parts

Table.parts
  data.genders -- list of sub data
  (\gender ->  -- sub data columns
    [ Table.column ( Table.none, Table.none )
      { header  = Table.th [] [ gender.value |> H.text ]
      , summary = Table.td [ "is-center" |> A.class ]
        [ H.p [] [ gender.sum |> String.fromInt |> H.text ] ]
      , content = \row -> Table.td [ "is-center" |> A.class ]
        [ H.p []
          [ if row.gender == gender.value
            then "v" |> H.text
            else ""  |> H.text
          ]
        ]
      }
    ]
  )

rows : (row -> List data) -> List (Column data msg) -> Column row msg

define rows

Table.rows
  ( \row -> -- list of sub data
    row.roles |> List.map (\role -> ( row, role ))
  )
  [ Table.column -- sub data columns
    ( Table.none, Table.none )
    { header  = Table.th [] [ "roles" |> H.text ]
    , summary = Table.empty
    , content = \(row,role) -> Table.td []
      [ H.p [] [ role |> H.text ] ]
    }
  ]

Border Construction

none : Struct.BorderStyle

border style None

single : Struct.BorderStyle

border style Single

double : Struct.BorderStyle

border style Double

Cell Construction

empty : Struct.Cell (Cell msg)

empty Cell

summary = Table.empty -- nothing for summary

th : List (Html.Attribute msg) -> List (Html msg) -> Struct.Cell (Cell msg)

th Cell

Table.th []          -- attributes
  [ "id" |> H.text ] -- contents

td : List (Html.Attribute msg) -> List (Html msg) -> Struct.Cell (Cell msg)

td Cell

Table.td [] -- attributes
  [ H.p []  -- content
    [ "content" |> H.text ]
  ]

Helper

map : (msg -> super) -> Column row msg -> Column row super

map Column

type Msg
  = Sub SubMsg

column : model -> Table.Column row SubMsg

model |> column |> Table.map Sub