diff --git a/dist/styles/bootstrap-darkster.css b/dist/styles/bootstrap-darkster.css index 2d0d0ffe49c8438c52eeb24e4bed8ef0c89acc27..d9fdcaa5460a87fc2010883e7d10e71513c03b88 100644 --- a/dist/styles/bootstrap-darkster.css +++ b/dist/styles/bootstrap-darkster.css @@ -8747,31 +8747,6 @@ a:focus, a:hover { top: 50%; } -.table tr td { - color: #005a9aff; -} -.table tr td .active { - font-weight: bold; - text-decoration: underline; -} -.table tr td .ngrams-selector { - display: flex; -} -.table tr td .ngrams-selector .ngrams-chooser { - padding: 3px; -} -.table tr td .trash { - text-decoration: line-through; -} - -.action-search { - margin: 10px; -} - -.search-bar { - margin: 10px; -} - /* */ .join-button { padding-bottom: 100px; @@ -8960,6 +8935,116 @@ select.form-control { opacity: var(--over50, 0); } +.table tr td { + height: 48px; +} +.table .page-paint-raw--selected { + position: relative; +} +.table .page-paint-raw--selected td:first-child::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + width: 3px; + background-color: #0F81C7; + position: absolute; +} +.table .page-paint-row--trash { + text-decoration: line-through; +} +.table .doc-chooser { + padding-top: 3px; + text-align: center; +} +.table .rating-group { + display: flex; + padding-top: 3px; +} +.table .rating-group__action { + width: 14px; + margin-right: 8px; +} + +.search-button-prepend .input-group-text { + width: 41px; + z-index: initial; +} + +.ngrams-table-container__header { + display: flex; + align-items: flex-start; +} +.ngrams-table-container__header__item { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__header__item:not(:first-child) { + margin-left: 8px; +} +.ngrams-table-container__header__item:not(:last-child) { + margin-right: 8px; +} +.ngrams-table-container__add-term { + margin-top: -8px; + margin-bottom: 12px; +} +.ngrams-table-container__footer { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__footer__item { + text-align: center; +} + +.ngrams-tree-edit-real { + min-width: 416px; + width: fit-content; + border-color: #FF550B; + margin-top: 16px; + margin-bottom: 16px; +} +.ngrams-tree-edit-real__actions { + display: flex; + margin-top: 16px; +} +.ngrams-tree-edit-real__actions .b-button { + margin-right: 8px; +} + +.loaded-ngrams-table-header { + text-align: center; + margin-top: 12px; + margin-bottom: 12px; +} +.loaded-ngrams-table-header__icon { + font-size: 16px; +} +.loaded-ngrams-table-header__text { + font-size: 22px; + font-weight: bold; + font-family: "Comfortaa"; +} + +.ngrams-tree-loaded-node--first-child::before, .ngrams-tree-loaded-node--grand-child::before { + color: #6C757D; + font-size: 11px; + margin-right: 4px; +} +.right-handed .ngrams-tree-loaded-node--first-child::before, .right-handed .ngrams-tree-loaded-node--grand-child::before { + content: "└"; +} + +.left-handed .ngrams-tree-loaded-node--first-child::before, .left-handed .ngrams-tree-loaded-node--grand-child::before { + content: "┘"; +} + +.ngrams-tree-loaded-node--first-child { + margin-left: -2px; +} +.ngrams-tree-loaded-node--grand-child { + margin-left: 13px; +} + .annotation-run { cursor: pointer; } diff --git a/dist/styles/bootstrap-default.css b/dist/styles/bootstrap-default.css index f16537832d216638188ed88fa91b52f30c983088..d3ce27d2eaeb1c3117a2c62937f0af5fe705f5c5 100644 --- a/dist/styles/bootstrap-default.css +++ b/dist/styles/bootstrap-default.css @@ -8700,31 +8700,6 @@ a:focus, a:hover { top: 50%; } -.table tr td { - color: #005a9aff; -} -.table tr td .active { - font-weight: bold; - text-decoration: underline; -} -.table tr td .ngrams-selector { - display: flex; -} -.table tr td .ngrams-selector .ngrams-chooser { - padding: 3px; -} -.table tr td .trash { - text-decoration: line-through; -} - -.action-search { - margin: 10px; -} - -.search-bar { - margin: 10px; -} - /* */ .join-button { padding-bottom: 100px; @@ -8913,6 +8888,115 @@ select.form-control { opacity: var(--over50, 0); } +.table tr td { + height: 48px; +} +.table .page-paint-raw--selected { + position: relative; +} +.table .page-paint-raw--selected td:first-child::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + width: 3px; + background-color: #17a2b8; + position: absolute; +} +.table .page-paint-row--trash { + text-decoration: line-through; +} +.table .doc-chooser { + padding-top: 3px; + text-align: center; +} +.table .rating-group { + display: flex; + padding-top: 3px; +} +.table .rating-group__action { + width: 14px; + margin-right: 8px; +} + +.search-button-prepend .input-group-text { + width: 41px; + z-index: initial; +} + +.ngrams-table-container__header { + display: flex; + align-items: flex-start; +} +.ngrams-table-container__header__item { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__header__item:not(:first-child) { + margin-left: 8px; +} +.ngrams-table-container__header__item:not(:last-child) { + margin-right: 8px; +} +.ngrams-table-container__add-term { + margin-top: -8px; + margin-bottom: 12px; +} +.ngrams-table-container__footer { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__footer__item { + text-align: center; +} + +.ngrams-tree-edit-real { + min-width: 416px; + width: fit-content; + border-color: #005a9a; + margin-top: 16px; + margin-bottom: 16px; +} +.ngrams-tree-edit-real__actions { + display: flex; + margin-top: 16px; +} +.ngrams-tree-edit-real__actions .b-button { + margin-right: 8px; +} + +.loaded-ngrams-table-header { + text-align: center; + margin-top: 12px; + margin-bottom: 12px; +} +.loaded-ngrams-table-header__icon { + font-size: 16px; +} +.loaded-ngrams-table-header__text { + font-size: 22px; + font-weight: bold; +} + +.ngrams-tree-loaded-node--first-child::before, .ngrams-tree-loaded-node--grand-child::before { + color: #CED4DA; + font-size: 11px; + margin-right: 4px; +} +.right-handed .ngrams-tree-loaded-node--first-child::before, .right-handed .ngrams-tree-loaded-node--grand-child::before { + content: "└"; +} + +.left-handed .ngrams-tree-loaded-node--first-child::before, .left-handed .ngrams-tree-loaded-node--grand-child::before { + content: "┘"; +} + +.ngrams-tree-loaded-node--first-child { + margin-left: -2px; +} +.ngrams-tree-loaded-node--grand-child { + margin-left: 13px; +} + .annotation-run { cursor: pointer; } diff --git a/dist/styles/bootstrap-greyson.css b/dist/styles/bootstrap-greyson.css index b6a593f06009a873f28648a17a2311f7575126ea..c11b0878f9d7102ace9f69a31494eff185b23d0a 100644 --- a/dist/styles/bootstrap-greyson.css +++ b/dist/styles/bootstrap-greyson.css @@ -8456,31 +8456,6 @@ a:focus, a:hover { top: 50%; } -.table tr td { - color: #005a9aff; -} -.table tr td .active { - font-weight: bold; - text-decoration: underline; -} -.table tr td .ngrams-selector { - display: flex; -} -.table tr td .ngrams-selector .ngrams-chooser { - padding: 3px; -} -.table tr td .trash { - text-decoration: line-through; -} - -.action-search { - margin: 10px; -} - -.search-bar { - margin: 10px; -} - /* */ .join-button { padding-bottom: 100px; @@ -8669,6 +8644,116 @@ select.form-control { opacity: var(--over50, 0); } +.table tr td { + height: 48px; +} +.table .page-paint-raw--selected { + position: relative; +} +.table .page-paint-raw--selected td:first-child::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + width: 3px; + background-color: #5c8f94; + position: absolute; +} +.table .page-paint-row--trash { + text-decoration: line-through; +} +.table .doc-chooser { + padding-top: 3px; + text-align: center; +} +.table .rating-group { + display: flex; + padding-top: 3px; +} +.table .rating-group__action { + width: 14px; + margin-right: 8px; +} + +.search-button-prepend .input-group-text { + width: 41px; + z-index: initial; +} + +.ngrams-table-container__header { + display: flex; + align-items: flex-start; +} +.ngrams-table-container__header__item { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__header__item:not(:first-child) { + margin-left: 8px; +} +.ngrams-table-container__header__item:not(:last-child) { + margin-right: 8px; +} +.ngrams-table-container__add-term { + margin-top: -8px; + margin-bottom: 12px; +} +.ngrams-table-container__footer { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__footer__item { + text-align: center; +} + +.ngrams-tree-edit-real { + min-width: 416px; + width: fit-content; + border-color: #2f3c48; + margin-top: 16px; + margin-bottom: 16px; +} +.ngrams-tree-edit-real__actions { + display: flex; + margin-top: 16px; +} +.ngrams-tree-edit-real__actions .b-button { + margin-right: 8px; +} + +.loaded-ngrams-table-header { + text-align: center; + margin-top: 12px; + margin-bottom: 12px; +} +.loaded-ngrams-table-header__icon { + font-size: 16px; +} +.loaded-ngrams-table-header__text { + font-size: 22px; + font-weight: bold; + font-family: "Oswald"; +} + +.ngrams-tree-loaded-node--first-child::before, .ngrams-tree-loaded-node--grand-child::before { + color: #CED4DA; + font-size: 11px; + margin-right: 4px; +} +.right-handed .ngrams-tree-loaded-node--first-child::before, .right-handed .ngrams-tree-loaded-node--grand-child::before { + content: "└"; +} + +.left-handed .ngrams-tree-loaded-node--first-child::before, .left-handed .ngrams-tree-loaded-node--grand-child::before { + content: "┘"; +} + +.ngrams-tree-loaded-node--first-child { + margin-left: -2px; +} +.ngrams-tree-loaded-node--grand-child { + margin-left: 13px; +} + .annotation-run { cursor: pointer; } diff --git a/dist/styles/bootstrap-herbie.css b/dist/styles/bootstrap-herbie.css index 976bae684fd7c12e42013b6b2ee0cc3828462b0b..39bfdd55f765bbcdcc1fa08ca1673c87a651f537 100644 --- a/dist/styles/bootstrap-herbie.css +++ b/dist/styles/bootstrap-herbie.css @@ -8704,31 +8704,6 @@ a:focus, a:hover { top: 50%; } -.table tr td { - color: #005a9aff; -} -.table tr td .active { - font-weight: bold; - text-decoration: underline; -} -.table tr td .ngrams-selector { - display: flex; -} -.table tr td .ngrams-selector .ngrams-chooser { - padding: 3px; -} -.table tr td .trash { - text-decoration: line-through; -} - -.action-search { - margin: 10px; -} - -.search-bar { - margin: 10px; -} - /* */ .join-button { padding-bottom: 100px; @@ -8917,6 +8892,116 @@ select.form-control { opacity: var(--over50, 0); } +.table tr td { + height: 48px; +} +.table .page-paint-raw--selected { + position: relative; +} +.table .page-paint-raw--selected td:first-child::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + width: 3px; + background-color: #74DBEF; + position: absolute; +} +.table .page-paint-row--trash { + text-decoration: line-through; +} +.table .doc-chooser { + padding-top: 3px; + text-align: center; +} +.table .rating-group { + display: flex; + padding-top: 3px; +} +.table .rating-group__action { + width: 14px; + margin-right: 8px; +} + +.search-button-prepend .input-group-text { + width: 41px; + z-index: initial; +} + +.ngrams-table-container__header { + display: flex; + align-items: flex-start; +} +.ngrams-table-container__header__item { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__header__item:not(:first-child) { + margin-left: 8px; +} +.ngrams-table-container__header__item:not(:last-child) { + margin-right: 8px; +} +.ngrams-table-container__add-term { + margin-top: -8px; + margin-bottom: 12px; +} +.ngrams-table-container__footer { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__footer__item { + text-align: center; +} + +.ngrams-tree-edit-real { + min-width: 416px; + width: fit-content; + border-color: #083358; + margin-top: 16px; + margin-bottom: 16px; +} +.ngrams-tree-edit-real__actions { + display: flex; + margin-top: 16px; +} +.ngrams-tree-edit-real__actions .b-button { + margin-right: 8px; +} + +.loaded-ngrams-table-header { + text-align: center; + margin-top: 12px; + margin-bottom: 12px; +} +.loaded-ngrams-table-header__icon { + font-size: 16px; +} +.loaded-ngrams-table-header__text { + font-size: 22px; + font-weight: bold; + font-family: "Crete Round"; +} + +.ngrams-tree-loaded-node--first-child::before, .ngrams-tree-loaded-node--grand-child::before { + color: #CED4DA; + font-size: 11px; + margin-right: 4px; +} +.right-handed .ngrams-tree-loaded-node--first-child::before, .right-handed .ngrams-tree-loaded-node--grand-child::before { + content: "└"; +} + +.left-handed .ngrams-tree-loaded-node--first-child::before, .left-handed .ngrams-tree-loaded-node--grand-child::before { + content: "┘"; +} + +.ngrams-tree-loaded-node--first-child { + margin-left: -2px; +} +.ngrams-tree-loaded-node--grand-child { + margin-left: 13px; +} + .annotation-run { cursor: pointer; } diff --git a/dist/styles/bootstrap-monotony.css b/dist/styles/bootstrap-monotony.css index a8f3031c450506082a65d036e7b411aad6935b1c..354fa1750d8f2231772b457042d936c417276034 100644 --- a/dist/styles/bootstrap-monotony.css +++ b/dist/styles/bootstrap-monotony.css @@ -8705,31 +8705,6 @@ a:focus, a:hover { top: 50%; } -.table tr td { - color: #005a9aff; -} -.table tr td .active { - font-weight: bold; - text-decoration: underline; -} -.table tr td .ngrams-selector { - display: flex; -} -.table tr td .ngrams-selector .ngrams-chooser { - padding: 3px; -} -.table tr td .trash { - text-decoration: line-through; -} - -.action-search { - margin: 10px; -} - -.search-bar { - margin: 10px; -} - /* */ .join-button { padding-bottom: 100px; @@ -8918,6 +8893,116 @@ select.form-control { opacity: var(--over50, 0); } +.table tr td { + height: 48px; +} +.table .page-paint-raw--selected { + position: relative; +} +.table .page-paint-raw--selected td:first-child::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + width: 3px; + background-color: #515151; + position: absolute; +} +.table .page-paint-row--trash { + text-decoration: line-through; +} +.table .doc-chooser { + padding-top: 3px; + text-align: center; +} +.table .rating-group { + display: flex; + padding-top: 3px; +} +.table .rating-group__action { + width: 14px; + margin-right: 8px; +} + +.search-button-prepend .input-group-text { + width: 41px; + z-index: initial; +} + +.ngrams-table-container__header { + display: flex; + align-items: flex-start; +} +.ngrams-table-container__header__item { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__header__item:not(:first-child) { + margin-left: 8px; +} +.ngrams-table-container__header__item:not(:last-child) { + margin-right: 8px; +} +.ngrams-table-container__add-term { + margin-top: -8px; + margin-bottom: 12px; +} +.ngrams-table-container__footer { + padding: 0.75rem 1.25rem; +} +.ngrams-table-container__footer__item { + text-align: center; +} + +.ngrams-tree-edit-real { + min-width: 416px; + width: fit-content; + border-color: #222222; + margin-top: 16px; + margin-bottom: 16px; +} +.ngrams-tree-edit-real__actions { + display: flex; + margin-top: 16px; +} +.ngrams-tree-edit-real__actions .b-button { + margin-right: 8px; +} + +.loaded-ngrams-table-header { + text-align: center; + margin-top: 12px; + margin-bottom: 12px; +} +.loaded-ngrams-table-header__icon { + font-size: 16px; +} +.loaded-ngrams-table-header__text { + font-size: 22px; + font-weight: bold; + font-family: "Open Sans"; +} + +.ngrams-tree-loaded-node--first-child::before, .ngrams-tree-loaded-node--grand-child::before { + color: #CED4DA; + font-size: 11px; + margin-right: 4px; +} +.right-handed .ngrams-tree-loaded-node--first-child::before, .right-handed .ngrams-tree-loaded-node--grand-child::before { + content: "└"; +} + +.left-handed .ngrams-tree-loaded-node--first-child::before, .left-handed .ngrams-tree-loaded-node--grand-child::before { + content: "┘"; +} + +.ngrams-tree-loaded-node--first-child { + margin-left: -2px; +} +.ngrams-tree-loaded-node--grand-child { + margin-left: 13px; +} + .annotation-run { cursor: pointer; } diff --git a/src/Gargantext/Components/Category.purs b/src/Gargantext/Components/Category.purs index 3fb242369ac5cad7b8f636e7e3101a552f4cde32..1a5b7cac1ca27efb4e98217cd0f5bf949cbb8411 100644 --- a/src/Gargantext/Components/Category.purs +++ b/src/Gargantext/Components/Category.purs @@ -9,12 +9,15 @@ import Data.Map as Map import Data.Maybe (Maybe(..)) import Effect.Aff (launchAff_) import Effect.Class (liftEffect) +import Gargantext.Components.Bootstrap as B +import Gargantext.Components.Bootstrap.Types (Variant(..)) import Gargantext.Components.Category.Types (Category(..), Star(..), cat2score, categories, clickAgain, star2score, stars) import Gargantext.Components.DocsTable.Types (DocumentsView(..), LocalCategories, LocalUserScore) import Gargantext.Config.REST (AffRESTError) import Gargantext.Routes (SessionRoute(NodeAPI)) import Gargantext.Sessions (Session, put) import Gargantext.Types (NodeID, NodeType(..)) +import Gargantext.Utils ((?)) import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Toestand as T2 import Reactix as R @@ -42,23 +45,53 @@ ratingCpt = here.component "rating" cpt where , row: DocumentsView r , score , session - , setLocalCategories } _ = - pure $ H.div { className:"flex" } divs where - divs = map (\s -> H.div { className : icon' score s - , on: { click: onClick s } } []) stars - icon' Star_0 Star_0 = "fa fa-times-circle" - icon' _ Star_0 = "fa fa-times" - icon' c s = if star2score c < star2score s then "fa fa-star-o" else "fa fa-star" + , setLocalCategories + } _ = do + -- | Computed + -- | + let + icon' Star_0 Star_0 = "times-circle" + icon' _ Star_0 = "times" + icon' c s = star2score c < star2score s ? "star-o" $ "star" + + variant' Star_0 Star_0 = Dark + variant' _ Star_0 = Dark + variant' _ _ = Dark + + className' Star_0 Star_0 = "rating-group__action" + className' _ Star_0 = "rating-group__action" + className' _ _ = "rating-group__star" + + -- | Behaviors + -- | + let onClick c _ = do - let c' = if score == c - then clickAgain c - else c + let c' = score == c ? clickAgain c $ c setLocalCategories $ Map.insert r._id c' - launchAff_ $ do - _ <- putRating session nodeId $ RatingQuery { nodeIds: [r._id], rating: c' } + launchAff_ do + _ <- putRating session nodeId $ RatingQuery + { nodeIds: [r._id] + , rating: c' + } liftEffect $ T2.reload chartReload + -- | Render + -- | + pure $ + + H.div + { className: "rating-group" } $ + stars <#> \s -> + B.iconButton + { name: icon' score s + , callback: onClick s + , overlay: false + , variant: variant' score s + , className: className' score s + } + + newtype RatingQuery = RatingQuery { nodeIds :: Array Int , rating :: Star diff --git a/src/Gargantext/Components/DocsTable.purs b/src/Gargantext/Components/DocsTable.purs index 647de9e361da6f67e1b32fe2193150b89828a83a..39222c141c5a26c598842f42ef9f033f536e8f23 100644 --- a/src/Gargantext/Components/DocsTable.purs +++ b/src/Gargantext/Components/DocsTable.purs @@ -6,7 +6,6 @@ import Gargantext.Prelude import DOM.Simple.Event as DE import Data.Array (any) import Data.Array as A -import Data.Either (Either) import Data.Generic.Rep (class Generic) import Data.Lens ((^.)) import Data.Lens.At (at) @@ -27,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(..)) +import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..)) import Gargantext.Components.Category (rating) import Gargantext.Components.Category.Types (Star(..)) import Gargantext.Components.DocsTable.DocumentFormCreation as DFC @@ -540,6 +539,9 @@ pagePaintRawCpt = here.component "pagePaintRaw" cpt where reload <- T.useBox GUT.newReload localCategories' <- T.useLive T.unequal localCategories + let + selected = mCurrentDocId' == Just nodeId + pure $ TT.table { colNames , container: TT.defaultContainer @@ -551,9 +553,9 @@ pagePaintRawCpt = here.component "pagePaintRaw" cpt where } where sid = sessionId session - trashClassName Star_0 _ = "trash" - trashClassName _ true = "active" - trashClassName _ false = "" + trashClassName Star_0 _ = "page-paint-row page-paint-row--trash" + trashClassName _ true = "page-paint-row page-paint-row--active" + trashClassName _ false = "" corpusDocument | Just cid <- mCorpusId = Routes.CorpusDocument sid cid listId | otherwise = Routes.Document sid listId @@ -563,7 +565,14 @@ pagePaintRawCpt = here.component "pagePaintRaw" cpt where where row dv@(DocumentsView r@{ _id, category }) = { row: - TT.makeRow [ -- H.div {} [ H.a { className, style, on: {click: click Favorite} } [] ] + TT.makeRow' + { className: "page-paint-raw " <> + (selected ? + "page-paint-raw--selected" $ + "" + ) + } + [ -- H.div {} [ H.a { className, style, on: {click: click Favorite} } [] ] H.div { className: "" } [ docChooser { boxes , listId @@ -582,10 +591,17 @@ pagePaintRawCpt = here.component "pagePaintRaw" cpt where --, H.input { type: "checkbox", defaultValue: checked, on: {click: click Trash} } -- TODO show date: Year-Month-Day only , H.div { className: tClassName } [ R2.showText r.date ] - , H.div { className: tClassName } - [ H.a { href: url frontends $ corpusDocument r._id, target: "_blank" } - [ H.text r.title ] - ] + , + H.div + { className: tClassName } + [ + H.a + { href: url frontends $ corpusDocument r._id + , target: "_blank" + , className: "text-primary" + } + [ H.text r.title ] + ] , H.div { className: tClassName } [ H.text $ showSource r.source ] , H.div {} [ H.text $ maybe "-" show r.ngramCount ] ] @@ -623,11 +639,19 @@ docChooserCpt = here.component "docChooser" cpt mCurrentDocId' <- T.useLive T.unequal mCurrentDocId let selected = mCurrentDocId' == Just nodeId - eyeClass = if selected then "fa-eye" else "fa-eye-slash" + eyeClass = selected ? "eye" $ "eye-slash" + variant = selected ? Info $ Dark - pure $ H.div { className: "btn" } [ - H.span { className: "fa " <> eyeClass - , on: { click: onClick selected } } [] + pure $ + H.div + { className: "doc-chooser" } + [ + B.iconButton + { name: eyeClass + , overlay: false + , variant + , callback: onClick selected + } ] where onClick selected _ = do diff --git a/src/Gargantext/Components/Forest/Tree/Node/Action/Search.purs b/src/Gargantext/Components/Forest/Tree/Node/Action/Search.purs index 3568fab23fbe46ee877e9aa4bb2b91a21e9f5533..9ba560389044d11d3f05238823383c6696d52226 100644 --- a/src/Gargantext/Components/Forest/Tree/Node/Action/Search.purs +++ b/src/Gargantext/Components/Forest/Tree/Node/Action/Search.purs @@ -37,7 +37,7 @@ actionSearchCpt = here.component "actionSearch" cpt cpt { boxes: { errors }, dispatch, id, session } _ = do search <- T.useBox $ defaultSearch { node_id = id } pure $ R.fragment - [ H.p { className: "action-search" } + [ H.p { className: "action-search m-1" } [ H.text $ "Search and create a private " <> "corpus with the search query as corpus name." ] , searchBar { errors diff --git a/src/Gargantext/Components/Forest/Tree/Node/Action/Search/SearchBar.purs b/src/Gargantext/Components/Forest/Tree/Node/Action/Search/SearchBar.purs index b3c0667aa360aa85b1355525098c9a7c68404c47..d0dee907aca522ff2aa155a5dbe454634107eee5 100644 --- a/src/Gargantext/Components/Forest/Tree/Node/Action/Search/SearchBar.purs +++ b/src/Gargantext/Components/Forest/Tree/Node/Action/Search/SearchBar.purs @@ -33,7 +33,7 @@ searchBarCpt = here.component "searchBar" cpt where cpt { errors, langs, onSearch, search, session } _ = do --onSearchChange session s - pure $ H.div { className: "search-bar" } + pure $ H.div { className: "search-bar m-1" } [ searchField { databases: allDatabases , errors , langs diff --git a/src/Gargantext/Components/NgramsTable.purs b/src/Gargantext/Components/NgramsTable.purs index 6217b255b18bf141234fb8847fc657d0db544185..23184ec75427beb5e18cda54f55144efb1df216d 100644 --- a/src/Gargantext/Components/NgramsTable.purs +++ b/src/Gargantext/Components/NgramsTable.purs @@ -13,12 +13,12 @@ import Gargantext.Prelude import Data.Array as A import Data.Either (Either(..)) import Data.FunctorWithIndex (mapWithIndex) -import Data.Lens (to, view, (%~), (.~), (^.), (^?), (^..)) +import Data.Lens (to, view, (.~), (^.), (^?)) import Data.Lens.At (at) import Data.Lens.Common (_Just) import Data.Lens.Fold (folded) import Data.Lens.Index (ix) -import Data.List (List) +import Data.List (List, intercalate) import Data.List as List import Data.Map (Map) import Data.Map as Map @@ -32,26 +32,25 @@ import Data.Tuple (Tuple(..)) import Data.Tuple.Nested ((/\)) import Effect (Effect) import Effect.Aff (Aff) -import Effect.Class (liftEffect) import Gargantext.Components.App.Store (Boxes) import Gargantext.Components.Bootstrap as B -import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) -import Gargantext.Core.NgramsTable.Functions (addNewNgramA, applyNgramsPatches, chartsAfterSync, commitPatch, convOrderBy, coreDispatch, filterTermSize, ngramsRepoElementToNgramsElement, normNgram, patchSetFromMap, setTermListA, singletonNgramsTablePatch, tablePatchHasNgrams, toVersioned) +import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Sizing(..), Variant(..)) +import Gargantext.Components.NgramsTable.Loader (useLoaderWithCacheAPI) import Gargantext.Components.NgramsTable.Search as NTS import Gargantext.Components.NgramsTable.SelectionCheckbox as NTSC -import Gargantext.Components.NgramsTable.Tree (renderNgramsItem, renderNgramsTree) -import Gargantext.Components.NgramsTable.Loader (useLoaderWithCacheAPI) import Gargantext.Components.NgramsTable.SyncResetButton (syncResetButtons) +import Gargantext.Components.NgramsTable.Tree (renderNgramsItem, renderNgramsTree) import Gargantext.Components.Nodes.Lists.Types as NT import Gargantext.Components.Table as TT import Gargantext.Components.Table.Types as TT import Gargantext.Config.REST (AffRESTError, RESTError, logRESTError) -import Gargantext.Core.NgramsTable.Types (Action(..), CoreAction(..), CoreState, Dispatch, NgramsActionRef, NgramsClick, NgramsDepth, NgramsElement(..), NgramsPatch(..), NgramsTable, NgramsTablePatch(..), NgramsTerm(..), PageParams, PatchMap(..), Versioned(..), VersionedNgramsTable, VersionedWithCountNgramsTable, _NgramsElement, _NgramsRepoElement, _NgramsTable, _children, _list, _ngrams, _ngrams_repo_elements, _ngrams_scores, _occurrences, _root, applyPatchSet, ngramsTermText, replace) +import Gargantext.Core.NgramsTable.Functions (addNewNgramA, applyNgramsPatches, chartsAfterSync, commitPatch, convOrderBy, coreDispatch, filterTermSize, ngramsRepoElementToNgramsElement, normNgram, patchSetFromMap, singletonNgramsTablePatch, tablePatchHasNgrams, toVersioned) +import Gargantext.Core.NgramsTable.Types (Action(..), CoreAction(..), CoreState, Dispatch, NgramsActionRef, NgramsClick, NgramsElement(..), NgramsPatch(..), NgramsTable, NgramsTablePatch(..), NgramsTerm(..), PageParams, PatchMap(..), Versioned(..), VersionedNgramsTable, VersionedWithCountNgramsTable, _NgramsElement, _NgramsRepoElement, _NgramsTable, _children, _list, _ngrams, _ngrams_repo_elements, _ngrams_scores, _occurrences, _root, applyPatchSet, ngramsTermText, replace) import Gargantext.Hooks.Loader (useLoaderBox) -import Gargantext.Routes (SessionRoute(..)) as R +import Gargantext.Routes (SessionRoute(..)) as Routes import Gargantext.Sessions (Session, get) import Gargantext.Types (CTabNgramType, ListId, NodeID, OrderBy(..), SearchQuery, TabType, TermList(..), TermSize, termLists, termSizes) -import Gargantext.Utils (queryExactMatchesLabel, queryMatchesLabel, toggleSet, sortWith) +import Gargantext.Utils (nbsp, queryExactMatchesLabel, queryMatchesLabel, sortWith, toggleSet) import Gargantext.Utils.CacheAPI as GUC import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Seq as Seq @@ -147,107 +146,230 @@ tableContainerCpt { addCallback , queryExactMatches , syncResetButton , tabNgramType - } = here.component "tableContainer" cpt - where - cpt props _ = do - { searchQuery, termListFilter, termSizeFilter } <- T.useLive T.unequal path - - pure $ H.div {className: "container-fluid"} - [ R2.row - [ H.div {className: "card col-12"} - [ H.div {className: "card-header"} - [ R2.row - [ H.div { className: "col-md-2", style: {marginTop: "6px" } } - [ H.div {} syncResetButton - , if (not queryExactMatches || A.null props.tableBody) && searchQuery /= "" then - -- , if (not $ Set.member (normNgram tabNgramType searchQuery) ngramsSelection) && searchQuery /= "" then - H.li { className: "list-group-item" } - [ - B.button - { variant: ButtonVariant Primary - , callback: const $ addCallback searchQuery - } - [ H.text ("Add " <> searchQuery) ] - ] else H.div {} [] - ] - , H.div {className: "col-md-2", style: {marginTop : "6px"}} - [ H.li {className: "list-group-item"} - [ R2.select { id: "picklistmenu" - , className: "form-control custom-select" - , defaultValue: (maybe "" show termListFilter) - , on: {change: setTermListFilter <<< read <<< R.unsafeEventValue}} - (map optps1 termLists)] - ] - , H.div {className: "col-md-2", style: {marginTop : "6px"}} - [ H.li {className: "list-group-item"} - [ R2.select {id: "picktermtype" - , className: "form-control custom-select" - , defaultValue: (maybe "" show termSizeFilter) - , on: {change: setTermSizeFilter <<< read <<< R.unsafeEventValue}} - (map optps1 termSizes)] - ] - , H.div { className: "col-md-2", style: { marginTop: "6px" } } - [ H.li {className: "list-group-item"} - [ H.div { className: "form-inline" } - [ H.div { className: "form-group" } - [ props.pageSizeControl - , H.label {} [ H.text " items" ] - -- H.div { className: "col-md-6" } [ props.pageSizeControl ] - -- , H.div { className: "col-md-6" } [ - -- ] - ] - ] - ] - ] - , H.div {className: "col-md-4", style: {marginTop : "6px", marginBottom : "1px"}} - [ H.li {className: "list-group-item"} - [ props.pageSizeDescription - , props.paginationLinks - ] - ] - ] + } = here.component "tableContainer" cpt where + cpt props _ = do + -- | States + -- | + { searchQuery + , termListFilter + , termSizeFilter + } <- T.useLive T.unequal path + + -- | Computed + -- | + let + showAddNewTerm = + ( + (not queryExactMatches || A.null props.tableBody) + && + (searchQuery /= "") + ) + + -- | Render + -- | + pure $ + + H.div + { className: intercalate " " + [ "ngrams-table-container" + , "card" + ] + } + [ + + H.div + { className: intercalate " " + [ "ngrams-table-container__header" + , "card-header" ] - , if (selectionsExist ngramsSelection) - then H.li {className: "list-group-item"} [selectButtons true] - else H.div {} [] - , H.div {id: "terms_table", className: "card-body"} - [ H.table {className: "table able"} - [ H.thead {className: ""} [props.tableHead] - , H.tbody {} props.tableBody + } + [ + -- H.div + -- { className: "col-md-2" + -- , style: { marginTop: "6px" } + -- } + -- [ + -- H.li + -- { className: "list-group-item" } + -- syncResetButton + -- , + -- -- , if (not $ Set.member (normNgram tabNgramType searchQuery) ngramsSelection) && searchQuery /= "" then + -- + -- ] + -- , + + H.div + { className: intercalate " " + [ "ngrams-table-container__header__item" + , "card" + ] + } + syncResetButton + , + H.div + { className: intercalate " " + [ "ngrams-table-container__header__item" + , "card" ] - , H.li {className: "list-group-item"} - [ H.div { className: "row" } - [ H.div { className: "col-md-4" } - [selectButtons (selectionsExist ngramsSelection)] - , H.div {className: "col-md-4 col-md-offset-4"} - [props.paginationLinks] - ] + } + [ + R2.select + { id: "picklistmenu" + , className: "form-control custom-select" + , defaultValue: (maybe "" show termListFilter) + , on: {change: setTermListFilter <<< read <<< R.unsafeEventValue} + } + (map optps1 termLists) + ] + , + H.div + { className: intercalate " " + [ "ngrams-table-container__header__item" + , "card" ] + } + [ + R2.select + { id: "picktermtype" + , className: "form-control custom-select" + , defaultValue: (maybe "" show termSizeFilter) + , on: {change: setTermSizeFilter <<< read <<< R.unsafeEventValue} + } + (map optps1 termSizes) + ] + , + H.div + { className: intercalate " " + [ "ngrams-table-container__header__item" + , "card" + ] + } + [ + B.wad + [ "d-flex", "align-items-center" ] + [ + props.pageSizeControl + , + B.wad_ [ "mr-2", "d-inline-block" ] + , + B.label_ "items" + -- H.div { className: "col-md-6" } [ props.pageSizeControl ] + -- , H.div { className: "col-md-6" } [ + -- ] + ] + ] + , + H.div + { className: intercalate " " + [ "ngrams-table-container__header__item" + , "card" + , "flex-grow-1" + ] + } + [ + props.pageSizeDescription + , + props.paginationLinks + ] + ] + , + R2.when (selectionsExist ngramsSelection) $ + + H.li + { className: "card" } + [ + selectButtons true + ] + , + H.div + { id: "terms_table" + , className: "card-body" + } + [ + R2.when showAddNewTerm $ + + H.div + { className: "ngrams-table-container__add-term" } + [ + B.button + { variant: ButtonVariant Light + , callback: const $ addCallback searchQuery + } + [ + B.icon + { name: "circle" + , className: "mr-1 graph-term" + } + , + H.text "Add" + , + H.text $ nbsp 1 + , + B.b_ $ "« " <> searchQuery <> " »" + , + H.text $ nbsp 1 + , + H.text "to Map terms" + ] + ] + , + H.table + { className: "table able" } + [ + H.thead + {} + [ + props.tableHead + ] + , + H.tbody + {} + props.tableBody + ] + , + H.li + { className: intercalate " " + [ "ngrams-table-container__footer" + , "card" + ] + } + [ + H.div + { className: "ngrams-table-container__footer__item" } + [ + selectButtons (selectionsExist ngramsSelection) + ] + , + H.div + { className: "ngrams-table-container__footer__item" } + [ + props.paginationLinks ] ] ] ] - -- WHY setPath f = origSetPageParams (const $ f path) - setTermListFilter x = T.modify (_ { termListFilter = x }) path - setTermSizeFilter x = T.modify (_ { termSizeFilter = x }) path - setSelection term = dispatch $ setTermListSetA ngramsTableCache ngramsSelection term - - selectionsExist :: Set NgramsTerm -> Boolean - selectionsExist = not <<< Set.isEmpty - - selectButtons false = H.div {} [] - selectButtons true = - H.div {} [ - H.button { className: "btn btn-primary" - , on: { click: const $ setSelection MapTerm } - } [ H.text "Map" ] - , H.button { className: "btn btn-primary" - , on: { click: const $ setSelection StopTerm } - } [ H.text "Stop" ] - , H.button { className: "btn btn-primary" - , on: { click: const $ setSelection CandidateTerm } - } [ H.text "Candidate" ] - ] + + -- WHY setPath f = origSetPageParams (const $ f path) + setTermListFilter x = T.modify (_ { termListFilter = x }) path + setTermSizeFilter x = T.modify (_ { termSizeFilter = x }) path + setSelection term = dispatch $ setTermListSetA ngramsTableCache ngramsSelection term + + selectionsExist :: Set NgramsTerm -> Boolean + selectionsExist = not <<< Set.isEmpty + + selectButtons false = H.div {} [] + selectButtons true = + H.div {} [ + H.button { className: "btn btn-primary" + , on: { click: const $ setSelection MapTerm } + } [ H.text "Map" ] + , H.button { className: "btn btn-primary" + , on: { click: const $ setSelection StopTerm } + } [ H.text "Stop" ] + , H.button { className: "btn btn-primary" + , on: { click: const $ setSelection CandidateTerm } + } [ H.text "Candidate" ] + ] -- NEXT @@ -280,14 +402,30 @@ loadedNgramsTableHeader :: R2.Component LoadedNgramsTableHeaderProps loadedNgramsTableHeader = R.createElement loadedNgramsTableHeaderCpt loadedNgramsTableHeaderCpt :: R.Component LoadedNgramsTableHeaderProps loadedNgramsTableHeaderCpt = here.component "loadedNgramsTableHeader" cpt where - cpt { searchQuery } _ = do - pure $ R.fragment - [ H.h4 { style: { textAlign : "center" } } - [ H.span { className: "fa fa-hand-o-down" } [] - , H.text "Extracted Terms" ] - , NTS.searchInput { key: "search-input" - , searchQuery } + cpt { searchQuery } _ = pure $ + + R.fragment + [ + H.div + { className: "loaded-ngrams-table-header" } + [ + B.icon + { name: "hand-o-down" + , className: "loaded-ngrams-table-header__icon" + } + , + B.wad_ [ "mr-1", "d-inline-block" ] + , + B.span' + { className: "loaded-ngrams-table-header__text" } $ + "Extracted Terms" ] + , + NTS.searchInput + { key: "search-input" + , searchQuery + } + ] loadedNgramsTableBody :: R2.Component PropsNoReload loadedNgramsTableBody = R.createElement loadedNgramsTableBodyCpt @@ -441,7 +579,12 @@ loadedNgramsTableBodyCpt = here.component "loadedNgramsTableBody" cpt where , wrapColElts: wrapColElts { allNgramsSelected, dispatch: performAction, ngramsSelection } scoreType } - , syncResetButton + , + B.wad + [ "mt-2", "d-inline-block" ] + [ + syncResetButton + ] ] where colNames = TT.ColumnName <$> ["Show", "Select", "Map", "Stop", "Terms", "Score"] -- see convOrderBy @@ -576,7 +719,7 @@ type MainNgramsTableProps = ( getNgramsChildrenAff :: Session -> NodeID -> Array ListId -> TabType -> NgramsTerm -> Aff (Array NgramsTerm) getNgramsChildrenAff session nodeId listIds tabType (NormNgramsTerm ngrams) = do - res :: Either RESTError ({ data :: Array { children :: Array String, ngrams :: String }}) <- get session $ R.GetNgrams params (Just nodeId) + res :: Either RESTError ({ data :: Array { children :: Array String, ngrams :: String }}) <- get session $ Routes.GetNgrams params (Just nodeId) case res of Left err -> pure [] Right { data: lst } -> case A.uncons (A.filter (\d -> d.ngrams == ngrams) lst) of @@ -613,7 +756,7 @@ mainNgramsTableCpt = here.component "mainNgramsTable" cpt -- , getNgramsChildren: getNgramsChildrenAff session nodeId' tabType -- , onCancelRef -- , onNgramsClickRef - -- , onSaveRef + -- , onSaveRef -- } -- let path = initialPageParams session nodeId [defaultListId] tabType @@ -650,11 +793,14 @@ ngramsTreeEditCpt = here.component "ngramsTreeEdit" cpt where ngramsParentFocused <- T.useFocused (_.ngramsParent) (\a b -> b { ngramsParent = a}) box ngramsParentFocused' <- T.useLive T.unequal ngramsParentFocused + let + gutter = B.wad_ [ "mb-2", "d-inline-block" ] + pure $ if isEditingFocused' then case ngramsParentFocused' of - Nothing -> H.div {} [] + Nothing -> gutter Just ngramsParent' -> ngramsTreeEditReal (Record.merge props { ngramsParent' }) [] - else H.div {} [] + else gutter type NgramsTreeEditRealProps = ( ngramsParent' :: NgramsTerm @@ -669,40 +815,91 @@ ngramsTreeEditRealCpt = here.component "ngramsTreeEditReal" cpt where , ngramsParent' , onCancelRef , onNgramsClickRef - , onSaveRef } _ = do - { ngramsChildren, ngramsChildrenDiff } <- T.useLive T.unequal box - - let ngramsDepth = { depth: 0, ngrams: ngramsParent' } - ngramsChildrenPatched :: Set NgramsTerm - ngramsChildrenPatched = applyPatchSet (patchSetFromMap ngramsChildrenDiff) $ Set.fromFoldable ngramsChildren - -- A patched version of getNgramsChildren. This is used - -- because we're editing the tree and so won't fetch the API - -- ngrams children. - gnc ngrams = if ngrams == ngramsParent' - then do - pure $ A.fromFoldable ngramsChildrenPatched - else do - pure [] - - pure $ H.div {} - [ H.p {} - [ H.text $ "Editing " <> ngramsTermText ngramsDepth.ngrams ] - , renderNgramsTree { getNgramsChildren: gnc - , ngramsClick - , ngramsDepth - , ngramsEdit - , ngramsStyle: [] - , key: show ngramsParent' - <> "-" <> show ngramsChildren - <> "-" <> show ngramsChildrenDiff - } - , H.button { className: "btn btn-primary" - , on: { click: onSaveClick } --(const $ dispatch AddTermChildren)} - } [ H.text "Save" ] - , H.button { className: "btn btn-primary" - , on: { click: onCancelClick } --(const $ dispatch ClearTreeEdit)} - } [ H.text "Cancel" ] + , onSaveRef + } _ = do + -- | States + -- | + { ngramsChildren + , ngramsChildrenDiff + } <- T.useLive T.unequal box + + -- | Computed + -- | + let + ngramsDepth = { depth: 0, ngrams: ngramsParent' } + + ngramsChildrenPatched :: Set NgramsTerm + ngramsChildrenPatched = applyPatchSet (patchSetFromMap ngramsChildrenDiff) $ Set.fromFoldable ngramsChildren + -- A patched version of getNgramsChildren. This is used + -- because we're editing the tree and so won't fetch the API + -- ngrams children. + gnc ngrams = + if ngrams == ngramsParent' + then do + pure $ A.fromFoldable ngramsChildrenPatched + else do + pure [] + + + -- | Render + -- | + pure $ + + H.div + { className: intercalate " " + [ "ngrams-tree-edit-real" + , "card" + ] + } + [ + H.div + { className: "card-header" } + [ + B.icon + { name: "pencil-square-o" + } + , + B.wad_ + [ "mr-1", "d-inline-block" ] + , + B.b_ $ ngramsTermText ngramsDepth.ngrams + ] + , + H.div + { className: "card-body" } + [ + renderNgramsTree + { getNgramsChildren: gnc + , ngramsClick + , ngramsDepth + , ngramsEdit + , ngramsStyle: [] + , key: show ngramsParent' + <> "-" <> show ngramsChildren + <> "-" <> show ngramsChildrenDiff + } + , + H.div + { className: "ngrams-tree-edit-real__actions" } + [ + B.button + { variant: ButtonVariant Light + , callback: onCancelClick --(const $ dispatch ClearTreeEdit)} + , size: SmallSize + } + [ H.text "Cancel" ] + , + B.button + { variant: ButtonVariant Primary + , callback: onSaveClick --(const $ dispatch AddTermChildren)} + , size: SmallSize + } + [ H.text "Save" ] + ] + ] ] + -- | Helpers + -- | where --ngramsClick {depth: 1, ngrams: child} = Just $ dispatch $ ToggleChild false child --ngramsClick _ = Nothing @@ -712,11 +909,11 @@ ngramsTreeEditRealCpt = here.component "ngramsTreeEditReal" cpt where Just ngc -> ngc nd ngramsEdit :: NgramsClick ngramsEdit _ = Nothing - onCancelClick :: forall e. e -> Effect Unit + onCancelClick :: Unit -> Effect Unit onCancelClick _ = case R.readRef onCancelRef of Nothing -> pure unit Just onCancel -> onCancel unit - onSaveClick :: forall e. e -> Effect Unit + onSaveClick :: Unit -> Effect Unit onSaveClick _ = case R.readRef onSaveRef of Nothing -> pure unit Just onSave -> onSave unit @@ -759,7 +956,7 @@ mainNgramsTableCacheOnCpt = here.component "mainNgramsTableCacheOn" cpt where , renderer: render , spinnerClass: Nothing } - versionEndpoint { defaultListId, path: { nodeId, tabType, session } } _ = get session $ R.GetNgramsTableVersion { listId: defaultListId, tabType } (Just nodeId) + versionEndpoint { defaultListId, path: { nodeId, tabType, session } } _ = get session $ Routes.GetNgramsTableVersion { listId: defaultListId, tabType } (Just nodeId) errorHandler = logRESTError here "[mainNgramsTable]" mkRequest :: PageParams -> GUC.Request mkRequest path@{ session } = GUC.makeGetRequest session $ url path @@ -767,7 +964,7 @@ mainNgramsTableCacheOnCpt = here.component "mainNgramsTableCacheOn" cpt where url { listIds , nodeId , tabType - } = R.GetNgramsTableAll { listIds + } = Routes.GetNgramsTableAll { listIds , tabType } (Just nodeId) handleResponse :: VersionedNgramsTable -> VersionedNgramsTable handleResponse v = v @@ -810,7 +1007,7 @@ mainNgramsTableCacheOffCpt = here.component "mainNgramsTableCacheOff" cpt where , termListFilter , termSizeFilter } = - get session $ R.GetNgrams params (Just nodeId) + get session $ Routes.GetNgrams params (Just nodeId) where params = { limit , listIds diff --git a/src/Gargantext/Components/NgramsTable/Search.purs b/src/Gargantext/Components/NgramsTable/Search.purs index 4d6c0de126c567b71802747795f342827b24a808..7dc1c0db2f3feccd143941b0931adf5b5c1e4d7e 100644 --- a/src/Gargantext/Components/NgramsTable/Search.purs +++ b/src/Gargantext/Components/NgramsTable/Search.purs @@ -1,8 +1,12 @@ module Gargantext.Components.NgramsTable.Search where -import Data.Nullable (Nullable, null) -import DOM.Simple as DOM import Gargantext.Prelude + +import DOM.Simple as DOM +import Data.Foldable (intercalate) +import Data.Nullable (Nullable, null) +import Gargantext.Components.Bootstrap as B +import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) import Gargantext.Utils.Reactix as R2 import Reactix as R import Reactix.DOM.HTML as H @@ -24,7 +28,7 @@ searchInputCpt = here.component "searchInput" cpt where cpt { searchQuery } _ = do inputRef <- R.useRef null - + pure $ R2.row [ H.div { className: "col-12" } [ H.div { className: "input-group" } @@ -46,14 +50,34 @@ searchButtonCpt = here.component "searchButton" cpt where cpt { inputRef, searchQuery } _ = do searchQuery' <- T.useLive T.unequal searchQuery - pure $ H.div { className: "input-group-prepend" } - [ if searchQuery' /= "" + pure $ + + H.div + { className: intercalate " " + [ "search-button-prepend" + , "input-group-prepend" + ] + } + [ + if searchQuery' /= "" then - H.button { className: "btn btn-danger" - , on: { click: \_ -> R2.setInputValue inputRef "" } } - -- T.write "" searchQuery } } - [ H.span {className: "fa fa-times"} []] - else H.span { className: "fa fa-search input-group-text" } [] + B.button + { variant: ButtonVariant Light + , callback: \_ -> R2.setInputValue inputRef "" + -- T.write "" searchQuery + , className: "input-group-text" + } + [ + B.icon + { name: "times" + , className: "text-danger" + } + ] + else + B.icon + { name: "search" + , className: "input-group-text" + } ] type SearchFieldInputProps = @@ -67,7 +91,7 @@ searchFieldInputCpt :: R.Component SearchFieldInputProps searchFieldInputCpt = here.component "searchFieldInput" cpt where cpt { inputRef, searchQuery } _ = do -- searchQuery' <- T.useLive T.unequal searchQuery - + pure $ H.input { className: "form-control" -- , defaultValue: searchQuery' , name: "search" diff --git a/src/Gargantext/Components/NgramsTable/SyncResetButton.purs b/src/Gargantext/Components/NgramsTable/SyncResetButton.purs index f906c015b296bbd5714ffa9b2f45f6eb3d525ff7..a9fa3c04e26b103e338beac5808d6e709cc1baa4 100644 --- a/src/Gargantext/Components/NgramsTable/SyncResetButton.purs +++ b/src/Gargantext/Components/NgramsTable/SyncResetButton.purs @@ -1,10 +1,13 @@ module Gargantext.Components.NgramsTable.SyncResetButton where +import Gargantext.Prelude + import Effect.Aff (Aff) import Effect.Class (liftEffect) import FFI.Simple.Functions (delay) +import Gargantext.Components.Bootstrap as B +import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Variant(..)) import Gargantext.Core.NgramsTable.Types (CoreAction(..), CoreDispatch, NgramsTablePatch) -import Gargantext.Prelude import Gargantext.Utils.Reactix as R2 import Reactix as R import Reactix.DOM.HTML as H @@ -32,9 +35,14 @@ syncResetButtonsCpt = here.component "syncResetButtons" cpt let hasChanges = ngramsLocalPatch /= mempty - hasChangesClass = if hasChanges then "" else " disabled" - synchronizingClass = if synchronizing' then " disabled" else "" + statusReset _ true = Disabled + statusReset false _ = Disabled + statusReset _ _ = Enabled + + statusSync _ true = Deferred + statusSync false _ = Disabled + statusSync _ _ = Enabled resetClick _ = do performAction ResetPatches @@ -47,16 +55,24 @@ syncResetButtonsCpt = here.component "syncResetButtons" cpt afterSync x liftEffect $ T.write_ false synchronizing - pure $ H.div { className: "btn-toolbar" } - [ H.div { className: "btn-group mr-2" } - [ H.button { className: "btn btn-danger " <> hasChangesClass <> synchronizingClass - , on: { click: resetClick } - } [ H.text "Reset" ] - ] - , H.div { className: "btn-group mr-2" } - [ H.button { className: "btn btn-primary " <> hasChangesClass <> synchronizingClass - , on: { click: synchronizeClick } - } [ H.text "Sync" ] - ] - ] + pure $ + B.wad + [ "d-flex" ] + [ + B.button + { variant: ButtonVariant Light + , callback: resetClick + , status: statusReset hasChanges synchronizing' + } + [ H.text "Reset" ] + , + B.wad_ [ "mr-1", "d-inline-block" ] + , + B.button + { variant: ButtonVariant Primary + , callback: synchronizeClick + , status: statusSync hasChanges synchronizing' + } + [ H.text "Sync" ] + ] diff --git a/src/Gargantext/Components/NgramsTable/Tree.purs b/src/Gargantext/Components/NgramsTable/Tree.purs index f5fb3f7f416ef08641557534f33969e97bf838d9..c7ce87bdcb972e8820c2a3bc38976c350bb5d668 100644 --- a/src/Gargantext/Components/NgramsTable/Tree.purs +++ b/src/Gargantext/Components/NgramsTable/Tree.purs @@ -1,30 +1,29 @@ module Gargantext.Components.NgramsTable.Tree where +import Gargantext.Prelude + import Data.Array as A import Data.Either (Either(..)) import Data.Lens ((^..), (^.), view) -import Data.Lens.At (at) import Data.Lens.Fold (folded) import Data.Lens.Index (ix) -import Data.List (List) +import Data.List (List, intercalate) import Data.List as L -import Data.Map (Map) -import Data.Map as Map -import Data.Maybe (Maybe(..), maybe, isJust) -import Data.Nullable (Nullable, null, toMaybe) +import Data.Maybe (Maybe(..), maybe) import Data.Set (Set) import Data.Set as Set -import DOM.Simple as DOM import Effect (Effect) -import Effect.Aff (Aff, launchAff_) -import Effect.Class (liftEffect) -import Gargantext.Core.NgramsTable.Functions (applyNgramsPatches, setTermListA, tablePatchHasNgrams) -import Gargantext.Core.NgramsTable.Types (Action(..), NgramsClick, NgramsDepth, NgramsElement, NgramsTable, NgramsTablePatch(..), NgramsTerm, _NgramsElement, _NgramsRepoElement, _PatchMap, _children, _list, _ngrams, _occurrences, ngramsTermText, replace) +import Effect.Aff (Aff) +import Gargantext.Components.Bootstrap as B +import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..)) import Gargantext.Components.Table as Tbl import Gargantext.Config.REST (logRESTError) +import Gargantext.Core.NgramsTable.Functions (applyNgramsPatches, setTermListA, tablePatchHasNgrams) +import Gargantext.Core.NgramsTable.Types (Action(..), NgramsClick, NgramsDepth, NgramsElement, NgramsTable, NgramsTablePatch, NgramsTerm, _NgramsElement, _NgramsRepoElement, _children, _list, _ngrams, _occurrences, ngramsTermText, replace) import Gargantext.Hooks.Loader (useLoader) -import Gargantext.Prelude (Unit, bind, const, discard, map, mempty, not, otherwise, pure, show, unit, ($), (+), (/=), (<<<), (<>), (==), (>), (||)) +import Gargantext.Prelude (Unit, bind, const, map, mempty, not, otherwise, pure, show, unit, ($), (+), (<<<), (<>), (==), (>), (||)) import Gargantext.Types as GT +import Gargantext.Utils ((?)) import Gargantext.Utils.Reactix as R2 import React.DOM (a, span, text) import React.DOM.Props as DOM @@ -55,7 +54,9 @@ renderNgramsTreeCpt :: R.Component RenderNgramsTree renderNgramsTreeCpt = here.component "renderNgramsTree" cpt where cpt { getNgramsChildren, ngramsClick, ngramsDepth, ngramsEdit, ngramsStyle } _ = do - pure $ H.ul {} + pure $ + H.ul + { className: "render-ngrams-tree" } [ H.span { className: "tree" } [ H.span { className: "righthanded" } [ tree { getNgramsChildren @@ -118,13 +119,37 @@ treeLoaded :: Record TreeLoaded -> R.Element treeLoaded p = R.createElement treeLoadedCpt p [] treeLoadedCpt :: R.Component TreeLoaded treeLoadedCpt = here.component "treeLoaded" cpt where - cpt params@{ ngramsChildren, ngramsClick, ngramsDepth, ngramsEdit, ngramsStyle } _ = do + cpt params@{ ngramsChildren + , ngramsClick + , ngramsDepth + , ngramsEdit + , ngramsStyle + } _ = do pure $ - H.li { style: { width : "100%" } } - ([ H.i { className, style } [] ] - <> [ R2.buff $ tag [ text $ " " <> ngramsTermText ngramsDepth.ngrams ] ] - <> maybe [] edit (ngramsEdit ngramsDepth) - <> [ forest ngramsChildren ] + + H.li + -- { className: "ngrams-tree-loaded-node" } + { className: intercalate " " + [ "ngrams-tree-loaded-node" + , ngramsDepth.depth == 1 ? + "ngrams-tree-loaded-node--first-child" $ + "" + , ngramsDepth.depth > 1 ? + "ngrams-tree-loaded-node--grand-child" $ + "" + ] + } + ( + -- @NOTE #414: currently commenting this, as the below icon is not + -- a call-to-action, thus deceiving the user of possible + -- yet-to-become reveal/collapse node children feature + -- [ H.i { className, style } [] ] + -- <> + [ R2.buff $ tag [ text $ " " <> ngramsTermText ngramsDepth.ngrams ] ] + <> + maybe [] edit (ngramsEdit ngramsDepth) + <> + [ forest ngramsChildren ] ) where tag = @@ -133,10 +158,16 @@ treeLoadedCpt = here.component "treeLoaded" cpt where a (ngramsStyle <> [DOM.onClick $ const effect]) Nothing -> span ngramsStyle - edit effect = [ H.text " " - , H.i { className: "fa fa-pencil" - , on: { click: const effect } } [] - ] + edit effect = + [ + B.iconButton + { name: "pencil" + , className: "ml-1" + , variant: Secondary + , callback: const effect + , overlay: false + } + ] leaf = L.null ngramsChildren className = "fa fa-chevron-" <> if open then "down" else "right" style = if leaf then {color: "#adb5bd"} else {color: ""} @@ -177,29 +208,55 @@ renderNgramsItemCpt = here.component "renderNgramsItem" cpt , ngramsTable } _ = do isEditing' <- T.useLive T.unequal isEditing - + pure $ Tbl.makeRow - [ H.div { className: "ngrams-selector" } - [ H.span { className: "ngrams-chooser fa fa-eye-slash" - , on: { click: onClick } } [] + [ + H.div + { className: "text-center" + , style: { marginTop: "6px" } + } + [ + B.iconButton + { name: "eye-slash" + , status: Disabled -- see `onClick` behavior + , callback: onClick + , className: "" + } ] , selected , checkbox GT.MapTerm , checkbox GT.StopTerm , H.div {} ( if isEditing' - then [ H.a { on: { click: const $ dispatch $ ToggleChild true ngrams } } - [ H.i { className: "fa fa-plus" } [] ] - , R2.buff $ tag [ text $ " " <> ngramsTermText ngramsDepth.ngrams ] - ] - else [ renderNgramsTree { getNgramsChildren: getNgramsChildren' - , ngramsClick - , ngramsDepth - , ngramsEdit - , ngramsStyle - , key: "" } ] + then + [ + B.iconButton + { name: "plus" + , className: "mr-1 align-bottom" + , overlay: false + , variant: Primary + , callback: const $ dispatch $ ToggleChild true ngrams + } + , + R2.buff $ + tag [ text $ " " <> ngramsTermText ngramsDepth.ngrams ] + ] + else + [ + renderNgramsTree + { getNgramsChildren: getNgramsChildren' + , ngramsClick + , ngramsDepth + , ngramsEdit + , ngramsStyle + , key: "" + } + ] ) - , H.text $ show (ngramsElement ^. _NgramsElement <<< _occurrences) + , + B.wad' + [ "pl-3" ] $ + show (ngramsElement ^. _NgramsElement <<< _occurrences) ] where ngramsDepth = { ngrams, depth: 0 } @@ -232,21 +289,42 @@ renderNgramsItemCpt = here.component "renderNgramsItem" cpt -- | ngramsTransient = const Nothing -- | otherwise = Just <<< dispatch <<< cycleTermListItem <<< view _ngrams selected = - H.input { checked: Set.member ngrams ngramsSelection - , className: "checkbox" - , on: { change: const $ dispatch $ ToggleSelect ngrams } - , type: "checkbox" - } + B.wad + [ "text-center" ] + [ + H.input + { checked: Set.member ngrams ngramsSelection + , className: "checkbox" + , on: { change: const $ dispatch $ ToggleSelect ngrams } + , type: "checkbox" + , style: + { cursor: "pointer" + , marginTop: "6px" + } + } + ] + checkbox termList' = let chkd = termList == termList' termList'' = if chkd then GT.CandidateTerm else termList' in - H.input { checked: chkd - , className: "checkbox" - , on: { change: const $ dispatch $ CoreAction $ - setTermListA ngrams (replace termList termList'') } - , readOnly: ngramsTransient - , type: "checkbox" } + B.wad + [ "text-center" ] + [ + H.input + { checked: chkd + , className: "checkbox" + , on: { change: const $ dispatch $ CoreAction $ + setTermListA ngrams (replace termList termList'') } + , readOnly: ngramsTransient + , type: "checkbox" + , style: + { cursor: "pointer" + , marginTop: "6px" + } + } + ] + ngramsTransient = tablePatchHasNgrams ngramsLocalPatch ngrams -- ^ TODO here we do not look at ngramsNewElems, shall we? ngramsOpacity diff --git a/src/Gargantext/Components/Table.purs b/src/Gargantext/Components/Table.purs index 7ca76cb9f6983a3b01a7a213377f78650cc8994b..867b65d620880b1fe749bbd3236a59d47cf9c15c 100644 --- a/src/Gargantext/Components/Table.purs +++ b/src/Gargantext/Components/Table.purs @@ -306,6 +306,8 @@ tableCpt = here.component "table" cpt makeRow :: Array R.Element -> R.Element makeRow els = H.tr {} $ (\c -> H.td {} [c]) <$> els +makeRow' :: forall r. Record r -> Array R.Element -> R.Element +makeRow' p els = H.tr p $ (\c -> H.td {} [c]) <$> els type FilterRowsParams = ( @@ -320,7 +322,8 @@ filterRows { params: { limit, offset } } rs = newRs defaultContainer :: Record TableContainerProps -> R.Element defaultContainer props = R.fragment $ props.syncResetButton <> controls where - controls = [ R2.row + controls = [ H.div + { className: "d-flex align-items-center mb-2" } [ H.div {className: "col-md-4"} [ props.pageSizeDescription ] , H.div {className: "col-md-4"} [ props.paginationLinks ] , H.div {className: "col-md-4"} [ props.pageSizeControl ] @@ -374,7 +377,7 @@ sizeDDCpt = here.component "sizeDD" cpt textDescription :: Int -> PageSizes -> Int -> R.Element textDescription currPage pageSize totalRecords = - H.div {className: "row1"} [ H.div {className: ""} [ H.text msg ] ] -- TODO or col-md-6 ? + H.div {className: ""} [ H.text msg ] -- TODO or col-md-6 ? where start = (currPage - 1) * pageSizes2Int pageSize + 1 end' = currPage * pageSizes2Int pageSize diff --git a/src/sass/_legacy.sass b/src/sass/_legacy.sass index db7b67396c53cdaba54e9be9d40fd6b74efac9fb..e7cd9238545adffb0aeb9d073c6d4ae215508ae2 100644 --- a/src/sass/_legacy.sass +++ b/src/sass/_legacy.sass @@ -4,6 +4,7 @@ @import "./_legacy/_tree" @import "./_legacy/_code_editor" @import "./_legacy/_styles" +@import "./_legacy/_list" @import "./_legacy/_annotation" @import "./_legacy/_folder_view" @import "./_legacy/_phylo" diff --git a/src/sass/_legacy/_list.sass b/src/sass/_legacy/_list.sass new file mode 100644 index 0000000000000000000000000000000000000000..04edfbc76940784a988f86fed06acf0072949fb2 --- /dev/null +++ b/src/sass/_legacy/_list.sass @@ -0,0 +1,156 @@ +.table + $row-min-height: 48px + + tr td + height: $row-min-height + + /////////////////////// + + .page-paint-raw + + &--selected + position: relative + + td:first-child::before + @include fit-positions + + content: "" + width: 3px + background-color: $info + position: absolute + + /////////////////////// + + .page-paint-row + + &--active + // font-weight: bold + + &--trash + text-decoration: line-through + + /////////////////////// + + .doc-chooser + $offset-top: 3px // flex alignment won't work, hence empirical value + + padding-top: $offset-top + text-align: center + + /////////////////////// + + .rating-group + $offset-top: 3px // flex alignment won't work, hence empirical value + + display: flex + padding-top: $offset-top + + &__action + // @XXX Glyphicon icons lack of homogeneous width + width: 14px + margin-right: space-x(1) + +///////////////////////////////////////////////////: + +.search-button-prepend + + .input-group-text + // @XXX Glyphicon icons lack of homogeneous width + width: 41px + + // @XXX Bootstrap adding unwanted `z-index` on "_input-group.scss" + z-index: initial + +///////////////////////////////////////////////////: + +.ngrams-table-container + + &__header + display: flex + align-items: flex-start + + &__item + padding: $card-spacer-y $card-spacer-x + + &:not(:first-child) + margin-left: space-x(1) + &:not(:last-child) + margin-right: space-x(1) + + &__add-term + // struggling to create harmonious vertical gutters due to Bootstrap adding + // helper classes surrounding the element + $offset-top: -8px + $offset-bottom: 12px + + margin-top: $offset-top + margin-bottom: $offset-bottom + + &__footer + padding: $card-spacer-y $card-spacer-x + + &__item + text-align: center + +///////////////////////////////////////////////////: + +.ngrams-tree-edit-real + // empirical value fitting the best size of the render tree + $min-with: 416px + + min-width: $min-with + width: fit-content // growing purpose + border-color: $primary + margin-top: space-x(2) + margin-bottom: space-x(2) + + + &__actions + display: flex + margin-top: space-x(2) + + .b-button + margin-right: space-x(1) + +////////////////////////////////////////////////// + +.loaded-ngrams-table-header + text-align: center + margin-top: space-x(1.5) + margin-bottom: space-x(1.5) + + &__icon + font-size: 16px + + &__text + font-size: 22px + font-weight: bold + font-family: $headings-font-family + +////////////////////////////////////////////////// + +.ngrams-tree-loaded-node + + + &--first-child::before, + &--grand-child::before + color: $gray-400 + font-size: 11px + margin-right: space-x(0.5) + + @include right-handed + content: "└" + @include left-handed + content: "┘" + + &--first-child + // empirical value where the child separator aligns with its parent text + $child-offset: -2px + + margin-left: -2px + + &--grand-child + // empirical value where the child separator aligns with its parent text + $child-offset: 13px + + margin-left: 13px diff --git a/src/sass/_legacy/_styles.sass b/src/sass/_legacy/_styles.sass index ef6c459dfbfd19d13a85c4c0f715610f8611dc3b..95d0af2907de44f45b46538218c2da8af5605f28 100644 --- a/src/sass/_legacy/_styles.sass +++ b/src/sass/_legacy/_styles.sass @@ -63,25 +63,6 @@ left: 50% top: 50% -.table - tr - td - color: #005a9aff - .active - font-weight: bold - text-decoration: underline - .ngrams-selector - display: flex - .ngrams-chooser - padding: 3px - .trash - text-decoration: line-through - -.action-search - margin: 10px - -.search-bar - margin: 10px /* */ @@ -93,7 +74,6 @@ select.form-control cursor: pointer - #app width: 100%