Commit 89d1033b authored by arturo's avatar arturo

>>> continue (store)

parent b8befd87
...@@ -6,7 +6,6 @@ import Gargantext.Prelude ...@@ -6,7 +6,6 @@ import Gargantext.Prelude
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Set as Set import Data.Set as Set
import Gargantext.AsyncTasks as GAT import Gargantext.AsyncTasks as GAT
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.Lang as Lang import Gargantext.Components.Lang as Lang
import Gargantext.Components.Nodes.Lists.Types as ListsT import Gargantext.Components.Nodes.Lists.Types as ListsT
import Gargantext.Components.Nodes.Texts.Types as TextsT import Gargantext.Components.Nodes.Texts.Types as TextsT
...@@ -36,7 +35,6 @@ type App = ...@@ -36,7 +35,6 @@ type App =
, showCorpus :: Boolean , showCorpus :: Boolean
, showLogin :: Boolean , showLogin :: Boolean
, showTree :: Boolean , showTree :: Boolean
, sidePanelGraph :: Maybe (Record GEST.SidePanel)
, sidePanelLists :: Maybe (Record ListsT.SidePanel) , sidePanelLists :: Maybe (Record ListsT.SidePanel)
, sidePanelTexts :: Maybe (Record TextsT.SidePanel) , sidePanelTexts :: Maybe (Record TextsT.SidePanel)
, sidePanelState :: SidePanelState , sidePanelState :: SidePanelState
...@@ -63,7 +61,6 @@ emptyApp = ...@@ -63,7 +61,6 @@ emptyApp =
, showCorpus : false , showCorpus : false
, showLogin : false , showLogin : false
, showTree : true , showTree : true
, sidePanelGraph : GEST.initialSidePanel
, sidePanelLists : ListsT.initialSidePanel , sidePanelLists : ListsT.initialSidePanel
, sidePanelTexts : TextsT.initialSidePanel , sidePanelTexts : TextsT.initialSidePanel
, sidePanelState : InitialClosed , sidePanelState : InitialClosed
...@@ -89,7 +86,6 @@ type Boxes = ...@@ -89,7 +86,6 @@ type Boxes =
, showCorpus :: T.Box Boolean , showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean , showLogin :: T.Box Boolean
, showTree :: T.Box Boolean , showTree :: T.Box Boolean
, sidePanelGraph :: T.Box (Maybe (Record GEST.SidePanel))
, sidePanelLists :: T.Box (Maybe (Record ListsT.SidePanel)) , sidePanelLists :: T.Box (Maybe (Record ListsT.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel)) , sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState , sidePanelState :: T.Box SidePanelState
......
...@@ -7,7 +7,7 @@ import Data.Array as A ...@@ -7,7 +7,7 @@ import Data.Array as A
import Data.FoldableWithIndex (foldMapWithIndex) import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Int (toNumber) import Data.Int (toNumber)
import Data.Map as Map import Data.Map as Map
import Data.Maybe (Maybe(..), fromJust, maybe) import Data.Maybe (Maybe(..), fromJust)
import Data.Nullable (null, Nullable) import Data.Nullable (null, Nullable)
import Data.Sequence as Seq import Data.Sequence as Seq
import Data.Set as Set import Data.Set as Set
...@@ -16,13 +16,13 @@ import Gargantext.Components.App.Data (Boxes) ...@@ -16,13 +16,13 @@ import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.Resources as Graph import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Sidebar as GES import Gargantext.Components.GraphExplorer.Sidebar as GES
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Toolbar.Controls (Controls)
import Gargantext.Components.GraphExplorer.Toolbar.Controls as Controls import Gargantext.Components.GraphExplorer.Toolbar.Controls as Controls
import Gargantext.Components.GraphExplorer.TopBar as GETB import Gargantext.Components.GraphExplorer.TopBar as GETB
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config (defaultFrontends) import Gargantext.Config (defaultFrontends)
import Gargantext.Data.Louvain as Louvain import Gargantext.Data.Louvain as Louvain
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types as GT import Gargantext.Types as GT
...@@ -30,71 +30,63 @@ import Gargantext.Types as Types ...@@ -30,71 +30,63 @@ import Gargantext.Types as Types
import Gargantext.Utils ((?)) import Gargantext.Utils ((?))
import Gargantext.Utils.Range as Range import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Math as Math import Math as Math
import Partial.Unsafe (unsafePartial) import Partial.Unsafe (unsafePartial)
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Layout"
type Props = type Props =
( mMetaData' :: Maybe GET.MetaData ( session :: Session
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes , boxes :: Boxes
, sigmaRef :: R.Ref Sigmax.Sigma
, graphId :: GET.GraphId , graphId :: GET.GraphId
, controls :: Record Controls
) )
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Layout"
layout :: R2.Leaf Props layout :: R2.Leaf Props
layout = R2.leaf layoutCpt layout = R2.leaf layoutCpt
layoutCpt :: R.Memo Props layoutCpt :: R.Memo Props
layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
cpt props@{ boxes cpt props@{ boxes
, graph
, mMetaData'
, graphId
, session , session
, hyperdataGraph , sigmaRef
, controls , graphId
} _ = do } _ = do
-- Computed -- | Computed
----------------- -- |
let let
topBarPortalKey = "portal-topbar::" <> show graphId topBarPortalKey = "portal-topbar::" <> show graphId
-- States -- | States
----------------- -- |
{ mMetaData: mMetaDataBox { showSidebar
, showSidebar
, showDoc , showDoc
} <- GEST.focusedSidePanel boxes.sidePanelGraph , mMetaData
, showControls
} <- Stores.useStore GraphStore.context
_graphVersion' <- T.useLive T.unequal boxes.graphVersion
showSidebar' <- R2.useLive' showSidebar showSidebar' <- R2.useLive' showSidebar
showDoc' <- R2.useLive' showDoc
showDoc' <- R2.useLive' showDoc mMetaData' <- R2.useLive' mMetaData
showControls' <- R2.useLive' showControls
-- _dataRef <- R.useRef graph -- _dataRef <- R.useRef graph
graphRef <- R.useRef null graphRef <- R.useRef null
-- Hooks -- | Hooks
----------------- -- |
mTopBarHost <- R.unsafeHooksEffect $ R2.getElementById "portal-topbar" mTopBarHost <- R.unsafeHooksEffect $ R2.getElementById "portal-topbar"
showControls' <- R2.useLive' controls.showControls
-- graphVersionRef <- R.useRef graphVersion' -- graphVersionRef <- R.useRef graphVersion'
-- R.useEffect' $ do -- R.useEffect' $ do
...@@ -116,8 +108,8 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -116,8 +108,8 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
-- T.write_ Graph.Init controls.graphStage -- T.write_ Graph.Init controls.graphStage
-- T.write_ Types.InitialClosed controls.sidePanelState -- T.write_ Types.InitialClosed controls.sidePanelState
-- Render -- | Render
----------------- -- |
pure $ pure $
...@@ -130,7 +122,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -130,7 +122,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
R2.fragmentWithKey topBarPortalKey R2.fragmentWithKey topBarPortalKey
[ [
GETB.topBar GETB.topBar
{ sidePanelGraph: props.boxes.sidePanelGraph } {}
] ]
] ]
, ,
...@@ -173,8 +165,6 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -173,8 +165,6 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
GES.sidebar GES.sidebar
{ boxes { boxes
, frontends: defaultFrontends , frontends: defaultFrontends
, graph
, graphId
, metaData , metaData
, session , session
} }
...@@ -190,7 +180,11 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -190,7 +180,11 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
, style: { display: showControls' ? "block" $ "none" } , style: { display: showControls' ? "block" $ "none" }
} }
[ [
Controls.controls controls [] Controls.controls
{ reloadForest: boxes.reloadForest
, session
, sigmaRef
}
] ]
, ,
-- Content -- Content
...@@ -201,11 +195,8 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -201,11 +195,8 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
[ [
graphView graphView
{ boxes: props.boxes { boxes: props.boxes
, controls
, elRef: graphRef , elRef: graphRef
, graph , sigmaRef
, hyperdataGraph
, mMetaData: mMetaDataBox
} }
] ]
] ]
...@@ -213,73 +204,87 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where ...@@ -213,73 +204,87 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
-------------------------------------------------------------- --------------------------------------------------------------
type GraphProps = type GraphProps =
( boxes :: Boxes ( boxes :: Boxes
, controls :: Record Controls.Controls , elRef :: R.Ref (Nullable Element)
, elRef :: R.Ref (Nullable Element) , sigmaRef :: R.Ref Sigmax.Sigma
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, mMetaData :: T.Box (Maybe GET.MetaData)
) )
graphView :: R2.Leaf GraphProps graphView :: R2.Leaf GraphProps
graphView = R2.leaf graphViewCpt graphView = R2.leaf graphViewCpt
graphViewCpt :: R.Memo GraphProps graphViewCpt :: R.Memo GraphProps
graphViewCpt = R.memo' $ here.component "graphView" cpt graphViewCpt = R.memo' $ here.component "graphView" cpt where
where cpt { boxes
cpt { boxes , elRef
, controls , sigmaRef
, elRef } _ = do
, graph -- | States
, hyperdataGraph: GET.HyperdataGraph { mCamera } -- |
, mMetaData } _ = do { edgeConfluence
edgeConfluence' <- T.useLive T.unequal controls.edgeConfluence , edgeWeight
edgeWeight' <- T.useLive T.unequal controls.edgeWeight , multiSelectEnabled
mMetaData' <- T.useLive T.unequal mMetaData , nodeSize
multiSelectEnabled' <- T.useLive T.unequal controls.multiSelectEnabled , removedNodeIds
nodeSize' <- T.useLive T.unequal controls.nodeSize , selectedNodeIds
removedNodeIds' <- T.useLive T.unequal controls.removedNodeIds , showEdges
selectedNodeIds' <- T.useLive T.unequal controls.selectedNodeIds , showLouvain
showEdges' <- T.useLive T.unequal controls.showEdges , hyperdataGraph
showLouvain' <- T.useLive T.unequal controls.showLouvain , graph
} <- Stores.useStore GraphStore.context
multiSelectEnabledRef <- R.useRef multiSelectEnabled'
edgeConfluence' <- R2.useLive' edgeConfluence
-- TODO Cache this? edgeWeight' <- R2.useLive' edgeWeight
let louvainGraph = multiSelectEnabled' <- R2.useLive' multiSelectEnabled
if showLouvain' then nodeSize' <- R2.useLive' nodeSize
let louvain = Louvain.louvain unit in removedNodeIds' <- R2.useLive' removedNodeIds
let cluster = Louvain.init louvain (SigmaxT.louvainNodes graph) (SigmaxT.louvainEdges graph) in selectedNodeIds' <- R2.useLive' selectedNodeIds
SigmaxT.louvainGraph graph cluster showEdges' <- R2.useLive' showEdges
else showLouvain' <- R2.useLive' showLouvain
graph hyperdataGraph' <- R2.useLive' hyperdataGraph
let transformedGraph = transformGraph louvainGraph { edgeConfluence' graph' <- R2.useLive' graph
, edgeWeight'
, nodeSize' multiSelectEnabledRef <- R.useRef multiSelectEnabled'
, removedNodeIds'
, selectedNodeIds' -- | Computed
, showEdges' } -- |
let startForceAtlas = maybe true (\(GET.MetaData { startForceAtlas: sfa }) -> sfa) mMetaData'
-- TODO Cache this?
R.useEffect1' multiSelectEnabled' $ do let louvainGraph =
R.setRef multiSelectEnabledRef multiSelectEnabled' if showLouvain' then
let louvain = Louvain.louvain unit in
pure $ let cluster = Louvain.init louvain (SigmaxT.louvainNodes graph') (SigmaxT.louvainEdges graph') in
SigmaxT.louvainGraph graph' cluster
Graph.graph else
{ boxes graph'
, elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings let transformedGraph = transformGraph louvainGraph { edgeConfluence'
, graph , edgeWeight'
, mCamera , nodeSize'
, multiSelectEnabledRef , removedNodeIds'
, selectedNodeIds: controls.selectedNodeIds , selectedNodeIds'
, showEdges: controls.showEdges , showEdges' }
, sigmaRef: controls.sigmaRef
, sigmaSettings: Graph.sigmaSettings let mCamera' (GET.HyperdataGraph { mCamera }) = mCamera
, stage: controls.graphStage
, startForceAtlas -- | Hooks
, transformedGraph -- |
}
R.useEffect1' multiSelectEnabled' $ do
R.setRef multiSelectEnabledRef multiSelectEnabled'
-- | Render
-- |
pure $
Graph.drawGraph
{ boxes
, elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings
, mCamera: mCamera' hyperdataGraph'
, multiSelectEnabledRef
, sigmaRef
, sigmaSettings: Graph.sigmaSettings
, transformedGraph
}
-------------------------------------------------------- --------------------------------------------------------
......
module Gargantext.Components.GraphExplorer.Resources module Gargantext.Components.GraphExplorer.Resources
-- ( graph, graphCpt ( drawGraph
-- , sigmaSettings, SigmaSettings, SigmaOptionalSettings , sigmaSettings, SigmaSettings--, SigmaOptionalSettings
-- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings , forceAtlas2Settings, ForceAtlas2Settings--, ForceAtlas2OptionalSettings
-- ) )
where where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -10,10 +10,10 @@ import Gargantext.Prelude ...@@ -10,10 +10,10 @@ import Gargantext.Prelude
import DOM.Simple (window) import DOM.Simple (window)
import DOM.Simple.Types (Element) import DOM.Simple.Types (Element)
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable) import Data.Nullable (Nullable)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.Themes (darksterTheme) import Gargantext.Components.Themes (darksterTheme)
import Gargantext.Components.Themes as Themes import Gargantext.Components.Themes as Themes
...@@ -21,6 +21,7 @@ import Gargantext.Hooks.Sigmax as Sigmax ...@@ -21,6 +21,7 @@ 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.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as RH import Reactix.DOM.HTML as RH
import Record (merge) import Record (merge)
...@@ -30,146 +31,160 @@ import Toestand as T ...@@ -30,146 +31,160 @@ import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Graph" here = R2.here "Gargantext.Components.Graph"
data Stage = Init | Ready | Cleanup
derive instance Generic Stage _
derive instance Eq Stage
type Props sigma forceatlas2 = type Props sigma forceatlas2 =
( boxes :: Boxes ( boxes :: Boxes
, elRef :: R.Ref (Nullable Element) , elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2 , forceAtlas2Settings :: forceatlas2
, graph :: SigmaxTypes.SGraph
, mCamera :: Maybe GET.Camera , mCamera :: Maybe GET.Camera
, multiSelectEnabledRef :: R.Ref Boolean , multiSelectEnabledRef :: R.Ref Boolean
, selectedNodeIds :: T.Box SigmaxTypes.NodeIds
, showEdges :: T.Box SigmaxTypes.ShowEdgesState
, sigmaRef :: R.Ref Sigmax.Sigma , sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma , sigmaSettings :: sigma
, stage :: T.Box Stage
, startForceAtlas :: Boolean
, transformedGraph :: SigmaxTypes.SGraph , transformedGraph :: SigmaxTypes.SGraph
) )
graph :: forall s fa2. R2.Leaf (Props s fa2) drawGraph :: forall s fa2. R2.Leaf (Props s fa2)
graph = R2.leaf graphCpt drawGraph = R2.leaf drawGraphCpt
drawGraphCpt :: forall s fa2. R.Memo (Props s fa2)
drawGraphCpt = R.memo' $ here.component "graph" cpt where
-- | Component
-- |
cpt props@{ elRef
, sigmaRef
} _ = do
{ showEdges
, graphStage
, graph
, startForceAtlas
, selectedNodeIds
} <- Stores.useStore GraphStore.context
showEdges' <- R2.useLive' showEdges
graphStage' <- R2.useLive' graphStage
graph' <- R2.useLive' graph
startForceAtlas' <- R2.useLive' startForceAtlas
stageHooks
-- @WIP: record merge
(
Record.merge
{ showEdges'
, graphStage'
, selectedNodeIds
, graphStage
, startForceAtlas'
, graph'
}
props
)
R.useEffectOnce $ do
pure $ do
here.log "[graphCpt (Cleanup)]"
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do
Sigma.stopForceAtlas2 sigma
here.log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma
Sigma.kill sigma
here.log "[graphCpt (Cleanup)] sigma killed"
-- NOTE: This div is not empty after sigma initializes.
-- When we change state, we make it empty though.
--pure $ RH.div { ref: elRef, style: {height: "95%"} } []
pure $ case R.readNullableRef elRef of
Nothing -> RH.div {} []
Just el -> R.createPortal [] el
-- | Stage Hooks
-- |
stageHooks { elRef
, mCamera
, multiSelectEnabledRef
, selectedNodeIds
, forceAtlas2Settings: fa2
, graph'
, sigmaRef
, graphStage
, graphStage': GET.Init
, startForceAtlas'
, boxes
} = do
R.useEffectOnce' $ do
let rSigma = R.readRef sigmaRef
case Sigmax.readSigma rSigma of
Nothing -> do
theme <- T.read boxes.theme
eSigma <- Sigma.sigma {settings: sigmaSettings theme}
case eSigma of
Left err -> here.warn2 "[graphCpt] error creating sigma" err
Right sig -> do
Sigmax.writeSigma rSigma $ Just sig
Sigmax.dependOnContainer elRef "[graphCpt (Ready)] container not found" $ \c -> do
_ <- Sigma.addRenderer sig {
"type": "canvas"
, container: c
, additionalContexts: ["mouseSelector"]
}
pure unit
graphCpt :: forall s fa2. R.Memo (Props s fa2) Sigmax.refreshData sig $ Sigmax.sigmafy graph'
graphCpt = R.memo' $ here.component "graph" cpt where
cpt props@{ elRef
, showEdges
, sigmaRef
, stage } _ = do
showEdges' <- T.useLive T.unequal showEdges
stage' <- T.useLive T.unequal stage
stageHooks (Record.merge { showEdges', stage' } props)
R.useEffectOnce $ do
pure $ do
here.log "[graphCpt (Cleanup)]"
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Cleanup)] no sigma" $ \sigma -> do
Sigma.stopForceAtlas2 sigma
here.log2 "[graphCpt (Cleanup)] forceAtlas stopped for" sigma
Sigma.kill sigma
here.log "[graphCpt (Cleanup)] sigma killed"
-- NOTE: This div is not empty after sigma initializes.
-- When we change state, we make it empty though.
--pure $ RH.div { ref: elRef, style: {height: "95%"} } []
pure $ case R.readNullableRef elRef of
Nothing -> RH.div {} []
Just el -> R.createPortal [] el
stageHooks { elRef
, mCamera
, multiSelectEnabledRef
, selectedNodeIds
, forceAtlas2Settings: fa2
, graph: graph'
, sigmaRef
, stage
, stage': Init
, startForceAtlas
, boxes
} = do
R.useEffectOnce' $ do
let rSigma = R.readRef sigmaRef
case Sigmax.readSigma rSigma of
Nothing -> do
theme <- T.read boxes.theme
eSigma <- Sigma.sigma {settings: sigmaSettings theme}
case eSigma of
Left err -> here.warn2 "[graphCpt] error creating sigma" err
Right sig -> do
Sigmax.writeSigma rSigma $ Just sig
Sigmax.dependOnContainer elRef "[graphCpt (Ready)] container not found" $ \c -> do
_ <- Sigma.addRenderer sig {
"type": "canvas"
, container: c
, additionalContexts: ["mouseSelector"]
}
pure unit
Sigmax.refreshData sig $ Sigmax.sigmafy graph'
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
-- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabledRef
_ <- Sigma.bindMouseSelectorPlugin sigma
pure unit
Sigmax.setEdges sig false
-- here.log2 "[graph] startForceAtlas" startForceAtlas
if startForceAtlas then
Sigma.startForceAtlas2 sig fa2
else
Sigma.stopForceAtlas2 sig
case mCamera of
Just (GET.Camera { ratio, x, y }) -> do
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
_ <- flip T.listen boxes.theme \{ old, new } ->
if (eq old new) then pure unit
else Sigma.proxySetSettings window sig $ sigmaSettings new
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
-- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick sigma selectedNodeIds multiSelectEnabledRef
_ <- Sigma.bindMouseSelectorPlugin sigma
pure unit pure unit
Just _sig -> do
pure unit
T.write Ready stage Sigmax.setEdges sig false
-- here.log2 "[graph] startForceAtlas" startForceAtlas
if startForceAtlas' then
Sigma.startForceAtlas2 sig fa2
else
Sigma.stopForceAtlas2 sig
case mCamera of
Just (GET.Camera { ratio, x, y }) -> do
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 }
stageHooks { showEdges' -- Reload Sigma on Theme changes
, sigmaRef _ <- flip T.listen boxes.theme \{ old, new } ->
, stage': Ready if (eq old new) then pure unit
, transformedGraph else Sigma.proxySetSettings window sig $ sigmaSettings new
} = do
let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap transformedGraph
-- TODO Probably this can be optimized to re-mark selected nodes only when they changed pure unit
R.useEffect' $ do Just _sig -> do
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do pure unit
Sigmax.performDiff sigma transformedGraph
Sigmax.updateEdges sigma tEdgesMap
Sigmax.updateNodes sigma tNodesMap
let edgesState = not $ SigmaxTypes.edgeStateHidden showEdges'
here.log2 "[graphCpt] edgesState" edgesState
Sigmax.setEdges sigma edgesState
T.write GET.Ready graphStage
stageHooks _ = pure unit
stageHooks { showEdges'
, sigmaRef
, graphStage': GET.Ready
, transformedGraph
} = do
let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap transformedGraph
-- TODO Probably this can be optimized to re-mark selected nodes only when they changed
R.useEffect' $ do
Sigmax.dependOnSigma (R.readRef sigmaRef) "[graphCpt (Ready)] no sigma" $ \sigma -> do
Sigmax.performDiff sigma transformedGraph
Sigmax.updateEdges sigma tEdgesMap
Sigmax.updateNodes sigma tNodesMap
let edgesState = not $ SigmaxTypes.edgeStateHidden showEdges'
here.log2 "[graphCpt] edgesState" edgesState
Sigmax.setEdges sigma edgesState
stageHooks _ = pure unit
type SigmaSettings = type SigmaSettings =
......
module Gargantext.Components.GraphExplorer.Sidebar module Gargantext.Components.GraphExplorer.Sidebar
( Props, sidebar ( sidebar
, Common
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -25,7 +24,7 @@ import Gargantext.Components.Bootstrap as B ...@@ -25,7 +24,7 @@ import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.GraphExplorer.Sidebar.DocList (docList) import Gargantext.Components.GraphExplorer.Sidebar.DocList (docList)
import Gargantext.Components.GraphExplorer.Sidebar.Legend as Legend import Gargantext.Components.GraphExplorer.Sidebar.Legend as Legend
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.Lang (Lang(..)) import Gargantext.Components.Lang (Lang(..))
import Gargantext.Components.NgramsTable.Core as NTC import Gargantext.Components.NgramsTable.Core as NTC
...@@ -39,76 +38,69 @@ import Gargantext.Sessions (Session) ...@@ -39,76 +38,69 @@ import Gargantext.Sessions (Session)
import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, TabSubType(..), TabType(..), TermList(..), ListId, modeTabType) import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, TabSubType(..), TabType(..), TermList(..), ListId, modeTabType)
import Gargantext.Utils (nbsp) import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import Math as Math import Math as Math
import Partial.Unsafe (unsafePartial) import Partial.Unsafe (unsafePartial)
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 Record.Extra as RX
import Toestand as T import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar" here = R2.here "Gargantext.Components.GraphExplorer.Sidebar"
type Common = type Props =
( boxes :: Boxes ( boxes :: Boxes
, graphId :: NodeID
, metaData :: GET.MetaData , metaData :: GET.MetaData
, session :: Session , session :: Session
) , frontends :: Frontends
type Props =
( frontends :: Frontends
, graph :: SigmaxT.SGraph
| Common
) )
sidebar :: R2.Leaf Props sidebar :: R2.Leaf Props
sidebar = R2.leaf sidebarCpt sidebar = R2.leaf sidebarCpt
sidebarCpt :: R.Component Props
sidebarCpt = here.component "sidebar" cpt
where
cpt props@{ boxes: { sidePanelGraph } } _ = do
-- States
{ sideTab
} <- GEST.focusedSidePanel sidePanelGraph
sideTab' <- T.useLive T.unequal sideTab
-- Computed
let
sideTabs =
[ GET.SideTabLegend
, GET.SideTabData
, GET.SideTabCommunity
]
sideTabProps = (RX.pick props :: Record Props) sidebarCpt :: R.Component Props
sidebarCpt = here.component "sidebar" cpt where
cpt props _ = do
-- States
{ sideTab
} <- Stores.useStore GraphStore.context
-- Render sideTab' <- R2.useLive' sideTab
pure $
H.div -- Computed
{ className: "graph-sidebar" } let
[ sideTabs =
-- Menu [ GET.SideTabLegend
B.tabs , GET.SideTabData
{ value: sideTab' , GET.SideTabCommunity
, list: sideTabs
, callback: flip T.write_ sideTab
}
,
case sideTab' of
GET.SideTabLegend -> sideTabLegend sideTabProps
GET.SideTabData -> sideTabData sideTabProps
GET.SideTabCommunity -> sideTabCommunity sideTabProps
] ]
-- Render
pure $
H.div
{ className: "graph-sidebar" }
[
-- Menu
B.tabs
{ value: sideTab'
, list: sideTabs
, callback: flip T.write_ sideTab
}
,
case sideTab' of
GET.SideTabLegend -> sideTabLegend props
GET.SideTabData -> sideTabData props
GET.SideTabCommunity -> sideTabCommunity props
]
------------------------------------------------------------ ------------------------------------------------------------
sideTabLegend :: R2.Leaf Props sideTabLegend :: R2.Leaf Props
sideTabLegend = R2.leaf sideTabLegendCpt sideTabLegend = R2.leaf sideTabLegendCpt
sideTabLegendCpt :: R.Component Props sideTabLegendCpt :: R.Component Props
sideTabLegendCpt = here.component "sideTabLegend" cpt sideTabLegendCpt = here.component "sideTabLegend" cpt
where where
...@@ -129,129 +121,133 @@ sideTabLegendCpt = here.component "sideTabLegend" cpt ...@@ -129,129 +121,133 @@ sideTabLegendCpt = here.component "sideTabLegend" cpt
sideTabData :: R2.Leaf Props sideTabData :: R2.Leaf Props
sideTabData = R2.leaf sideTabDataCpt sideTabData = R2.leaf sideTabDataCpt
sideTabDataCpt :: R.Component Props sideTabDataCpt :: R.Component Props
sideTabDataCpt = here.component "sideTabData" cpt sideTabDataCpt = here.component "sideTabData" cpt where
where cpt props _ = do
cpt props@{ boxes: { sidePanelGraph } } _ = do -- States
-- States { selectedNodeIds
{ selectedNodeIds , showDoc
, showDoc , graph
} <- GEST.focusedSidePanel sidePanelGraph } <- Stores.useStore GraphStore.context
selectedNodeIds' <- T.useLive T.unequal selectedNodeIds selectedNodeIds' <- R2.useLive' selectedNodeIds
graph' <- R2.useLive' graph
-- Computed -- Computed
let let
hasSelection = not $ Set.isEmpty selectedNodeIds' hasSelection = not $ Set.isEmpty selectedNodeIds'
-- Render -- Render
pure $ pure $
H.div H.div
{ className: "graph-sidebar__data-tab" } { className: "graph-sidebar__data-tab" }
[ [
case hasSelection of case hasSelection of
-- No result -- No result
false -> false ->
B.caveat B.caveat
{} {}
[ [
H.text "Select one or more nodes to get their informations" H.text "Select one or more nodes to get their informations"
] ]
-- Nodes have been selected -- Nodes have been selected
true -> true ->
R.fragment R.fragment
[ [
selectedNodes $ selectedNodes $
{ nodesMap: SigmaxT.nodesGraphMap props.graph { nodesMap: SigmaxT.nodesGraphMap graph'
} `Record.merge` props } `Record.merge` props
, ,
sideBarTabSeparator sideBarTabSeparator
, ,
neighborhood neighborhood
props {}
, ,
sideBarTabSeparator sideBarTabSeparator
, ,
docListWrapper docListWrapper
{ frontends: props.frontends { frontends: props.frontends
, metaData: props.metaData , metaData: props.metaData
, nodesMap: SigmaxT.nodesGraphMap props.graph , nodesMap: SigmaxT.nodesGraphMap graph'
, searchType: SearchDoc , searchType: SearchDoc
, selectedNodeIds: selectedNodeIds' , selectedNodeIds: selectedNodeIds'
, session: props.session , session: props.session
, showDoc , showDoc
} }
] ]
] ]
------------------------------------------------------------ ------------------------------------------------------------
sideTabCommunity :: R2.Leaf Props sideTabCommunity :: R2.Leaf Props
sideTabCommunity = R2.leaf sideTabCommunityCpt sideTabCommunity = R2.leaf sideTabCommunityCpt
sideTabCommunityCpt :: R.Component Props sideTabCommunityCpt :: R.Component Props
sideTabCommunityCpt = here.component "sideTabCommunity" cpt sideTabCommunityCpt = here.component "sideTabCommunity" cpt where
where cpt props@{ frontends } _ = do
cpt props@{ boxes: { sidePanelGraph } -- States
, frontends } _ = do { selectedNodeIds
-- States , showDoc
{ selectedNodeIds , graph
, showDoc } <- Stores.useStore GraphStore.context
} <- GEST.focusedSidePanel sidePanelGraph
selectedNodeIds' <- T.useLive T.unequal selectedNodeIds selectedNodeIds' <- R2.useLive' selectedNodeIds
graph' <- R2.useLive' graph
-- Computed -- Computed
let let
hasSelection = not $ Set.isEmpty selectedNodeIds' hasSelection = not $ Set.isEmpty selectedNodeIds'
-- Render -- Render
pure $ pure $
H.div H.div
{ className: "graph-sidebar__community-tab" } { className: "graph-sidebar__community-tab" }
[ [
case hasSelection of case hasSelection of
-- No result -- No result
false -> false ->
B.caveat B.caveat
{} {}
[ [
H.text "Select one or more nodes to get their informations" H.text "Select one or more nodes to get their informations"
] ]
-- Nodes have been selection -- Nodes have been selection
true -> true ->
R.fragment R.fragment
[ [
selectedNodes $ selectedNodes $
{ nodesMap: SigmaxT.nodesGraphMap props.graph { nodesMap: SigmaxT.nodesGraphMap graph'
} `Record.merge` props } `Record.merge` props
, ,
sideBarTabSeparator sideBarTabSeparator
, ,
neighborhood neighborhood
props {}
, ,
sideBarTabSeparator sideBarTabSeparator
, ,
docListWrapper docListWrapper
{ frontends { frontends
, metaData: props.metaData , metaData: props.metaData
, nodesMap: SigmaxT.nodesGraphMap props.graph , nodesMap: SigmaxT.nodesGraphMap graph'
, searchType: SearchContact , searchType: SearchContact
, selectedNodeIds: selectedNodeIds' , selectedNodeIds: selectedNodeIds'
, session: props.session , session: props.session
, showDoc , showDoc
} }
] ]
] ]
------------------------------------------- -------------------------------------------
...@@ -275,18 +271,17 @@ type SelectedNodesProps = ...@@ -275,18 +271,17 @@ type SelectedNodesProps =
selectedNodes :: R2.Leaf SelectedNodesProps selectedNodes :: R2.Leaf SelectedNodesProps
selectedNodes = R2.leaf selectedNodesCpt selectedNodes = R2.leaf selectedNodesCpt
selectedNodesCpt :: R.Component SelectedNodesProps selectedNodesCpt :: R.Component SelectedNodesProps
selectedNodesCpt = here.component "selectedNodes" cpt where selectedNodesCpt = here.component "selectedNodes" cpt where
cpt props@{ boxes: { sidePanelGraph } cpt props _ = do
, graph
, nodesMap } _ = do
-- States -- States
{ selectedNodeIds } <- GEST.focusedSidePanel sidePanelGraph { selectedNodeIds
selectedNodeIds' <- T.useLive T.unequal selectedNodeIds , graph
} <- Stores.useStore GraphStore.context
-- Computed selectedNodeIds' <- R2.useLive' selectedNodeIds
let graph' <- R2.useLive' graph
commonProps = RX.pick props :: Record Common
-- Behaviors -- Behaviors
let let
...@@ -309,7 +304,7 @@ selectedNodesCpt = here.component "selectedNodes" cpt where ...@@ -309,7 +304,7 @@ selectedNodesCpt = here.component "selectedNodes" cpt where
{} $ {} $
Seq.toUnfoldable $ Seq.toUnfoldable $
flip Seq.map (badges graph selectedNodeIds') \node -> flip Seq.map (badges graph' selectedNodeIds') \node ->
H.li H.li
{ className: "graph-selected-nodes__item" } { className: "graph-selected-nodes__item" }
...@@ -334,18 +329,16 @@ selectedNodesCpt = here.component "selectedNodes" cpt where ...@@ -334,18 +329,16 @@ selectedNodesCpt = here.component "selectedNodes" cpt where
} }
[ [
updateTermButton updateTermButton
( commonProps `Record.merge` ( props `Record.merge`
{ variant: ButtonVariant Secondary { variant: ButtonVariant Secondary
, rType: CandidateTerm , rType: CandidateTerm
, nodesMap
} }
) )
[ H.text "Move as candidate" ] [ H.text "Move as candidate" ]
, ,
updateTermButton updateTermButton
( commonProps `Record.merge` ( props `Record.merge`
{ variant: ButtonVariant Danger { variant: ButtonVariant Danger
, nodesMap
, rType: StopTerm , rType: StopTerm
} }
) )
...@@ -355,19 +348,22 @@ selectedNodesCpt = here.component "selectedNodes" cpt where ...@@ -355,19 +348,22 @@ selectedNodesCpt = here.component "selectedNodes" cpt where
--------------------------------------------------------- ---------------------------------------------------------
neighborhood :: R2.Leaf Props neighborhood :: R2.Leaf ()
neighborhood = R2.leaf neighborhoodCpt neighborhood = R2.leaf neighborhoodCpt
neighborhoodCpt :: R.Memo Props
neighborhoodCpt :: R.Memo ()
neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where
cpt { boxes: { sidePanelGraph } cpt _ _ = do
, graph
} _ = do
-- States -- States
{ selectedNodeIds } <- { selectedNodeIds
GEST.focusedSidePanel sidePanelGraph , graph
} <- Stores.useStore GraphStore.context
selectedNodeIds' <- selectedNodeIds' <-
T.useLive T.unequal selectedNodeIds R2.useLive' selectedNodeIds
graph' <-
R2.useLive' graph
showMore /\ showMoreBox <- showMore /\ showMoreBox <-
R2.useBox' false R2.useBox' false
...@@ -380,9 +376,9 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where ...@@ -380,9 +376,9 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where
-- Computed -- Computed
let let
minSize = F.foldl Math.min 0.0 (Seq.map _.size (SigmaxT.graphNodes graph)) minSize = F.foldl Math.min 0.0 (Seq.map _.size (SigmaxT.graphNodes graph'))
maxSize = F.foldl Math.max 0.0 (Seq.map _.size (SigmaxT.graphNodes graph)) maxSize = F.foldl Math.max 0.0 (Seq.map _.size (SigmaxT.graphNodes graph'))
maxTruncateResult = 5 maxTruncateResult = 5
...@@ -395,7 +391,7 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where ...@@ -395,7 +391,7 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where
-- Effects -- Effects
R.useEffect1' selectedNodeIds' do R.useEffect1' selectedNodeIds' do
let refreshed = neighbourBadges graph selectedNodeIds' let refreshed = neighbourBadges graph' selectedNodeIds'
let count = Seq.length refreshed let count = Seq.length refreshed
let ordered = A.sortWith (\n -> -n.size) $ Seq.toUnfoldable refreshed let ordered = A.sortWith (\n -> -n.size) $ Seq.toUnfoldable refreshed
T.write_ count termCountBox T.write_ count termCountBox
...@@ -482,26 +478,32 @@ type UpdateTermButtonProps = ...@@ -482,26 +478,32 @@ type UpdateTermButtonProps =
( variant :: ButtonVariant ( variant :: ButtonVariant
, nodesMap :: SigmaxT.NodesMap , nodesMap :: SigmaxT.NodesMap
, rType :: TermList , rType :: TermList
| Common | Props
) )
updateTermButton :: R2.Component UpdateTermButtonProps updateTermButton :: R2.Component UpdateTermButtonProps
updateTermButton = R2.component updateTermButtonCpt updateTermButton = R2.component updateTermButtonCpt
updateTermButtonCpt :: R.Component UpdateTermButtonProps updateTermButtonCpt :: R.Component UpdateTermButtonProps
updateTermButtonCpt = here.component "updateTermButton" cpt where updateTermButtonCpt = here.component "updateTermButton" cpt where
cpt { boxes: { errors cpt { boxes:
, reloadForest { errors
, sidePanelGraph } , reloadForest
}
, variant , variant
, graphId
, metaData , metaData
, nodesMap , nodesMap
, rType , rType
, session , session
} children = do } children = do
-- States -- States
{ removedNodeIds, selectedNodeIds } <- GEST.focusedSidePanel sidePanelGraph { removedNodeIds
selectedNodeIds' <- T.useLive T.unequal selectedNodeIds , selectedNodeIds
, graphId
} <- Stores.useStore GraphStore.context
selectedNodeIds' <- R2.useLive' selectedNodeIds
graphId' <- R2.useLive' graphId
-- Behaviors -- Behaviors
let let
...@@ -509,12 +511,13 @@ updateTermButtonCpt = here.component "updateTermButton" cpt where ...@@ -509,12 +511,13 @@ updateTermButtonCpt = here.component "updateTermButton" cpt where
let nodes = mapMaybe (\id -> Map.lookup id nodesMap) let nodes = mapMaybe (\id -> Map.lookup id nodesMap)
$ Set.toUnfoldable selectedNodeIds' $ Set.toUnfoldable selectedNodeIds'
sendPatches { errors sendPatches { errors
, graphId: graphId , graphId: graphId'
, metaData: metaData , metaData: metaData
, nodes , nodes
, session: session , session: session
, termList: rType , termList: rType
, reloadForest } , reloadForest
}
T.write_ selectedNodeIds' removedNodeIds T.write_ selectedNodeIds' removedNodeIds
T.write_ SigmaxT.emptyNodeIds selectedNodeIds T.write_ SigmaxT.emptyNodeIds selectedNodeIds
......
module Gargantext.Components.GraphExplorer.Sidebar.Types where
import Gargantext.Prelude
import Data.Maybe (Maybe(..), maybe)
import Data.Set as Set
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Types as GT
import Reactix as R
import Toestand as T
type SidePanel =
(
mGraph :: Maybe SigmaxT.SGraph
, mMetaData :: Maybe GET.MetaData
, multiSelectEnabled :: Boolean
, removedNodeIds :: SigmaxT.NodeIds
, selectedNodeIds :: SigmaxT.NodeIds
, showControls :: Boolean
, sideTab :: GET.SideTab
, showSidebar :: GT.SidePanelState
, showDoc :: Maybe GT.ListId
)
initialSidePanel :: Maybe (Record SidePanel)
initialSidePanel = Nothing
focusedSidePanel :: T.Box (Maybe (Record SidePanel))
-> R.Hooks { mGraph :: T.Box (Maybe SigmaxT.SGraph)
, mMetaData :: T.Box (Maybe GET.MetaData)
, multiSelectEnabled :: T.Box Boolean
, removedNodeIds :: T.Box SigmaxT.NodeIds
, selectedNodeIds :: T.Box SigmaxT.NodeIds
, showControls :: T.Box Boolean
, sideTab :: T.Box GET.SideTab
, showSidebar :: T.Box GT.SidePanelState
, showDoc :: T.Box (Maybe GT.ListId)
}
focusedSidePanel sidePanel = do
mGraph <- T.useFocused
(maybe Nothing _.mGraph)
(\val -> maybe Nothing (\sp -> Just $ sp { mGraph = val })) sidePanel
mMetaData <- T.useFocused
(maybe Nothing _.mMetaData)
(\val -> maybe Nothing (\sp -> Just $ sp { mMetaData = val })) sidePanel
multiSelectEnabled <- T.useFocused
(maybe false _.multiSelectEnabled)
(\val -> maybe Nothing (\sp -> Just $ sp { multiSelectEnabled = val })) sidePanel
removedNodeIds <- T.useFocused
(maybe Set.empty _.removedNodeIds)
(\val -> maybe Nothing (\sp -> Just $ sp { removedNodeIds = val })) sidePanel
selectedNodeIds <- T.useFocused
(maybe Set.empty _.selectedNodeIds)
(\val -> maybe Nothing (\sp -> Just $ sp { selectedNodeIds = val })) sidePanel
showControls <- T.useFocused
(maybe false _.showControls)
(\val -> maybe Nothing (\sp -> Just $ sp { showControls = val })) sidePanel
sideTab <- T.useFocused
(maybe GET.SideTabLegend _.sideTab)
(\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
showDoc <- T.useFocused
(maybe Nothing _.showDoc)
(\val -> maybe Nothing (\sp -> Just $ sp { showDoc = val }
)) sidePanel
pure $ {
mGraph
, mMetaData
, multiSelectEnabled
, removedNodeIds
, selectedNodeIds
, showControls
, sideTab
, showSidebar
, showDoc
}
module Gargantext.Components.GraphExplorer.Store
( Store
, State
, options
, context
, provide
) where
import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Data.Set as Set
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Types as GT
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Reactix as R
import Toestand as T
import Unsafe.Coerce (unsafeCoerce)
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Store"
type Store =
-- Data
( graph :: T.Box SigmaxT.SGraph
, graphId :: T.Box GET.GraphId
, mMetaData :: T.Box (Maybe GET.MetaData)
, hyperdataGraph :: T.Box GET.HyperdataGraph
-- Layout
, showControls :: T.Box Boolean
, sideTab :: T.Box GET.SideTab
, showSidebar :: T.Box GT.SidePanelState
, showDoc :: T.Box (Maybe GT.ListId)
-- Controls
, multiSelectEnabled :: T.Box Boolean
, edgeConfluence :: T.Box Range.NumberRange
, edgeWeight :: T.Box Range.NumberRange
, forceAtlasState :: T.Box SigmaxT.ForceAtlasState
, graphStage :: T.Box GET.Stage
, nodeSize :: T.Box Range.NumberRange
, showEdges :: T.Box SigmaxT.ShowEdgesState
, showLouvain :: T.Box Boolean
, labelSize :: T.Box Number
, mouseSelectorSize :: T.Box Number
, startForceAtlas :: T.Box Boolean
-- Terms update
, removedNodeIds :: T.Box SigmaxT.NodeIds
, selectedNodeIds :: T.Box SigmaxT.NodeIds
)
type State =
-- Data
( graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, mMetaData :: Maybe GET.MetaData
, hyperdataGraph :: GET.HyperdataGraph
-- Layout
, showControls :: Boolean
, sideTab :: GET.SideTab
, showSidebar :: GT.SidePanelState
, showDoc :: Maybe GT.ListId
-- Controls
, multiSelectEnabled :: Boolean
, edgeConfluence :: Range.NumberRange
, edgeWeight :: Range.NumberRange
, forceAtlasState :: SigmaxT.ForceAtlasState
, graphStage :: GET.Stage
, nodeSize :: Range.NumberRange
, showEdges :: SigmaxT.ShowEdgesState
, showLouvain :: Boolean
, labelSize :: Number
, mouseSelectorSize :: Number
, startForceAtlas :: Boolean
-- Terms update
, removedNodeIds :: SigmaxT.NodeIds
, selectedNodeIds :: SigmaxT.NodeIds
)
options ::
{ labelSize :: Number
, mouseSelectorSize :: Number
, multiSelectEnabled :: Boolean
, removedNodeIds :: SigmaxT.NodeIds
, selectedNodeIds :: SigmaxT.NodeIds
, showControls :: Boolean
, showDoc :: Maybe GT.ListId
, showSidebar :: GT.SidePanelState
, sideTab :: GET.SideTab
, edgeConfluence :: Range.NumberRange
, graphStage :: GET.Stage
, nodeSize :: Range.NumberRange
, showLouvain :: Boolean
, showEdges :: SigmaxT.ShowEdgesState
}
options =
-- Layout
{ showControls : false
, sideTab : GET.SideTabLegend
, showSidebar : GT.InitialClosed
, showDoc : Nothing
-- Controls
, multiSelectEnabled : false
, labelSize : 14.0
, mouseSelectorSize : 15.0
, edgeConfluence : Range.Closed { min: 0.0, max: 1.0 }
, graphStage : GET.Init
, nodeSize : Range.Closed { min: 0.0, max: 100.0 }
, showLouvain : false
, showEdges : SigmaxT.EShow
-- Terms update
, removedNodeIds : Set.empty
, selectedNodeIds : Set.empty
}
context :: R.Context (Record Store)
context = R.createContext $ unsafeCoerce unit
provide :: Record State -> Array R.Element -> R.Element
provide values = Stores.provideStore here.name values context
module Gargantext.Components.GraphExplorer.Toolbar.Controls module Gargantext.Components.GraphExplorer.Toolbar.Controls
( Controls ( controls
, useGraphControls
, controls
, controlsCpt
) where ) where
import Prelude import Prelude
...@@ -15,10 +12,10 @@ import Data.Sequence as Seq ...@@ -15,10 +12,10 @@ import Data.Sequence as Seq
import Data.Set as Set import Data.Set as Set
import Effect.Timer (setTimeout) import Effect.Timer (setTimeout)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Toolbar.Buttons (centerButton, cameraButton, edgesToggleButton, louvainToggleButton, pauseForceAtlasButton, multiSelectEnabledButton) import Gargantext.Components.GraphExplorer.Toolbar.Buttons (centerButton, cameraButton, edgesToggleButton, louvainToggleButton, pauseForceAtlasButton, multiSelectEnabledButton)
import Gargantext.Components.GraphExplorer.Toolbar.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl) import Gargantext.Components.GraphExplorer.Toolbar.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.GraphExplorer.Toolbar.SlideButton (labelSizeButton, mouseSelectorSizeButton) import Gargantext.Components.GraphExplorer.Toolbar.SlideButton (labelSizeButton, mouseSelectorSizeButton)
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax as Sigmax
...@@ -27,6 +24,7 @@ import Gargantext.Sessions (Session) ...@@ -27,6 +24,7 @@ import Gargantext.Sessions (Session)
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils.Range as Range import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -36,340 +34,262 @@ here :: R2.Here ...@@ -36,340 +34,262 @@ here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Toolbar.Controls" here = R2.here "Gargantext.Components.GraphExplorer.Toolbar.Controls"
type Controls = type Controls =
( edgeConfluence :: T.Box Range.NumberRange ( reloadForest :: T2.ReloadS
, edgeWeight :: T.Box Range.NumberRange
, forceAtlasState :: T.Box SigmaxT.ForceAtlasState
, graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, graphStage :: T.Box Graph.Stage
, hyperdataGraph :: GET.HyperdataGraph
, multiSelectEnabled :: T.Box Boolean
, nodeSize :: T.Box Range.NumberRange
, reloadForest :: T2.ReloadS
, removedNodeIds :: T.Box SigmaxT.NodeIds
, selectedNodeIds :: T.Box SigmaxT.NodeIds
, session :: Session , session :: Session
, showControls :: T.Box Boolean
, showEdges :: T.Box SigmaxT.ShowEdgesState
, showLouvain :: T.Box Boolean
, showSidebar :: T.Box GT.SidePanelState
, sideTab :: T.Box GET.SideTab
, sigmaRef :: R.Ref Sigmax.Sigma , sigmaRef :: R.Ref Sigmax.Sigma
) )
type LocalControls = ( labelSize :: T.Box Number, mouseSelectorSize :: T.Box Number ) controls :: R2.Leaf Controls
controls = R2.leaf controlsCpt
initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
labelSize <- T.useBox 14.0
mouseSelectorSize <- T.useBox 15.0
pure $ { labelSize, mouseSelectorSize }
controls :: R2.Component Controls controlsCpt :: R.Memo Controls
controls = R.createElement controlsCpt controlsCpt = R.memo' $ here.component "controls" cpt where
controlsCpt :: R.Component Controls cpt { reloadForest
controlsCpt = here.component "controls" cpt , session
where , sigmaRef
cpt { edgeConfluence } _ = do
, edgeWeight -- | States
, forceAtlasState -- |
, graph { edgeConfluence
, graphId , edgeWeight
, graphStage , forceAtlasState
, hyperdataGraph , graph
, multiSelectEnabled , graphId
, nodeSize , graphStage
, reloadForest , hyperdataGraph
, selectedNodeIds , multiSelectEnabled
, session , nodeSize
, showEdges , selectedNodeIds
, showLouvain , showEdges
, showSidebar , showLouvain
, sideTab , showSidebar
, sigmaRef } _ = do , sideTab
, mouseSelectorSize
, labelSize
} <- Stores.useStore GraphStore.context
-- | States forceAtlasState' <- R2.useLive' forceAtlasState
-- | graph' <- R2.useLive' graph
graphId' <- R2.useLive' graphId
graphStage' <- R2.useLive' graphStage
hyperdataGraph' <- R2.useLive' hyperdataGraph
selectedNodeIds' <- R2.useLive' selectedNodeIds
showSidebar' <- R2.useLive' showSidebar
forceAtlasState' <- T.useLive T.unequal forceAtlasState -- ref to track automatic FA pausing
graphStage' <- T.useLive T.unequal graphStage -- If user pauses FA before auto is triggered, clear the timeoutId
selectedNodeIds' <- T.useLive T.unequal selectedNodeIds mFAPauseRef <- R.useRef Nothing
showSidebar' <- T.useLive T.unequal showSidebar
localControls <- initialLocalControls
-- ref to track automatic FA pausing
-- If user pauses FA before auto is triggered, clear the timeoutId
mFAPauseRef <- R.useRef Nothing
-- | Effects
-- |
-- | Effects -- When graph is changed, cleanup the mFAPauseRef so that forceAtlas
-- | -- timeout is retriggered.
R.useEffect' $ do
case graphStage' of
GET.Init -> R.setRef mFAPauseRef Nothing
_ -> pure unit
-- When graph is changed, cleanup the mFAPauseRef so that forceAtlas -- Handle case when FA is paused from outside events, eg. the automatic timer.
-- timeout is retriggered. R.useEffect' $ Sigmax.handleForceAtlas2Pause sigmaRef forceAtlasState mFAPauseRef Graph.forceAtlas2Settings
R.useEffect' $ do
case graphStage' of
Graph.Init -> R.setRef mFAPauseRef Nothing
_ -> pure unit
-- Handle case when FA is paused from outside events, eg. the automatic timer. -- Handle automatic edge hiding when FA is running (to prevent flickering).
R.useEffect' $ Sigmax.handleForceAtlas2Pause sigmaRef forceAtlasState mFAPauseRef Graph.forceAtlas2Settings -- TODO Commented temporarily: this breaks forceatlas rendering after reset
-- NOTE This is a hack anyways. It's force atlas that should be fixed.
R.useEffect2' sigmaRef forceAtlasState' $ do
T.modify_ (SigmaxT.forceAtlasEdgeState forceAtlasState') showEdges
-- Handle automatic edge hiding when FA is running (to prevent flickering). -- Automatic opening of sidebar when a node is selected (but only first time).
-- TODO Commented temporarily: this breaks forceatlas rendering after reset R.useEffect' $ do
-- NOTE This is a hack anyways. It's force atlas that should be fixed. if showSidebar' == GT.InitialClosed && (not Set.isEmpty selectedNodeIds') then do
R.useEffect2' sigmaRef forceAtlasState' $ do T.write_ GT.Opened showSidebar
T.modify_ (SigmaxT.forceAtlasEdgeState forceAtlasState') showEdges T.write_ GET.SideTabData sideTab
else
pure unit
-- Automatic opening of sidebar when a node is selected (but only first time). -- Timer to turn off the initial FA. This is because FA eats up lot of
R.useEffect' $ do -- CPU, has memory leaks etc.
if showSidebar' == GT.InitialClosed && (not Set.isEmpty selectedNodeIds') then do R.useEffect1' forceAtlasState' $ do
T.write_ GT.Opened showSidebar if forceAtlasState' == SigmaxT.InitialRunning then do
T.write_ GET.SideTabData sideTab timeoutId <- setTimeout 9000 $ do
case forceAtlasState' of
SigmaxT.InitialRunning ->
T.write_ SigmaxT.Paused forceAtlasState
_ -> pure unit
R.setRef mFAPauseRef Nothing
R.setRef mFAPauseRef $ Just timeoutId
pure unit
else else
pure unit pure unit
-- Timer to turn off the initial FA. This is because FA eats up lot of
-- CPU, has memory leaks etc.
R.useEffect1' forceAtlasState' $ do
if forceAtlasState' == SigmaxT.InitialRunning then do
timeoutId <- setTimeout 9000 $ do
case forceAtlasState' of
SigmaxT.InitialRunning ->
T.write_ SigmaxT.Paused forceAtlasState
_ -> pure unit
R.setRef mFAPauseRef Nothing
R.setRef mFAPauseRef $ Just timeoutId
pure unit
else
pure unit
-- | Computed -- | Computed
-- | -- |
let edgesConfluenceSorted = A.sortWith (_.confluence) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph let edgesConfluenceSorted = A.sortWith (_.confluence) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph'
let edgeConfluenceMin = maybe 0.0 _.confluence $ A.head edgesConfluenceSorted let edgeConfluenceMin = maybe 0.0 _.confluence $ A.head edgesConfluenceSorted
let edgeConfluenceMax = maybe 100.0 _.confluence $ A.last edgesConfluenceSorted let edgeConfluenceMax = maybe 100.0 _.confluence $ A.last edgesConfluenceSorted
let edgeConfluenceRange = Range.Closed { min: edgeConfluenceMin, max: edgeConfluenceMax } let edgeConfluenceRange = Range.Closed { min: edgeConfluenceMin, max: edgeConfluenceMax }
--let edgesWeightSorted = A.sortWith (_.weight) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph --let edgesWeightSorted = A.sortWith (_.weight) $ Seq.toUnfoldable $ SigmaxT.graphEdges graph
--let edgeWeightMin = maybe 0.0 _.weight $ A.head edgesWeightSorted --let edgeWeightMin = maybe 0.0 _.weight $ A.head edgesWeightSorted
--let edgeWeightMax = maybe 100.0 _.weight $ A.last edgesWeightSorted --let edgeWeightMax = maybe 100.0 _.weight $ A.last edgesWeightSorted
--let edgeWeightRange = Range.Closed { min: edgeWeightMin, max: edgeWeightMax } --let edgeWeightRange = Range.Closed { min: edgeWeightMin, max: edgeWeightMax }
let edgeWeightRange = Range.Closed { let edgeWeightRange = Range.Closed {
min: 0.0 min: 0.0
, max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph'
} }
let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxT.graphNodes graph let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxT.graphNodes graph'
let nodeSizeMin = maybe 0.0 _.size $ A.head nodesSorted let nodeSizeMin = maybe 0.0 _.size $ A.head nodesSorted
let nodeSizeMax = maybe 100.0 _.size $ A.last nodesSorted let nodeSizeMax = maybe 100.0 _.size $ A.last nodesSorted
let nodeSizeRange = Range.Closed { min: nodeSizeMin, max: nodeSizeMax } let nodeSizeRange = Range.Closed { min: nodeSizeMin, max: nodeSizeMax }
let gap = H.span { className: "graph-toolbar__gap" } [] let gap = H.span { className: "graph-toolbar__gap" } []
-- | Render -- | Render
-- | -- |
pure $ pure $
H.nav H.nav
{ className: "graph-toolbar" } { className: "graph-toolbar" }
[
H.div
{ className: "flex-shrink-0" }
[ [
H.div H.div
{ className: "flex-shrink-0" } { className: "d-flex" }
[ [
H.div -- View Settings
{ className: "d-flex" } B.fieldset
{ className: "graph-toolbar__section"
, titleSlot: H.text "View settings"
}
[ [
-- View Settings -- change type button (?)
B.fieldset centerButton sigmaRef
{ className: "graph-toolbar__section"
, titleSlot: H.text "View settings"
}
[
-- change type button (?)
centerButton sigmaRef
,
gap
,
edgesToggleButton
{ state: showEdges
, stateAtlas: forceAtlasState
}
,
gap
,
louvainToggleButton { state: showLouvain }
]
, ,
-- Actions gap
B.fieldset ,
{ className: "graph-toolbar__section" edgesToggleButton
, titleSlot: H.text "Actions" { state: showEdges
, stateAtlas: forceAtlasState
} }
[ ,
-- resetForceAtlasButton { forceAtlasState, sigmaRef } gap
pauseForceAtlasButton { state: forceAtlasState } ,
, louvainToggleButton { state: showLouvain }
gap
,
cameraButton
{ id: graphId
, hyperdataGraph: hyperdataGraph
, session: session
, sigmaRef: sigmaRef
, reloadForest
}
]
] ]
, ,
-- Selection Settings -- Actions
B.fieldset B.fieldset
{ className: intercalate " " { className: "graph-toolbar__section"
[ "graph-toolbar__section" , titleSlot: H.text "Actions"
, "graph-toolbar__section--selection"
]
, titleSlot: H.text "Selection settings"
} }
[ [
-- zoom: 0 -100 - calculate ratio -- resetForceAtlasButton { forceAtlasState, sigmaRef }
multiSelectEnabledButton { state: multiSelectEnabled } pauseForceAtlasButton { state: forceAtlasState }
, ,
gap gap
, ,
-- toggle multi node selection cameraButton
-- save button { id: graphId'
mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize , hyperdataGraph: hyperdataGraph'
, session: session
, sigmaRef: sigmaRef
, reloadForest
}
] ]
] ]
, ,
-- Controls -- Selection Settings
B.fieldset B.fieldset
{ className: intercalate " " { className: intercalate " "
[ "graph-toolbar__section" [ "graph-toolbar__section"
, "graph-toolbar__section--controls" , "graph-toolbar__section--selection"
, "flex-grow-1 flex-shrink-1"
] ]
, titleSlot: H.text "Controls" , titleSlot: H.text "Selection settings"
} }
[ [
H.div -- zoom: 0 -100 - calculate ratio
{ className: "d-flex justify-content-between mb-3" } multiSelectEnabledButton { state: multiSelectEnabled }
[ ,
edgeConfluenceControl gap
{ range: edgeConfluenceRange ,
, state: edgeConfluence } -- toggle multi node selection
, -- save button
edgeWeightControl mouseSelectorSizeButton sigmaRef mouseSelectorSize
{ range: edgeWeightRange ]
, state: edgeWeight } ]
,
-- Controls
B.fieldset
{ className: intercalate " "
[ "graph-toolbar__section"
, "graph-toolbar__section--controls"
, "flex-grow-1 flex-shrink-1"
] ]
, titleSlot: H.text "Controls"
}
[
H.div
{ className: "d-flex justify-content-between mb-3" }
[
edgeConfluenceControl
{ range: edgeConfluenceRange
, state: edgeConfluence }
, ,
H.div edgeWeightControl
{ className: "d-flex justify-content-between" } { range: edgeWeightRange
[ , state: edgeWeight }
-- change level ]
-- file upload ,
-- run demo H.div
-- search button { className: "d-flex justify-content-between" }
-- search topics [
labelSizeButton sigmaRef localControls.labelSize -- change level
, -- file upload
-- labels size: 1-4 -- run demo
nodeSizeControl -- search button
{ range: nodeSizeRange -- search topics
, state: nodeSize } labelSizeButton sigmaRef labelSize
,
-- labels size: 1-4
nodeSizeControl
{ range: nodeSizeRange
, state: nodeSize }
]
] ]
] ]
]
-- H.ul {} [ -- change type button (?) -- H.ul {} [ -- change type button (?)
-- H.li {} [ centerButton sigmaRef ] -- H.li {} [ centerButton sigmaRef ]
-- , H.li {} [ pauseForceAtlasButton {state: forceAtlasState} ] -- , H.li {} [ pauseForceAtlasButton {state: forceAtlasState} ]
-- , H.li {} [ edgesToggleButton {state: showEdges} ] -- , H.li {} [ edgesToggleButton {state: showEdges} ]
-- , H.li {} [ louvainToggleButton showLouvain ] -- , H.li {} [ louvainToggleButton showLouvain ]
-- , H.li {} [ edgeConfluenceControl edgeConfluenceRange edgeConfluence ] -- , H.li {} [ edgeConfluenceControl edgeConfluenceRange edgeConfluence ]
-- , H.li {} [ edgeWeightControl edgeWeightRange edgeWeight ] -- , H.li {} [ edgeWeightControl edgeWeightRange edgeWeight ]
-- -- change level -- -- change level
-- -- file upload -- -- file upload
-- -- run demo -- -- run demo
-- -- search button -- -- search button
-- -- search topics -- -- search topics
-- , H.li {} [ labelSizeButton sigmaRef localControls.labelSize ] -- labels size: 1-4 -- , H.li {} [ labelSizeButton sigmaRef localControls.labelSize ] -- labels size: 1-4
-- , H.li {} [ nodeSizeControl nodeSizeRange nodeSize ] -- , H.li {} [ nodeSizeControl nodeSizeRange nodeSize ]
-- -- zoom: 0 -100 - calculate ratio -- -- zoom: 0 -100 - calculate ratio
-- , H.li {} [ multiSelectEnabledButton multiSelectEnabled ] -- toggle multi node selection -- , H.li {} [ multiSelectEnabledButton multiSelectEnabled ] -- toggle multi node selection
-- -- save button -- -- save button
-- , H.li {} [ nodeSearchControl { graph: graph -- , H.li {} [ nodeSearchControl { graph: graph
-- , multiSelectEnabled: multiSelectEnabled -- , multiSelectEnabled: multiSelectEnabled
-- , selectedNodeIds: selectedNodeIds } ] -- , selectedNodeIds: selectedNodeIds } ]
-- , H.li {} [ mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize ] -- , H.li {} [ mouseSelectorSizeButton sigmaRef localControls.mouseSelectorSize ]
-- , H.li {} [ cameraButton { id: graphId -- , H.li {} [ cameraButton { id: graphId
-- , hyperdataGraph: hyperdataGraph -- , hyperdataGraph: hyperdataGraph
-- , session: session -- , session: session
-- , sigmaRef: sigmaRef -- , sigmaRef: sigmaRef
-- , reloadForest: reloadForest } ] -- , reloadForest: reloadForest } ]
-- ] -- ]
-- ] -- ]
useGraphControls :: { forceAtlasS :: SigmaxT.ForceAtlasState
, graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, hyperdataGraph :: GET.HyperdataGraph
, reloadForest :: T2.ReloadS
, session :: Session
, sidePanel :: T.Box (Maybe (Record GEST.SidePanel))
}
-> R.Hooks (Record Controls)
useGraphControls { forceAtlasS
, graph
, graphId
, hyperdataGraph
, reloadForest
, session
, sidePanel
} = do
edgeConfluence <- T.useBox $ Range.Closed { min: 0.0, max: 1.0 }
edgeWeight <- T.useBox $ Range.Closed {
min: 0.0
, max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
}
forceAtlasState <- T.useBox forceAtlasS
graphStage <- T.useBox Graph.Init
nodeSize <- T.useBox $ Range.Closed { min: 0.0, max: 100.0 }
showEdges <- T.useBox SigmaxT.EShow
showLouvain <- T.useBox false
sigma <- Sigmax.initSigma
sigmaRef <- R.useRef sigma
{ multiSelectEnabled
, removedNodeIds
, selectedNodeIds
, showControls
, sideTab
, showSidebar
} <- GEST.focusedSidePanel sidePanel
pure { edgeConfluence
, edgeWeight
, forceAtlasState
, graph
, graphId
, graphStage
, hyperdataGraph
, multiSelectEnabled
, nodeSize
, removedNodeIds
, selectedNodeIds
, session
, showControls
, showEdges
, showLouvain
, showSidebar
, sideTab
, sigmaRef
, reloadForest
}
...@@ -2,42 +2,38 @@ module Gargantext.Components.GraphExplorer.TopBar (topBar) where ...@@ -2,42 +2,38 @@ module Gargantext.Components.GraphExplorer.TopBar (topBar) where
import Gargantext.Prelude hiding (max, min) import Gargantext.Prelude hiding (max, min)
import Data.Maybe (Maybe)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Topbar.Search (nodeSearchControl) import Gargantext.Components.GraphExplorer.Topbar.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils ((?)) import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
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
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"
topBar :: R2.Leaf Props topBar :: R2.Leaf ()
topBar = R2.leaf component topBar = R2.leaf component
component :: R.Component Props component :: R.Component ()
component = here.component "topBar" cpt where component = here.component "topBar" cpt where
cpt { sidePanelGraph } _ = do cpt _ _ = do
-- States -- States
{ mGraph { graph
, multiSelectEnabled , multiSelectEnabled
, selectedNodeIds , selectedNodeIds
, showControls , showControls
, showSidebar , showSidebar
} <- GEST.focusedSidePanel sidePanelGraph } <- Stores.useStore GraphStore.context
mGraph' <- R2.useLive' mGraph graph' <- R2.useLive' graph
showControls' <- R2.useLive' showControls showControls' <- R2.useLive' showControls
showSidebar' <- R2.useLive' showSidebar showSidebar' <- R2.useLive' showSidebar
-- Render -- Render
pure $ pure $
...@@ -73,12 +69,10 @@ component = here.component "topBar" cpt where ...@@ -73,12 +69,10 @@ component = here.component "topBar" cpt where
] ]
, ,
-- Search -- Search
R2.fromMaybe_ mGraph' \graph -> nodeSearchControl
{ graph: graph'
nodeSearchControl , multiSelectEnabled
{ graph , selectedNodeIds
, multiSelectEnabled , className: "graph-topbar__search"
, selectedNodeIds }
, className: "graph-topbar__search"
}
] ]
module Gargantext.Components.GraphExplorer.Types where module Gargantext.Components.GraphExplorer.Types where
import Gargantext.Prelude
import Data.Array ((!!), length) import Data.Array ((!!), length)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Eq.Generic (genericEq) import Data.Eq.Generic (genericEq)
import Data.Maybe (Maybe(..), fromJust) import Data.Maybe (Maybe(..), fromJust)
import Data.Newtype (class Newtype) import Data.Newtype (class Newtype)
import Data.Ord
import Data.Ord.Generic (genericCompare) import Data.Ord.Generic (genericCompare)
import Data.Symbol (SProxy(..)) import Data.Symbol (SProxy(..))
import Partial.Unsafe (unsafePartial) import Partial.Unsafe (unsafePartial)
import Record as Record import Record as Record
import Simple.JSON as JSON import Simple.JSON as JSON
import Gargantext.Prelude
type GraphId = Int type GraphId = Int
newtype Node = Node { newtype Node = Node {
...@@ -164,7 +163,7 @@ instance Ord SelectedNode where compare = genericCompare ...@@ -164,7 +163,7 @@ instance Ord SelectedNode where compare = genericCompare
instance Show SelectedNode where show (SelectedNode node) = node.label instance Show SelectedNode where show (SelectedNode node) = node.label
type State = ( -- type State = (
-- corpusId :: R.State Int -- corpusId :: R.State Int
--, filePath :: R.State String --, filePath :: R.State String
--, graphData :: R.State GraphData --, graphData :: R.State GraphData
...@@ -177,7 +176,7 @@ type State = ( ...@@ -177,7 +176,7 @@ type State = (
--, sigmaGraphData :: R.State (Maybe SigmaxTypes.SGraph) --, sigmaGraphData :: R.State (Maybe SigmaxTypes.SGraph)
--, sigmaSettings :: R.State ({|Graph.SigmaSettings}) --, sigmaSettings :: R.State ({|Graph.SigmaSettings})
--treeId :: R.State (Maybe TreeId) --treeId :: R.State (Maybe TreeId)
) -- )
initialGraphData :: GraphData initialGraphData :: GraphData
initialGraphData = GraphData { initialGraphData = GraphData {
...@@ -255,3 +254,7 @@ instance JSON.ReadForeign HyperdataGraph where ...@@ -255,3 +254,7 @@ instance JSON.ReadForeign HyperdataGraph where
pure $ HyperdataGraph $ Record.rename cameraP mCameraP inst pure $ HyperdataGraph $ Record.rename cameraP mCameraP inst
instance JSON.WriteForeign HyperdataGraph where instance JSON.WriteForeign HyperdataGraph where
writeImpl (HyperdataGraph c) = JSON.writeImpl $ Record.rename mCameraP cameraP c writeImpl (HyperdataGraph c) = JSON.writeImpl $ Record.rename mCameraP cameraP c
data Stage = Init | Ready | Cleanup
derive instance Generic Stage _
derive instance Eq Stage
...@@ -5,21 +5,24 @@ module Gargantext.Components.Nodes.Corpus.Graph ...@@ -5,21 +5,24 @@ module Gargantext.Components.Nodes.Corpus.Graph
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple (document, querySelector) import DOM.Simple (document, querySelector)
import Data.Int as I
import Data.Maybe (Maybe(..), isJust, maybe) import Data.Maybe (Maybe(..), isJust, maybe)
import Data.Set as Set import Data.Sequence as Seq
import Data.Tuple (Tuple(..)) import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\)) 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.Bootstrap as B
import Gargantext.Components.GraphExplorer.Layout (convert, layout) import Gargantext.Components.GraphExplorer.Layout (convert, layout)
import Gargantext.Components.GraphExplorer.Toolbar.Controls as Controls import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect) import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Routes (SessionRoute(NodeAPI)) import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Sessions (Session, get) import Gargantext.Sessions (Session, get)
import Gargantext.Types as Types import Gargantext.Types as Types
import Gargantext.Utils.Range as Range
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 as R
...@@ -123,18 +126,18 @@ graphLayoutCpt = here.component "explorerLayout" cpt where ...@@ -123,18 +126,18 @@ graphLayoutCpt = here.component "explorerLayout" cpt where
handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) = handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) =
content { graph content { graph
, hyperdataGraph: loaded , hyperdataGraph: loaded
, mMetaData' , mMetaData
, session , session
, boxes: props.boxes , boxes: props.boxes
, graphId , graphId
} }
where where
Tuple mMetaData' graph = convert hyperdataGraph Tuple mMetaData graph = convert hyperdataGraph
-------------------------------------------------------- --------------------------------------------------------
type ContentProps = type ContentProps =
( mMetaData' :: Maybe GET.MetaData ( mMetaData :: Maybe GET.MetaData
, graph :: SigmaxT.SGraph , graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph , hyperdataGraph :: GET.HyperdataGraph
, session :: Session , session :: Session
...@@ -147,59 +150,63 @@ content = R2.leaf contentCpt ...@@ -147,59 +150,63 @@ content = R2.leaf contentCpt
contentCpt :: R.Component ContentProps contentCpt :: R.Component ContentProps
contentCpt = here.component "content" cpt where contentCpt = here.component "content" cpt where
cpt props@{ boxes cpt { boxes
, mMetaData' , mMetaData
, graph
, graphId
, session
, hyperdataGraph
} _ = do
-- | Computed
-- |
let
startForceAtlas = maybe true
(\(GET.MetaData { startForceAtlas: sfa }) -> sfa) mMetaData'
forceAtlasS = if startForceAtlas
then SigmaxT.InitialRunning
else SigmaxT.InitialStopped
-- | Hooks
-- |
-- Hydrate controls
controls <- Controls.useGraphControls
{ forceAtlasS
, graph , graph
, graphId , graphId
, hyperdataGraph
, reloadForest: boxes.reloadForest
, session , session
, sidePanel: boxes.sidePanelGraph , hyperdataGraph
} } _ = do
-- | Computed
-- |
let
startForceAtlas = maybe true
(\(GET.MetaData { startForceAtlas: sfa }) -> sfa) mMetaData
forceAtlasState
= if startForceAtlas
then SigmaxT.InitialRunning
else SigmaxT.InitialStopped
-- Hydrate GraphStore
state :: Record GraphStore.State
state =
-- Data
{ graph
, graphId
, mMetaData
, hyperdataGraph
-- Controls
, startForceAtlas
, forceAtlasState
, edgeWeight: Range.Closed
{ min: 0.0
, max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
}
-- (default options)
-- @WIP: testing order of Record.merge
} `Record.merge` GraphStore.options
-- Hydrate Boxes -- | Hooks
R.useEffectOnce' $ -- |
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
, showDoc: Nothing
}
-- | Render sigmaRef <- Sigmax.initSigma >>= R.useRef
-- |
-- | Render
-- |
pure $ pure $
layout $ GraphStore.provide
{ controls state
} `Record.merge` props [
layout
{ session
, boxes
, sigmaRef
, graphId
}
]
-------------------------------------------------------------- --------------------------------------------------------------
......
...@@ -91,6 +91,7 @@ type Module = String ...@@ -91,6 +91,7 @@ type Module = String
type Here = type Here =
{ component :: forall p. String -> R.HooksComponent p -> R.Component p { component :: forall p. String -> R.HooksComponent p -> R.Component p
, ntComponent :: forall p. String -> NTHooksComponent p -> NTComponent p , ntComponent :: forall p. String -> NTHooksComponent p -> NTComponent p
, name :: Module
| RowConsole | RowConsole
} }
...@@ -98,6 +99,7 @@ here :: Module -> Here ...@@ -98,6 +99,7 @@ here :: Module -> Here
here mod = here mod =
{ component : R.hooksComponentWithModule mod { component : R.hooksComponentWithModule mod
, ntComponent : ntHooksComponentWithModule mod , ntComponent : ntHooksComponentWithModule mod
, name : mod
, log : Console.print Console.Main mod Console.Log , log : Console.print Console.Main mod Console.Log
, log2 : Console.print2 Console.Main mod Console.Log , log2 : Console.print2 Console.Main mod Console.Log
, log3 : Console.print3 Console.Main mod Console.Log , log3 : Console.print3 Console.Main mod Console.Log
......
module Gargantext.Utils.Stores
( createStore
, provideStore
, useStore
) where
import Gargantext.Prelude
import Prim.RowList (class RowToList)
import Reactix as R
import Toestand as T
import Toestand.Records as TR
-- StoreFactory: R.Hook (Record Store) → create focus boxes, hydrate box values
-- StoreProvider: R.Hooks (Record Store) → provide context with previous boxes
-- ↓
-- StoreHook: R.Hooks (Record Store) → use context
-- | From state values to focused boxes
createStore :: forall boxes l state.
RowToList state l
=> TR.UseFocusedFields l boxes () (T.Box (Record state))
=> Record state
-> R.Hooks (Record boxes)
createStore = T.useBox >=> flip T.useFocusedFields {}
-- | Set <Store.Provider> bearing Stores as a Context
-- |
-- | (!) do not use store binds in this specific component (eg. `useStores`)
provideStore :: forall boxes l state.
RowToList state l
=> TR.UseFocusedFields l boxes () (T.Box (Record state))
=> String
-> Record state
-> R.Context (Record boxes)
-> Array R.Element
-> R.Element
provideStore name state context
= R.createElement (R.hooksComponent name cpt) {}
where
cpt _ children = do
store <- createStore state
pure $ R.provideContext context store $ children
-- | (?) As we use "React Provide API", we just want to rely on its Global
-- | Reference (and not as Mutable State thanks to "Consumer API")
-- | Hence the "unsafeCoerce" used on every provided context, avoiding
-- | unwanted computing at each import
-- |
-- | It also implies that every call to the proxy reference (made thanks to
-- | below <Store.Provider> are made AFTER first mount of this very,
-- | component, otherwise, every call will return the empty `unit`)
useStore :: forall boxes.
R.Context (Record boxes)
-> R.Hooks (Record boxes)
useStore = R.useContext
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