Controls.purs 8.56 KB
Newer Older
James Laver's avatar
James Laver committed
1
module Gargantext.Components.GraphExplorer.Controls
2 3 4 5
 ( Controls
 , useGraphControls
 , controls
 , controlsCpt
James Laver's avatar
James Laver committed
6 7 8 9
 , getShowTree, setShowTree
 , getShowControls, setShowControls
 ) where

10
import Data.Array as A
11
import Data.Int as I
12 13
import Data.Maybe (Maybe(..), maybe)
import Data.Sequence as Seq
14 15
import Data.Set as Set
import Data.Tuple (fst, snd)
16
import Data.Tuple.Nested ((/\))
17
import Effect (Effect)
18
import Effect.Timer (setTimeout)
19
import Prelude
James Laver's avatar
James Laver committed
20 21 22
import Reactix as R
import Reactix.DOM.HTML as RH

23
import Gargantext.Components.Graph as Graph
24
import Gargantext.Components.GraphExplorer.Button (centerButton)
25
import Gargantext.Components.GraphExplorer.RangeControl (edgeConfluenceControl, edgeWeightControl, nodeSizeControl)
26
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
27
import Gargantext.Components.GraphExplorer.SlideButton (labelSizeButton, mouseSelectorSizeButton)
28
import Gargantext.Components.GraphExplorer.ToggleButton (multiSelectEnabledButton, edgesToggleButton, louvainToggleButton, pauseForceAtlasButton)
29
import Gargantext.Components.GraphExplorer.Types as GET
30
import Gargantext.Hooks.Sigmax as Sigmax
31
import Gargantext.Hooks.Sigmax.Types as SigmaxT
32
import Gargantext.Utils.Range as Range
33 34
import Gargantext.Utils.Reactix as R2

James Laver's avatar
James Laver committed
35
type Controls =
36
  ( edgeConfluence :: R.State Range.NumberRange
37
  , edgeWeight :: R.State Range.NumberRange
38 39
  , forceAtlasState :: R.State SigmaxT.ForceAtlasState
  , graph           :: SigmaxT.SGraph
40
  , graphStage      :: R.State Graph.Stage
41
  , multiSelectEnabled :: R.State Boolean
42
  , nodeSize        :: R.State Range.NumberRange
43 44
  , removedNodeIds  :: R.State SigmaxT.NodeIds
  , selectedNodeIds :: R.State SigmaxT.NodeIds
45
  , showControls    :: R.State Boolean
46
  , showEdges       :: R.State SigmaxT.ShowEdgesState
47
  , showLouvain     :: R.State Boolean
48
  , showSidePanel   :: R.State GET.SidePanelState
49
  , showTree        :: R.State Boolean
50
  , sigmaRef        :: R.Ref Sigmax.Sigma
James Laver's avatar
James Laver committed
51 52
  )

53
type LocalControls =
54
  ( labelSize :: R.State Number
55
  , mouseSelectorSize :: R.State Number
56 57 58 59
  )

initialLocalControls :: R.Hooks (Record LocalControls)
initialLocalControls = do
60
  labelSize <- R.useState' 14.0
61
  mouseSelectorSize <- R.useState' 15.0
62 63

  pure $ {
64
    labelSize
65
  , mouseSelectorSize
66 67
  }

68
controls :: Record Controls -> R.Element
James Laver's avatar
James Laver committed
69 70 71 72 73
controls props = R.createElement controlsCpt props []

controlsCpt :: R.Component Controls
controlsCpt = R.hooksComponent "GraphControls" cpt
  where
74 75
    cpt props _ = do
      localControls <- initialLocalControls
76 77
      -- ref to track automatic FA pausing
      -- If user pauses FA before auto is triggered, clear the timeoutId
78
      mFAPauseRef <- R.useRef Nothing
79

80 81
      -- When graph is changed, cleanup the mFAPauseRef so that forceAtlas
      -- timeout is retriggered.
82 83 84 85 86
      R.useEffect' $ do
        case fst props.graphStage of
          Graph.Init -> R.setRef mFAPauseRef Nothing
          _          -> pure unit

87 88
      -- Handle case when FA is paused from outside events, eg. the automatic timer.
      R.useEffect' $ Sigmax.handleForceAtlas2Pause props.sigmaRef props.forceAtlasState mFAPauseRef
89

90 91
      -- Handle automatic edge hiding when FA is running (to prevent flickering).
      R.useEffect2' props.sigmaRef props.forceAtlasState $
92
        snd props.showEdges $ SigmaxT.forceAtlasEdgeState (fst props.forceAtlasState)
93 94

      -- Automatic opening of sidebar when a node is selected (but only first time).
95 96 97 98 99 100
      R.useEffect' $ do
        if fst props.showSidePanel == GET.InitialClosed && (not Set.isEmpty $ fst props.selectedNodeIds) then
          snd props.showSidePanel $ \_ -> GET.Opened
        else
          pure unit

101 102
      -- Timer to turn off the initial FA. This is because FA eats up lot of
      -- CPU, has memory leaks etc.
103
      R.useEffect1' (fst props.forceAtlasState) $ do
104
        if (fst props.forceAtlasState) == SigmaxT.InitialRunning then do
105 106 107
          timeoutId <- setTimeout 2000 $ do
            let (toggled /\ setToggled) = props.forceAtlasState
            case toggled of
108
              SigmaxT.InitialRunning -> setToggled $ const SigmaxT.Paused
109 110 111 112 113 114
              _ -> pure unit
            R.setRef mFAPauseRef Nothing
          R.setRef mFAPauseRef $ Just timeoutId
          pure unit
         else
           pure unit
115

116
      let edgesConfluenceSorted = A.sortWith (_.confluence) $ Seq.toUnfoldable $ SigmaxT.graphEdges props.graph
117 118 119 120
      let edgeConfluenceMin = maybe 0.0 _.confluence $ A.head edgesConfluenceSorted
      let edgeConfluenceMax = maybe 100.0 _.confluence $ A.last edgesConfluenceSorted
      let edgeConfluenceRange = Range.Closed { min: edgeConfluenceMin, max: edgeConfluenceMax }

121
      --let edgesWeightSorted = A.sortWith (_.weight) $ Seq.toUnfoldable $ SigmaxT.graphEdges props.graph
122 123 124 125 126
      --let edgeWeightMin = maybe 0.0 _.weight $ A.head edgesWeightSorted
      --let edgeWeightMax = maybe 100.0 _.weight $ A.last edgesWeightSorted
      --let edgeWeightRange = Range.Closed { min: edgeWeightMin, max: edgeWeightMax }
      let edgeWeightRange = Range.Closed {
           min: 0.0
127
         , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges props.graph
128
         }
129

130
      let nodesSorted = A.sortWith (_.size) $ Seq.toUnfoldable $ SigmaxT.graphNodes props.graph
131 132 133 134
      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 }

135 136 137
      pure $ case getShowControls props of
        false -> RH.div {} []
        true -> RH.div { className: "col-md-12", style: { paddingBottom: "10px" } }
138 139 140
            [ R2.menu { id: "toolbar" }
              [ RH.ul {}
                [ -- change type button (?)
141
                  RH.li {} [ centerButton props.sigmaRef ]
142
                , RH.li {} [ pauseForceAtlasButton {state: props.forceAtlasState} ]
143
                , RH.li {} [ edgesToggleButton {state: props.showEdges} ]
144
                , RH.li {} [ louvainToggleButton props.showLouvain ]
145 146
                , RH.li {} [ edgeConfluenceControl edgeConfluenceRange props.edgeConfluence ]
                , RH.li {} [ edgeWeightControl edgeWeightRange props.edgeWeight ]
147 148 149 150 151
                  -- change level
                  -- file upload
                  -- run demo
                  -- search button
                  -- search topics
152
                , RH.li {} [ labelSizeButton props.sigmaRef localControls.labelSize ] -- labels size: 1-4
153
                , RH.li {} [ nodeSizeControl nodeSizeRange props.nodeSize ]
154
                  -- zoom: 0 -100 - calculate ratio
155
                , RH.li {} [ multiSelectEnabledButton props.multiSelectEnabled ]  -- toggle multi node selection
156
                  -- save button
157
                , RH.li {} [ nodeSearchControl { graph: props.graph
158
                                               , multiSelectEnabled: props.multiSelectEnabled
159
                                               , selectedNodeIds: props.selectedNodeIds } ]
160
                , RH.li {} [ mouseSelectorSizeButton props.sigmaRef localControls.mouseSelectorSize ]
161
                ]
James Laver's avatar
James Laver committed
162 163 164
              ]
            ]

165
useGraphControls :: SigmaxT.SGraph -> R.Hooks (Record Controls)
166
useGraphControls graph = do
167
  edgeConfluence <- R.useState' $ Range.Closed { min: 0.0, max: 1.0 }
168 169
  edgeWeight <- R.useState' $ Range.Closed {
      min: 0.0
170
    , max: I.toNumber $ Seq.length $ SigmaxT.graphEdges graph
171
    }
172
  forceAtlasState <- R.useState' SigmaxT.InitialRunning
173
  graphStage      <- R.useState' Graph.Init
174
  multiSelectEnabled <- R.useState' false
175
  nodeSize <- R.useState' $ Range.Closed { min: 0.0, max: 100.0 }
176 177
  removedNodeIds <- R.useState' SigmaxT.emptyNodeIds
  selectedNodeIds <- R.useState' SigmaxT.emptyNodeIds
178
  showControls    <- R.useState' false
179
  showEdges <- R.useState' SigmaxT.EShow
180
  showLouvain <- R.useState' false
181
  showSidePanel   <- R.useState' GET.InitialClosed
182
  showTree <- R.useState' false
183 184
  sigma <- Sigmax.initSigma
  sigmaRef <- R.useRef sigma
185

186
  pure { edgeConfluence
187
       , edgeWeight
188
       , forceAtlasState
189
       , graph
190
       , graphStage
191
       , multiSelectEnabled
192
       , nodeSize
193
       , removedNodeIds
194
       , selectedNodeIds
195
       , showControls
196
       , showEdges
197
       , showLouvain
198 199 200 201
       , showSidePanel
       , showTree
       , sigmaRef
       }
202 203

getShowControls :: Record Controls -> Boolean
James Laver's avatar
James Laver committed
204 205
getShowControls { showControls: ( should /\ _ ) } = should

206 207 208
getShowTree :: Record Controls -> Boolean
getShowTree { showTree: ( should /\ _ ) } = should

209 210
setShowControls :: Record Controls -> Boolean -> Effect Unit
setShowControls { showControls: ( _ /\ set ) } v = set $ const v
James Laver's avatar
James Laver committed
211

212
setShowTree :: Record Controls -> Boolean -> Effect Unit
213
setShowTree { showTree: ( _ /\ set ) } v = set $ not <<< const v