Commit 22f768b1 authored by arturo's avatar arturo

>>> BEGIN 375

parent 55b2a8c9
......@@ -10362,6 +10362,9 @@ h4, h5, h6 {
.graph-topbar {
padding: 0 8px;
height: 100%;
display: flex;
padding-left: 4px;
padding-right: 4px;
}
.right-handed .graph-topbar {
border-left: 1px solid rgba(255, 255, 255, 0.05);
......@@ -10371,6 +10374,20 @@ h4, h5, h6 {
border-right: 1px solid rgba(255, 255, 255, 0.05);
}
.graph-topbar__toolbar, .graph-topbar__sidebar {
width: 136px;
margin-left: 4px;
margin-right: 4px;
}
.graph-topbar__search {
width: 304px;
margin-left: 4px;
margin-right: 4px;
}
.graph-topbar__search [type=submit] {
display: none;
}
#logo-designed {
border: 15px;
}
......@@ -11355,8 +11372,6 @@ select.form-control {
}
/* Grid constants */
/* Topbar constants */
/* Sidebar constant */
/* Colors */
.phylo {
/* ---------- axis ---------- */
......
module Gargantext.Components.GraphExplorer where
module Gargantext.Components.GraphExplorer.Layout where
import Gargantext.Prelude hiding (max, min)
import Control.Bind ((=<<))
import DOM.Simple.Types (Element)
import Data.Array as A
import Data.FoldableWithIndex (foldMapWithIndex)
......@@ -22,7 +21,6 @@ import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Data.Louvain as Louvain
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.Sigmax.Sigma (startForceAtlas2)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Sessions (Session, get)
......@@ -34,50 +32,25 @@ import Math as Math
import Partial.Unsafe (unsafePartial)
import Reactix as R
import Reactix.DOM.HTML as RH
import Record as Record
import Record.Extra as RX
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer"
here = R2.here "Gargantext.Components.GraphExplorer.Layout"
type BaseProps =
( boxes :: Boxes
, graphId :: GET.GraphId
)
type LayoutProps =
( session :: Session
| BaseProps )
type Props =
( graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
| LayoutProps
)
type GraphWriteProps =
( mMetaData' :: Maybe GET.MetaData
| Props
type Props' =
( key :: String
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
type LayoutWithKeyProps =
( key :: String
| LayoutProps )
--------------------------------------------------------------
explorerLayoutWithKey :: R2.Component LayoutWithKeyProps
explorerLayoutWithKey = R.createElement explorerLayoutWithKeyCpt
explorerLayoutWithKeyCpt :: R.Component LayoutWithKeyProps
explorerLayoutWithKeyCpt = here.component "explorerLayoutWithKey" cpt where
cpt { boxes, graphId, session } _ = do
pure $ explorerLayout { boxes, graphId, session } []
explorerLayout :: R2.Component LayoutProps
explorerLayout = R.createElement explorerLayoutCpt
explorerLayoutCpt :: R.Component LayoutProps
explorerLayoutCpt = here.component "explorerLayout" cpt where
layout :: R2.Leaf Props'
layout = R2.leaf layoutCpt
layoutCpt :: R.Component Props'
layoutCpt = here.component "explorerLayout" cpt where
cpt props@{ boxes: { graphVersion }, graphId, session } _ = do
graphVersion' <- T.useLive T.unequal graphVersion
......@@ -88,45 +61,97 @@ explorerLayoutCpt = here.component "explorerLayout" cpt where
where
errorHandler = logRESTError here "[explorerLayout]"
handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) =
explorerWriteGraph (Record.merge props { graph, hyperdataGraph: loaded, mMetaData' }) []
render { graph
, hyperdataGraph: loaded
, mMetaData'
, session
, boxes: props.boxes
, graphId
}
where
Tuple mMetaData' graph = convert hyperdataGraph
explorerWriteGraph :: R2.Component GraphWriteProps
explorerWriteGraph = R.createElement explorerWriteGraphCpt
explorerWriteGraphCpt :: R.Component GraphWriteProps
explorerWriteGraphCpt = here.component "explorerWriteGraph" cpt where
cpt props@{ boxes: { sidePanelGraph }
--------------------------------------------------------
type RenderProps =
( mMetaData' :: Maybe GET.MetaData
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
render :: R2.Leaf RenderProps
render = R2.leaf renderCpt
renderCpt :: R.Component RenderProps
renderCpt = here.component "explorerWriteGraph" cpt where
cpt props@{ boxes
, graph
, mMetaData' } _ = do
, mMetaData'
, graphId
} _ = do
-- Computed
let
topBarPortalKey = "portal-topbar::" <> show graphId
-- Hooks
mTopBarHost <- R.unsafeHooksEffect $ R2.getElementById "portal-topbar"
R.useEffectOnce' $ do
T.write_ (Just { mGraph: Just graph
-- Effects
R.useEffectOnce' do
flip T.write_ boxes.sidePanelGraph $ Just
{ mGraph: Just graph
, mMetaData: mMetaData'
, multiSelectEnabled: false
, removedNodeIds: Set.empty
, selectedNodeIds: Set.empty
, showControls: false
, sideTab: GET.SideTabLegend }) sidePanelGraph
, sideTab: GET.SideTabLegend
}
-- Render
pure $ R.fragment
[
explorer (RX.pick props :: Record Props) []
-- Explorer
explorer
(RX.pick props :: Record ExplorerProps)
,
-- Topbar
R2.createPortal' mTopBarHost
[
GETB.topBar { boxes: props.boxes }
R2.fragmentWithKey topBarPortalKey
[
GETB.topBar
{ sidePanelState: props.boxes.sidePanelState
, sidePanelGraph: props.boxes.sidePanelGraph
}
]
]
-- ,
-- Sidebar
]
--------------------------------------------------------------
explorer :: R2.Component Props
explorer = R.createElement explorerCpt
explorerCpt :: R.Component Props
explorerCpt = here.component "explorer" cpt
where
cpt props@{ boxes: { graphVersion, handed, reloadForest, showTree, sidePanelGraph, sidePanelState }
----------------------------------------------------------
type ExplorerProps =
( graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
explorer :: R2.Leaf ExplorerProps
explorer = R2.leaf explorerCpt
explorerCpt :: R.Component ExplorerProps
explorerCpt = here.component "explorer" cpt where
cpt props@{ boxes: { graphVersion
, reloadForest
, showTree
, sidePanelGraph
, sidePanelState
}
, graph
, graphId
, hyperdataGraph
......@@ -134,7 +159,6 @@ explorerCpt = here.component "explorer" cpt
} _ = do
{ mMetaData } <- GEST.focusedSidePanel sidePanelGraph
_graphVersion' <- T.useLive T.unequal graphVersion
handed' <- T.useLive T.unequal handed
mMetaData' <- T.useLive T.unequal mMetaData
let startForceAtlas = maybe true (\(GET.MetaData { startForceAtlas: sfa }) -> sfa) mMetaData'
......@@ -178,7 +202,7 @@ explorerCpt = here.component "explorer" cpt
pure $
RH.div { className: "graph-meta-container" }
[ RH.div { className: "graph-container" }
[ RH.div { className: "container-fluid " <> hClass handed' }
[ RH.div { className: "container-fluid" }
[ RH.div { id: "controls-container" } [ Controls.controls controls [] ]
, RH.div { className: "row graph-row" }
[ RH.div { ref: graphRef, id: "graph-view", className: "col-md-12" } []
......@@ -188,15 +212,13 @@ explorerCpt = here.component "explorer" cpt
, graph
, hyperdataGraph
, mMetaData
} []
}
]
]
]
]
hClass h = case h of
Types.LeftHanded -> "lefthanded"
Types.RightHanded -> "righthanded"
--------------------------------------------------------------
type GraphProps =
( boxes :: Boxes
......@@ -207,8 +229,8 @@ type GraphProps =
, mMetaData :: T.Box (Maybe GET.MetaData)
)
graphView :: R2.Component GraphProps
graphView = R.createElement graphViewCpt
graphView :: R2.Leaf GraphProps
graphView = R2.leaf graphViewCpt
graphViewCpt :: R.Component GraphProps
graphViewCpt = here.component "graphView" cpt
where
......@@ -217,7 +239,7 @@ graphViewCpt = here.component "graphView" cpt
, elRef
, graph
, hyperdataGraph: GET.HyperdataGraph { mCamera }
, mMetaData } _children = do
, mMetaData } _ = do
edgeConfluence' <- T.useLive T.unequal controls.edgeConfluence
edgeWeight' <- T.useLive T.unequal controls.edgeWeight
mMetaData' <- T.useLive T.unequal mMetaData
......@@ -264,6 +286,8 @@ graphViewCpt = here.component "graphView" cpt
, transformedGraph
} []
--------------------------------------------------------
convert :: GET.GraphData -> Tuple (Maybe GET.MetaData) SigmaxT.SGraph
convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
where
......@@ -310,6 +334,8 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
targetNode = unsafePartial $ fromJust $ Map.lookup e.target nodesMap
color = sourceNode.color
--------------------------------------------------------------
-- | See sigmajs/plugins/sigma.renderers.customShapes/shape-library.js
modeGraphType :: Types.Mode -> String
modeGraphType Types.Authors = "square"
......@@ -317,6 +343,7 @@ modeGraphType Types.Institutes = "equilateral"
modeGraphType Types.Sources = "star"
modeGraphType Types.Terms = "def"
--------------------------------------------------------------
getNodes :: Session -> T2.Reload -> GET.GraphId -> AffRESTError GET.HyperdataGraph
getNodes session graphVersion graphId =
......@@ -324,6 +351,8 @@ getNodes session graphVersion graphId =
(Just graphId)
("?version=" <> (show graphVersion))
--------------------------------------------------------------
type LiveProps = (
edgeConfluence' :: Range.NumberRange
, edgeWeight' :: Range.NumberRange
......
......@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search
import Prelude
import DOM.Simple.Console (log2)
import Data.Foldable (foldl)
import Data.Foldable (foldl, intercalate)
import Data.Sequence as Seq
import Data.Set as Set
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Utils (queryMatchesLabel)
......@@ -23,6 +24,7 @@ type Props = (
graph :: SigmaxT.SGraph
, multiSelectEnabled :: T.Box Boolean
, selectedNodeIds :: T.Box SigmaxT.NodeIds
, className :: String
)
-- | Whether a node matches a search string
......@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax
searchNodes "" _ = Seq.empty
searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes
nodeSearchControl :: R2.Component Props
nodeSearchControl = R.createElement nodeSearchControlCpt
nodeSearchControl :: R2.Leaf Props
nodeSearchControl = R2.leaf nodeSearchControlCpt
nodeSearchControlCpt :: R.Component Props
nodeSearchControlCpt = here.component "nodeSearchControl" cpt
where
cpt { graph, multiSelectEnabled, selectedNodeIds } _ = do
cpt props@{ graph, multiSelectEnabled, selectedNodeIds } _ = do
search <- T.useBox ""
search' <- T.useLive T.unequal search
multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled
let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds
pure $ R.fragment
[ inputWithAutocomplete { autocompleteSearch: autocompleteSearch graph
, classes: "mx-2"
pure $
H.form
{ className: intercalate " "
[ "graph-node-search"
, props.className
]
}
[
inputWithAutocomplete
{ autocompleteSearch: autocompleteSearch graph
, onAutocompleteClick: doSearch
, onEnterPress: doSearch
, state: search } []
, H.div { className: "btn input-group-addon"
, on: { click: \_ -> doSearch search' }
, classes: ""
, state: search
}
[ H.span { className: "fa fa-search" } [] ]
,
B.button
{ callback: \_ -> doSearch search'
, type: "submit"
, className: "graph-node-search__submit"
}
[
B.icon { name: "search"}
]
]
autocompleteSearch :: SigmaxT.SGraph -> String -> Array String
......
......@@ -6,7 +6,6 @@ module Gargantext.Components.GraphExplorer.ToggleButton
, edgesToggleButton
, louvainToggleButton
, multiSelectEnabledButton
, sidebarToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
, treeToggleButton
......@@ -203,35 +202,3 @@ treeToggleButtonCpt = here.component "treeToggleButton" cpt
, onClick: \_ -> T.modify_ not state
, style: "light"
} []
type SidebarToggleButtonProps = (
state :: T.Box GT.SidePanelState
)
sidebarToggleButton :: R2.Component SidebarToggleButtonProps
sidebarToggleButton = R.createElement sidebarToggleButtonCpt
sidebarToggleButtonCpt :: R.Component SidebarToggleButtonProps
sidebarToggleButtonCpt = here.component "sidebarToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.div { className: "btn btn-outline-light " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text onMessage offMessage state') ] ]
cls GT.Opened = "active"
cls _ = ""
onMessage = "Hide Sidebar"
offMessage = "Show Sidebar"
text on _off GT.Opened = on
text _on off GT.InitialClosed = off
text _on off GT.Closed = off
onClick state = \_ ->
T.modify_ GT.toggleSidePanelState state
-- case s of
-- GET.InitialClosed -> GET.Opened GET.SideTabLegend
-- GET.Closed -> GET.Opened GET.SideTabLegend
-- (GET.Opened _) -> GET.Closed) state
module Gargantext.Components.GraphExplorer.TopBar where
module Gargantext.Components.GraphExplorer.TopBar (topBar) where
import Data.Maybe (Maybe(..))
import Reactix as R
import Reactix.DOM.HTML as RH
import Toestand as T
import Gargantext.Prelude hiding (max,min)
import Gargantext.Prelude hiding (max, min)
import Gargantext.Components.App.Data (Boxes)
import Data.Maybe (Maybe(..))
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.GraphExplorer.ToggleButton as Toggle
import Gargantext.Types (SidePanelState)
import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
type Props =
( sidePanelGraph :: T.Box (Maybe (Record GEST.SidePanel))
, sidePanelState :: T.Box (SidePanelState)
)
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.TopBar"
type TopBar =
(
boxes :: Boxes
)
topBar :: R2.Leaf Props
topBar = R2.leaf component
topBar :: R2.Leaf TopBar
topBar = R2.leafComponent topBarCpt
topBarCpt :: R.Component TopBar
topBarCpt = here.component "topBar" cpt where
cpt { boxes: { sidePanelGraph
, sidePanelState } } _ = do
{ mGraph, multiSelectEnabled, selectedNodeIds, showControls } <- GEST.focusedSidePanel sidePanelGraph
component :: R.Component Props
component = here.component "topBar" cpt where
cpt { sidePanelGraph, sidePanelState } _ = do
-- States
{ mGraph
, multiSelectEnabled
, selectedNodeIds
, showControls
} <- GEST.focusedSidePanel sidePanelGraph
mGraph' <- T.useLive T.unequal mGraph
mGraph' <- R2.useLive' mGraph
showControls' <- R2.useLive' showControls
sidePanelState' <- R2.useLive' sidePanelState
let search = case mGraph' of
Just graph -> nodeSearchControl { graph
, multiSelectEnabled
, selectedNodeIds } []
Nothing -> RH.div {} []
-- Render
pure $
pure $ RH.form { className: "graph-topbar d-flex" }
[ Toggle.controlsToggleButton { state: showControls } []
, Toggle.sidebarToggleButton { state: sidePanelState } []
, search
H.div
{ className: "graph-topbar" }
[
-- Toolbar toggle
B.button
{ className: "graph-topbar__toolbar"
, callback: \_ -> T.modify_ (not) showControls
, variant: showControls' ?
ButtonVariant Light $
OutlinedButtonVariant Light
}
[
H.text $ showControls' ? "Hide toolbar" $ "Show toolbar"
]
,
-- Sidebar toggle
B.button
{ className: "graph-topbar__sidebar"
, callback: \_ -> T.modify_ GT.toggleSidePanelState sidePanelState
, variant: sidePanelState' == GT.Opened ?
ButtonVariant Light $
OutlinedButtonVariant Light
}
[
H.text $ sidePanelState' == GT.Opened ?
"Hide sidebar" $
"Show sidebar"
]
,
-- Search
-- @WIP: R2.fromMaybe_
case mGraph' of
Nothing -> mempty
Just graph ->
nodeSearchControl
{ graph
, multiSelectEnabled
, selectedNodeIds
, className: "graph-topbar__search"
}
]
......@@ -29,8 +29,8 @@ type Props =
, state :: T.Box String
)
inputWithAutocomplete :: R2.Component Props
inputWithAutocomplete = R.createElement inputWithAutocompleteCpt
inputWithAutocomplete :: R2.Leaf Props
inputWithAutocomplete = R2.leaf inputWithAutocompleteCpt
inputWithAutocompleteCpt :: R.Component Props
inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
where
......
......@@ -14,7 +14,7 @@ import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.ErrorsView (errorsView)
import Gargantext.Components.Footer (footer)
import Gargantext.Components.Forest as Forest
import Gargantext.Components.GraphExplorer as GraphExplorer
import Gargantext.Components.GraphExplorer.Layout as GraphExplorer
import Gargantext.Components.GraphExplorer.Sidebar as GES
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.Lang (LandingLang(LL_EN))
......@@ -434,12 +434,13 @@ graphExplorerCpt = here.component "graphExplorer" cpt where
authedProps =
Record.merge
{ content:
\session -> GraphExplorer.explorerLayoutWithKey
\session -> GraphExplorer.layout
{ boxes
, graphId: nodeId
, key: "graphId-" <> show nodeId
, session }
[]
, session
}
}
sessionProps
......
......@@ -86,5 +86,25 @@
width: 300px
top: 50px
/////////////////////////////////////////////
.graph-topbar
@include aside-topbar
display: flex
padding-left: $topbar-item-margin
padding-right: $topbar-item-margin
&__toolbar,
&__sidebar
width: $topbar-fixed-button-width
margin-left: $topbar-item-margin
margin-right: $topbar-item-margin
&__search
width: $topbar-input-width
margin-left: $topbar-item-margin
margin-right: $topbar-item-margin
[type="submit"]
display: none
......@@ -26,7 +26,6 @@
/* Grid constants */
$topbar-height: 56px; // ~ unworthy empirical value (@TODO topbar height calculation)
$graph-margin: 16px;
$layout-height: calc(100vh - #{ $topbar-height} );
......@@ -38,16 +37,6 @@ $left-column-width: 10%;
$center-column-width: 85%;
$right-column-width: 5%;
/* Topbar constants */
$topbar-input-width: 304px;
/* Sidebar constant */
$sidebar-width: 480px;
$sidebar-height: calc(100vh - #{ $topbar-height });
$tab-margin-x: space-x(2.5);
/* Colors */
$graph-background-color: $body-bg;
......@@ -448,34 +437,33 @@ $decreasing-color: #11638F;
////////////////////////////////////////////////////////////////
.phylo-topbar {
$margin: space-x(0.5);
$fixed-button-width: 136px;
@include aside-topbar();
padding-left: $margin;
padding-right: $margin;
padding-left: $topbar-item-margin;
padding-right: $topbar-item-margin;
display: flex;
&__toolbar,
&__sidebar {
width: $fixed-button-width;
margin-left: $margin;
margin-right: $margin;
width: $topbar-fixed-button-width;
margin-left: $topbar-item-margin;
margin-right: $topbar-item-margin;
}
&__source {
width: $topbar-input-width;
margin-left: $margin;
margin-right: $margin;
margin-left: $topbar-item-margin;
margin-right: $topbar-item-margin;
}
&__autocomplete {
display: flex;
width: $topbar-input-width;
position: relative;
margin-left: $margin;
margin-right: $margin;
margin-left: $topbar-item-margin;
margin-right: $topbar-item-margin;
}
&__suggestion {
......@@ -559,7 +547,7 @@ $decreasing-color: #11638F;
}
.phylo-details-tab {
$margin-x: $tab-margin-x;
$margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2);
&__counter {
......@@ -585,7 +573,7 @@ $decreasing-color: #11638F;
}
.phylo-selection-tab {
$margin-x: $tab-margin-x;
$margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2);
&__highlight {
......
/// Topbar
$topbar-height: 56px; // ~ unworthy empirical value (@TODO topbar height calculation)
$topbar-input-width: 304px;
$topbar-item-margin: space-x(0.5);
$topbar-fixed-button-width: 136px;
/// Sidebar
$sidebar-width: 480px;
$sidebar-height: calc(100vh - #{ $topbar-height });
$sidebar-tab-margin-x: space-x(2.5);
.with-icon-font {
font-family: ForkAwesome, $font-family-base;
}
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