Commit 7cad1696 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-arxiv

parents 2a29dcc9 9e3893bb
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{ {
"name": "Gargantext", "name": "Gargantext",
"version": "0.0.5.8.4", "version": "0.0.5.8.5",
"scripts": { "scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix", "generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash", "generate-psc-packages-nix": "./nix/generate-packages-json.bash",
......
...@@ -15,12 +15,14 @@ type Props = ...@@ -15,12 +15,14 @@ type Props =
) )
type Options = type Options =
( className :: String ( className :: String
, contentClassName :: String
) )
options :: Record Options options :: Record Options
options = options =
{ className: "" { className : ""
, contentClassName : ""
} }
-- | Component simulating a native <fieldset> -- | Component simulating a native <fieldset>
...@@ -36,12 +38,21 @@ component = R.hooksComponent componentName cpt where ...@@ -36,12 +38,21 @@ component = R.hooksComponent componentName cpt where
cpt props@{ titleSlot cpt props@{ titleSlot
} children = do } children = do
-- Computed -- Computed
className <- pure $ intercalate " " let
-- provided custom className className = intercalate " "
[ props.className -- provided custom className
-- BEM classNames [ props.className
, componentName -- BEM classNames
] , componentName
]
contentClassName = intercalate " "
-- provided custom className
[ props.contentClassName
-- BEM classNames
, componentName <> "__content"
]
-- Render -- Render
pure $ pure $
...@@ -53,6 +64,6 @@ component = R.hooksComponent componentName cpt where ...@@ -53,6 +64,6 @@ component = R.hooksComponent componentName cpt where
[ titleSlot ] [ titleSlot ]
, ,
H.div H.div
{ className: componentName <> "__content" } { className: contentClassName}
children children
] ]
...@@ -14,7 +14,9 @@ import Gargantext.Components.Bootstrap.Icon(icon) as Exports ...@@ -14,7 +14,9 @@ import Gargantext.Components.Bootstrap.Icon(icon) as Exports
import Gargantext.Components.Bootstrap.IconButton(iconButton) as Exports import Gargantext.Components.Bootstrap.IconButton(iconButton) as Exports
import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports
import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports
import Gargantext.Components.Bootstrap.Tooltip(tooltip, tooltipBind, tooltipContainer) as Exports import Gargantext.Components.Bootstrap.Tabs(tabs) as Exports
import Gargantext.Components.Bootstrap.Tooltip(tooltip, TooltipBindingProps, tooltipBind, tooltipBind', tooltipContainer) as Exports
import Gargantext.Components.Bootstrap.Wad(wad, wad', wad_) as Exports
import Gargantext.Components.Bootstrap.Shortcut( import Gargantext.Components.Bootstrap.Shortcut(
div', div_ div', div_
......
module Gargantext.Components.Bootstrap.Tooltip module Gargantext.Components.Bootstrap.Tooltip
( tooltip ( tooltip
, tooltipBind , TooltipBindingProps, tooltipBind, tooltipBind'
, tooltipContainer , tooltipContainer
) where ) where
...@@ -90,6 +90,13 @@ tooltipBind = ...@@ -90,6 +90,13 @@ tooltipBind =
, "data-tip": true , "data-tip": true
} }
-- | Derived empty state
tooltipBind' :: Record TooltipBindingProps
tooltipBind' =
{ "data-for": ""
, "data-tip": false
}
------------------------------------------------------------- -------------------------------------------------------------
type ContainerProps = type ContainerProps =
......
module Gargantext.Components.Bootstrap.Wad
( wad
, wad'
, wad_
) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Reactix as R
import Reactix.DOM.HTML as H
componentName :: String
componentName = "b-wad"
-- | Structural Component for a simple Element only serving the purpose to add
-- | some classes in it
-- |
-- | Hence the name: Wad (noun): a small mass, lump, or ball of anything ;
-- | a roll of something
wad :: Array String -> Array R.Element -> R.Element
wad classes children = R.createDOMElement "div" cls children
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
-- | Shorthand for using <wad> Component without writing its text node
wad' :: Array String -> String -> R.Element
wad' classes text = R.createDOMElement "div" cls chd
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
chd = [ H.text text ]
-- | Shorthand for using <wad> Component without any child
wad_ :: Array String -> R.Element
wad_ classes = R.createDOMElement "div" cls []
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
module Gargantext.Components.Bootstrap.Tabs(tabs) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Effect (Effect)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props a =
( value :: a
, callback :: a -> Effect Unit
, list :: Array a
| Options
)
type Options =
( className :: String
)
options :: Record Options
options =
{ className : ""
}
-- | Structural molecular component to the Bootstrap <nav-tabs> + <nav-item>
-- | simplifying a lot of the available UI/UX possibilites (type, disabled
-- | tabs, etc)
-- |
-- | https://getbootstrap.com/docs/4.6/components/navs/#tabs
tabs :: forall r a.
Show a
=> Eq a
=> R2.OptLeaf Options (Props a) r
tabs = R2.optLeaf component options
componentName :: String
componentName = "b-tabs"
component :: forall a.
Show a
=> Eq a
=> R.Component (Props a)
component = R.hooksComponent componentName cpt where
cpt props@{ list, value, callback } _ = do
-- Computed
let
className = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, "nav nav-tabs"
]
-- Render
pure $
H.ul
{ className } $
flip map list \item ->
H.li
{ className: "nav-item"
, on: { click: \_ -> callback item }
}
[
H.a
{ className: intercalate " "
[ "nav-link"
, value == item ? "active" $ ""
]
}
[
H.text $ show item
]
]
...@@ -16,7 +16,6 @@ import Gargantext.Ends (Frontends) ...@@ -16,7 +16,6 @@ import Gargantext.Ends (Frontends)
import Gargantext.Hooks.LinkHandler (useLinkHandler) import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Routes (AppRoute(..)) import Gargantext.Routes (AppRoute(..))
import Gargantext.Sessions (Session(..), unSessions) import Gargantext.Sessions (Session(..), unSessions)
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -132,9 +131,10 @@ plusCpt = here.component "plus" cpt where ...@@ -132,9 +131,10 @@ plusCpt = here.component "plus" cpt where
, variant: ButtonVariant Light , variant: ButtonVariant Light
} }
[ [
B.icon { name: "universal-access" } B.icon
{ name: "universal-access" }
, ,
H.text $ nbsp 1 B.wad_ [ "d-inline-block", "w-1" ]
, ,
H.text $ "Log in/out" H.text $ "Log in/out"
] ]
...@@ -150,7 +150,7 @@ forestLayout :: R2.Leaf Props ...@@ -150,7 +150,7 @@ forestLayout :: R2.Leaf Props
forestLayout = R2.leaf forestLayoutCpt forestLayout = R2.leaf forestLayoutCpt
forestLayoutCpt :: R.Memo Props forestLayoutCpt :: R.Memo Props
forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where
cpt p children = pure $ cpt p _ = pure $
H.div H.div
{ className: "forest-layout" } { className: "forest-layout" }
......
exports.nodeUserRegexp = /(@{1}.*).gargantext.org$/;
...@@ -31,15 +31,15 @@ type NodeActionsGraphProps = ...@@ -31,15 +31,15 @@ type NodeActionsGraphProps =
nodeActionsGraph :: R2.Component NodeActionsGraphProps nodeActionsGraph :: R2.Component NodeActionsGraphProps
nodeActionsGraph = R.createElement nodeActionsGraphCpt nodeActionsGraph = R.createElement nodeActionsGraphCpt
nodeActionsGraphCpt :: R.Component NodeActionsGraphProps nodeActionsGraphCpt :: R.Component NodeActionsGraphProps
nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt where
where cpt { id, graphVersions, session, refresh } _ =
cpt { id, graphVersions, session, refresh } _ = do let sameVersions = (graphVersions.gv_graph == Just graphVersions.gv_repo)
pure $ H.div { className: "node-actions" } [ in pure $
if graphVersions.gv_graph == Just graphVersions.gv_repo then
H.div {} [] R2.if' (not sameVersions) $
else
graphUpdateButton { id, session, refresh } graphUpdateButton { id, session, refresh }
]
type GraphUpdateButtonProps = type GraphUpdateButtonProps =
( id :: GT.ID ( id :: GT.ID
...@@ -99,12 +99,9 @@ type NodeActionsNodeListProps = ...@@ -99,12 +99,9 @@ type NodeActionsNodeListProps =
nodeActionsNodeList :: Record NodeActionsNodeListProps -> R.Element nodeActionsNodeList :: Record NodeActionsNodeListProps -> R.Element
nodeActionsNodeList p = R.createElement nodeActionsNodeListCpt p [] nodeActionsNodeList p = R.createElement nodeActionsNodeListCpt p []
nodeActionsNodeListCpt :: R.Component NodeActionsNodeListProps nodeActionsNodeListCpt :: R.Component NodeActionsNodeListProps
nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt where
where cpt props _ = pure $ nodeListUpdateButton props
cpt props _ = do
pure $ H.div { className: "node-actions" } [
nodeListUpdateButton props
]
type NodeListUpdateButtonProps = type NodeListUpdateButtonProps =
( listId :: GT.ListId ( listId :: GT.ListId
......
...@@ -132,9 +132,12 @@ graphCpt = here.component "graph" cpt where ...@@ -132,9 +132,12 @@ graphCpt = here.component "graph" cpt where
Sigma.stopForceAtlas2 sig Sigma.stopForceAtlas2 sig
case mCamera of case mCamera of
Nothing -> pure unit
Just (GET.Camera { ratio, x, y }) -> do Just (GET.Camera { ratio, x, y }) -> do
Sigma.updateCamera sig { ratio, x, y } Sigma.updateCamera sig { ratio, x, y }
-- Default camera: slightly de-zoom the graph to avoid
-- nodes sticking to the container borders
Nothing ->
Sigma.updateCamera sig { ratio: 1.1, x: 0.0, y: 0.0 }
-- Reload Sigma on Theme changes -- Reload Sigma on Theme changes
_ <- flip T.listen boxes.theme \{ old, new } -> _ <- flip T.listen boxes.theme \{ old, new } ->
......
module Gargantext.Components.GraphExplorer.Button module Gargantext.Components.GraphExplorer.Buttons
( Props, centerButton, simpleButton, cameraButton ) where ( Props
, centerButton
, simpleButton
, cameraButton
, edgesToggleButton
, louvainToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
, multiSelectEnabledButton
) where
import Prelude import Prelude
import DOM.Simple.Console (log2)
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Enum (fromEnum) import Data.Enum (fromEnum)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.String as DS import Data.String as DS
import DOM.Simple.Console (log2)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (launchAff_) import Effect.Aff (launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Now as EN import Effect.Now as EN
import Reactix as R import Gargantext.Components.Bootstrap as B
import Reactix.DOM.HTML as H import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryData) import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryData)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat(..)) import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat(..))
import Gargantext.Components.GraphExplorer.API (cloneGraph) import Gargantext.Components.GraphExplorer.API (cloneGraph)
import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.GraphExplorer.Utils as GEU import Gargantext.Components.GraphExplorer.Utils as GEU
import Gargantext.Hooks.Sigmax as Sigmax import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Button" here = R2.here "Gargantext.Components.GraphExplorer.Button"
...@@ -36,9 +50,12 @@ type Props = ( ...@@ -36,9 +50,12 @@ type Props = (
, text :: String , text :: String
) )
-- @WIP
simpleButton :: Record Props -> R.Element simpleButton :: Record Props -> R.Element
simpleButton props = R.createElement simpleButtonCpt props [] simpleButton props = R.createElement simpleButtonCpt props []
------------------------------------------------------
simpleButtonCpt :: R.Component Props simpleButtonCpt :: R.Component Props
simpleButtonCpt = here.component "simpleButton" cpt simpleButtonCpt = here.component "simpleButton" cpt
where where
...@@ -48,14 +65,16 @@ simpleButtonCpt = here.component "simpleButton" cpt ...@@ -48,14 +65,16 @@ simpleButtonCpt = here.component "simpleButton" cpt
} [ R2.small {} [ H.text text ] ] } [ R2.small {} [ H.text text ] ]
centerButton :: R.Ref Sigmax.Sigma -> R.Element centerButton :: R.Ref Sigmax.Sigma -> R.Element
centerButton sigmaRef = simpleButton { centerButton sigmaRef = B.button
onClick: \_ -> do { variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[centerButton] sigma: Nothing" $ \s -> Sigmax.dependOnSigma sigma "[centerButton] sigma: Nothing" $ \s ->
Sigma.goToAllCameras s {x: 0.0, y: 0.0, ratio: 1.0, angle: 0.0} Sigma.goToAllCameras s {x: 0.0, y: 0.0, ratio: 1.0, angle: 0.0}
, text: "Center"
} }
[ H.text "Center" ]
------------------------------------------------------
type CameraButtonProps = type CameraButtonProps =
( id :: Int ( id :: Int
...@@ -71,8 +90,10 @@ cameraButton { id ...@@ -71,8 +90,10 @@ cameraButton { id
, hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph } , hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph }
, session , session
, sigmaRef , sigmaRef
, reloadForest } = simpleButton { , reloadForest } = B.button
onClick: \_ -> do
{ variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do
screen <- Sigma.takeScreenshot s screen <- Sigma.takeScreenshot s
...@@ -105,5 +126,171 @@ cameraButton { id ...@@ -105,5 +126,171 @@ cameraButton { id
Left err -> liftEffect $ log2 "[cameraButton] RESTError" err Left err -> liftEffect $ log2 "[cameraButton] RESTError" err
Right _ret -> do Right _ret -> do
liftEffect $ T2.reload reloadForest liftEffect $ T2.reload reloadForest
, text: "Screenshot"
} }
[ H.text "Screenshot" ]
------------------------------------------------------
type EdgesButtonProps =
( state :: T.Box SigmaxTypes.ShowEdgesState
, stateAtlas :: T.Box SigmaxTypes.ForceAtlasState
)
edgesToggleButton :: R2.Leaf EdgesButtonProps
edgesToggleButton = R2.leaf edgesToggleButtonCpt
edgesToggleButtonCpt :: R.Component EdgesButtonProps
edgesToggleButtonCpt = here.component "edgesToggleButton" cpt
where
cpt { state, stateAtlas } _ = do
-- States
state' <- R2.useLive' state
stateAtlas' <- R2.useLive' stateAtlas
-- Computed
let
cst SigmaxTypes.InitialRunning = Disabled
cst SigmaxTypes.Running = Disabled
cst _ = Enabled
-- Render
pure $
B.button
{ variant: state' == SigmaxTypes.EShow ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, status: cst stateAtlas'
-- TODO: Move this to Graph.purs to the R.useEffect handler which renders nodes/edges
, callback: \_ -> T.modify_ SigmaxTypes.toggleShowEdgesState state
}
[ H.text "Edges" ]
------------------------------------------------------
type LouvainToggleButtonProps =
( state :: T.Box Boolean
)
louvainToggleButton :: R2.Leaf LouvainToggleButtonProps
louvainToggleButton = R2.leaf louvainToggleButtonCpt
louvainToggleButtonCpt :: R.Component LouvainToggleButtonProps
louvainToggleButtonCpt = here.component "louvainToggleButton" cpt
where
cpt { state } _ = do
state' <- R2.useLive' state
pure $
B.button
{ variant: state' ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, callback: \_ -> T.modify_ (not) state
}
[ H.text "Louvain" ]
--------------------------------------------------------------
type ForceAtlasProps =
( state :: T.Box SigmaxTypes.ForceAtlasState
)
pauseForceAtlasButton :: R2.Leaf ForceAtlasProps
pauseForceAtlasButton = R2.leaf pauseForceAtlasButtonCpt
pauseForceAtlasButtonCpt :: R.Component ForceAtlasProps
pauseForceAtlasButtonCpt = here.component "forceAtlasToggleButton" cpt
where
cpt { state } _ = do
-- States
state' <- R2.useLive' state
-- Computed
let
cls SigmaxTypes.InitialRunning = "on-running-animation active"
cls SigmaxTypes.Running = "on-running-animation active"
cls _ = ""
vrt SigmaxTypes.InitialRunning = ButtonVariant Secondary
vrt SigmaxTypes.Running = ButtonVariant Secondary
vrt _ = OutlinedButtonVariant Secondary
icn SigmaxTypes.InitialRunning = "pause"
icn SigmaxTypes.InitialStopped = "play"
icn SigmaxTypes.Running = "pause"
icn SigmaxTypes.Paused = "play"
icn SigmaxTypes.Killed = "play"
-- Render
pure $
B.button
{ variant: vrt state'
, className: cls state'
, callback: \_ -> T.modify_ SigmaxTypes.toggleForceAtlasState state
}
[
B.icon
{ name: icn state'}
]
--------------------------------------------------------
type ResetForceAtlasProps =
( forceAtlasState :: T.Box SigmaxTypes.ForceAtlasState
, sigmaRef :: R.Ref Sigmax.Sigma
)
resetForceAtlasButton :: R2.Leaf ResetForceAtlasProps
resetForceAtlasButton = R2.leaf resetForceAtlasButtonCpt
resetForceAtlasButtonCpt :: R.Component ResetForceAtlasProps
resetForceAtlasButtonCpt = here.component "resetForceAtlasToggleButton" cpt
where
cpt { forceAtlasState, sigmaRef } _ = do
pure $ H.button { className: "btn btn-outline-secondary"
, on: { click: onClick forceAtlasState sigmaRef }
} [ R2.small {} [ H.text "Reset Force Atlas" ] ]
onClick forceAtlasState sigmaRef _ = do
-- TODO Sigma.killForceAtlas2 sigma
-- startForceAtlas2 sigma
Sigmax.dependOnSigma (R.readRef sigmaRef) "[resetForceAtlasButton] no sigma" $ \sigma -> do
Sigma.killForceAtlas2 sigma
Sigma.refreshForceAtlas sigma Graph.forceAtlas2Settings
T.write_ SigmaxTypes.Killed forceAtlasState
------------------------------------------------------------------
type MultiSelectEnabledButtonProps =
( state :: T.Box Boolean
)
multiSelectEnabledButton :: R2.Leaf MultiSelectEnabledButtonProps
multiSelectEnabledButton = R2.leaf multiSelectEnabledButtonCpt
multiSelectEnabledButtonCpt :: R.Component MultiSelectEnabledButtonProps
multiSelectEnabledButtonCpt = here.component "multiSelectEnabledButton" cpt
where
cpt { state } _ = do
state' <- R2.useLive' state
pure $
H.div
{ className: "btn-group"
, role: "group"
}
[
B.button
{ variant: state' ?
OutlinedButtonVariant Secondary $
ButtonVariant Secondary
, callback: \_ -> T.write_ false state
}
[ H.text "Single" ]
,
B.button
{ variant: state' ?
ButtonVariant Secondary $
OutlinedButtonVariant Secondary
, callback: \_ -> T.write_ true state
}
[ H.text "Multiple" ]
]
module Gargantext.Components.GraphExplorer.Legend module Gargantext.Components.GraphExplorer.Legend
( Props, legend, legendCpt ( Props, legend
) where ) where
import Prelude hiding (map) import Prelude hiding (map)
...@@ -7,31 +7,39 @@ import Prelude hiding (map) ...@@ -7,31 +7,39 @@ import Prelude hiding (map)
import Data.Sequence (Seq) import Data.Sequence (Seq)
import Data.Traversable (foldMap) import Data.Traversable (foldMap)
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as RH import Reactix.DOM.HTML as H
import Gargantext.Components.GraphExplorer.Types (Legend(..), intColor) import Gargantext.Components.GraphExplorer.Types (Legend(..), intColor)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Legend" here = R2.here "Gargantext.Components.GraphExplorer.Legend"
type Props = ( items :: Seq Legend ) type Props = ( items :: Seq Legend )
legend :: Record Props -> R.Element legend :: R2.Leaf Props
legend props = R.createElement legendCpt props [] legend = R2.leaf legendCpt
legendCpt :: R.Component Props legendCpt :: R.Component Props
legendCpt = here.component "legend" cpt legendCpt = here.component "legend" cpt where
where cpt { items } _ = pure $
cpt {items} _ = pure $ RH.div {} [foldMap entry items]
H.ul
entry :: Legend -> R.Element { className: "graph-legend" }
entry (Legend {id_, label}) = [
RH.p {} flip foldMap items \(Legend { id_, label }) ->
[ RH.span { style: { width : 10
, height: 10 H.li
, backgroundColor: intColor id_ { className: "graph-legend__item" }
, display: "inline-block" [
} H.span
} [] { className: "graph-legend__code"
, RH.text $ " " <> label , style: { backgroundColor: intColor id_ }
] }
[]
,
H.span
{ className: "graph-legend__caption" }
[ H.text label ]
]
]
...@@ -18,37 +18,45 @@ import Gargantext.Utils.Reactix as R2 ...@@ -18,37 +18,45 @@ import Gargantext.Utils.Reactix as R2
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.RangeControl" here = R2.here "Gargantext.Components.GraphExplorer.RangeControl"
type Props = ( type Props =
caption :: String ( caption :: String
, sliderProps :: Record RS.Props , sliderProps :: Record RS.Props
) )
rangeControl :: R2.Component Props rangeControl :: R2.Leaf Props
rangeControl = R.createElement rangeControlCpt rangeControl = R2.leaf rangeControlCpt
rangeControlCpt :: R.Component Props rangeControlCpt :: R.Component Props
rangeControlCpt = here.component "rangeButton" cpt rangeControlCpt = here.component "rangeButton" cpt
where where
cpt {caption, sliderProps} _ = do cpt {caption, sliderProps} _ = pure $
pure $
H.span {className: "range text-center"} H.span
[ H.label {} [ R2.small {} [ H.text caption ] ] { className: "range-control" }
, RS.rangeSlider sliderProps [
] H.label
{ className: "range-control__label" }
type EdgeConfluenceControlProps = ( [ H.text caption ]
range :: Range.NumberRange ,
RS.rangeSlider sliderProps
]
----------------------------------------
type EdgeConfluenceControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
edgeConfluenceControl :: R2.Component EdgeConfluenceControlProps edgeConfluenceControl :: R2.Leaf EdgeConfluenceControlProps
edgeConfluenceControl = R.createElement edgeConfluenceControlCpt edgeConfluenceControl = R2.leaf edgeConfluenceControlCpt
edgeConfluenceControlCpt :: R.Component EdgeConfluenceControlProps edgeConfluenceControlCpt :: R.Component EdgeConfluenceControlProps
edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -62,21 +70,24 @@ edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt ...@@ -62,21 +70,24 @@ edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
type EdgeWeightControlProps = ( --------------------------------------
range :: Range.NumberRange
type EdgeWeightControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
edgeWeightControl :: R2.Component EdgeWeightControlProps edgeWeightControl :: R2.Leaf EdgeWeightControlProps
edgeWeightControl = R.createElement edgeWeightControlCpt edgeWeightControl = R2.leaf edgeWeightControlCpt
edgeWeightControlCpt :: R.Component EdgeWeightControlProps edgeWeightControlCpt :: R.Component EdgeWeightControlProps
edgeWeightControlCpt = here.component "edgeWeightControl" cpt edgeWeightControlCpt = here.component "edgeWeightControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -90,21 +101,24 @@ edgeWeightControlCpt = here.component "edgeWeightControl" cpt ...@@ -90,21 +101,24 @@ edgeWeightControlCpt = here.component "edgeWeightControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
--------------------------------------
type NodeSideControlProps = ( type NodeSideControlProps =
range :: Range.NumberRange ( range :: Range.NumberRange
, state :: T.Box Range.NumberRange , state :: T.Box Range.NumberRange
) )
nodeSizeControl :: R2.Component NodeSideControlProps nodeSizeControl :: R2.Leaf NodeSideControlProps
nodeSizeControl = R.createElement nodeSizeControlCpt nodeSizeControl = R2.leaf nodeSizeControlCpt
nodeSizeControlCpt :: R.Component NodeSideControlProps nodeSizeControlCpt :: R.Component NodeSideControlProps
nodeSizeControlCpt = here.component "nodeSizeControl" cpt nodeSizeControlCpt = here.component "nodeSizeControl" cpt
where where
cpt { range: Range.Closed { min, max } cpt { range: Range.Closed { min, max }
, state } _ = do , state
} _ = do
state' <- T.useLive T.unequal state state' <- T.useLive T.unequal state
pure $ rangeControl { pure $ rangeControl {
...@@ -118,4 +132,4 @@ nodeSizeControlCpt = here.component "nodeSizeControl" cpt ...@@ -118,4 +132,4 @@ nodeSizeControlCpt = here.component "nodeSizeControl" cpt
, height: 5.0 , height: 5.0
, onChange: \rng -> T.write_ rng state , onChange: \rng -> T.write_ rng state
} }
} [] }
This diff is collapsed.
...@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search ...@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search
import Prelude import Prelude
import DOM.Simple.Console (log2) import DOM.Simple.Console (log2)
import Data.Foldable (foldl) import Data.Foldable (foldl, intercalate)
import Data.Sequence as Seq import Data.Sequence as Seq
import Data.Set as Set import Data.Set as Set
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete) import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete)
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Utils (queryMatchesLabel) import Gargantext.Utils (queryMatchesLabel)
...@@ -23,6 +24,7 @@ type Props = ( ...@@ -23,6 +24,7 @@ type Props = (
graph :: SigmaxT.SGraph graph :: SigmaxT.SGraph
, multiSelectEnabled :: T.Box Boolean , multiSelectEnabled :: T.Box Boolean
, selectedNodeIds :: T.Box SigmaxT.NodeIds , selectedNodeIds :: T.Box SigmaxT.NodeIds
, className :: String
) )
-- | Whether a node matches a search string -- | Whether a node matches a search string
...@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax ...@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax
searchNodes "" _ = Seq.empty searchNodes "" _ = Seq.empty
searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes
nodeSearchControl :: R2.Component Props nodeSearchControl :: R2.Leaf Props
nodeSearchControl = R.createElement nodeSearchControlCpt nodeSearchControl = R2.leaf nodeSearchControlCpt
nodeSearchControlCpt :: R.Component Props nodeSearchControlCpt :: R.Component Props
nodeSearchControlCpt = here.component "nodeSearchControl" cpt nodeSearchControlCpt = here.component "nodeSearchControl" cpt
where where
cpt { graph, multiSelectEnabled, selectedNodeIds } _ = do cpt props@{ graph, multiSelectEnabled, selectedNodeIds } _ = do
search <- T.useBox "" search <- T.useBox ""
search' <- T.useLive T.unequal search search' <- T.useLive T.unequal search
multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled
let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds
pure $ R.fragment pure $
[ inputWithAutocomplete { autocompleteSearch: autocompleteSearch graph
, classes: "mx-2" H.form
, onAutocompleteClick: doSearch { className: intercalate " "
, onEnterPress: doSearch [ "graph-node-search"
, state: search } [] , props.className
, H.div { className: "btn input-group-addon" ]
, on: { click: \_ -> doSearch search' } }
} [
[ H.span { className: "fa fa-search" } [] ] inputWithAutocomplete
{ autocompleteSearch: autocompleteSearch graph
, onAutocompleteClick: doSearch
, onEnterPress: doSearch
, classes: ""
, state: search
}
,
B.button
{ callback: \_ -> doSearch search'
, type: "submit"
, className: "graph-node-search__submit"
}
[
B.icon { name: "search"}
]
] ]
autocompleteSearch :: SigmaxT.SGraph -> String -> Array String autocompleteSearch :: SigmaxT.SGraph -> String -> Array String
......
module Gargantext.Components.GraphExplorer.Sidebar.Types where module Gargantext.Components.GraphExplorer.Sidebar.Types where
import Data.Maybe (Maybe(..), maybe)
import Data.Set as Set
import Reactix as R
import Toestand as T
import Gargantext.Prelude import Gargantext.Prelude
import Data.Maybe (Maybe(..), maybe)
import Data.Set as Set
import Gargantext.Components.GraphExplorer.Types as GET import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Hooks.Sigmax.Types as SigmaxT import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Types as GT
import Reactix as R
import Toestand as T
type SidePanel = type SidePanel =
( (
...@@ -19,6 +19,7 @@ type SidePanel = ...@@ -19,6 +19,7 @@ type SidePanel =
, selectedNodeIds :: SigmaxT.NodeIds , selectedNodeIds :: SigmaxT.NodeIds
, showControls :: Boolean , showControls :: Boolean
, sideTab :: GET.SideTab , sideTab :: GET.SideTab
, showSidebar :: GT.SidePanelState
) )
initialSidePanel :: Maybe (Record SidePanel) initialSidePanel :: Maybe (Record SidePanel)
...@@ -32,7 +33,9 @@ focusedSidePanel :: T.Box (Maybe (Record SidePanel)) ...@@ -32,7 +33,9 @@ focusedSidePanel :: T.Box (Maybe (Record SidePanel))
, removedNodeIds :: T.Box SigmaxT.NodeIds , removedNodeIds :: T.Box SigmaxT.NodeIds
, selectedNodeIds :: T.Box SigmaxT.NodeIds , selectedNodeIds :: T.Box SigmaxT.NodeIds
, showControls :: T.Box Boolean , showControls :: T.Box Boolean
, sideTab :: T.Box GET.SideTab } , sideTab :: T.Box GET.SideTab
, showSidebar :: T.Box GT.SidePanelState
}
focusedSidePanel sidePanel = do focusedSidePanel sidePanel = do
mGraph <- T.useFocused mGraph <- T.useFocused
(maybe Nothing _.mGraph) (maybe Nothing _.mGraph)
...@@ -55,6 +58,9 @@ focusedSidePanel sidePanel = do ...@@ -55,6 +58,9 @@ focusedSidePanel sidePanel = do
sideTab <- T.useFocused sideTab <- T.useFocused
(maybe GET.SideTabLegend _.sideTab) (maybe GET.SideTabLegend _.sideTab)
(\val -> maybe Nothing (\sp -> Just $ sp { sideTab = val })) sidePanel (\val -> maybe Nothing (\sp -> Just $ sp { sideTab = val })) sidePanel
showSidebar <- T.useFocused
(maybe GT.InitialClosed _.showSidebar)
(\val -> maybe Nothing (\sp -> Just $ sp { showSidebar = val })) sidePanel
pure $ { pure $ {
mGraph mGraph
...@@ -64,4 +70,5 @@ focusedSidePanel sidePanel = do ...@@ -64,4 +70,5 @@ focusedSidePanel sidePanel = do
, selectedNodeIds , selectedNodeIds
, showControls , showControls
, sideTab , sideTab
, showSidebar
} }
...@@ -34,20 +34,35 @@ sizeButtonCpt :: R.Component Props ...@@ -34,20 +34,35 @@ sizeButtonCpt :: R.Component Props
sizeButtonCpt = here.component "sizeButton" cpt where sizeButtonCpt = here.component "sizeButton" cpt where
cpt { state, caption, min, max, onChange } _ = do cpt { state, caption, min, max, onChange } _ = do
defaultValue <- T.useLive T.unequal state defaultValue <- T.useLive T.unequal state
pure $ H.span { className: "range-simple" }
[ H.label {} [ R2.small {} [ H.text caption ] ] pure $
, H.input { type: "range"
, className: "form-control" H.span
, min: show min { className: "range-simple" }
, max: show max [
, defaultValue H.label
, on: { input: onChange } }] { className: "range-simple__label" }
[ H.text caption ]
,
H.span
{ className: "range-simple__field" }
[
H.input
{ type: "range"
, min: show min
, max: show max
, defaultValue
, on: { input: onChange }
, className: "range-simple__input"
}
]
]
labelSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element labelSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element
labelSizeButton sigmaRef state = labelSizeButton sigmaRef state =
sizeButton { sizeButton {
state state
, caption: "Label Size" , caption: "Label size"
, min: 1.0 , min: 1.0
, max: 30.0 , max: 30.0
, onChange: \e -> do , onChange: \e -> do
...@@ -67,7 +82,7 @@ mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element ...@@ -67,7 +82,7 @@ mouseSelectorSizeButton :: R.Ref Sigmax.Sigma -> T.Box Number -> R.Element
mouseSelectorSizeButton sigmaRef state = mouseSelectorSizeButton sigmaRef state =
sizeButton { sizeButton {
state state
, caption: "Selector Size" , caption: "Selector size"
, min: 1.0 , min: 1.0
, max: 50.0 , max: 50.0
, onChange: \e -> do , onChange: \e -> do
......
...@@ -3,27 +3,18 @@ module Gargantext.Components.GraphExplorer.ToggleButton ...@@ -3,27 +3,18 @@ module Gargantext.Components.GraphExplorer.ToggleButton
, toggleButton , toggleButton
, toggleButtonCpt , toggleButtonCpt
, controlsToggleButton , controlsToggleButton
, edgesToggleButton
, louvainToggleButton
, multiSelectEnabledButton
, sidebarToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
) where ) where
import Prelude import Prelude
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Graph as Graph
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Toestand as T import Toestand as T
-- @WIP: used?
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.ToggleButton" here = R2.here "Gargantext.Components.GraphExplorer.ToggleButton"
...@@ -56,6 +47,8 @@ toggleButtonCpt = here.component "toggleButton" cpt ...@@ -56,6 +47,8 @@ toggleButtonCpt = here.component "toggleButton" cpt
text on _off true = on text on _off true = on
text _on off false = off text _on off false = off
----------------------------------------------------------------
type ControlsToggleButtonProps = ( type ControlsToggleButtonProps = (
state :: T.Box Boolean state :: T.Box Boolean
) )
...@@ -73,147 +66,3 @@ controlsToggleButtonCpt = here.component "controlsToggleButton" cpt ...@@ -73,147 +66,3 @@ controlsToggleButtonCpt = here.component "controlsToggleButton" cpt
, onClick: \_ -> T.modify_ not state , onClick: \_ -> T.modify_ not state
, style: "light" , style: "light"
} [] } []
type EdgesButtonProps = (
state :: T.Box SigmaxTypes.ShowEdgesState
)
edgesToggleButton :: R2.Component EdgesButtonProps
edgesToggleButton = R.createElement edgesToggleButtonCpt
edgesToggleButtonCpt :: R.Component EdgesButtonProps
edgesToggleButtonCpt = here.component "edgesToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.button { className: "btn btn-outline-secondary " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text state') ] ]
text s = if SigmaxTypes.edgeStateHidden s then "Show edges" else "Hide edges"
cls SigmaxTypes.EShow = ""
cls _ = "active"
-- TODO: Move this to Graph.purs to the R.useEffect handler which renders nodes/edges
onClick state _ = T.modify_ SigmaxTypes.toggleShowEdgesState state
type LouvainToggleButtonProps = (
state :: T.Box Boolean
)
louvainToggleButton :: R2.Component LouvainToggleButtonProps
louvainToggleButton = R.createElement louvainToggleButtonCpt
louvainToggleButtonCpt :: R.Component LouvainToggleButtonProps
louvainToggleButtonCpt = here.component "louvainToggleButton" cpt
where
cpt { state } _ = do
pure $ toggleButton {
state: state
, onMessage: "Louvain off"
, offMessage: "Louvain on"
, onClick: \_ -> T.modify_ not state
, style: "secondary"
} []
type MultiSelectEnabledButtonProps = (
state :: T.Box Boolean
)
multiSelectEnabledButton :: R2.Component MultiSelectEnabledButtonProps
multiSelectEnabledButton = R.createElement multiSelectEnabledButtonCpt
multiSelectEnabledButtonCpt :: R.Component MultiSelectEnabledButtonProps
multiSelectEnabledButtonCpt = here.component "lmultiSelectEnabledButton" cpt
where
cpt { state } _ = do
pure $ toggleButton {
state: state
, onMessage: "Single-node"
, offMessage: "Multi-node"
, onClick: \_ -> T.modify_ not state
, style : "primary"
} []
type ForceAtlasProps = (
state :: T.Box SigmaxTypes.ForceAtlasState
)
pauseForceAtlasButton :: R2.Component ForceAtlasProps
pauseForceAtlasButton = R.createElement pauseForceAtlasButtonCpt
pauseForceAtlasButtonCpt :: R.Component ForceAtlasProps
pauseForceAtlasButtonCpt = here.component "forceAtlasToggleButton" cpt
where
cpt { state } _ = do
state' <- T.useLive T.unequal state
pure $ H.button { className: "btn btn-outline-secondary " <> cls state'
, on: { click: onClick state }
} [ R2.small {} [ H.text (text state') ] ]
cls SigmaxTypes.InitialRunning = "active"
cls SigmaxTypes.Running = "active"
cls _ = ""
text SigmaxTypes.InitialRunning = "Pause"
text SigmaxTypes.InitialStopped = "Start"
text SigmaxTypes.Running = "Pause"
text SigmaxTypes.Paused = "Start"
text SigmaxTypes.Killed = "Start"
onClick state _ = T.modify_ SigmaxTypes.toggleForceAtlasState state
type ResetForceAtlasProps = (
forceAtlasState :: T.Box SigmaxTypes.ForceAtlasState
, sigmaRef :: R.Ref Sigmax.Sigma
)
resetForceAtlasButton :: R2.Component ResetForceAtlasProps
resetForceAtlasButton = R.createElement resetForceAtlasButtonCpt
resetForceAtlasButtonCpt :: R.Component ResetForceAtlasProps
resetForceAtlasButtonCpt = here.component "resetForceAtlasToggleButton" cpt
where
cpt { forceAtlasState, sigmaRef } _ = do
pure $ H.button { className: "btn btn-outline-secondary"
, on: { click: onClick forceAtlasState sigmaRef }
} [ R2.small {} [ H.text "Reset Force Atlas" ] ]
onClick forceAtlasState sigmaRef _ = do
-- TODO Sigma.killForceAtlas2 sigma
-- startForceAtlas2 sigma
Sigmax.dependOnSigma (R.readRef sigmaRef) "[resetForceAtlasButton] no sigma" $ \sigma -> do
Sigma.killForceAtlas2 sigma
Sigma.refreshForceAtlas sigma Graph.forceAtlas2Settings
T.write_ SigmaxTypes.Killed forceAtlasState
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 Gargantext.Prelude hiding (max, min)
import Reactix as R
import Reactix.DOM.HTML as RH
import Toestand as T
import Gargantext.Prelude hiding (max,min) import Data.Maybe (Maybe)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.GraphExplorer.Search (nodeSearchControl) import Gargantext.Components.GraphExplorer.Search (nodeSearchControl)
import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST import Gargantext.Components.GraphExplorer.Sidebar.Types as GEST
import Gargantext.Components.GraphExplorer.ToggleButton as Toggle import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 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))
)
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.TopBar" here = R2.here "Gargantext.Components.GraphExplorer.TopBar"
type TopBar = topBar :: R2.Leaf Props
( topBar = R2.leaf component
boxes :: Boxes
) component :: R.Component Props
component = here.component "topBar" cpt where
cpt { sidePanelGraph } _ = do
-- States
{ mGraph
, multiSelectEnabled
, selectedNodeIds
, showControls
, showSidebar
} <- GEST.focusedSidePanel sidePanelGraph
mGraph' <- R2.useLive' mGraph
showControls' <- R2.useLive' showControls
showSidebar' <- R2.useLive' showSidebar
-- Render
pure $
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 showSidebar
, variant: showSidebar' == GT.Opened ?
ButtonVariant Light $
OutlinedButtonVariant Light
}
[
H.text $ showSidebar' == GT.Opened ?
"Hide sidebar" $
"Show sidebar"
]
,
-- Search
R2.fromMaybe_ mGraph' \graph ->
topBar :: R2.Leaf TopBar nodeSearchControl
topBar = R2.leafComponent topBarCpt { graph
topBarCpt :: R.Component TopBar , multiSelectEnabled
topBarCpt = here.component "topBar" cpt where , selectedNodeIds
cpt { boxes: { sidePanelGraph , className: "graph-topbar__search"
, sidePanelState } } _ = do }
{ mGraph, multiSelectEnabled, selectedNodeIds, showControls } <- GEST.focusedSidePanel sidePanelGraph
mGraph' <- T.useLive T.unequal mGraph
let search = case mGraph' of
Just graph -> nodeSearchControl { graph
, multiSelectEnabled
, selectedNodeIds } []
Nothing -> RH.div {} []
pure $ RH.form { className: "graph-topbar d-flex" }
[ Toggle.controlsToggleButton { state: showControls } []
, Toggle.sidebarToggleButton { state: sidePanelState } []
, search
] ]
...@@ -26,7 +26,8 @@ type UserInfo ...@@ -26,7 +26,8 @@ type UserInfo
, ui_cwTouchPhone :: Maybe String , ui_cwTouchPhone :: Maybe String
, ui_cwTouchMail :: Maybe String } , ui_cwTouchMail :: Maybe String }
type UserInfoM type UserInfoM
= { ui_id :: NotNull Int = { token :: NotNull String
, ui_id :: NotNull Int
, ui_username :: String , ui_username :: String
, ui_email :: String , ui_email :: String
, ui_title :: String , ui_title :: String
......
...@@ -29,8 +29,8 @@ type Props = ...@@ -29,8 +29,8 @@ type Props =
, state :: T.Box String , state :: T.Box String
) )
inputWithAutocomplete :: R2.Component Props inputWithAutocomplete :: R2.Leaf Props
inputWithAutocomplete = R.createElement inputWithAutocompleteCpt inputWithAutocomplete = R2.leaf inputWithAutocompleteCpt
inputWithAutocompleteCpt :: R.Component Props inputWithAutocompleteCpt :: R.Component Props
inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
where where
......
...@@ -7,9 +7,6 @@ module Gargantext.Components.Nodes.Annuaire.User.Contact ...@@ -7,9 +7,6 @@ module Gargantext.Components.Nodes.Annuaire.User.Contact
, saveUserInfo , saveUserInfo
) where ) where
import Gargantext.Components.GraphQL.User (UserInfo, _ui_cwCity, _ui_cwCountry, _ui_cwFirstName, _ui_cwLabTeamDeptsFirst, _ui_cwLastName, _ui_cwOffice, _ui_cwOrganizationFirst, _ui_cwRole, _ui_cwTouchMail, _ui_cwTouchPhone)
import Gargantext.Prelude (Unit, bind, discard, pure, show, ($), (<$>), (<>))
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Lens as L import Data.Lens as L
import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
...@@ -19,6 +16,7 @@ import Effect.Class (liftEffect) ...@@ -19,6 +16,7 @@ import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.GraphQL (getClient) import Gargantext.Components.GraphQL (getClient)
import Gargantext.Components.GraphQL.Endpoints (getUserInfo) import Gargantext.Components.GraphQL.Endpoints (getUserInfo)
import Gargantext.Components.GraphQL.User (UserInfo, _ui_cwCity, _ui_cwCountry, _ui_cwFirstName, _ui_cwLabTeamDeptsFirst, _ui_cwLastName, _ui_cwOffice, _ui_cwOrganizationFirst, _ui_cwRole, _ui_cwTouchMail, _ui_cwTouchPhone)
import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData', HyperdataContact(..)) import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData', HyperdataContact(..))
...@@ -26,8 +24,9 @@ import Gargantext.Components.Nodes.Lists.Types as LT ...@@ -26,8 +24,9 @@ import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Prelude (Unit, bind, discard, pure, show, ($), (<$>), (<>))
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, get, put, sessionId) import Gargantext.Sessions (Session(..), get, put, sessionId)
import Gargantext.Types (NodeType(..)) import Gargantext.Types (NodeType(..))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
...@@ -196,11 +195,13 @@ saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "") ...@@ -196,11 +195,13 @@ saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "")
saveUserInfo :: Session -> Int -> UserInfo -> AffRESTError Int saveUserInfo :: Session -> Int -> UserInfo -> AffRESTError Int
saveUserInfo session id ui = do saveUserInfo session id ui = do
let token = getToken session
client <- liftEffect $ getClient session client <- liftEffect $ getClient session
res <- mutation res <- mutation
client client
"update user_info" "update user_info"
{ update_user_info: onlyArgs { ui_id: id { update_user_info: onlyArgs { token: token
, ui_id: id
, ui_cwFirstName: ga ui.ui_cwFirstName , ui_cwFirstName: ga ui.ui_cwFirstName
, ui_cwLastName: ga ui.ui_cwLastName , ui_cwLastName: ga ui.ui_cwLastName
, ui_cwOrganization: ui.ui_cwOrganization , ui_cwOrganization: ui.ui_cwOrganization
...@@ -215,6 +216,7 @@ saveUserInfo session id ui = do ...@@ -215,6 +216,7 @@ saveUserInfo session id ui = do
where where
ga Nothing = ArgL IgnoreArg ga Nothing = ArgL IgnoreArg
ga (Just val) = ArgR val ga (Just val) = ArgR val
getToken (Session { token }) = token
type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps ) type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps )
......
...@@ -5,19 +5,20 @@ module Gargantext.Components.Nodes.Corpus.Phylo ...@@ -5,19 +5,20 @@ module Gargantext.Components.Nodes.Corpus.Phylo
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple (document, querySelector) import DOM.Simple (document, querySelector)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..), isJust)
import FFI.Simple ((..), (.=)) import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.API (get) import Gargantext.Components.PhyloExplorer.API (get)
import Gargantext.Components.PhyloExplorer.Layout (layout) import Gargantext.Components.PhyloExplorer.Layout (layout)
import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet) import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet)
import Gargantext.Config.REST (logRESTError) import Gargantext.Config.REST (logRESTError)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H
type MainProps = type MainProps =
( nodeId :: NodeID ( nodeId :: NodeID
...@@ -35,60 +36,93 @@ phyloLayoutCpt :: R.Component MainProps ...@@ -35,60 +36,93 @@ phyloLayoutCpt :: R.Component MainProps
phyloLayoutCpt = here.component "main" cpt where phyloLayoutCpt = here.component "main" cpt where
cpt { nodeId, session } _ = do cpt { nodeId, session } _ = do
-- | States
-- |
state' /\ state <- R2.useBox' Nothing
-- | Computed
-- |
let let
errorHandler = logRESTError here "[phylo]" errorHandler = logRESTError here "[phylo]"
handler (dataset :: PhyloDataSet) = handler (phyloDataSet :: PhyloDataSet) =
content layout
{ nodeId { nodeId
, dataset , phyloDataSet
} }
useLoader
-- | Hooks
-- |
useLoaderEffect
{ errorHandler { errorHandler
, loader: get session , loader: get session
, path: nodeId , path: nodeId
, render: handler , state
} }
-- @XXX: Runtime odd behavior
-- cannot use the `useEffect` + its cleanup function within the
-- same `Effect`, otherwise the below cleanup example will be
-- execute at mount
-------------------------------------------------------- -- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route .container"
type ContentProps = case mEl of
( nodeId :: NodeID Nothing -> R.nothing
, dataset :: PhyloDataSet Just el -> R2.addClass el [ "d-none" ]
)
content :: R2.Leaf ContentProps R.useEffectOnce do
content = R2.leaf contentCpt pure do
mEl <- querySelector document ".main-page__main-route .container"
contentCpt :: R.Component ContentProps case mEl of
contentCpt = here.component "content" cpt where Nothing -> R.nothing
cpt { nodeId, dataset } _ = do Just el -> R2.removeClass el [ "d-none" ]
-- Hooks
-- @XXX: reset "main-page__main-route" wrapper margin
-- see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route"
useFirstEffect' do
-- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?)
mEl <- querySelector document ".main-page__main-route .container"
case mEl of case mEl of
Nothing -> pure unit Nothing -> R.nothing
Just el -> do Just el -> R2.addClass el [ "p-0" ]
style <- pure $ (el .. "style")
pure $ (style .= "display") "none" R.useEffectOnce do
-- @XXX: reset "main-page__main-route" wrapper margin pure do
-- see Gargantext.Components.Router) (@TODO?) mEl <- querySelector document ".main-page__main-route"
mEl' <- querySelector document ".main-page__main-route"
case mEl' of case mEl of
Nothing -> pure unit Nothing -> R.nothing
Just el -> do Just el -> R2.removeClass el [ "p-0" ]
style <- pure $ (el .. "style")
pure $ (style .= "padding") "initial"
-- | Render
-- Render -- |
pure $ pure $
layout B.cloak
{ nodeId { isDisplayed: isJust state'
, phyloDataSet: dataset , idlingPhaseDuration: Just 150
, cloakSlot:
-- mimicking `PhyloExplorer.layout` preloading template
H.div
{ className: "phylo" }
[
H.div
{ className: "phylo__spinner-wrapper" }
[
B.spinner
{ className: "phylo__spinner" }
]
]
, defaultSlot:
R2.fromMaybe_ state' handler
} }
module Gargantext.Components.Nodes.Corpus.Graph
( graphLayout
) where
import Gargantext.Prelude
import DOM.Simple (document, querySelector)
import Data.Maybe (Maybe(..), isJust)
import Data.Set as Set
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.Layout (convert, layout)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Sessions (Session, get)
import Gargantext.Types as Types
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
type Props =
( key :: String
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
here :: R2.Here
here = R2.here "Gargantext.Components.Nodes.Corpus.Graph"
graphLayout :: R2.Leaf Props
graphLayout = R2.leaf graphLayoutCpt
graphLayoutCpt :: R.Component Props
graphLayoutCpt = here.component "explorerLayout" cpt where
cpt props@{ boxes: { graphVersion }, graphId, session } _ = do
-- | States
-- |
graphVersion' <- T.useLive T.unequal graphVersion
state' /\ state <- R2.useBox' Nothing
-- | Hooks
-- |
useLoaderEffect
{ errorHandler
, loader: getNodes session graphVersion'
, path: graphId
, state
}
-- @XXX: Runtime odd behavior
-- cannot use the `useEffect` + its cleanup function within the
-- same `Effect`, otherwise the below cleanup example will be
-- execute at mount
-- @XXX: inopinent <div> (see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route .container"
case mEl of
Nothing -> R.nothing
Just el -> R2.addClass el [ "d-none" ]
R.useEffectOnce do
pure do
mEl <- querySelector document ".main-page__main-route .container"
case mEl of
Nothing -> R.nothing
Just el -> R2.removeClass el [ "d-none" ]
-- @XXX: reset "main-page__main-route" wrapper margin
-- see Gargantext.Components.Router) (@TODO?)
R.useEffectOnce' do
mEl <- querySelector document ".main-page__main-route"
case mEl of
Nothing -> R.nothing
Just el -> R2.addClass el [ "p-0" ]
R.useEffectOnce do
pure do
mEl <- querySelector document ".main-page__main-route"
case mEl of
Nothing -> R.nothing
Just el -> R2.removeClass el [ "p-0" ]
-- | Render
-- |
pure $
B.cloak
{ isDisplayed: isJust state'
, idlingPhaseDuration: Just 150
, cloakSlot:
H.div
{ className: "graph-loader" }
[
B.spinner
{ className: "graph-loader__spinner" }
]
, defaultSlot:
R2.fromMaybe_ state' handler
}
where
errorHandler = logRESTError here "[explorerLayout]"
handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) =
content { graph
, hyperdataGraph: loaded
, mMetaData'
, session
, boxes: props.boxes
, graphId
}
where
Tuple mMetaData' graph = convert hyperdataGraph
--------------------------------------------------------
type ContentProps =
( mMetaData' :: Maybe GET.MetaData
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
content :: R2.Leaf ContentProps
content = R2.leaf contentCpt
contentCpt :: R.Component ContentProps
contentCpt = here.component "content" cpt where
cpt props@{ boxes, mMetaData', graph } _ = do
-- Hooks
R.useEffectOnce' $
-- Hydrate Boxes
flip T.write_ boxes.sidePanelGraph $ Just
{ mGraph: Just graph
, mMetaData: mMetaData'
, multiSelectEnabled: false
, removedNodeIds: Set.empty
, selectedNodeIds: Set.empty
, showControls: false
, sideTab: GET.SideTabLegend
, showSidebar: Types.InitialClosed
}
-- Render
pure $
layout
props
--------------------------------------------------------------
getNodes :: Session -> T2.Reload -> GET.GraphId -> AffRESTError GET.HyperdataGraph
getNodes session graphVersion graphId =
get session $ NodeAPI Types.Graph
(Just graphId)
("?version=" <> (show graphVersion))
...@@ -21,7 +21,7 @@ import Gargantext.Components.PhyloExplorer.TopBar (topBar) ...@@ -21,7 +21,7 @@ import Gargantext.Components.PhyloExplorer.TopBar (topBar)
import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), PhyloDataSet(..), ExtractedTerm, ExtractedCount, Source, Term, sortSources) import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), PhyloDataSet(..), ExtractedTerm, ExtractedCount, Source, Term, sortSources)
import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1') import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID, SidePanelState(..))
import Gargantext.Utils (getter, (?)) import Gargantext.Utils (getter, (?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Graphics.D3.Base (d3) import Graphics.D3.Base (d3)
...@@ -46,6 +46,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -46,6 +46,7 @@ layoutCpt = here.component "layout" cpt where
cpt { phyloDataSet: (PhyloDataSet o) cpt { phyloDataSet: (PhyloDataSet o)
, nodeId , nodeId
} _ = do } _ = do
-- States -- States
--------- ---------
...@@ -82,7 +83,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -82,7 +83,7 @@ layoutCpt = here.component "layout" cpt where
R2.useBox' false R2.useBox' false
sideBarDisplayed /\ sideBarDisplayedBox <- sideBarDisplayed /\ sideBarDisplayedBox <-
R2.useBox' false R2.useBox' InitialClosed
extractedTerms /\ extractedTermsBox <- extractedTerms /\ extractedTermsBox <-
R2.useBox' (mempty :: Array ExtractedTerm) R2.useBox' (mempty :: Array ExtractedTerm)
...@@ -256,7 +257,7 @@ layoutCpt = here.component "layout" cpt where ...@@ -256,7 +257,7 @@ layoutCpt = here.component "layout" cpt where
{ className: "phylo__sidebar" { className: "phylo__sidebar"
-- @XXX: ReactJS lack of "keep-alive" feature workaround solution -- @XXX: ReactJS lack of "keep-alive" feature workaround solution
-- @link https://github.com/facebook/react/issues/12039 -- @link https://github.com/facebook/react/issues/12039
, style: { display: sideBarDisplayed ? "block" $ "none" } , style: { display: sideBarDisplayed == Opened? "block" $ "none" }
} }
[ [
sideBar sideBar
......
...@@ -86,7 +86,7 @@ component = R.hooksComponent componentName cpt where ...@@ -86,7 +86,7 @@ component = R.hooksComponent componentName cpt where
B.caveat B.caveat
{ className: "phylo-selection-tab__nil" } { className: "phylo-selection-tab__nil" }
[ [
H.text "No selection has been made" H.text "Select term, branch or source to get their informations"
] ]
, ,
-- Selected source -- Selected source
...@@ -212,8 +212,7 @@ component = R.hooksComponent componentName cpt where ...@@ -212,8 +212,7 @@ component = R.hooksComponent componentName cpt where
{ className: "phylo-selection-tab__separator" } { className: "phylo-selection-tab__separator" }
[ [
B.icon B.icon
{ name: "angle-down" { name: "angle-down" }
}
] ]
, ,
-- No extracted result -- No extracted result
...@@ -291,6 +290,7 @@ component = R.hooksComponent componentName cpt where ...@@ -291,6 +290,7 @@ component = R.hooksComponent componentName cpt where
] ]
, ,
R2.if' (truncateResults) $ R2.if' (truncateResults) $
B.button B.button
{ variant: ButtonVariant Light { variant: ButtonVariant Light
, callback: \_ -> T.modify_ not showMoreBox , callback: \_ -> T.modify_ not showMoreBox
......
...@@ -4,15 +4,14 @@ module Gargantext.Components.PhyloExplorer.SideBar ...@@ -4,15 +4,14 @@ module Gargantext.Components.PhyloExplorer.SideBar
import Gargantext.Prelude import Gargantext.Prelude
import Data.Foldable (intercalate)
import Data.Maybe (Maybe) import Data.Maybe (Maybe)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.DetailsTab (detailsTab) import Gargantext.Components.PhyloExplorer.DetailsTab (detailsTab)
import Gargantext.Components.PhyloExplorer.SelectionTab (selectionTab) import Gargantext.Components.PhyloExplorer.SelectionTab (selectionTab)
import Gargantext.Components.PhyloExplorer.Types (ExtractedCount, ExtractedTerm, TabView(..)) import Gargantext.Components.PhyloExplorer.Types (ExtractedCount, ExtractedTerm, TabView(..))
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -48,84 +47,45 @@ component = R.hooksComponent componentName cpt where ...@@ -48,84 +47,45 @@ component = R.hooksComponent componentName cpt where
-- States -- States
tabView /\ tabViewBox <- R2.useBox' DetailsTab tabView /\ tabViewBox <- R2.useBox' DetailsTab
-- Computed
let
tabList = [ DetailsTab, SelectionTab ]
-- Render -- Render
pure $ pure $
H.div H.div
{ className: "phylo-sidebar" } { className: "phylo-sidebar" }
[ [
-- Teasers
H.div
{ className: "phylo-sidebar__top-teaser" }
[]
,
-- Menu -- Menu
H.ul B.tabs
{ className: intercalate " " { value: tabView
[ "nav nav-tabs" , list: tabList
, "phylo-sidebar__menu" , callback: flip T.write_ tabViewBox
]
} }
[ ,
H.li -- Content
{ className: "nav-item" case tabView of
, on: { click: \_ -> T.write_ DetailsTab tabViewBox }
} DetailsTab ->
[ detailsTab
H.a { key: (show props.nodeId) <> "-details"
{ className: intercalate " " , docCount: props.docCount
[ "nav-link" , foundationCount: props.foundationCount
, tabView == DetailsTab ? "active" $ "" , periodCount: props.periodCount
] , termCount: props.termCount
, groupCount: props.groupCount
, branchCount: props.branchCount
} }
[
H.text "Details" SelectionTab ->
] selectionTab
] { key: (show props.nodeId) <> "-selection"
, , extractedTerms: props.extractedTerms
H.li , extractedCount: props.extractedCount
{ className: "nav-item" , selectedTerm: props.selectedTerm
, on: { click: \_ -> T.write_ SelectionTab tabViewBox } , selectedBranch: props.selectedBranch
} , selectedSource: props.selectedSource
[ , selectTermCallback: props.selectTermCallback
H.a
{ className: intercalate " "
[ "nav-link"
, tabView == SelectionTab ? "active" $ ""
]
} }
[
H.text "Selection"
]
]
]
,
-- Details tab
R2.if' (tabView == DetailsTab) $
detailsTab
{ key: (show props.nodeId) <> "-details"
, docCount: props.docCount
, foundationCount: props.foundationCount
, periodCount: props.periodCount
, termCount: props.termCount
, groupCount: props.groupCount
, branchCount: props.branchCount
}
,
-- Selection tab
R2.if' (tabView == SelectionTab) $
selectionTab
{ key: (show props.nodeId) <> "-selection"
, extractedTerms: props.extractedTerms
, extractedCount: props.extractedCount
, selectedTerm: props.selectedTerm
, selectedBranch: props.selectedBranch
, selectedSource: props.selectedSource
, selectTermCallback: props.selectTermCallback
}
,
-- Teaser
H.div
{ className: "phylo-sidebar__bottom-teaser" }
[]
] ]
...@@ -9,6 +9,7 @@ import Effect (Effect) ...@@ -9,6 +9,7 @@ import Effect (Effect)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..)) import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..))
import Gargantext.Components.PhyloExplorer.Types (Term(..), Source(..)) import Gargantext.Components.PhyloExplorer.Types (Term(..), Source(..))
import Gargantext.Types (SidePanelState(..), toggleSidePanelState)
import Gargantext.Utils ((?)) import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix (nothing) import Reactix (nothing)
...@@ -27,7 +28,7 @@ type Props = ...@@ -27,7 +28,7 @@ type Props =
, resultCallback :: Maybe Term -> Effect Unit , resultCallback :: Maybe Term -> Effect Unit
, toolBar :: T.Box (Boolean) , toolBar :: T.Box (Boolean)
, sideBar :: T.Box (Boolean) , sideBar :: T.Box (SidePanelState)
) )
here :: R2.Here here :: R2.Here
...@@ -71,12 +72,12 @@ component = here.component "main" cpt where ...@@ -71,12 +72,12 @@ component = here.component "main" cpt where
-- Sidebar toggle -- Sidebar toggle
B.button B.button
{ className: "phylo-topbar__sidebar" { className: "phylo-topbar__sidebar"
, callback: \_ -> T.modify_ (not) sideBar , callback: \_ -> T.modify_ (toggleSidePanelState) sideBar
, variant: sideBar' ? , variant: sideBar' == Opened ?
ButtonVariant Light $ ButtonVariant Light $
OutlinedButtonVariant Light OutlinedButtonVariant Light
} }
[ H.text $ sideBar' ? "Hide sidebar" $ "Show sidebar" ] [ H.text $ sideBar' == Opened ? "Hide sidebar" $ "Show sidebar" ]
, ,
-- Source -- Source
H.div H.div
......
...@@ -469,6 +469,9 @@ data TabView ...@@ -469,6 +469,9 @@ data TabView
derive instance Generic TabView _ derive instance Generic TabView _
derive instance Eq TabView derive instance Eq TabView
instance Show TabView where
show DetailsTab = "Legend"
show SelectionTab = "Data"
----------------------------------------------------------- -----------------------------------------------------------
......
...@@ -158,14 +158,14 @@ renderScale :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange ...@@ -158,14 +158,14 @@ renderScale :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange
renderScale ref {width,height} (Range.Closed {min, max}) = renderScale ref {width,height} (Range.Closed {min, max}) =
H.div { ref, className, width, height, aria } [] H.div { ref, className, width, height, aria } []
where where
className = "scale" className = "range-slider__scale"
aria = { label: "Scale running from " <> show min <> " to " <> show max } aria = { label: "Scale running from " <> show min <> " to " <> show max }
renderScaleSel :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange -> R.Element renderScaleSel :: R.Ref (Nullable DOM.Element) -> Record Props -> Range.NumberRange -> R.Element
renderScaleSel ref props (Range.Closed {min, max}) = renderScaleSel ref props (Range.Closed {min, max}) =
H.div { ref, className, style} [] H.div { ref, className, style} []
where where
className = "scale-sel" className = "range-slider__scale-sel"
style = {left: computeLeft, width: computeWidth} style = {left: computeLeft, width: computeWidth}
percOffsetMin = Range.normalise props.bounds min percOffsetMin = Range.normalise props.bounds min
percOffsetMax = Range.normalise props.bounds max percOffsetMax = Range.normalise props.bounds max
...@@ -176,7 +176,7 @@ renderScaleSel ref props (Range.Closed {min, max}) = ...@@ -176,7 +176,7 @@ renderScaleSel ref props (Range.Closed {min, max}) =
renderKnob :: Knob -> R.Ref (Nullable DOM.Element) -> Range.NumberRange -> Bounds -> T.Box (Maybe Knob) -> Int -> R.Element renderKnob :: Knob -> R.Ref (Nullable DOM.Element) -> Range.NumberRange -> Bounds -> T.Box (Maybe Knob) -> Int -> R.Element
renderKnob knob ref (Range.Closed value) bounds set precision = renderKnob knob ref (Range.Closed value) bounds set precision =
H.div { ref, tabIndex, className, aria, on: { mouseDown: onMouseDown }, style } [ H.div { ref, tabIndex, className, aria, on: { mouseDown: onMouseDown }, style } [
H.div { className: "button" } H.div { className: "range-slider__placeholder" }
[ [
H.text $ text $ toFixed precision val H.text $ text $ toFixed precision val
] ]
...@@ -185,7 +185,7 @@ renderKnob knob ref (Range.Closed value) bounds set precision = ...@@ -185,7 +185,7 @@ renderKnob knob ref (Range.Closed value) bounds set precision =
text (Just num) = num text (Just num) = num
text Nothing = "error" text Nothing = "error"
tabIndex = 0 tabIndex = 0
className = "knob" className = "range-slider__knob"
aria = { label: labelPrefix knob <> "value: " <> show val } aria = { label: labelPrefix knob <> "value: " <> show val }
labelPrefix MinKnob = "Minimum " labelPrefix MinKnob = "Minimum "
labelPrefix MaxKnob = "Maximum " labelPrefix MaxKnob = "Maximum "
...@@ -220,4 +220,3 @@ roundRange :: Epsilon -> Bounds -> Range.NumberRange -> Range.NumberRange ...@@ -220,4 +220,3 @@ roundRange :: Epsilon -> Bounds -> Range.NumberRange -> Range.NumberRange
roundRange epsilon bounds (Range.Closed initial) = Range.Closed { min, max } roundRange epsilon bounds (Range.Closed initial) = Range.Closed { min, max }
where min = round epsilon bounds initial.min where min = round epsilon bounds initial.min
max = round epsilon bounds initial.max max = round epsilon bounds initial.max
This diff is collapsed.
...@@ -9,6 +9,7 @@ import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) ...@@ -9,6 +9,7 @@ import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.Lang (langSwitcher, allFeLangs) import Gargantext.Components.Lang (langSwitcher, allFeLangs)
import Gargantext.Components.Themes (themeSwitcher, allThemes) import Gargantext.Components.Themes (themeSwitcher, allThemes)
import Gargantext.Types (Handed(..)) import Gargantext.Types (Handed(..))
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -64,7 +65,9 @@ topBarCpt = here.component "topBar" cpt ...@@ -64,7 +65,9 @@ topBarCpt = here.component "topBar" cpt
] ]
, ,
B.button B.button
{ variant: ButtonVariant Light { variant: showTree' ?
ButtonVariant Light $
OutlinedButtonVariant Light
, callback: const $ T.modify_ (not) showTree , callback: const $ T.modify_ (not) showTree
, className: "main-topbar__tree-switcher" , className: "main-topbar__tree-switcher"
} }
......
...@@ -10,6 +10,8 @@ import Reactix (nothing, thenNothing) ...@@ -10,6 +10,8 @@ import Reactix (nothing, thenNothing)
import Reactix as R import Reactix as R
-- | Hook triggered on first mount event only -- | Hook triggered on first mount event only
-- |
-- | /!\ @TODO cleanup function not working
useFirstMount :: R.Hooks (Boolean) useFirstMount :: R.Hooks (Boolean)
useFirstMount = do useFirstMount = do
firstMount <- R.useRef true firstMount <- R.useRef true
......
module Gargantext.Utils.Popover where module Gargantext.Utils.Popover where
import Gargantext.Prelude
import DOM.Simple as DOM
import Data.Maybe (maybe) import Data.Maybe (maybe)
import Data.Nullable (Nullable, toMaybe) import Data.Nullable (Nullable, toMaybe)
import DOM.Simple as DOM
import Effect (Effect) import Effect (Effect)
import Effect.Uncurried (EffectFn2, runEffectFn2) import Effect.Uncurried (EffectFn2, runEffectFn2)
import Reactix as R import Reactix as R
import Record as Record
import Gargantext.Prelude
type PopoverRef = R.Ref (Nullable DOM.Element) type PopoverRef = R.Ref (Nullable DOM.Element)
...@@ -22,8 +23,11 @@ type Props = ...@@ -22,8 +23,11 @@ type Props =
foreign import popoverCpt :: R.Component Props foreign import popoverCpt :: R.Component Props
-- | https://github.com/vaheqelyan/react-awesome-popover
popover :: Record Props -> Array R.Element -> R.Element popover :: Record Props -> Array R.Element -> R.Element
popover = R.rawCreateElement popoverCpt popover props children = R.rawCreateElement popoverCpt props' children
where
props' = Record.merge props { className: "awesome-popover" }
foreign import _setState :: forall a. EffectFn2 DOM.Element a Unit foreign import _setState :: forall a. EffectFn2 DOM.Element a Unit
......
@import "./base/_reset.scss"
@import "./base/_general.scss"
@import "./base/_form.scss" @import "./base/_form.scss"
@import "./base/_layout.scss" @import "./base/_layout.scss"
@import "./base/_nav.scss" @import "./base/_nav.scss"
@import "./base/_typography.scss" @import "./base/_typography.scss"
@import "./base/_animations.scss" @import "./base/_animations.scss"
@import "./base/_bootstrap.scss"
@import "./base/_placeholder.scss" @import "./base/_placeholder.scss"
@import "./base/_utilities.scss"
@import "./base/_range_slider.sass"
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
@import "./_legacy/_tree" @import "./_legacy/_tree"
@import "./_legacy/_code_editor" @import "./_legacy/_code_editor"
@import "./_legacy/_styles" @import "./_legacy/_styles"
@import "./_legacy/_range_slider"
@import "./_legacy/_annotation" @import "./_legacy/_annotation"
@import "./_legacy/_folder_view" @import "./_legacy/_folder_view"
@import "./_legacy/_phylo" @import "./_legacy/_phylo"
This diff is collapsed.
...@@ -92,9 +92,6 @@ li#rename ...@@ -92,9 +92,6 @@ li#rename
overflow: visible overflow: visible
height: auto height: auto
#graph-tree
.tree
margin-top: 27px
.nopadding .nopadding
padding: 0 !important padding: 0 !important
......
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
&__tree-switcher &__tree-switcher
margin-right: space-x(1) margin-right: space-x(1)
&__tree-switcher
$fixed-width: 136px
width: $fixed-width
// add hovering effect // add hovering effect
&.navbar-dark .navbar-text:hover &.navbar-dark .navbar-text:hover
color: $navbar-dark-hover-color color: $navbar-dark-hover-color
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
/* Grid constants */ /* Grid constants */
$topbar-height: 56px; // ~ unworthy empirical value (@TODO topbar height calculation)
$graph-margin: 16px; $graph-margin: 16px;
$layout-height: calc(100vh - #{ $topbar-height} ); $layout-height: calc(100vh - #{ $topbar-height} );
...@@ -38,16 +37,6 @@ $left-column-width: 10%; ...@@ -38,16 +37,6 @@ $left-column-width: 10%;
$center-column-width: 85%; $center-column-width: 85%;
$right-column-width: 5%; $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 */ /* Colors */
$graph-background-color: $body-bg; $graph-background-color: $body-bg;
...@@ -448,34 +437,33 @@ $decreasing-color: #11638F; ...@@ -448,34 +437,33 @@ $decreasing-color: #11638F;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
.phylo-topbar { .phylo-topbar {
$margin: space-x(0.5);
$fixed-button-width: 136px; $fixed-button-width: 136px;
@include aside-topbar(); @include aside-topbar();
padding-left: $margin; padding-left: $topbar-item-margin;
padding-right: $margin; padding-right: $topbar-item-margin;
display: flex; display: flex;
&__toolbar, &__toolbar,
&__sidebar { &__sidebar {
width: $fixed-button-width; width: $topbar-fixed-button-width;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__source { &__source {
width: $topbar-input-width; width: $topbar-input-width;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__autocomplete { &__autocomplete {
display: flex; display: flex;
width: $topbar-input-width; width: $topbar-input-width;
position: relative; position: relative;
margin-left: $margin; margin-left: $topbar-item-margin;
margin-right: $margin; margin-right: $topbar-item-margin;
} }
&__suggestion { &__suggestion {
...@@ -506,60 +494,11 @@ $decreasing-color: #11638F; ...@@ -506,60 +494,11 @@ $decreasing-color: #11638F;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
.phylo-sidebar { .phylo-sidebar {
$teaser-height: 16px; @include sidebar;
background-color: $body-bg;
height: 100%;
// avoiding ugly scrollbar
scrollbar-width: none;
overflow-y: scroll;
overflow-x: visible;
&::-webkit-scrollbar {
display: none;
}
// adjust nav menu gutter (@TODO: generic?)
&__menu .nav-item {
&:first-child {
margin-left: space-x(2);
}
&:last-child {
margin-right: space-x(2);
}
}
// UX best pratice: when a lengthy column is overflowy hidden
// (with a scroll), a teaser UI element shows to the user that a scroll
// is possible
&__top-teaser {
@include top-teaser;
z-index: z-index('phylo-sidebar', 'teaser');
pointer-events: none;
position: sticky;
top: 0;
height: $teaser-height;
width: 100%;
}
&__bottom-teaser {
@include bottom-teaser;
z-index: z-index('phylo-sidebar', 'teaser');
pointer-events: none;
position: sticky;
bottom: 0;
height: $teaser-height;
width: 100%;
}
} }
.phylo-details-tab { .phylo-details-tab {
$margin-x: $tab-margin-x; $margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2); $margin-y: space-x(2);
&__counter { &__counter {
...@@ -585,7 +524,7 @@ $decreasing-color: #11638F; ...@@ -585,7 +524,7 @@ $decreasing-color: #11638F;
} }
.phylo-selection-tab { .phylo-selection-tab {
$margin-x: $tab-margin-x; $margin-x: $sidebar-tab-margin-x;
$margin-y: space-x(2); $margin-y: space-x(2);
&__highlight { &__highlight {
...@@ -609,7 +548,6 @@ $decreasing-color: #11638F; ...@@ -609,7 +548,6 @@ $decreasing-color: #11638F;
&__item { &__item {
white-space: normal; white-space: normal;
word-break: break-word; word-break: break-word;
// remove "_reboot.scss" line height
line-height: initial; line-height: initial;
&:not(:last-child) { &:not(:last-child) {
......
.range
width: 400px
/* some space for the right knob */
padding-right: 30px
.range-slider
position: relative
width: 85%
.scale
position: absolute
width: 100%
height: 3px
margin-top: 2px
background-color: #d8d8d8
.scale-sel
position: absolute
background-color: rgb(39, 196, 112)
width: 100%
height: 7px
.knob
position: absolute
cursor: pointer
-moz-user-select: none
-webkit-user-select: none
-ms-user-select: none
user-select: none
-o-user-select: none
margin-top: -4px
z-index: 1
box-shadow: 1px 1px 3px grey
.button
margin-top: -3px
background: #eee
width: 30px
height: 20px
.range-simple
input
width: 85%
...@@ -69,12 +69,6 @@ ...@@ -69,12 +69,6 @@
/* */ /* */
.btn-primary
color: white
background-color: #005a9aff
border-color: black
.frame .frame
height: 100vh height: 100vh
iframe iframe
......
This diff is collapsed.
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
} }
/// Mixins /// Mixins
///-------------------------- ///--------------------------
...@@ -122,3 +121,13 @@ ...@@ -122,3 +121,13 @@
bottom: $value; bottom: $value;
left: $value; left: $value;
} }
/// Hidden vertical scrollbar
@mixin hidden-scrollbar() {
scrollbar-width: none;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
}
This diff is collapsed.
...@@ -46,3 +46,7 @@ ...@@ -46,3 +46,7 @@
from { transform: rotate(0deg); } from { transform: rotate(0deg); }
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
@keyframes pulse {
from { transform: scale(1); }
to { transform: scale(1.1); }
}
/* Bootstrap 4.x Generic Custom Styles */
// originally added to "table" tag on "_reboot.scss"
.table {
border-collapse: collapse;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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