Commit 9658b382 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'purescript-format' into 'dev'

Purescript format

See merge request !488
parents 15a3dfbf 29c676c4
Pipeline #7078 failed with stages
in 24 minutes and 13 seconds
3ab0ce9ebc2b62ae09099e9d037e953f8ba252cf
# Fixing the nixos image saves CI time so it doesn't have to pull new
#image every time (nixos/nix updates quite often).
# image every time (nixos/nix updates quite often).
# image: nixos/nix:latest
image: nixos/nix:2.24.8
#before_script:
#- nix-env -iA nixpkgs.nix nixpkgs.cacert
#- apt-get update
#- apt-get install make xz-utils
stages:
# - deps
- compile
- test
- lint
# deps:
# stage: deps
......@@ -38,8 +34,6 @@ compile:
test:
stage: test
cache:
# cache per branch name
# key: ${CI_COMMIT_REF_SLUG}
paths:
- /nix/store
- node_modules/
......@@ -51,3 +45,15 @@ test:
- nix --extra-experimental-features "nix-command flakes" run .#test-ps
- nix-collect-garbage --delete-older-than 14d
lint:
stage: lint
cache:
paths:
- /nix/store
- node_modules/
- output/
- .spago/
script:
- nix --extra-experimental-features "nix-command flakes" run .#purs-tidy -- check src
purs-tidy check "src"
if [ $? -ne 0 ]; then
echo "purs tidy check failed. Please fix the issues before committing."
exit 1
fi
......@@ -22,12 +22,10 @@
dependencies = with pkgs; [
purs-bin.purs-0_15_16-1 # from the purescript-overlay
spago-bin.spago-0_93_37
#purescript
#spago-unstable
nodePackages.purs-tidy
watchexec
esbuild
nodejs
watchexec
pscid
nodePackages.npm
];
......@@ -37,12 +35,15 @@
npm run server
'';
build-watch = pkgs.writeShellScriptBin "build-watch" ''
setup-gitblame = pkgs.writeShellScriptBin "setup-gitblame" ''
set -e
echo "Build watch"
npm spago build -w --then browserify
if git config --get blame.ignoreRevsFile > /dev/null; then
echo "blame.ignoreRevsFile is already set."
else
git config blame.ignoreRevsFile .git-blame-ignore-revs
echo "blame.ignoreRevsFile has been set to .git-blame-ignore-revs."
fi
'';
build-zephyr = pkgs.writeShellScriptBin "build-zephyr" ''
......@@ -157,7 +158,7 @@
self.packages.${system}.test-ps
self.packages.${system}.repl
build-watch
setup-gitblame
build-zephyr
minify-bundle
serve
......
......@@ -60,6 +60,7 @@
"@babel/preset-react": "~7.24.1",
"@getgauge/cli": "~1.4.0",
"esbuild": "~0.21.1",
"husky": "^9.1.7",
"parcel": "~2.8.2",
"process": "^0.11.10",
"react-testing-library": "~8.0.1",
......@@ -7428,6 +7429,21 @@
"node": ">= 6"
}
},
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
"bin": {
"husky": "bin.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"license": "MIT",
......
......@@ -20,7 +20,8 @@
"server-ssl": "ssl-serve --ssl dist",
"test": "spago test",
"dev": "concurrently \"npm run watch\" \"npm run serve\" ",
"serve": "npx parcel serve --dist-dir ./dist/dev/bundle ./dist/dev/index.html"
"serve": "npx parcel serve --dist-dir ./dist/dev/bundle ./dist/dev/index.html",
"prepare": "husky"
},
"dependencies": {
"@fontsource/crete-round": "~5.0.12",
......@@ -75,6 +76,7 @@
"@babel/preset-react": "~7.24.1",
"@getgauge/cli": "~1.4.0",
"esbuild": "~0.21.1",
"husky": "^9.1.7",
"parcel": "~2.8.2",
"process": "^0.11.10",
"react-testing-library": "~8.0.1",
......
module Gargantext.AsyncTasks (
Task
module Gargantext.AsyncTasks
( Task
, TaskList
, Storage(..)
, insert
......@@ -8,8 +8,7 @@ module Gargantext.AsyncTasks (
-- , asyncTaskTTriggersAppReload
-- , asyncTaskTTriggersTreeReload
-- , asyncTaskTTriggersMainPageReload
)
where
) where
import Gargantext.Prelude
......@@ -18,7 +17,7 @@ import Data.Array as A
import Data.Either (Either(..))
import Data.Map as Map
import Data.Maybe (Maybe(..), maybe, fromMaybe)
import Data.Monoid (class Monoid)
import Data.Monoid (class Monoid)
import Data.Semigroup (class Semigroup)
import Data.Tuple (Tuple(..))
import Effect (Effect)
......@@ -33,25 +32,27 @@ import Simple.JSON as JSON
import Toestand as T
import Web.Storage.Storage as WSS
type Task = GT.WorkerTask
type TaskList = Array Task
newtype Storage = Storage (Map.Map GT.NodeID TaskList)
derive newtype instance Semigroup Storage
derive newtype instance Monoid Storage
instance JSON.ReadForeign Storage where
readImpl f = do
m <- GUJ.readMapInt f
pure $ Storage m
instance JSON.WriteForeign Storage where
writeImpl (Storage s) = JSON.writeImpl $ FO.fromFoldable arr
where
arr :: Array (Tuple String TaskList)
arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s)
arr :: Array (Tuple String TaskList)
arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s)
modifyTaskBox :: (Storage -> Storage) -> T.Box Storage -> Effect Unit
modifyTaskBox f box = T.modify_ f box
-- modifyAsyncTasks (const newS)
-- modifyAsyncTasks (const newS)
getTasks :: GT.NodeID -> Storage -> TaskList
getTasks nodeId (Storage storage) = fromMaybe [] $ Map.lookup nodeId storage
......@@ -66,16 +67,16 @@ removeTaskFromList :: TaskList -> Task -> TaskList
removeTaskFromList ts (GT.WorkerTask { message_id }) =
A.filter (\(GT.WorkerTask { message_id: message_id' }) -> message_id /= message_id') ts
type ReductorProps = (
reloadForest :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, storage :: Storage
type ReductorProps =
( reloadForest :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, storage :: Storage
)
insert :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
insert id task storageBox = modifyTaskBox newStorage storageBox
where
newStorage (Storage s) = Storage $ Map.alter (maybe (Just [task]) (\ts -> Just $ A.nub $ A.cons task ts)) id s
newStorage (Storage s) = Storage $ Map.alter (maybe (Just [ task ]) (\ts -> Just $ A.nub $ A.cons task ts)) id s
finish :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
finish id task storage = remove id task storage
......@@ -83,8 +84,7 @@ finish id task storage = remove id task storage
remove :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
remove id task storageBox = modifyTaskBox newStorage storageBox
where
newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s
newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s
-- AsyncTaskWithType is deprecated, but we leave these functions here,
-- becuase they're a useful reference
......@@ -112,7 +112,6 @@ remove id task storageBox = modifyTaskBox newStorage storageBox
-- asyncTaskTTriggersTreeReload :: GT.AsyncTaskWithType -> Boolean
-- asyncTaskTTriggersTreeReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersTreeReload typ
-- With push-based notifications, it doesn't make sense to store jobs in localStorage
-- readAsyncTasks :: Effect Storage
-- readAsyncTasks = R2.loadLocalStorageState' R2.asyncTasksKey mempty
......
......@@ -26,30 +26,31 @@ type Props =
type AnnotationMenu =
( closeCallback :: Unit -> Effect Unit
, redrawMenu :: T.Box Boolean
, x :: Number
, y :: Number
, list :: Maybe TermList
, menuType :: MenuType
, setList :: TermList -> Effect Unit -- not a state hook setter
, redrawMenu :: T.Box Boolean
, x :: Number
, y :: Number
, list :: Maybe TermList
, menuType :: MenuType
, setList :: TermList -> Effect Unit -- not a state hook setter
)
annotationMenu :: R2.Leaf Props
annotationMenu = R2.leaf annotationMenuCpt
annotationMenuCpt :: R.Component Props
annotationMenuCpt = here.component "main" cpt where
annotationMenuCpt = here.component "main" cpt
where
cpt { menuRef } _ = do
-- Render
pure $
R2.fromMaybe (R.readRef menuRef) \props' ->
B.contextMenu
{ x: props'.x
, y: props'.y
, closeCallback: props'.closeCallback
} $
(addToList props') <$> [ MapTerm, CandidateTerm, StopTerm ]
{ x: props'.x
, y: props'.y
, closeCallback: props'.closeCallback
}
$ (addToList props')
<$> [ MapTerm, CandidateTerm, StopTerm ]
--------------------------------------------------------------------------
......@@ -75,33 +76,29 @@ annotationMenuCpt = here.component "main" cpt where
-- click _ = setList t
addToList :: Record AnnotationMenu -> TermList -> R.Element
addToList {list: Just t', menuType} t
addToList { list: Just t', menuType } t
| t == t' =
B.contextMenuItem
{ callback: const R.nothing
, status: Disabled
}
[
B.icon
{ name: "circle"
, className: "mr-2 disabled-term"
}
,
H.text (label t menuType)
]
B.contextMenuItem
{ callback: const R.nothing
, status: Disabled
}
[ B.icon
{ name: "circle"
, className: "mr-2 disabled-term"
}
, H.text (label t menuType)
]
addToList {menuType, setList} t =
B.contextMenuItem
addToList { menuType, setList } t =
B.contextMenuItem
{ callback: const $ setList t }
[
B.icon
{ name: "circle"
, className: "mr-2 " <> termClass t
}
,
H.text (label t menuType)
[ B.icon
{ name: "circle"
, className: "mr-2 " <> termClass t
}
, H.text (label t menuType)
]
label :: TermList -> MenuType -> String
label t NewNgram = "Add to " <> (toLower $ termListName t)
label t NewNgram = "Add to " <> (toLower $ termListName t)
label t SetTermListItem = "Change to " <> (toLower $ termListName t)
......@@ -2,8 +2,7 @@ module Gargantext.Components.Annotation.Types
( MenuType(..)
, termClass
, ModeType(..)
)
where
) where
import Gargantext.Prelude
......@@ -16,6 +15,7 @@ import Gargantext.Types (TermList(..))
---------------------------------------------------------
data MenuType = NewNgram | SetTermListItem
derive instance Generic MenuType _
instance Eq MenuType where
eq = genericEq
......@@ -24,8 +24,8 @@ instance Eq MenuType where
termClass :: TermList -> String
termClass CandidateTerm = "candidate-term"
termClass MapTerm = "graph-term"
termClass StopTerm = "stop-term"
termClass MapTerm = "graph-term"
termClass StopTerm = "stop-term"
---------------------------------------------------------
......@@ -34,11 +34,15 @@ data ModeType
| AdditionMode
derive instance Generic ModeType _
instance Eq ModeType where eq = genericEq
instance Show ModeType where show = genericShow
instance Eq ModeType where
eq = genericEq
instance Show ModeType where
show = genericShow
instance Read ModeType where
read :: String -> Maybe ModeType
read = case _ of
"EditionMode" -> Just EditionMode
"EditionMode" -> Just EditionMode
"AdditionMode" -> Just AdditionMode
_ -> Nothing
_ -> Nothing
......@@ -25,11 +25,12 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.App"
app :: R2.Leaf ()
app = R2.leaf appCpt
appCpt :: R.Component ()
appCpt = here.component "container" cpt where
appCpt = here.component "container" cpt
where
cpt _ _ = do
-- | States
-- |
......@@ -55,14 +56,18 @@ type HydrateStoreProps =
hydrateStore :: R2.Leaf HydrateStoreProps
hydrateStore = R2.leaf hydrateStoreCpt
hydrateStoreCpt :: R.Component HydrateStoreProps
hydrateStoreCpt = here.component "hydrateStore" cpt where
cpt { cacheParams
} _ = do
hydrateStoreCpt = here.component "hydrateStore" cpt
where
cpt
{ cacheParams
}
_ = do
-- | Computed
-- |
wsNotification <- RU.hook $ \_ -> NotificationsT.emptyWSNotification
(state :: Record AppStore.State) <- pure $
-- (cache options)
{ expandTableEdition: getter _.expandTableEdition cacheParams
......@@ -73,20 +78,20 @@ hydrateStoreCpt = here.component "hydrateStore" cpt where
-- | Render
-- |
pure $
AppStore.provide
state
[
mainApp
{}
]
state
[ mainApp
{}
]
--------------------------------------------------------------
mainApp :: R2.Leaf ()
mainApp = R2.leaf mainAppCpt
mainAppCpt :: R.Component ()
mainAppCpt = here.component "main" cpt where
mainAppCpt = here.component "main" cpt
where
cpt _ _ = do
boxes <- AppStore.use
-- tasks <- T.useBox Nothing -- storage for asynchronous tasks reductor
......@@ -100,7 +105,7 @@ mainAppCpt = here.component "main" cpt where
-- R.useEffectOnce' $ do
-- tasksStorage <- GAT.readAsyncTasks
-- T.write_ tasksStorage boxes.tasks
-- R.useEffectOnce' $ do
-- T.write (Just tasksReductor) tasks
R.useEffectOnce' $ do
......@@ -117,10 +122,10 @@ mainAppCpt = here.component "main" cpt where
wsProto <- Notifications.wsProtocol
h <- host
Notifications.connect ws (wsProto <> "://" <> h <> "/ws") session
-- T.write_ ws boxes.wsNotification
-- NOTE: Dummy subscription
-- let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree (-1)) "some-uuid" (\_ -> here.log "callback!")
-- Notifications.performAction ws action
-- T.write_ ws boxes.wsNotification
-- NOTE: Dummy subscription
-- let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree (-1)) "some-uuid" (\_ -> here.log "callback!")
-- Notifications.performAction ws action
-- Store in the box the current backend, as
-- derived from the href, before the window gets
......@@ -130,4 +135,4 @@ mainAppCpt = here.component "main" cpt where
T.write_ mLoc boxes.backend
useHashRouter Router.router boxes.route -- Install router to window
pure $ router { boxes } -- Render router component
pure $ router { boxes } -- Render router component
......@@ -9,7 +9,6 @@ module Gargantext.Components.App.Store
, Boxes
) where
import Gargantext.Prelude
import Data.Map (Map)
......@@ -37,100 +36,99 @@ import Record as Record
import Toestand as T
import Unsafe.Coerce (unsafeCoerce)
here :: R2.Here
here = R2.here "Gargantext.Components.App.Store"
type Store =
( backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, expandTableEdition :: T.Box Boolean
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, loginRedirect :: T.Box (Maybe (Tuple String ID))
, pinnedTreeId :: T.Box (Map String Int)
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, showSearch :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsSP.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
, wsNotification :: T.Box Notifications.WSNotification
( backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, expandTableEdition :: T.Box Boolean
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, loginRedirect :: T.Box (Maybe (Tuple String ID))
, pinnedTreeId :: T.Box (Map String Int)
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, showSearch :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsSP.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
, wsNotification :: T.Box Notifications.WSNotification
)
type State =
( backend :: Maybe Backend
, errors :: Array FrontendError
, expandTableEdition :: Boolean
, forestOpen :: OpenNodes
, graphVersion :: T2.Reload
, handed :: Handed
, lang :: Lang.LandingLang
, loginRedirect :: Maybe (Tuple String ID)
, pinnedTreeId :: Map String Int
, reloadForest :: T2.Reload
, reloadMainPage :: T2.Reload
, reloadRoot :: T2.Reload
, route :: AppRoute
, session :: Maybe Session
, sessions :: Sessions
, showCorpus :: Boolean
, showLogin :: Boolean
, showTree :: Boolean
, showSearch :: Boolean
, sidePanelLists :: Maybe (Record ListsSP.SidePanel)
, sidePanelTexts :: Maybe (Record TextsT.SidePanel)
, sidePanelState :: SidePanelState
, tasks :: GAT.Storage
, theme :: Themes.Theme
, tileAxisXList :: Array (Record Tile)
, tileAxisYList :: Array (Record Tile)
, wsNotification :: Notifications.WSNotification
( backend :: Maybe Backend
, errors :: Array FrontendError
, expandTableEdition :: Boolean
, forestOpen :: OpenNodes
, graphVersion :: T2.Reload
, handed :: Handed
, lang :: Lang.LandingLang
, loginRedirect :: Maybe (Tuple String ID)
, pinnedTreeId :: Map String Int
, reloadForest :: T2.Reload
, reloadMainPage :: T2.Reload
, reloadRoot :: T2.Reload
, route :: AppRoute
, session :: Maybe Session
, sessions :: Sessions
, showCorpus :: Boolean
, showLogin :: Boolean
, showTree :: Boolean
, showSearch :: Boolean
, sidePanelLists :: Maybe (Record ListsSP.SidePanel)
, sidePanelTexts :: Maybe (Record TextsT.SidePanel)
, sidePanelState :: SidePanelState
, tasks :: GAT.Storage
, theme :: Themes.Theme
, tileAxisXList :: Array (Record Tile)
, tileAxisYList :: Array (Record Tile)
, wsNotification :: Notifications.WSNotification
)
options :: Notifications.WSNotification -> Record State
options wsNotification =
{ wsNotification } `Record.merge`
{ backend : Nothing
, errors : []
, expandTableEdition : false
, forestOpen : OpenNodes $ Set.empty
, graphVersion : T2.newReload
, handed : RightHanded
, lang : Lang.LL_EN
, loginRedirect : Nothing
, pinnedTreeId : Map.empty
, reloadForest : T2.newReload
, reloadMainPage : T2.newReload
, reloadRoot : T2.newReload
, route : Home
, session : Nothing
, sessions : Sessions.empty
, showCorpus : false
, showLogin : false
, showTree : true
, showSearch : false
, sidePanelLists : ListsSP.initialSidePanel
, sidePanelTexts : TextsT.initialSidePanel
, sidePanelState : InitialClosed
, tasks : mempty
, theme : Themes.defaultTheme
, tileAxisXList : mempty
, tileAxisYList : mempty
}
{ backend: Nothing
, errors: []
, expandTableEdition: false
, forestOpen: OpenNodes $ Set.empty
, graphVersion: T2.newReload
, handed: RightHanded
, lang: Lang.LL_EN
, loginRedirect: Nothing
, pinnedTreeId: Map.empty
, reloadForest: T2.newReload
, reloadMainPage: T2.newReload
, reloadRoot: T2.newReload
, route: Home
, session: Nothing
, sessions: Sessions.empty
, showCorpus: false
, showLogin: false
, showTree: true
, showSearch: false
, sidePanelLists: ListsSP.initialSidePanel
, sidePanelTexts: TextsT.initialSidePanel
, sidePanelState: InitialClosed
, tasks: mempty
, theme: Themes.defaultTheme
, tileAxisXList: mempty
, tileAxisYList: mempty
}
context :: R.Context (Record Store)
context = R.createContext $ unsafeCoerce unit
......
......@@ -12,16 +12,14 @@ import React.DOM (div')
import Reactix as R
import Reactix.DOM.HTML as H
here :: R2.Here
here = R2.here "Gargantext.Components.AutoUpdate"
data Action = Update
type PropsRow =
( duration :: Int
, effect :: Effect Unit
, effect :: Effect Unit
)
type Props = { | PropsRow }
......@@ -31,17 +29,19 @@ type State = { intervalId :: Maybe IntervalId }
autoUpdateClass :: ReactClass { children :: Children | PropsRow }
autoUpdateClass =
React.component "AutoUpdate"
(\this -> do
pure { state: {intervalId: Nothing}
, render: pure $ div' []
, componentDidMount: do
{duration, effect} <- React.getProps this
intervalId <- setInterval duration effect
React.setState this {intervalId: Just intervalId}
, componentWillUnmount: do
{intervalId} <- React.getState this
traverse_ clearInterval intervalId
})
( \this -> do
pure
{ state: { intervalId: Nothing }
, render: pure $ div' []
, componentDidMount: do
{ duration, effect } <- React.getProps this
intervalId <- setInterval duration effect
React.setState this { intervalId: Just intervalId }
, componentWillUnmount: do
{ intervalId } <- React.getState this
traverse_ clearInterval intervalId
}
)
autoUpdateElt :: Props -> ReactElement
autoUpdateElt props = React.createElement autoUpdateClass props []
......@@ -52,18 +52,18 @@ autoUpdate props = R.createElement autoUpdateCpt props []
autoUpdateCpt :: R.Component PropsRow
autoUpdateCpt = here.component "autoUpdate" cpt
where
cpt { duration, effect } _ = do
intervalRef <- R.useRef Nothing
cpt { duration, effect } _ = do
intervalRef <- R.useRef Nothing
R.useEffect' $ do
let mInterval = R.readRef intervalRef
case mInterval of
Nothing -> do
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
Just intervalId -> do
clearInterval intervalId
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
R.useEffect' $ do
let mInterval = R.readRef intervalRef
case mInterval of
Nothing -> do
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
Just intervalId -> do
clearInterval intervalId
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
pure $ H.div {} []
pure $ H.div {} []
......@@ -14,23 +14,22 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Toestand as T
type Props =
( defaultSlot :: R.Element
, cloakSlot :: R.Element
, isDisplayed :: Boolean
( defaultSlot :: R.Element
, cloakSlot :: R.Element
, isDisplayed :: Boolean
| Options
)
type Options =
( idlingPhaseDuration :: Maybe Int -- Milliseconds
( idlingPhaseDuration :: Maybe Int -- Milliseconds
, sustainingPhaseDuration :: Maybe Int -- Milliseconds
)
options :: Record Options
options =
{ idlingPhaseDuration : Nothing
, sustainingPhaseDuration : Nothing
{ idlingPhaseDuration: Nothing
, sustainingPhaseDuration: Nothing
}
cname :: String
......@@ -111,15 +110,17 @@ cname = "b-cloak"
-- | ```
cloak :: forall r. R2.OptLeaf Options Props r
cloak = R2.optLeaf component options
component :: R.Component Props
component = R.hooksComponent cname cpt where
component = R.hooksComponent cname cpt
where
cpt props _ = do
-- State
phase /\ phaseBox <- R2.useBox' (Idle :: Phase)
-- Computed
let
canCloakBeDisplayed = elem phase [ Sustain, Wait ]
canCloakBeDisplayed = elem phase [ Sustain, Wait ]
canContentBeDisplayed = elem phase [ Display ]
-- Behaviors
......@@ -128,55 +129,46 @@ component = R.hooksComponent cname cpt where
execDisplayingPhaseOr :: (Unit -> Effect Unit) -> Effect Unit
execDisplayingPhaseOr thunk =
if props.isDisplayed
then T.write_ Display phaseBox
if props.isDisplayed then T.write_ Display phaseBox
else thunk unit
execWaitingPhase :: Unit -> Effect Unit
execWaitingPhase _ = execDisplayingPhaseOr $ const $
T.write_ Wait phaseBox
T.write_ Wait phaseBox
execSustainingPhase :: Unit -> Effect Unit
execSustainingPhase _ = execDisplayingPhaseOr $ const $
T.write_ Sustain phaseBox
execSustainingPhase _ = execDisplayingPhaseOr $ const
$ 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 $
execIdlingPhase _ = execDisplayingPhaseOr $ const
$ T.write_ Idle phaseBox
T.write_ Idle phaseBox
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
-- Effects
useFirstEffect' $ execIdlingPhase unit
R.useEffect2' props.isDisplayed phase $
if (props.isDisplayed && phase == Wait)
then T.write_ Display phaseBox
if (props.isDisplayed && phase == Wait) then T.write_ Display phaseBox
else pure unit
-- Render
pure $
R.fragment
[
R2.when canCloakBeDisplayed props.cloakSlot
,
R2.when canContentBeDisplayed props.defaultSlot
]
[ R2.when canCloakBeDisplayed props.cloakSlot
, R2.when canContentBeDisplayed props.defaultSlot
]
data Phase =
Idle
data Phase
= Idle
| Sustain
| Wait
| Display
......
......@@ -10,22 +10,21 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options )
type Props = (| Options)
type Options =
( status :: ComponentStatus
, className :: String
, variant :: Variant
( status :: ComponentStatus
, className :: String
, variant :: Variant
)
options :: Record Options
options =
{ variant : Light
, status : Enabled
, className : ""
{ variant: Light
, status: Enabled
, className: ""
}
-- | Component for a Ripple Effect on DOMElement click (without JavaScript)
-- |
-- | ```
......@@ -42,10 +41,14 @@ componentName :: String
componentName = "b-ripple"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ variant
, status
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ variant
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -57,12 +60,11 @@ component = R.hooksComponent componentName cpt where
, componentName <> "--" <> show variant
]
-- Render
pure $
R.fragment $
[
H.div
{ className }
[]
]
<> children
pure
$ R.fragment
$
[ H.div
{ className }
[]
]
<> children
module Gargantext.Components.Bootstrap.Caveat(caveat) where
module Gargantext.Components.Bootstrap.Caveat (caveat) where
import Gargantext.Prelude
......@@ -8,16 +8,16 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options )
type Props = (| Options)
type Options =
( className :: String
, variant :: Variant
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, variant : Light
{ className: ""
, variant: Light
}
-- | Smart reference to the <alert> Bootstrap component,
......@@ -34,7 +34,8 @@ bootstrapName :: String
bootstrapName = "alert"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props children = do
-- Computed
let
......@@ -49,7 +50,6 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
children
{ className }
children
......@@ -15,14 +15,14 @@ type Props =
)
type Options =
( className :: String
, contentClassName :: String
( className :: String
, contentClassName :: String
)
options :: Record Options
options =
{ className : ""
, contentClassName : ""
{ className: ""
, contentClassName: ""
}
-- | Component simulating a native <fieldset>
......@@ -34,9 +34,13 @@ componentName :: String
componentName = "b-fieldset"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ titleSlot
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ titleSlot
}
children = do
-- Computed
let
className = intercalate " "
......@@ -55,15 +59,12 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
H.section
{ className }
[
H.div
{ className: componentName <> "__legend" }
[ titleSlot ]
,
H.div
{ className: contentClassName}
children
]
{ className }
[ H.div
{ className: componentName <> "__legend" }
[ titleSlot ]
, H.div
{ className: contentClassName }
children
]
module Gargantext.Components.Bootstrap.Preloader(preloader) where
module Gargantext.Components.Bootstrap.Preloader (preloader) where
import Gargantext.Prelude
......@@ -8,14 +8,14 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options)
type Props = (| Options)
type Options =
( className :: String
)
options :: Record Options
options =
{ className : ""
{ className: ""
}
-- | Structural Component wrapping our <Spinner.BorderTheme> within
......@@ -27,7 +27,8 @@ componentName :: String
componentName = "b-preloader"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -39,10 +40,8 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
[
spinner
{ className: componentName <> "__spinner" }
]
{ className }
[ spinner
{ className: componentName <> "__spinner" }
]
module Gargantext.Components.Bootstrap.ProgressBar(progressBar) where
module Gargantext.Components.Bootstrap.ProgressBar (progressBar) where
import Gargantext.Prelude
......@@ -8,21 +8,21 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( value :: Number
type Props =
( value :: Number
, waitingTextClass :: String
| Options
)
type Options =
( className :: String
, variant :: Variant
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, variant : Primary
{ className: ""
, variant: Primary
}
-- | Structural Component for the Bootsrap "Progress Bar"
......@@ -38,7 +38,8 @@ bootstrapName :: String
bootstrapName = "progress"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -52,28 +53,25 @@ component = R.hooksComponent componentName cpt where
]
-- 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"
}
[]
,
H.div
{ className: intercalate " "
[ "progress-text"
, props.waitingTextClass
]
}
[ H.text "Waiting task..." ]
]
{ 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"
}
[]
, H.div
{ className: intercalate " "
[ "progress-text"
, props.waitingTextClass
]
}
[ H.text "Waiting task..." ]
]
module Gargantext.Components.Bootstrap.Spinner(spinner) where
module Gargantext.Components.Bootstrap.Spinner (spinner) where
import Gargantext.Prelude
......@@ -8,16 +8,16 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options)
type Props = (| Options)
type Options =
( theme :: SpinnerTheme
( theme :: SpinnerTheme
, className :: String
)
options :: Record Options
options =
{ theme : BorderTheme
, className : ""
{ theme: BorderTheme
, className: ""
}
-- | Structural Component for the Bootstrap spinner
......@@ -33,7 +33,8 @@ bootstrapName :: String
bootstrapName = "spinner"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -47,7 +48,6 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
[]
{ className }
[]
......@@ -2,41 +2,56 @@ module Gargantext.Components.Bootstrap
( module Exports
) where
import Gargantext.Components.Bootstrap.BaseModal(baseModal) as Exports
import Gargantext.Components.Bootstrap.Button(button) as Exports
import Gargantext.Components.Bootstrap.ButtonGroup(buttonGroup) as Exports
import Gargantext.Components.Bootstrap.Caveat(caveat) as Exports
import Gargantext.Components.Bootstrap.BaseModal (baseModal) as Exports
import Gargantext.Components.Bootstrap.Button (button) as Exports
import Gargantext.Components.Bootstrap.ButtonGroup (buttonGroup) as Exports
import Gargantext.Components.Bootstrap.Caveat (caveat) as Exports
import Gargantext.Components.Bootstrap.Cloak (cloak) as Exports
import Gargantext.Components.Bootstrap.ContextMenu(contextMenu, contextMenuItem) as Exports
import Gargantext.Components.Bootstrap.Fieldset(fieldset) as Exports
import Gargantext.Components.Bootstrap.FormCheckbox(formCheckbox) 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.IconButton(iconButton) as Exports
import Gargantext.Components.Bootstrap.Preloader(preloader) as Exports
import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports
import Gargantext.Components.Bootstrap.Ripple(ripple) 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.ContextMenu (contextMenu, contextMenuItem) as Exports
import Gargantext.Components.Bootstrap.Fieldset (fieldset) as Exports
import Gargantext.Components.Bootstrap.FormCheckbox (formCheckbox) 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.IconButton (iconButton) as Exports
import Gargantext.Components.Bootstrap.Preloader (preloader) as Exports
import Gargantext.Components.Bootstrap.ProgressBar (progressBar) as Exports
import Gargantext.Components.Bootstrap.Ripple (ripple) 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_
, p', p_
, td', td_
, th', th_
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_
, p'
, p_
, td'
, td_
, th'
, th_
) as Exports
module Gargantext.Components.Bootstrap.BaseModal
( baseModal
, showModal, hideModal
, showModal
, hideModal
) where
import Gargantext.Prelude
......@@ -23,63 +24,63 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Bootstrap.BaseModal"
type ModalCallback = Unit -> Effect Unit
type ModalEvents =
( onHide :: ModalCallback
, onShow :: ModalCallback )
, onShow :: ModalCallback
)
foreign import _show :: EffectFn3
Window
String
(Record ModalEvents)
Unit
foreign import _show
:: EffectFn3
Window
String
(Record ModalEvents)
Unit
showModal ::
Window
showModal
:: Window
-> String
-> Record ModalEvents
-> Effect Unit
showModal = runEffectFn3 _show
foreign import _hide :: EffectFn2
Window
String
Unit
foreign import _hide
:: EffectFn2
Window
String
Unit
hideModal ::
Window
hideModal
:: Window
-> String
-> Effect Unit
hideModal = runEffectFn2 _hide
type Props =
( isVisibleBox :: T.Box Boolean
( isVisibleBox :: T.Box Boolean
| Options
)
type Options =
( modalClassName :: String
, title :: Maybe String
, hasCollapsibleBackground :: Boolean
, hasInnerScroll :: Boolean
, noHeader :: Boolean
, noBody :: Boolean -- ie. Bootstrap Body
, size :: ModalSizing
( modalClassName :: String
, title :: Maybe String
, hasCollapsibleBackground :: Boolean
, hasInnerScroll :: Boolean
, noHeader :: Boolean
, noBody :: Boolean -- ie. Bootstrap Body
, size :: ModalSizing
)
options :: Record Options
options =
{ modalClassName : ""
, title : Nothing
, hasCollapsibleBackground : true
, hasInnerScroll : false
, noHeader : false
, noBody : false
, size : MediumModalSize
{ modalClassName: ""
, title: Nothing
, hasCollapsibleBackground: true
, hasInnerScroll: false
, noHeader: false
, noBody: false
, size: MediumModalSize
}
componentName :: String
......@@ -99,16 +100,19 @@ baseModal :: forall r. R2.OptComponent Options Props r
baseModal = R2.optComponent component options
component :: R.Memo Props
component = R.memo' $ R.hooksComponent componentName cpt where
cpt props@{ isVisibleBox
, title
, hasCollapsibleBackground
, hasInnerScroll
, noHeader
, noBody
, size
} children
= do
component = R.memo' $ R.hooksComponent componentName cpt
where
cpt
props@
{ isVisibleBox
, title
, hasCollapsibleBackground
, hasInnerScroll
, noHeader
, noBody
, size
}
children = do
-- | States
-- |
isVisible <- R2.useLive' isVisibleBox
......@@ -131,19 +135,18 @@ component = R.memo' $ R.hooksComponent componentName cpt where
-- | Hooks
-- |
let modalEvents = {
onHide: \_ -> T.write_ false isVisibleBox
let
modalEvents =
{ onHide: \_ -> T.write_ false isVisibleBox
, onShow: \_ -> T.write_ true isVisibleBox
}
}
useUpdateEffect1' isVisible
if isVisible
then showModal window selector modalEvents
if isVisible then showModal window selector modalEvents
else hideModal window selector
-- | When box is true, show the modal immediately
R.useEffectOnce' $ do
if isVisible
then showModal window selector modalEvents
if isVisible then showModal window selector modalEvents
else hideModal window selector
-- | Behaviors
......@@ -154,82 +157,75 @@ component = R.memo' $ R.hooksComponent componentName cpt where
-- [ Render
-- |
R.createPortal
[
H.div
{ id: id
, className
, tabIndex: "-1"
, key: id
, data:
{ keyboard: "true"
, backdrop: hasCollapsibleBackground ?
"true" $
"static"
}
}
[
-- Overlay fixing collapsable click event
R2.when (hasCollapsibleBackground) $
H.div
{ className: componentName <> "__overlay"
, on: { click: onCloseButtonClick }
}
[]
,
H.div
{ className: intercalate " "
-- Bootstrap classNames
[ "modal-dialog"
, show size
, "modal-dialog-centered"
, hasInnerScroll ? "modal-dialog-scrollable" $ ""
-- provided custom className
, props.modalClassName
]
[ H.div
{ id: id
, className
, tabIndex: "-1"
, key: id
, data:
{ keyboard: "true"
, backdrop: hasCollapsibleBackground
? "true"
$
"static"
}
}
[
H.div
{ className: intercalate " "
[ componentName <> "__content"
, "modal-content"
]
}
[
-- Header
R2.when (not noHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, "modal-header"
]
}
[
R2.fromMaybe (title) \title' ->
H.div
{ className: componentName <> "__header__title" }
[ H.text title' ]
,
iconButton
{ name: "times"
, callback: onCloseButtonClick
, elevation: Level2
, className: componentName <> "__header__close"
}
]
,
-- Body
-- Overlay fixing collapsable click event
R2.when (hasCollapsibleBackground) $
H.div
{ className: componentName <> "__overlay"
, on: { click: onCloseButtonClick }
}
[]
, H.div
{ className: intercalate " "
[ componentName <> "__body"
, noBody ? "" $ "modal-body"
-- Bootstrap classNames
[ "modal-dialog"
, show size
, "modal-dialog-centered"
, hasInnerScroll ? "modal-dialog-scrollable" $ ""
-- provided custom className
, props.modalClassName
]
}
children
]
[ H.div
{ className: intercalate " "
[ componentName <> "__content"
, "modal-content"
]
}
[
-- Header
R2.when (not noHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, "modal-header"
]
}
[ R2.fromMaybe (title) \title' ->
H.div
{ className: componentName <> "__header__title" }
[ H.text title' ]
, iconButton
{ name: "times"
, callback: onCloseButtonClick
, elevation: Level2
, className: componentName <> "__header__close"
}
]
,
-- Body
H.div
{ className: intercalate " "
[ componentName <> "__body"
, noBody ? "" $ "modal-body"
]
}
children
]
]
]
]
]
<$> R2.getPortalHost
......@@ -27,8 +27,8 @@ import Reactix.SyntheticEvent as RE
type Props =
( closeCallback :: Unit -> Effect Unit
, x :: Number
, y :: Number
, x :: Number
, y :: Number
)
contextMenu :: R2.Component Props
......@@ -38,116 +38,114 @@ componentName :: String
componentName = "b-context-menu"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt { closeCallback
, x
, y
} children
= R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
component = R.hooksComponent componentName cpt
where
cpt
{ closeCallback
, x
, y
}
children = R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- | States
-- |
ref <- R.useRef (null :: Nullable DOM.Element)
-- | Hooks
-- |
{ enableScroll, disableScroll } <- useScrollbar
R.useLayoutEffect1 [] do
-- Mount
disableScroll
-- Unmount
pure enableScroll
-- /!\ for some reason we have to use the hook's effect with cleanup
-- function (even if empty)
R.useLayoutEffect1 (R.readRef ref) do
for_ (toMaybe $ R.readRef ref) \el -> do
let rect = Element.boundingRect el
let pos = position { x, y } rect
let style = el .. "style"
let toPixels = show >>> (_ <> "px")
void $ pure $ setProperty' style "left" [ pos.left # toPixels ]
void $ pure $ setProperty' style "top" [ pos.top # toPixels ]
R.nothing # R.thenNothing
-- | Computed
-- |
let
containerId :: String
containerId = componentName <> "-" <> uuid
containerCallback :: forall e. SE.SyntheticEvent e -> Effect Unit
containerCallback e =
let
eventTargetId :: Maybe String
eventTargetId = SE.unsafeEventTarget e # flip DOM.attr "id"
hasClickedOnContainer :: Boolean
hasClickedOnContainer = maybe false (eq containerId) eventTargetId
in
when hasClickedOnContainer $ closeCallback unit
-- | Render
-- |
R.createPortal
[
H.div
{ className: componentName
, on: { click: containerCallback }
, key: uuid
, id: containerId
}
[
H.div
{ className: componentName <> "__inner"
, data: { placement: "right", toggle: "popover" }
, ref
}
children
-- | States
-- |
ref <- R.useRef (null :: Nullable DOM.Element)
-- | Hooks
-- |
{ enableScroll, disableScroll } <- useScrollbar
R.useLayoutEffect1 [] do
-- Mount
disableScroll
-- Unmount
pure enableScroll
-- /!\ for some reason we have to use the hook's effect with cleanup
-- function (even if empty)
R.useLayoutEffect1 (R.readRef ref) do
for_ (toMaybe $ R.readRef ref) \el -> do
let rect = Element.boundingRect el
let pos = position { x, y } rect
let style = el .. "style"
let toPixels = show >>> (_ <> "px")
void $ pure $ setProperty' style "left" [ pos.left # toPixels ]
void $ pure $ setProperty' style "top" [ pos.top # toPixels ]
R.nothing # R.thenNothing
-- | Computed
-- |
let
containerId :: String
containerId = componentName <> "-" <> uuid
containerCallback :: forall e. SE.SyntheticEvent e -> Effect Unit
containerCallback e =
let
eventTargetId :: Maybe String
eventTargetId = SE.unsafeEventTarget e # flip DOM.attr "id"
hasClickedOnContainer :: Boolean
hasClickedOnContainer = maybe false (eq containerId) eventTargetId
in
when hasClickedOnContainer $ closeCallback unit
-- | Render
-- |
R.createPortal
[ H.div
{ className: componentName
, on: { click: containerCallback }
, key: uuid
, id: containerId
}
[ H.div
{ className: componentName <> "__inner"
, data: { placement: "right", toggle: "popover" }
, ref
}
children
]
]
]
<$> R2.getPortalHost
<$> R2.getPortalHost
position ::
{ x :: Number
position
:: { x :: Number
, y :: Number
}
-> DOMRect
-> { left :: Number
, top :: Number
, top :: Number
}
position mouse { width: menuWidth, height: menuHeight } = { left, top }
where
left = if isRight then mouse.x else mouse.x - menuWidth
top = if isAbove then mouse.y else mouse.y - menuHeight
isRight = screenWidth - mouse.x > menuWidth -- is there enough space to show above
isAbove = screenHeight - mouse.y > menuHeight -- is there enough space to show to the right?
screenWidth = window .. "innerWidth"
screenHeight = window .. "innerHeight"
left = if isRight then mouse.x else mouse.x - menuWidth
top = if isAbove then mouse.y else mouse.y - menuHeight
isRight = screenWidth - mouse.x > menuWidth -- is there enough space to show above
isAbove = screenHeight - mouse.y > menuHeight -- is there enough space to show to the right?
screenWidth = window .. "innerWidth"
screenHeight = window .. "innerHeight"
--------------------------------------------------------------
type ItemProps =
( callback :: Unit -> Effect Unit
( callback :: Unit -> Effect Unit
| ItemOptions
)
type ItemOptions =
( className :: String
, status :: ComponentStatus
( className :: String
, status :: ComponentStatus
)
itemOptions :: Record ItemOptions
itemOptions =
{ className : ""
, status : Enabled
{ className: ""
, status: Enabled
}
contextMenuItem :: forall r. R2.OptComponent ItemOptions ItemProps r
......@@ -157,10 +155,14 @@ itemComponentName :: String
itemComponentName = "b-context-menu-item"
itemCpt :: R.Component ItemProps
itemCpt = R.hooksComponent itemComponentName cpt where
cpt props@{ callback
, status
} children = do
itemCpt = R.hooksComponent itemComponentName cpt
where
cpt
props@
{ callback
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -174,24 +176,23 @@ itemCpt = R.hooksComponent itemComponentName cpt where
click = onClick status callback
-- Render
pure $
H.div
{ className
, on: { click }
} $
[
ripple
{ status
, variant: Dark
}
children
]
pure
$ H.div
{ className
, on: { click }
}
$
[ ripple
{ status
, variant: Dark
}
children
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick ::
ComponentStatus
onClick
:: ComponentStatus
-> (Unit -> Effect Unit)
-> RE.SyntheticEvent DE.Event
-> Effect Unit
......
module Gargantext.Components.Bootstrap.Tooltip
( tooltip
, TooltipBindingProps, tooltipBind, tooltipBind'
, TooltipBindingProps
, tooltipBind
, tooltipBind'
, tooltipContainer
) where
......@@ -19,30 +21,29 @@ import Type.Proxy (Proxy(..))
foreign import reactTooltipCpt :: R.Component Props
type Props =
( id :: String
( id :: String
| Options
)
type Options =
( effect :: TooltipEffect
, variant :: Variant
, delayHide :: Int
, delayShow :: Int
, className :: String
, position :: TooltipPosition
( 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
{ effect: SolidEffect
, variant: Dark
, delayHide: 0
, delayShow: 0
, className: ""
, position: AutomaticPosition
}
-- | Adapter Component for React Tooltip [1]
-- |
-- |
......@@ -55,30 +56,31 @@ options =
-- |
-- |
-- | https://github.com/wwayne/react-tooltip [1]
tooltip :: forall provided.
CO.Defaults (Record Options) (Record provided) (Record Props)
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
(Proxy :: Proxy "effect")
(show props'.effect)
>>> Record.set
(Proxy :: Proxy "variant")
(show props'.variant)
>>> Record.rename
(Proxy :: Proxy "variant")
(Proxy :: Proxy "type")
>>> Record.set
(Proxy :: Proxy "position")
(show props'.position)
>>> Record.rename
(Proxy :: Proxy "position")
(Proxy :: Proxy "place")
props' = CO.defaults options props
props'' = props'
# Record.set
(Proxy :: Proxy "effect")
(show props'.effect)
>>> Record.set
(Proxy :: Proxy "variant")
(show props'.variant)
>>> Record.rename
(Proxy :: Proxy "variant")
(Proxy :: Proxy "type")
>>> Record.set
(Proxy :: Proxy "position")
(show props'.position)
>>> Record.rename
(Proxy :: Proxy "position")
(Proxy :: Proxy "place")
-------------------------------------------------------------
......@@ -113,11 +115,14 @@ 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)
tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt
where
cpt
props@
{ tooltipSlot
, defaultSlot
}
_ = R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- Computed
let
......@@ -126,14 +131,11 @@ tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt where
{ id: uuid }
pure $
R2.fragmentWithKey uuid
[
tooltip
tooltipProps
[ tooltipSlot ]
,
H.span
(tooltipBind uuid)
[ defaultSlot ]
]
[ tooltip
tooltipProps
[ tooltipSlot ]
, H.span
(tooltipBind uuid)
[ defaultSlot ]
]
......@@ -11,20 +11,20 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: Boolean -> Effect Unit
, value :: Boolean
( callback :: Boolean -> Effect Unit
, value :: Boolean
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
( status :: ComponentStatus
, className :: String
)
options :: Record Options
options =
{ status : Enabled
, className : ""
{ status: Enabled
, className: ""
}
-- | Structural Component for an <input type="checkbox">
......@@ -39,10 +39,14 @@ componentName :: String
componentName = "b-form-checkbox"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -55,27 +59,26 @@ component = R.hooksComponent componentName cpt where
change <- pure $ onChange status callback
-- Render
pure $
H.input
{ className
, on: { change }
, type: "checkbox"
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, value: props.value
, checked: props.value
}
{ className
, on: { change }
, type: "checkbox"
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, value: props.value
, checked: props.value
}
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (Boolean -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.checked
if status == Enabled then callback $ (unsafeCoerce event).target.checked
else pure unit
......@@ -11,32 +11,32 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, type :: String
( status :: ComponentStatus
, className :: String
, type :: String
, placeholder :: String
, size :: Sizing
, step :: String
, min :: String
, max :: String
, size :: Sizing
, step :: String
, min :: String
, max :: String
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, type : "text"
, placeholder : ""
, size : MediumSize
, step : ""
, min : ""
, max : ""
{ status: Enabled
, className: ""
, type: "text"
, placeholder: ""
, size: MediumSize
, step: ""
, min: ""
, max: ""
}
-- | Structural Component for the Bootstrap input
......@@ -52,10 +52,14 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -71,31 +75,30 @@ component = R.hooksComponent componentName cpt where
change <- pure $ onChange status callback
-- Render
pure $
H.input
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, type: props.type
, step: props.step
, min: props.min
, max: props.max
, autoComplete: "off"
, value: props.value
}
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, type: props.type
, step: props.step
, min: props.min
, max: props.max
, autoComplete: "off"
, value: props.value
}
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
if status == Enabled then callback $ (unsafeCoerce event).target.value
else pure unit
......@@ -15,26 +15,26 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, type :: String
( status :: ComponentStatus
, className :: String
, type :: String
, placeholder :: String
, size :: Sizing
, size :: Sizing
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, type : "text"
, placeholder : ""
, size : MediumSize
{ status: Enabled
, className: ""
, type: "text"
, placeholder: ""
, size: MediumSize
}
-- | Structural Component for the Bootstrap select
......@@ -67,10 +67,14 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -91,30 +95,29 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
R2.select
{ className
, on: { change }
, disabled: elem status [ Disabled, Idled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: props.value
}
children
{ className
, on: { change }
, disabled: elem status [ Disabled, Idled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: props.value
}
children
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
if status == Enabled then callback $ (unsafeCoerce event).target.value
else R.nothing
-----------------------------------------------------------------------
type AnyTypeProps a =
......@@ -139,24 +142,30 @@ type AnyTypeProps a =
-- | (?) Note that HTML option tags will be automatically added thanks to
-- | to the provided `list` prop. You can add additional HTML option within
-- | the `children` prop
formSelect' :: forall r a.
Show a
formSelect'
:: forall r a
. Show a
=> R2.OptComponent Options (AnyTypeProps a) r
formSelect' = R2.optComponent component' options
component' :: forall a.
Show a
component'
:: forall a
. Show a
=> R.Component (AnyTypeProps a)
component' = R.hooksComponent (componentName <> "__helper") cpt where
cpt props@{ callback
, list
, status
, value
} children = do
component' = R.hooksComponent (componentName <> "__helper") cpt
where
cpt
props@
{ callback
, list
, status
, value
}
children = do
-- Computed
let
className = intercalate " "
-- provided custom className
-- provided custom className
[ props.className
-- BEM classNames
, componentName
......@@ -173,38 +182,37 @@ component' = R.hooksComponent (componentName <> "__helper") cpt where
-- Render
pure $
R2.select
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: show value
}
(
children
<>
flip map list \raw ->
H.option
{ value: show raw }
[ H.text $ show raw ]
)
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: show value
}
( children
<>
flip map list \raw ->
H.option
{ value: show raw }
[ H.text $ show raw ]
)
where
reader = GUS.reader list
reader = GUS.reader list
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange' :: forall event a.
Show a
onChange'
:: forall event a
. Show a
=> ComponentStatus
-> (String -> Maybe a)
-> (a -> Effect Unit)
-> event
-> Effect Unit
onChange' status reader callback event = do
if status == Enabled
then event # unsafeCoerce >>> _.target.value >>> reader >>> case _ of
if status == Enabled then event # unsafeCoerce >>> _.target.value >>> reader >>> case _ of
Nothing -> R.nothing
Just v -> callback v
Just v -> callback v
else R.nothing
......@@ -11,24 +11,24 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, placeholder :: String
, rows :: Int
( status :: ComponentStatus
, className :: String
, placeholder :: String
, rows :: Int
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, placeholder : ""
, rows : 2
{ status: Enabled
, className: ""
, placeholder: ""
, rows: 2
}
-- | Structural Component for the Bootstrap textarea
......@@ -44,11 +44,15 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, rows
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
, rows
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -64,27 +68,27 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
H.textarea
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, autoComplete: "off"
, rows
} []
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, autoComplete: "off"
, rows
}
[]
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
if status == Enabled then callback $ (unsafeCoerce event).target.value
else pure unit
......@@ -15,29 +15,29 @@ import Reactix.DOM.HTML as H
import Reactix.SyntheticEvent as RE
type Props =
( callback :: Unit -> Effect Unit
( callback :: Unit -> Effect Unit
| Options
)
type Options =
( block :: Boolean
( block :: Boolean
, className :: String
, size :: Sizing
, status :: ComponentStatus
, title :: String
, type :: String
, variant :: ButtonVariant
, size :: Sizing
, status :: ComponentStatus
, title :: String
, type :: String
, variant :: ButtonVariant
)
options :: Record Options
options =
{ block : false
, className : ""
, status : Enabled
, size : MediumSize
, title : ""
, type : "button"
, variant : ButtonVariant Primary
{ block: false
, className: ""
, status: Enabled
, size: MediumSize
, title: ""
, type: "button"
, variant: ButtonVariant Primary
}
-- | Structural Component for the Bootstrap button
......@@ -53,10 +53,14 @@ bootstrapName :: String
bootstrapName = "btn"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -69,37 +73,36 @@ component = R.hooksComponent componentName cpt where
, bootstrapName
, bootstrapName <> "-" <> show props.variant
, bootstrapName <> "-" <> show props.size
, props.block == true ?
bootstrapName <> "-block" $
mempty
, props.block == true
? bootstrapName
<> "-block"
$
mempty
]
click = onClick status callback
-- Render
pure $
H.button
{ className
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
, type: props.type
, title: props.title
}
[
R2.when (status == Deferred) $
spinner
{ className: componentName <> "__spinner" }
,
H.span
{ className: componentName <> "__inner" }
children
]
{ className
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
, type: props.type
, title: props.title
}
[ R2.when (status == Deferred) $
spinner
{ className: componentName <> "__spinner" }
, H.span
{ className: componentName <> "__inner" }
children
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick ::
ComponentStatus
onClick
:: ComponentStatus
-> (Unit -> Effect Unit)
-> RE.SyntheticEvent DE.Event
-> Effect Unit
......
......@@ -10,16 +10,16 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options )
type Props = (| Options)
type Options =
( className :: String
, collapse :: Boolean
, collapse :: Boolean
)
options :: Record Options
options =
{ className : ""
, collapse : true
{ className: ""
, collapse: true
}
-- | Structural Component for the Bootstrap Button Group
......@@ -35,7 +35,8 @@ bootstrapName :: String
bootstrapName = "btn-group"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props children = do
-- Computed
let
......@@ -44,18 +45,19 @@ component = R.hooksComponent componentName cpt where
[ props.className
-- BEM classNames
, componentName
, props.collapse ?
componentName <> "--collapse" $
componentName <> "--no-collapse"
, props.collapse
? componentName
<> "--collapse"
$ componentName
<> "--no-collapse"
-- Bootstrap specific classNames
, bootstrapName
]
-- Render
pure $
H.div
{ className
, role: "group"
}
children
{ className
, role: "group"
}
children
......@@ -8,7 +8,7 @@ import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( name :: String
( name :: String
| Options
)
......@@ -18,7 +18,7 @@ type Options =
options :: Record Options
options =
{ className : ""
{ className: ""
}
-- | Structural Component for a simple Glyphicon element
......@@ -34,7 +34,8 @@ bootstrapName :: String
bootstrapName = "fa"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
className <- pure $ intercalate " "
......@@ -48,7 +49,6 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.i
{ className }
[]
{ className }
[]
......@@ -12,30 +12,30 @@ import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( name :: String
, callback :: Unit -> Effect Unit
( name :: String
, callback :: Unit -> Effect Unit
| Options
)
type Options =
( className :: String
, status :: ComponentStatus
, title :: String
, overlay :: Boolean
, status :: ComponentStatus
, title :: String
, overlay :: Boolean
, elevation :: Elevation
, variant :: Variant
, variant :: Variant
, focusRing :: Boolean
)
options :: Record Options
options =
{ className : ""
, status : Enabled
, title : ""
, overlay : true
, elevation : Level0
, variant : Secondary
, focusRing : true
{ className: ""
, status: Enabled
, title: ""
, overlay: true
, elevation: Level0
, variant: Secondary
, focusRing: true
}
-- | Structural Component for a simple Glyphicon element with call-to-action
......@@ -51,11 +51,15 @@ bootstrapName :: String
bootstrapName = "fa"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, name
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
, name
}
_ = do
-- Computed
let
wrapperClassName = intercalate " "
......@@ -65,13 +69,17 @@ component = R.hooksComponent componentName cpt where
, componentName
, componentName <> "--" <> show status
, componentName <> "--" <> show props.variant
, props.overlay ?
componentName <> "--overlay" $
""
, props.overlay
? componentName
<> "--overlay"
$
""
, componentName <> "--" <> show props.elevation
, props.focusRing ?
componentName <> "--focus-ring" $
""
, props.focusRing
? componentName
<> "--focus-ring"
$
""
]
contentClassName = intercalate " "
......@@ -84,33 +92,30 @@ component = R.hooksComponent componentName cpt where
click = onClick status callback
-- Render
pure $
H.span
{ className: wrapperClassName
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
}
[
H.span
{ className: componentName <> "__inner" }
[
H.i
{ title: props.title
, className: contentClassName
}
[]
{ className: wrapperClassName
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
}
[ H.span
{ className: componentName <> "__inner" }
[ H.i
{ title: props.title
, className: contentClassName
}
[]
]
]
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick :: forall event.
ComponentStatus
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
if status == Enabled then callback unit
else pure unit
......@@ -21,27 +21,36 @@ componentName = "b-wad"
wad :: Array String -> Array R.Element -> R.Element
wad classes children = R.createDOMElement "div" cls children
where
cls = { className: intercalate " " $
[ componentName
] <> classes
}
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
}
cls =
{ className: intercalate " "
$
[ componentName
]
<> classes
}
chd = [ H.text text ]
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
}
cls =
{ className: intercalate " "
$
[ componentName
]
<> classes
}
module Gargantext.Components.Bootstrap.Tabs(tabs) where
module Gargantext.Components.Bootstrap.Tabs (tabs) where
import Gargantext.Prelude
......@@ -10,9 +10,9 @@ import Reactix as R
import Reactix.DOM.HTML as H
type Props a =
( value :: a
, callback :: a -> Effect Unit
, list :: Array a
( value :: a
, callback :: a -> Effect Unit
, list :: Array a
| Options
)
......@@ -22,7 +22,7 @@ type Options =
options :: Record Options
options =
{ className : ""
{ className: ""
}
-- | Structural molecular component to the Bootstrap <nav-tabs> + <nav-item>
......@@ -30,8 +30,9 @@ options =
-- | tabs, etc)
-- |
-- | https://getbootstrap.com/docs/4.6/components/navs/#tabs
tabs :: forall r a.
Show a
tabs
:: forall r a
. Show a
=> Eq a
=> R2.OptLeaf Options (Props a) r
tabs = R2.optLeaf component options
......@@ -39,11 +40,13 @@ tabs = R2.optLeaf component options
componentName :: String
componentName = "b-tabs"
component :: forall a.
Show a
component
:: forall a
. Show a
=> Eq a
=> R.Component (Props a)
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props@{ list, value, callback } _ = do
-- Computed
let
......@@ -56,24 +59,21 @@ component = R.hooksComponent componentName cpt where
, "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
]
]
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_
, p', p_
, td', td_
, th', th_
( div'
, div_
, h1'
, h1_
, h2'
, h2_
, h3'
, h3_
, h4'
, h4_
, h5'
, h5_
, h6'
, h6_
, span'
, span_
, li'
, li_
, b'
, b_
, code'
, code_
, label'
, label_
, p'
, p_
, td'
, td_
, th'
, th_
) where
import Reactix as R
......@@ -27,7 +42,6 @@ div' props content = H.div props [ H.text content ]
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 ]
......
module Gargantext.Components.Bootstrap.Types
( ComponentStatus(..)
, Variant(..), ButtonVariant(..)
, Variant(..)
, ButtonVariant(..)
, Sizing(..)
, SpinnerTheme(..)
, TooltipEffect(..), TooltipPosition(..)
, TooltipEffect(..)
, TooltipPosition(..)
, Position(..)
, Elevation(..)
, ModalSizing(..)
......@@ -76,9 +78,9 @@ data ButtonVariant
derive instance Generic ButtonVariant _
derive instance Eq ButtonVariant
instance Show ButtonVariant where
show (ButtonVariant a) = (kebabCase <<< genericShow) a
show (ButtonVariant a) = (kebabCase <<< genericShow) a
show (OutlinedButtonVariant a) = (append "outline-" <<< kebabCase <<< genericShow) a
show LinkButtonVariant = "link"
show LinkButtonVariant = "link"
----------------------------------------------------------------------
......@@ -101,9 +103,9 @@ data Sizing
derive instance Generic Sizing _
derive instance Eq Sizing
instance Show Sizing where
show SmallSize = "sm"
show SmallSize = "sm"
show MediumSize = "md"
show LargeSize = "lg"
show LargeSize = "lg"
----------------------------------------------------------------------
......@@ -118,7 +120,7 @@ derive instance Generic SpinnerTheme _
derive instance Eq SpinnerTheme
instance Show SpinnerTheme where
show BorderTheme = "border"
show GrowTheme = "grow"
show GrowTheme = "grow"
----------------------------------------------------------------------
......@@ -126,8 +128,9 @@ instance Show SpinnerTheme where
-- |
-- | https://github.com/wwayne/react-tooltip#options
data TooltipEffect
= FloatEffect
| SolidEffect
= FloatEffect
| SolidEffect
----------------------------------------------------------------------
derive instance Generic TooltipEffect _
derive instance Eq TooltipEffect
......@@ -146,7 +149,8 @@ data Position
derive instance Generic Position _
derive instance Eq Position
instance Show Position where show = kebabCase <<< genericShow
instance Show Position where
show = kebabCase <<< genericShow
----------------------------------------------------------------------
......@@ -160,8 +164,8 @@ data TooltipPosition
derive instance Generic TooltipPosition _
derive instance Eq TooltipPosition
instance Show TooltipPosition where
show (TooltipPosition a) = (kebabCase <<< genericShow) a
show AutomaticPosition = ""
show (TooltipPosition a) = (kebabCase <<< genericShow) a
show AutomaticPosition = ""
----------------------------------------------------------------------
......@@ -176,14 +180,15 @@ data Elevation
derive instance Generic Elevation _
derive instance Eq Elevation
instance Show Elevation where show = kebabCase <<< genericShow
instance Show Elevation where
show = kebabCase <<< genericShow
----------------------------------------------------------------------
-- | Modal custom sizing used by Bootstrap for its modals
-- |
-- | https://getbootstrap.com/docs/4.6/components/modal/#optional-sizes
data ModalSizing
data ModalSizing
= SmallModalSize
| MediumModalSize
| LargeModalSize
......@@ -192,7 +197,7 @@ data ModalSizing
derive instance Generic ModalSizing _
derive instance Eq ModalSizing
instance Show ModalSizing where
show SmallModalSize = "modal-sm"
show MediumModalSize = ""
show LargeModalSize = "modal-lg"
show ExtraLargeModalSize = "modal-xl"
show SmallModalSize = "modal-sm"
show MediumModalSize = ""
show LargeModalSize = "modal-lg"
show ExtraLargeModalSize = "modal-xl"
This diff is collapsed.
......@@ -14,6 +14,7 @@ import Gargantext.Prelude
------------------------------------------------------------------------
data Category = Trash | UnRead | Checked | Topic | Favorite | ToCite
{-
- `UnRead` is assigned initially for new docs
- After reading a doc, `Checked` should be assigned automatically
......@@ -21,27 +22,38 @@ data Category = Trash | UnRead | Checked | Topic | Favorite | ToCite
-}
categories :: Array Category
categories = [Trash, UnRead, Checked, Topic, Favorite, ToCite]
categories = [ Trash, UnRead, Checked, Topic, Favorite, ToCite ]
derive instance Generic Category _
instance Ord Category where compare = genericCompare
instance Ord Category where
compare = genericCompare
instance Enum Category where
pred = genericPred
succ = genericSucc
instance Bounded Category where
bottom = genericBottom
top = genericTop
instance BoundedEnum Category where
cardinality = genericCardinality
fromEnum = genericFromEnum
toEnum = genericToEnum
instance Show Category where show = genericShow
instance Eq Category where eq = genericEq
instance Show Category where
show = genericShow
instance Eq Category where
eq = genericEq
instance JSON.ReadForeign Category where
readImpl f = do
inst <- JSON.readImpl f
pure $ decodeCategory inst
instance JSON.WriteForeign Category where writeImpl = JSON.writeImpl <<< cat2score
instance JSON.WriteForeign Category where
writeImpl = JSON.writeImpl <<< cat2score
catSucc :: Category -> Category
catSucc c = fromMaybe ToCite $ succ c
......@@ -50,13 +62,12 @@ catPred :: Category -> Category
catPred c = fromMaybe Trash $ pred c
clickAgain :: Category -> Category
clickAgain _ = UnRead
clickAgain _ = UnRead
-- | `categoryNextState :: current -> clicked -> new State`
categoryNextState :: Category -> Star -> Category
categoryNextState Trash Star_0 = UnRead
categoryNextState _ Star_0 = Trash
categoryNextState _ Star_0 = Trash
categoryNextState current clicked =
if (cat2star current) == clicked then
clickAgain current
......@@ -65,7 +76,7 @@ categoryNextState current clicked =
favCategory :: Category -> Category
favCategory Favorite = Topic
favCategory _ = Favorite
favCategory _ = Favorite
trashCategory :: Category -> Category
trashCategory = const Trash
......@@ -81,32 +92,38 @@ cat2score = fromEnum
data Star = Star_0 | Star_1 | Star_2 | Star_3 | Star_4
stars :: Array Star
stars = [Star_0, Star_1, Star_2, Star_3, Star_4]
stars = [ Star_0, Star_1, Star_2, Star_3, Star_4 ]
derive instance Generic Star _
instance Show Star where show = genericShow
instance Eq Star where eq = genericEq
instance Ord Star where compare = genericCompare
instance Show Star where
show = genericShow
instance Eq Star where
eq = genericEq
instance Ord Star where
compare = genericCompare
instance Enum Star where
pred = genericPred
succ = genericSucc
instance Bounded Star where
bottom = genericBottom
top = genericTop
instance BoundedEnum Star where
cardinality = genericCardinality
fromEnum = genericFromEnum
toEnum = genericToEnum
cat2star :: Category -> Star
cat2star Trash = Star_0
cat2star UnRead = Star_0
cat2star Checked = Star_1
cat2star Topic = Star_2
cat2star Trash = Star_0
cat2star UnRead = Star_0
cat2star Checked = Star_1
cat2star Topic = Star_2
cat2star Favorite = Star_3
cat2star ToCite = Star_4
cat2star ToCite = Star_4
-- | This is a "reverse" of `cat2star`
star2catSimple :: Star -> Category
......
module Gargantext.Components.Charts.Options.Color
( Color
, stringColor
, cssColor
, transparent
, red
, blue
, magenta
, violet
, black
, grey
, green
) where
( Color
, stringColor
, cssColor
, transparent
, red
, blue
, magenta
, violet
, black
, grey
, green
) where
import Prelude
......@@ -34,7 +34,8 @@ red = stringColor "red"
blue :: Color
blue = cssColor $ rgba 100 150 200 1.0
-- stringColor "blue"
-- stringColor "blue"
magenta :: Color
magenta = stringColor "magenta"
......
......@@ -13,9 +13,9 @@ type DataLegend =
}
type DataAxis = Array String
{- value :: String
, textStyle :: TextStyle
-}
{- value :: String
, textStyle :: TextStyle
-}
type RequiredData v o =
{ value :: v
......@@ -23,14 +23,14 @@ type RequiredData v o =
}
type OptionalData =
( name :: String
, symbolSize :: Number
, itemStyle :: ItemStyle
( name :: String
, symbolSize :: Number
, itemStyle :: ItemStyle
-- ^ the style setting about single data point(bubble).
, label :: { show :: Boolean }
, emphasis :: { itemStyle :: ItemStyle }
, selectedMode :: SelectedMode
, select :: { itemStyle :: ItemStyle }
, label :: { show :: Boolean }
, emphasis :: { itemStyle :: ItemStyle }
, selectedMode :: SelectedMode
, select :: { itemStyle :: ItemStyle }
-- ^ need "selectedMode" to be defined
)
......@@ -40,7 +40,7 @@ dataSerie :: forall v o. Optional o OptionalData => RequiredData v o -> DataSeri
dataSerie = unsafeCoerce
dataSerieV :: forall v. v -> DataSerie v
dataSerieV value = dataSerie {value}
dataSerieV value = dataSerie { value }
type DataD1 = DataSerie Number
type DataD2 = DataSerie (Array Number)
......@@ -49,4 +49,4 @@ dataD1 :: forall o. Optional o OptionalData => Record o -> Number -> DataD1
dataD1 o x = unsafeCoerce (unsafeSet "value" x o)
dataD2 :: forall o. Optional o OptionalData => Record o -> Number -> Number -> DataD2
dataD2 o x y = unsafeCoerce (unsafeSet "value" [x,y] o)
dataD2 o x y = unsafeCoerce (unsafeSet "value" [ x, y ] o)
......@@ -29,115 +29,112 @@ chart = echarts <<< chartWith
chartWith :: Options -> Echarts
chartWith options =
{ option : opts options
--, className : Nothing
--, style : Nothing
--, theme : Nothing
--, group : Nothing
--, initOpts : Nothing
--, notMerge : Nothing
--, lazyUpdate: Nothing
--, loading : Nothing
--, optsLoading: Nothing
--, onReady : Nothing
--, resizable : Nothing
, onEvents : getEvents options
, ref : refListener options
{ option: opts options
--, className : Nothing
--, style : Nothing
--, theme : Nothing
--, group : Nothing
--, initOpts : Nothing
--, notMerge : Nothing
--, lazyUpdate: Nothing
--, loading : Nothing
--, optsLoading: Nothing
--, onReady : Nothing
--, resizable : Nothing
, onEvents: getEvents options
, ref: refListener options
}
where
getEvents (Options { onClick }) =
{ click: listenerFn1 \e -> case onClick of
-- sanitize parsing (see MouseEvent comment)
Just fn -> RX.pick (e :: MouseEvent) # fn
Nothing -> pure unit
}
refListener (Options { onInit }) = case onInit of
where
getEvents (Options { onClick }) =
{ click: listenerFn1 \e -> case onClick of
-- sanitize parsing (see MouseEvent comment)
Just fn -> RX.pick (e :: MouseEvent) # fn
Nothing -> pure unit
Just fn -> listenerFn1 (execOnInit fn)
}
refListener (Options { onInit }) = case onInit of
Nothing -> pure unit
Just fn -> listenerFn1 (execOnInit fn)
execOnInit fn = toMaybe >>> case _ of
Nothing -> pure unit
-- Just (ref :: Record EChartRef) -> fn =<< ref.getEchartsInstance
-- ^ this line can break for some reasons... (see Issue #312)
Just (ref :: Record EChartRef) -> do
i <- ref.getEchartsInstance
fn i
execOnInit fn = toMaybe >>> case _ of
Nothing -> pure unit
-- Just (ref :: Record EChartRef) -> fn =<< ref.getEchartsInstance
-- ^ this line can break for some reasons... (see Issue #312)
Just (ref :: Record EChartRef) -> do
i <- ref.getEchartsInstance
fn i
echarts :: Echarts -> R.Element
echarts c = R2.buff $ unsafeCreateElementDynamic (unsafeCoerce eChartsClass) c []
type MainTitle = String
type SubTitle = String
type SubTitle = String
title :: MainTitle -> SubTitle -> Title
title mainTitle subTitle =
{
id: ""
,show: true
,text: mainTitle
,link: ""
,target: "blank"
,textStyle: textStyle
,subtext: subTitle
,sublink: ""
,subtarget: "blank"
,subtextStyle: textStyle2
,padding: 10.0
,itemGap: 0.0
,zlevel: 2.0
,z: 2.0
,left: relativePosition (Relative RightPos)
,top: relativePosition (Relative Top)
,right: numberPosition 60.0
,bottom: percentPosition 40.0
,backgroundColor: transparent
,borderColor: transparent
,borderWidth: 0.0
,borderRadius: 0.0
,shadowBlur: 0.0
,shadowColor: transparent
,shadowOffsetX: 0.0
,shadowOffsetY: 0.0
{ id: ""
, show: true
, text: mainTitle
, link: ""
, target: "blank"
, textStyle: textStyle
, subtext: subTitle
, sublink: ""
, subtarget: "blank"
, subtextStyle: textStyle2
, padding: 10.0
, itemGap: 0.0
, zlevel: 2.0
, z: 2.0
, left: relativePosition (Relative RightPos)
, top: relativePosition (Relative Top)
, right: numberPosition 60.0
, bottom: percentPosition 40.0
, backgroundColor: transparent
, borderColor: transparent
, borderWidth: 0.0
, borderRadius: 0.0
, shadowBlur: 0.0
, shadowColor: transparent
, shadowOffsetX: 0.0
, shadowOffsetY: 0.0
}
legend :: Legend
legend =
{
id: "Muda"
,"type": legendType Plain
, show: true
, zlevel: 0.0
, z: 2.0
, left: relativePosition Auto
, top: relativePosition Auto
, right: relativePosition Auto
, bottom: relativePosition Auto
, width: relativePosition Auto
, height: relativePosition Auto
, orient: orient Horizontal
, align: relativePosition Auto
, padding: 5.0
, itemGap: 10.0
, itemWidth: 25.0
, itemHeight: 14.0
--, formatter: Nothing
, selectedMode: selectedMode $ Bool true
, inactiveColor: violet
--- selected: Nothing
, textStyle: textStyle
, "data": [data1]
{ id: "Muda"
, "type": legendType Plain
, show: true
, zlevel: 0.0
, z: 2.0
, left: relativePosition Auto
, top: relativePosition Auto
, right: relativePosition Auto
, bottom: relativePosition Auto
, width: relativePosition Auto
, height: relativePosition Auto
, orient: orient Horizontal
, align: relativePosition Auto
, padding: 5.0
, itemGap: 10.0
, itemWidth: 25.0
, itemHeight: 14.0
--, formatter: Nothing
, selectedMode: selectedMode $ Bool true
, inactiveColor: violet
--- selected: Nothing
, textStyle: textStyle
, "data": [ data1 ]
}
data1 :: DataLegend
data1 = {name: "Map terms coverage", icon: icon $ Shape Circle, textStyle: textStyle'}
data1 = { name: "Map terms coverage", icon: icon $ Shape Circle, textStyle: textStyle' }
data2 :: DataLegend
data2 = {name: "Favorites", icon: icon $ Shape Circle, textStyle: textStyle'}
data2 = { name: "Favorites", icon: icon $ Shape Circle, textStyle: textStyle' }
data3 :: DataLegend
data3 = {name: "Test", icon: icon $ Shape Diamond, textStyle: textStyle'}
data3 = { name: "Test", icon: icon $ Shape Diamond, textStyle: textStyle' }
yAxisVoid :: YAxis
yAxisVoid = yAxis
......@@ -145,43 +142,45 @@ yAxisVoid = yAxis
, name: ""
, min: 0
, position: ""
, axisLabel: {formatter: ""}
, axisLabel: { formatter: "" }
, show: false
}
xAxis' :: Array String -> XAxis
xAxis' [] = unsafeCoerce {show:false}
xAxis' [] = unsafeCoerce { show: false }
xAxis' xs = xAxis
{ "data": xs
, "type": "category"
, axisTick: {alignWithLabel: true}
, show: true
, axisLabel: {formatter: "{value}"}
}
{ "data": xs
, "type": "category"
, axisTick: { alignWithLabel: true }
, show: true
, axisLabel: { formatter: "{value}" }
}
-- TODO try to use Optional
yAxis' :: { position :: String
, show :: Boolean
, min :: Int
} -> YAxis
yAxis' {position, show, min} = yAxis
yAxis'
:: { position :: String
, show :: Boolean
, min :: Int
}
-> YAxis
yAxis' { position, show, min } = yAxis
{ "type": "value"
, name: ""
, min: min
, axisLabel: {formatter: "{value}"}
, axisLabel: { formatter: "{value}" }
, position
, show
}
data Options = Options
{ mainTitle :: MainTitle
, subTitle :: SubTitle
, xAxis :: XAxis
, yAxis :: YAxis
, series :: Array Series
, addZoom :: Boolean
, tooltip :: Tooltip
, onClick :: Maybe (MouseEvent -> Effect Unit)
, subTitle :: SubTitle
, xAxis :: XAxis
, yAxis :: YAxis
, series :: Array Series
, addZoom :: Boolean
, tooltip :: Tooltip
, onClick :: Maybe (MouseEvent -> Effect Unit)
-- (?) `onInit` custom listener
--
-- * in addition of the already existing `onReady` native listener
......@@ -194,30 +193,33 @@ data Options = Options
-- library actions
--
-- [1] https://echarts.apache.org/v4/en/api.html#echarts.init
, onInit :: Maybe (EChartsInstance -> Effect Unit)
, onInit :: Maybe (EChartsInstance -> Effect Unit)
}
tooltipTriggerAxis :: Tooltip
tooltipTriggerAxis = mkTooltip { trigger: "axis"}
tooltipTriggerAxis = mkTooltip { trigger: "axis" }
opts :: Options -> Option
opts (Options { mainTitle
, subTitle
, xAxis
, yAxis
, series
, tooltip
, addZoom
}) =
opts
( Options
{ mainTitle
, subTitle
, xAxis
, yAxis
, series
, tooltip
, addZoom
}
) =
{ title: title mainTitle subTitle
, legend
, tooltip
, grid: {containLabel: true}
, grid: { containLabel: true }
, series
, xAxis
, yAxis
, dataZoom: if addZoom then [zoom Slider, zoom Inside] else []
, children : unsafeCoerce [] -- TODO
, dataZoom: if addZoom then [ zoom Slider, zoom Inside ] else []
, children: unsafeCoerce [] -- TODO
, toolbox: mkToolBox
}
......@@ -228,25 +230,25 @@ instance Show Zoom where
show Inside = "inside"
zoom :: Zoom -> DataZoom
zoom z = {
"type": show z
,xAxisIndex: 0
,filterMode: "empty"
,start: 0
,end: 100
zoom z =
{ "type": show z
, xAxisIndex: 0
, filterMode: "empty"
, start: 0
, end: 100
}
seriesPie :: Series
seriesPie = seriesPieD1
{ name: "Pie name" }
(dataSerie <$> [ {name: "t1", value: 50.0}
, {name: "t2", value: 45.0}
, {name: "t3", value: 65.0}
, {name: "t4", value: 15.0}
, {name: "t5", value: 23.0}
])