module Gargantext.Components.Forest.Tree.Node.Tools where import Gargantext.Prelude ( class Ord, class Read, class Show, Unit , bind, const, discard, map, not, pure, read, show, when, mempty , ($), (<), (<<<), (<>), (<$>), (<*>) ) import Data.Maybe (fromMaybe, Maybe(..)) import Data.Nullable (null) import Data.Set (Set) import Data.Set as Set import Data.String as S import Data.String.CodeUnits as DSCU import Effect (Effect) import Effect.Aff (Aff, launchAff, launchAff_) import Reactix as R import Reactix.DOM.HTML as H import Toestand as T import Gargantext.Components.Forest.Tree.Node.Action (Action, icon, text) import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Ends (Frontends, url) import Gargantext.Sessions (Session, sessionId) import Gargantext.Types as GT import Gargantext.Utils (toggleSet) import Gargantext.Utils.Glyphicon (glyphicon) import Gargantext.Utils.ReactTooltip as ReactTooltip import Gargantext.Utils.Reactix as R2 here :: R2.Here here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools" fragmentPT :: String -> R.Element fragmentPT text = H.div { style: { margin: "10px" }} [ H.text text ] type Body = Array R.Element type Footer = R.Element panel :: Body -> Footer -> R.Element panel bodies submit = R.fragment [ H.div { className: "card-body" } [ H.div { className: "row" } -- TODO add type for text or form here [ H.form {className: "form-horizontal"} bodies ] [ H.div { className: "col-12" } bodies ]] , H.div {className: "card-footer"} [ H.div { className: "row" } [ H.div { className: "mx-auto"} [ submit ] ]]] type TextInputBoxProps = ( id :: GT.ID , dispatch :: Action -> Aff Unit , text :: String , isOpen :: T.Box Boolean , boxName :: String , boxAction :: String -> Action ) textInputBox :: R2.Component TextInputBoxProps textInputBox = R.createElement textInputBoxCpt textInputBoxCpt :: R.Component TextInputBoxProps textInputBoxCpt = here.component "textInputBox" cpt where cpt { boxAction, boxName, dispatch, id, isOpen, text } _ = content <$> T.useLive T.unequal isOpen <*> R.useRef text where content false _ = (R.fragment []) content true renameNodeNameRef = H.div { className: "from-group row" } [ textInput renameNodeNameRef , submitBtn renameNodeNameRef , cancelBtn ] textInput renameNodeNameRef = H.div { className: "col-8" } [ inputWithEnter { autoFocus: true , className: "form-control" , defaultValue: text , onBlur: R.setRef renameNodeNameRef , onEnter: submit renameNodeNameRef , onValueChanged: R.setRef renameNodeNameRef , placeholder: (boxName <> " Node") , type: "text" } ] submitBtn renameNodeNameRef = H.a { type: "button" , title: "Submit" , on: { click: submit renameNodeNameRef } , className: "col-2 " <> glyphicon "floppy-o" } [] cancelBtn = H.a { type: "button", title: "Cancel", on: { click } , className: "text-danger col-2 " <> glyphicon "times" } [] submit ref _ = do launchAff_ $ dispatch (boxAction $ R.readRef ref) T.write_ false isOpen click _ = T.write_ false isOpen type DefaultText = String formEdit :: forall prev next . DefaultText -> ((prev -> String) -> Effect next) -> R.Element formEdit defaultValue setter = H.div { className: "form-group" } [ H.input { defaultValue, type: "text", on: { input } , placeholder: defaultValue, className: "form-control" } ] where input = setter <<< const <<< R.unsafeEventValue -- | Form Choice input -- if the list of options is not big enough, a button is used instead formChoiceSafe :: forall item m . Read item => Show item => Array item -> item -> (item -> Effect m) -> (item -> String) -> R.Element formChoiceSafe [] _ _ _ = mempty formChoiceSafe [n] _ cbk prnt = formButton n cbk prnt formChoiceSafe arr def cbk prnt = formChoice arr def cbk prnt -- | List Form formChoice :: forall item m . Read item => Show item => Array item -> item -> (item -> Effect m) -> (item -> String) -> R.Element formChoice items def cbk prnt = H.div { className: "form-group"} [ R2.select { className: "form-control with-icon-font" , on: { change } } $ map option items ] where change e = cbk $ fromMaybe def $ read $ R.unsafeEventValue e option opt = H.option { value: show opt } [ H.text $ prnt opt ] -- | Button Form -- FIXME: currently needs a click from the user (by default, we could avoid such click) formButton :: forall item m . item -> (item -> Effect m) -> (item -> String) -> R.Element formButton item cbk prnt = H.div {} [ H.text $ "Confirm the selection of: " <> prnt item , cta ] where cta = H.button { className : "cold-md-5 btn btn-primary center" , type : "button" , title: "Form Button" , style : { width: "100%" } , on: { click: \_ -> cbk item } } [ H.text "Confirmation" ] ------------------------------------------------------------------------ ------------------------------------------------------------------------ submitButton :: Action -> (Action -> Aff Unit) -> R.Element submitButton action dispatch = H.button { className : "btn btn-primary fa fa-" <> icon action , type: "button" , id: S.toLower $ show action , title: show action , on: {click: \_ -> launchAff $ dispatch action} } [ H.text $ " " <> text action] type Href = String submitButtonHref :: Action -> Href -> R.Element submitButtonHref action href = H.a { className, href, target: "_blank" } [ H.text $ " " <> text action ] where className = "btn btn-primary fa fa-" <> icon action ------------------------------------------------------------------------ -- | CheckBox tools -- checkboxes: Array of boolean values (basic: without pending option) -- checkbox : One boolean value only type CheckboxProps = ( value :: T.Box Boolean ) checkbox :: R2.Leaf CheckboxProps checkbox props = R.createElement checkboxCpt props [] checkboxCpt :: R.Component CheckboxProps checkboxCpt = here.component "checkbox" cpt where cpt { value } _ = do value' <- T.useLive T.unequal value pure $ H.input { className: "form-check-input" , on: { click } , type: "checkbox" , value: value' } where click _ = T.modify_ not value data CheckBoxes = Multiple | Uniq type CheckboxesListGroup a = ( groups :: Array a , options :: T.Box (Set a) ) checkboxesListGroup :: forall a. Ord a => Show a => R2.Component (CheckboxesListGroup a) checkboxesListGroup = R.createElement checkboxesListGroupCpt checkboxesListGroupCpt :: forall a. Ord a => Show a => R.Component (CheckboxesListGroup a) checkboxesListGroupCpt = here.component "checkboxesListGroup" cpt where cpt { groups, options } _ = do options' <- T.useLive T.unequal options let one a = H.li { className: "list-group-item" } [ H.div { className: "form-check" } [ H.input { defaultChecked: Set.member a options' , on: { click: \_ -> T.write_ (toggleSet a options') options , type: "checkbox" } , className: "form-check-input" } , H.label { className: "form-check-label" } [ H.text (show a) ] ] ] pure $ R.fragment $ map one $ Set.toUnfoldable options' prettyNodeType :: GT.NodeType -> String prettyNodeType = S.replace (S.Pattern "Node") (S.Replacement " ") <<< S.replace (S.Pattern "Folder") (S.Replacement " ") <<< show tooltipId :: GT.NodeID -> String tooltipId id = "node-link-" <> show id -- START node link type NodeLinkProps = ( frontends :: Frontends , folderOpen :: T.Box Boolean , handed :: GT.Handed , id :: Int , isSelected :: Boolean , name :: GT.Name , nodeType :: GT.NodeType , session :: Session ) nodeLink :: R2.Component NodeLinkProps nodeLink = R.createElement nodeLinkCpt nodeLinkCpt :: R.Component NodeLinkProps nodeLinkCpt = here.component "nodeLink" cpt where cpt { folderOpen , frontends , handed , id , isSelected , name , nodeType , session } _ = do popoverRef <- R.useRef null pure $ H.div { className: "node-link" , on: { click } } [ H.a { href, data: { for: tooltipId id, tip: true } } [ nodeText { handed, isSelected, name } [] , ReactTooltip.reactTooltip { effect: "float", id: tooltipId id, type: "dark" } [ R2.row [ H.h4 {className: GT.fldr nodeType true} [ H.text $ GT.prettyNodeType nodeType ] ] , R2.row [ H.span {} [ H.text $ name ]] ] ] ] where -- NOTE Don't toggle tree if it is not selected -- click on closed -> open -- click on open -> ? click _ = when (not isSelected) (T.write_ true folderOpen) href = url frontends $ GT.NodePath (sessionId session) nodeType (Just id) -- END node link type NodeTextProps = ( isSelected :: Boolean , handed :: GT.Handed , name :: GT.Name ) nodeText :: R2.Component NodeTextProps nodeText = R.createElement nodeTextCpt nodeTextCpt :: R.Component NodeTextProps nodeTextCpt = here.component "nodeText" cpt where cpt { isSelected, handed, name } _ = pure $ if isSelected then H.u { className } [ H.b {} [ H.text ("| " <> name15 name <> " | ") ] ] else GT.flipHanded l r handed where l = H.text "..." r = H.text (name15 name) name_ len n = if S.length n < len then n else case (DSCU.slice 0 len n) of Nothing -> "???" Just s -> s <> "..." name15 = name_ 15 className = "node-text"