Commit 89aa1f16 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-hal-notebook

parents 6db7d12b 7cad1696
Pipeline #2706 failed with stage
in 0 seconds
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -5,8 +5,8 @@ import ...@@ -5,8 +5,8 @@ import
pkgs.fetchFromGitHub { pkgs.fetchFromGitHub {
owner = "justinwoo"; owner = "justinwoo";
repo = "easy-purescript-nix"; repo = "easy-purescript-nix";
rev = "678070816270726e2f428da873fe3f2736201f42"; rev = "9c5ffd3e54c41dece66ed84f8f23970a4f1f3883";
sha256 = "JEabdJ+3cZEYDVnzgMH/YFsaGtIBiCFcgvVO9XRgiY4="; sha256 = "8erFzbiRJYqPgJHuQwhgBPltQeaWeAZom/5X3lyUAcc=";
} }
) { ) {
inherit pkgs; inherit pkgs;
......
import ( import (
builtins.fetchTarball { builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/21.05.tar.gz"; url = "https://github.com/NixOS/nixpkgs/archive/21.11.tar.gz";
sha256 = "1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36"; sha256 = "162dywda2dvfj1248afxc45kcrg83appjd0nmdb541hl7rnncf02";
} }
) )
...@@ -122,7 +122,7 @@ ...@@ -122,7 +122,7 @@
name = "colors"; name = "colors";
version = "v6.0.0"; version = "v6.0.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/sharkdp/purescript-colors.git"; url = "https://github.com/purescript-contrib/purescript-colors.git";
rev = "v6.0.0"; rev = "v6.0.0";
sha256 = "05gdjx8xhv7xxm1prrrc2brxjn1gi19qf1004syk8qx37slrjf87"; sha256 = "05gdjx8xhv7xxm1prrrc2brxjn1gi19qf1004syk8qx37slrjf87";
}; };
...@@ -336,11 +336,11 @@ ...@@ -336,11 +336,11 @@
}; };
"free" = { "free" = {
name = "free"; name = "free";
version = "v6.0.1"; version = "v6.2.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-free.git"; url = "https://github.com/purescript/purescript-free.git";
rev = "v6.0.1"; rev = "v6.2.0";
sha256 = "0kpq83qjfjzf1l2f1cnnx36kjwnm5czgjyh2imwp3bna8js6sk39"; sha256 = "10zsw49wzlzz78882b3grl19gpca5llpdk3ph608075h0ygk3q3k";
}; };
}; };
"functions" = { "functions" = {
...@@ -516,11 +516,11 @@ ...@@ -516,11 +516,11 @@
}; };
"milkis" = { "milkis" = {
name = "milkis"; name = "milkis";
version = "v7.4.0"; version = "v7.5.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/justinwoo/purescript-milkis.git"; url = "https://github.com/justinwoo/purescript-milkis.git";
rev = "v7.4.0"; rev = "v7.5.0";
sha256 = "10ahz4idcb3qwys7x15k3za5gkw9zbk15cdmcqsi8lfh6fg2w2db"; sha256 = "01kaqzndxq2agi6xr1k5gsn1pzvd3xxn8v1s1gl87kmiic94w6vc";
}; };
}; };
"mmorph" = { "mmorph" = {
...@@ -552,11 +552,11 @@ ...@@ -552,11 +552,11 @@
}; };
"node-fs" = { "node-fs" = {
name = "node-fs"; name = "node-fs";
version = "v6.1.0"; version = "v6.2.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript-node/purescript-node-fs.git"; url = "https://github.com/purescript-node/purescript-node-fs.git";
rev = "v6.1.0"; rev = "v6.2.0";
sha256 = "1w97m2afn7yn757niknkbk7w6nyg4n5dabxr7gzfz368z1nkf45s"; sha256 = "1b89sxr6asxvgx59myhfbahiiz1z6sg2qfrm9bqd46h93ai8bhn1";
}; };
}; };
"node-path" = { "node-path" = {
...@@ -579,11 +579,11 @@ ...@@ -579,11 +579,11 @@
}; };
"nonempty" = { "nonempty" = {
name = "nonempty"; name = "nonempty";
version = "v6.0.0"; version = "v6.1.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-nonempty.git"; url = "https://github.com/purescript/purescript-nonempty.git";
rev = "v6.0.0"; rev = "v6.1.0";
sha256 = "0azk1jrpqnjf2i97lcp63wcm31c4hddklp1mfmdan27zap3zqyjm"; sha256 = "0hhhw5x5xvs2bd9373gklja1545glnzi1xc2sj16kkznnayrmvsn";
}; };
}; };
"now" = { "now" = {
...@@ -615,11 +615,11 @@ ...@@ -615,11 +615,11 @@
}; };
"ordered-collections" = { "ordered-collections" = {
name = "ordered-collections"; name = "ordered-collections";
version = "v2.0.1"; version = "v2.0.2";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-ordered-collections.git"; url = "https://github.com/purescript/purescript-ordered-collections.git";
rev = "v2.0.1"; rev = "v2.0.2";
sha256 = "1p592g0s07c56639y71782af0zz5cndpjxd5w9n41hdszsz1b86h"; sha256 = "0g57043ylj3kldkm5vn233yd6hiamryhdfh72cxx9h3mn0ra8ghd";
}; };
}; };
"orders" = { "orders" = {
...@@ -642,11 +642,11 @@ ...@@ -642,11 +642,11 @@
}; };
"parsing" = { "parsing" = {
name = "parsing"; name = "parsing";
version = "v6.0.2"; version = "v8.4.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-parsing.git"; url = "https://github.com/purescript-contrib/purescript-parsing.git";
rev = "v6.0.2"; rev = "v8.4.0";
sha256 = "0lhri5bfk4j6dgzi4d5gvmd628hjs9jgyky910ylj6qzalw9fj56"; sha256 = "1rq57cwfx7rx3h3hdc4m8lv90724i5gf2pak9ljj0fcnw96gsaa1";
}; };
}; };
"partial" = { "partial" = {
...@@ -912,11 +912,11 @@ ...@@ -912,11 +912,11 @@
}; };
"string-parsers" = { "string-parsers" = {
name = "string-parsers"; name = "string-parsers";
version = "v6.0.1"; version = "v7.0.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-string-parsers.git"; url = "https://github.com/purescript-contrib/purescript-string-parsers.git";
rev = "v6.0.1"; rev = "v7.0.0";
sha256 = "143a2s56kbx3i0xi5wfqp28znr0hdydy902jla236i7kal5y098m"; sha256 = "0kjqx6f8jgcbaf0v401hrdqw61zzvp8my9ik8cn55njn6i3mx74w";
}; };
}; };
"strings" = { "strings" = {
...@@ -1056,11 +1056,11 @@ ...@@ -1056,11 +1056,11 @@
}; };
"unicode" = { "unicode" = {
name = "unicode"; name = "unicode";
version = "v5.0.0"; version = "v5.0.1";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-unicode.git"; url = "https://github.com/purescript-contrib/purescript-unicode.git";
rev = "v5.0.0"; rev = "v5.0.1";
sha256 = "0sqvgl3il2rl3zxkbzsqb19wib108zvyw73jxiavpfdm6hdmnxvc"; sha256 = "0xh9wwyrl9nsw3h3wzalc1gaph39drj0i6k648cf9bnbb96nxa4z";
}; };
}; };
"unsafe-coerce" = { "unsafe-coerce" = {
...@@ -1092,11 +1092,11 @@ ...@@ -1092,11 +1092,11 @@
}; };
"variant" = { "variant" = {
name = "variant"; name = "variant";
version = "v7.0.3"; version = "v7.1.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/natefaubion/purescript-variant.git"; url = "https://github.com/natefaubion/purescript-variant.git";
rev = "v7.0.3"; rev = "v7.1.0";
sha256 = "1q2pky3gf177ihy2zjzqvp1cj18ycaki9vm4ghw18p7hf256lqmc"; sha256 = "064aijlcphyg5mjhxx4acwjlnh7ha3v033zzan31rhkqi6m4r3da";
}; };
}; };
"versions" = { "versions" = {
...@@ -1137,11 +1137,11 @@ ...@@ -1137,11 +1137,11 @@
}; };
"web-html" = { "web-html" = {
name = "web-html"; name = "web-html";
version = "v3.1.0"; version = "v3.2.0";
src = pkgs.fetchgit { src = pkgs.fetchgit {
url = "https://github.com/purescript-web/purescript-web-html.git"; url = "https://github.com/purescript-web/purescript-web-html.git";
rev = "v3.1.0"; rev = "v3.2.0";
sha256 = "007anmqqifrjnpfa4xf7qa8xnpbhvcxqdraj9lnhizwq65vx53sn"; sha256 = "1ds26vwyba0chhpa09m938brw9q8pxjk6z1n3d4nc30hvdkrjnbh";
}; };
}; };
"web-storage" = { "web-storage" = {
......
{ {
"name": "Gargantext", "name": "Gargantext",
"version": "0.0.5.8.3", "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",
......
let upstream = let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.14.5-20220110/packages.dhall sha256:8dbf71bfc6c7a11043619eebe90ff85f7d884541048aa8cc48eef1ee781cbc0e https://github.com/purescript/package-sets/releases/download/psc-0.14.7-20220404/packages.dhall sha256:75d0f0719f32456e6bdc3efd41cfc64785655d2b751e3d080bd849033ed053f2
let overrides = let overrides =
{ globals = { globals =
...@@ -7,6 +7,63 @@ let overrides = ...@@ -7,6 +7,63 @@ let overrides =
, repo = "https://github.com/purescript/purescript-globals" , repo = "https://github.com/purescript/purescript-globals"
, version = "v4.1.0" , version = "v4.1.0"
} }
-- TODO Remove graphql-client when
-- https://github.com/OxfordAbstracts/purescript-graphql-client/issues/73
-- is merged
, graphql-client =
{ dependencies =
[ "aff"
, "aff-promise"
, "affjax"
, "argonaut-codecs"
, "argonaut-core"
, "arrays"
, "bifunctors"
, "control"
, "datetime"
, "effect"
, "either"
, "enums"
, "exceptions"
, "foldable-traversable"
, "foreign"
, "foreign-generic"
, "foreign-object"
, "functions"
, "halogen-subscriptions"
, "heterogeneous"
, "http-methods"
, "integers"
, "lists"
, "maybe"
, "media-types"
, "newtype"
, "node-buffer"
, "node-fs"
, "nullable"
, "numbers"
, "ordered-collections"
, "parsing"
, "prelude"
, "profunctor"
, "profunctor-lenses"
, "psci-support"
, "quickcheck"
, "record"
, "spec"
, "spec-discovery"
, "string-parsers"
, "strings"
, "strings-extra"
, "transformers"
, "tuples"
, "typelevel-prelude"
, "unicode"
]
, repo = "https://github.com/OxfordAbstracts/purescript-graphql-client.git"
, version = "update-package-set-#73"
--, version = "v7.0.0"
}
, smolder = , smolder =
{ dependencies = { dependencies =
[ "bifunctors" [ "bifunctors"
......
...@@ -105,7 +105,7 @@ let ...@@ -105,7 +105,7 @@ let
in in
pkgs.mkShell { pkgs.mkShell {
buildInputs = [ buildInputs = [
easy-ps.purs-0_14_5 easy-ps.purs-0_14_7
easy-ps.psc-package easy-ps.psc-package
easy-ps.dhall-json-simple easy-ps.dhall-json-simple
easy-ps.zephyr easy-ps.zephyr
......
...@@ -5,9 +5,11 @@ module Gargantext.Components.Bootstrap.Cloak ...@@ -5,9 +5,11 @@ module Gargantext.Components.Bootstrap.Cloak
import Gargantext.Prelude import Gargantext.Prelude
import Data.Foldable (elem) import Data.Foldable (elem)
import Data.Maybe (Maybe, fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Timer (setTimeout) import Effect.Timer (setTimeout)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Toestand as T import Toestand as T
...@@ -17,10 +19,20 @@ type Props = ...@@ -17,10 +19,20 @@ type Props =
( defaultSlot :: R.Element ( defaultSlot :: R.Element
, cloakSlot :: R.Element , cloakSlot :: R.Element
, isDisplayed :: Boolean , isDisplayed :: Boolean
, idlingPhaseDuration :: Maybe Int -- Milliseconds | Options
)
type Options =
( idlingPhaseDuration :: Maybe Int -- Milliseconds
, sustainingPhaseDuration :: Maybe Int -- Milliseconds , sustainingPhaseDuration :: Maybe Int -- Milliseconds
) )
options :: Record Options
options =
{ idlingPhaseDuration : Nothing
, sustainingPhaseDuration : Nothing
}
-- | Abstract component type easing the transition display between a content -- | Abstract component type easing the transition display between a content
-- | component and transitional (or cloak) component -- | component and transitional (or cloak) component
-- | -- |
...@@ -94,8 +106,8 @@ type Props = ...@@ -94,8 +106,8 @@ type Props =
-- | , sustainingPhaseDuration : Just 400 -- | , sustainingPhaseDuration : Just 400
-- | } -- | }
-- | ``` -- | ```
cloak :: R2.Leaf Props cloak :: forall r. R2.OptLeaf Options Props r
cloak = R2.leaf component cloak = R2.optLeaf component options
cname :: String cname :: String
cname = "b-cloak" cname = "b-cloak"
...@@ -107,47 +119,50 @@ component = R.hooksComponent cname cpt where ...@@ -107,47 +119,50 @@ component = R.hooksComponent cname cpt where
phase /\ phaseBox <- R2.useBox' (Idle :: Phase) phase /\ phaseBox <- R2.useBox' (Idle :: Phase)
-- Computed -- Computed
canCloakBeDisplayed <- pure $ elem phase [ Sustain, Wait ] let
canContentBeDisplayed <- pure $ elem phase [ Display ] canCloakBeDisplayed = elem phase [ Sustain, Wait ]
canContentBeDisplayed = elem phase [ Display ]
-- Behaviors
let
execDisplayingPhaseOr :: (Unit -> Effect Unit) -> Effect Unit
execDisplayingPhaseOr thunk =
if props.isDisplayed
then T.write_ Display phaseBox
else thunk unit
-- @executeDisplayingPhase execWaitingPhase :: Unit -> Effect Unit
execDisplayingPhase <- pure $ const $ execWaitingPhase _ = execDisplayingPhaseOr $ const $
T.write_ Display phaseBox
-- Helpers T.write_ Wait phaseBox
execDisplayingPhaseOr <- pure $ \fn ->
if props.isDisplayed execSustainingPhase :: Unit -> Effect Unit
then execDisplayingPhase unit execSustainingPhase _ = execDisplayingPhaseOr $ const $
else fn
-- @executeWaitingPhase T.write_ Sustain phaseBox
execWaitingPhase <- pure $ const $ execDisplayingPhaseOr $
T.write_ Wait phaseBox
-- @executeSustainingPhase <* setTimeout
execSustainingPhase <- pure $ const $ execDisplayingPhaseOr $ (fromMaybe 0 props.sustainingPhaseDuration)
T.write_ Sustain phaseBox (execWaitingPhase unit)
<* setTimeout execIdlingPhase :: Unit -> Effect Unit
(fromMaybe 0 props.sustainingPhaseDuration) execIdlingPhase _ = execDisplayingPhaseOr $ const $
(execWaitingPhase unit)
-- @executeIdlingPhase T.write_ Idle phaseBox
execIdlingPhase <- pure $ const $ execDisplayingPhaseOr $
T.write_ Idle phaseBox
<* setTimeout <* setTimeout
(fromMaybe 0 props.idlingPhaseDuration) (fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit) (execSustainingPhase unit)
-- Effects -- Effects
R.useEffectOnce' $ execIdlingPhase unit useFirstEffect' $ execIdlingPhase unit
R.useEffect1' props.isDisplayed $ R.useEffect2' props.isDisplayed phase $
if (props.isDisplayed && phase == Wait) if (props.isDisplayed && phase == Wait)
then execDisplayingPhase unit then T.write_ Display phaseBox
else pure unit else pure unit
-- Render -- Render
......
...@@ -37,15 +37,16 @@ component :: R.Component Props ...@@ -37,15 +37,16 @@ component :: R.Component Props
component = R.hooksComponent componentName cpt where component = R.hooksComponent componentName cpt where
cpt props children = do cpt props 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
-- Bootstrap specific classNames , componentName
, bootstrapName -- Bootstrap specific classNames
, bootstrapName <> "-" <> show props.variant , bootstrapName
] , bootstrapName <> "-" <> show props.variant
]
-- Render -- Render
pure $ pure $
......
module Gargantext.Components.Bootstrap.Div (div', div_) where
import Reactix as R
import Reactix.DOM.HTML as H
-- | Shorthand for using HTML <div> without writing its text node
div' :: forall r. Record r -> String -> R.Element
div' props content = H.div props [ H.text content ]
-- | Shorthand for using HTML <div> without writing its text node nor props
div_ :: String -> R.Element
div_ content = H.div {} [ H.text content ]
...@@ -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
] ]
module Gargantext.Components.Bootstrap.ProgressBar(progressBar) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Gargantext.Components.Bootstrap.Types (Variant(..))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( value :: Number
| Options
)
type Options =
( className :: String
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, variant : Primary
}
-- | Structural Component for the Bootsrap "Progress Bar"
-- |
-- | https://getbootstrap.com/docs/4.6/components/progress/
progressBar :: forall r. R2.OptLeaf Options Props r
progressBar = R2.optLeaf component options
componentName :: String
componentName = "b-progress-bar"
bootstrapName :: String
bootstrapName = "progress"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props _ = do
-- Computed
let
className = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, bootstrapName
]
-- Render
pure $
H.div
{ className }
[
H.div
{ className: intercalate " "
[ "progress-bar"
, "bg-" <> show props.variant
]
, style: { width: (show props.value) <> "%" }
, role: "progress-bar"
, "aria-valuenow": show $ props.value
, "aria-valuemin": "0"
, "aria-valuemax": "100"
}
[]
]
...@@ -5,12 +5,30 @@ module Gargantext.Components.Bootstrap ...@@ -5,12 +5,30 @@ module Gargantext.Components.Bootstrap
import Gargantext.Components.Bootstrap.BaseModal(baseModal) as Exports import Gargantext.Components.Bootstrap.BaseModal(baseModal) as Exports
import Gargantext.Components.Bootstrap.Button(button) as Exports import Gargantext.Components.Bootstrap.Button(button) as Exports
import Gargantext.Components.Bootstrap.Caveat(caveat) as Exports import Gargantext.Components.Bootstrap.Caveat(caveat) as Exports
import Gargantext.Components.Bootstrap.Cloak(cloak) as Exports import Gargantext.Components.Bootstrap.Cloak (cloak) as Exports
import Gargantext.Components.Bootstrap.Div(div', div_) as Exports
import Gargantext.Components.Bootstrap.Fieldset(fieldset) as Exports import Gargantext.Components.Bootstrap.Fieldset(fieldset) as Exports
import Gargantext.Components.Bootstrap.FormInput(formInput) as Exports import Gargantext.Components.Bootstrap.FormInput(formInput) as Exports
import Gargantext.Components.Bootstrap.FormSelect(formSelect, formSelect') as Exports import Gargantext.Components.Bootstrap.FormSelect(formSelect, formSelect') as Exports
import Gargantext.Components.Bootstrap.FormTextarea(formTextarea) as Exports import Gargantext.Components.Bootstrap.FormTextarea(formTextarea) as Exports
import Gargantext.Components.Bootstrap.Icon(icon) as Exports import Gargantext.Components.Bootstrap.Icon(icon) as Exports
import Gargantext.Components.Bootstrap.Label(label', label_) as Exports import Gargantext.Components.Bootstrap.IconButton(iconButton) 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.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(
div', div_
, h1', h1_
, h2', h2_
, h3', h3_
, h4', h4_
, h5', h5_
, h6', h6_
, span', span_
, li', li_
, b', b_
, code', code_
, label', label_
) as Exports
module Gargantext.Components.Bootstrap.Tooltip
( tooltip
, TooltipBindingProps, tooltipBind, tooltipBind'
, tooltipContainer
) where
import Gargantext.Prelude
import ConvertableOptions as CO
import Data.Symbol (SProxy(..))
import Data.UUID as UUID
import Gargantext.Components.Bootstrap.Types (TooltipEffect(..), TooltipPosition(..), Variant(..))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
import Record.Extra as RX
foreign import reactTooltipCpt :: R.Component Props
type Props =
( id :: String
| Options
)
type Options =
( effect :: TooltipEffect
, variant :: Variant
, delayHide :: Int
, delayShow :: Int
, className :: String
, position :: TooltipPosition
)
options :: Record Options
options =
{ effect : SolidEffect
, variant : Dark
, delayHide : 0
, delayShow : 0
, className : ""
, position : AutomaticPosition
}
-- | Adapter Component for React Tooltip
-- |
-- |
-- | @XXX: tooltip position not working
-- | @link https://github.com/wwayne/react-tooltip/issues/747
-- |
-- |
-- | https://github.com/wwayne/react-tooltip
tooltip :: forall provided.
CO.Defaults (Record Options) (Record provided) (Record Props)
=> Record provided
-> Array R.Element
-> R.Element
tooltip props = R.rawCreateElement reactTooltipCpt props''
where
props' = CO.defaults options props
props'' = props'
# Record.set
(SProxy :: SProxy "effect")
(show props'.effect)
>>> Record.set
(SProxy :: SProxy "variant")
(show props'.variant)
>>> Record.rename
(SProxy :: SProxy "variant")
(SProxy :: SProxy "type")
>>> Record.set
(SProxy :: SProxy "position")
(show props'.position)
>>> Record.rename
(SProxy :: SProxy "position")
(SProxy :: SProxy "place")
-------------------------------------------------------------
type TooltipBindingProps =
( "data-tip" :: Boolean
, "data-for" :: String
)
-- | Bind a Component props to an existing <tooltip>
tooltipBind :: String -> Record TooltipBindingProps
tooltipBind =
{ "data-for": _
, "data-tip": true
}
-- | Derived empty state
tooltipBind' :: Record TooltipBindingProps
tooltipBind' =
{ "data-for": ""
, "data-tip": false
}
-------------------------------------------------------------
type ContainerProps =
( defaultSlot :: R.Element
, tooltipSlot :: R.Element
| Options
)
tooltipContainer :: forall r. R2.OptLeaf Options ContainerProps r
tooltipContainer = R2.optLeaf tooltipContainerCpt options
tooltipContainerCpt :: R.Memo ContainerProps
tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt where
cpt props@{ tooltipSlot
, defaultSlot
} _
= R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- Computed
let
tooltipProps = Record.merge
(RX.pick props :: Record Options)
{ id: uuid }
pure $
R2.fragmentWithKey uuid
[
tooltip
tooltipProps
[ tooltipSlot ]
,
H.span
(tooltipBind uuid)
[ defaultSlot ]
]
...@@ -71,8 +71,9 @@ component = R.hooksComponent componentName cpt where ...@@ -71,8 +71,9 @@ component = R.hooksComponent componentName cpt where
bootstrapName <> "-block" $ bootstrapName <> "-block" $
mempty mempty
] ]
-- @click -- Behaviors
click <- pure $ \event -> onClick status callback event let
click = onClick status callback
-- Render -- Render
pure $ pure $
......
module Gargantext.Components.Bootstrap.IconButton (iconButton) where
import Gargantext.Prelude
import Data.Foldable (elem, intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..))
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import React.SyntheticEvent as SE
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( name :: String
, callback :: Unit -> Effect Unit
| Options
)
type Options =
( className :: String
, status :: ComponentStatus
, title :: String
, overlay :: Boolean
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, status : Enabled
, title : ""
, overlay : false
, variant : Dark
}
-- | Structural Component for a simple Glyphicon element with call-to-action
-- |
-- | https://forkaweso.me/Fork-Awesome/icons/
iconButton :: forall r. R2.OptLeaf Options Props r
iconButton = R2.optLeaf component options
componentName :: String
componentName = "b-icon-button"
bootstrapName :: String
bootstrapName = "fa"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, name } _ = do
-- Computed
let
wrapperClassName = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
, componentName <> "--" <> show status
, componentName <> "--" <> show props.variant
, props.overlay ?
componentName <> "--overlay" $
""
]
contentClassName = intercalate " "
-- Bootstrap specific classNames
[ bootstrapName
, bootstrapName <> "-" <> name
]
-- Behaviors
let
click = onClick status callback
-- Render
pure $
H.span
{ className: wrapperClassName
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
}
[
H.i
{ title: props.title
, className: contentClassName
}
[]
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick :: forall event.
ComponentStatus
-> (Unit -> Effect Unit)
-> SE.SyntheticEvent_ event
-> Effect Unit
onClick status callback event = do
SE.preventDefault event
if status == Enabled
then callback unit
else pure unit
module Gargantext.Components.Bootstrap.Label
( label'
, label_
) where
import Reactix as R
import Reactix.DOM.HTML as H
-- | Shorthand for using HTML <label> without writing its text node
label' :: forall r. Record r -> String -> R.Element
label' props content = H.label props [ H.text content ]
-- | Shorthand for using HTML <label> without writing its text node nor props
label_ :: String -> R.Element
label_ content = H.label {} [ H.text content ]
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
]
]
module Gargantext.Components.Bootstrap.Shortcut
( div', div_
, h1', h1_
, h2', h2_
, h3', h3_
, h4', h4_
, h5', h5_
, h6', h6_
, span', span_
, li', li_
, b', b_
, code', code_
, label', label_
) where
import Reactix as R
import Reactix.DOM.HTML as H
-- | Shorthand for using HTML <div> without writing its text node
div' :: forall r. Record r -> String -> R.Element
div' props content = H.div props [ H.text content ]
-- | Shorthand for using HTML <div> without writing its text node nor props
div_ :: String -> R.Element
div_ content = H.div {} [ H.text content ]
-- | Shorthand for using HTML <h1> without writing its text node
h1' :: forall r. Record r -> String -> R.Element
h1' props content = H.h1 props [ H.text content ]
-- | Shorthand for using HTML <h1> without writing its text node nor props
h1_ :: String -> R.Element
h1_ content = H.h1 {} [ H.text content ]
-- | Shorthand for using HTML <h2> without writing its text node
h2' :: forall r. Record r -> String -> R.Element
h2' props content = H.h2 props [ H.text content ]
-- | Shorthand for using HTML <h2> without writing its text node nor props
h2_ :: String -> R.Element
h2_ content = H.h2 {} [ H.text content ]
-- | Shorthand for using HTML <h3> without writing its text node
h3' :: forall r. Record r -> String -> R.Element
h3' props content = H.h3 props [ H.text content ]
-- | Shorthand for using HTML <h3> without writing its text node nor props
h3_ :: String -> R.Element
h3_ content = H.h3 {} [ H.text content ]
-- | Shorthand for using HTML <h4> without writing its text node
h4' :: forall r. Record r -> String -> R.Element
h4' props content = H.h4 props [ H.text content ]
-- | Shorthand for using HTML <h4> without writing its text node nor props
h4_ :: String -> R.Element
h4_ content = H.h4 {} [ H.text content ]
-- | Shorthand for using HTML <h5> without writing its text node
h5' :: forall r. Record r -> String -> R.Element
h5' props content = H.h5 props [ H.text content ]
-- | Shorthand for using HTML <h5> without writing its text node nor props
h5_ :: String -> R.Element
h5_ content = H.h5 {} [ H.text content ]
-- | Shorthand for using HTML <h6> without writing its text node
h6' :: forall r. Record r -> String -> R.Element
h6' props content = H.h6 props [ H.text content ]
-- | Shorthand for using HTML <h6> without writing its text node nor props
h6_ :: String -> R.Element
h6_ content = H.h6 {} [ H.text content ]
-- | Shorthand for using HTML <span> without writing its text node
span' :: forall r. Record r -> String -> R.Element
span' props content = H.span props [ H.text content ]
-- | Shorthand for using HTML <span> without writing its text node nor props
span_ :: String -> R.Element
span_ content = H.span {} [ H.text content ]
-- | Shorthand for using HTML <li> without writing its text node
li' :: forall r. Record r -> String -> R.Element
li' props content = H.li props [ H.text content ]
-- | Shorthand for using HTML <li> without writing its text node nor props
li_ :: String -> R.Element
li_ content = H.li {} [ H.text content ]
-- | Shorthand for using HTML <b> without writing its text node
b' :: forall r. Record r -> String -> R.Element
b' props content = H.b props [ H.text content ]
-- | Shorthand for using HTML <b> without writing its text node nor props
b_ :: String -> R.Element
b_ content = H.b {} [ H.text content ]
-- | Shorthand for using HTML <code> without writing its text node
code' :: forall r. Record r -> String -> R.Element
code' props content = H.code props [ H.text content ]
-- | Shorthand for using HTML <code> without writing its text node nor props
code_ :: String -> R.Element
code_ content = H.code {} [ H.text content ]
-- | Shorthand for using HTML <label> without writing its text node
label' :: forall r. Record r -> String -> R.Element
label' props content = H.label props [ H.text content ]
-- | Shorthand for using HTML <label> without writing its text node nor props
label_ :: String -> R.Element
label_ content = H.label {} [ H.text content ]
...@@ -3,6 +3,8 @@ module Gargantext.Components.Bootstrap.Types ...@@ -3,6 +3,8 @@ module Gargantext.Components.Bootstrap.Types
, Variant(..), ButtonVariant(..) , Variant(..), ButtonVariant(..)
, Sizing(..) , Sizing(..)
, SpinnerTheme(..) , SpinnerTheme(..)
, TooltipEffect(..), TooltipPosition(..)
, Position(..)
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -115,3 +117,46 @@ derive instance Eq SpinnerTheme ...@@ -115,3 +117,46 @@ derive instance Eq SpinnerTheme
instance Show SpinnerTheme where instance Show SpinnerTheme where
show BorderTheme = "border" show BorderTheme = "border"
show GrowTheme = "grow" show GrowTheme = "grow"
----------------------------------------------------------------------
-- | Effect used on React Tooltip
-- |
-- | https://github.com/wwayne/react-tooltip#options
data TooltipEffect
= FloatEffect
| SolidEffect
----------------------------------------------------------------------
derive instance Generic TooltipEffect _
derive instance Eq TooltipEffect
instance Show TooltipEffect where
show FloatEffect = "float"
show SolidEffect = "solid"
----------------------------------------------------------------------
-- | Generic enum type used by various libraries and components
data Position
= Top
| Right
| Left
| Bottom
derive instance Generic Position _
derive instance Eq Position
instance Show Position where show = kebabCase <<< genericShow
----------------------------------------------------------------------
-- | Position used on React Tooltip
-- |
-- | -- | https://github.com/wwayne/react-tooltip#options
data TooltipPosition
= TooltipPosition Position
| AutomaticPosition
derive instance Generic TooltipPosition _
derive instance Eq TooltipPosition
instance Show TooltipPosition where
show (TooltipPosition a) = (kebabCase <<< genericShow) a
show AutomaticPosition = ""
...@@ -185,7 +185,7 @@ docViewCpt = here.component "docView" cpt where ...@@ -185,7 +185,7 @@ docViewCpt = here.component "docView" cpt where
R.fragment R.fragment
[ [
H.div { className: "doc-table-doc-view container1" } H.div { className: "doc-table-doc-view" }
[ R2.row [ R2.row
[ chart [ chart
, if showSearch then searchBar { query } [] else H.div {} [] , if showSearch then searchBar { query } [] else H.div {} []
......
...@@ -131,7 +131,7 @@ docViewCpt = here.component "docView" cpt ...@@ -131,7 +131,7 @@ docViewCpt = here.component "docView" cpt
else else
void $ T.write ipp path void $ T.write ipp path
pure $ H.div { className: "facets-doc-view container1" } pure $ H.div { className: "facets-doc-view" }
[ R2.row [ R2.row
[ chart [ chart
, H.div { className: "col-md-12" } , H.div { className: "col-md-12" }
...@@ -140,9 +140,9 @@ docViewCpt = here.component "docView" cpt ...@@ -140,9 +140,9 @@ docViewCpt = here.component "docView" cpt
[ H.button { style: buttonStyle, on: { click: trashClick deletions } } [ H.button { style: buttonStyle, on: { click: trashClick deletions } }
[ H.i { className: "glyphitem fa fa-trash" [ H.i { className: "glyphitem fa fa-trash"
, style: { marginRight : "9px" }} [] , style: { marginRight : "9px" }} []
, H.text "Delete document!" ] , H.text "Delete document!" ]
] ]
-} ] -} ]
] ]
performDeletions :: Session -> Int -> T.Box Deletions -> Deletions -> Effect Unit performDeletions :: Session -> Int -> T.Box Deletions -> Deletions -> Effect Unit
...@@ -188,7 +188,7 @@ docViewGraphCpt = here.component "docViewGraph" cpt ...@@ -188,7 +188,7 @@ docViewGraphCpt = here.component "docViewGraph" cpt
, H.button { style: buttonStyle, on: { click: performClick } } , H.button { style: buttonStyle, on: { click: performClick } }
[ H.i { className: "glyphitem fa fa-trash" [ H.i { className: "glyphitem fa fa-trash"
, style: { marginRight : "9px" } } [] , style: { marginRight : "9px" } } []
, H.text "Delete document!" , H.text "Delete document!"
] ]
] ]
] ]
...@@ -408,4 +408,3 @@ derive newtype instance JSON.WriteForeign DeleteDocumentQuery ...@@ -408,4 +408,3 @@ derive newtype instance JSON.WriteForeign DeleteDocumentQuery
deleteDocuments :: Session -> Int -> DeleteDocumentQuery -> AffRESTError (Array Int) deleteDocuments :: Session -> Int -> DeleteDocumentQuery -> AffRESTError (Array Int)
deleteDocuments session nodeId = deleteDocuments session nodeId =
deleteWithBody session $ NodeAPI Node (Just nodeId) "documents" deleteWithBody session $ NodeAPI Node (Just nodeId) "documents"
...@@ -7,12 +7,14 @@ module Gargantext.Components.Forest ...@@ -7,12 +7,14 @@ module Gargantext.Components.Forest
import Gargantext.Prelude import Gargantext.Prelude
import Data.Array as A import Data.Array as A
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Position(..), TooltipPosition(..), Variant(..))
import Gargantext.Components.Forest.Tree (treeLoader) import Gargantext.Components.Forest.Tree (treeLoader)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Routes (AppRoute(..), appPath) import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Routes (AppRoute(..))
import Gargantext.Sessions (Session(..), unSessions) import Gargantext.Sessions (Session(..), unSessions)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
...@@ -48,17 +50,21 @@ forestCpt = here.component "forest" cpt where ...@@ -48,17 +50,21 @@ forestCpt = here.component "forest" cpt where
-- TODO If `reloadForest` is set, `reload` state should be updated -- TODO If `reloadForest` is set, `reload` state should be updated
-- TODO fix tasks ref -- TODO fix tasks ref
pure $ H.div { className: "forest-layout-content" } pure $ R.fragment
(A.cons (plus { boxes }) (trees handed' sessions')) (A.cons (plus { boxes }) (trees handed' sessions'))
where where
trees handed' sessions' = (tree handed') <$> unSessions sessions' trees handed' sessions' = (tree handed') <$> unSessions sessions'
tree handed' s@(Session { treeId }) = tree handed' s@(Session { treeId }) =
treeLoader { boxes H.div
{ className: "forest-layout-tree" }
[
treeLoader { boxes
, frontends , frontends
, handed: handed' , handed: handed'
, reload: reloadForest , reload: reloadForest
, root: treeId , root: treeId
, session: s } [] , session: s } []
]
type Plus = ( boxes :: Boxes ) type Plus = ( boxes :: Boxes )
...@@ -66,67 +72,92 @@ plus :: R2.Leaf Plus ...@@ -66,67 +72,92 @@ plus :: R2.Leaf Plus
plus = R2.leafComponent plusCpt plus = R2.leafComponent plusCpt
plusCpt :: R.Component Plus plusCpt :: R.Component Plus
plusCpt = here.component "plus" cpt where plusCpt = here.component "plus" cpt where
cpt { boxes: { backend, showLogin } } _ = pure $ cpt { boxes: { backend, showLogin } } _ = do
-- Hooks
{ goToRoute } <- useLinkHandler
H.div -- Behaviors
{ className: "forest-layout-action" } let
[ click _ = do
H.a -- NOTE Reset backend in case G.C.N.Home.homeLayout set that to (Just b)
{ className: intercalate " " -- from current url
[ "btn btn-primary" _ <- T.write Nothing backend
, "forest-layout-action__button" T.write_ true showLogin
]
, href: appPath Home -- Render
} pure $
H.div
{ className: "forest-layout-action" }
[ [
H.i -- H.a
{ className: "fa fa-home" -- { className: intercalate " "
, title: "Back to home" -- [ "btn btn-primary"
-- , "forest-layout-action__button"
-- ]
-- , href: appPath Home
-- }
-- [
-- H.i
-- { className: "fa fa-home"
-- , title: "Back to home"
-- }
-- []
-- ]
B.tooltipContainer
{ delayShow: 600
, position: TooltipPosition Top
, tooltipSlot:
B.span_ "Back to home"
, defaultSlot:
B.button
{ className: "forest-layout-action__button"
, callback: const $ goToRoute Home
, variant: ButtonVariant Light
}
[
B.icon { name: "home" }
]
}
,
B.tooltipContainer
{ delayShow: 600
, position: TooltipPosition Top
, tooltipSlot:
B.span_ "Add or remove connection to the server(s)"
, defaultSlot:
B.button
{ className: "forest-layout-action__button"
, callback: click
, variant: ButtonVariant Light
}
[
B.icon
{ name: "universal-access" }
,
B.wad_ [ "d-inline-block", "w-1" ]
,
H.text $ "Log in/out"
]
} }
[]
]
,
H.button
{ className: intercalate " "
[ "btn btn-primary d-block"
, "forest-layout-action__button"
]
, on: { click }
, title: "Add or remove connections to the server(s)."
}
[
H.span
{ className: "fa fa-universal-access" }
[ H.text " Log in/out " ]
] ]
]
--, H.div { "type": "", className: "fa fa-plus-circle fa-lg"} [] --, H.div { "type": "", className: "fa fa-plus-circle fa-lg"} []
--, H.div { "type": "", className: "fa fa-minus-circle fa-lg"} [] --, H.div { "type": "", className: "fa fa-minus-circle fa-lg"} []
-- TODO same as the one in the Login Modal (same CSS) -- TODO same as the one in the Login Modal (same CSS)
-- [ H.i { className: "material-icons md-36"} [] ] -- [ H.i { className: "material-icons md-36"} [] ]
where
click _ = do
-- NOTE Reset backend in case G.C.N.Home.homeLayout set that to (Just b)
-- from current url
_ <- T.write Nothing backend
T.write_ true showLogin
forestLayout :: R2.Component Props forestLayout :: R2.Leaf Props
forestLayout = R.createElement forestLayoutCpt forestLayout = R2.leaf forestLayoutCpt
forestLayoutCpt :: R.Component Props forestLayoutCpt :: R.Memo Props
forestLayoutCpt = here.component "forestLayout" cpt where forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where
cpt p _ = pure $ cpt p _ = pure $
H.div { className: "forest-layout-wrapper col-md-2" } H.div
{ className: "forest-layout" }
[ [
H.div { className: "forest-layout" } H.div { className: "forest-layout__top-teaser" } []
[ ,
H.div { className: "forest-layout-top-teaser" } [] forest p []
, ,
forest p [] H.div { className: "forest-layout__bottom-teaser" } []
,
H.div { className: "forest-layout-bottom-teaser" } []
]
] ]
...@@ -3,14 +3,17 @@ module Gargantext.Components.Forest.Tree where ...@@ -3,14 +3,17 @@ module Gargantext.Components.Forest.Tree where
import Gargantext.Prelude import Gargantext.Prelude
import Data.Array as A import Data.Array as A
import Data.Maybe (Maybe(..)) import Data.Array as Array
import Data.Traversable (traverse_, traverse) import Data.Maybe (Maybe(..), isJust)
import Data.Traversable (intercalate, traverse, traverse_)
import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Forest.Tree.Node (nodeSpan) import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Forest.Tree.Node (blankNodeSpan, nodeSpan)
import Gargantext.Components.Forest.Tree.Node.Action.Add (AddNodeValue(..), addNode) import Gargantext.Components.Forest.Tree.Node.Action.Add (AddNodeValue(..), addNode)
import Gargantext.Components.Forest.Tree.Node.Action.Contact as Contact import Gargantext.Components.Forest.Tree.Node.Action.Contact as Contact
import Gargantext.Components.Forest.Tree.Node.Action.Delete (deleteNode, unpublishNode) import Gargantext.Components.Forest.Tree.Node.Action.Delete (deleteNode, unpublishNode)
...@@ -28,12 +31,13 @@ import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(.. ...@@ -28,12 +31,13 @@ import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError) import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader, useLoaderEffect)
import Gargantext.Routes as GR import Gargantext.Routes as GR
import Gargantext.Sessions (Session, get, mkNodeId) import Gargantext.Sessions (Session, get, mkNodeId)
import Gargantext.Sessions.Types (useOpenNodesMemberBox, openNodesInsert, openNodesDelete) import Gargantext.Sessions.Types (useOpenNodesMemberBox, openNodesInsert, openNodesDelete)
import Gargantext.Types (Handed, ID, isPublic, publicize, switchHanded) import Gargantext.Types (Handed, ID, isPublic, publicize, switchHanded)
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils (nbsp, (?))
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 as R
...@@ -110,12 +114,30 @@ treeLoaderCpt = here.component "treeLoader" cpt where ...@@ -110,12 +114,30 @@ treeLoaderCpt = here.component "treeLoader" cpt where
-- treeLoaderCpt = R.memo (here.component "treeLoader" cpt) memoCmp where -- treeLoaderCpt = R.memo (here.component "treeLoader" cpt) memoCmp where
-- memoCmp ({ root: t1 }) ({ root: t2 }) = t1 == t2 -- memoCmp ({ root: t1 }) ({ root: t2 }) = t1 == t2
cpt p@{ root, session } _ = do cpt p@{ root, session } _ = do
-- States
-- app <- T.useLive T.unequal p.reloadRoot -- app <- T.useLive T.unequal p.reloadRoot
state /\ stateBox <- R2.useBox' Nothing
let fetch { root: r } = getNodeTree session r let fetch { root: r } = getNodeTree session r
useLoader { errorHandler
, loader: fetch -- Hooks
, path: { root } useLoaderEffect
, render: loaded } { errorHandler
, loader: fetch
, path: { root }
, state: stateBox
}
-- Render
pure $
B.cloak
{ isDisplayed: isJust state
, sustainingPhaseDuration: Just 50
, cloakSlot:
blankTree {}
, defaultSlot:
R2.fromMaybe_ state $ loaded
}
where where
loaded tree' = tree props where loaded tree' = tree props where
props = Record.merge common extra where props = Record.merge common extra where
...@@ -135,58 +157,88 @@ treeCpt :: R.Component TreeProps ...@@ -135,58 +157,88 @@ treeCpt :: R.Component TreeProps
treeCpt = here.component "tree" cpt where treeCpt = here.component "tree" cpt where
cpt p@{ boxes: boxes@{ forestOpen } cpt p@{ boxes: boxes@{ forestOpen }
, frontends , frontends
, handed
, reload , reload
, root , root
, session , session
, tree: NTree (LNode { id, name, nodeType }) children } _ = do , tree: NTree (LNode { id, name, nodeType }) children } _ = do
setPopoverRef <- R.useRef Nothing setPopoverRef <- R.useRef Nothing
folderOpen <- useOpenNodesMemberBox nodeId forestOpen folderOpen <- useOpenNodesMemberBox nodeId forestOpen
pure $ H.ul { className: ulClass } folderOpen' <- T.useLive T.unequal folderOpen
[ H.li { className: childrenClass children' }
[ nodeSpan { boxes pure $
, dispatch: dispatch setPopoverRef
, folderOpen H.div
, frontends { className: intercalate " "
, id [ "maintree"
, isLeaf , Array.null children' ?
, name "maintree--no-child" $
, nodeType "maintree--with-child"
, reload ]
, root }
, session [
, setPopoverRef } H.div
[ renderChildren (Record.merge p { childProps: { children', folderOpen, render: tree } } ) [] ] { className: "maintree__node" }
[
nodeSpan
{ boxes
, dispatch: dispatch setPopoverRef
, folderOpen
, frontends
, id
, isLeaf
, name
, nodeType
, reload
, root
, session
, setPopoverRef
}
<>
R2.if' (folderOpen')
(
renderTreeChildren $
{ childProps:
{ children'
, folderOpen
, render: tree
}
} `Record.merge` p
)
] ]
] ]
where where
isLeaf = A.null children isLeaf = A.null children
nodeId = mkNodeId session id nodeId = mkNodeId session id
ulClass = switchHanded "ml left" "mr right" handed <> "-auto tree handed"
children' = A.sortWith fTreeID pubChildren children' = A.sortWith fTreeID pubChildren
pubChildren = if isPublic nodeType then map (map pub) children else children pubChildren = if isPublic nodeType then map (map pub) children else children
dispatch setPopoverRef a = performAction a (Record.merge common' spr) where dispatch setPopoverRef a = performAction a (Record.merge common' spr) where
common' = RecordE.pick p :: Record PACommon common' = RecordE.pick p :: Record PACommon
spr = { setPopoverRef } spr = { setPopoverRef }
pub (LNode n@{ nodeType: t }) = LNode (n { nodeType = publicize t }) pub (LNode n@{ nodeType: t }) = LNode (n { nodeType = publicize t })
childrenClass [] = "no-children"
childrenClass _ = "with-children"
renderChildren :: R2.Component ChildrenTreeProps
renderChildren = R.createElement renderChildrenCpt
renderChildrenCpt :: R.Component ChildrenTreeProps
renderChildrenCpt = here.component "renderChildren" cpt where
cpt p@{ childProps: { folderOpen } } _ = do
folderOpen' <- T.useLive T.unequal folderOpen
if folderOpen' then blankTree :: R2.Leaf ()
pure $ renderTreeChildren p [] blankTree = R2.leaf blankTreeCpt
else blankTreeCpt :: R.Component ()
pure $ H.div {} [] blankTreeCpt = here.component "__blank__" cpt where
cpt _ _ = pure $
renderTreeChildren :: R2.Component ChildrenTreeProps H.div
renderTreeChildren = R.createElement renderTreeChildrenCpt { className: "maintree maintree--blank" }
[
H.div
{ className: "maintree__node" }
[
blankNodeSpan
{}
]
]
renderTreeChildren :: R2.Leaf ChildrenTreeProps
renderTreeChildren = R2.leaf renderTreeChildrenCpt
renderTreeChildrenCpt :: R.Component ChildrenTreeProps renderTreeChildrenCpt :: R.Component ChildrenTreeProps
renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where
cpt p@{ childProps: { children' cpt p@{ childProps: { children'
...@@ -199,6 +251,7 @@ renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where ...@@ -199,6 +251,7 @@ renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where
renderChild (NTree (LNode {id: cId}) _) = childLoader props [] where renderChild (NTree (LNode {id: cId}) _) = childLoader props [] where
props = Record.merge nodeProps { id: cId, render, root } props = Record.merge nodeProps { id: cId, render, root }
childLoader :: R2.Component ChildLoaderProps childLoader :: R2.Component ChildLoaderProps
childLoader = R.createElement childLoaderCpt childLoader = R.createElement childLoaderCpt
childLoaderCpt :: R.Component ChildLoaderProps childLoaderCpt :: R.Component ChildLoaderProps
...@@ -207,13 +260,32 @@ childLoaderCpt = here.component "childLoader" cpt where ...@@ -207,13 +260,32 @@ childLoaderCpt = here.component "childLoader" cpt where
, reloadTree , reloadTree
, render , render
, root } _ = do , root } _ = do
-- States
reload <- T.useBox T2.newReload reload <- T.useBox T2.newReload
state /\ stateBox <- R2.useBox' Nothing
let reloads = [ reload, reloadRoot, reloadTree ] let reloads = [ reload, reloadRoot, reloadTree ]
cache <- (A.cons p.id) <$> traverse (T.useLive T.unequal) reloads cache <- (A.cons p.id) <$> traverse (T.useLive T.unequal) reloads
useLoader { errorHandler
, loader: fetch -- Hooks
, path: cache useLoaderEffect
, render: paint reload } { errorHandler
, loader: fetch
, path: cache
, state: stateBox
}
-- Render
pure $
B.cloak
{ isDisplayed: isJust state
, sustainingPhaseDuration: Just 50
, cloakSlot:
blankTree {}
, defaultSlot:
R2.fromMaybe_ state $ paint reload
}
where where
errorHandler = logRESTError here "[childLoader]" errorHandler = logRESTError here "[childLoader]"
fetch _ = getNodeTreeFirstLevel p.session p.id fetch _ = getNodeTreeFirstLevel p.session p.id
......
exports.nodeUserRegexp = /(@{1}.*).gargantext.org$/;
...@@ -178,6 +178,15 @@ isExternal :: Maybe DataField -> Boolean ...@@ -178,6 +178,15 @@ isExternal :: Maybe DataField -> Boolean
isExternal (Just (External _)) = true isExternal (Just (External _)) = true
isExternal _ = false isExternal _ = false
isArxiv :: Maybe DataField -> Boolean
isArxiv (Just
( External
( Just Arxiv
)
)
) = true
isArxiv _ = false
isHAL :: Maybe DataField -> Boolean isHAL :: Maybe DataField -> Boolean
isHAL (Just isHAL (Just
( External ( External
......
...@@ -104,6 +104,7 @@ datafield2database _ = Empty ...@@ -104,6 +104,7 @@ datafield2database _ = Empty
allDatabases :: Array Database allDatabases :: Array Database
allDatabases = [ Empty allDatabases = [ Empty
, PubMed , PubMed
, Arxiv
, HAL Nothing , HAL Nothing
, IsTex , IsTex
, IsTex_Advanced , IsTex_Advanced
...@@ -116,6 +117,7 @@ allDatabases = [ Empty ...@@ -116,6 +117,7 @@ allDatabases = [ Empty
data Database = All_Databases data Database = All_Databases
| Empty | Empty
| PubMed | PubMed
| Arxiv
| HAL (Maybe Org) | HAL (Maybe Org)
| IsTex | IsTex
| IsTex_Advanced | IsTex_Advanced
...@@ -124,34 +126,37 @@ data Database = All_Databases ...@@ -124,34 +126,37 @@ data Database = All_Databases
-- | SocialNetworks -- | SocialNetworks
derive instance Generic Database _ derive instance Generic Database _
instance Show Database where instance Show Database where
show All_Databases= "All Databases" show All_Databases = "All Databases"
show PubMed = "PubMed" show PubMed = "PubMed"
show (HAL _)= "HAL" show Arxiv = "Arxiv"
show IsTex = "IsTex" show (HAL _) = "HAL"
show IsTex_Advanced = "IsTex_Advanced" show IsTex = "IsTex"
show Isidore= "Isidore" show IsTex_Advanced = "IsTex_Advanced"
show Empty = "Empty" show Isidore = "Isidore"
show Empty = "Empty"
-- show News = "News" -- show News = "News"
-- show SocialNetworks = "Social Networks" -- show SocialNetworks = "Social Networks"
instance Doc Database where instance Doc Database where
doc All_Databases = "All databases" doc All_Databases = "All databases"
doc PubMed = "All Medical publications" doc PubMed = "All Medical publications"
doc (HAL _) = "All open science (archives ouvertes)" doc Arxiv = "Arxiv"
doc IsTex = "All Elsevier enriched by CNRS/INIST" doc (HAL _) = "All open science (archives ouvertes)"
doc IsTex = "All Elsevier enriched by CNRS/INIST"
doc IsTex_Advanced = "IsTex advanced search" doc IsTex_Advanced = "IsTex advanced search"
doc Isidore = "All (French) Social Sciences" doc Isidore = "All (French) Social Sciences"
doc Empty = "Empty" doc Empty = "Empty"
-- doc News = "Web filtered by News" -- doc News = "Web filtered by News"
-- doc SocialNetworks = "Web filtered by MicroBlogs" -- doc SocialNetworks = "Web filtered by MicroBlogs"
instance Read Database where instance Read Database where
read :: String -> Maybe Database read :: String -> Maybe Database
read "All Databases" = Just All_Databases read "All Databases" = Just All_Databases
read "PubMed" = Just PubMed read "PubMed" = Just PubMed
read "HAL" = Just $ HAL Nothing read "Arxiv" = Just Arxiv
read "Isidore"= Just Isidore read "HAL" = Just $ HAL Nothing
read "IsTex" = Just IsTex read "Isidore" = Just Isidore
read "IsTex" = Just IsTex
read "IsTex_Advanced" = Just IsTex_Advanced read "IsTex_Advanced" = Just IsTex_Advanced
-- read "Web" = Just Web -- read "Web" = Just Web
-- read "News" = Just News -- read "News" = Just News
......
...@@ -432,7 +432,7 @@ fileTypeViewCpt = here.component "fileTypeView" cpt ...@@ -432,7 +432,7 @@ fileTypeViewCpt = here.component "fileTypeView" cpt
droppedFile' <- T.useLive T.unequal droppedFile droppedFile' <- T.useLive T.unequal droppedFile
case droppedFile' of case droppedFile' of
Nothing -> pure $ H.div {} [] Nothing -> pure $ mempty
Just df -> Just df ->
pure $ H.div tooltipProps [ H.div { className: "card"} pure $ H.div tooltipProps [ H.div { className: "card"}
[ panelHeading [ panelHeading
......
module Gargantext.Components.Forest.Tree.Node.Tools where module Gargantext.Components.Forest.Tree.Node.Tools where
import Data.Foldable (intercalate) import Gargantext.Prelude
import Data.Maybe (fromMaybe, Maybe(..))
import Data.Maybe (fromMaybe)
import Data.Set (Set) import Data.Set (Set)
import Data.Set as Set import Data.Set as Set
import Data.String as S import Data.String as S
import Data.String.CodeUnits as DSCU
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff, launchAff_) import Effect.Aff (Aff, launchAff, launchAff_)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action (icon, text) import Gargantext.Components.Forest.Tree.Node.Action (icon, text)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.GraphExplorer.Types (mCameraP)
import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Ends (Frontends, url)
import Gargantext.Prelude (class Ord, class Read, class Show, Unit, bind, const, discard, map, not, pure, read, show, when, mempty, ($), (<), (<<<), (<>), (<$>), (<*>))
import Gargantext.Sessions (Session, sessionId)
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils (toggleSet, (?)) import Gargantext.Utils (toggleSet)
import Gargantext.Utils.Glyphicon (glyphicon) import Gargantext.Utils.Glyphicon (glyphicon)
import Gargantext.Utils.ReactTooltip as ReactTooltip
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
import Web.HTML.ValidityState (valid)
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools" here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools"
...@@ -60,7 +53,7 @@ textInputBox :: R2.Component TextInputBoxProps ...@@ -60,7 +53,7 @@ textInputBox :: R2.Component TextInputBoxProps
textInputBox = R.createElement textInputBoxCpt textInputBox = R.createElement textInputBoxCpt
textInputBoxCpt :: R.Component TextInputBoxProps textInputBoxCpt :: R.Component TextInputBoxProps
textInputBoxCpt = here.component "textInputBox" cpt where textInputBoxCpt = here.component "textInputBox" cpt where
cpt { boxAction, boxName, dispatch, id, isOpen, text } _ = cpt { boxAction, boxName, dispatch, isOpen, text } _ =
content <$> T.useLive T.unequal isOpen <*> R.useRef text content <$> T.useLive T.unequal isOpen <*> R.useRef text
where where
content false _ = (R.fragment []) content false _ = (R.fragment [])
...@@ -256,101 +249,3 @@ prettyNodeType ...@@ -256,101 +249,3 @@ prettyNodeType
= S.replace (S.Pattern "Node") (S.Replacement " ") = S.replace (S.Pattern "Node") (S.Replacement " ")
<<< S.replace (S.Pattern "Folder") (S.Replacement " ") <<< S.replace (S.Pattern "Folder") (S.Replacement " ")
<<< show <<< show
tooltipId :: GT.NodeID -> String
tooltipId id = "node-link-" <> show id
-- START node link
type NodeLinkProps = (
boxes :: Boxes
, folderOpen :: T.Box Boolean
, frontends :: Frontends
, id :: Int
, isSelected :: Boolean
, name :: GT.Name
, nodeType :: GT.NodeType
, session :: Session
)
nodeLink :: R2.Component NodeLinkProps
nodeLink = R.createElement nodeLinkCpt
nodeLinkCpt :: R.Component NodeLinkProps
nodeLinkCpt = here.component "nodeLink" cpt
where
cpt { boxes
, folderOpen
, frontends
, id
, isSelected
, name
, nodeType
, session
} _ = do
pure $
H.div { className: "node-link"
, on: { click } }
[ H.a { href, data: { for: name <> "-" <> (tooltipId id), tip: true } }
[ nodeText { isSelected, name }
, ReactTooltip.reactTooltip { effect: "float", id: name <> "-" <> (tooltipId id), type: "dark" }
[ R2.row
[ H.h4 {className: GT.fldr nodeType true}
[ H.text $ GT.prettyNodeType nodeType ]
]
, R2.row [ H.span {} [ H.text $ name ]]
]
]
]
where
-- NOTE Don't toggle tree if it is not selected
-- click on closed -> open
-- click on open -> ?
click _ = when (not isSelected) (T.write_ true folderOpen)
href = url frontends $ GT.NodePath (sessionId session) nodeType (Just id)
-- END node link
type NodeTextProps =
( isSelected :: Boolean
, name :: GT.Name
)
nodeText :: R2.Leaf NodeTextProps
nodeText p = R.createElement nodeTextCpt p []
nodeTextCpt :: R.Memo NodeTextProps
nodeTextCpt = R.memo' $ here.component "nodeText" cpt where
cpt props@{ isSelected } _ = do
-- Computed
let
className = intercalate " "
[ "node-text"
, isSelected ? "node-text--selected" $ ""
]
prefix = isSelected ?
"" $
"..."
name = isSelected ?
"| " <> (textEllipsisBreak 15 props.name) <> " | " $
textEllipsisBreak 15 props.name
-- Render
pure $
H.span { className }
[
H.span {}
[ H.text prefix ]
,
H.span {}
[ H.text name ]
]
textEllipsisBreak :: Int -> String -> String
textEllipsisBreak len n =
if S.length n < len then n
else case (DSCU.slice 0 len n) of
Nothing -> "???"
Just s -> s <> "..."
...@@ -17,6 +17,7 @@ import Gargantext.Types as GT ...@@ -17,6 +17,7 @@ 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 Record.Extra as RX
import Toestand as T import Toestand as T
here :: R2.Here here :: R2.Here
...@@ -51,7 +52,8 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt ...@@ -51,7 +52,8 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt
R.useEffectOnce' $ do R.useEffectOnce' $ do
intervalId <- setInterval 1000 $ do intervalId <- setInterval 1000 $ do
launchAff_ $ do launchAff_ $ do
eAsyncProgress <- queryProgress props let rdata = (RX.pick props :: Record QueryProgressData)
eAsyncProgress <- queryProgress rdata
handleRESTError errors eAsyncProgress $ \asyncProgress -> liftEffect $ do handleRESTError errors eAsyncProgress $ \asyncProgress -> liftEffect $ do
let GT.AsyncProgress { status } = asyncProgress let GT.AsyncProgress { status } = asyncProgress
T.write_ (min 100.0 $ GT.progressPercent asyncProgress) progress T.write_ (min 100.0 $ GT.progressPercent asyncProgress) progress
...@@ -71,6 +73,9 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt ...@@ -71,6 +73,9 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt
pure $ progressIndicator { barType, label: id, progress } pure $ progressIndicator { barType, label: id, progress }
--------------------------------------------------------------
type ProgressIndicatorProps = type ProgressIndicatorProps =
( barType :: BarType ( barType :: BarType
, label :: String , label :: String
...@@ -102,7 +107,16 @@ progressIndicatorCpt = here.component "progressIndicator" cpt ...@@ -102,7 +107,16 @@ progressIndicatorCpt = here.component "progressIndicator" cpt
] ]
] ]
queryProgress :: Record Props -> AffRESTError GT.AsyncProgress
--------------------------------------------------------------
type QueryProgressData =
( asyncTask :: GT.AsyncTaskWithType
, nodeId :: GT.ID
, session :: Session
)
queryProgress :: Record QueryProgressData -> AffRESTError GT.AsyncProgress
queryProgress { asyncTask: GT.AsyncTaskWithType { task: GT.AsyncTask {id} queryProgress { asyncTask: GT.AsyncTaskWithType { task: GT.AsyncTask {id}
, typ , typ
} }
......
...@@ -10,7 +10,6 @@ import Data.Tuple.Nested ((/\)) ...@@ -10,7 +10,6 @@ import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action (Props, subTreeOut, setTreeOut) import Gargantext.Components.Forest.Tree.Node.Action (Props, subTreeOut, setTreeOut)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action) import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.Forest.Tree.Node.Tools (nodeText)
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..)) import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..))
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeParams(..), SubTreeOut(..)) import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeParams(..), SubTreeOut(..))
import Gargantext.Config.REST (AffRESTError, logRESTError) import Gargantext.Config.REST (AffRESTError, logRESTError)
...@@ -18,7 +17,7 @@ import Gargantext.Hooks.Loader (useLoader) ...@@ -18,7 +17,7 @@ import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes as GR import Gargantext.Routes as GR
import Gargantext.Sessions (Session(..), get) import Gargantext.Sessions (Session(..), get)
import Gargantext.Types as GT import Gargantext.Types as GT
import Gargantext.Utils ((?)) import Gargantext.Utils (textEllipsisBreak, (?))
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
...@@ -141,11 +140,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where ...@@ -141,11 +140,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
{ className: intercalate " " { className: intercalate " "
[ "subtree__node" [ "subtree__node"
, validNodeType ? "subtree__node--can-be-selected" $ "" , validNodeType ? "subtree__node--can-be-selected" $ ""
, (isSelected targetId action) ? "subtree__node--is-selected" $ ""
] ]
} }
[ [
H.div H.div
{ className: "subtree__node__text" } { className: "subtree__node__inner" }
[ [
H.div H.div
{ className: "subtree__node__icons" { className: "subtree__node__icons"
...@@ -162,13 +162,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where ...@@ -162,13 +162,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
H.span { className: "fa fa-chevron-right" } [] H.span { className: "fa fa-chevron-right" } []
] ]
, ,
H.div H.span
{ on: { click: selectCbk } } { on: { click: selectCbk }
, className: "subtree__node__text"
}
[ [
nodeText H.text $ textEllipsisBreak 15 name
{ isSelected: isSelected targetId action
, name
}
] ]
] ]
, ,
......
module Gargantext.Components.Forest.Tree.Node.Tools.Sync where module Gargantext.Components.Forest.Tree.Node.Tools.Sync where
import Gargantext.Prelude
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..))
import Gargantext.Components.GraphExplorer.API as GraphAPI import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Prelude (Unit, bind, discard, pure, unit, ($), (<>), (==))
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types as GT 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 as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
...@@ -27,15 +31,15 @@ type NodeActionsGraphProps = ...@@ -27,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
...@@ -53,13 +57,24 @@ graphUpdateButtonCpt = here.component "graphUpdateButton" cpt ...@@ -53,13 +57,24 @@ graphUpdateButtonCpt = here.component "graphUpdateButton" cpt
enabled <- T.useBox true enabled <- T.useBox true
enabled' <- T.useLive T.unequal enabled enabled' <- T.useLive T.unequal enabled
pure $ H.div { className: "update-button " pure $
<> if enabled'
then "enabled" B.iconButton
else "disabled text-muted" { className: "mainleaf__update-icon"
} [ H.span { className: "fa fa-refresh" , variant: Secondary
, on: { click: onClick enabled' enabled } } [] , overlay: true
] , status: enabled' ? Enabled $ Disabled
, callback: const $ onClick enabled' enabled
, name: "refresh"
}
-- H.div { className: "update-button "
-- <> if enabled'
-- then "enabled"
-- else "disabled text-muted"
-- } [ H.span { className: "fa fa-refresh"
-- , on: { click: onClick enabled' enabled } } []
-- ]
where where
onClick false _ = pure unit onClick false _ = pure unit
onClick true enabled = do onClick true enabled = do
...@@ -84,12 +99,9 @@ type NodeActionsNodeListProps = ...@@ -84,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
......
...@@ -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
......
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.
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.
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.
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