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
85a9a63a
Commit
85a9a63a
authored
Dec 11, 2019
by
Przemyslaw Kaminski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Graph] more multiselect work
parent
5d91a271
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
110 additions
and
83 deletions
+110
-83
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
+16
-14
GraphExplorer.purs
src/Gargantext/Components/GraphExplorer.purs
+26
-17
Controls.purs
src/Gargantext/Components/GraphExplorer/Controls.purs
+5
-15
Search.purs
src/Gargantext/Components/GraphExplorer/Search.purs
+11
-7
ToggleButton.purs
src/Gargantext/Components/GraphExplorer/ToggleButton.purs
+13
-1
Sigmax.purs
src/Gargantext/Hooks/Sigmax.purs
+23
-11
Sigma.purs
src/Gargantext/Hooks/Sigmax/Sigma.purs
+3
-0
Types.purs
src/Gargantext/Hooks/Sigmax/Types.purs
+4
-6
No files found.
dist/styles/Login.css
View file @
85a9a63a
...
...
@@ -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 @
85a9a63a
.range
{
width
:
400px
;
/* some space for the right knob */
padding-right
:
30px
;
}
.range
.range-slider
{
position
:
relative
;
...
...
dist/styles/range-slider.sass
View file @
85a9a63a
.range
width
:
400px
/* some space for the right knob */
padding-right
:
30px
.range-slider
position
:
relative
...
...
src/Gargantext/Components/Graph.purs
View file @
85a9a63a
...
...
@@ -4,10 +4,13 @@ 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)
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log, log2)
import DOM.Simple.Types (Element)
...
...
@@ -27,7 +30,7 @@ type Props sigma forceatlas2 =
( elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2
, graph :: SigmaxTypes.SGraph
,
selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
,
multiSelectEnabledRef :: R.Ref Boolean
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma
...
...
@@ -51,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
...
...
@@ -64,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
...
...
@@ -73,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 props.selectedEdgeIds props.graph
--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 @
85a9a63a
...
...
@@ -2,6 +2,7 @@ module Gargantext.Components.GraphExplorer where
import Gargantext.Prelude hiding (max,min)
import Data.Array as A
import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Foldable (foldMap)
import Data.Int (toNumber)
...
...
@@ -12,7 +13,7 @@ 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.Console (log2)
import DOM.Simple.Types (Element)
import Effect.Aff (Aff)
import Math (log)
...
...
@@ -88,7 +89,6 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
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
...
...
@@ -110,10 +110,6 @@ explorerCpt = R.hooksComponent "G.C.GraphExplorer.explorer" cpt
, elRef: graphRef
, graphId
, graph
, graphStage: controls.graphStage
, selectedEdgeIds: controls.selectedEdgeIds
, selectedNodeIds: controls.selectedNodeIds
, sigmaRef: controls.sigmaRef
}
, mSidebar graph mMetaData { frontends
, session
...
...
@@ -173,10 +169,6 @@ type GraphProps = (
, elRef :: R.Ref (Nullable Element)
, graphId :: GraphId
, graph :: SigmaxTypes.SGraph
, graphStage :: R.State Graph.Stage
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, selectedEdgeIds :: R.State SigmaxTypes.SelectedEdgeIds
, sigmaRef :: R.Ref Sigmax.Sigma
)
graphView :: Record GraphProps -> R.Element
...
...
@@ -185,19 +177,30 @@ graphView props = R.createElement el props []
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} _children = do
-- TODO Cache this?
let transformedGraph = transformGraph controls graph
multiSelectEnabledRef <- R.useRef $ fst controls.multiSelectEnabled
R.useEffect' $ do
let nodeColor {id, color} = [id, color]
let onlySelected g = Seq.filter (\n -> Set.member n.id (fst controls.selectedNodeIds)) $ SigmaxTypes.graphNodes g
log2 "[graphView] selectedNodeIds" $ A.fromFoldable $ fst controls.selectedNodeIds
log2 "[graphView] transformedGraph.nodes" $ A.fromFoldable $ map nodeColor $ onlySelected transformedGraph
log2 "[graphView] graph.nodes" $ A.fromFoldable $ map nodeColor $ onlySelected graph
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
}
...
...
@@ -367,16 +370,22 @@ transformGraph controls graph = SigmaxTypes.Graph {nodes: newNodes, edges: newEd
nodes = SigmaxTypes.graphNodes graph
graphEdgesMap = SigmaxTypes.edgesGraphMap graph
graphNodesMap = SigmaxTypes.nodesGraphMap graph
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 = nodeSizes <$> nodeMarked <$> nodes
newEdges = edgeMarked <$> edges
hasSelection = not $ Set.isEmpty (fst controls.selectedNodeIds)
nodeSizes node@{ size } =
if Range.within (fst controls.nodeSize) size then
node
else
node { hidden = true }
edgeMarked edge@{ id } = do
let isSelected = Set.member id
(fst controls.selectedEdgeIds)
let isSelected = Set.member id
selectedEdgeIds
let sourceNode = Map.lookup edge.source graphNodesMap
case Tuple hasSelection isSelected of
Tuple false true -> edge { color = "#ff0000" }
...
...
src/Gargantext/Components/GraphExplorer/Controls.purs
View file @
85a9a63a
...
...
@@ -7,7 +7,6 @@ module Gargantext.Components.GraphExplorer.Controls
, getShowTree, setShowTree
, getShowControls, setShowControls
, getCursorSize, setCursorSize
, getMultiNodeSelect, setMultiNodeSelect
) where
import Data.Array as A
...
...
@@ -27,7 +26,7 @@ import Gargantext.Components.GraphExplorer.Button (centerButton)
import Gargantext.Components.GraphExplorer.RangeControl (edgeSizeControl, 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
...
...
@@ -38,9 +37,8 @@ type Controls =
( cursorSize :: R.State Number
, 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 SigmaxTypes.SelectedEdgeIds
, selectedNodeIds :: R.State SigmaxTypes.SelectedNodeIds
, showControls :: R.State Boolean
, showSidePanel :: R.State GET.SidePanelState
...
...
@@ -136,7 +134,7 @@ controlsCpt = R.hooksComponent "GraphControls" cpt
, RH.li {} [ labelSizeButton props.sigmaRef localControls.labelSize ] -- labels size: 1-4
, 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 } ]
]
...
...
@@ -150,11 +148,10 @@ useGraphControls graph = do
cursorSize <- R.useState' 10.0
graphStage <- R.useState' Graph.Init
multi
NodeSelect <- R.useRef fals
e
multi
SelectEnabled <- R.useState' tru
e
nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 100.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
...
...
@@ -163,9 +160,8 @@ useGraphControls graph = do
pure { cursorSize
, graph
, graphStage
, multi
NodeSelect
, multi
SelectEnabled
, nodeSize
, selectedEdgeIds
, selectedNodeIds
, showControls
, showSidePanel
...
...
@@ -182,9 +178,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
...
...
@@ -193,6 +186,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/Search.purs
View file @
85a9a63a
...
...
@@ -33,14 +33,18 @@ sizeButtonCpt = R.hooksComponent "NodeSearchControl" cpt
(search /\ setSearch) <- R.useState' ""
pure $
H.span {}
[ H.input { type: "text"
, className: "form-control"
, defaultValue: search
, on: { input: \e -> setSearch $ const $ e .. "target" .. "value" }
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.button { className: "btn btn-primary"
, on: { click: \_ -> log2 "[sizeButtonCpt] search" search }} [ H.text "Search"
]
[ H.span { className: "fa fa-search" } [] ]
]
]
-- TODO Wherefrom do I get graph nodes?
...
...
src/Gargantext/Components/GraphExplorer/ToggleButton.purs
View file @
85a9a63a
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/Hooks/Sigmax.purs
View file @
85a9a63a
...
...
@@ -5,7 +5,7 @@ import Prelude (Unit, bind, discard, flip, pure, unit, ($), (*>), (<<<), (<>), (
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)
...
...
@@ -22,7 +22,7 @@ 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(..), SGraph, EdgesMap, NodesMap, SelectedNodeIds, SelectedEdgeIds)
import Gargantext.Hooks.Sigmax.Types (Graph(..), SGraph, EdgesMap, NodesMap, SelectedNodeIds, SelectedEdgeIds
, graphEdges
)
import Gargantext.Utils.Reactix as R2
import Reactix as R
...
...
@@ -210,16 +210,28 @@ updateNodes sigma nodesMap = do
Sigma.refresh sigma
bindSelectedNodesClick :: R.Ref Sigma -> R.State SelectedNodeIds -> R.State SelectedEdgeIds -> SGraph -> Effect Unit
bindSelectedNodesClick sigmaRef (_ /\ setSelectedNodeIds) (_ /\ setSelectedEdgeIds) (Graph {edges, nodes}) =
dependOnSigma (R.readRef sigmaRef) "[graphCpt] no sigma" $ \sigma -> do
Sigma.bindClickNodes sigma $ \nodes -> do
let nodeIds = Set.fromFoldable $ map _.id nodes
-- | 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
log2 "[bindSelectedNodesClick] nodes" nodes
let multiSelectEnabled = R.readRef multiSelectEnabledRef
log2 "[bindSelectedNodesClick] multiSelectEnabled" multiSelectEnabled
let nodeIds = Set.fromFoldable $ map _.id nodes
if multiSelectEnabled then
setSelectedNodeIds $ multiSelectUpdate nodeIds
else
setSelectedNodeIds $ const nodeIds
setSelectedEdgeIds \_ ->
Set.fromFoldable
$ Seq.map _.id
$ Seq.filter (\e -> Set.member e.source nodeIds) edges
bindSelectedEdgesClick :: R.Ref Sigma -> R.State SelectedEdgeIds -> Effect Unit
bindSelectedEdgesClick sigmaRef (_ /\ setSelectedEdgeIds) =
...
...
src/Gargantext/Hooks/Sigmax/Sigma.purs
View file @
85a9a63a
...
...
@@ -147,6 +147,9 @@ 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 @
85a9a63a
...
...
@@ -48,17 +48,15 @@ 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