TreeSearch.purs 7.3 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)
12
import Gargantext.Config.REST (AffRESTError)
Karen Konou's avatar
Karen Konou committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
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 87 88 89
                         , autoFocus: true
                         , onEnter: submit inputRef query
                         , onValueChanged: R.setRef inputRef
                         , onBlur: R.setRef inputRef
                         , type: "value"
                         , defaultValue: ""
                         , required: true
                         , placeholder: "Search keys..."
                         }
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
        ]
      Nothing -> pure $ H.div {} []

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

treeSearchContainerCpt :: R.Component ContainerProps
145 146
treeSearchContainerCpt = R2.hereComponent here "treeSearchContainerCpt" hCpt where
  hCpt hp {query, visible, session } _ = do
Karen Konou's avatar
Karen Konou committed
147

148
    useLoader { errorHandler: Nothing
149
              , herePrefix: hp
150
              , path: { session, query }
Karen Konou's avatar
Karen Konou committed
151 152 153 154 155 156 157 158 159 160 161
              , loader: loadSearch
              , render: \searchData -> treeSearchRenderContainer { visible, session, searchData }
    }

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
162
    pure $ treeSearchRender { visible, session, searchData, goToRoute }
Karen Konou's avatar
Karen Konou committed
163 164 165 166 167 168

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

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

    pure $ H.div {className: "search-modal__results"} (results searchData)
Karen Konou's avatar
Karen Konou committed
172 173 174
      where
        results s = map searchResult s
          where
Karen Konou's avatar
Karen Konou committed
175
            searchResult sd = H.div 
176
                                { className: "result mt-1"} 
Karen Konou's avatar
Karen Konou committed
177 178 179 180 181 182 183
                                [ 
                                  B.tooltipContainer
                                  { delayShow: 600
                                  , position: TooltipPosition Right
                                  , tooltipSlot: B.span_ $ show $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id
                                  , defaultSlot:
                                    B.button 
184
                                    { className: "result__button"
Karen Konou's avatar
Karen Konou committed
185 186 187 188 189 190 191 192 193 194 195
                                    , 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
196

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

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