Table.purs 8.93 KB
Newer Older
Nicolas Pouillard's avatar
Nicolas Pouillard committed
1 2
module Gargantext.Components.Table where

3
import Prelude
Nicolas Pouillard's avatar
Nicolas Pouillard committed
4
import Data.Array (filter)
5 6
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
7
import Data.Maybe (Maybe(..))
8
import Data.Tuple (fst, snd)
9
import Data.Tuple.Nested ((/\))
10
import DOM.Simple.Console (log2)
Nicolas Pouillard's avatar
Nicolas Pouillard committed
11
import Effect (Effect)
12 13
import Reactix as R
import Reactix.DOM.HTML as H
14
import Gargantext.Utils.Reactix as R2
Nicolas Pouillard's avatar
Nicolas Pouillard committed
15

16
type TableContainerProps =
17 18 19 20 21 22
  ( pageSizeControl     :: R.Element
  , pageSizeDescription :: R.Element
  , paginationLinks     :: R.Element
  , tableHead           :: R.Element
  , tableBody           :: Array R.Element
  )
23

24 25
type Row = { row :: Array R.Element, delete :: Boolean }
type Rows = Array Row
Nicolas Pouillard's avatar
Nicolas Pouillard committed
26

27 28
type OrderBy = Maybe (OrderByDirection ColumnName)

29
type Params = { offset :: Int, limit :: Int, orderBy :: OrderBy }
30 31 32

newtype ColumnName = ColumnName String

33 34 35 36 37
derive instance genericColumnName :: Generic ColumnName _

instance showColumnName :: Show ColumnName where
  show = genericShow

38 39 40 41 42 43
derive instance eqColumnName :: Eq ColumnName

columnName :: ColumnName -> String
columnName (ColumnName c) = c

data OrderByDirection a = ASC a | DESC a
Nicolas Pouillard's avatar
Nicolas Pouillard committed
44

45 46 47 48 49
derive instance genericOrderByDirection :: Generic (OrderByDirection a) _

instance showOrderByDirection :: Show a => Show (OrderByDirection a) where
  show = genericShow

50 51
derive instance eqOrderByDirection :: Eq a => Eq (OrderByDirection a)

52
type Props =
53
  ( colNames     :: Array ColumnName
Nicolas Pouillard's avatar
Nicolas Pouillard committed
54
  , totalRecords :: Int
55
  , params       :: R.State Params
56
  , rows         :: Rows
57
  , container    :: Record TableContainerProps -> R.Element
Nicolas Pouillard's avatar
Nicolas Pouillard committed
58 59 60
  )

type State =
61 62 63
  { page     :: Int
  , pageSize :: PageSizes
  , orderBy  :: OrderBy
Nicolas Pouillard's avatar
Nicolas Pouillard committed
64 65
  }

66 67 68 69 70 71 72 73 74 75 76 77 78
stateParams :: State -> Params
stateParams {pageSize, page, orderBy} = {offset, limit, orderBy}
  where
    limit = pageSizes2Int pageSize
    offset = limit * (page - 1)

type TableHeaderLayoutProps =
  ( title :: String
  , desc  :: String
  , query :: String
  , date  :: String
  , user  :: String
  )
Nicolas Pouillard's avatar
Nicolas Pouillard committed
79

80
initialParams :: Params
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
initialParams = stateParams {page: 1, pageSize: PS10, orderBy: Nothing}
-- TODO: Not sure this is the right place for this

tableHeaderLayout :: Record TableHeaderLayoutProps -> R.Element
tableHeaderLayout props = R.createElement tableHeaderLayoutCpt props []

tableHeaderLayoutCpt :: R.Component TableHeaderLayoutProps
tableHeaderLayoutCpt = R.staticComponent "TableHeaderLayout" cpt
  where
    cpt {title, desc, query, date, user} _ =
      R.fragment
      [ H.div {className: "row"}
        [ H.div {className: "col-md-3"} [ H.h3 {} [H.text title] ]
        , H.div {className: "col-md-9"}
          [ H.hr {style: {height: "2px", backgroundColor: "black"}} ]
        ]
      , H.div {className: "row"}
        [ H.div {className: "jumbotron1", style: {padding: "12px 0px 20px 12px"}}
          [ H.div {className: "col-md-8 content"}
            [ H.p {}
              [ H.i {className: "glyphicon glyphicon-globe"} []
              , H.text $ " " <> desc
          ]
            , H.p {}
              [ H.i {className: "glyphicon glyphicon-zoom-in"} []
              , H.text $ " " <> query
107
              ]
108 109 110 111 112
            ]
          , H.div {className: "col-md-4 content"}
            [ H.p {}
              [ H.i {className: "glyphicon glyphicon-calendar"} []
              , H.text $ " " <> date
113
              ]
114 115 116 117 118 119
            , H.p {}
              [ H.i {className: "glyphicon glyphicon-user"} []
              , H.text $ " " <> user
              ]
            ]
          ]
120
        ]
121 122 123 124
      ]
  
table :: Record Props -> R.Element
table props = R.createElement tableCpt props []
125

126 127
tableCpt :: R.Component Props
tableCpt = R.hooksComponent "Table" cpt
Nicolas Pouillard's avatar
Nicolas Pouillard committed
128
  where
129
    cpt {container, colNames, totalRecords, rows, params} _ = do
130
      pageSize@(pageSize' /\ setPageSize) <- R.useState' PS10
131 132
      (page /\ setPage) <- R.useState' 1
      (orderBy /\ setOrderBy) <- R.useState' Nothing
133 134
      let state = {pageSize: pageSize', orderBy, page}
      let ps = pageSizes2Int pageSize'
135
      let totalPages = (totalRecords / ps) + min 1 (totalRecords `mod` ps)
136
      R.useEffect1' state $ when (fst params /= stateParams state) $ (snd params) (const $ stateParams state)
137
      pure $ container
138 139
        { pageSizeControl: sizeDD pageSize
        , pageSizeDescription: textDescription page pageSize' totalRecords
140 141 142 143
        , paginationLinks: pagination setPage totalPages page
        , tableHead: H.tr {} (colHeader setOrderBy orderBy <$> colNames)
        , tableBody: map (H.tr {} <<< map (\c -> H.td {} [c]) <<< _.row) rows
        }
144 145 146 147 148 149 150 151 152 153 154
        where
          colHeader :: (R2.Setter OrderBy) -> OrderBy -> ColumnName -> R.Element
          colHeader setOrderBy orderBy c = H.th {scope: "col"} [ H.b {} cs ]
            where
              lnk mc = effectLink (setOrderBy (const mc))
              cs :: Array R.Element
              cs =
                case orderBy of
                  Just (ASC d)  | c == d -> [lnk (Just (DESC c)) "DESC ",  lnk Nothing (columnName c)]
                  Just (DESC d) | c == d -> [lnk (Just (ASC  c)) "ASC ", lnk Nothing (columnName c)]
                  _ -> [lnk (Just (ASC c)) (columnName c)]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
155

156 157 158 159 160 161
defaultContainer :: {title :: String} -> Record TableContainerProps -> R.Element
defaultContainer {title} props = R.fragment
  [ H.div {className: "row"}
    [ H.div {className: "col-md-4"} [ props.pageSizeDescription ]
    , H.div {className: "col-md-4"} [ props.paginationLinks ]
    , H.div {className: "col-md-4"} [ props.pageSizeControl ]
162
    ]
163 164 165
  , H.table {className: "table"}
    [ H.thead {className: "thead-dark"} [ props.tableHead ]
    , H.tbody {} props.tableBody
166 167
    ]
  ]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
168

169
-- TODO: this needs to be in Gargantext.Pages.Corpus.Graph.Tabs
170
graphContainer :: {title :: String} -> Record TableContainerProps -> R.Element
171
graphContainer {title} props =
172 173 174 175 176
  -- TODO title in tabs name (above)
  H.table {className: "table"}
  [ H.thead {className: "thead-dark"} [ props.tableHead ]
  , H.tbody {} props.tableBody
  ]
177 178 179 180
   -- TODO better rendering of the paginationLinks
   -- , props.pageSizeControl
   -- , props.pageSizeDescription
   -- , props.paginationLinks
181

182 183 184
sizeDD :: R.State PageSizes -> R.Element
sizeDD (ps /\ setPageSize) =
  H.span {} [ R2.select { className, defaultValue: ps, on: {change} } sizes ]
185
  where
186 187
    className = "form-control"
    change e = setPageSize $ const (string2PageSize $ R2.unsafeEventValue e)
188 189 190
    sizes = map option pageSizes
    option size = H.option {value} [H.text value]
      where value = show size
Nicolas Pouillard's avatar
Nicolas Pouillard committed
191

192 193 194
textDescription :: Int -> PageSizes -> Int -> R.Element
textDescription currPage pageSize totalRecords =
  H.div {className: "row1"} [ H.div {className: ""} [ H.text msg ] ] -- TODO or col-md-6 ?
195
  where
196 197 198 199 200 201 202 203 204 205 206 207
    start = (currPage - 1) * pageSizes2Int pageSize + 1
    end' = currPage * pageSizes2Int pageSize
    end  = if end' > totalRecords then totalRecords else end'
    msg = "Showing " <> show start <> " to " <> show end <> " of " <> show totalRecords

effectLink :: Effect Unit -> String -> R.Element
effectLink eff msg = H.a {on: {click: const eff}} [H.text msg]

pagination :: (R2.Setter Int) -> Int -> Int -> R.Element
pagination changePage tp cp =
  H.span {} $
    [ H.text " ", prev, first, ldots]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
208 209 210
    <>
    lnums
    <>
211
    [H.b {} [H.text $ " " <> show cp <> " "]]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
212 213 214 215 216 217
    <>
    rnums
    <>
    [ rdots, last, next ]
    where
      prev = if cp == 1 then
218
               H.text " Prev. "
219
             else
220
               changePageLink (cp - 1) "Prev."
Nicolas Pouillard's avatar
Nicolas Pouillard committed
221
      next = if cp == tp then
222
               H.text " Next "
223 224
             else
               changePageLink (cp + 1) "Next"
Nicolas Pouillard's avatar
Nicolas Pouillard committed
225
      first = if cp == 1 then
226
                H.text ""
227 228
              else
                changePageLink' 1
Nicolas Pouillard's avatar
Nicolas Pouillard committed
229
      last = if cp == tp then
230
               H.text ""
Nicolas Pouillard's avatar
Nicolas Pouillard committed
231
             else
232
               changePageLink' tp
Nicolas Pouillard's avatar
Nicolas Pouillard committed
233
      ldots = if cp >= 5 then
234
                H.text " ... "
Nicolas Pouillard's avatar
Nicolas Pouillard committed
235
                else
236
                H.text ""
Nicolas Pouillard's avatar
Nicolas Pouillard committed
237
      rdots = if cp + 3 < tp then
238
                H.text " ... "
Nicolas Pouillard's avatar
Nicolas Pouillard committed
239
                else
240
                H.text ""
241 242
      lnums = map changePageLink' $ filter (1  < _) [cp - 2, cp - 1]
      rnums = map changePageLink' $ filter (tp > _) [cp + 1, cp + 2]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
243

244 245 246 247 248 249
      changePageLink :: Int -> String -> R.Element
      changePageLink i s =
        H.span {}
          [ H.text " "
          , effectLink (changePage (const i)) s
          , H.text " "
250 251
          ]

252
      changePageLink' :: Int -> R.Element
253
      changePageLink' i = changePageLink i (show i)
Nicolas Pouillard's avatar
Nicolas Pouillard committed
254

255
data PageSizes = PS10 | PS20 | PS50 | PS100 | PS200
Nicolas Pouillard's avatar
Nicolas Pouillard committed
256 257 258 259 260 261 262 263

derive instance eqPageSizes :: Eq PageSizes

instance showPageSize :: Show PageSizes where
  show PS10  = "10"
  show PS20  = "20"
  show PS50  = "50"
  show PS100 = "100"
264
  show PS200 = "200"
Nicolas Pouillard's avatar
Nicolas Pouillard committed
265 266 267 268 269 270

pageSizes2Int :: PageSizes -> Int
pageSizes2Int PS10  = 10
pageSizes2Int PS20  = 20
pageSizes2Int PS50  = 50
pageSizes2Int PS100 = 100
271
pageSizes2Int PS200 = 200
Nicolas Pouillard's avatar
Nicolas Pouillard committed
272

273 274
pageSizes :: Array PageSizes
pageSizes = [PS10, PS20, PS50, PS100, PS200]
Nicolas Pouillard's avatar
Nicolas Pouillard committed
275 276 277 278 279 280

string2PageSize :: String -> PageSizes
string2PageSize "10" = PS10
string2PageSize "20" = PS20
string2PageSize "50" = PS50
string2PageSize "100" = PS100
281
string2PageSize "200" = PS200
Nicolas Pouillard's avatar
Nicolas Pouillard committed
282
string2PageSize _    = PS10