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
pkgs.fetchFromGitHub {
owner = "justinwoo";
repo = "easy-purescript-nix";
rev = "678070816270726e2f428da873fe3f2736201f42";
sha256 = "JEabdJ+3cZEYDVnzgMH/YFsaGtIBiCFcgvVO9XRgiY4=";
rev = "9c5ffd3e54c41dece66ed84f8f23970a4f1f3883";
sha256 = "8erFzbiRJYqPgJHuQwhgBPltQeaWeAZom/5X3lyUAcc=";
}
) {
inherit pkgs;
......
import (
builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/21.05.tar.gz";
sha256 = "1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36";
url = "https://github.com/NixOS/nixpkgs/archive/21.11.tar.gz";
sha256 = "162dywda2dvfj1248afxc45kcrg83appjd0nmdb541hl7rnncf02";
}
)
......@@ -122,7 +122,7 @@
name = "colors";
version = "v6.0.0";
src = pkgs.fetchgit {
url = "https://github.com/sharkdp/purescript-colors.git";
url = "https://github.com/purescript-contrib/purescript-colors.git";
rev = "v6.0.0";
sha256 = "05gdjx8xhv7xxm1prrrc2brxjn1gi19qf1004syk8qx37slrjf87";
};
......@@ -336,11 +336,11 @@
};
"free" = {
name = "free";
version = "v6.0.1";
version = "v6.2.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-free.git";
rev = "v6.0.1";
sha256 = "0kpq83qjfjzf1l2f1cnnx36kjwnm5czgjyh2imwp3bna8js6sk39";
rev = "v6.2.0";
sha256 = "10zsw49wzlzz78882b3grl19gpca5llpdk3ph608075h0ygk3q3k";
};
};
"functions" = {
......@@ -516,11 +516,11 @@
};
"milkis" = {
name = "milkis";
version = "v7.4.0";
version = "v7.5.0";
src = pkgs.fetchgit {
url = "https://github.com/justinwoo/purescript-milkis.git";
rev = "v7.4.0";
sha256 = "10ahz4idcb3qwys7x15k3za5gkw9zbk15cdmcqsi8lfh6fg2w2db";
rev = "v7.5.0";
sha256 = "01kaqzndxq2agi6xr1k5gsn1pzvd3xxn8v1s1gl87kmiic94w6vc";
};
};
"mmorph" = {
......@@ -552,11 +552,11 @@
};
"node-fs" = {
name = "node-fs";
version = "v6.1.0";
version = "v6.2.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript-node/purescript-node-fs.git";
rev = "v6.1.0";
sha256 = "1w97m2afn7yn757niknkbk7w6nyg4n5dabxr7gzfz368z1nkf45s";
rev = "v6.2.0";
sha256 = "1b89sxr6asxvgx59myhfbahiiz1z6sg2qfrm9bqd46h93ai8bhn1";
};
};
"node-path" = {
......@@ -579,11 +579,11 @@
};
"nonempty" = {
name = "nonempty";
version = "v6.0.0";
version = "v6.1.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-nonempty.git";
rev = "v6.0.0";
sha256 = "0azk1jrpqnjf2i97lcp63wcm31c4hddklp1mfmdan27zap3zqyjm";
rev = "v6.1.0";
sha256 = "0hhhw5x5xvs2bd9373gklja1545glnzi1xc2sj16kkznnayrmvsn";
};
};
"now" = {
......@@ -615,11 +615,11 @@
};
"ordered-collections" = {
name = "ordered-collections";
version = "v2.0.1";
version = "v2.0.2";
src = pkgs.fetchgit {
url = "https://github.com/purescript/purescript-ordered-collections.git";
rev = "v2.0.1";
sha256 = "1p592g0s07c56639y71782af0zz5cndpjxd5w9n41hdszsz1b86h";
rev = "v2.0.2";
sha256 = "0g57043ylj3kldkm5vn233yd6hiamryhdfh72cxx9h3mn0ra8ghd";
};
};
"orders" = {
......@@ -642,11 +642,11 @@
};
"parsing" = {
name = "parsing";
version = "v6.0.2";
version = "v8.4.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-parsing.git";
rev = "v6.0.2";
sha256 = "0lhri5bfk4j6dgzi4d5gvmd628hjs9jgyky910ylj6qzalw9fj56";
rev = "v8.4.0";
sha256 = "1rq57cwfx7rx3h3hdc4m8lv90724i5gf2pak9ljj0fcnw96gsaa1";
};
};
"partial" = {
......@@ -912,11 +912,11 @@
};
"string-parsers" = {
name = "string-parsers";
version = "v6.0.1";
version = "v7.0.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-string-parsers.git";
rev = "v6.0.1";
sha256 = "143a2s56kbx3i0xi5wfqp28znr0hdydy902jla236i7kal5y098m";
rev = "v7.0.0";
sha256 = "0kjqx6f8jgcbaf0v401hrdqw61zzvp8my9ik8cn55njn6i3mx74w";
};
};
"strings" = {
......@@ -1056,11 +1056,11 @@
};
"unicode" = {
name = "unicode";
version = "v5.0.0";
version = "v5.0.1";
src = pkgs.fetchgit {
url = "https://github.com/purescript-contrib/purescript-unicode.git";
rev = "v5.0.0";
sha256 = "0sqvgl3il2rl3zxkbzsqb19wib108zvyw73jxiavpfdm6hdmnxvc";
rev = "v5.0.1";
sha256 = "0xh9wwyrl9nsw3h3wzalc1gaph39drj0i6k648cf9bnbb96nxa4z";
};
};
"unsafe-coerce" = {
......@@ -1092,11 +1092,11 @@
};
"variant" = {
name = "variant";
version = "v7.0.3";
version = "v7.1.0";
src = pkgs.fetchgit {
url = "https://github.com/natefaubion/purescript-variant.git";
rev = "v7.0.3";
sha256 = "1q2pky3gf177ihy2zjzqvp1cj18ycaki9vm4ghw18p7hf256lqmc";
rev = "v7.1.0";
sha256 = "064aijlcphyg5mjhxx4acwjlnh7ha3v033zzan31rhkqi6m4r3da";
};
};
"versions" = {
......@@ -1137,11 +1137,11 @@
};
"web-html" = {
name = "web-html";
version = "v3.1.0";
version = "v3.2.0";
src = pkgs.fetchgit {
url = "https://github.com/purescript-web/purescript-web-html.git";
rev = "v3.1.0";
sha256 = "007anmqqifrjnpfa4xf7qa8xnpbhvcxqdraj9lnhizwq65vx53sn";
rev = "v3.2.0";
sha256 = "1ds26vwyba0chhpa09m938brw9q8pxjk6z1n3d4nc30hvdkrjnbh";
};
};
"web-storage" = {
......
{
"name": "Gargantext",
"version": "0.0.5.8.3",
"version": "0.0.5.8.5",
"scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash",
......
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 =
{ globals =
......@@ -7,6 +7,63 @@ let overrides =
, repo = "https://github.com/purescript/purescript-globals"
, 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 =
{ dependencies =
[ "bifunctors"
......
......@@ -105,7 +105,7 @@ let
in
pkgs.mkShell {
buildInputs = [
easy-ps.purs-0_14_5
easy-ps.purs-0_14_7
easy-ps.psc-package
easy-ps.dhall-json-simple
easy-ps.zephyr
......
......@@ -5,9 +5,11 @@ module Gargantext.Components.Bootstrap.Cloak
import Gargantext.Prelude
import Data.Foldable (elem)
import Data.Maybe (Maybe, fromMaybe)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Timer (setTimeout)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Toestand as T
......@@ -17,10 +19,20 @@ type Props =
( defaultSlot :: R.Element
, cloakSlot :: R.Element
, isDisplayed :: Boolean
, idlingPhaseDuration :: Maybe Int -- Milliseconds
| Options
)
type Options =
( idlingPhaseDuration :: 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
-- | component and transitional (or cloak) component
-- |
......@@ -94,8 +106,8 @@ type Props =
-- | , sustainingPhaseDuration : Just 400
-- | }
-- | ```
cloak :: R2.Leaf Props
cloak = R2.leaf component
cloak :: forall r. R2.OptLeaf Options Props r
cloak = R2.optLeaf component options
cname :: String
cname = "b-cloak"
......@@ -107,47 +119,50 @@ component = R.hooksComponent cname cpt where
phase /\ phaseBox <- R2.useBox' (Idle :: Phase)
-- Computed
canCloakBeDisplayed <- pure $ elem phase [ Sustain, Wait ]
canContentBeDisplayed <- pure $ elem phase [ Display ]
let
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
execDisplayingPhase <- pure $ const $
T.write_ Display phaseBox
execWaitingPhase :: Unit -> Effect Unit
execWaitingPhase _ = execDisplayingPhaseOr $ const $
-- Helpers
execDisplayingPhaseOr <- pure $ \fn ->
T.write_ Wait phaseBox
if props.isDisplayed
then execDisplayingPhase unit
else fn
execSustainingPhase :: Unit -> Effect Unit
execSustainingPhase _ = execDisplayingPhaseOr $ const $
-- @executeWaitingPhase
execWaitingPhase <- pure $ const $ execDisplayingPhaseOr $
T.write_ Wait phaseBox
T.write_ Sustain phaseBox
-- @executeSustainingPhase
execSustainingPhase <- pure $ const $ execDisplayingPhaseOr $
T.write_ Sustain phaseBox
<* setTimeout
(fromMaybe 0 props.sustainingPhaseDuration)
(execWaitingPhase unit)
<* setTimeout
(fromMaybe 0 props.sustainingPhaseDuration)
(execWaitingPhase unit)
execIdlingPhase :: Unit -> Effect Unit
execIdlingPhase _ = execDisplayingPhaseOr $ const $
-- @executeIdlingPhase
execIdlingPhase <- pure $ const $ execDisplayingPhaseOr $
T.write_ Idle phaseBox
T.write_ Idle phaseBox
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
-- Effects
R.useEffectOnce' $ execIdlingPhase unit
useFirstEffect' $ execIdlingPhase unit
R.useEffect1' props.isDisplayed $
R.useEffect2' props.isDisplayed phase $
if (props.isDisplayed && phase == Wait)
then execDisplayingPhase unit
then T.write_ Display phaseBox
else pure unit
-- Render
......
......@@ -37,15 +37,16 @@ component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props children = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, bootstrapName
, bootstrapName <> "-" <> show props.variant
]
let
className = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, bootstrapName
, bootstrapName <> "-" <> show props.variant
]
-- Render
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 =
)
type Options =
( className :: String
( className :: String
, contentClassName :: String
)
options :: Record Options
options =
{ className: ""
{ className : ""
, contentClassName : ""
}
-- | Component simulating a native <fieldset>
......@@ -36,12 +38,21 @@ component = R.hooksComponent componentName cpt where
cpt props@{ titleSlot
} children = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
]
let
className = intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
]
contentClassName = intercalate " "
-- provided custom className
[ props.contentClassName
-- BEM classNames
, componentName <> "__content"
]
-- Render
pure $
......@@ -53,6 +64,6 @@ component = R.hooksComponent componentName cpt where
[ titleSlot ]
,
H.div
{ className: componentName <> "__content" }
{ className: contentClassName}
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
import Gargantext.Components.Bootstrap.BaseModal(baseModal) as Exports
import Gargantext.Components.Bootstrap.Button(button) as Exports
import Gargantext.Components.Bootstrap.Caveat(caveat) as Exports
import Gargantext.Components.Bootstrap.Cloak(cloak) as Exports
import Gargantext.Components.Bootstrap.Div(div', div_) as Exports
import Gargantext.Components.Bootstrap.Cloak (cloak) as Exports
import Gargantext.Components.Bootstrap.Fieldset(fieldset) as Exports
import Gargantext.Components.Bootstrap.FormInput(formInput) as Exports
import Gargantext.Components.Bootstrap.FormSelect(formSelect, formSelect') as Exports
import Gargantext.Components.Bootstrap.FormTextarea(formTextarea) 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.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
bootstrapName <> "-block" $
mempty
]
-- @click
click <- pure $ \event -> onClick status callback event
-- Behaviors
let
click = onClick status callback
-- Render
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
, Variant(..), ButtonVariant(..)
, Sizing(..)
, SpinnerTheme(..)
, TooltipEffect(..), TooltipPosition(..)
, Position(..)
) where
import Gargantext.Prelude
......@@ -115,3 +117,46 @@ derive instance Eq SpinnerTheme
instance Show SpinnerTheme where
show BorderTheme = "border"
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
R.fragment
[
H.div { className: "doc-table-doc-view container1" }
H.div { className: "doc-table-doc-view" }
[ R2.row
[ chart
, if showSearch then searchBar { query } [] else H.div {} []
......
......@@ -131,7 +131,7 @@ docViewCpt = here.component "docView" cpt
else
void $ T.write ipp path
pure $ H.div { className: "facets-doc-view container1" }
pure $ H.div { className: "facets-doc-view" }
[ R2.row
[ chart
, H.div { className: "col-md-12" }
......@@ -140,9 +140,9 @@ docViewCpt = here.component "docView" cpt
[ H.button { style: buttonStyle, on: { click: trashClick deletions } }
[ H.i { className: "glyphitem fa fa-trash"
, style: { marginRight : "9px" }} []
, H.text "Delete document!" ]
]
-} ]
, H.text "Delete document!" ]
]
-} ]
]
performDeletions :: Session -> Int -> T.Box Deletions -> Deletions -> Effect Unit
......@@ -188,7 +188,7 @@ docViewGraphCpt = here.component "docViewGraph" cpt
, H.button { style: buttonStyle, on: { click: performClick } }
[ H.i { className: "glyphitem fa fa-trash"
, style: { marginRight : "9px" } } []
, H.text "Delete document!"
, H.text "Delete document!"
]
]
]
......@@ -408,4 +408,3 @@ derive newtype instance JSON.WriteForeign DeleteDocumentQuery
deleteDocuments :: Session -> Int -> DeleteDocumentQuery -> AffRESTError (Array Int)
deleteDocuments session nodeId =
deleteWithBody session $ NodeAPI Node (Just nodeId) "documents"
......@@ -7,12 +7,14 @@ module Gargantext.Components.Forest
import Gargantext.Prelude
import Data.Array as A
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
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.Ends (Frontends)
import Gargantext.Routes (AppRoute(..), appPath)
import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Routes (AppRoute(..))
import Gargantext.Sessions (Session(..), unSessions)
import Gargantext.Utils.Reactix as R2
import Reactix as R
......@@ -48,17 +50,21 @@ forestCpt = here.component "forest" cpt where
-- TODO If `reloadForest` is set, `reload` state should be updated
-- TODO fix tasks ref
pure $ H.div { className: "forest-layout-content" }
pure $ R.fragment
(A.cons (plus { boxes }) (trees handed' sessions'))
where
trees handed' sessions' = (tree handed') <$> unSessions sessions'
tree handed' s@(Session { treeId }) =
treeLoader { boxes
H.div
{ className: "forest-layout-tree" }
[
treeLoader { boxes
, frontends
, handed: handed'
, reload: reloadForest
, root: treeId
, session: s } []
]
type Plus = ( boxes :: Boxes )
......@@ -66,67 +72,92 @@ plus :: R2.Leaf Plus
plus = R2.leafComponent plusCpt
plusCpt :: R.Component Plus
plusCpt = here.component "plus" cpt where
cpt { boxes: { backend, showLogin } } _ = pure $
cpt { boxes: { backend, showLogin } } _ = do
-- Hooks
{ goToRoute } <- useLinkHandler
H.div
{ className: "forest-layout-action" }
[
H.a
{ className: intercalate " "
[ "btn btn-primary"
, "forest-layout-action__button"
]
, href: appPath Home
}
-- Behaviors
let
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
-- Render
pure $
H.div
{ className: "forest-layout-action" }
[
H.i
{ className: "fa fa-home"
, title: "Back to home"
-- H.a
-- { className: intercalate " "
-- [ "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-minus-circle fa-lg"} []
-- TODO same as the one in the Login Modal (same CSS)
-- [ 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 = R.createElement forestLayoutCpt
forestLayoutCpt :: R.Component Props
forestLayoutCpt = here.component "forestLayout" cpt where
forestLayout :: R2.Leaf Props
forestLayout = R2.leaf forestLayoutCpt
forestLayoutCpt :: R.Memo Props
forestLayoutCpt = R.memo' $ here.component "forestLayout" cpt where
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" } []
,
forest p []
,
H.div { className: "forest-layout-bottom-teaser" } []
]
H.div { className: "forest-layout__top-teaser" } []
,
forest p []
,
H.div { className: "forest-layout__bottom-teaser" } []
]
......@@ -3,14 +3,17 @@ module Gargantext.Components.Forest.Tree where
import Gargantext.Prelude
import Data.Array as A
import Data.Maybe (Maybe(..))
import Data.Traversable (traverse_, traverse)
import Data.Array as Array
import Data.Maybe (Maybe(..), isJust)
import Data.Traversable (intercalate, traverse, traverse_)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
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.Contact as Contact
import Gargantext.Components.Forest.Tree.Node.Action.Delete (deleteNode, unpublishNode)
......@@ -28,12 +31,13 @@ import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Hooks.Loader (useLoader, useLoaderEffect)
import Gargantext.Routes as GR
import Gargantext.Sessions (Session, get, mkNodeId)
import Gargantext.Sessions.Types (useOpenNodesMemberBox, openNodesInsert, openNodesDelete)
import Gargantext.Types (Handed, ID, isPublic, publicize, switchHanded)
import Gargantext.Types as GT
import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R
......@@ -110,12 +114,30 @@ treeLoaderCpt = here.component "treeLoader" cpt where
-- treeLoaderCpt = R.memo (here.component "treeLoader" cpt) memoCmp where
-- memoCmp ({ root: t1 }) ({ root: t2 }) = t1 == t2
cpt p@{ root, session } _ = do
-- States
-- app <- T.useLive T.unequal p.reloadRoot
state /\ stateBox <- R2.useBox' Nothing
let fetch { root: r } = getNodeTree session r
useLoader { errorHandler
, loader: fetch
, path: { root }
, render: loaded }
-- Hooks
useLoaderEffect
{ 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
loaded tree' = tree props where
props = Record.merge common extra where
......@@ -135,58 +157,88 @@ treeCpt :: R.Component TreeProps
treeCpt = here.component "tree" cpt where
cpt p@{ boxes: boxes@{ forestOpen }
, frontends
, handed
, reload
, root
, session
, tree: NTree (LNode { id, name, nodeType }) children } _ = do
setPopoverRef <- R.useRef Nothing
folderOpen <- useOpenNodesMemberBox nodeId forestOpen
pure $ H.ul { className: ulClass }
[ H.li { className: childrenClass children' }
[ nodeSpan { boxes
, dispatch: dispatch setPopoverRef
, folderOpen
, frontends
, id
, isLeaf
, name
, nodeType
, reload
, root
, session
, setPopoverRef }
[ renderChildren (Record.merge p { childProps: { children', folderOpen, render: tree } } ) [] ]
folderOpen' <- T.useLive T.unequal folderOpen
pure $
H.div
{ className: intercalate " "
[ "maintree"
, Array.null children' ?
"maintree--no-child" $
"maintree--with-child"
]
}
[
H.div
{ 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
isLeaf = A.null children
nodeId = mkNodeId session id
ulClass = switchHanded "ml left" "mr right" handed <> "-auto tree handed"
children' = A.sortWith fTreeID pubChildren
pubChildren = if isPublic nodeType then map (map pub) children else children
dispatch setPopoverRef a = performAction a (Record.merge common' spr) where
common' = RecordE.pick p :: Record PACommon
spr = { setPopoverRef }
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
pure $ renderTreeChildren p []
else
pure $ H.div {} []
blankTree :: R2.Leaf ()
blankTree = R2.leaf blankTreeCpt
blankTreeCpt :: R.Component ()
blankTreeCpt = here.component "__blank__" cpt where
cpt _ _ = pure $
renderTreeChildren :: R2.Component ChildrenTreeProps
renderTreeChildren = R.createElement renderTreeChildrenCpt
H.div
{ className: "maintree maintree--blank" }
[
H.div
{ className: "maintree__node" }
[
blankNodeSpan
{}
]
]
renderTreeChildren :: R2.Leaf ChildrenTreeProps
renderTreeChildren = R2.leaf renderTreeChildrenCpt
renderTreeChildrenCpt :: R.Component ChildrenTreeProps
renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where
cpt p@{ childProps: { children'
......@@ -199,6 +251,7 @@ renderTreeChildrenCpt = here.component "renderTreeChildren" cpt where
renderChild (NTree (LNode {id: cId}) _) = childLoader props [] where
props = Record.merge nodeProps { id: cId, render, root }
childLoader :: R2.Component ChildLoaderProps
childLoader = R.createElement childLoaderCpt
childLoaderCpt :: R.Component ChildLoaderProps
......@@ -207,13 +260,32 @@ childLoaderCpt = here.component "childLoader" cpt where
, reloadTree
, render
, root } _ = do
-- States
reload <- T.useBox T2.newReload
state /\ stateBox <- R2.useBox' Nothing
let reloads = [ reload, reloadRoot, reloadTree ]
cache <- (A.cons p.id) <$> traverse (T.useLive T.unequal) reloads
useLoader { errorHandler
, loader: fetch
, path: cache
, render: paint reload }
-- Hooks
useLoaderEffect
{ 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
errorHandler = logRESTError here "[childLoader]"
fetch _ = getNodeTreeFirstLevel p.session p.id
......
exports.nodeUserRegexp = /(@{1}.*).gargantext.org$/;
......@@ -178,6 +178,15 @@ isExternal :: Maybe DataField -> Boolean
isExternal (Just (External _)) = true
isExternal _ = false
isArxiv :: Maybe DataField -> Boolean
isArxiv (Just
( External
( Just Arxiv
)
)
) = true
isArxiv _ = false
isHAL :: Maybe DataField -> Boolean
isHAL (Just
( External
......
......@@ -104,6 +104,7 @@ datafield2database _ = Empty
allDatabases :: Array Database
allDatabases = [ Empty
, PubMed
, Arxiv
, HAL Nothing
, IsTex
, IsTex_Advanced
......@@ -116,6 +117,7 @@ allDatabases = [ Empty
data Database = All_Databases
| Empty
| PubMed
| Arxiv
| HAL (Maybe Org)
| IsTex
| IsTex_Advanced
......@@ -124,34 +126,37 @@ data Database = All_Databases
-- | SocialNetworks
derive instance Generic Database _
instance Show Database where
show All_Databases= "All Databases"
show PubMed = "PubMed"
show (HAL _)= "HAL"
show IsTex = "IsTex"
show IsTex_Advanced = "IsTex_Advanced"
show Isidore= "Isidore"
show Empty = "Empty"
show All_Databases = "All Databases"
show PubMed = "PubMed"
show Arxiv = "Arxiv"
show (HAL _) = "HAL"
show IsTex = "IsTex"
show IsTex_Advanced = "IsTex_Advanced"
show Isidore = "Isidore"
show Empty = "Empty"
-- show News = "News"
-- show SocialNetworks = "Social Networks"
instance Doc Database where
doc All_Databases = "All databases"
doc PubMed = "All Medical publications"
doc (HAL _) = "All open science (archives ouvertes)"
doc IsTex = "All Elsevier enriched by CNRS/INIST"
doc All_Databases = "All databases"
doc PubMed = "All Medical publications"
doc Arxiv = "Arxiv"
doc (HAL _) = "All open science (archives ouvertes)"
doc IsTex = "All Elsevier enriched by CNRS/INIST"
doc IsTex_Advanced = "IsTex advanced search"
doc Isidore = "All (French) Social Sciences"
doc Empty = "Empty"
doc Isidore = "All (French) Social Sciences"
doc Empty = "Empty"
-- doc News = "Web filtered by News"
-- doc SocialNetworks = "Web filtered by MicroBlogs"
instance Read Database where
read :: String -> Maybe Database
read "All Databases" = Just All_Databases
read "PubMed" = Just PubMed
read "HAL" = Just $ HAL Nothing
read "Isidore"= Just Isidore
read "IsTex" = Just IsTex
read "All Databases" = Just All_Databases
read "PubMed" = Just PubMed
read "Arxiv" = Just Arxiv
read "HAL" = Just $ HAL Nothing
read "Isidore" = Just Isidore
read "IsTex" = Just IsTex
read "IsTex_Advanced" = Just IsTex_Advanced
-- read "Web" = Just Web
-- read "News" = Just News
......
......@@ -432,7 +432,7 @@ fileTypeViewCpt = here.component "fileTypeView" cpt
droppedFile' <- T.useLive T.unequal droppedFile
case droppedFile' of
Nothing -> pure $ H.div {} []
Nothing -> pure $ mempty
Just df ->
pure $ H.div tooltipProps [ H.div { className: "card"}
[ panelHeading
......
module Gargantext.Components.Forest.Tree.Node.Tools where
import Data.Foldable (intercalate)
import Data.Maybe (fromMaybe, Maybe(..))
import Gargantext.Prelude
import Data.Maybe (fromMaybe)
import Data.Set (Set)
import Data.Set as Set
import Data.String as S
import Data.String.CodeUnits as DSCU
import Effect (Effect)
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.Types (Action)
import Gargantext.Components.GraphExplorer.Types (mCameraP)
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.Utils (toggleSet, (?))
import Gargantext.Utils (toggleSet)
import Gargantext.Utils.Glyphicon (glyphicon)
import Gargantext.Utils.ReactTooltip as ReactTooltip
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
import Web.HTML.ValidityState (valid)
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools"
......@@ -60,7 +53,7 @@ textInputBox :: R2.Component TextInputBoxProps
textInputBox = R.createElement textInputBoxCpt
textInputBoxCpt :: R.Component TextInputBoxProps
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
where
content false _ = (R.fragment [])
......@@ -256,101 +249,3 @@ prettyNodeType
= S.replace (S.Pattern "Node") (S.Replacement " ")
<<< S.replace (S.Pattern "Folder") (S.Replacement " ")
<<< 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
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record.Extra as RX
import Toestand as T
here :: R2.Here
......@@ -51,7 +52,8 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt
R.useEffectOnce' $ do
intervalId <- setInterval 1000 $ do
launchAff_ $ do
eAsyncProgress <- queryProgress props
let rdata = (RX.pick props :: Record QueryProgressData)
eAsyncProgress <- queryProgress rdata
handleRESTError errors eAsyncProgress $ \asyncProgress -> liftEffect $ do
let GT.AsyncProgress { status } = asyncProgress
T.write_ (min 100.0 $ GT.progressPercent asyncProgress) progress
......@@ -71,6 +73,9 @@ asyncProgressBarCpt = here.component "asyncProgressBar" cpt
pure $ progressIndicator { barType, label: id, progress }
--------------------------------------------------------------
type ProgressIndicatorProps =
( barType :: BarType
, label :: String
......@@ -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}
, typ
}
......
......@@ -10,7 +10,6 @@ import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
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.Tools (nodeText)
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..))
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeParams(..), SubTreeOut(..))
import Gargantext.Config.REST (AffRESTError, logRESTError)
......@@ -18,7 +17,7 @@ import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes as GR
import Gargantext.Sessions (Session(..), get)
import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils (textEllipsisBreak, (?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
......@@ -141,11 +140,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
{ className: intercalate " "
[ "subtree__node"
, validNodeType ? "subtree__node--can-be-selected" $ ""
, (isSelected targetId action) ? "subtree__node--is-selected" $ ""
]
}
[
H.div
{ className: "subtree__node__text" }
{ className: "subtree__node__inner" }
[
H.div
{ className: "subtree__node__icons"
......@@ -162,13 +162,12 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
H.span { className: "fa fa-chevron-right" } []
]
,
H.div
{ on: { click: selectCbk } }
H.span
{ on: { click: selectCbk }
, className: "subtree__node__text"
}
[
nodeText
{ isSelected: isSelected targetId action
, name
}
H.text $ textEllipsisBreak 15 name
]
]
,
......
module Gargantext.Components.Forest.Tree.Node.Tools.Sync where
import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff_)
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.Prelude (Unit, bind, discard, pure, unit, ($), (<>), (==))
import Gargantext.Sessions (Session)
import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
......@@ -27,15 +31,15 @@ type NodeActionsGraphProps =
nodeActionsGraph :: R2.Component NodeActionsGraphProps
nodeActionsGraph = R.createElement nodeActionsGraphCpt
nodeActionsGraphCpt :: R.Component NodeActionsGraphProps
nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt
where
cpt { id, graphVersions, session, refresh } _ = do
pure $ H.div { className: "node-actions" } [
if graphVersions.gv_graph == Just graphVersions.gv_repo then
H.div {} []
else
graphUpdateButton { id, session, refresh }
]
nodeActionsGraphCpt = here.component "nodeActionsGraph" cpt where
cpt { id, graphVersions, session, refresh } _ =
let sameVersions = (graphVersions.gv_graph == Just graphVersions.gv_repo)
in pure $
R2.if' (not sameVersions) $
graphUpdateButton { id, session, refresh }
type GraphUpdateButtonProps =
( id :: GT.ID
......@@ -53,13 +57,24 @@ graphUpdateButtonCpt = here.component "graphUpdateButton" cpt
enabled <- T.useBox true
enabled' <- T.useLive T.unequal enabled
pure $ H.div { className: "update-button "
<> if enabled'
then "enabled"
else "disabled text-muted"
} [ H.span { className: "fa fa-refresh"
, on: { click: onClick enabled' enabled } } []
]
pure $
B.iconButton
{ className: "mainleaf__update-icon"
, variant: Secondary
, 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
onClick false _ = pure unit
onClick true enabled = do
......@@ -84,12 +99,9 @@ type NodeActionsNodeListProps =
nodeActionsNodeList :: Record NodeActionsNodeListProps -> R.Element
nodeActionsNodeList p = R.createElement nodeActionsNodeListCpt p []
nodeActionsNodeListCpt :: R.Component NodeActionsNodeListProps
nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt
where
cpt props _ = do
pure $ H.div { className: "node-actions" } [
nodeListUpdateButton props
]
nodeActionsNodeListCpt = here.component "nodeActionsNodeList" cpt where
cpt props _ = pure $ nodeListUpdateButton props
type NodeListUpdateButtonProps =
( listId :: GT.ListId
......
......@@ -132,9 +132,12 @@ graphCpt = here.component "graph" cpt where
Sigma.stopForceAtlas2 sig
case mCamera of
Nothing -> pure unit
Just (GET.Camera { ratio, x, y }) -> do
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
_ <- flip T.listen boxes.theme \{ old, new } ->
......
module Gargantext.Components.GraphExplorer.Button
( Props, centerButton, simpleButton, cameraButton ) where
module Gargantext.Components.GraphExplorer.Buttons
( Props
, centerButton
, simpleButton
, cameraButton
, edgesToggleButton
, louvainToggleButton
, pauseForceAtlasButton
, resetForceAtlasButton
, multiSelectEnabledButton
) where
import Prelude
import DOM.Simple.Console (log2)
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.Either (Either(..))
import Data.Enum (fromEnum)
import Data.Maybe (Maybe(..))
import Data.DateTime as DDT
import Data.DateTime.Instant as DDI
import Data.String as DS
import DOM.Simple.Console (log2)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Effect.Now as EN
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Components.Bootstrap as B
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.Types (FileFormat(..))
import Gargantext.Components.GraphExplorer.API (cloneGraph)
import Gargantext.Components.GraphExplorer.Resources as Graph
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.GraphExplorer.Utils as GEU
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Sessions (Session)
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Button"
......@@ -36,9 +50,12 @@ type Props = (
, text :: String
)
-- @WIP
simpleButton :: Record Props -> R.Element
simpleButton props = R.createElement simpleButtonCpt props []
------------------------------------------------------
simpleButtonCpt :: R.Component Props
simpleButtonCpt = here.component "simpleButton" cpt
where
......@@ -48,14 +65,16 @@ simpleButtonCpt = here.component "simpleButton" cpt
} [ R2.small {} [ H.text text ] ]
centerButton :: R.Ref Sigmax.Sigma -> R.Element
centerButton sigmaRef = simpleButton {
onClick: \_ -> do
centerButton sigmaRef = B.button
{ variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[centerButton] sigma: Nothing" $ \s ->
Sigma.goToAllCameras s {x: 0.0, y: 0.0, ratio: 1.0, angle: 0.0}
, text: "Center"
}
[ H.text "Center" ]
------------------------------------------------------
type CameraButtonProps =
( id :: Int
......@@ -71,8 +90,10 @@ cameraButton { id
, hyperdataGraph: GET.HyperdataGraph { graph: GET.GraphData hyperdataGraph }
, session
, sigmaRef
, reloadForest } = simpleButton {
onClick: \_ -> do
, reloadForest } = B.button
{ variant: OutlinedButtonVariant Secondary
, callback: \_ -> do
let sigma = R.readRef sigmaRef
Sigmax.dependOnSigma sigma "[cameraButton] sigma: Nothing" $ \s -> do
screen <- Sigma.takeScreenshot s
......@@ -105,5 +126,171 @@ cameraButton { id
Left err -> liftEffect $ log2 "[cameraButton] RESTError" err
Right _ret -> do
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
( Props, legend, legendCpt
( Props, legend
) where
import Prelude hiding (map)
......@@ -7,31 +7,39 @@ import Prelude hiding (map)
import Data.Sequence (Seq)
import Data.Traversable (foldMap)
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.Utils.Reactix as R2
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Legend"
type Props = ( items :: Seq Legend )
legend :: Record Props -> R.Element
legend props = R.createElement legendCpt props []
legend :: R2.Leaf Props
legend = R2.leaf legendCpt
legendCpt :: R.Component Props
legendCpt = here.component "legend" cpt
where
cpt {items} _ = pure $ RH.div {} [foldMap entry items]
entry :: Legend -> R.Element
entry (Legend {id_, label}) =
RH.p {}
[ RH.span { style: { width : 10
, height: 10
, backgroundColor: intColor id_
, display: "inline-block"
}
} []
, RH.text $ " " <> label
]
legendCpt = here.component "legend" cpt where
cpt { items } _ = pure $
H.ul
{ className: "graph-legend" }
[
flip foldMap items \(Legend { id_, label }) ->
H.li
{ className: "graph-legend__item" }
[
H.span
{ className: "graph-legend__code"
, style: { backgroundColor: intColor id_ }
}
[]
,
H.span
{ className: "graph-legend__caption" }
[ H.text label ]
]
]
......@@ -18,37 +18,45 @@ import Gargantext.Utils.Reactix as R2
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.RangeControl"
type Props = (
caption :: String
type Props =
( caption :: String
, sliderProps :: Record RS.Props
)
rangeControl :: R2.Component Props
rangeControl = R.createElement rangeControlCpt
rangeControl :: R2.Leaf Props
rangeControl = R2.leaf rangeControlCpt
rangeControlCpt :: R.Component Props
rangeControlCpt = here.component "rangeButton" cpt
where
cpt {caption, sliderProps} _ = do
pure $
H.span {className: "range text-center"}
[ H.label {} [ R2.small {} [ H.text caption ] ]
, RS.rangeSlider sliderProps
]
type EdgeConfluenceControlProps = (
range :: Range.NumberRange
cpt {caption, sliderProps} _ = pure $
H.span
{ className: "range-control" }
[
H.label
{ className: "range-control__label" }
[ H.text caption ]
,
RS.rangeSlider sliderProps
]
----------------------------------------
type EdgeConfluenceControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange
)
edgeConfluenceControl :: R2.Component EdgeConfluenceControlProps
edgeConfluenceControl = R.createElement edgeConfluenceControlCpt
edgeConfluenceControl :: R2.Leaf EdgeConfluenceControlProps
edgeConfluenceControl = R2.leaf edgeConfluenceControlCpt
edgeConfluenceControlCpt :: R.Component EdgeConfluenceControlProps
edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
where
cpt { range: Range.Closed { min, max }
, state } _ = do
, state
} _ = do
state' <- T.useLive T.unequal state
pure $ rangeControl {
......@@ -62,21 +70,24 @@ edgeConfluenceControlCpt = here.component "edgeConfluenceControl" cpt
, height: 5.0
, onChange: \rng -> T.write_ rng state
}
} []
}
type EdgeWeightControlProps = (
range :: Range.NumberRange
--------------------------------------
type EdgeWeightControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange
)
edgeWeightControl :: R2.Component EdgeWeightControlProps
edgeWeightControl = R.createElement edgeWeightControlCpt
edgeWeightControl :: R2.Leaf EdgeWeightControlProps
edgeWeightControl = R2.leaf edgeWeightControlCpt
edgeWeightControlCpt :: R.Component EdgeWeightControlProps
edgeWeightControlCpt = here.component "edgeWeightControl" cpt
where
cpt { range: Range.Closed { min, max }
, state } _ = do
, state
} _ = do
state' <- T.useLive T.unequal state
pure $ rangeControl {
......@@ -90,21 +101,24 @@ edgeWeightControlCpt = here.component "edgeWeightControl" cpt
, height: 5.0
, onChange: \rng -> T.write_ rng state
}
} []
}
--------------------------------------
type NodeSideControlProps = (
range :: Range.NumberRange
type NodeSideControlProps =
( range :: Range.NumberRange
, state :: T.Box Range.NumberRange
)
nodeSizeControl :: R2.Component NodeSideControlProps
nodeSizeControl = R.createElement nodeSizeControlCpt
nodeSizeControl :: R2.Leaf NodeSideControlProps
nodeSizeControl = R2.leaf nodeSizeControlCpt
nodeSizeControlCpt :: R.Component NodeSideControlProps
nodeSizeControlCpt = here.component "nodeSizeControl" cpt
where
cpt { range: Range.Closed { min, max }
, state } _ = do
, state
} _ = do
state' <- T.useLive T.unequal state
pure $ rangeControl {
......@@ -118,4 +132,4 @@ nodeSizeControlCpt = here.component "nodeSizeControl" cpt
, height: 5.0
, onChange: \rng -> T.write_ rng state
}
} []
}
This diff is collapsed.
......@@ -4,10 +4,11 @@ module Gargantext.Components.GraphExplorer.Search
import Prelude
import DOM.Simple.Console (log2)
import Data.Foldable (foldl)
import Data.Foldable (foldl, intercalate)
import Data.Sequence as Seq
import Data.Set as Set
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Utils (queryMatchesLabel)
......@@ -23,6 +24,7 @@ type Props = (
graph :: SigmaxT.SGraph
, multiSelectEnabled :: T.Box Boolean
, selectedNodeIds :: T.Box SigmaxT.NodeIds
, className :: String
)
-- | Whether a node matches a search string
......@@ -37,28 +39,43 @@ searchNodes :: String -> Seq.Seq (Record SigmaxT.Node) -> Seq.Seq (Record Sigmax
searchNodes "" _ = Seq.empty
searchNodes s nodes = Seq.filter (nodeMatchesSearch s) nodes
nodeSearchControl :: R2.Component Props
nodeSearchControl = R.createElement nodeSearchControlCpt
nodeSearchControl :: R2.Leaf Props
nodeSearchControl = R2.leaf nodeSearchControlCpt
nodeSearchControlCpt :: R.Component Props
nodeSearchControlCpt = here.component "nodeSearchControl" cpt
where
cpt { graph, multiSelectEnabled, selectedNodeIds } _ = do
cpt props@{ graph, multiSelectEnabled, selectedNodeIds } _ = do
search <- T.useBox ""
search' <- T.useLive T.unequal search
multiSelectEnabled' <- T.useLive T.unequal multiSelectEnabled
let doSearch s = triggerSearch graph s multiSelectEnabled' selectedNodeIds
pure $ R.fragment
[ inputWithAutocomplete { autocompleteSearch: autocompleteSearch graph
, classes: "mx-2"
, onAutocompleteClick: doSearch
, onEnterPress: doSearch
, state: search } []
, H.div { className: "btn input-group-addon"
, on: { click: \_ -> doSearch search' }
}
[ H.span { className: "fa fa-search" } [] ]
pure $
H.form
{ className: intercalate " "
[ "graph-node-search"
, props.className
]
}
[
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
......
......@@ -26,7 +26,8 @@ type UserInfo
, ui_cwTouchPhone :: Maybe String
, ui_cwTouchMail :: Maybe String }
type UserInfoM
= { ui_id :: NotNull Int
= { token :: NotNull String
, ui_id :: NotNull Int
, ui_username :: String
, ui_email :: String
, ui_title :: String
......
......@@ -29,8 +29,8 @@ type Props =
, state :: T.Box String
)
inputWithAutocomplete :: R2.Component Props
inputWithAutocomplete = R.createElement inputWithAutocompleteCpt
inputWithAutocomplete :: R2.Leaf Props
inputWithAutocomplete = R2.leaf inputWithAutocompleteCpt
inputWithAutocompleteCpt :: R.Component Props
inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
where
......
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