SearchField.purs 11.8 KB
Newer Older
1
module Gargantext.Components.Search.SearchField
2
  ( Search, Props, defaultSearch, searchField, searchFieldComponent, isIsTex) where
3

4 5
import Prelude (const, map, pure, show, ($), (&&), (<), (<$>), (<>), (==))
import Data.Maybe (Maybe(..), maybe)
6
import Data.String (length)
7
import Data.Set as Set
8
import Data.Tuple (fst)
9
import Data.Tuple.Nested ((/\))
10
import Gargantext.Utils.Reactix as R2
11
import FFI.Simple ((..))
12
import Reactix as R
13
import Reactix.DOM.HTML as H
14
import Gargantext.Components.Search.Types -- (Database(..), readDatabase, Lang(..), readLang, Org(..), readOrg, allOrgs, allIMTorgs, HAL_Filters(..), IMT_org(..))
15

16 17 18 19 20
select :: forall props.
          R.IsComponent String props (Array R.Element)
          => Record props
          -> Array R.Element
          -> R.Element
21 22
select = R.createElement "select"

23 24 25 26
type Search = { datafield :: Maybe DataField
              , term      :: String
              , lang      :: Maybe Lang
              , node_id   :: Maybe Int
27
              }
28

29 30 31 32 33 34
eqSearch :: Search -> Search -> Boolean
eqSearch s s' =    (s.datafield == s'.datafield)
                && (s.term == s'.term)
                && (s.lang == s'.lang)
                && (s.node_id == s'.node_id)

35
defaultSearch :: Search
36
defaultSearch = { datafield: Nothing
37
                , term: ""
38
                , lang: Nothing
39
                , node_id: Nothing
40
                }
41

42
type Props =
43 44
  -- list of databases to search, or parsers to use on uploads
  ( databases :: Array Database
45
  , langs     :: Array Lang
46
  -- State hook for a search, how we get data in and out
47
  , search    :: R.State Search
48 49 50 51 52 53
  )

searchField :: Record Props -> R.Element
searchField p = R.createElement searchFieldComponent p []

searchFieldComponent :: R.Memo Props
54
searchFieldComponent = R.memo (R.hooksComponent "SearchField" cpt) eqProps
55
  where
56
    cpt props@{search: search@(s /\ _)} _ = do
57
      pure $
58
        H.div { className: "search-field-group", style: { width: "100%" } }
59
          [
60
            H.div { className: "row" }
61
              [
62
                H.div { className: "col-md-12" }
63 64
                [ searchInput search
                , if length s.term < 3
65
                  then
66
                    H.div {}[]
67
                  else
68
                    H.div {} [ dataFieldNav search dataFields
69 70
                            , if isExternal s.datafield
                              then databaseInput search props.databases
71
                              else H.div {} []
72 73 74

                            , if isHAL s.datafield
                              then orgInput search allOrgs
75
                              else H.div {} []
76 77 78

                            , if isIMT s.datafield
                              then componentIMT search
79
                              else H.div {} []
80 81 82

                            , if isCNRS s.datafield
                              then componentCNRS search
83
                              else H.div {} []
84
                            ]
85
                ]
86
              ]
87 88 89 90
          , H.div { className : "panel-footer" }
                [ if needsLang s.datafield then langNav search props.langs else H.div {} []
                , H.div {} []
                , H.div {className: "flex-center"} [submitButton search]
91
                ]
92
          ]
93 94 95 96 97 98
    eqProps p p' =    (fst p.search == fst p'.search)
                   && (p.databases  == p'.databases )
                   && (p.langs      == p'.langs     )
                   && (eqSearch (fst p.search) (fst p'.search))
--                   && (fst p.filters == fst p'.filters   )
    componentIMT (search /\ setSearch) =
99
      R.fragment
100
      [ H.ul {} $ map liCpt allIMTorgs
101
      --, filterInput fi
102
      ]
103
      where
104
        liCpt org =
105 106
          H.li {}
          [ H.input { type: "checkbox"
107 108 109 110 111 112
                  , checked: isIn org search.datafield
                  , on: {
                    change: \_ -> (setSearch $ _ { datafield = updateFilter org search.datafield })
                    }
                  }
          , if org == All_IMT
113 114
            then H.i {} [H.text  $ " " <> show org]
            else H.text $ " " <> show org
115
          ]
116 117
    componentCNRS (search /\ setSearch) =
      R.fragment [
118
        H.div {} []
119 120
      --, filterInput fi
      ]
121

122

123 124 125 126 127 128 129 130
isExternal :: Maybe DataField -> Boolean
isExternal (Just (External _)) = true
isExternal _ = false

isHAL :: Maybe DataField -> Boolean
isHAL (Just (External (Just (HAL _)))) = true
isHAL _ = false

131 132 133 134
isIsTex :: Maybe DataField -> Boolean
isIsTex (Just (External (Just (IsTex)))) = true
isIsTex _ = false

135 136 137 138 139 140 141 142
isIMT :: Maybe DataField -> Boolean
isIMT (Just ( External ( Just ( HAL ( Just ( IMT _)))))) = true
isIMT _ = false

isCNRS :: Maybe DataField -> Boolean
isCNRS (Just ( External ( Just ( HAL ( Just ( CNRS _)))))) = true
isCNRS _ = false

143 144 145 146 147
needsLang :: Maybe DataField -> Boolean
needsLang (Just Gargantext) = true
needsLang (Just Web)        = true
needsLang (Just ( External ( Just (HAL _)))) = true
needsLang _ = false
148 149 150 151 152 153 154 155 156


isIn :: IMT_org -> Maybe DataField -> Boolean
isIn org (Just (External (Just (HAL (Just (IMT imtOrgs)))))) = Set.member org imtOrgs
isIn _ _ = false

updateFilter :: IMT_org -> Maybe DataField -> Maybe DataField
updateFilter org (Just (External (Just (HAL (Just (IMT imtOrgs)))))) =
 (Just (External (Just (HAL (Just $ IMT imtOrgs')))))
157 158
  where
    imtOrgs' = if Set.member org imtOrgs
159 160 161
                  then
                    if org == All_IMT
                       then Set.empty
162
                       else Set.delete All_IMT $ Set.delete org imtOrgs
163 164 165 166
                  else
                    if org == All_IMT
                       then Set.fromFoldable allIMTorgs
                       else Set.insert org imtOrgs
167

168
updateFilter org _ = (Just (External (Just (HAL (Just (IMT imtOrgs'))))))
169
  where
170 171 172
    imtOrgs' = if org == All_IMT
                  then Set.fromFoldable allIMTorgs
                  else Set.fromFoldable [org]
173

174
------------------------------------------------------------------------
175
langList :: R.State Search -> Array Lang -> R.Element
176
langList (lang /\ setLang) langs =
177 178
              H.div { className: "form-group" }
                   [ H.div {className: "text-primary center"} [H.text "with lang"]
179
                   , R2.select { className: "form-control"
180
                               , on: { change: \e -> setLang $ _ {lang = lang' e}}
181 182 183 184
                               } (liItem <$> langs)
                   ]
    where
      liItem :: Lang -> R.Element
185
      liItem l = H.option {className : "text-primary center"} [ H.text (show l) ]
186

187 188 189
      lang' e = readLang $ e .. "target" .. "value"


190 191
langNav :: R.State Search -> Array Lang -> R.Element
langNav ({lang} /\ setSearch) langs =
192 193
  R.fragment [ H.div {className: "text-primary center"} [H.text "with lang"]
             , H.div { className: "nav nav-tabs"} (liItem <$> langs)
194 195 196
             ]
    where
      liItem :: Lang -> R.Element
197
      liItem  lang' =
198
        H.div { className : "nav-item nav-link" <> if (Just lang') == lang then " active" else ""
199
            , on: { click: \_ -> setSearch $ _ { lang = Just lang' } }
200
            } [ H.text (show lang') ]
201 202 203

------------------------------------------------------------------------

204 205
dataFieldNav :: R.State Search -> Array DataField -> R.Element
dataFieldNav ({datafield} /\ setSearch) datafields =
206 207 208
  R.fragment [ H.div {className: "text-primary center"} [H.text "with DataField"]
             , H.div { className: "nav nav-tabs"} (liItem <$> dataFields)
             , H.div {className:"center"} [ H.text $ maybe "" doc datafield ]
209 210 211
             ]
    where
      liItem :: DataField -> R.Element
212
      liItem  df' =
213
        H.div { className : "nav-item nav-link" <> if (Just df') == datafield then " active" else ""
214
            , on: { click: \_ -> setSearch $ _ { datafield = Just df'} }
215
            } [ H.text (show df') ]
216

217

218
------------------------------------------------------------------------
219 220 221 222 223

databaseNav  :: R.State Search
              -> Array Database
              -> R.Element
databaseNav ({datafield} /\ setSearch) dbs =
224 225 226
  R.fragment [ H.div {className: "text-primary center"} [H.text "with DataField"]
             , H.div { className: "nav nav-tabs"} (liItem <$> dbs)
             , H.div {className:"center"} [ H.text $ maybe "" doc db ]
227 228 229 230 231 232 233 234 235
             ]
    where

      db = case datafield of
        (Just (External (Just x))) -> Just x
        _                          -> Nothing

      liItem :: Database -> R.Element
      liItem  df' =
236
        H.div { className : "nav-item nav-link" <> if (Just $ External $ Just df') == datafield then " active" else ""
237
            , on: { click: \_ -> setSearch $ _ { datafield = Just $ External $ Just df' } }
238
            } [ H.text (show df') ]
239 240 241



242
databaseInput :: R.State Search
243 244
              -> Array Database
              -> R.Element
245
databaseInput ({datafield} /\ setSearch) dbs =
246 247
   H.div { className: "form-group" }
   [ H.div {className: "text-primary center"} [H.text "in database"]
248 249 250
   , R2.select { className: "form-control"
               , on: { change: onChange }
               } (liItem <$> dbs)
251
   , H.div {className:"center"} [ H.text $ maybe "" doc db ]
252
   ]
253
    where
254
      db = case datafield of
255 256 257
        (Just (External (Just x))) -> Just x
        _                          -> Nothing

258
      liItem :: Database -> R.Element
259
      liItem  db' = H.option {className : "text-primary center"} [ H.text (show db') ]
260

261 262 263
      onChange e = do
        let value = e .. "target" .. "value"
        setSearch $ _ {datafield =  Just $ External $ readDatabase value }
264

265 266 267

orgInput :: R.State Search -> Array Org -> R.Element
orgInput ({datafield} /\ setSearch) orgs =
268 269
  H.div { className: "form-group" }
  [ H.div {className: "text-primary center"} [H.text "filter with organization: "]
270 271 272 273
  , R2.select { className: "form-control"
              , on: { change: onChange }
              } (liItem <$> orgs)
  ]
274 275
    where
      liItem :: Org -> R.Element
276
      liItem  org = H.option {className : "text-primary center"} [ H.text (show org) ]
277 278 279
      onChange e = do
        let value = e .. "target" .. "value"
        setSearch $ _ { datafield = Just $ External $ Just $ HAL $ readOrg value }
280 281 282

filterInput :: R.State String -> R.Element
filterInput (term /\ setTerm) =
283
  H.div {className: "form-group"} [ H.input { defaultValue: term
284 285 286 287 288 289 290 291 292 293 294 295 296
                              , className: "form-control"
                              , type: "text"
                              , on: { change: \e -> setTerm
                                                  $ const
                                                  $ e .. "target" .. "value"
                                    }
                              , "required pattern": "[[0-9]+[ ]+]*"
                              -- TODO                          ^FIXME not sure about the regex comprehension: that should match "123 2334 44545" only (Integers separated by one space)
                              -- form validation with CSS
                              -- DOC: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation
                              , placeholder : "Filter with struct_Ids as integer" 
                              }
                      ]
297

298

299 300
searchInput :: R.State Search -> R.Element
searchInput ({term} /\ setSearch) =
301 302 303 304 305 306
  H.div { className : "" }
  [ H.input { defaultValue: term
            , className: "form-control"
            , type: "text"
            , on: { change : onChange }
            , placeholder: "Your Query here" }
307
  ]
308 309 310 311
  where
    onChange e = do
      let value = e .. "target" .. "value"
      setSearch $ _ {term = value }
312

313

314
submitButton :: R.State Search
315
             -> R.Element
316
submitButton (search /\ setSearch) =
317
  H.button { className: "btn btn-primary"
318 319 320
         , type: "button"
         , on: {click: doSearch}
         , style: { width: "100%" } 
321
         } [ H.text "Launch Search" ]
322
  where
323
    doSearch = \_ -> do
324 325 326
      case search.term of
        "" -> setSearch $ const defaultSearch
        _  -> setSearch $ const search