Commit 7cad1696 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-arxiv

parents 2a29dcc9 9e3893bb
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{ {
"name": "Gargantext", "name": "Gargantext",
"version": "0.0.5.8.4", "version": "0.0.5.8.5",
"scripts": { "scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix", "generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash", "generate-psc-packages-nix": "./nix/generate-packages-json.bash",
......
...@@ -16,11 +16,13 @@ type Props = ...@@ -16,11 +16,13 @@ type Props =
type Options = type Options =
( className :: String ( className :: String
, contentClassName :: String
) )
options :: Record Options options :: Record Options
options = options =
{ className: "" { className : ""
, contentClassName : ""
} }
-- | Component simulating a native <fieldset> -- | Component simulating a native <fieldset>
...@@ -36,12 +38,21 @@ component = R.hooksComponent componentName cpt where ...@@ -36,12 +38,21 @@ component = R.hooksComponent componentName cpt where
cpt props@{ titleSlot cpt props@{ titleSlot
} children = do } children = do
-- Computed -- Computed
className <- pure $ intercalate " " let
className = intercalate " "
-- provided custom className -- provided custom className
[ props.className [ props.className
-- BEM classNames -- BEM classNames
, componentName , componentName
] ]
contentClassName = intercalate " "
-- provided custom className
[ props.contentClassName
-- BEM classNames
, componentName <> "__content"
]
-- Render -- Render
pure $ pure $
...@@ -53,6 +64,6 @@ component = R.hooksComponent componentName cpt where ...@@ -53,6 +64,6 @@ component = R.hooksComponent componentName cpt where
[ titleSlot ] [ titleSlot ]
, ,
H.div H.div
{ className: componentName <> "__content" } { className: contentClassName}
children children
] ]
...@@ -14,7 +14,9 @@ import Gargantext.Components.Bootstrap.Icon(icon) as Exports ...@@ -14,7 +14,9 @@ import Gargantext.Components.Bootstrap.Icon(icon) as Exports
import Gargantext.Components.Bootstrap.IconButton(iconButton) as Exports import Gargantext.Components.Bootstrap.IconButton(iconButton) as Exports
import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports
import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports
import Gargantext.Components.Bootstrap.Tooltip(tooltip, tooltipBind, tooltipContainer) as Exports import Gargantext.Components.Bootstrap.Tabs(tabs) as Exports
import Gargantext.Components.Bootstrap.Tooltip(tooltip, TooltipBindingProps, tooltipBind, tooltipBind', tooltipContainer) as Exports
import Gargantext.Components.Bootstrap.Wad(wad, wad', wad_) as Exports
import Gargantext.Components.Bootstrap.Shortcut( import Gargantext.Components.Bootstrap.Shortcut(
div', div_ div', div_
......
module Gargantext.Components.Bootstrap.Tooltip module Gargantext.Components.Bootstrap.Tooltip
( tooltip ( tooltip
, tooltipBind , TooltipBindingProps, tooltipBind, tooltipBind'
, tooltipContainer , tooltipContainer
) where ) where
...@@ -90,6 +90,13 @@ tooltipBind = ...@@ -90,6 +90,13 @@ tooltipBind =
, "data-tip": true , "data-tip": true
} }
-- | Derived empty state
tooltipBind' :: Record TooltipBindingProps
tooltipBind' =
{ "data-for": ""
, "data-tip": false
}
------------------------------------------------------------- -------------------------------------------------------------
type ContainerProps = type ContainerProps =
......
module Gargantext.Components.Bootstrap.Wad
( wad
, wad'
, wad_
) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Reactix as R
import Reactix.DOM.HTML as H
componentName :: String
componentName = "b-wad"
-- | Structural Component for a simple Element only serving the purpose to add
-- | some classes in it
-- |
-- | Hence the name: Wad (noun): a small mass, lump, or ball of anything ;
-- | a roll of something
wad :: Array String -> Array R.Element -> R.Element
wad classes children = R.createDOMElement "div" cls children
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
-- | Shorthand for using <wad> Component without writing its text node
wad' :: Array String -> String -> R.Element
wad' classes text = R.createDOMElement "div" cls chd
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
chd = [ H.text text ]
-- | Shorthand for using <wad> Component without any child
wad_ :: Array String -> R.Element
wad_ classes = R.createDOMElement "div" cls []
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
module Gargantext.Components.Bootstrap.Tabs(tabs) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Effect (Effect)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props a =
( value :: a
, callback :: a -> Effect Unit
, list :: Array a
| Options
)
type Options =
( className :: String
)
options :: Record Options
options =
{ className : ""
}
-- | Structural molecular component to the Bootstrap <nav-tabs> + <nav-item>
-- | simplifying a lot of the available UI/UX possibilites (type, disabled
-- | tabs, etc)
-- |
-- | https://getbootstrap.com/docs/4.6/components/navs/#tabs
tabs :: forall r a.
Show a
=> Eq a
=> R2.OptLeaf Options (Props a) r
tabs = R2.optLeaf component options
componentName :: String
componentName = "b-tabs"
component :: forall a.
Show a
=> Eq a
=> R.Component (Props a)
component = R.hooksComponent componentName cpt where
cpt props@{ list, value, callback } _ = do
-- Computed
let
className = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, "nav nav-tabs"
]
-- Render
pure $
H.ul
{ className } $
flip map list \item ->
H.li
{ className: "nav-item"
, on: { click: \_ -> callback item }
}
[
H.a
{ className: intercalate " "
[ "nav-link"
, value == item ? "active" $ ""
]
}
[
H.text $ show item
]
]
...@@ -16,7 +16,6 @@ import Gargantext.Ends (Frontends) ...@@ -16,7 +16,6 @@ import Gargantext.Ends (Frontends)
import Gargantext.Hooks.LinkHandler (useLinkHandler) import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Routes (AppRoute(..)) import Gargantext.Routes (AppRoute(..))
import Gargantext.Sessions (Session(..), unSessions) import Gargantext.Sessions (Session(..), unSessions)
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -132,9 +131,10 @@ plusCpt = here.component "plus" cpt where ...@@ -132,9 +131,10 @@ plusCpt = here.component "plus" cpt where
, variant: ButtonVariant Light , variant: ButtonVariant Light
} }
[ [
B.icon { name: "universal-access" } B.icon
{ name: "universal-access" }
, ,
H.text $ nbsp 1 B.wad_ [ "d-inline-block", "w-1" ]
, ,
H.text $ "Log in/out" H.text $ "Log in/out"
] ]
...@@ -150,7 +150,7 @@ forestLayout :: R2.Leaf Props ...@@ -150,7 +150,7 @@ forestLayout :: R2.Leaf Props
forestLayout = R2.leaf forestLayoutCpt forestLayout = R2.leaf forestLayoutCpt
forestLayoutCpt :: R.Memo Props forestLayoutCpt :: R.Memo Props
forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where
cpt p children = pure $ cpt p _ = pure $
H.div H.div
{ className: "forest-layout" } { className: "forest-layout" }
......
exports.nodeUserRegexp = /(@{1}.*).gargantext.org$/;
module Gargantext.Components.Forest.Tree.Node where module Gargantext.Components.Forest.Tree.Node
( nodeSpan
, blankNodeSpan
) where
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple as DOM import Data.Array.NonEmpty as NArray
import DOM.Simple.Event as DE
import Data.Foldable (intercalate) import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..), maybe)
import Data.Nullable (Nullable, null) import Data.Nullable (null)
import Data.String.Regex as Regex
import Data.Symbol (SProxy(..)) import Data.Symbol (SProxy(..))
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
...@@ -15,24 +18,21 @@ import Effect.Class (liftEffect) ...@@ -15,24 +18,21 @@ import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Tooltip (tooltipBind)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), TooltipEffect(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), TooltipEffect(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..)) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fileTypeView) import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fileTypeView)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..)) import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..))
import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView) import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView)
import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox) import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox)
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (BarType(..), asyncProgressBar)
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar as PB
import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, nodeActionsNodeList) import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, nodeActionsNodeList)
import Gargantext.Components.GraphExplorer.API as GraphAPI import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Components.Lang (Lang(EN)) import Gargantext.Components.Lang (Lang(EN))
import Gargantext.Components.Nodes.Corpus (loadCorpusWithChild) import Gargantext.Components.Nodes.Corpus (loadCorpusWithChild)
import Gargantext.Config.REST (logRESTError) import Gargantext.Config.REST (logRESTError)
import Gargantext.Context.Progress (AsyncProps, asyncContext, asyncProgress) import Gargantext.Context.Progress (asyncContext, asyncProgress)
import Gargantext.Ends (Frontends, url) import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Hooks.Loader (useLoader, useLoaderEffect) import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Version (Version, useVersion) import Gargantext.Hooks.Version (Version, useVersion)
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId) import Gargantext.Sessions (Session, sessionId)
...@@ -42,14 +42,15 @@ import Gargantext.Utils (nbsp, textEllipsisBreak, (?)) ...@@ -42,14 +42,15 @@ import Gargantext.Utils (nbsp, textEllipsisBreak, (?))
import Gargantext.Utils.Popover as Popover import Gargantext.Utils.Popover as Popover
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import React.SyntheticEvent (SyntheticEvent_)
import React.SyntheticEvent as E
import React.SyntheticEvent as SE import React.SyntheticEvent as SE
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Record as Record import Record as Record
import Toestand as T import Toestand as T
-- (?) never been able to properly declare PureScript Regex...
foreign import nodeUserRegexp :: Regex.Regex
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node" here = R2.here "Gargantext.Components.Forest.Tree.Node"
...@@ -78,7 +79,6 @@ nodeSpanCpt :: R.Component NodeSpanProps ...@@ -78,7 +79,6 @@ nodeSpanCpt :: R.Component NodeSpanProps
nodeSpanCpt = here.component "nodeSpan" cpt nodeSpanCpt = here.component "nodeSpan" cpt
where where
cpt props@{ boxes: boxes@{ errors cpt props@{ boxes: boxes@{ errors
, handed
, reloadMainPage , reloadMainPage
, reloadRoot , reloadRoot
, route , route
...@@ -119,7 +119,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -119,7 +119,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt
dropClass Nothing _ = "" dropClass Nothing _ = ""
name' :: String -> GT.NodeType -> Session -> String name' :: String -> GT.NodeType -> Session -> String
name' _ GT.NodeUser session = show session name' _ GT.NodeUser s = show s
name' n _ _ = n name' n _ _ = n
isSelected = Just route' == Routes.nodeTypeAppRoute nodeType (sessionId session) id isSelected = Just route' == Routes.nodeTypeAppRoute nodeType (sessionId session) id
...@@ -128,6 +128,8 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -128,6 +128,8 @@ nodeSpanCpt = here.component "nodeSpan" cpt
href = url frontends $ GT.NodePath (sessionId session) nodeType (Just id) href = url frontends $ GT.NodePath (sessionId session) nodeType (Just id)
name = name' props.name nodeType session
-- Methods -- Methods
dropHandler :: forall event. dropHandler :: forall event.
...@@ -151,18 +153,18 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -151,18 +153,18 @@ nodeSpanCpt = here.component "nodeSpan" cpt
T.Box Boolean T.Box Boolean
-> SE.SyntheticEvent_ event -> SE.SyntheticEvent_ event
-> Effect Unit -> Effect Unit
onDragOverHandler isDragOver e = do onDragOverHandler box e = do
-- prevent redirection when file is dropped -- prevent redirection when file is dropped
-- https://stackoverflow.com/a/6756680/941471 -- https://stackoverflow.com/a/6756680/941471
SE.preventDefault e SE.preventDefault e
SE.stopPropagation e SE.stopPropagation e
T.write_ true isDragOver T.write_ true box
onDragLeave :: forall event. onDragLeave :: forall event.
T.Box Boolean T.Box Boolean
-> SE.SyntheticEvent_ event -> SE.SyntheticEvent_ event
-> Effect Unit -> Effect Unit
onDragLeave isDragOver _ = T.write_ false isDragOver onDragLeave box _ = T.write_ false box
onTaskFinish :: onTaskFinish ::
GT.NodeID GT.NodeID
...@@ -231,6 +233,27 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -231,6 +233,27 @@ nodeSpanCpt = here.component "nodeSpan" cpt
} }
[ [
-- // Abstract informations //
nodeTooltip
{ id
, nodeType
, name
}
[
case mVersion of
Nothing -> mempty
Just v -> versionComparator v
]
,
R.createPortal
[
fileTypeView
{ dispatch, droppedFile, id, isDragOver, nodeType } []
]
host
,
-- // Leaf informations data // -- // Leaf informations data //
folderIcon folderIcon
...@@ -240,15 +263,18 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -240,15 +263,18 @@ nodeSpanCpt = here.component "nodeSpan" cpt
} }
, ,
nodeIcon nodeIcon
(
{ nodeType { nodeType
, isLeaf , isLeaf
, callback: const $ T.modify_ (not) folderOpen , callback: const $ T.modify_ (not) folderOpen
, isSelected
} }
)
[ [
case mVersion of case mVersion of
Nothing -> mempty Nothing -> mempty
Just { clientVersion, remoteVersion} -> Just { clientVersion, remoteVersion} ->
B.iconButton B.iconButton $
{ className: intercalate " " { className: intercalate " "
[ "mainleaf__version-badge" [ "mainleaf__version-badge"
, clientVersion == remoteVersion ? , clientVersion == remoteVersion ?
...@@ -267,6 +293,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -267,6 +293,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt
, href , href
, id , id
, name: name' props.name nodeType session , name: name' props.name nodeType session
, type: nodeType
} }
, ,
...@@ -305,7 +332,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -305,7 +332,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt
{ boxes { boxes
, dispatch , dispatch
, id , id
, name: name' props.name nodeType session , name
, nodeType , nodeType
, onPopoverClose: const $ onPopoverClose popoverRef , onPopoverClose: const $ onPopoverClose popoverRef
, session , session
...@@ -325,26 +352,6 @@ nodeSpanCpt = here.component "nodeSpan" cpt ...@@ -325,26 +352,6 @@ nodeSpanCpt = here.component "nodeSpan" cpt
taskProgress taskProgress
{} {}
] ]
,
-- // Abstract informations //
nodeTooltip
{ id
, nodeType
, name: name' props.name nodeType session
}
[
case mVersion of
Nothing -> mempty
Just v -> versionComparator v
]
,
R.createPortal
[
fileTypeView
{ dispatch, droppedFile, id, isDragOver, nodeType } []
]
host
] ]
...@@ -354,6 +361,7 @@ type NodeIconProps = ...@@ -354,6 +361,7 @@ type NodeIconProps =
( nodeType :: GT.NodeType ( nodeType :: GT.NodeType
, callback :: Unit -> Effect Unit , callback :: Unit -> Effect Unit
, isLeaf :: Boolean , isLeaf :: Boolean
, isSelected :: Boolean
) )
nodeIcon :: R2.Component NodeIconProps nodeIcon :: R2.Component NodeIconProps
...@@ -363,7 +371,10 @@ nodeIconCpt = here.component "nodeIcon" cpt where ...@@ -363,7 +371,10 @@ nodeIconCpt = here.component "nodeIcon" cpt where
cpt { nodeType cpt { nodeType
, callback , callback
, isLeaf , isLeaf
} children = pure $ , isSelected
} children = do
-- Render
pure $
H.span H.span
{ className: "mainleaf__node-icon" } $ { className: "mainleaf__node-icon" } $
...@@ -372,6 +383,7 @@ nodeIconCpt = here.component "nodeIcon" cpt where ...@@ -372,6 +383,7 @@ nodeIconCpt = here.component "nodeIcon" cpt where
{ name: GT.getIcon nodeType true { name: GT.getIcon nodeType true
, callback , callback
, status: isLeaf ? Idled $ Enabled , status: isLeaf ? Idled $ Enabled
, variant: isSelected ? Primary $ Dark
} }
] ]
<> children <> children
...@@ -408,25 +420,25 @@ folderIconCpt = here.component "folderIcon" cpt where ...@@ -408,25 +420,25 @@ folderIconCpt = here.component "folderIcon" cpt where
----------------------------------------------- -----------------------------------------------
type NodeLinkProps = type NodeLinkProps =
( callback :: Unit -> Effect Unit ( callback :: Unit -> Effect Unit
, href :: String , href :: String
, id :: Int , id :: Int
, name :: GT.Name , name :: GT.Name
, type :: GT.NodeType
) )
nodeLink :: R2.Leaf NodeLinkProps nodeLink :: R2.Leaf NodeLinkProps
nodeLink = R2.leaf nodeLinkCpt nodeLink = R2.leaf nodeLinkCpt
nodeLinkCpt :: R.Component NodeLinkProps nodeLinkCpt :: R.Component NodeLinkProps
nodeLinkCpt = here.component "nodeLink" cpt nodeLinkCpt = here.component "nodeLink" cpt where
where
cpt { callback cpt { callback
, href , href
, id , id
, name , name
, type: nodeType
} _ = do } _ = do
-- Computed
let let
tid = tooltipId name id tid = tooltipId name id
...@@ -434,6 +446,7 @@ nodeLinkCpt = here.component "nodeLink" cpt ...@@ -434,6 +446,7 @@ nodeLinkCpt = here.component "nodeLink" cpt
{ href { href
} `Record.merge` B.tooltipBind tid } `Record.merge` B.tooltipBind tid
-- Render
pure $ pure $
H.div H.div
...@@ -444,10 +457,18 @@ nodeLinkCpt = here.component "nodeLink" cpt ...@@ -444,10 +457,18 @@ nodeLinkCpt = here.component "nodeLink" cpt
H.a H.a
aProps aProps
[ [
B.span_ $ textEllipsisBreak 15 name B.span_ $ nodeLinkText nodeType name
] ]
] ]
nodeLinkText :: GT.NodeType -> String -> String
nodeLinkText GT.NodeUser s = s # (truncateNodeUser)
>>> maybe s identity
nodeLinkText _ s = textEllipsisBreak 15 s
truncateNodeUser :: String -> Maybe String
truncateNodeUser = Regex.match (nodeUserRegexp) >=> flip NArray.index 1 >>> join
--------------------------------------------------- ---------------------------------------------------
type NodeTooltipProps = type NodeTooltipProps =
......
...@@ -31,15 +31,15 @@ type NodeActionsGraphProps = ...@@ -31,15 +31,15 @@ type NodeActionsGraphProps =
nodeActionsGraph :: R2.Component NodeActionsGraphProps nodeActionsGraph :: R2.Component NodeActionsGraphProps
nodeActionsGraph = R.createElement nodeActionsGraphCpt nodeActionsGraph = R.createElement nodeActionsGraphCpt
nodeActionsGraphCpt :: R.Component NodeActionsGraphProps nodeActionsGraphCpt :: R.Component NodeActionsGraphProps
nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt where
where cpt { id, graphVersions, session, refresh } _ =
cpt { id, graphVersions, session, refresh } _ = do let sameVersions = (graphVersions.gv_graph == Just graphVersions.gv_repo)
pure $ H.div { className: "node-actions" } [ in pure $
if graphVersions.gv_graph == Just graphVersions.gv_repo then
H.div {} [] R2.if' (not sameVersions) $
else
graphUpdateButton { id, session, refresh } graphUpdateButton { id, session, refresh }
]
type GraphUpdateButtonProps = type GraphUpdateButtonProps =
( id :: GT.ID ( id :: GT.ID
...@@ -99,12 +99,9 @@ type NodeActionsNodeListProps = ...@@ -99,12 +99,9 @@ type NodeActionsNodeListProps =
nodeActionsNodeList :: Record NodeActionsNodeListProps -> R.Element nodeActionsNodeList :: Record NodeActionsNodeListProps -> R.Element
nodeActionsNodeList p = R.createElement nodeActionsNodeListCpt p [] nodeActionsNodeList p = R.createElement nodeActionsNodeListCpt p []
nodeActionsNodeListCpt :: R.Component NodeActionsNodeListProps nodeActionsNodeListCpt :: R.Component NodeActionsNodeListProps
nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt where
where cpt props _ = pure $ nodeListUpdateButton props
cpt props _ = do
pure $ H.div { className: "node-actions" } [
nodeListUpdateButton props
]
type NodeListUpdateButtonProps = type NodeListUpdateButtonProps =
( listId :: GT.ListId ( listId :: GT.ListId
......
...@@ -132,9 +132,12 @@ graphCpt = here.component "graph" cpt where ...@@ -132,9 +132,12 @@ graphCpt = here.component "graph" cpt where
Sigma.stopForceAtlas2 sig Sigma.stopForceAtlas2 sig
case mCamera of case mCamera of
Nothing -> pure unit
Just (GET.Camera { ratio, x, y }) -> do Just (GET.Camera { ratio, x, y }) -> do
Sigma.updateCamera sig { ratio, x, y } Sigma.updateCamera sig { ratio, x, y }
-- Default camera: slightly de-zoom the graph to avoid
-- nodes sticking to the container borders
Nothing ->
Sigma.updateCamera sig { ratio: 1.1, x: 0.0, y: 0.0 }
-- Reload Sigma on Theme changes -- Reload Sigma on Theme changes
_ <- flip T.listen boxes.theme \{ old, new } -> _ <- flip T.listen boxes.theme \{ old, new } ->
......
module Gargantext.Components.GraphExplorer.Button module Gargantext.Components.GraphExplorer.Buttons
( Props, centerButton, simpleButton, cameraButton ) where ( Props
, centerButton
, simpleButton
, cameraButton
, edgesToggleButton
, louvainToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
, multiSelectEnabledButton
) where
import Prelude import Prelude
import DOM.Simple.Console (log2)
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Enum (fromEnum) import Data.Enum (fromEnum)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.String as DS import Data.String as DS
import DOM.Simple.Console (log2)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (launchAff_) import Effect.Aff (launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Now as EN import Effect.Now as EN
import Reactix as R import Gargantext.Components.Bootstrap as B
import Reactix.DOM.HTML as H import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryData) import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryData)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat(..)) import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat(..))
import Gargantext.Components.GraphExplorer.API (cloneGraph) import Gargantext.Components.GraphExplorer.API (cloneGraph)
import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.GraphExplorer.Utils as GEU import Gargantext.Components.GraphExplorer.Utils as GEU
import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Button" here = R2.here "Gargantext.Components.GraphExplorer.Button"
...@@ -36,9 +50,12 @@ type Props = ( ...@@ -36,9 +50,12 @@ type Props = (
, text :: String , text :: String
) )
-- @WIP
simpleButton :: Record Props -> R.Element simpleButton :: Record Props -> R.Element
simpleButton props = R.createElement simpleButtonCpt props [] simpleButton props = R.createElement simpleButtonCpt props []
------------------------------------------------------
simpleButtonCpt :: R.Component Props simpleButtonCpt :: R.Component Props
simpleButtonCpt = here.component "simpleButton" cpt simpleButtonCpt = here.component "simpleButton" cpt
where where
...@@ -48,14 +65,16 @@ simpleButtonCpt = here.component "simpleButton" cpt ...@@ -48,14 +65,16 @@ simpleButtonCpt = here.component "simpleButton" cpt
} [ R2.small {} [ H.text text ] ] } [ R2.small {} [ H.text text ] ]
centerButton :: R.Ref Sigmax.Sigma -> R.Element centerButton :: R.Ref Sigmax.Sigma -> R.Element
centerButton sigmaRef = simpleButton { centerButton sigmaRef = B.button
onClick: \_ -> do { variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[centerButton] sigma: Nothing" $ \s -> Sigmax.dependOnSigma sigma "[centerButton] sigma: Nothing" $ \s ->
Sigma.goToAllCameras s {x: 0.0, y: 0.0, ratio: 1.0, angle: 0.0} Sigma.goToAllCameras s {x: 0.0, y: 0.0, ratio: 1.0, angle: 0.0}
, text: "Center"
} }
[ H.text "Center" ]
------------------------------------------------------
type CameraButtonProps = type CameraButtonProps =
( id :: Int ( id :: Int
...@@ -71,8 +90,10 @@ cameraButton { id ...@@ -71,8 +90,10 @@ cameraButton { id
, hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph } , hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph }
, session , session
, sigmaRef , sigmaRef
, reloadForest } = simpleButton { , reloadForest } = B.button
onClick: \_ -> do
{ variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do
screen <- Sigma.takeScreenshot s screen <- Sigma.takeScreenshot s
...@@ -105,5 +126,171 @@ cameraButton { id ...@@ -105,5 +126,171 @@ cameraButton { id
Left err -> liftEffect $ log2 "[cameraButton] RESTError" err Left err -> liftEffect $ log2 "[cameraButton] RESTError" err
Right _ret -> do Right _ret -> do
liftEffect $ T2.reload reloadForest liftEffect $ T2.reload reloadForest
, text: "Screenshot"
} }
[ H.text "Screenshot" ]
------------------------------------------------------
type EdgesButtonProps =
( state :: T.Box SigmaxTypes.ShowEdgesState
, stateAtlas :: T.Box SigmaxTypes.ForceAtlasState
)
edgesToggleButton :: R2.Leaf EdgesButtonProps
edgesToggleButton = R2.leaf edgesToggleButtonCpt
edgesToggleButtonCpt :: R.Component EdgesButtonProps
edgesToggleButtonCpt = here.component "edgesToggleButton" cpt
where
cpt { state, stateAtlas } _ = do
-- States
state' <- R2.useLive' state
stateAtlas' <- R2.useLive' stateAtlas
-- Computed
let
cst SigmaxTypes.InitialRunning = Disabled
cst SigmaxTypes.Running = Disabled
cst _ = Enabled
-- Render
pure $
B.button
{ variant: state' == SigmaxTypes.EShow ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, status: cst stateAtlas'
-- TODO: Move this to Graph.purs to the R.useEffect handler which renders nodes/edges
, callback: \_ -> T.modify_ SigmaxTypes.toggleShowEdgesState state
}
[ H.text "Edges" ]
------------------------------------------------------
type LouvainToggleButtonProps =
( state :: T.Box Boolean
)
louvainToggleButton :: R2.Leaf LouvainToggleButtonProps
louvainToggleButton = R2.leaf louvainToggleButtonCpt
louvainToggleButtonCpt :: R.Component LouvainToggleButtonProps
louvainToggleButtonCpt = here.component "louvainToggleButton" cpt
where
cpt { state } _ = do
state' <- R2.useLive' state
pure $
B.button
{ variant: state' ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, callback: \_ -> T.modify_ (not) state
}
[ H.text "Louvain" ]
--------------------------------------------------------------
type ForceAtlasProps =
( state :: T.Box SigmaxTypes.ForceAtlasState
)
pauseForceAtlasButton :: R2.Leaf ForceAtlasProps
pauseForceAtlasButton = R2.leaf pauseForceAtlasButtonCpt
pauseForceAtlasButtonCpt :: R.Component ForceAtlasProps
pauseForceAtlasButtonCpt = here.component "forceAtlasToggleButton" cpt
where
cpt { state } _ = do
-- States
state' <- R2.useLive' state
-- Computed
let
cls SigmaxTypes.InitialRunning = "on-running-animation active"
cls SigmaxTypes.Running = "on-running-animation active"
cls _ = ""
vrt SigmaxTypes.InitialRunning = ButtonVariant Secondary
vrt SigmaxTypes.Running = ButtonVariant Secondary
vrt _ = OutlinedButtonVariant Secondary
icn SigmaxTypes.InitialRunning = "pause"
icn SigmaxTypes.InitialStopped = "play"
icn SigmaxTypes.Running = "pause"
icn SigmaxTypes.Paused = "play"
icn SigmaxTypes.Killed = "play"
-- Render
pure $
B.button
{ variant: vrt state'
, className: cls state'
, callback: \_ -> T.modify_ SigmaxTypes.toggleForceAtlasState state
}
[
B.icon
{ name: icn state'}
]
--------------------------------------------------------
type ResetForceAtlasProps =
( forceAtlasState :: T.Box SigmaxTypes.ForceAtlasState
, sigmaRef :: R.Ref Sigmax.Sigma
)
resetForceAtlasButton :: R2.Leaf ResetForceAtlasProps
resetForceAtlasButton = R2.leaf resetForceAtlasButtonCpt
resetForceAtlasButtonCpt :: R.Component ResetForceAtlasProps
resetForceAtlasButtonCpt = here.component "resetForceAtlasToggleButton" cpt
where
cpt { forceAtlasState, sigmaRef } _ = do
pure $ H.button { className: "btn btn-outline-secondary"
, on: { click: onClick forceAtlasState sigmaRef }
} [ R2.small {} [ H.text "Reset Force Atlas" ] ]
onClick forceAtlasState sigmaRef _ = do
-- TODO Sigma.killForceAtlas2 sigma
-- startForceAtlas2 sigma
Sigmax.dependOnSigma (R.readRef sigmaRef) "[resetForceAtlasButton] no sigma" $ \sigma -> do
Sigma.killForceAtlas2 sigma
Sigma.refreshForceAtlas sigma Graph.forceAtlas2Settings
T.write_ SigmaxTypes.Killed forceAtlasState
------------------------------------------------------------------
type MultiSelectEnabledButtonProps =
( state :: T.Box Boolean
)
multiSelectEnabledButton :: R2.Leaf MultiSelectEnabledButtonProps
multiSelectEnabledButton = R2.leaf multiSelectEnabledButtonCpt
multiSelectEnabledButtonCpt :: R.Component MultiSelectEnabledButtonProps
multiSelectEnabledButtonCpt = here.component "multiSelectEnabledButton" cpt
where
cpt { state } _ = do
state' <- R2.useLive' state
pure $
H.div
{ className: "btn-group"
, role: "group"
}
[
B.button
{ variant: state' ?
OutlinedButtonVariant Secondary $
ButtonVariant Secondary
, callback: \_ -> T.write_ false state
}
[ H.text "Single" ]
,
B.button
{ variant: state' ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, callback: \_ -> T.write_ true state
}
[ H.text "Multiple" ]
]
module Gargantext.Components.GraphExplorer.Legend module Gargantext.Components.GraphExplorer.Legend
( Props, legend, legendCpt ( Props, legend
) where ) where
import Prelude hiding (map) import Prelude hiding (map)
...@@ -7,31 +7,39 @@ import Prelude hiding (map) ...@@ -7,31 +7,39 @@ import Prelude hiding (map)
import Data.Sequence (Seq) import Data.Sequence (Seq)
import Data.Traversable (foldMap) import Data.Traversable (foldMap)
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as RH import Reactix.DOM.HTML as H
import Gargantext.Components.GraphExplorer.Types (Legend(..), intColor) import Gargantext.Components.GraphExplorer.Types (Legend(..), intColor)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Legend" here = R2.here "Gargantext.Components.GraphExplorer.Legend"
type Props = ( items :: Seq Legend ) type Props = ( items :: Seq Legend )
legend :: Record Props -> R.Element legend :: R2.Leaf Props
legend props = R.createElement legendCpt props [] legend = R2.leaf legendCpt
legendCpt :: R.Component Props legendCpt :: R.Component Props
legendCpt = here.component "legend" cpt legendCpt = here.component "legend" cpt where
where cpt { items } _ = pure $
cpt {items} _ = pure $ RH.div {} [foldMap entry items]
H.ul
entry :: Legend -> R.Element { className: "graph-legend" }
entry (Legend {id_, label}) = [
RH.p {} flip foldMap items \(Legend { id_, label }) ->
[ RH.span { style: { width : 10
, height: 10 H.li
, backgroundColor: intColor id_ { className: "graph-legend__item" }
, display: "inline-block" [
H.span
{ className: "graph-legend__code"
, style: { backgroundColor: intColor id_ }
} }
} [] []
, RH.text $ " " <> label ,
H.span
{ className: "graph-legend__caption" }
[ H.text label ]
]
] ]
...@@ -18,37 +18,45 @@ import Gargantext.Utils.Reactix as R2 ...@@ -18,37 +18,45 @@ import Gargantext.Utils.Reactix as R2
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.RangeControl" here = R2.here "Gargantext.Components.GraphExplorer.RangeControl"
type Props = ( type Props =
caption :: String ( caption :: String
, sliderProps :: Record RS.Props , sliderProps :: Record RS.Props
) )
rangeControl :: R2.Component Props rangeControl :: R2.Leaf Props
rangeControl = R.createElement rangeControlCpt rangeControl = R2.leaf rangeControlCpt
rangeControlCpt :: R.Component Props rangeControlCpt :: R.Component Props
rangeControlCpt = here.component "rangeButton" cpt rangeControlCpt = here.component "rangeButton" cpt
where where
cpt {caption, sliderProps} _ = do cpt {caption, sliderProps} _ = pure $
pure $
H.span {className: "range text-center"} H.span
[ H.label {} [ R2.small {} [ H.text caption ] ] { className: "range-control" }
, RS.rangeSlider sliderProps [
H.label
{ className: "range-control__label" }
[ H.text caption ]
,
RS.rangeSlider sliderProps
] ]
type EdgeConfluenceControlProps = ( ----------------------------------------
range :: Range.NumberRange
type EdgeConfluenceControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
edgeConfluenceControl :: R2.Component EdgeConfluenceControlProps edgeConfluenceControl :: R2.Leaf EdgeConfluenceControlProps
edgeConfluenceControl = R.createElement edgeConfluenceControlCpt edgeConfluenceControl = R2.leaf edgeConfluenceControlCpt
edgeConfluenceControlCpt :: R.Component EdgeConfluenceControlProps edgeConfluenceControlCpt :: R.Component EdgeConfluenceControlProps
edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -62,21 +70,24 @@ edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt ...@@ -62,21 +70,24 @@ edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
--------------------------------------
type EdgeWeightControlProps = ( type EdgeWeightControlProps =
range :: Range.NumberRange ( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
edgeWeightControl :: R2.Component EdgeWeightControlProps edgeWeightControl :: R2.Leaf EdgeWeightControlProps
edgeWeightControl = R.createElement edgeWeightControlCpt edgeWeightControl = R2.leaf edgeWeightControlCpt
edgeWeightControlCpt :: R.Component EdgeWeightControlProps edgeWeightControlCpt :: R.Component EdgeWeightControlProps
edgeWeightControlCpt = here.component "edgeWeightControl" cpt edgeWeightControlCpt = here.component "edgeWeightControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -90,21 +101,24 @@ edgeWeightControlCpt = here.component "edgeWeightControl" cpt ...@@ -90,21 +101,24 @@ edgeWeightControlCpt = here.component "edgeWeightControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
--------------------------------------
type NodeSideControlProps = ( type NodeSideControlProps =
range :: Range.NumberRange ( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
nodeSizeControl :: R2.Component NodeSideControlProps nodeSizeControl :: R2.Leaf NodeSideControlProps
nodeSizeControl = R.createElement nodeSizeControlCpt nodeSizeControl = R2.leaf nodeSizeControlCpt
nodeSizeControlCpt :: R.Component NodeSideControlProps nodeSizeControlCpt :: R.Component NodeSideControlProps
nodeSizeControlCpt = here.component "nodeSizeControl" cpt nodeSizeControlCpt = here.component "nodeSizeControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -118,4 +132,4 @@ nodeSizeControlCpt = here.component "nodeSizeControl" cpt ...@@ -118,4 +132,4 @@ nodeSizeControlCpt = here.component "nodeSizeControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
This diff is collapsed.
...@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search ...@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search
import Prelude import Prelude
import DOM.Simple.Console (log2) import DOM.Simple.Console (log2)
import Data.Foldable (foldl) import Data.Foldable (foldl, intercalate)
import Data.Sequence as Seq import Data.Sequence as Seq
import Data.Set as Set import Data.Set as Set
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete) import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete)
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Utils (queryMatchesLabel) import Gargantext.Utils (queryMatchesLabel)
...@@ -23,6 +24,7 @@ type Props = ( ...@@ -23,6 +24,7 @@ type Props = (
graph :: SigmaxT.SGraph graph :: SigmaxT.SGraph
, multiSelectEnabled :: T.Box Boolean , multiSelectEnabled :: T.Box Boolean
, selectedNodeIds :: T.Box SigmaxT.NodeIds , selectedNodeIds :: T.Box SigmaxT.NodeIds
, className :: String
) )
-- | Whether a node matches a search string -- | Whether a node matches a search string
...@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax ...@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax
searchNodes "" _ = Seq.empty searchNodes "" _ = Seq.empty
searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes
nodeSearchControl :: R2.Component Props nodeSearchControl :: R2.Leaf Props
nodeSearchControl = R.createElement nodeSearchControlCpt nodeSearchControl = R2.leaf nodeSearchControlCpt
nodeSearchControlCpt :: R.Component Props nodeSearchControlCpt :: R.Component Props
nodeSearchControlCpt = here.component "nodeSearchControl" cpt nodeSearchControlCpt = here.component "nodeSearchControl" cpt
where where
cpt { graph, multiSelectEnabled, selectedNodeIds } _ = do cpt props@{ graph, multiSelectEnabled, selectedNodeIds } _ = do
search <- T.useBox "" search <- T.useBox ""
search' <- T.useLive T.unequal search search' <- T.useLive T.unequal search
multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled
let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds
pure $ R.fragment pure $
[ inputWithAutocomplete { autocompleteSearch: autocompleteSearch graph
, classes: "mx-2" H.form
{ className: intercalate " "
[ "graph-node-search"
, props.className
]
}
[
inputWithAutocomplete
{ autocompleteSearch: autocompleteSearch graph
, onAutocompleteClick: doSearch , onAutocompleteClick: doSearch
, onEnterPress: doSearch , onEnterPress: doSearch
, state: search } [] , classes: ""
, H.div { className: "btn input-group-addon" , state: search
, on: { click: \_ -> doSearch search' }
} }
[ H.span { className: "fa fa-search" } [] ] ,
B.button
{ callback: \_ -> doSearch search'
, type: "submit"
, className: "graph-node-search__submit"
}
[
B.icon { name: "search"}
]
] ]
autocompleteSearch :: SigmaxT.SGraph -> String -> Array String autocompleteSearch :: SigmaxT.SGraph -> String -> Array String
......
module Gargantext.Components.GraphExplorer.Sidebar.Types where module Gargantext.Components.GraphExplorer.Sidebar.Types where
import Data.Maybe (Maybe(..), maybe)
import Data.Set as Set
import Reactix as R
import Toestand as T
import Gargantext.Prelude import Gargantext.Prelude
import Data.Maybe (Maybe(..), maybe)
import Data.Set as Set
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Types as GT
import Reactix as R
import Toestand as T
type SidePanel = type SidePanel =
( (
...@@ -19,6 +19,7 @@ type SidePanel = ...@@ -19,6 +19,7 @@ type SidePanel =
, selectedNodeIds :: SigmaxT.NodeIds , selectedNodeIds :: SigmaxT.NodeIds
, showControls :: Boolean , showControls :: Boolean
, sideTab :: GET.SideTab , sideTab :: GET.SideTab
, showSidebar :: GT.SidePanelState
) )
initialSidePanel :: Maybe (Record SidePanel) initialSidePanel :: Maybe (Record SidePanel)
...@@ -32,7 +33,9 @@ focusedSidePanel :: T.Box (Maybe (Record SidePanel)) ...@@ -32,7 +33,9 @@ focusedSidePanel :: T.Box (Maybe (Record SidePanel))
, removedNodeIds :: T.Box SigmaxT.NodeIds , removedNodeIds :: T.Box SigmaxT.NodeIds
, selectedNodeIds :: T.Box SigmaxT.NodeIds , selectedNodeIds :: T.Box SigmaxT.NodeIds
, showControls :: T.Box Boolean , showControls :: T.Box Boolean
, sideTab :: T.Box GET.SideTab } , sideTab :: T.Box GET.SideTab
, showSidebar :: T.Box GT.SidePanelState
}
focusedSidePanel sidePanel = do focusedSidePanel sidePanel = do
mGraph <- T.useFocused mGraph <- T.useFocused
(maybe Nothing _.mGraph) (maybe Nothing _.mGraph)
...@@ -55,6 +58,9 @@ focusedSidePanel sidePanel = do ...@@ -55,6 +58,9 @@ focusedSidePanel sidePanel = do
sideTab <- T.useFocused sideTab <- T.useFocused
(maybe GET.SideTabLegend _.sideTab) (maybe GET.SideTabLegend _.sideTab)
(\val -> maybe Nothing (\sp -> Just $ sp { sideTab = val })) sidePanel (\val -> maybe Nothing (\sp -> Just $ sp { sideTab = val })) sidePanel
showSidebar <- T.useFocused
(maybe GT.InitialClosed _.showSidebar)
(\val -> maybe Nothing (\sp -> Just $ sp { showSidebar = val })) sidePanel
pure $ { pure $ {
mGraph mGraph
...@@ -64,4 +70,5 @@ focusedSidePanel sidePanel = do ...@@ -64,4 +70,5 @@ focusedSidePanel sidePanel = do
, selectedNodeIds , selectedNodeIds
, showControls , showControls
, sideTab , sideTab
, showSidebar
} }
...@@ -34,20 +34,35 @@ sizeButtonCpt :: R.Component Props ...@@ -34,20 +34,35 @@ sizeButtonCpt :: R.Component Props
sizeButtonCpt = here.component "sizeButton" cpt where sizeButtonCpt = here.component "sizeButton" cpt where
cpt { state, caption, min, max, onChange } _ = do cpt { state, caption, min, max, onChange } _ = do
defaultValue <- T.useLive T.unequal state defaultValue <- T.useLive T.unequal state
pure $ H.span { className: "range-simple" }
[ H.label {} [ R2.small {} [ H.text caption ] ] pure $
, H.input { type: "range"
, className: "form-control" H.span
{ className: "range-simple" }
[
H.label
{ className: "range-simple__label" }
[ H.text caption ]
,
H.span
{ className: "range-simple__field" }
[
H.input
{ type: "range"
, min: show min , min: show min
, max: show max , max: show max
, defaultValue , defaultValue
, on: { input: onChange } }] , on: { input: onChange }
, className: "range-simple__input"
}
]
]
labelSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element labelSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element
labelSizeButton sigmaRef state = labelSizeButton sigmaRef state =
sizeButton { sizeButton {
state state
, caption: "Label Size" , caption: "Label size"
, min: 1.0 , min: 1.0
, max: 30.0 , max: 30.0
, onChange: \e -> do , onChange: \e -> do
...@@ -67,7 +82,7 @@ mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element ...@@ -67,7 +82,7 @@ mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element
mouseSelectorSizeButton sigmaRef state = mouseSelectorSizeButton sigmaRef state =
sizeButton { sizeButton {
state state
, caption: "Selector Size" , caption: "Selector size"
, min: 1.0 , min: 1.0
, max: 50.0 , max: 50.0
, onChange: \e -> do , onChange: \e -> do
......
...@@ -3,27 +3,18 @@ module Gargantext.Components.GraphExplorer.ToggleButton ...@@ -3,27 +3,18 @@ module Gargantext.Components.GraphExplorer.ToggleButton
, toggleButton , toggleButton
, toggleButtonCpt , toggleButtonCpt
, controlsToggleButton , controlsToggleButton
, edgesToggleButton
, louvainToggleButton
, multiSelectEnabledButton
, sidebarToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
) where ) where
import Prelude import Prelude
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Graph as Graph
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Toestand as T import Toestand as T
-- @WIP: used?
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.ToggleButton" here = R2.here "Gargantext.Components.GraphExplorer.ToggleButton"
...@@ -56,6 +47,8 @@ toggleButtonCpt = here.component "toggleButton" cpt ...@@ -56,6 +47,8 @@ toggleButtonCpt = here.component "toggleButton" cpt
text on _off true = on text on _off true = on
text _on off false = off text _on off false = off
----------------------------------------------------------------
type ControlsToggleButtonProps = ( type ControlsToggleButtonProps = (
state :: T.Box Boolean state :: T.Box Boolean
) )
...@@ -73,147 +66,3 @@ controlsToggleButtonCpt = here.component "controlsToggleButton" cpt ...@@ -73,147 +66,3 @@ controlsToggleButtonCpt = here.component "controlsToggleButton" cpt
, onClick: \_ -> T.modify_ not state , onClick: \_ -> T.modify_ not state
, style: "light" , style: "light"
} [] } []
type EdgesButtonProps = (
state :: T.Box SigmaxTypes.ShowEdgesState
)
edgesToggleButton :: R2.Component EdgesButtonProps
edgesToggleButton = R.createElement edgesToggleButtonCpt
edgesToggleButtonCpt :: R.Component EdgesButtonProps
edgesToggleButtonCpt = here.component "edgesToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.button { className: "btn btn-outline-secondary " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text state') ] ]
text s = if SigmaxTypes.edgeStateHidden s then "Show edges" else "Hide edges"
cls SigmaxTypes.EShow = ""
cls _ = "active"
-- TODO: Move this to Graph.purs to the R.useEffect handler which renders nodes/edges
onClick state _ = T.modify_ SigmaxTypes.toggleShowEdgesState state
type LouvainToggleButtonProps = (
state :: T.Box Boolean
)
louvainToggleButton :: R2.Component LouvainToggleButtonProps
louvainToggleButton = R.createElement louvainToggleButtonCpt
louvainToggleButtonCpt :: R.Component LouvainToggleButtonProps
louvainToggleButtonCpt = here.component "louvainToggleButton" cpt
where
cpt { state } _ = do
pure $ toggleButton {
state: state
, onMessage: "Louvain off"
, offMessage: "Louvain on"
, onClick: \_ -> T.modify_ not state
, style: "secondary"
} []
type MultiSelectEnabledButtonProps = (
state :: T.Box Boolean
)
multiSelectEnabledButton :: R2.Component MultiSelectEnabledButtonProps
multiSelectEnabledButton = R.createElement multiSelectEnabledButtonCpt
multiSelectEnabledButtonCpt :: R.Component MultiSelectEnabledButtonProps
multiSelectEnabledButtonCpt = here.component "lmultiSelectEnabledButton" cpt
where
cpt { state } _ = do
pure $ toggleButton {
state: state
, onMessage: "Single-node"
, offMessage: "Multi-node"
, onClick: \_ -> T.modify_ not state
, style : "primary"
} []
type ForceAtlasProps = (
state :: T.Box SigmaxTypes.ForceAtlasState
)
pauseForceAtlasButton :: R2.Component ForceAtlasProps
pauseForceAtlasButton = R.createElement pauseForceAtlasButtonCpt
pauseForceAtlasButtonCpt :: R.Component ForceAtlasProps
pauseForceAtlasButtonCpt = here.component "forceAtlasToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.button { className: "btn btn-outline-secondary " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text state') ] ]
cls SigmaxTypes.InitialRunning = "active"
cls SigmaxTypes.Running = "active"
cls _ = ""
text SigmaxTypes.InitialRunning = "Pause"
text SigmaxTypes.InitialStopped = "Start"
text SigmaxTypes.Running = "Pause"
text SigmaxTypes.Paused = "Start"
text SigmaxTypes.Killed = "Start"
onClick state _ = T.modify_ SigmaxTypes.toggleForceAtlasState state
type ResetForceAtlasProps = (
forceAtlasState :: T.Box SigmaxTypes.ForceAtlasState
, sigmaRef :: R.Ref Sigmax.Sigma
)
resetForceAtlasButton :: R2.Component ResetForceAtlasProps
resetForceAtlasButton = R.createElement resetForceAtlasButtonCpt
resetForceAtlasButtonCpt :: R.Component ResetForceAtlasProps
resetForceAtlasButtonCpt = here.component "resetForceAtlasToggleButton" cpt
where
cpt { forceAtlasState, sigmaRef } _ = do
pure $ H.button { className: "btn btn-outline-secondary"
, on: { click: onClick forceAtlasState sigmaRef }
} [ R2.small {} [ H.text "Reset Force Atlas" ] ]
onClick forceAtlasState sigmaRef _ = do
-- TODO Sigma.killForceAtlas2 sigma
-- startForceAtlas2 sigma
Sigmax.dependOnSigma (R.readRef sigmaRef) "[resetForceAtlasButton] no sigma" $ \sigma -> do
Sigma.killForceAtlas2 sigma
Sigma.refreshForceAtlas sigma Graph.forceAtlas2Settings
T.write_ SigmaxTypes.Killed forceAtlasState
type SidebarToggleButtonProps = (
state :: T.Box GT.SidePanelState
)
sidebarToggleButton :: R2.Component SidebarToggleButtonProps
sidebarToggleButton = R.createElement sidebarToggleButtonCpt
sidebarToggleButtonCpt :: R.Component SidebarToggleButtonProps
sidebarToggleButtonCpt = here.component "sidebarToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.div { className: "btn btn-outline-light " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text onMessage offMessage state') ] ]
cls GT.Opened = "active"
cls _ = ""
onMessage = "Hide Sidebar"
offMessage = "Show Sidebar"
text on _off GT.Opened = on
text _on off GT.InitialClosed = off
text _on off GT.Closed = off
onClick state = \_ ->
T.modify_ GT.toggleSidePanelState state
-- case s of
-- GET.InitialClosed -> GET.Opened GET.SideTabLegend
-- GET.Closed -> GET.Opened GET.SideTabLegend
-- (GET.Opened _) -> GET.Closed) state
module Gargantext.Components.GraphExplorer.TopBar where module Gargantext.Components.GraphExplorer.TopBar (topBar) where
import Data.Maybe (Maybe(..)) import Gargantext.Prelude hiding (max, min)
import Reactix as R
import Reactix.DOM.HTML as RH
import Toestand as T
import Gargantext.Prelude hiding (max,min) import Data.Maybe (Maybe)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl) import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.GraphExplorer.ToggleButton as Toggle import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
type Props =
( sidePanelGraph :: T.Box (Maybe (Record GEST.SidePanel))
)
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.TopBar" here = R2.here "Gargantext.Components.GraphExplorer.TopBar"
type TopBar = topBar :: R2.Leaf Props
( topBar = R2.leaf component
boxes :: Boxes
)
topBar :: R2.Leaf TopBar component :: R.Component Props
topBar = R2.leafComponent topBarCpt component = here.component "topBar" cpt where
topBarCpt :: R.Component TopBar cpt { sidePanelGraph } _ = do
topBarCpt = here.component "topBar" cpt where -- States
cpt { boxes: { sidePanelGraph { mGraph
, sidePanelState } } _ = do , multiSelectEnabled
{ mGraph, multiSelectEnabled, selectedNodeIds, showControls } <- GEST.focusedSidePanel sidePanelGraph , selectedNodeIds
, showControls
, showSidebar
} <- GEST.focusedSidePanel sidePanelGraph
mGraph' <- T.useLive T.unequal mGraph mGraph' <- R2.useLive' mGraph
showControls' <- R2.useLive' showControls
showSidebar' <- R2.useLive' showSidebar
let search = case mGraph' of -- Render
Just graph -> nodeSearchControl { graph pure $
, multiSelectEnabled
, selectedNodeIds } [] H.div
Nothing -> RH.div {} [] { className: "graph-topbar" }
[
-- Toolbar toggle
B.button
{ className: "graph-topbar__toolbar"
, callback: \_ -> T.modify_ (not) showControls
, variant: showControls' ?
ButtonVariant Light $
OutlinedButtonVariant Light
}
[
H.text $ showControls' ? "Hide toolbar" $ "Show toolbar"
]
,
-- Sidebar toggle
B.button
{ className: "graph-topbar__sidebar"
, callback: \_ -> T.modify_ GT.toggleSidePanelState showSidebar
pure $ RH.form { className: "graph-topbar d-flex" } , variant: showSidebar' == GT.Opened ?
[ Toggle.controlsToggleButton { state: showControls } [] ButtonVariant Light $
, Toggle.sidebarToggleButton { state: sidePanelState } [] OutlinedButtonVariant Light
, search }
[
H.text $ showSidebar' == GT.Opened ?
"Hide sidebar" $
"Show sidebar"
]
,
-- Search
R2.fromMaybe_ mGraph' \graph ->
nodeSearchControl
{ graph
, multiSelectEnabled
, selectedNodeIds
, className: "graph-topbar__search"
}
] ]
...@@ -26,7 +26,8 @@ type UserInfo ...@@ -26,7 +26,8 @@ type UserInfo
, ui_cwTouchPhone :: Maybe String , ui_cwTouchPhone :: Maybe String
, ui_cwTouchMail :: Maybe String } , ui_cwTouchMail :: Maybe String }
type UserInfoM type UserInfoM
= { ui_id :: NotNull Int = { token :: NotNull String
, ui_id :: NotNull Int
, ui_username :: String , ui_username :: String
, ui_email :: String , ui_email :: String
, ui_title :: String , ui_title :: String
......
...@@ -29,8 +29,8 @@ type Props = ...@@ -29,8 +29,8 @@ type Props =
, state :: T.Box String , state :: T.Box String
) )
inputWithAutocomplete :: R2.Component Props inputWithAutocomplete :: R2.Leaf Props
inputWithAutocomplete = R.createElement inputWithAutocompleteCpt inputWithAutocomplete = R2.leaf inputWithAutocompleteCpt
inputWithAutocompleteCpt :: R.Component Props inputWithAutocompleteCpt :: R.Component Props
inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
where where
......
...@@ -7,9 +7,6 @@ module Gargantext.Components.Nodes.Annuaire.User.Contact ...@@ -7,9 +7,6 @@ module Gargantext.Components.Nodes.Annuaire.User.Contact
, saveUserInfo , saveUserInfo
) where ) where
import Gargantext.Components.GraphQL.User (UserInfo, _ui_cwCity, _ui_cwCountry, _ui_cwFirstName, _ui_cwLabTeamDeptsFirst, _ui_cwLastName, _ui_cwOffice, _ui_cwOrganizationFirst, _ui_cwRole, _ui_cwTouchMail, _ui_cwTouchPhone)
import Gargantext.Prelude (Unit, bind, discard, pure, show, ($), (<$>), (<>))
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Lens as L import Data.Lens as L
import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
...@@ -19,6 +16,7 @@ import Effect.Class (liftEffect) ...@@ -19,6 +16,7 @@ import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.GraphQL (getClient) import Gargantext.Components.GraphQL (getClient)
import Gargantext.Components.GraphQL.Endpoints (getUserInfo) import Gargantext.Components.GraphQL.Endpoints (getUserInfo)
import Gargantext.Components.GraphQL.User (UserInfo, _ui_cwCity, _ui_cwCountry, _ui_cwFirstName, _ui_cwLabTeamDeptsFirst, _ui_cwLastName, _ui_cwOffice, _ui_cwOrganizationFirst, _ui_cwRole, _ui_cwTouchMail, _ui_cwTouchPhone)
import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData', HyperdataContact(..)) import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData', HyperdataContact(..))
...@@ -26,8 +24,9 @@ import Gargantext.Components.Nodes.Lists.Types as LT ...@@ -26,8 +24,9 @@ import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Prelude (Unit, bind, discard, pure, show, ($), (<$>), (<>))
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, get, put, sessionId) import Gargantext.Sessions (Session(..), get, put, sessionId)
import Gargantext.Types (NodeType(..)) import Gargantext.Types (NodeType(..))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
...@@ -196,11 +195,13 @@ saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "") ...@@ -196,11 +195,13 @@ saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "")
saveUserInfo :: Session -> Int -> UserInfo -> AffRESTError Int saveUserInfo :: Session -> Int -> UserInfo -> AffRESTError Int
saveUserInfo session id ui = do saveUserInfo session id ui = do
let token = getToken session
client <- liftEffect $ getClient session client <- liftEffect $ getClient session
res <- mutation res <- mutation
client client
"update user_info" "update user_info"
{ update_user_info: onlyArgs { ui_id: id { update_user_info: onlyArgs { token: token
, ui_id: id
, ui_cwFirstName: ga ui.ui_cwFirstName , ui_cwFirstName: ga ui.ui_cwFirstName
, ui_cwLastName: ga ui.ui_cwLastName , ui_cwLastName: ga ui.ui_cwLastName
, ui_cwOrganization: ui.ui_cwOrganization , ui_cwOrganization: ui.ui_cwOrganization
...@@ -215,6 +216,7 @@ saveUserInfo session id ui = do ...@@ -215,6 +216,7 @@ saveUserInfo session id ui = do
where where
ga Nothing = ArgL IgnoreArg ga Nothing = ArgL IgnoreArg
ga (Just val) = ArgR val ga (Just val) = ArgR val
getToken (Session { token }) = token
type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps ) type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps )
......
...@@ -5,19 +5,20 @@ module Gargantext.Components.Nodes.Corpus.Phylo ...@@ -5,19 +5,20 @@ module Gargantext.Components.Nodes.Corpus.Phylo
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple (document, querySelector) import DOM.Simple (document, querySelector)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..), isJust)
import FFI.Simple ((..), (.=)) import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.API (get) import Gargantext.Components.PhyloExplorer.API (get)
import Gargantext.Components.PhyloExplorer.Layout (layout) import Gargantext.Components.PhyloExplorer.Layout (layout)
import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet) import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet)
import Gargantext.Config.REST (logRESTError) import Gargantext.Config.REST (logRESTError)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H
type MainProps = type MainProps =
( nodeId :: NodeID ( nodeId :: NodeID
...@@ -35,60 +36,93 @@ phyloLayoutCpt :: R.Component MainProps ...@@ -35,60 +36,93 @@ phyloLayoutCpt :: R.Component MainProps
phyloLayoutCpt = here.component "main" cpt where phyloLayoutCpt = here.component "main" cpt where
cpt { nodeId, session } _ = do cpt { nodeId, session } _ = do
-- | States
-- |
state' /\ state <- R2.useBox' Nothing
-- | Computed
-- |
let let
errorHandler = logRESTError here "[phylo]" errorHandler = logRESTError here "[phylo]"
handler (dataset :: PhyloDataSet) = handler (phyloDataSet :: PhyloDataSet) =
content layout
{ nodeId { nodeId
, dataset , phyloDataSet
} }
useLoader
-- | Hooks
-- |
useLoaderEffect
{ errorHandler { errorHandler
, loader: get session , loader: get session
, path: nodeId , path: nodeId
, render: handler , state
} }
-- @XXX: Runtime odd behavior
-- cannot use the `useEffect` + its cleanup function within the
-- same `Effect`, otherwise the below cleanup example will be
-- execute at mount
-------------------------------------------------------- -- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
type ContentProps = mEl <- querySelector document ".main-page__main-route .container"
( nodeId :: NodeID
, dataset :: PhyloDataSet
)
content :: R2.Leaf ContentProps
content = R2.leaf contentCpt
contentCpt :: R.Component ContentProps case mEl of
contentCpt = here.component "content" cpt where Nothing -> R.nothing
cpt { nodeId, dataset } _ = do Just el -> R2.addClass el [ "d-none" ]
-- Hooks
useFirstEffect' do R.useEffectOnce do
-- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?) pure do
mEl <- querySelector document ".main-page__main-route .container" mEl <- querySelector document ".main-page__main-route .container"
case mEl of case mEl of
Nothing -> pure unit Nothing -> R.nothing
Just el -> do Just el -> R2.removeClass el [ "d-none" ]
style <- pure $ (el .. "style")
pure $ (style .= "display") "none"
-- @XXX: reset "main-page__main-route" wrapper margin -- @XXX: reset "main-page__main-route" wrapper margin
-- see Gargantext.Components.Router) (@TODO?) -- see Gargantext.Components.Router) (@TODO?)
mEl' <- querySelector document ".main-page__main-route" R.useEffectOnce' do
case mEl' of mEl <- querySelector document ".main-page__main-route"
Nothing -> pure unit
Just el -> do case mEl of
style <- pure $ (el .. "style") Nothing -> R.nothing
pure $ (style .= "padding") "initial" Just el -> R2.addClass el [ "p-0" ]
-- Render R.useEffectOnce do
pure do
mEl <- querySelector document ".main-page__main-route"
case mEl of
Nothing -> R.nothing
Just el -> R2.removeClass el [ "p-0" ]
-- | Render
-- |
pure $ pure $
layout B.cloak
{ nodeId { isDisplayed: isJust state'
, phyloDataSet: dataset , idlingPhaseDuration: Just 150
, cloakSlot:
-- mimicking `PhyloExplorer.layout` preloading template
H.div
{ className: "phylo" }
[
H.div
{ className: "phylo__spinner-wrapper" }
[
B.spinner
{ className: "phylo__spinner" }
]
]
, defaultSlot:
R2.fromMaybe_ state' handler
} }
module Gargantext.Components.Nodes.Corpus.Graph
( graphLayout
) where
import Gargantext.Prelude
import DOM.Simple (document, querySelector)
import Data.Maybe (Maybe(..), isJust)
import Data.Set as Set
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.Layout (convert, layout)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Sessions (Session, get)
import Gargantext.Types as Types
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
type Props =
( key :: String
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
here :: R2.Here
here = R2.here "Gargantext.Components.Nodes.Corpus.Graph"
graphLayout :: R2.Leaf Props
graphLayout = R2.leaf graphLayoutCpt
graphLayoutCpt :: R.Component Props
graphLayoutCpt = here.component "explorerLayout" cpt where
cpt props@{ boxes: { graphVersion }, graphId, session } _ = do
-- | States
-- |
graphVersion' <- T.useLive T.unequal graphVersion
state' /\ state <- R2.useBox' Nothing
-- | Hooks
-- |
useLoaderEffect
{ errorHandler
, loader: getNodes session graphVersion'
, path: graphId
, state
}
-- @XXX: Runtime odd behavior
-- cannot use the `useEffect` + its cleanup function within the
-- same `Effect`, otherwise the below cleanup example will be
-- execute at mount
-- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route .container"
case mEl of
Nothing -> R.nothing
Just el -> R2.addClass el [ "d-none" ]
R.useEffectOnce do
pure do
mEl <- querySelector document ".main-page__main-route .container"
case mEl of
Nothing -> R.nothing
Just el -> R2.removeClass el [ "d-none" ]
-- @XXX: reset "main-page__main-route" wrapper margin
-- see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route"
case mEl of
Nothing -> R.nothing
Just el -> R2.addClass el [ "p-0" ]
R.useEffectOnce do
pure do
mEl <- querySelector document ".main-page__main-route"
case mEl of
Nothing -> R.nothing
Just el -> R2.removeClass el [ "p-0" ]
-- | Render
-- |
pure $
B.cloak
{ isDisplayed: isJust state'
, idlingPhaseDuration: Just 150
, cloakSlot:
H.div
{ className: "graph-loader" }
[
B.spinner
{ className: "graph-loader__spinner" }
]
, defaultSlot:
R2.fromMaybe_ state' handler
}
where
errorHandler = logRESTError here "[explorerLayout]"
handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) =
content { graph
, hyperdataGraph: loaded
, mMetaData'
, session
, boxes: props.boxes
, graphId
}
where
Tuple mMetaData' graph = convert hyperdataGraph
--------------------------------------------------------
type ContentProps =
( mMetaData' :: Maybe GET.MetaData
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
content :: R2.Leaf ContentProps
content = R2.leaf contentCpt
contentCpt :: R.Component ContentProps
contentCpt = here.component "content" cpt where
cpt props@{ boxes, mMetaData', graph } _ = do
-- Hooks
R.useEffectOnce' $
-- Hydrate Boxes
flip T.write_ boxes.sidePanelGraph $ Just
{ mGraph: Just graph
, mMetaData: mMetaData'
, multiSelectEnabled: false
, removedNodeIds: Set.empty
, selectedNodeIds: Set.empty
, showControls: false
, sideTab: GET.SideTabLegend
, showSidebar: Types.InitialClosed
}
-- Render
pure $
layout
props
--------------------------------------------------------------
getNodes :: Session -> T2.Reload -> GET.GraphId -> AffRESTError GET.HyperdataGraph
getNodes session graphVersion graphId =
get session $ NodeAPI Types.Graph
(Just graphId)
("?version=" <> (show graphVersion))
...@@ -21,7 +21,7 @@ import Gargantext.Components.PhyloExplorer.TopBar (topBar) ...@@ -21,7 +21,7 @@ import Gargantext.Components.PhyloExplorer.TopBar (topBar)
import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), PhyloDataSet(..), ExtractedTerm, ExtractedCount, Source, Term, sortSources) import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), PhyloDataSet(..), ExtractedTerm, ExtractedCount, Source, Term, sortSources)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1') import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID, SidePanelState(..))
import Gargantext.Utils (getter, (?)) import Gargantext.Utils (getter, (?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Graphics.D3.Base (d3) import Graphics.D3.Base (d3)
...@@ -46,6 +46,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -46,6 +46,7 @@ layoutCpt = here.component "layout" cpt where
cpt { phyloDataSet: (PhyloDataSet o) cpt { phyloDataSet: (PhyloDataSet o)
, nodeId , nodeId
} _ = do } _ = do
-- States -- States
--------- ---------
...@@ -82,7 +83,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -82,7 +83,7 @@ layoutCpt = here.component "layout" cpt where
R2.useBox' false R2.useBox' false
sideBarDisplayed /\ sideBarDisplayedBox <- sideBarDisplayed /\ sideBarDisplayedBox <-
R2.useBox' false R2.useBox' InitialClosed
extractedTerms /\ extractedTermsBox <- extractedTerms /\ extractedTermsBox <-
R2.useBox' (mempty :: Array ExtractedTerm) R2.useBox' (mempty :: Array ExtractedTerm)
...@@ -256,7 +257,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -256,7 +257,7 @@ layoutCpt = here.component "layout" cpt where
{ className: "phylo__sidebar" { className: "phylo__sidebar"
-- @XXX: ReactJS lack of "keep-alive" feature workaround solution -- @XXX: ReactJS lack of "keep-alive" feature workaround solution
-- @link https://github.com/facebook/react/issues/12039 -- @link https://github.com/facebook/react/issues/12039
, style: { display: sideBarDisplayed ? "block" $ "none" } , style: { display: sideBarDisplayed == Opened? "block" $ "none" }
} }
[ [
sideBar sideBar
......
...@@ -86,7 +86,7 @@ component = R.hooksComponent componentName cpt where ...@@ -86,7 +86,7 @@ component = R.hooksComponent componentName cpt where
B.caveat B.caveat
{ className: "phylo-selection-tab__nil" } { className: "phylo-selection-tab__nil" }
[ [
H.text "No selection has been made" H.text "Select term, branch or source to get their informations"
] ]
, ,
-- Selected source -- Selected source
...@@ -212,8 +212,7 @@ component = R.hooksComponent componentName cpt where ...@@ -212,8 +212,7 @@ component = R.hooksComponent componentName cpt where
{ className: "phylo-selection-tab__separator" } { className: "phylo-selection-tab__separator" }
[ [
B.icon B.icon
{ name: "angle-down" { name: "angle-down" }
}
] ]
, ,
-- No extracted result -- No extracted result
...@@ -291,6 +290,7 @@ component = R.hooksComponent componentName cpt where ...@@ -291,6 +290,7 @@ component = R.hooksComponent componentName cpt where
] ]
, ,
R2.if' (truncateResults) $ R2.if' (truncateResults) $
B.button B.button
{ variant: ButtonVariant Light { variant: ButtonVariant Light
, callback: \_ -> T.modify_ not showMoreBox , callback: \_ -> T.modify_ not showMoreBox
......
...@@ -4,15 +4,14 @@ module Gargantext.Components.PhyloExplorer.SideBar ...@@ -4,15 +4,14 @@ module Gargantext.Components.PhyloExplorer.SideBar
import Gargantext.Prelude import Gargantext.Prelude
import Data.Foldable (intercalate)
import Data.Maybe (Maybe) import Data.Maybe (Maybe)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.DetailsTab (detailsTab) import Gargantext.Components.PhyloExplorer.DetailsTab (detailsTab)
import Gargantext.Components.PhyloExplorer.SelectionTab (selectionTab) import Gargantext.Components.PhyloExplorer.SelectionTab (selectionTab)
import Gargantext.Components.PhyloExplorer.Types (ExtractedCount, ExtractedTerm, TabView(..)) import Gargantext.Components.PhyloExplorer.Types (ExtractedCount, ExtractedTerm, TabView(..))
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -48,60 +47,27 @@ component = R.hooksComponent componentName cpt where ...@@ -48,60 +47,27 @@ component = R.hooksComponent componentName cpt where
-- States -- States
tabView /\ tabViewBox <- R2.useBox' DetailsTab tabView /\ tabViewBox <- R2.useBox' DetailsTab
-- Computed
let
tabList = [ DetailsTab, SelectionTab ]
-- Render -- Render
pure $ pure $
H.div H.div
{ className: "phylo-sidebar" } { className: "phylo-sidebar" }
[ [
-- Teasers
H.div
{ className: "phylo-sidebar__top-teaser" }
[]
,
-- Menu -- Menu
H.ul B.tabs
{ className: intercalate " " { value: tabView
[ "nav nav-tabs" , list: tabList
, "phylo-sidebar__menu" , callback: flip T.write_ tabViewBox
]
}
[
H.li
{ className: "nav-item"
, on: { click: \_ -> T.write_ DetailsTab tabViewBox }
}
[
H.a
{ className: intercalate " "
[ "nav-link"
, tabView == DetailsTab ? "active" $ ""
]
}
[
H.text "Details"
]
]
,
H.li
{ className: "nav-item"
, on: { click: \_ -> T.write_ SelectionTab tabViewBox }
}
[
H.a
{ className: intercalate " "
[ "nav-link"
, tabView == SelectionTab ? "active" $ ""
]
} }
[
H.text "Selection"
]
]
]
, ,
-- Details tab -- Content
R2.if' (tabView == DetailsTab) $ case tabView of
DetailsTab ->
detailsTab detailsTab
{ key: (show props.nodeId) <> "-details" { key: (show props.nodeId) <> "-details"
, docCount: props.docCount , docCount: props.docCount
...@@ -111,9 +77,8 @@ component = R.hooksComponent componentName cpt where ...@@ -111,9 +77,8 @@ component = R.hooksComponent componentName cpt where
, groupCount: props.groupCount , groupCount: props.groupCount
, branchCount: props.branchCount , branchCount: props.branchCount
} }
,
-- Selection tab SelectionTab ->
R2.if' (tabView == SelectionTab) $
selectionTab selectionTab
{ key: (show props.nodeId) <> "-selection" { key: (show props.nodeId) <> "-selection"
, extractedTerms: props.extractedTerms , extractedTerms: props.extractedTerms
...@@ -123,9 +88,4 @@ component = R.hooksComponent componentName cpt where ...@@ -123,9 +88,4 @@ component = R.hooksComponent componentName cpt where
, selectedSource: props.selectedSource , selectedSource: props.selectedSource
, selectTermCallback: props.selectTermCallback , selectTermCallback: props.selectTermCallback
} }
,
-- Teaser
H.div
{ className: "phylo-sidebar__bottom-teaser" }
[]
] ]
...@@ -9,6 +9,7 @@ import Effect (Effect) ...@@ -9,6 +9,7 @@ import Effect (Effect)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
import Gargantext.Components.PhyloExplorer.Types (Term(..), Source(..)) import Gargantext.Components.PhyloExplorer.Types (Term(..), Source(..))
import Gargantext.Types (SidePanelState(..), toggleSidePanelState)
import Gargantext.Utils ((?)) import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix (nothing) import Reactix (nothing)
...@@ -27,7 +28,7 @@ type Props = ...@@ -27,7 +28,7 @@ type Props =
, resultCallback :: Maybe Term -> Effect Unit , resultCallback :: Maybe Term -> Effect Unit
, toolBar :: T.Box (Boolean) , toolBar :: T.Box (Boolean)
, sideBar :: T.Box (Boolean) , sideBar :: T.Box (SidePanelState)
) )
here :: R2.Here here :: R2.Here
...@@ -71,12 +72,12 @@ component = here.component "main" cpt where ...@@ -71,12 +72,12 @@ component = here.component "main" cpt where
-- Sidebar toggle -- Sidebar toggle
B.button B.button
{ className: "phylo-topbar__sidebar" { className: "phylo-topbar__sidebar"
, callback: \_ -> T.modify_ (not) sideBar , callback: \_ -> T.modify_ (toggleSidePanelState) sideBar
, variant: sideBar' ? , variant: sideBar' == Opened ?
ButtonVariant Light $ ButtonVariant Light $
OutlinedButtonVariant Light OutlinedButtonVariant Light
} }
[ H.text $ sideBar' ? "Hide sidebar" $ "Show sidebar" ] [ H.text $ sideBar' == Opened ? "Hide sidebar" $ "Show sidebar" ]
, ,
-- Source -- Source
H.div H.div
......
...@@ -469,6 +469,9 @@ data TabView ...@@ -469,6 +469,9 @@ data TabView
derive instance Generic TabView _ derive instance Generic TabView _
derive instance Eq TabView derive instance Eq TabView
instance Show TabView where
show DetailsTab = "Legend"
show SelectionTab = "Data"
----------------------------------------------------------- -----------------------------------------------------------
......
...@@ -158,14 +158,14 @@ renderScale :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange ...@@ -158,14 +158,14 @@ renderScale :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange
renderScale ref {width,height} (Range.Closed {min, max}) = renderScale ref {width,height} (Range.Closed {min, max}) =
H.div { ref, className, width, height, aria } [] H.div { ref, className, width, height, aria } []
where where
className = "scale" className = "range-slider__scale"
aria = { label: "Scale running from " <> show min <> " to " <> show max } aria = { label: "Scale running from " <> show min <> " to " <> show max }
renderScaleSel :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange -> R.Element renderScaleSel :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange -> R.Element
renderScaleSel ref props (Range.Closed {min, max}) = renderScaleSel ref props (Range.Closed {min, max}) =
H.div { ref, className, style} [] H.div { ref, className, style} []
where where
className = "scale-sel" className = "range-slider__scale-sel"
style = {left: computeLeft, width: computeWidth} style = {left: computeLeft, width: computeWidth}
percOffsetMin = Range.normalise props.bounds min percOffsetMin = Range.normalise props.bounds min
percOffsetMax = Range.normalise props.bounds max percOffsetMax = Range.normalise props.bounds max
...@@ -176,7 +176,7 @@ renderScaleSel ref props (Range.Closed {min, max}) = ...@@ -176,7 +176,7 @@ renderScaleSel ref props (Range.Closed {min, max}) =
renderKnob :: Knob -> R.Ref (Nullable DOM.Element) -> Range.NumberRange -> Bounds -> T.Box (Maybe Knob) -> Int -> R.Element renderKnob :: Knob -> R.Ref (Nullable DOM.Element) -> Range.NumberRange -> Bounds -> T.Box (Maybe Knob) -> Int -> R.Element
renderKnob knob ref (Range.Closed value) bounds set precision = renderKnob knob ref (Range.Closed value) bounds set precision =
H.div { ref, tabIndex, className, aria, on: { mouseDown: onMouseDown }, style } [ H.div { ref, tabIndex, className, aria, on: { mouseDown: onMouseDown }, style } [
H.div { className: "button" } H.div { className: "range-slider__placeholder" }
[ [
H.text $ text $ toFixed precision val H.text $ text $ toFixed precision val
] ]
...@@ -185,7 +185,7 @@ renderKnob knob ref (Range.Closed value) bounds set precision = ...@@ -185,7 +185,7 @@ renderKnob knob ref (Range.Closed value) bounds set precision =
text (Just num) = num text (Just num) = num
text Nothing = "error" text Nothing = "error"
tabIndex = 0 tabIndex = 0
className = "knob" className = "range-slider__knob"
aria = { label: labelPrefix knob <> "value: " <> show val } aria = { label: labelPrefix knob <> "value: " <> show val }
labelPrefix MinKnob = "Minimum " labelPrefix MinKnob = "Minimum "
labelPrefix MaxKnob = "Maximum " labelPrefix MaxKnob = "Maximum "
...@@ -220,4 +220,3 @@ roundRange :: Epsilon -> Bounds -> Range.NumberRange -> Range.NumberRange ...@@ -220,4 +220,3 @@ roundRange :: Epsilon -> Bounds -> Range.NumberRange -> Range.NumberRange
roundRange epsilon bounds (Range.Closed initial) = Range.Closed { min, max } roundRange epsilon bounds (Range.Closed initial) = Range.Closed { min, max }
where min = round epsilon bounds initial.min where min = round epsilon bounds initial.min
max = round epsilon bounds initial.max max = round epsilon bounds initial.max
This diff is collapsed.
...@@ -9,6 +9,7 @@ import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) ...@@ -9,6 +9,7 @@ import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.Lang (langSwitcher, allFeLangs) import Gargantext.Components.Lang (langSwitcher, allFeLangs)
import Gargantext.Components.Themes (themeSwitcher, allThemes) import Gargantext.Components.Themes (themeSwitcher, allThemes)
import Gargantext.Types (Handed(..)) import Gargantext.Types (Handed(..))
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -64,7 +65,9 @@ topBarCpt = here.component "topBar" cpt ...@@ -64,7 +65,9 @@ topBarCpt = here.component "topBar" cpt
] ]
, ,
B.button B.button
{ variant: ButtonVariant Light { variant: showTree' ?
ButtonVariant Light $
OutlinedButtonVariant Light
, callback: const $ T.modify_ (not) showTree , callback: const $ T.modify_ (not) showTree
, className: "main-topbar__tree-switcher" , className: "main-topbar__tree-switcher"
} }
......
...@@ -10,6 +10,8 @@ import Reactix (nothing, thenNothing) ...@@ -10,6 +10,8 @@ import Reactix (nothing, thenNothing)
import Reactix as R import Reactix as R
-- | Hook triggered on first mount event only -- | Hook triggered on first mount event only
-- |
-- | /!\ @TODO cleanup function not working
useFirstMount :: R.Hooks (Boolean) useFirstMount :: R.Hooks (Boolean)
useFirstMount = do useFirstMount = do
firstMount <- R.useRef true firstMount <- R.useRef true
......
module Gargantext.Utils.Popover where module Gargantext.Utils.Popover where
import Gargantext.Prelude
import DOM.Simple as DOM
import Data.Maybe (maybe) import Data.Maybe (maybe)
import Data.Nullable (Nullable, toMaybe) import Data.Nullable (Nullable, toMaybe)
import DOM.Simple as DOM
import Effect (Effect) import Effect (Effect)
import Effect.Uncurried (EffectFn2, runEffectFn2) import Effect.Uncurried (EffectFn2, runEffectFn2)
import Reactix as R import Reactix as R
import Record as Record
import Gargantext.Prelude
type PopoverRef = R.Ref (Nullable DOM.Element) type PopoverRef = R.Ref (Nullable DOM.Element)
...@@ -22,8 +23,11 @@ type Props = ...@@ -22,8 +23,11 @@ type Props =
foreign import popoverCpt :: R.Component Props foreign import popoverCpt :: R.Component Props
-- | https://github.com/vaheqelyan/react-awesome-popover
popover :: Record Props -> Array R.Element -> R.Element popover :: Record Props -> Array R.Element -> R.Element
popover = R.rawCreateElement popoverCpt popover props children = R.rawCreateElement popoverCpt props' children
where
props' = Record.merge props { className: "awesome-popover" }
foreign import _setState :: forall a. EffectFn2 DOM.Element a Unit foreign import _setState :: forall a. EffectFn2 DOM.Element a Unit
......
@import "./base/_reset.scss"
@import "./base/_general.scss"
@import "./base/_form.scss" @import "./base/_form.scss"
@import "./base/_layout.scss" @import "./base/_layout.scss"
@import "./base/_nav.scss" @import "./base/_nav.scss"
@import "./base/_typography.scss" @import "./base/_typography.scss"
@import "./base/_animations.scss" @import "./base/_animations.scss"
@import "./base/_bootstrap.scss"
@import "./base/_placeholder.scss" @import "./base/_placeholder.scss"
@import "./base/_utilities.scss"
@import "./base/_range_slider.sass"
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
@import "./_legacy/_tree" @import "./_legacy/_tree"
@import "./_legacy/_code_editor" @import "./_legacy/_code_editor"
@import "./_legacy/_styles" @import "./_legacy/_styles"
@import "./_legacy/_range_slider"
@import "./_legacy/_annotation" @import "./_legacy/_annotation"
@import "./_legacy/_folder_view" @import "./_legacy/_folder_view"
@import "./_legacy/_phylo" @import "./_legacy/_phylo"
@mixin sidePanelCommon /* Grid constants */
position: absolute
max-height: 600px $layout-height: calc(100vh - #{ $topbar-height} )
//overflow-y: scroll
top: 170px
//z-index: 1
#graph-explorer
margin-left: 20rem
margin-right: 20rem
padding-top: 0px
.graph-container
#sp-container
@include sidePanelCommon
border: 1px white solid
background-color: white
width: 28%
z-index: 15
#myTab
marginBottom: 18px
marginTop: 18px
#myTabContent
borderBottom: 1px solid black
paddingBottom : 19px
#horizontal-checkbox
ul
display: inline
float : left
.lefthanded #sp-container
left: 0%
.righthanded #sp-container
left: 70%
.graph-tree
@include sidePanelCommon
background-color: #fff
z-index: 1
.lefthanded .graph-tree
left: 80%
.righthanded .graph-tree
left: 0%
/* #toggle-container
/* position: fixed
/* z-index: 999 // needs to appear above solid menu bar
/* right: 25%
/* top: 10px
/* width: 50%
/* .container-fluid
/* padding-top: 90px
#controls-container
// position: fixed
position: absolute
// needs to appear above graph elements
z-index: 900
backdrop-filter: blur(4px)
background: rgba(255,255,255,75%)
// overflow: auto
left: 0
right: 0
top: 60px
.nav-item /////////////////////////////////////////////
padding-left: 0.8rem
.graph-row .graph-loader
height: 100vh width: 100%
height: 100%
#graph-view &__spinner
height: 95vh $size: 100px
$weight: 6px
#tree
position: absolute position: absolute
z-index: 1 font-size: $weight
height: $size
width: $size
// (?) `centered` mixin will not work here, due to Bootstrap process
// interfering with the transform rule
top: calc( 50% - #{ $size / 2 } )
left: calc( 50% - #{ $size / 2 } )
.input-with-autocomplete
.completions /////////////////////////////////////////////
.graph-layout
position: relative
width: 100%
height: $layout-height
&__sidebar
position: fixed position: fixed
max-height: 300px width: $sidebar-width
overflow-y: scroll height: $sidebar-height
width: 300px z-index: z-index('graph-layout', 'sidebar')
top: 50px
@include right-handed
right: 0
border-left: 1px solid $border-color
@include left-handed
left: 0
border-right: 1px solid $border-color
&__toolbar
position: absolute
z-index: z-index('graph-layout', 'toolbar')
background-color: $body-bg
width: 100%
border-bottom: 1px solid $border-color
.b-fieldset
background-color: $body-bg
&__content
width: 100%
height: 100%
/////////////////////////////////////////////
.graph-topbar .graph-topbar
@include aside-topbar @include aside-topbar
display: flex
padding-left: $topbar-item-margin
padding-right: $topbar-item-margin
&__toolbar,
&__sidebar
width: $topbar-fixed-button-width
margin-left: $topbar-item-margin
margin-right: $topbar-item-margin
&__search
width: $topbar-input-width
margin-left: $topbar-item-margin
margin-right: $topbar-item-margin
[type="submit"]
display: none
/////////////////////////////////////////////
.graph-sidebar
@include sidebar
$margin-x: $sidebar-tab-margin-x
$margin-y: space-x(2)
&__legend-tab
padding: $margin-y $margin-x
&__data-tab
padding: $margin-y $margin-x
&__community-tab
padding: $margin-y $margin-x
&__separator
margin-top: $margin-y
margin-bottom: $margin-y
color: $gray-500
text-align: center
.graph-legend
$legend-code-width: 24px
$legend-code-height: 12px
&__item
list-style: none
margin-bottom: space-x(0.75)
&__code
width: $legend-code-width
height: $legend-code-height
display: inline-block
margin-right: space-x(2.5)
border: 1px solid $gray-500
&__caption
vertical-align: top
.graph-documentation
&__text-section
margin-bottom: space-x(3)
font-size: 15px
line-height: 1.5
p
margin-bottom: space-x(1)
li
list-style-type: circle
padding-left: space-x(0.5)
margin-left: space-x(3)
line-height: 1.4
&:not(:last-child)
margin-bottom: space-x(1.5)
.graph-selected-nodes
&__item
&:not(:last-child)
margin-bottom: space-x(0.5)
&__badge
font-size: $font-size-base
white-space: normal
word-break: break-word
&__actions
$gutter: space-x(2)
display: flex
justify-content: space-around
.b-button
width: calc(50% - #{ space-x(2) / 2 } )
.graph-neighborhood
&__counter
font-weight: bold
&__badge
white-space: normal
word-break: break-word
&:not(:last-child)
margin-bottom: space-x(0.75)
&__show-more
margin-top: space-x(2)
/////////////////////////////////////////////
.graph-toolbar
$self: &
$section-margin: space-x(2)
$item-margin: space-x(1)
display: flex
padding: #{ $section-margin / 2 }
&__gap
width: $item-margin
display: inline-block
&__section
margin: #{ $section-margin / 2 }
// Selection settings
&--selection
.b-fieldset__content
display: flex
#{ $self }__gap
width: #{ $item-margin * 2 }
.range-simple
flex-grow: 1
// Controls
&--controls
.b-fieldset__content
position: relative
#{ $self }__gap
width: #{ $item-margin * 2 }
.range-control,
.range-simple
flex-basis: calc(50% - #{ $item-margin * 2 })
// Atlas button animation
.on-running-animation .b-icon
animation-name: pulse
animation-duration: 0.5s
animation-timing-function: ease
animation-direction: alternate
animation-iteration-count: infinite
animation-play-state: running
...@@ -92,9 +92,6 @@ li#rename ...@@ -92,9 +92,6 @@ li#rename
overflow: visible overflow: visible
height: auto height: auto
#graph-tree
.tree
margin-top: 27px
.nopadding .nopadding
padding: 0 !important padding: 0 !important
......
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
&__tree-switcher &__tree-switcher
margin-right: space-x(1) margin-right: space-x(1)
&__tree-switcher
$fixed-width: 136px
width: $fixed-width
// add hovering effect // add hovering effect
&.navbar-dark .navbar-text:hover &.navbar-dark .navbar-text:hover
color: $navbar-dark-hover-color color: $navbar-dark-hover-color
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
/* Grid constants */ /* Grid constants */
$topbar-height: 56px; // ~ unworthy empirical value (@TODO topbar height calculation)
$graph-margin: 16px; $graph-margin: 16px;
$layout-height: calc(100vh - #{ $topbar-height} ); $layout-height: calc(100vh - #{ $topbar-height} );
...@@ -38,16 +37,6 @@ $left-column-width: 10%; ...@@ -38,16 +37,6 @@ $left-column-width: 10%;
$center-column-width: 85%; $center-column-width: 85%;
$right-column-width: 5%; $right-column-width: 5%;
/* Topbar constants */
$topbar-input-width: 304px;
/* Sidebar constant */
$sidebar-width: 480px;
$sidebar-height: calc(100vh - #{ $topbar-height });
$tab-margin-x: space-x(2.5);
/* Colors */ /* Colors */
$graph-background-color: $body-bg; $graph-background-color: $body-bg;
...@@ -448,34 +437,33 @@ $decreasing-color: #11638F; ...@@ -448,34 +437,33 @@ $decreasing-color: #11638F;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
.phylo-topbar { .phylo-topbar {
$margin: space-x(0.5);
$fixed-button-width: 136px; $fixed-button-width: 136px;
@include aside-topbar(); @include aside-topbar();
padding-left: $margin; padding-left: $topbar-item-margin;
padding-right: $margin; padding-right: $topbar-item-margin;
display: flex; display: flex;
&__toolbar, &__toolbar,
&__sidebar { &__sidebar {
width: $fixed-button-width; width: $topbar-fixed-button-width;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__source { &__source {
width: $topbar-input-width; width: $topbar-input-width;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__autocomplete { &__autocomplete {
display: flex; display: flex;
width: $topbar-input-width; width: $topbar-input-width;
position: relative; position: relative;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__suggestion { &__suggestion {
...@@ -506,60 +494,11 @@ $decreasing-color: #11638F; ...@@ -506,60 +494,11 @@ $decreasing-color: #11638F;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
.phylo-sidebar { .phylo-sidebar {
$teaser-height: 16px; @include sidebar;
background-color: $body-bg;
height: 100%;
// avoiding ugly scrollbar
scrollbar-width: none;
overflow-y: scroll;
overflow-x: visible;
&::-webkit-scrollbar {
display: none;
}
// adjust nav menu gutter (@TODO: generic?)
&__menu .nav-item {
&:first-child {
margin-left: space-x(2);
}
&:last-child {
margin-right: space-x(2);
}
}
// UX best pratice: when a lengthy column is overflowy hidden
// (with a scroll), a teaser UI element shows to the user that a scroll
// is possible
&__top-teaser {
@include top-teaser;
z-index: z-index('phylo-sidebar', 'teaser');
pointer-events: none;
position: sticky;
top: 0;
height: $teaser-height;
width: 100%;
}
&__bottom-teaser {
@include bottom-teaser;
z-index: z-index('phylo-sidebar', 'teaser');
pointer-events: none;
position: sticky;
bottom: 0;
height: $teaser-height;
width: 100%;
}
} }
.phylo-details-tab { .phylo-details-tab {
$margin-x: $tab-margin-x; $margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2); $margin-y: space-x(2);
&__counter { &__counter {
...@@ -585,7 +524,7 @@ $decreasing-color: #11638F; ...@@ -585,7 +524,7 @@ $decreasing-color: #11638F;
} }
.phylo-selection-tab { .phylo-selection-tab {
$margin-x: $tab-margin-x; $margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2); $margin-y: space-x(2);
&__highlight { &__highlight {
...@@ -609,7 +548,6 @@ $decreasing-color: #11638F; ...@@ -609,7 +548,6 @@ $decreasing-color: #11638F;
&__item { &__item {
white-space: normal; white-space: normal;
word-break: break-word; word-break: break-word;
// remove "_reboot.scss" line height
line-height: initial; line-height: initial;
&:not(:last-child) { &:not(:last-child) {
......
This diff is collapsed.
...@@ -69,12 +69,6 @@ ...@@ -69,12 +69,6 @@
/* */ /* */
.btn-primary
color: white
background-color: #005a9aff
border-color: black
.frame .frame
height: 100vh height: 100vh
iframe iframe
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -46,3 +46,7 @@ ...@@ -46,3 +46,7 @@
from { transform: rotate(0deg); } from { transform: rotate(0deg); }
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
@keyframes pulse {
from { transform: scale(1); }
to { transform: scale(1.1); }
}
/* Bootstrap 4.x Generic Custom Styles */
// originally added to "table" tag on "_reboot.scss"
.table {
border-collapse: collapse;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment