Commit 85a9a63a authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[Graph] more multiselect work

parent 5d91a271
......@@ -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;
}
......
.range {
width: 400px;
/* some space for the right knob */
padding-right: 30px;
}
.range .range-slider {
position: relative;
......
.range
width: 400px
/* some space for the right knob */
padding-right: 30px
.range-slider
position: relative
......
......@@ -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@{stage: (Ready /\ setStage)} = do
let tEdgesMap = SigmaxTypes.edgesGraphMap props.transformedGraph
let tNodesMap = SigmaxTypes.nodesGraphMap props.transformedGraph
stageHooks props@{sigmaRef, 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
......
......@@ -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: props.graphStage
, stage: controls.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" }
......
......@@ -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
, multiNodeSelect :: R.Ref Boolean
, multiSelectEnabled :: 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
multiNodeSelect <- R.useRef false
multiSelectEnabled <- R.useState' true
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
, multiNodeSelect
, multiSelectEnabled
, 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
......@@ -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?
......
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 {
......
......@@ -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) =
......
......@@ -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
......
......@@ -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)
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