Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
purescript-gargantext
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Grégoire Locqueville
purescript-gargantext
Commits
9b8fde6e
Commit
9b8fde6e
authored
Dec 11, 2019
by
Alexandre Delanoë
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev-warnings-cleanup' into dev
parents
73386281
57f6fa5c
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
293 additions
and
165 deletions
+293
-165
Login.css
dist/styles/Login.css
+5
-12
range-slider.css
dist/styles/range-slider.css
+2
-0
range-slider.sass
dist/styles/range-slider.sass
+2
-0
Graph.purs
src/Gargantext/Components/Graph.purs
+17
-18
GraphExplorer.purs
src/Gargantext/Components/GraphExplorer.purs
+79
-64
Controls.purs
src/Gargantext/Components/GraphExplorer/Controls.purs
+37
-28
RangeControl.purs
src/Gargantext/Components/GraphExplorer/RangeControl.purs
+33
-16
Search.purs
src/Gargantext/Components/GraphExplorer/Search.purs
+51
-0
Sidebar.purs
src/Gargantext/Components/GraphExplorer/Sidebar.purs
+1
-2
ToggleButton.purs
src/Gargantext/Components/GraphExplorer/ToggleButton.purs
+13
-1
Types.purs
src/Gargantext/Components/GraphExplorer/Types.purs
+1
-1
Sigmax.purs
src/Gargantext/Hooks/Sigmax.purs
+27
-15
Sigma.purs
src/Gargantext/Hooks/Sigmax/Sigma.purs
+8
-0
Types.purs
src/Gargantext/Hooks/Sigmax/Types.purs
+17
-8
No files found.
dist/styles/Login.css
View file @
9b8fde6e
...
...
@@ -79,18 +79,6 @@ li#rename #rename-a {
width
:
1300px
;
}
#search-popup-tooltip
{
position
:
absolute
;
left
:
300px
;
top
:
-300px
;
background-color
:
white
;
z-index
:
1000
;
}
#search-popup-tooltip
:hover
{
border
:
none
;
text-decoration
:
none
;
}
#create-node-tooltip
{
position
:
absolute
;
left
:
96px
;
...
...
@@ -216,6 +204,11 @@ li:hover a#rename-leaf {
justify-content
:
center
;
}
.flex-center
{
display
:
flex
;
justify-content
:
space-between
;
}
a
:focus
,
a
:hover
{
cursor
:
pointer
;
}
...
...
dist/styles/range-slider.css
View file @
9b8fde6e
.range
{
width
:
400px
;
/* some space for the right knob */
padding-right
:
30px
;
}
.range
.range-slider
{
position
:
relative
;
...
...
dist/styles/range-slider.sass
View file @
9b8fde6e
.range
width
:
400px
/* some space for the right knob */
padding-right
:
30px
.range-slider
position
:
relative
...
...
src/Gargantext/Components/Graph.purs
View file @
9b8fde6e
...
...
@@ -4,7 +4,9 @@ module Gargantext.Components.Graph
-- , forceAtlas2Settings, ForceAtlas2Settings, ForceAtlas2OptionalSettings
-- )
where
import Prelude (bind, const, discard, pure, ($), unit)
import Prelude (bind, const, discard, pure, ($), unit, map)
import Data.Array as A
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable)
...
...
@@ -22,20 +24,18 @@ import Gargantext.Hooks.Sigmax.Sigma as Sigma
type OnProps = ()
type Graph = SigmaxTypes.Graph SigmaxTypes.Node SigmaxTypes.Edge
data Stage = Init | Ready | Cleanup
type Props sigma forceatlas2 =
( elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2
, graph :: Graph
,
selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, graph ::
SigmaxTypes.S
Graph
,
multiSelectEnabledRef :: R.Ref Boolean
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma
, stage :: R.State Stage
, transformedGraph :: Graph
, transformedGraph ::
SigmaxTypes.S
Graph
)
graph :: forall s fa2. Record (Props s fa2) -> R.Element
...
...
@@ -54,9 +54,8 @@ graphCpt = R.hooksComponent "Graph" cpt
Nothing -> RH.div {} []
Just el -> R.createPortal [] el
stageHooks props@{stage: (Init /\ setStage)} = do
stageHooks props@{
multiSelectEnabledRef, selectedNodeIds, sigmaRef,
stage: (Init /\ setStage)} = do
R.useEffectOnce $ do
log "[graphCpt] effect once"
let rSigma = R.readRef props.sigmaRef
case Sigmax.readSigma rSigma of
...
...
@@ -67,7 +66,7 @@ graphCpt = R.hooksComponent "Graph" cpt
Right sig -> do
Sigmax.writeSigma rSigma $ Just sig
Sigmax.dependOnContainer props.elRef "[graphCpt] container not found" $ \c -> do
Sigmax.dependOnContainer props.elRef "[graphCpt
(Ready)
] container not found" $ \c -> do
_ <- Sigma.addRenderer sig {
"type": "canvas"
, container: c
...
...
@@ -76,28 +75,28 @@ graphCpt = R.hooksComponent "Graph" cpt
Sigmax.refreshData sig $ Sigmax.sigmafy props.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
Sigmax.setEdges sig false
Sigma.startForceAtlas2 sig props.forceAtlas2Settings
-- bind the click event only initially, when ref was empty
Sigmax.bindSelectedNodesClick props.sigmaRef props.selectedNodeIds
Sigmax.bindSelectedEdgesClick props.sigmaRef props.selectedEdgeIds
Just sig -> do
pure unit
setStage $ const
$
Ready
setStage $ const Ready
delay unit $ \_ -> do
log "[graphCpt] cleanup"
pure $ pure unit
stageHooks props@{s
tage: (Ready /\ setStage)
} = do
let tEdgesMap = SigmaxTypes.edgesGraphMap
props.
transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap
props.
transformedGraph
stageHooks props@{s
igmaRef, stage: (Ready /\ setStage), 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
props.sigmaRef) "[graphCpt
] no sigma" $ \sigma -> do
Sigmax.dependOnSigma (R.readRef
sigmaRef) "[graphCpt (Ready)
] no sigma" $ \sigma -> do
Sigmax.updateEdges sigma tEdgesMap
Sigmax.updateNodes sigma tNodesMap
...
...
src/Gargantext/Components/GraphExplorer.purs
View file @
9b8fde6e
...
...
@@ -6,18 +6,18 @@ import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Foldable (foldMap)
import Data.Int (toNumber)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Maybe (Maybe(..)
, fromJust
)
import Data.Nullable (null, Nullable)
import Data.Sequence as Seq
import Data.Set as Set
import Data.Tuple (fst, snd, Tuple(..))
import Data.Tuple.Nested ((/\))
-- import DOM.Simple.Console (log2)
import DOM.Simple.Types (Element)
import Effect.Aff (Aff)
import Math (log)
import Partial.Unsafe (unsafePartial)
import Reactix as R
import Reactix.DOM.HTML as RH
import Math (log)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.Sigmax as Sigmax
...
...
@@ -47,7 +47,7 @@ type LayoutProps =
)
type Props = (
graph ::
Maybe Graph.
Graph
graph ::
SigmaxTypes.S
Graph
, mMetaData :: Maybe GET.MetaData
| LayoutProps
)
...
...
@@ -64,7 +64,7 @@ explorerLayoutCpt = R.hooksComponent "G.C.GraphExplorer.explorerLayout" cpt
where
handler loaded =
explorer { graphId, mCurrentRoute, mMetaData
, session, sessions, graph
: Just graph
, frontends, showLogin}
, session, sessions, graph, frontends, showLogin}
where (Tuple mMetaData graph) = convert loaded
--------------------------------------------------------------
...
...
@@ -77,19 +77,19 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
cpt {frontends, graph, graphId, mCurrentRoute, mMetaData, session, sessions, showLogin} _ = do
dataRef <- R.useRef graph
graphRef <- R.useRef null
controls <- Controls.useGraphControls
controls <- Controls.useGraphControls graph
multiSelectEnabledRef <- R.useRef $ fst controls.multiSelectEnabled
R.useEffect' $ do
case Tuple (R.readRef dataRef) graph of
Tuple Nothing Nothing -> pure unit
Tuple (Just g1) (Just g2) | SigmaxTypes.eqGraph g1 g2 -> pure unit
_ -> do
let rSigma = R.readRef controls.sigmaRef
Sigmax.cleanupSigma rSigma "explorerCpt"
R.setRef dataRef graph
snd controls.selectedEdgeIds $ const Set.empty
snd controls.selectedNodeIds $ const Set.empty
snd controls.graphStage $ const Graph.Init
let readData = R.readRef dataRef
if SigmaxTypes.eqGraph readData graph then
pure unit
else do
let rSigma = R.readRef controls.sigmaRef
Sigmax.cleanupSigma rSigma "explorerCpt"
R.setRef dataRef graph
snd controls.selectedNodeIds $ const Set.empty
snd controls.graphStage $ const Graph.Init
pure $
RH.div
...
...
@@ -105,15 +105,12 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, row [ Controls.controls controls ]
, row [ tree (fst controls.showTree) {sessions, mCurrentRoute, frontends} (snd showLogin)
, RH.div { ref: graphRef, id: "graph-view", className: graphClassName controls, style: {height: "95%"} } [] -- graph container
, mGraph { controls
, elRef: graphRef
, graphId
, graph
, graphStage: controls.graphStage
, selectedEdgeIds: controls.selectedEdgeIds
, selectedNodeIds: controls.selectedNodeIds
, sigmaRef: controls.sigmaRef
}
, graphView { controls
, elRef: graphRef
, graphId
, graph
, multiSelectEnabledRef
}
, mSidebar graph mMetaData { frontends
, session
, selectedNodeIds: controls.selectedNodeIds
...
...
@@ -150,29 +147,15 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
RH.div {className: "col-md-2", style: {paddingTop: "60px"}}
[forest {sessions, route, frontends, showLogin}]
mGraph :: { controls :: Record Controls.Controls
, elRef :: R.Ref (Nullable Element)
, graphId :: GraphId
, graph :: Maybe Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, sigmaRef :: R.Ref Sigmax.Sigma
}
-> R.Element
mGraph {graph: Nothing} = RH.div {} []
mGraph r@{graph: Just graph} = graphView $ r { graph = graph }
mSidebar :: Maybe Graph.Graph
mSidebar :: SigmaxTypes.SGraph
-> Maybe GET.MetaData
-> { frontends :: Frontends
, showSidePanel :: GET.SidePanelState
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, session :: Session }
-> R.Element
mSidebar Nothing _ _ = RH.div {} []
mSidebar _ Nothing _ = RH.div {} []
mSidebar
(Just graph)
(Just metaData) {frontends, session, selectedNodeIds, showSidePanel} =
mSidebar
graph
(Just metaData) {frontends, session, selectedNodeIds, showSidePanel} =
Sidebar.sidebar { frontends
, graph
, metaData
...
...
@@ -185,36 +168,37 @@ type GraphProps = (
controls :: Record Controls.Controls
, elRef :: R.Ref (Nullable Element)
, graphId :: GraphId
, graph :: Graph.Graph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, sigmaRef :: R.Ref Sigmax.Sigma
, graph :: SigmaxTypes.SGraph
, multiSelectEnabledRef :: R.Ref Boolean
)
graphView :: Record GraphProps -> R.Element
--graphView sigmaRef props = R.createElement (R.memo el memoCmp) props []
graphView props = R.createElement el props []
graphView props = R.createElement graphViewCpt props []
graphViewCpt :: R.Component GraphProps
graphViewCpt = R.hooksComponent "GraphView" cpt
where
--memoCmp props1 props2 = props1.graphId == props2.graphId
el = R.hooksComponent "GraphView" cpt
cpt {controls, elRef, graphId, graph, selectedEdgeIds, selectedNodeIds, sigmaRef} _children = do
cpt {controls, elRef, graphId, graph, multiSelectEnabledRef} _children = do
-- TODO Cache this?
let transformedGraph = transformGraph controls graph
R.useEffect1' (fst controls.multiSelectEnabled) $ do
R.setRef multiSelectEnabledRef $ fst controls.multiSelectEnabled
pure $ Graph.graph {
elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings
, graph
,
selectedEdgeIds
, selectedNodeIds
, sigmaRef
,
multiSelectEnabledRef
, selectedNodeIds
: controls.selectedNodeIds
, sigmaRef
: controls.sigmaRef
, sigmaSettings: Graph.sigmaSettings
, stage:
prop
s.graphStage
, stage:
control
s.graphStage
, transformedGraph
}
convert :: GET.GraphData -> Tuple (Maybe GET.MetaData)
Graph.
Graph
convert :: GET.GraphData -> Tuple (Maybe GET.MetaData)
SigmaxTypes.S
Graph
convert (GET.GraphData r) = Tuple r.metaData $ SigmaxTypes.Graph {nodes, edges}
where
nodes = foldMapWithIndex nodeFn r.nodes
...
...
@@ -232,7 +216,14 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxTypes.Graph {nodes, edges}
cDef (GET.Cluster {clustDefault}) = clustDefault
nodesMap = SigmaxTypes.nodesMap nodes
edges = foldMap edgeFn r.edges
edgeFn (GET.Edge e) = Seq.singleton {id : e.id_, color, size: 1.0, source : e.source, target : e.target}
edgeFn (GET.Edge e) = Seq.singleton { id : e.id_
, color
, confluence : e.confluence
, hidden : false
, size: 1.0
, source : e.source
, target : e.target
, weight : e.weight }
where
color = case Map.lookup e.source nodesMap of
Nothing -> "#000000"
...
...
@@ -373,23 +364,47 @@ getNodes :: Session -> GraphId -> Aff GET.GraphData
getNodes session graphId = get session $ NodeAPI Graph (Just graphId) ""
transformGraph :: Record Controls.Controls ->
Graph.Graph -> Graph.
Graph
transformGraph controls graph
@(SigmaxTypes.Graph {nodes, edges})
= SigmaxTypes.Graph {nodes: newNodes, edges: newEdges}
transformGraph :: Record Controls.Controls ->
SigmaxTypes.SGraph -> SigmaxTypes.S
Graph
transformGraph controls graph = SigmaxTypes.Graph {nodes: newNodes, edges: newEdges}
where
edges = SigmaxTypes.graphEdges graph
nodes = SigmaxTypes.graphNodes graph
graphEdgesMap = SigmaxTypes.edgesGraphMap graph
graphNodesMap = SigmaxTypes.nodesGraphMap graph
newNodes = nodeSizes <$> nodeMarked <$> nodes
newEdges = edgeMarked <$> edges
nodeSizes node@{ size } =
selectedEdgeIds =
Set.fromFoldable
$ Seq.map _.id
$ Seq.filter (\e -> Set.member e.source (fst controls.selectedNodeIds)) edges
hasSelection = not $ Set.isEmpty (fst controls.selectedNodeIds)
newNodes = nodeSizeFilter <$> nodeMarked <$> nodes
newEdges = edgeConfluenceFilter <$> edgeWeightFilter <$> edgeMarked <$> edges
nodeSizeFilter node@{ size } =
if Range.within (fst controls.nodeSize) size then
node
else
node { hidden = true }
edgeMarked edge@{ id } =
if Set.member id (fst controls.selectedEdgeIds) then
edge { color = "#ff0000" }
edgeConfluenceFilter edge@{ confluence } =
if Range.within (fst controls.edgeConfluence) confluence then
edge
else
edge { hidden = true }
edgeWeightFilter edge@{ weight } =
if Range.within (fst controls.edgeWeight) weight then
edge
else
edge { hidden = true }
edgeMarked edge@{ id } = do
let isSelected = Set.member id selectedEdgeIds
let sourceNode = Map.lookup edge.source graphNodesMap
case Tuple hasSelection isSelected of
Tuple false true -> edge { color = "#ff0000" }
Tuple true true -> edge { color = (unsafePartial $ fromJust sourceNode).color }
Tuple true false -> edge { color = "#dddddd" }
_ -> edge
nodeMarked node@{ id } =
if Set.member id (fst controls.selectedNodeIds) then
node { color = "#ff0000" }
...
...
src/Gargantext/Components/GraphExplorer/Controls.purs
View file @
9b8fde6e
...
...
@@ -7,10 +7,11 @@ module Gargantext.Components.GraphExplorer.Controls
, getShowTree, setShowTree
, getShowControls, setShowControls
, getCursorSize, setCursorSize
, getMultiNodeSelect, setMultiNodeSelect
) where
import Data.Maybe (Maybe(..))
import Data.Array as A
import Data.Maybe (Maybe(..), maybe)
import Data.Sequence as Seq
import Data.Set as Set
import Data.Tuple (fst, snd)
import Data.Tuple.Nested ((/\), get1)
...
...
@@ -22,9 +23,10 @@ import Reactix.DOM.HTML as RH
import Gargantext.Components.Graph as Graph
import Gargantext.Components.GraphExplorer.Button (centerButton)
import Gargantext.Components.GraphExplorer.RangeControl (edgeSizeControl, nodeSizeControl)
import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.SlideButton (cursorSizeButton, labelSizeButton)
import Gargantext.Components.GraphExplorer.ToggleButton (edgesToggleButton, pauseForceAtlasButton)
import Gargantext.Components.GraphExplorer.ToggleButton (
multiSelectEnabledButton,
edgesToggleButton, pauseForceAtlasButton)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
...
...
@@ -33,11 +35,13 @@ import Gargantext.Utils.Reactix as R2
type Controls =
( cursorSize :: R.State Number
, edgeConfluence :: R.State Range.NumberRange
, edgeWeight :: R.State Range.NumberRange
, graph :: SigmaxTypes.SGraph
, graphStage :: R.State Graph.Stage
, multi
NodeSelect :: R.Ref
Boolean
, multi
SelectEnabled :: R.State
Boolean
, nodeSize :: R.State Range.NumberRange
, selectedEdgeIds :: R.State (Set.Set String)
, selectedNodeIds :: R.State (Set.Set String)
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, showControls :: R.State Boolean
, showSidePanel :: R.State GET.SidePanelState
, showTree :: R.State Boolean
...
...
@@ -48,23 +52,21 @@ controlsToSigmaSettings :: Record Controls -> Record Graph.SigmaSettings
controlsToSigmaSettings { cursorSize: (cursorSize /\ _)} = Graph.sigmaSettings
type LocalControls =
( edgeSize :: R.State Range.NumberRange
, labelSize :: R.State Number
( labelSize :: R.State Number
, pauseForceAtlas :: R.State Boolean
, showEdges :: R.State Boolean
)
initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
edgeSize <- R.useState' $ Range.Closed { min: 0.5, max: 1.0 }
labelSize <- R.useState' 14.0
--nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 10.0 }
pauseForceAtlas <- R.useState' true
search <- R.useState' ""
showEdges <- R.useState' true
pure $ {
edgeSize
, labelSize
labelSize
--, nodeSize
, pauseForceAtlas
, showEdges
...
...
@@ -107,6 +109,11 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
R.setRef mFAPauseRef $ Just timeoutId
pure unit
let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxTypes.graphNodes props.graph
let nodeSizeMin = maybe 0.0 _.size $ A.head nodesSorted
let nodeSizeMax = maybe 100.0 _.size $ A.last nodesSorted
let nodeSizeRange = Range.Closed { min: nodeSizeMin, max: nodeSizeMax }
pure $ case getShowControls props of
false -> RH.div {} []
true -> RH.div { className: "col-md-12", style: { paddingBottom: "10px" } }
...
...
@@ -116,7 +123,8 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
RH.li {} [ centerButton props.sigmaRef ]
, RH.li {} [ pauseForceAtlasButton props.sigmaRef localControls.pauseForceAtlas ] -- spatialization (pause ForceAtlas2)
, RH.li {} [ edgesToggleButton props.sigmaRef localControls.showEdges ]
, RH.li {} [ edgeSizeControl props.sigmaRef localControls.edgeSize ] -- edge size : 0-3
, RH.li {} [ edgeConfluenceControl props.sigmaRef props.edgeConfluence ]
, RH.li {} [ edgeWeightControl props.sigmaRef props.edgeWeight ]
-- change level
-- file upload
-- run demo
...
...
@@ -124,33 +132,40 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
-- search topics
, RH.li {} [ cursorSizeButton props.cursorSize ] -- cursor size: 0-100
, RH.li {} [ labelSizeButton props.sigmaRef localControls.labelSize ] -- labels size: 1-4
, RH.li {} [ nodeSizeControl props.nodeSize ]
, RH.li {} [ nodeSizeControl
nodeSizeRange
props.nodeSize ]
-- zoom: 0 -100 - calculate ratio
-- toggle multi node selection
, RH.li {} [ multiSelectEnabledButton props.multiSelectEnabled ]
-- toggle multi node selection
-- save button
, RH.li {} [ nodeSearchControl { selectedNodeIds: props.selectedNodeIds } ]
]
]
]
useGraphControls :: R.Hooks (Record Controls)
useGraphControls = do
useGraphControls :: SigmaxTypes.SGraph -> R.Hooks (Record Controls)
useGraphControls graph = do
let edges = SigmaxTypes.graphEdges graph
let nodes = SigmaxTypes.graphNodes graph
cursorSize <- R.useState' 10.0
edgeConfluence <- R.useState' $ Range.Closed { min: 0.0, max: 1.0 }
edgeWeight <- R.useState' $ Range.Closed { min: 0.0, max: 1.0 }
graphStage <- R.useState' Graph.Init
multi
NodeSelect <- R.useRef
false
nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 10.0 }
multi
SelectEnabled <- R.useState'
false
nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 10
0
.0 }
showTree <- R.useState' false
selectedNodeIds <- R.useState' $ Set.empty
selectedEdgeIds <- R.useState' $ Set.empty
showControls <- R.useState' false
showSidePanel <- R.useState' GET.InitialClosed
sigma <- Sigmax.initSigma
sigmaRef <- R.useRef sigma
pure { cursorSize
, edgeConfluence
, edgeWeight
, graph
, graphStage
, multi
NodeSelect
, multi
SelectEnabled
, nodeSize
, selectedEdgeIds
, selectedNodeIds
, showControls
, showSidePanel
...
...
@@ -167,9 +182,6 @@ getShowTree { showTree: ( should /\ _ ) } = should
getCursorSize :: Record Controls -> Number
getCursorSize { cursorSize: ( size /\ _ ) } = size
getMultiNodeSelect :: Record Controls -> Boolean
getMultiNodeSelect { multiNodeSelect } = R.readRef multiNodeSelect
setShowControls :: Record Controls -> Boolean -> Effect Unit
setShowControls { showControls: ( _ /\ set ) } v = set $ const v
...
...
@@ -178,6 +190,3 @@ setShowTree { showTree: ( _ /\ set ) } v = set $ not <<< const v
setCursorSize :: Record Controls -> Number -> Effect Unit
setCursorSize { cursorSize: ( _ /\ setSize ) } v = setSize $ const v
setMultiNodeSelect :: Record Controls -> Boolean -> Effect Unit
setMultiNodeSelect { multiNodeSelect } = R.setRef multiNodeSelect
src/Gargantext/Components/GraphExplorer/RangeControl.purs
View file @
9b8fde6e
module Gargantext.Components.GraphExplorer.RangeControl
( Props
, rangeControl
, edgeSizeControl
, edgeConfluenceControl
, edgeWeightControl
, nodeSizeControl
) where
...
...
@@ -33,34 +34,50 @@ rangeControlCpt = R.hooksComponent "RangeButton" cpt
, RS.rangeSlider sliderProps
]
edge
Siz
eControl :: R.Ref Sigmax.Sigma -> R.State Range.NumberRange -> R.Element
edge
Siz
eControl sigmaRef (state /\ setState) =
edge
Confluenc
eControl :: R.Ref Sigmax.Sigma -> R.State Range.NumberRange -> R.Element
edge
Confluenc
eControl sigmaRef (state /\ setState) =
rangeControl {
caption: "Edge Size"
caption: "Edge Confluence Weight"
, sliderProps: {
bounds: Range.Closed { min: 0.0, max: 1.0 }
, initialValue: state
, epsilon: 0.01
, step: 1.0
, width: 10.0
, height: 5.0
, onChange: setState <<< const
}
}
edgeWeightControl :: R.Ref Sigmax.Sigma -> R.State Range.NumberRange -> R.Element
edgeWeightControl sigmaRef (state /\ setState) =
rangeControl {
caption: "Edge Weight"
, sliderProps: {
bounds: Range.Closed { min: 0.0, max: 3.0 }
, initialValue: state
, epsilon: 0.1
, epsilon: 0.
0
1
, step: 1.0
, width: 10.0
, height: 5.0
, onChange: \range@(Range.Closed {min, max}) -> do
let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[edgeSizeControl] sigma: Nothing" $ \s -> do
Sigma.setSettings s {
minEdgeSize: min
, maxEdgeSize: max
}
setState $ const range
, onChange: setState <<< const
-- , onChange: \range@(Range.Closed {min, max}) -> do
-- let sigma = R.readRef sigmaRef
-- Sigmax.dependOnSigma sigma "[edgeWeightControl] sigma: Nothing" $ \s -> do
-- Sigma.setSettings s {
-- minEdgeSize: min
-- , maxEdgeSize: max
-- }
-- setState $ const range
}
}
nodeSizeControl :: R.State Range.NumberRange -> R.Element
nodeSizeControl (state /\ setState) =
nodeSizeControl :: R
ange.NumberRange -> R
.State Range.NumberRange -> R.Element
nodeSizeControl (
Range.Closed { min: rangeMin, max: rangeMax }) (
state /\ setState) =
rangeControl {
caption: "Node Size"
, sliderProps: {
bounds: Range.Closed { min:
0.0, max: 15.0
}
bounds: Range.Closed { min:
rangeMin, max: rangeMax
}
, initialValue: state
, epsilon: 0.1
, step: 1.0
...
...
src/Gargantext/Components/GraphExplorer/Search.purs
0 → 100644
View file @
9b8fde6e
module Gargantext.Components.GraphExplorer.Search
( Props
, nodeSearchControl
) where
import Global (readFloat)
import Prelude
import Data.Set as Set
import Data.Tuple (snd)
import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log2)
import Effect (Effect)
import FFI.Simple ((..))
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Utils.Reactix as R2
type Props = (
selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
)
nodeSearchControl :: Record Props -> R.Element
nodeSearchControl props = R.createElement sizeButtonCpt props []
sizeButtonCpt :: R.Component Props
sizeButtonCpt = R.hooksComponent "NodeSearchControl" cpt
where
cpt {selectedNodeIds} _ = do
(search /\ setSearch) <- R.useState' ""
pure $
H.div { className: "form-group" }
[ H.div { className: "input-group" }
[ H.input { type: "text"
, className: "form-control"
, defaultValue: search
, on: { input: \e -> setSearch $ const $ e .. "target" .. "value" }
}
, H.div { className: "btn input-group-addon"
, on: { click: \_ -> log2 "[sizeButtonCpt] search" search }
}
[ H.span { className: "fa fa-search" } [] ]
]
]
-- TODO Wherefrom do I get graph nodes?
-- How to implement filtering here? I want to set selectedNodeIds based on graph data.
src/Gargantext/Components/GraphExplorer/Sidebar.purs
View file @
9b8fde6e
...
...
@@ -14,7 +14,6 @@ import Reactix.DOM.HTML as RH
import Gargantext.Data.Array (catMaybes)
import Gargantext.Components.RandomText (words)
import Gargantext.Components.Nodes.Corpus.Graph.Tabs as GT
import Gargantext.Components.Graph as Graph
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
...
...
@@ -22,7 +21,7 @@ import Gargantext.Sessions (Session)
type Props =
( frontends :: Frontends
, graph ::
Graph.
Graph
, graph ::
SigmaxTypes.S
Graph
, metaData :: GET.MetaData
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, session :: Session
...
...
src/Gargantext/Components/GraphExplorer/ToggleButton.purs
View file @
9b8fde6e
module Gargantext.Components.GraphExplorer.ToggleButton
( Props, toggleButton, toggleButtonCpt
( Props
, toggleButton
, toggleButtonCpt
, multiSelectEnabledButton
, controlsToggleButton
, edgesToggleButton
, sidebarToggleButton
...
...
@@ -66,6 +69,15 @@ edgesToggleButton sigmaRef state =
setToggled not
}
multiSelectEnabledButton :: R.State Boolean -> R.Element
multiSelectEnabledButton state =
toggleButton {
state: state
, onMessage: "Single-node"
, offMessage: "Multi-node"
, onClick: \_ -> snd state not
}
pauseForceAtlasButton :: R.Ref Sigmax.Sigma -> R.State Boolean -> R.Element
pauseForceAtlasButton sigmaRef state =
toggleButton {
...
...
src/Gargantext/Components/GraphExplorer/Types.purs
View file @
9b8fde6e
...
...
@@ -88,7 +88,7 @@ type State = (
--, showSidePanel :: R.State Boolean
--, showControls :: R.State Boolean
--, showTree :: R.State Boolean
--, sigmaGraphData :: R.State (Maybe
Graph.
Graph)
--, sigmaGraphData :: R.State (Maybe
SigmaxTypes.S
Graph)
--, sigmaSettings :: R.State ({|Graph.SigmaSettings})
--treeId :: R.State (Maybe TreeId)
)
...
...
src/Gargantext/Hooks/Sigmax.purs
View file @
9b8fde6e
module Gargantext.Hooks.Sigmax
where
import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (>>=), (||), not, const, map)
import Data.Array as A
import Data.Either (either)
import Data.Foldable (sequence_)
import Data.Foldable (sequence_
, foldl
)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable)
...
...
@@ -20,9 +22,8 @@ import Effect.Class.Console (error)
import Effect.Timer (TimeoutId, clearTimeout)
import FFI.Simple ((.=))
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types (Graph(..),
EdgesMap, NodesMap, SelectedNodeIds, SelectedEdgeId
s)
import Gargantext.Hooks.Sigmax.Types (Graph(..),
SGraph, EdgesMap, NodesMap, SelectedNodeIds, SelectedEdgeIds, graphEdge
s)
import Gargantext.Utils.Reactix as R2
import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (>>=), not)
import Reactix as R
type Sigma =
...
...
@@ -190,8 +191,9 @@ updateEdges sigma edgesMap = do
let mTEdge = Map.lookup e.id edgesMap
case mTEdge of
Nothing -> error $ "Edge id " <> e.id <> " not found in edgesMap"
(Just
tEdge@{color: tColor
}) -> do
(Just
{color: tColor, hidden: tHidden
}) -> do
_ <- pure $ (e .= "color") tColor
_ <- pure $ (e .= "hidden") tHidden
pure unit
Sigma.refresh sigma
...
...
@@ -202,28 +204,38 @@ updateNodes sigma nodesMap = do
let mTNode = Map.lookup n.id nodesMap
case mTNode of
Nothing -> error $ "Node id " <> n.id <> " not found in nodesMap"
(Just
tNode@
{color: tColor, hidden: tHidden}) -> do
(Just {color: tColor, hidden: tHidden}) -> do
_ <- pure $ (n .= "color") tColor
_ <- pure $ (n .= "hidden") tHidden
pure unit
Sigma.refresh sigma
bindSelectedNodesClick :: R.Ref Sigma -> R.State SelectedNodeIds -> Effect Unit
bindSelectedNodesClick sigmaRef (_ /\ setSelectedNodeIds) =
dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
Sigma.bindClickNode sigma $ \node -> do
setSelectedNodeIds \nids ->
if Set.member node.id nids then
Set.delete node.id nids
else
Set.insert node.id nids
-- | Toggles item visibility in the selected set
multiSelectUpdate :: SelectedNodeIds -> SelectedNodeIds -> SelectedNodeIds
multiSelectUpdate new selected = foldl fld selected new
where
fld selectedAcc item =
if Set.member item selectedAcc then
Set.delete item selectedAcc
else
Set.insert item selectedAcc
bindSelectedNodesClick :: Sigma.Sigma -> R.State SelectedNodeIds -> R.Ref Boolean -> Effect Unit
bindSelectedNodesClick sigma (_ /\ setSelectedNodeIds) multiSelectEnabledRef =
Sigma.bindClickNodes sigma $ \nodes -> do
let multiSelectEnabled = R.readRef multiSelectEnabledRef
let nodeIds = Set.fromFoldable $ map _.id nodes
if multiSelectEnabled then
setSelectedNodeIds $ multiSelectUpdate nodeIds
else
setSelectedNodeIds $ const nodeIds
bindSelectedEdgesClick :: R.Ref Sigma -> R.State SelectedEdgeIds -> Effect Unit
bindSelectedEdgesClick sigmaRef (_ /\ setSelectedEdgeIds) =
dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
Sigma.bindClickEdge sigma $ \edge -> do
log2 "[bindClickEdge] edge" edge
setSelectedEdgeIds \eids ->
if Set.member edge.id eids then
Set.delete edge.id eids
...
...
src/Gargantext/Hooks/Sigmax/Sigma.purs
View file @
9b8fde6e
...
...
@@ -142,6 +142,14 @@ bindClickNode s f = bind_ s "clickNode" $ \e -> do
unbindClickNode :: Sigma -> Effect Unit
unbindClickNode s = unbind_ s "clickNode"
bindClickNodes :: Sigma -> (Array (Record Types.Node) -> Effect Unit) -> Effect Unit
bindClickNodes s f = bind_ s "clickNodes" $ \e -> do
let nodes = e .. "data" .. "node" :: Array (Record Types.Node)
f nodes
unbindClickNodes :: Sigma -> Effect Unit
unbindClickNodes s = unbind_ s "clickNodes"
bindOverNode :: Sigma -> (Record Types.Node -> Effect Unit) -> Effect Unit
bindOverNode s f = bind_ s "overNode" $ \e -> do
let node = e .. "data" .. "node" :: Record Types.Node
...
...
src/Gargantext/Hooks/Sigmax/Types.purs
View file @
9b8fde6e
module Gargantext.Hooks.Sigmax.Types where
import Prelude (map, ($), (&&), (==))
import Prelude (map, ($), (&&), (==)
, class Ord, Ordering, compare
)
import Data.Map as Map
import Data.Sequence (Seq)
import Data.Set as Set
...
...
@@ -30,27 +30,36 @@ type Node =
type Edge =
( id :: String
, color :: String
, confluence :: Number
, hidden :: Boolean
, size :: Number
, source :: String
, target :: String )
, target :: String
, weight :: Number )
type SelectedNodeIds = Set.Set String
type SelectedEdgeIds = Set.Set String
type EdgesMap = Map.Map String (Record Edge)
type NodesMap = Map.Map String (Record Node)
type SGraph = Graph Node Edge
graphEdges :: SGraph -> Seq (Record Edge)
graphEdges (Graph {edges}) = edges
graphNodes :: SGraph -> Seq (Record Node)
graphNodes (Graph {nodes}) = nodes
edgesGraphMap :: Graph Node Edge -> EdgesMap
edgesGraphMap graph = do
let (Graph {edges}) = graph
Map.fromFoldable $ map (\e -> Tuple e.id e) edges
edgesGraphMap graph =
Map.fromFoldable $ map (\e -> Tuple e.id e) $ graphEdges graph
nodesMap :: Seq (Record Node) -> NodesMap
nodesMap nodes = Map.fromFoldable $ map (\n -> Tuple n.id n) nodes
nodesGraphMap :: Graph Node Edge -> NodesMap
nodesGraphMap graph = do
let (Graph {nodes}) = graph
nodesMap nodes
nodesGraphMap graph =
nodesMap $ graphNodes graph
eqGraph :: (Graph Node Edge) -> (Graph Node Edge) -> Boolean
eqGraph (Graph {nodes: n1, edges: e1}) (Graph {nodes: n2, edges: e2}) = (n1 == n2) && (e1 == e2)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment