TreeSearch.purs 7.32 KB
Newer Older
Karen Konou's avatar
Karen Konou committed
1 2 3 4
module Gargantext.Components.TreeSearch where

import Gargantext.Prelude

5
import Data.Array (head)
Karen Konou's avatar
Karen Konou committed
6 7 8 9
import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Gargantext.Components.Bootstrap (formSelect')
import Gargantext.Components.Bootstrap as B
Karen Konou's avatar
Karen Konou committed
10
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ModalSizing(..), Position(..), TooltipPosition(..), Variant(..))
11
import Gargantext.Components.InputWithEnter (inputWithEnter)
Karen Konou's avatar
Karen Konou committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes (AppRoute(..), appPath, nodeTypeAppRoute)
import Gargantext.Sessions (Session(..), Sessions, get, sessionId, unSessions)
import Gargantext.Sessions.Types (Session)
import Gargantext.Types (NodeID, NodeType, getIcon)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T

here :: R2.Here
here = R2.here "Gargantext.Components.TreeSearch"

type Props = (
  visible :: T.Box Boolean
, sessions :: T.Box Sessions
)

type StateProps = (
  visible :: T.Box Boolean
, sessions :: Sessions
35
, query :: T.Box String
Karen Konou's avatar
Karen Konou committed
36 37 38 39 40 41
)

type WrapperProps = (
  visible :: T.Box Boolean
, sessions :: Sessions
, session  :: T.Box (Maybe Session)
42
, query :: T.Box String
Karen Konou's avatar
Karen Konou committed
43 44
)

45
type ContainerProps = ( visible :: T.Box Boolean, session :: Session, query :: String)
Karen Konou's avatar
Karen Konou committed
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

type RenderContainerProps = ( visible :: T.Box Boolean, session :: Session, searchData :: Array SearchData )

type RenderProps = ( visible        :: T.Box Boolean
                   , session        :: Session
                   , searchData     :: Array SearchData
                   , goToRoute      :: AppRoute -> Effect Unit )

type SearchData = { name :: String
                  , type :: NodeType
                  , id   :: NodeID
}

treeSearch :: R2.Leaf Props
treeSearch = R2.leaf treeSearchCpt

treeSearchCpt :: R.Component Props
treeSearchCpt = here.component "treeSearch" cpt where
  cpt { sessions, visible } _ = do
    sessions' <- T.useLive T.unequal sessions
66 67
    query <- T.useBox ""
    inputRef <- R.useRef ""
Karen Konou's avatar
Karen Konou committed
68 69 70 71

    pure $ 
      B.baseModal
      { isVisibleBox: visible
72
      , title: Just "Search in tree"
Karen Konou's avatar
Karen Konou committed
73
      , size: MediumModalSize
Karen Konou's avatar
Karen Konou committed
74 75
      , modalClassName: ""
      }
76 77 78 79 80
      [ 
        H.div
        { className: "input-group p-1" }
        [
          inputWithEnter { className: "form-control"
81 82 83 84 85 86
                       , autoFocus: true
                       , onEnter: submit inputRef query
                       , onValueChanged: R.setRef inputRef
                       , onBlur: R.setRef inputRef
                       , type: "value"
                       , defaultValue: ""
87
                       , required: true
88
                       , placeholder: "Search keys..."
89
                       }
90 91 92 93 94 95 96 97 98 99 100
        ,
          H.div { className: "input-group-append"}
          [
            H.button { className: "input-group-text btn btn-light text-secondary"
                    , on: { click: submit inputRef query }
                    , type: "submit" }
            [ H.span {className: "fa fa-search"} [] ]
          ]
        ]
      , treeSearchState {visible, query, sessions: sessions'} 
      ]
101 102
      where
        submit ref box _ = T.write_ (R.readRef ref) box
Karen Konou's avatar
Karen Konou committed
103 104 105 106 107 108

treeSearchState :: R2.Leaf StateProps
treeSearchState = R2.leaf treeSearchStateCpt

treeSearchStateCpt :: R.Component StateProps
treeSearchStateCpt = here.component "treeSearchState" cpt where
109
  cpt { query, sessions, visible } _ = do
Karen Konou's avatar
Karen Konou committed
110 111
    session <- T.useBox $ head $ unSessions sessions

112
    pure $ treeSearchWrapper { query, visible, session, sessions}
Karen Konou's avatar
Karen Konou committed
113 114 115 116 117 118

treeSearchWrapper :: R2.Leaf WrapperProps
treeSearchWrapper = R2.leaf treeSearchWrapperCpt

treeSearchWrapperCpt :: R.Component WrapperProps
treeSearchWrapperCpt = here.component "treeSearchWrapper" cpt where
119
  cpt { query, visible, sessions, session } _ = do
Karen Konou's avatar
Karen Konou committed
120
    session' <- T.useLive T.unequal session
121
    query' <- T.useLive T.unequal query
Karen Konou's avatar
Karen Konou committed
122 123 124

    case session' of
      Just s ->
125 126
        pure $ H.div {className: "search-modal__results-wrapper px-1"} 
        [ formSelect'
Karen Konou's avatar
Karen Konou committed
127 128 129 130
            { callback: \v -> T.write_ (Just v) session
            , list: unSessions sessions
            , value: s
            } []
131 132
        ,
          H.hr {}
Karen Konou's avatar
Karen Konou committed
133
        , 
134
          if query' == "" then
135
            H.div {className: "search-modal__results mb-1"} [B.span_ "Please enter your search query..."]
136 137
          else
            treeSearchContainer {query: query', visible, session: s}
Karen Konou's avatar
Karen Konou committed
138 139 140 141 142 143 144 145
        ]
      Nothing -> pure $ H.div {} []

treeSearchContainer :: R2.Leaf ContainerProps
treeSearchContainer = R2.leaf treeSearchContainerCpt

treeSearchContainerCpt :: R.Component ContainerProps
treeSearchContainerCpt = here.component "treeSearchContainerCpt" cpt where
146
  cpt {query, visible, session } _ = do
Karen Konou's avatar
Karen Konou committed
147 148

    useLoader { errorHandler
149
              , path: { session, query }
Karen Konou's avatar
Karen Konou committed
150 151 152 153 154 155 156 157 158 159 160 161 162
              , loader: loadSearch
              , render: \searchData -> treeSearchRenderContainer { visible, session, searchData }
    }
    where
      errorHandler = logRESTError here "[treeSearchContainer]"

treeSearchRenderContainer :: R2.Leaf RenderContainerProps
treeSearchRenderContainer = R2.leaf treeSearchRenderContainerCpt

treeSearchRenderContainerCpt :: R.Component RenderContainerProps
treeSearchRenderContainerCpt = here.component "treeSearchRenderContainer" cpt where
  cpt { visible, session, searchData } _ = do
    { goToRoute } <- useLinkHandler
163
    pure $ treeSearchRender { visible, session, searchData, goToRoute }
Karen Konou's avatar
Karen Konou committed
164 165 166 167 168 169

treeSearchRender :: R2.Leaf RenderProps
treeSearchRender = R2.leaf treeSearchRenderCpt

treeSearchRenderCpt :: R.Component RenderProps
treeSearchRenderCpt = here.component "treeSearchRenderCpt" cpt where
170 171 172
  cpt { visible, session, searchData, goToRoute } _ = do

    pure $ H.div {className: "search-modal__results"} (results searchData)
Karen Konou's avatar
Karen Konou committed
173 174 175
      where
        results s = map searchResult s
          where
Karen Konou's avatar
Karen Konou committed
176
            searchResult sd = H.div 
177
                                { className: "result mt-1"} 
Karen Konou's avatar
Karen Konou committed
178 179 180 181 182 183 184
                                [ 
                                  B.tooltipContainer
                                  { delayShow: 600
                                  , position: TooltipPosition Right
                                  , tooltipSlot: B.span_ $ show $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id
                                  , defaultSlot:
                                    B.button 
185
                                    { className: "result__button"
Karen Konou's avatar
Karen Konou committed
186 187 188 189 190 191 192 193 194 195 196
                                    , callback: \_ -> do 
                                      T.write_ false visible
                                      goToRoute $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id 
                                    , variant: ButtonVariant Light }
                                    [
                                      B.icon {name: getIcon sd.type true}
                                    , B.wad_ [ "d-inline-block", "w-1" ] 
                                    , H.text sd.name
                                    ]
                                  }
                                ]
Karen Konou's avatar
Karen Konou committed
197

198
type LoadProps = ( session :: Session, query :: String )
Karen Konou's avatar
Karen Konou committed
199 200

loadSearch :: Record LoadProps -> AffRESTError (Array SearchData)
201
loadSearch { session: s, query: q} = get s $ appPath (TreeFlat (sessionId s) (sessionRoot s) q)
Karen Konou's avatar
Karen Konou committed
202
  where sessionRoot (Session {treeId}) = treeId