module Gargantext.Components.Search.SearchField ( Search, Props, searchField, searchFieldComponent )where import Prelude (bind, const, identity, pure, show, ($), (/=), (<$>), (||), (==), map, (<>), (&&), (*>), (>>=), (>=>), (<)) import Data.Maybe (Maybe(..), maybe, isJust) import Data.String (length) import Data.Set as Set import Data.Tuple (fst) import Data.Tuple.Nested ((/\)) import Gargantext.Utils.Reactix as R2 import Reactix.DOM.HTML as H import FFI.Simple ((..)) import Reactix as R import Reactix.DOM.HTML (text, button, div, input, span, ul, li, a, option, text, i) import Gargantext.Components.Search.Types -- (Database(..), readDatabase, Lang(..), readLang, Org(..), readOrg, allOrgs, allIMTorgs, HAL_Filters(..), IMT_org(..)) select :: forall props. R.IsComponent String props (Array R.Element) => Record props -> Array R.Element -> R.Element select = R.createElement "select" type Search = { datafield :: Maybe DataField , term :: String , lang :: Maybe Lang , node_id :: Maybe Int } defaultSearch :: Search defaultSearch = { datafield: Just Gargantext , term: "" , lang: Just EN , node_id: Nothing } type Props = -- list of databases to search, or parsers to use on uploads ( databases :: Array Database , langs :: Array Lang -- State hook for a search, how we get data in and out , search :: R.State (Maybe Search) , node_id :: Maybe Int ) searchField :: Record Props -> R.Element searchField p = R.createElement searchFieldComponent p [] searchFieldComponent :: R.Memo Props searchFieldComponent = R.memo (R.hooksComponent "SearchField" cpt) hasChanged where cpt props@{node_id} _ = do let search = maybe defaultSearch identity (fst props.search) term@(curTerm /\ _) <- R.useState' search.term df@(curDf /\ setDf) <- R.useState' (Just Gargantext :: Maybe DataField) lang@(curLg /\ _) <- R.useState' (Nothing :: Maybe Lang) fi <- R.useState' "" pure $ div { className: "search-field-group" } [ searchInput term , if length curTerm < 3 then div {}[] else div {} [ langNav lang props.langs , if curLg == Nothing then div {}[] else div {} [ dataFieldNav df dataFields , if isExternal curDf then databaseInput df props.databases else div {} [] , if isHAL curDf then orgInput df allOrgs else div {} [] , if isIMT curDf then R.fragment [ ul {} $ map ( \org -> li {} [ input { type: "checkbox" , checked: isIn org curDf , on: {change: \_ -> (setDf $ const $ updateFilter org curDf) } } , if org == All_IMT then i {} [text $ " " <> show org] else text $ " " <> show org ] ) allIMTorgs , filterInput fi ] else div {} [] , if isCNRS curDf then R.fragment [ div {} [], filterInput fi] else div {} [] , if isIsTex curDf then H.div { className: "" , id: "search-popup-tooltip" , title: "Node settings" , data: { toggle: "tooltip" , placement: "right" } } [ H.div {id: "arrow"} [] , H.div { className: "panel panel-default" , style: { border : "1px solid rgba(0,0,0,0.2)" , boxShadow : "0 2px 5px rgba(0,0,0,0.2)" } } [ H.iframe { src: "https://istex.gargantext.org", width: "100%", height: "100%"} [] ] ] else H.div {} [] ] ] , submitButton node_id df term lang props.search ] hasChanged p p' = (fst p.search /= fst p'.search) || (p.databases /= p'.databases ) || (p.langs /= p'.langs ) -- || (fst p.filters /= fst p'.filters ) isExternal :: Maybe DataField -> Boolean isExternal (Just (External _)) = true isExternal _ = false isHAL :: Maybe DataField -> Boolean isHAL (Just (External (Just (HAL _)))) = true isHAL _ = false isIsTex :: Maybe DataField -> Boolean isIsTex (Just (External (Just (IsTex)))) = true isIsTex _ = false 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 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'))))) where imtOrgs' = if Set.member org imtOrgs then if org == All_IMT then Set.empty else Set.delete All_IMT $ Set.delete org imtOrgs else if org == All_IMT then Set.fromFoldable allIMTorgs else Set.insert org imtOrgs updateFilter org _ = (Just (External (Just (HAL (Just (IMT imtOrgs')))))) where imtOrgs' = if org == All_IMT then Set.fromFoldable allIMTorgs else Set.fromFoldable [org] ------------------------------------------------------------------------ langList :: R.State (Maybe Lang) -> Array Lang -> R.Element langList (lang /\ setLang) langs = div { className: "form-group" } [ div {className: "text-primary center"} [text "with lang"] , R2.select { className: "form-control" , on: { change: \e -> setLang $ const $ readLang $ e .. "target" .. "value" } } (liItem <$> langs) ] where liItem :: Lang -> R.Element liItem lang = option {className : "text-primary center"} [ text (show lang) ] langNav :: R.State (Maybe Lang) -> Array Lang -> R.Element langNav (lang /\ setLang) langs = R.fragment [ div {className: "text-primary center"} [text "with lang"] , div { className: "nav nav-tabs"} (liItem <$> langs) ] where liItem :: Lang -> R.Element liItem lang' = div { className : "nav-item nav-link" <> if (Just lang') == lang then " active" else "" , on: { click: \_ -> setLang $ const $ Just lang' } } [ text (show lang') ] ------------------------------------------------------------------------ dataFieldNav :: R.State (Maybe DataField) -> Array DataField -> R.Element dataFieldNav (df /\ setDf) datafields = R.fragment [ div {className: "text-primary center"} [text "with DataField"] , div { className: "nav nav-tabs"} (liItem <$> dataFields) , div {className:"center"} [ text $ maybe "" doc df ] ] where liItem :: DataField -> R.Element liItem df' = div { className : "nav-item nav-link" <> if (Just df') == df then " active" else "" , on: { click: \_ -> setDf $ const $ Just df' } } [ text (show df') ] ------------------------------------------------------------------------ databaseInput :: R.State (Maybe DataField) -> Array Database -> R.Element databaseInput (df /\ setDf) dbs = div { className: "form-group" } [ div {className: "text-primary center"} [text "in database"] , R2.select { className: "form-control" , on: { change: \e -> setDf $ const $ Just $ External $ readDatabase $ e .. "target" .. "value" } } (liItem <$> dbs) , div {className:"center"} [ text $ maybe "" doc db ] ] where db = case df of (Just (External (Just x))) -> Just x _ -> Nothing liItem :: Database -> R.Element liItem db = option {className : "text-primary center"} [ text (show db) ] orgInput :: R.State (Maybe DataField) -> Array Org -> R.Element orgInput (curDf /\ setDf) orgs = div { className: "form-group" } [ div {className: "text-primary center"} [text "filter with organization: "] , R2.select { className: "form-control" , on: { change: \e -> setDf $ const $ Just $ External $ Just $ HAL $ readOrg $ e .. "target" .. "value" } } (liItem <$> orgs) ] where liItem :: Org -> R.Element liItem org = option {className : "text-primary center"} [ text (show org) ] filterInput :: R.State String -> R.Element filterInput (term /\ setTerm) = div {className: "form-group"} [ input { defaultValue: term , 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" } ] searchInput :: R.State String -> R.Element searchInput (term /\ setTerm) = div { className : "" } [ input { defaultValue: term , className: "form-control" , type: "text" , on: { change : \e -> setTerm $ const $ e .. "target" .. "value" } , placeholder: "Your Query here" } ] submitButton :: Maybe Int -> R.State (Maybe DataField) -> R.State String -> R.State (Maybe Lang) -> R.State (Maybe Search) -> R.Element submitButton node_id (datafield /\ _) (term /\ _) (lang /\ _) (_ /\ setSearch) = div { className : "panel-footer" } [ button { className: "btn btn-primary" , type: "button" , on: {click: doSearch} } [ text "Launch Search" ] ] where doSearch = \_ -> do case term of "" -> setSearch $ const Nothing _ -> setSearch $ const $ Just {datafield, term, lang, node_id}