...
 
Commits (3)
......@@ -12,6 +12,8 @@
<body>
<div id="app"></div>
<div id="portal"></div>
<script src="js/jquery@3.5.1/jquery.slim.min.js"></script>
<script src="js/bootstrap@4.6.2/bootstrap.bundle.min.js"></script>
<script src="bundle.js"></script>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -5941,30 +5941,23 @@ h3 {
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
margin-left: 1.75rem;
margin-right: 1.75rem;
}
.b-modal__header {
background-color: white;
}
.b-modal__header__content {
.b-modal__header__title {
color: #FF550B;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
......@@ -8404,65 +8397,37 @@ a:focus, a:hover {
padding-right: 20px;
}
#node-popup-tooltip {
position: fixed;
background-color: white;
border-radius: 6px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
#node-popup-tooltip .panel-actions .almost-useable {
.node-popup-tooltip .panel-actions__almost-useable {
color: orange;
}
#node-popup-tooltip .panel-actions .development-in-progress {
.node-popup-tooltip .panel-actions__development-in-progress {
color: red;
}
#node-popup-tooltip .panel-actions .ok-to-use {
.node-popup-tooltip .panel-actions__ok-to-use {
color: black;
}
#node-popup-tooltip .popup-container {
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container > .card {
width: auto !important;
min-width: 544px;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: initial;
flex-direction: column;
}
#node-popup-tooltip .popup-container > .card .fa-pencil {
.node-popup-tooltip .popup-container__header .fa-pencil {
color: black;
}
#node-popup-tooltip .popup-container > .card .card-body {
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container > .card .card-body .spacer {
margin: 8px;
background-color: #000000;
}
#node-popup-tooltip .popup-container .frame-search.card {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
.frame-search.card {
width: 1000px;
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
overflow-y: auto;
height: 600px;
}
.forest-layout {
......
......@@ -5895,30 +5895,23 @@ h3 {
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
margin-left: 1.75rem;
margin-right: 1.75rem;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
.b-modal__header__title {
color: #005a9a;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
......@@ -8357,65 +8350,37 @@ a:focus, a:hover {
padding-right: 20px;
}
#node-popup-tooltip {
position: fixed;
background-color: white;
border-radius: 6px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
#node-popup-tooltip .panel-actions .almost-useable {
.node-popup-tooltip .panel-actions__almost-useable {
color: orange;
}
#node-popup-tooltip .panel-actions .development-in-progress {
.node-popup-tooltip .panel-actions__development-in-progress {
color: red;
}
#node-popup-tooltip .panel-actions .ok-to-use {
.node-popup-tooltip .panel-actions__ok-to-use {
color: black;
}
#node-popup-tooltip .popup-container {
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container > .card {
width: auto !important;
min-width: 544px;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: initial;
flex-direction: column;
}
#node-popup-tooltip .popup-container > .card .fa-pencil {
.node-popup-tooltip .popup-container__header .fa-pencil {
color: black;
}
#node-popup-tooltip .popup-container > .card .card-body {
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container > .card .card-body .spacer {
margin: 8px;
background-color: #fff;
}
#node-popup-tooltip .popup-container .frame-search.card {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
.frame-search.card {
width: 1000px;
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
overflow-y: auto;
height: 600px;
}
.forest-layout {
......
......@@ -5650,30 +5650,23 @@ h3 {
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
margin-left: 1.75rem;
margin-right: 1.75rem;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
.b-modal__header__title {
color: #2f3c48;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
......@@ -8113,65 +8106,37 @@ a:focus, a:hover {
padding-right: 20px;
}
#node-popup-tooltip {
position: fixed;
background-color: white;
border-radius: 6px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
#node-popup-tooltip .panel-actions .almost-useable {
.node-popup-tooltip .panel-actions__almost-useable {
color: orange;
}
#node-popup-tooltip .panel-actions .development-in-progress {
.node-popup-tooltip .panel-actions__development-in-progress {
color: red;
}
#node-popup-tooltip .panel-actions .ok-to-use {
.node-popup-tooltip .panel-actions__ok-to-use {
color: black;
}
#node-popup-tooltip .popup-container {
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container > .card {
width: auto !important;
min-width: 544px;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: initial;
flex-direction: column;
}
#node-popup-tooltip .popup-container > .card .fa-pencil {
.node-popup-tooltip .popup-container__header .fa-pencil {
color: black;
}
#node-popup-tooltip .popup-container > .card .card-body {
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container > .card .card-body .spacer {
margin: 8px;
background-color: #fff;
}
#node-popup-tooltip .popup-container .frame-search.card {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
.frame-search.card {
width: 1000px;
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
overflow-y: auto;
height: 600px;
}
.forest-layout {
......
......@@ -5898,30 +5898,23 @@ h3 {
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
margin-left: 1.75rem;
margin-right: 1.75rem;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
.b-modal__header__title {
color: #083358;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
......@@ -8361,65 +8354,37 @@ a:focus, a:hover {
padding-right: 20px;
}
#node-popup-tooltip {
position: fixed;
background-color: white;
border-radius: 6px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
#node-popup-tooltip .panel-actions .almost-useable {
.node-popup-tooltip .panel-actions__almost-useable {
color: orange;
}
#node-popup-tooltip .panel-actions .development-in-progress {
.node-popup-tooltip .panel-actions__development-in-progress {
color: red;
}
#node-popup-tooltip .panel-actions .ok-to-use {
.node-popup-tooltip .panel-actions__ok-to-use {
color: black;
}
#node-popup-tooltip .popup-container {
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container > .card {
width: auto !important;
min-width: 544px;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: initial;
flex-direction: column;
}
#node-popup-tooltip .popup-container > .card .fa-pencil {
.node-popup-tooltip .popup-container__header .fa-pencil {
color: black;
}
#node-popup-tooltip .popup-container > .card .card-body {
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container > .card .card-body .spacer {
margin: 8px;
background-color: #fff;
}
#node-popup-tooltip .popup-container .frame-search.card {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
.frame-search.card {
width: 1000px;
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
overflow-y: auto;
height: 600px;
}
.forest-layout {
......
......@@ -5899,30 +5899,23 @@ h3 {
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
margin-left: 1.75rem;
margin-right: 1.75rem;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
.b-modal__header__title {
color: #222222;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
......@@ -8362,65 +8355,37 @@ a:focus, a:hover {
padding-right: 20px;
}
#node-popup-tooltip {
position: fixed;
background-color: white;
border-radius: 6px;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
#node-popup-tooltip .panel-actions .almost-useable {
.node-popup-tooltip .panel-actions__almost-useable {
color: orange;
}
#node-popup-tooltip .panel-actions .development-in-progress {
.node-popup-tooltip .panel-actions__development-in-progress {
color: red;
}
#node-popup-tooltip .panel-actions .ok-to-use {
.node-popup-tooltip .panel-actions__ok-to-use {
color: black;
}
#node-popup-tooltip .popup-container {
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container > .card {
width: auto !important;
min-width: 544px;
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: initial;
flex-direction: column;
}
#node-popup-tooltip .popup-container > .card .fa-pencil {
.node-popup-tooltip .popup-container__header .fa-pencil {
color: black;
}
#node-popup-tooltip .popup-container > .card .card-body {
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container > .card .card-body .spacer {
margin: 8px;
background-color: #fff;
}
#node-popup-tooltip .popup-container .frame-search.card {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
.frame-search.card {
width: 1000px;
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
overflow-y: auto;
height: 600px;
}
.forest-layout {
......
'use strict';
exports._addClassName = function(window, className) {
window.document.body.classList.add(className);
exports._show = show;
exports._hide = hide;
/**
* @function show
* @param {Window} window
* @param {string} querySelector
* @unpure {Object} window.$
*/
function show(window, querySelector) {
window.$(querySelector).modal('show');
}
exports._removeClassName = function(window, className) {
window.document.body.classList.remove(className);
/**
* @function hide
* @param {Window} window
* @param {string} querySelector
* @unpure {Object} window.$
*/
function hide(window, querySelector) {
window.$(querySelector).modal('hide');
// @XXX Bootstrap not removing some modal elements on "hide" method
// @https://stackoverflow.com/questions/50168312/bootstrap-4-close-modal-backdrop-doesnt-disappear
window.$('body').removeClass('modal-open');
window.$('body').css('padding-right', '0');
window.$('.modal-backdrop').remove();
}
......@@ -4,153 +4,207 @@ import Gargantext.Prelude
import DOM.Simple (Window, window)
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
import Data.UUID as UUID
import Effect (Effect)
import Effect.Uncurried (EffectFn2, runEffectFn2)
import Gargantext.Utils (nbsp, (?))
import Gargantext.Components.Bootstrap.Types (ModalSizing(..))
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
foreign import _addClassName :: EffectFn2 Window String Unit
foreign import _removeClassName :: EffectFn2 Window String Unit
foreign import _show :: EffectFn2
Window
String
Unit
showModal ::
Window
-> String
-> Effect Unit
showModal = runEffectFn2 _show
foreign import _hide :: EffectFn2
Window
String
Unit
hideModal ::
Window
-> String
-> Effect Unit
hideModal = runEffectFn2 _hide
type Props =
( isVisibleBox :: T.Box Boolean
( isVisibleBox :: T.Box Boolean
| Options
)
type Options =
( id :: String
, title :: String
, hasBackground :: Boolean
, hasCollapsibleBackground :: Boolean
( modalClassName :: String
, title :: Maybe String
, hasCollapsibleBackground :: Boolean
, hasInnerScroll :: Boolean
, noHeader :: Boolean
, noBody :: Boolean -- ie. Bootstrap Body
, size :: ModalSizing
)
options :: Record Options
options =
{ id: ""
, title: ""
, hasBackground: true
, hasCollapsibleBackground: true
{ modalClassName : ""
, title : Nothing
, hasCollapsibleBackground : true
, hasInnerScroll : false
, noHeader : false
, noBody : false
, size : MediumModalSize
}
componentName :: String
componentName = "b-modal"
vendorName :: String
vendorName = "modal"
-- | Structural Component for the Bootstrap modal
-- |
-- | @XXX Bootstrap not removing some modal elements on "hide" method
-- | This implies that:
-- | - a FFI fix has been added to remove left elements
-- | - an overlay has been added to synchronise the close button
-- | - the keyboard shortcut has been removed
-- | @https://stackoverflow.com/questions/50168312/bootstrap-4-close-modal-backdrop-doesnt-disappear
-- |
-- | https://getbootstrap.com/docs/4.6/components/modal/
baseModal :: forall r. R2.OptComponent Options Props r
baseModal = R2.optComponent component options
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt { isVisibleBox
, id
, title
, hasBackground
, hasCollapsibleBackground
} children = do
-- State
cpt props@{ isVisibleBox
, title
, hasCollapsibleBackground
, hasInnerScroll
, noHeader
, noBody
, size
} children
= R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- | States
-- |
isVisible <- R2.useLive' isVisibleBox
-- Hooks
R.useEffect1' isVisible $
(isVisible ? addClassName $ removeClassName) window "modal-open"
-- Computed
-- | Computed
-- |
let
className = intercalate " "
-- Component
[ componentName
, isVisible ?
componentName <> "--visible" $
componentName <> "--hidden"
-- Vendor
, vendorName
-- Bootstrap
, "modal"
]
hasHeader = not $ eq title ""
id = componentName <> "-" <> uuid
-- Render
-- | Hooks
-- |
useUpdateEffect1' isVisible
if isVisible
then showModal window $ "#" <> id
else hideModal window $ "#" <> id
-- | Behaviors
-- |
let
onCloseButtonClick _ = T.modify_ (not) isVisibleBox
-- [ Render
-- |
R.createPortal
[
H.div
{ id
{ id: id
, className
, role: "dialog"
, data: { show: true }
, tabIndex: "-1"
, key: id
, data:
{ keyboard: "false"
, backdrop: hasCollapsibleBackground ?
"true" $
"static"
}
}
[
R2.when (hasBackground) $
-- Overlay fixing collapsable click event
R2.when (hasCollapsibleBackground) $
H.div
{ className: intercalate " "
[ componentName <> "__overlay"
, hasCollapsibleBackground ?
componentName <> "__overlay--collapsible" $
""
]
, on: { click: hasCollapsibleBackground ?
toggle isVisibleBox $
const $ pure unit
}
{ className: componentName <> "__overlay"
, on: { click: onCloseButtonClick }
}
[ H.text $ nbsp 1 ]
[]
,
H.div
{ className: "modal-dialog modal-lg"
, role: "document"
{ className: intercalate " "
-- Bootstrap classNames
[ "modal-dialog"
, show size
, "modal-dialog-centered"
, hasInnerScroll ? "modal-dialog-scrollable" $ ""
-- provided custom className
, props.modalClassName
]
}
[
H.div
{ className: intercalate " "
[ componentName <> "__content"
, vendorName <> "-content"
, "modal-content"
]
}
[
R2.when (hasHeader) $
-- Header
R2.when (not noHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, vendorName <> "-header"
, "modal-header"
]
}
[
H.div
{ className: componentName <> "__header__content" }
[ H.text title ]
R2.fromMaybe (title) \title' ->
H.div
{ className: componentName <> "__header__title" }
[ H.text title' ]
,
H.button
{ type: "button"
, className: "close"
, data: { dismiss: "modal" }
}
[
H.a
{ on: { click: toggle isVisibleBox }
, className: "btn fa fa-times" }
{
on: { click: onCloseButtonClick }
, className: "btn fa fa-times"
}
[]
]
]
,
-- Body
H.div
{ className: "modal-body" }
{ className: intercalate " "
[ componentName <> "__body"
, noBody ? "" $ "modal-body"
]
}
children
]
]
]
]
<$> R2.getPortalHost
toggle :: forall event. T.Box Boolean -> event -> Effect Unit
toggle box _ = T.modify_ not box
addClassName :: Window -> String -> Effect Unit
addClassName = runEffectFn2 _addClassName
removeClassName :: Window -> String -> Effect Unit
removeClassName = runEffectFn2 _removeClassName
......@@ -42,7 +42,7 @@ options =
-- | Structural Component for the Bootstrap button
-- |
-- | https://getbootstrap.com/docs/4.0/components/buttons/
-- | https://getbootstrap.com/docs/4.6/components/buttons/
button :: forall r. R2.OptComponent Options Props r
button = R2.optComponent component options
......
......@@ -6,6 +6,7 @@ module Gargantext.Components.Bootstrap.Types
, TooltipEffect(..), TooltipPosition(..)
, Position(..)
, Elevation(..)
, ModalSizing(..)
) where
import Gargantext.Prelude
......@@ -164,7 +165,7 @@ instance Show TooltipPosition where
----------------------------------------------------------------------
-- | Elevarion measure scale values used on various custom components
-- | Elevation measure scale values used on various custom components
-- | and properties
-- |
-- | Example: https://material.io/design/environment/elevation.html
......@@ -176,3 +177,22 @@ data Elevation
derive instance Generic Elevation _
derive instance Eq Elevation
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
= SmallModalSize
| MediumModalSize
| LargeModalSize
| ExtraLargeModalSize
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"
......@@ -26,7 +26,7 @@ import Effect.Class (liftEffect)
import Effect.Timer (setTimeout)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..))
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), ModalSizing(..), Variant(..))
import Gargantext.Components.Category (rating)
import Gargantext.Components.Category.Types (Star(..))
import Gargantext.Components.DocsTable.DocumentFormCreation as DFC
......@@ -227,8 +227,9 @@ docViewCpt = here.component "docView" cpt where
-- Document Creation Modal
B.baseModal
{ isVisibleBox: isDocumentModalVisibleBox
, title: "Add a new document"
, title: Just "Add a new document"
, hasCollapsibleBackground: false
, size: LargeModalSize
}
[
DFC.documentFormCreation
......
......@@ -28,19 +28,24 @@ nodePopupViewCpt :: R.Component NodePopupProps
nodePopupViewCpt = here.component "nodePopupView" cpt where
cpt props _ = do
pure $ H.div tooltipProps
[ H.div { className: "popup-container" }
[ H.div { className: "card" }
[ panelHeading props
]]]
pure $
H.div
{ className: "node-popup-tooltip"
, title: "Node settings"
}
[
H.div
{ className: "popup-container card" }
[
panelHeading props
]
]
closePopover props = props.onPopoverClose <<< R.unsafeEventTarget
tooltipProps = { id: "node-popup-tooltip", title: "Node settings"
, data: { toggle: "tooltip", placement: "right" } }
panelHeading props@{ nodeType } =
H.div { className: "card-header" }
H.div { className: "popup-container__header card-header" }
[ R2.row
[ H.div { className: "col-4" }
[ H.span { className: GT.fldr nodeType true} [] -- TODO fix names
......@@ -49,4 +54,4 @@ nodePopupViewCpt = here.component "nodePopupView" cpt where
[ H.span { className: "text-primary center" } [ H.text props.name ] ]
, H.div { className: "col-1" }
[ H.a { type: "button", on: { click: closePopover props }, title: "Close"
, className: glyphicon "window-close" } [] ]]]
, className: glyphicon "window-close" } [] ]]]
......@@ -18,7 +18,7 @@ import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), TooltipEffect(..), Variant(..))
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), ModalSizing(..), TooltipEffect(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fileTypeView)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..))
......@@ -47,6 +47,7 @@ import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
import Toestand as T
import Unsafe.Coerce (unsafeCoerce)
-- (?) never been able to properly declare PureScript Regex...
foreign import nodeUserRegexp :: Regex.Regex
......@@ -101,13 +102,14 @@ nodeSpanCpt = here.component "nodeSpan" cpt
droppedFile' <- T.useLive T.unequal droppedFile
isDragOver <- T.useBox false
isDragOver' <- T.useLive T.unequal isDragOver
popoverRef <- R.useRef null
currentTasks <- GAT.focus id tasks
currentTasks <- GAT.focus id tasks
currentTasks' <- T.useLive T.unequal currentTasks
folderOpen' <- R2.useLive' folderOpen
isSettingsModalVisible <- T.useBox false
-- tasks' <- T.read tasks
-- Computed
......@@ -205,10 +207,10 @@ nodeSpanCpt = here.component "nodeSpan" cpt
onNodeLinkClick :: Unit -> Effect Unit
onNodeLinkClick _ = when (not isSelected) (T.write_ true folderOpen)
-- Hooks
toggleSettingsModal :: Unit -> Effect Unit
toggleSettingsModal _ = T.modify_ (not) isSettingsModalVisible
useFirstEffect' $
R.setRef setPopoverRef $ Just $ Popover.setOpen popoverRef
-- Hooks
mVersion <- useVersion $ nodeType == GT.NodeUser ?
Just { session } $
......@@ -313,42 +315,18 @@ nodeSpanCpt = here.component "nodeSpan" cpt
, session
} []
,
-- @XXX: React Awesome Popover not suited for the feature UX
-- We SHOULD use a more common `Modal` type of thing
-- As of now, we have issues on z-index management and erratic
-- popup close action
R2.when (showBox) $
Popover.popover
{ arrow: false
, open: false
, onClose: \_ -> pure unit
, onOpen: \_ -> pure unit
, ref: popoverRef
B.iconButton
{ name: "cog"
, className: "mainleaf__settings-icon"
, callback: toggleSettingsModal
, title:
"Each node of the Tree can perform some actions.\n"
<> "Click here to execute one of them."
, variant: Secondary
, elevation: Level1
}
[
B.iconButton
{ name: "cog"
, className: "mainleaf__settings-icon"
-- (cf. Popover callbacks)
, callback: const R.nothing
, title:
"Each node of the Tree can perform some actions.\n"
<> "Click here to execute one of them."
, variant: Secondary
, elevation: Level1
}
,
nodePopupView
{ boxes
, dispatch
, id
, name
, nodeType
, onPopoverClose: const $ onPopoverClose popoverRef
, session
}
]
,
R.fragment $ flip map currentTasks' \task ->
......@@ -363,6 +341,27 @@ nodeSpanCpt = here.component "nodeSpan" cpt
taskProgress
{}
]
,
-- // Modals //
B.baseModal
{ isVisibleBox: isSettingsModalVisible
, noBody: true
, noHeader: true
, modalClassName: "forest-tree-node-modal"
}
[
nodePopupView
{ boxes
, dispatch
, id
, name
, nodeType
, onPopoverClose: \_ -> toggleSettingsModal unit
, session
}
]
]
......
......@@ -5,11 +5,8 @@ import Gargantext.Prelude
import Data.Array as A
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff)
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Forest.Tree.Node.Action.Add (addNodeView)
import Gargantext.Components.Forest.Tree.Node.Action.Contact as Contact
import Gargantext.Components.Forest.Tree.Node.Action.Delete (actionDelete)
......@@ -34,6 +31,9 @@ import Gargantext.Types (ID, Name, prettyNodeType)
import Gargantext.Types as GT
import Gargantext.Utils.Glyphicon (glyphicon, glyphiconActive)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Box"
......@@ -53,24 +53,27 @@ nodePopupCpt = here.component "nodePopupView" cpt where
action <- T.useFocused (_.action) (\a b -> b { action = a }) nodePopup
nodePopup' <- T.useLive T.unequal nodePopup
pure $ H.div tooltipProps
[ H.div { className: "popup-container" }
[ H.div { className: "card" }
[ panelHeading renameIsOpen open p
, H.div { className: "popup-container-body" }
[
panelBody action p
,
mPanelAction nodePopup' p
]
]
pure $
H.div
{ className: "node-popup-tooltip"
, title: "Node settings"
}
[
H.div
{ className: "popup-container card" }
[
panelHeading renameIsOpen open p
,
panelBody action p
,
mPanelAction nodePopup' p
]
]
closePopover p = p.onPopoverClose <<< R.unsafeEventTarget
tooltipProps = { id: "node-popup-tooltip", title: "Node settings"
, data: { toggle: "tooltip", placement: "right" } }
panelHeading renameIsOpen open p@{ dispatch, id, name, nodeType } =
H.div { className: "card-header" }
H.div { className: "popup-container__header card-header" }
[ R2.row
[ H.div { className: "col-4" }
[ H.span { className: GT.fldr nodeType true} [] -- TODO fix names
......@@ -92,8 +95,8 @@ nodePopupCpt = here.component "nodePopupView" cpt where
panelBody :: T.Box (Maybe NodeAction) -> Record NodePopupProps -> R.Element
panelBody nodePopupState { nodeType } =
let (SettingsBox { doc, buttons }) = settingsBox nodeType in
H.div {className: "card-body flex-space-between"}
$ [ H.p { className: "spacer" } []
H.div {className: "popup-container__body card-body flex-space-between"}
$ [ B.wad_ [ "m-1" ]
, H.div { className: "flex-center" }
[ buttonClick { action: doc, state: nodePopupState, nodeType } ]
, H.div {className: "flex-center"}
......@@ -114,15 +117,15 @@ nodePopupCpt = here.component "nodePopupView" cpt where
, session
}
mPanelAction { action: Nothing } _ =
H.div { className: "card-footer" }
H.div { className: "popup-container__footer card-footer" }
[ H.div {className:"center fa-hand-pointer-o"}
[ H.h5 {} [ H.text " Select available actions of this node" ]
, H.ul { className: "panel-actions" }
[ H.div { className: "fa-thumbs-o-up ok-to-use" }
[ H.div { className: "fa-thumbs-o-up panel-actions__ok-to-use" }
[ H.text " Black: usable" ]
, H.div { className: "fa-exclamation-triangle almost-useable" }
, H.div { className: "fa-exclamation-triangle panel-actions__almost-useable" }
[ H.text " Orange: almost useable" ]
, H.div { className: "fa-rocket development-in-progress" }
, H.div { className: "fa-rocket panel-actions__development-in-progress" }
[ H.text " Red: development in progress" ]]]]
type ActionState =
......
......@@ -11,8 +11,9 @@ import Data.String as DST
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Login.Form (form)
import Gargantext.Components.Bootstrap.Types (ModalSizing(..))
import Gargantext.Components.Login.ForgotPassword (forgotPassword)
import Gargantext.Components.Login.Form (form)
import Gargantext.Components.Login.Types (FormType(..))
import Gargantext.Components.NgramsTable.Loader as NTL
import Gargantext.Ends (Backend(..))
......@@ -48,12 +49,14 @@ loginCpt = here.component "login" cpt where
mBackend <- R2.useLive' props.backend
formType <- T.useBox Login
formType' <- T.useLive T.unequal formType
-- Render
pure $
B.baseModal
{ isVisibleBox: visible
, title: "GarganText ecosystem explorer"
, title: Just "GarganText ecosystem explorer"
, size: ExtraLargeModalSize
}
[
case mBackend of
......
'use strict';
exports.modalShow = function(name) {
return function(){
var myModal = document.getElementById(name);
var myModalInstance = new Modal(myModal);
myModalInstance.show();
};
};
exports.modalHide = function(name){
return function() {
var myModal = document.getElementById(name);
var myModalInstance = new Modal(myModal);
myModalInstance.hide();
};
};
module Gargantext.Components.Modals.Modal where
import Prelude (Unit)
import Effect (Effect)
foreign import modalShow :: String -> Effect Unit
foreign import modalHide :: String -> Effect Unit
......@@ -2,19 +2,20 @@ module Gargantext.Components.Router (router) where
import Gargantext.Prelude
import DOM.Simple as DOM
import Data.Array (filter, length)
import Data.Array as A
import Data.Foldable (intercalate)
import Data.Map as M
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Maybe (Maybe(..), fromMaybe, maybe)
import Data.UUID (UUID)
import Data.UUID as UUID
import Effect (Effect)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.ErrorsView (errorsView)
import Gargantext.Components.Forest (forestLayout)
import Gargantext.Components.Login (login)
import Gargantext.Components.ForgotPassword (forgotPasswordLayout)
import Gargantext.Components.Login (login)
import Gargantext.Components.Nodes.Annuaire (annuaireLayout)
import Gargantext.Components.Nodes.Annuaire.User (userLayout)
import Gargantext.Components.Nodes.Annuaire.User.Contact (contactLayout)
......@@ -66,26 +67,31 @@ router :: R2.Leaf Props
router = R2.leafComponent routerCpt
routerCpt :: R.Component Props
routerCpt = here.component "router" cpt where
cpt { boxes: boxes@{ handed, showLogin } } _ = do
cpt { boxes: boxes@{ handed } } _ = do
-- States
handed' <- R2.useLive' handed
showLogin' <- R2.useLive' showLogin
-- Effects
-- Computed
let
handedClassName :: Handed -> String
handedClassName = case _ of
LeftHanded -> "left-handed"
RightHanded -> "right-handed"
R.useEffect1' handed' $
getElementById "app" >>= case _ of
Nothing -> pure unit
Just app -> do
R2.removeClass app
[ handedClassName LeftHanded
, handedClassName RightHanded
]
R2.addClass app [ handedClassName handed' ]
toggleHandedClass :: Handed -> DOM.Element -> Effect Unit
toggleHandedClass new el = do
R2.removeClass el
[ handedClassName LeftHanded
, handedClassName RightHanded
]
R2.addClass el
[ handedClassName new
]
-- Effects
R.useLayoutEffect1' handed' do
getElementById "app" >>= maybe R.nothing (toggleHandedClass handed')
getElementById "portal" >>= maybe R.nothing (toggleHandedClass handed')
-- Render
pure $
......@@ -93,9 +99,7 @@ routerCpt = here.component "router" cpt where
H.div
{ className: "router" }
[
-- loginModal { boxes }
R2.when showLogin' $
login' boxes
login' boxes
, TopBar.topBar { boxes }
, errorsView { errors: boxes.errors } []
, H.div { className: "router__inner" }
......@@ -706,5 +710,5 @@ forgotPasswordCpt = here.component "forgotPassword" cpt where
cpt { params } _ = do
let server = fromMaybe "" $ M.lookup "server" params
let uuid = fromMaybe "" $ M.lookup "uuid" params
pure $ forgotPasswordLayout { server, uuid } []
'use strict';
/**
* @param {DOMElement} element
* @param {string} attribute
* @param {string} value
* @returns
*/
exports.setAttribute = function(element) {
return function(attribute) {
return function(value) {
return function() {
element.setAttribute(attribute, value);
}
}
}
}
......@@ -2,34 +2,18 @@ module Gargantext.Components.Themes where
import Gargantext.Prelude
import DOM.Simple (document)
import Data.Array as A
import Data.Eq.Generic (genericEq)
import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..))
import Data.Nullable (toMaybe)
import Data.Maybe (Maybe(..), maybe)
import Effect (Effect)
import FFI.Simple ((...), (.=))
import FFI.Simple ((.=))
import Gargantext.Utils.Reactix (getElementById, (~~))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
-- (?) Unknown runtime DOM errors lead to a FFI workaround for setting the
-- property of the element (see `markThemeToDOMTree` method)
--
-- Both use cases throw the error:
--
-- ```
-- TypeError: FFI_Simple_Functions.applyMethod'(...)(...)(...) is not a function
-- ```
--
-- ```purescript
-- _ <- el ... "setAttribute" $ [ "data-theme", name ]
-- _ <- pure $ (el .= "data-theme") name
-- ```
foreign import setAttribute :: R.Element -> String -> String -> Effect Unit
here :: R2.Here
here = R2.here "Gargantext.Components.Themes"
......@@ -79,10 +63,9 @@ switchTheme (Theme { location }) = do
markThemeToDOMTree :: Theme -> Effect Unit
markThemeToDOMTree (Theme { name }) = do
mEl <- pure $ toMaybe (document ... "getElementById" $ [ "app" ])
case mEl of
Nothing -> pure unit
Just el -> setAttribute el "data-theme" name
let setTheme el = pure $ (el ~~ "setAttribute") [ "data-theme", name ]
getElementById "app" >>= maybe R.nothing (setTheme)
getElementById "portal" >>= maybe R.nothing (setTheme)
type ThemeSwitcherProps = (
......
$forest-layout-top-teaser-height: 24px
$forest-layout-bottom-teaser-height: 24px // ~line-height to 1.5 (covering tree)
$node-popop-radius: 6px
$node-popup-width: 544px
......@@ -430,89 +429,47 @@ $node-popup-width: 544px
/////////////////////////////////////////
#node-popup-tooltip
// @XXX "react-awesome-popover" lack of parent host parameter
//
// hence, the popover will be added from within the overflow context
// of component where its CTA lies, causing an issue with the hidden
// overflow of its parent component
//
// to avoid a truncated popover, this position workaround has to be set
// (so the popover will be hoisted to the overflow context of the window)
//
// @link https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/302
position: fixed
background-color: white
border-radius: $node-popop-radius
&:hover
border: none
text-decoration: none
// @XXX "react-awesome-popover" lack of parent host parameter
//
// as the container where the popover will be appended is actually a
// a heighthy sidebar (which a large is hidden with a scrollbar),
// the library tends to add the tooltip position in the middle of the
// sidebar height: which can be off screen
//
// this workaround appends the tooltip centered in a static position
@include centered
.forest-tree-node-modal
// will enlarge popup when inner content is larger (see issue #315),
// with a minimal width to avoid row item collapsing (see issue #324)
width: fit-content
min-width: $node-popup-width
max-width: 100vw // override Bootstrap modal rules (via sizing props)
.tree
.node
margin-top: 5px
.children
.node
padding-left: 15px
.node-popup-tooltip
.panel-actions
.almost-useable
&__almost-useable
color: orange
.development-in-progress
&__development-in-progress
color: red
.ok-to-use
&__ok-to-use
color: black
.popup-container
display: flex
flex-direction: colum
& > .card
// will enlarge popup when inner content is larger (see issue #315),
// with a minimal width to avoid row item collapsing (see issue #324)
width: auto !important
min-width: $node-popup-width
flex-direction: column
border: 1px solid rgba(0,0,0,0.2)
box-shadow: 0 2px 5px rgba(0,0,0,0.2)
margin-bottom: initial
&__header
.fa-pencil
color: black
.card-body
display: flex
justify-content: center
background-color: white
border: none
.spacer
margin: space-x(1)
.frame-search.card
border: 1px solid rgba(0,0,0,0.2)
box-shadow: 0 2px 5px rgba(0,0,0,0.2)
height: 600px
width: 1000px
.popup-container-body
// empirical value (see issue #308, #315)
max-height: 70vh
overflow-y: auto
&__body
display: flex
justify-content: center
background-color: $body-bg
/////////////////////////////////////////
.frame-search.card
$frame-width: 1000px
$frame-height: 600px
width: $frame-width
height: $frame-height
/////////////////////////////////////////
......
......@@ -22,39 +22,30 @@
$self: &;
$wrapper-margin: 1.75rem; // from "_modal.scss"
&--visible {
display: block;
}
&__content {
background-color: $modal-content-bg;
max-height: calc(100% - #{ $wrapper-margin * 2 } );
overflow: auto;
margin-left: $wrapper-margin;
margin-right: $wrapper-margin;
}
&__header {
background-color: $card-cap-bg;
&__content {
&__title {
color: $primary;
font-size: 21px;
}
}
// @XXX Bootstrap not removing some modal elements on "hide" method
// Adding an overlay that the user could click to synchronise the hide
// cinematic
// @https://stackoverflow.com/questions/50168312/bootstrap-4-close-modal-backdrop-doesnt-disappear
&__overlay {
width: 100%;
height: 100%;
background-color: mix-alpha($black, 50%);
position: absolute;
&--collapsible {
cursor: pointer;
}
}
// @at-root body.modal-open {
// overflow: hidden;
// }
}
/// Spinner
......