diff --git a/dist/styles/bootstrap-darkster.css b/dist/styles/bootstrap-darkster.css index 5fe7f3bad7ac25fc2bc2ed88a5a16e1b64222ab3..207b0a406c3cb38dcc05e91a49e0c17b9d2c6d12 100644 --- a/dist/styles/bootstrap-darkster.css +++ b/dist/styles/bootstrap-darkster.css @@ -7864,14 +7864,15 @@ input[type=range]:-moz-focusring { background-color: #121212; } .graph-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; content: ""; position: absolute; z-index: 1; width: 2px; background-color: #0F81C7; - left: 0; - top: 0; - bottom: 0; } .graph-doc-list__item--selected:first-child::before { border-top-left-radius: 0.25rem; @@ -9280,6 +9281,20 @@ select.form-control { border-right: 1px solid #dee2e6; } +.phylo__focus { + flex-grow: 1; + pointer-events: all; + position: relative; +} +.phylo__focus__inner { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-color: #000000; +} + .phylo-isoline { display: flex; position: relative; @@ -9443,7 +9458,7 @@ select.form-control { font-weight: bold; } .phylo-selection-tab__selection { - margin: 16px 20px 0; + margin: 16px 20px; } .phylo-selection-tab__selection__item { white-space: normal; @@ -9475,6 +9490,55 @@ select.form-control { color: #ADB5BD; text-align: center; } +.phylo-selection-tab__extracted-docs { + margin: 16px 20px; +} + +.phylo-doc-list__item { + cursor: pointer; + display: flex; + align-items: flex-start; + transition: all 0.2s ease-in-out; +} +.phylo-doc-list__item:focus { + outline: 0; +} +.phylo-doc-list__item:hover { + background-color: #121212; +} +.phylo-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: #0F81C7; +} +.phylo-doc-list__item--selected:first-child::before { + border-top-left-radius: 0.25rem; +} +.phylo-doc-list__item--selected:last-child::before { + border-bottom-left-radius: 0.25rem; +} +.phylo-doc-list__item__main { + flex-grow: 1; + padding-right: 1.25rem; +} +.phylo-doc-list__item__title, .phylo-doc-list__item__source, .phylo-doc-list__item__date { + line-height: 1.3; + margin-bottom: 4px; +} +.phylo-doc-list__item__source { + font-size: 15px; + color: #DEE2E6; +} +.phylo-doc-list__item__date { + font-size: 14px; + color: #CED4DA; +} .phylo-toolbar { display: flex; diff --git a/dist/styles/bootstrap-default.css b/dist/styles/bootstrap-default.css index f9c5b56d583cd6d8555621167d4a13c6694fe8c3..66b6f849284ad04da96c83bd9e82388c725c7ff3 100644 --- a/dist/styles/bootstrap-default.css +++ b/dist/styles/bootstrap-default.css @@ -7817,14 +7817,15 @@ input[type=range]:-moz-focusring { background-color: #FCFCFC; } .graph-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; content: ""; position: absolute; z-index: 1; width: 2px; background-color: #17a2b8; - left: 0; - top: 0; - bottom: 0; } .graph-doc-list__item--selected:first-child::before { border-top-left-radius: 0.25rem; @@ -9233,6 +9234,20 @@ select.form-control { border-right: 1px solid #dee2e6; } +.phylo__focus { + flex-grow: 1; + pointer-events: all; + position: relative; +} +.phylo__focus__inner { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-color: #fff; +} + .phylo-isoline { display: flex; position: relative; @@ -9396,7 +9411,7 @@ select.form-control { font-weight: bold; } .phylo-selection-tab__selection { - margin: 16px 20px 0; + margin: 16px 20px; } .phylo-selection-tab__selection__item { white-space: normal; @@ -9428,6 +9443,55 @@ select.form-control { color: #ADB5BD; text-align: center; } +.phylo-selection-tab__extracted-docs { + margin: 16px 20px; +} + +.phylo-doc-list__item { + cursor: pointer; + display: flex; + align-items: flex-start; + transition: all 0.2s ease-in-out; +} +.phylo-doc-list__item:focus { + outline: 0; +} +.phylo-doc-list__item:hover { + background-color: #FCFCFC; +} +.phylo-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: #17a2b8; +} +.phylo-doc-list__item--selected:first-child::before { + border-top-left-radius: 0.25rem; +} +.phylo-doc-list__item--selected:last-child::before { + border-bottom-left-radius: 0.25rem; +} +.phylo-doc-list__item__main { + flex-grow: 1; + padding-right: 1.25rem; +} +.phylo-doc-list__item__title, .phylo-doc-list__item__source, .phylo-doc-list__item__date { + line-height: 1.3; + margin-bottom: 4px; +} +.phylo-doc-list__item__source { + font-size: 15px; + color: #495057; +} +.phylo-doc-list__item__date { + font-size: 14px; + color: #6C757D; +} .phylo-toolbar { display: flex; diff --git a/dist/styles/bootstrap-greyson.css b/dist/styles/bootstrap-greyson.css index 1c2a217c3d941da34ad569cd98272ec7ca97f294..7cd7f7a0097366cc59602479e3342ebf4efea7e6 100644 --- a/dist/styles/bootstrap-greyson.css +++ b/dist/styles/bootstrap-greyson.css @@ -7573,14 +7573,15 @@ input[type=range]:-moz-focusring { background-color: #FCFCFC; } .graph-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; content: ""; position: absolute; z-index: 1; width: 2px; background-color: #5c8f94; - left: 0; - top: 0; - bottom: 0; } .graph-doc-list__item--selected:first-child::before { border-top-left-radius: 0.25rem; @@ -8989,6 +8990,20 @@ select.form-control { border-right: 1px solid #dee2e6; } +.phylo__focus { + flex-grow: 1; + pointer-events: all; + position: relative; +} +.phylo__focus__inner { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-color: #fff; +} + .phylo-isoline { display: flex; position: relative; @@ -9152,7 +9167,7 @@ select.form-control { font-weight: bold; } .phylo-selection-tab__selection { - margin: 16px 20px 0; + margin: 16px 20px; } .phylo-selection-tab__selection__item { white-space: normal; @@ -9184,6 +9199,55 @@ select.form-control { color: #ADB5BD; text-align: center; } +.phylo-selection-tab__extracted-docs { + margin: 16px 20px; +} + +.phylo-doc-list__item { + cursor: pointer; + display: flex; + align-items: flex-start; + transition: all 0.2s ease-in-out; +} +.phylo-doc-list__item:focus { + outline: 0; +} +.phylo-doc-list__item:hover { + background-color: #FCFCFC; +} +.phylo-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: #5c8f94; +} +.phylo-doc-list__item--selected:first-child::before { + border-top-left-radius: 0.25rem; +} +.phylo-doc-list__item--selected:last-child::before { + border-bottom-left-radius: 0.25rem; +} +.phylo-doc-list__item__main { + flex-grow: 1; + padding-right: 1.25rem; +} +.phylo-doc-list__item__title, .phylo-doc-list__item__source, .phylo-doc-list__item__date { + line-height: 1.3; + margin-bottom: 4px; +} +.phylo-doc-list__item__source { + font-size: 15px; + color: #495057; +} +.phylo-doc-list__item__date { + font-size: 14px; + color: #6C757D; +} .phylo-toolbar { display: flex; diff --git a/dist/styles/bootstrap-herbie.css b/dist/styles/bootstrap-herbie.css index f9a7dd64a01aebbe8dd9c050f03397ed7c7da6f2..f8b659e8979f7321f87494481468c3a534cc1a1f 100644 --- a/dist/styles/bootstrap-herbie.css +++ b/dist/styles/bootstrap-herbie.css @@ -7821,14 +7821,15 @@ input[type=range]:-moz-focusring { background-color: #FCFCFC; } .graph-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; content: ""; position: absolute; z-index: 1; width: 2px; background-color: #74DBEF; - left: 0; - top: 0; - bottom: 0; } .graph-doc-list__item--selected:first-child::before { border-top-left-radius: 0.25rem; @@ -9237,6 +9238,20 @@ select.form-control { border-right: 1px solid #dee2e6; } +.phylo__focus { + flex-grow: 1; + pointer-events: all; + position: relative; +} +.phylo__focus__inner { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-color: #fff; +} + .phylo-isoline { display: flex; position: relative; @@ -9400,7 +9415,7 @@ select.form-control { font-weight: bold; } .phylo-selection-tab__selection { - margin: 16px 20px 0; + margin: 16px 20px; } .phylo-selection-tab__selection__item { white-space: normal; @@ -9432,6 +9447,55 @@ select.form-control { color: #ADB5BD; text-align: center; } +.phylo-selection-tab__extracted-docs { + margin: 16px 20px; +} + +.phylo-doc-list__item { + cursor: pointer; + display: flex; + align-items: flex-start; + transition: all 0.2s ease-in-out; +} +.phylo-doc-list__item:focus { + outline: 0; +} +.phylo-doc-list__item:hover { + background-color: #FCFCFC; +} +.phylo-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: #74DBEF; +} +.phylo-doc-list__item--selected:first-child::before { + border-top-left-radius: 0.25rem; +} +.phylo-doc-list__item--selected:last-child::before { + border-bottom-left-radius: 0.25rem; +} +.phylo-doc-list__item__main { + flex-grow: 1; + padding-right: 1.25rem; +} +.phylo-doc-list__item__title, .phylo-doc-list__item__source, .phylo-doc-list__item__date { + line-height: 1.3; + margin-bottom: 4px; +} +.phylo-doc-list__item__source { + font-size: 15px; + color: #495057; +} +.phylo-doc-list__item__date { + font-size: 14px; + color: #6C757D; +} .phylo-toolbar { display: flex; diff --git a/dist/styles/bootstrap-monotony.css b/dist/styles/bootstrap-monotony.css index 6f26fc301e96c22ed9c661ba294f688af1ad3dfe..9031f03534d7ebf24403f6958dc6d8957ba552e8 100644 --- a/dist/styles/bootstrap-monotony.css +++ b/dist/styles/bootstrap-monotony.css @@ -7822,14 +7822,15 @@ input[type=range]:-moz-focusring { background-color: #FCFCFC; } .graph-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; content: ""; position: absolute; z-index: 1; width: 2px; background-color: #515151; - left: 0; - top: 0; - bottom: 0; } .graph-doc-list__item--selected:first-child::before { border-top-left-radius: 0.25rem; @@ -9238,6 +9239,20 @@ select.form-control { border-right: 1px solid #dee2e6; } +.phylo__focus { + flex-grow: 1; + pointer-events: all; + position: relative; +} +.phylo__focus__inner { + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; + background-color: #fff; +} + .phylo-isoline { display: flex; position: relative; @@ -9401,7 +9416,7 @@ select.form-control { font-weight: bold; } .phylo-selection-tab__selection { - margin: 16px 20px 0; + margin: 16px 20px; } .phylo-selection-tab__selection__item { white-space: normal; @@ -9433,6 +9448,55 @@ select.form-control { color: #ADB5BD; text-align: center; } +.phylo-selection-tab__extracted-docs { + margin: 16px 20px; +} + +.phylo-doc-list__item { + cursor: pointer; + display: flex; + align-items: flex-start; + transition: all 0.2s ease-in-out; +} +.phylo-doc-list__item:focus { + outline: 0; +} +.phylo-doc-list__item:hover { + background-color: #FCFCFC; +} +.phylo-doc-list__item--selected::before { + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: #515151; +} +.phylo-doc-list__item--selected:first-child::before { + border-top-left-radius: 0.25rem; +} +.phylo-doc-list__item--selected:last-child::before { + border-bottom-left-radius: 0.25rem; +} +.phylo-doc-list__item__main { + flex-grow: 1; + padding-right: 1.25rem; +} +.phylo-doc-list__item__title, .phylo-doc-list__item__source, .phylo-doc-list__item__date { + line-height: 1.3; + margin-bottom: 4px; +} +.phylo-doc-list__item__source { + font-size: 15px; + color: #495057; +} +.phylo-doc-list__item__date { + font-size: 14px; + color: #6C757D; +} .phylo-toolbar { display: flex; diff --git a/src/Gargantext/Components/Nodes/Corpus/Phylo.purs b/src/Gargantext/Components/Nodes/Corpus/Phylo.purs index bb9648984e17c593d52d469d3a3881364b18c086..a7255c19db4583c54fc916255ea155fdd4b1ad20 100644 --- a/src/Gargantext/Components/Nodes/Corpus/Phylo.purs +++ b/src/Gargantext/Components/Nodes/Corpus/Phylo.purs @@ -11,7 +11,7 @@ import Gargantext.Components.Bootstrap as B import Gargantext.Components.PhyloExplorer.API (get) import Gargantext.Components.PhyloExplorer.Layout (layout) import Gargantext.Components.PhyloExplorer.Store as PhyloStore -import Gargantext.Components.PhyloExplorer.Types (CacheParams, PhyloDataSet, defaultCacheParams) +import Gargantext.Components.PhyloExplorer.Types (CacheParams, PhyloSet(..), defaultCacheParams) import Gargantext.Config.REST (logRESTError) import Gargantext.Hooks.FirstEffect (useFirstEffect') import Gargantext.Hooks.Loader (useLoaderEffect) @@ -40,7 +40,7 @@ nodeCpt = here.component "node" cpt where -- | session <- useSession - state' /\ state <- R2.useBox' (Nothing :: Maybe PhyloDataSet) + state' /\ state <- R2.useBox' (Nothing :: Maybe PhyloSet) cache' /\ cache <- R2.useBox' (defaultCacheParams :: CacheParams) -- | Computed @@ -92,13 +92,15 @@ nodeCpt = here.component "node" cpt where ] ] , defaultSlot: - R2.fromMaybe state' \(phyloDataSet :: PhyloDataSet) -> + R2.fromMaybe state' \(PhyloSet { corpusId, listId, phyloData }) -> let state_ :: Record PhyloStore.State state_ = -- Data - { phyloDataSet + { phyloData + , corpusId + , listId , phyloId: nodeId -- (cache params) , expandSelection: getter _.expandSelection cache' diff --git a/src/Gargantext/Components/PhyloExplorer/API.purs b/src/Gargantext/Components/PhyloExplorer/API.purs index e4683bd40b331620ec495d879ef5dc644b3793d6..680fcf2e97a38f0567a33c981ebd625e6aafa757 100644 --- a/src/Gargantext/Components/PhyloExplorer/API.purs +++ b/src/Gargantext/Components/PhyloExplorer/API.purs @@ -16,8 +16,8 @@ import Data.Maybe (Maybe(..)) import Data.Newtype (class Newtype) import Data.Show.Generic (genericShow) import Data.Symbol (SProxy(..)) -import Gargantext.Components.PhyloExplorer.JSON (PhyloJSONSet) -import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet, parsePhyloJSONSet) +import Gargantext.Components.PhyloExplorer.JSON (PhyloJSON) +import Gargantext.Components.PhyloExplorer.Types (PhyloSet, parseToPhyloSet) import Gargantext.Config.REST (AffRESTError) import Gargantext.Routes (SessionRoute(..)) import Gargantext.Routes as GR @@ -30,10 +30,10 @@ import Simple.JSON as JSON import Simple.JSON.Generics as JSONG -get :: S.Session -> NodeID -> AffRESTError (PhyloDataSet) -get session nodeId = request >>= (_ <#> parsePhyloJSONSet) >>> pure +get :: S.Session -> NodeID -> AffRESTError (PhyloSet) +get session nodeId = request >>= (_ <#> parseToPhyloSet) >>> pure where - request :: AffRESTError (PhyloJSONSet) + request :: AffRESTError (PhyloJSON) request = S.get session $ PhyloAPI nodeId ---------------------------------------------------------- diff --git a/src/Gargantext/Components/PhyloExplorer/Frame/DocFocus.purs b/src/Gargantext/Components/PhyloExplorer/Frame/DocFocus.purs new file mode 100644 index 0000000000000000000000000000000000000000..def5f5dfa737b753eb0b7923019c82fad151b167 --- /dev/null +++ b/src/Gargantext/Components/PhyloExplorer/Frame/DocFocus.purs @@ -0,0 +1,65 @@ +module Gargantext.Components.PhyloExplorer.Frame.DocFocus + ( docFocus + ) where + +import Gargantext.Prelude + +import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Gargantext.Components.Bootstrap as B +import Gargantext.Components.Bootstrap.Types (Elevation(..)) +import Gargantext.Components.Nodes.Corpus.Document (node) +import Gargantext.Components.PhyloExplorer.Types (FrameDoc(..)) +import Gargantext.Sessions (Session, sessionId) +import Gargantext.Utils.Reactix as R2 +import Reactix as R +import Reactix.DOM.HTML as H + + +here :: R2.Here +here = R2.here "Gargantext.Components.GraphExplorer.Frame.DocFocus" + +type Props = + ( frameDoc :: FrameDoc + , session :: Session + , closeCallback :: Unit -> Effect Unit + ) + +docFocus :: R2.Leaf Props +docFocus = R2.leaf docFocusCpt + +docFocusCpt :: R.Component Props +docFocusCpt = here.component "main" cpt where + cpt { frameDoc: FrameDoc { docId, listId, corpusId } + , session + , closeCallback + } _ = do + -- | Render + -- | + pure $ + + H.div + { className: "graph-doc-focus" } + [ + H.div + { className: "graph-doc-focus__header" } + [ + B.iconButton + { name: "times" + , elevation: Level2 + , callback: closeCallback + } + ] + , + H.div + { className: "graph-doc-focus__body" } + [ + -- print the document node + node + { listId + , mCorpusId: Just corpusId + , nodeId: docId + , key: show (sessionId session) <> "-" <> show docId + } + ] + ] diff --git a/src/Gargantext/Components/PhyloExplorer/JSON.purs b/src/Gargantext/Components/PhyloExplorer/JSON.purs index ffe4dabd3df953accbce8f452fdac19d03314281..7cf256b813566e2717f998ebc400a6fc5bb953c4 100644 --- a/src/Gargantext/Components/PhyloExplorer/JSON.purs +++ b/src/Gargantext/Components/PhyloExplorer/JSON.purs @@ -1,4 +1,9 @@ -module Gargantext.Components.PhyloExplorer.JSON where +module Gargantext.Components.PhyloExplorer.JSON + ( PhyloJSON(..) + , GraphData(..) + , NodeData(..), RawObject(..) + , EdgeData(..), RawEdge(..) + ) where import Gargantext.Prelude @@ -10,6 +15,26 @@ import Gargantext.Utils.SimpleJSON (untaggedSumRep) import Simple.JSON as JSON +newtype PhyloJSON = PhyloJSON + { pd_corpusId :: Int + , pd_listId :: Int + , pd_data :: + { _subgraph_cnt :: Int + , directed :: Boolean + , edges :: Array RawEdge + , objects :: Array RawObject + , strict :: Boolean + | GraphData + } + } + +derive instance Generic PhyloJSON _ +derive instance Eq PhyloJSON +instance Show PhyloJSON where show = genericShow +derive newtype instance JSON.ReadForeign PhyloJSON + +-------------------------------------------------- + type GraphData = ( bb :: String , color :: String @@ -39,22 +64,6 @@ type GraphData = -------------------------------------------------- -newtype PhyloJSONSet = PhyloJSONSet - { _subgraph_cnt :: Int - , directed :: Boolean - , edges :: Array RawEdge - , objects :: Array RawObject - , strict :: Boolean - | GraphData - } - -derive instance Generic PhyloJSONSet _ -derive instance Eq PhyloJSONSet -instance Show PhyloJSONSet where show = genericShow -derive newtype instance JSON.ReadForeign PhyloJSONSet - --------------------------------------------------- - type NodeData = ( height :: String , label :: String @@ -125,7 +134,6 @@ instance Show RawObject where show = genericShow instance JSON.ReadForeign RawObject where readImpl f = GR.to <$> untaggedSumRep f - -------------------------------------------------- type EdgeData = diff --git a/src/Gargantext/Components/PhyloExplorer/Layout.purs b/src/Gargantext/Components/PhyloExplorer/Layout.purs index 03504579d5d9dcd4071dc7672869f91f16dbd0a2..0b70dfa08ca6e5a51b31d15c36b328437ad338fe 100644 --- a/src/Gargantext/Components/PhyloExplorer/Layout.purs +++ b/src/Gargantext/Components/PhyloExplorer/Layout.purs @@ -13,14 +13,16 @@ import Data.String (null) import Effect (Effect) import FFI.Simple ((..), (.=)) import Gargantext.Components.Bootstrap as B +import Gargantext.Components.PhyloExplorer.Frame.DocFocus (docFocus) import Gargantext.Components.PhyloExplorer.Resources (PubSubEvent(..)) import Gargantext.Components.PhyloExplorer.Resources as RS import Gargantext.Components.PhyloExplorer.SideBar (sideBar) import Gargantext.Components.PhyloExplorer.Store as PhyloStore import Gargantext.Components.PhyloExplorer.ToolBar (toolBar) import Gargantext.Components.PhyloExplorer.TopBar (topBar) -import Gargantext.Components.PhyloExplorer.Types (DisplayView, ExtractedCount, FrameDoc, PhyloDataSet(..), TabView(..), Term, sortSources) +import Gargantext.Components.PhyloExplorer.Types (DisplayView, ExtractedCount, FrameDoc, PhyloData(..), TabView(..), Term, sortSources) import Gargantext.Hooks.FirstEffect (useFirstEffect') +import Gargantext.Hooks.Session (useSession) import Gargantext.Hooks.UpdateEffect (useUpdateEffect1', useUpdateEffect3') import Gargantext.Types (SidePanelState(..)) import Gargantext.Utils (getter, (?)) @@ -59,11 +61,11 @@ layoutCpt = here.component "layout" cpt where , selectedSource , extractedCount , phyloId - , phyloDataSet + , phyloData , frameDoc } <- PhyloStore.use - (PhyloDataSet o) <- R2.useLive' phyloDataSet + (PhyloData o) <- R2.useLive' phyloData phyloId' <- R2.useLive' phyloId sources' <- R2.useLive' sources terms' <- R2.useLive' terms @@ -77,6 +79,8 @@ layoutCpt = here.component "layout" cpt where selectedSource' <- R2.useLive' selectedSource frameDoc' <- R2.useLive' frameDoc + session <- useSession + -- | Hooks -- | let topBarPortalKey = "portal-topbar::" <> show phyloId' @@ -143,6 +147,9 @@ layoutCpt = here.component "layout" cpt where mTerm <- RS.autocompleteSearch terms' s RS.autocompleteSubmit displayView mTerm + closeDocCallback :: Unit -> Effect Unit + closeDocCallback _ = T.write_ Nothing frameDoc + -- | Effects -- | @@ -150,7 +157,7 @@ layoutCpt = here.component "layout" cpt where useFirstEffect' do (sortSources >>> flip T.write_ sources) o.sources RS.setGlobalD3Reference window d3 - RS.setGlobalDependencies window (PhyloDataSet o) + RS.setGlobalDependencies window (PhyloData o) RS.drawPhylo o.branches o.periods @@ -256,7 +263,7 @@ layoutCpt = here.component "layout" cpt where { className: "phylo__frame" } [ -- Doc focus - R2.fromMaybe frameDoc' \(f :: FrameDoc) -> + R2.fromMaybe frameDoc' \(frameDoc_ :: FrameDoc) -> H.div { className: "phylo__focus" } @@ -264,7 +271,11 @@ layoutCpt = here.component "layout" cpt where H.div { className: "phylo__focus__inner" } [ - H.text $ "hello" + docFocus + { session + , frameDoc: frameDoc_ + , closeCallback: closeDocCallback + } ] ] , diff --git a/src/Gargantext/Components/PhyloExplorer/Resources.purs b/src/Gargantext/Components/PhyloExplorer/Resources.purs index 3569159d861178e489f54f01ae94fa331d833434..71f22f2f159ec8ef76d93ff02c4f4562b3a522de 100644 --- a/src/Gargantext/Components/PhyloExplorer/Resources.purs +++ b/src/Gargantext/Components/PhyloExplorer/Resources.purs @@ -25,7 +25,7 @@ import Data.String as String import Effect (Effect) import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn4, EffectFn7, runEffectFn1, runEffectFn2, runEffectFn4, runEffectFn7) import FFI.Simple ((..), (.=), (.?)) -import Gargantext.Components.PhyloExplorer.Types (AncestorLink, Branch, BranchLink, DisplayView(..), Group(..), Link, Period, PhyloDataSet(..), Source(..), Term(..)) +import Gargantext.Components.PhyloExplorer.Types (AncestorLink, Branch, BranchLink, DisplayView(..), Group(..), Link, Period, PhyloData(..), Source(..), Term(..)) import Gargantext.Utils (getter) import Gargantext.Utils.Reactix ((~~)) import Gargantext.Utils.Reactix as R2 @@ -203,8 +203,8 @@ selectionNodes = ffi ["selection", ""] "selection.nodes()" ----------------------------------------------------------- -setGlobalDependencies :: Window -> PhyloDataSet -> Effect Unit -setGlobalDependencies w (PhyloDataSet o) +setGlobalDependencies :: Window -> PhyloData -> Effect Unit +setGlobalDependencies w (PhyloData o) = do _ <- pure $ (w .= "freq") {} _ <- pure $ (w .= "nbBranches") o.nbBranches diff --git a/src/Gargantext/Components/PhyloExplorer/Sidebar/DetailsTab.purs b/src/Gargantext/Components/PhyloExplorer/Sidebar/DetailsTab.purs index d8268c0ff091a7d6ed51035b16e5f4f5f2d8f716..368c18e052fe1c2576fce185a8cb6f921a07ce84 100644 --- a/src/Gargantext/Components/PhyloExplorer/Sidebar/DetailsTab.purs +++ b/src/Gargantext/Components/PhyloExplorer/Sidebar/DetailsTab.purs @@ -5,7 +5,7 @@ module Gargantext.Components.PhyloExplorer.DetailsTab import Gargantext.Prelude import Gargantext.Components.PhyloExplorer.Store as PhyloStore -import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet(..)) +import Gargantext.Components.PhyloExplorer.Types (PhyloData(..)) import Gargantext.Utils (nbsp) import Gargantext.Utils.Reactix as R2 import Reactix as R @@ -25,7 +25,7 @@ detailsTabCpt = here.component "" cpt where -- | store <- PhyloStore.use - (PhyloDataSet o) <- R2.useLive' store.phyloDataSet + (PhyloData o) <- R2.useLive' store.phyloData -- | Render -- | diff --git a/src/Gargantext/Components/PhyloExplorer/Sidebar/DocList.purs b/src/Gargantext/Components/PhyloExplorer/Sidebar/DocList.purs index d12a84b1ff293dfbc403417d74f6e6c3718846d0..a8a6b5a6bf11c4e2d639027d536f1ab13ffdb361 100644 --- a/src/Gargantext/Components/PhyloExplorer/Sidebar/DocList.purs +++ b/src/Gargantext/Components/PhyloExplorer/Sidebar/DocList.purs @@ -1,102 +1,114 @@ module Gargantext.Components.PhyloExplorer.Sidebar.DocList - where -{- + ( docListWrapper + ) where import Gargantext.Prelude -import Control.Parallel (parTraverse) -import Data.Array (concat, head, last, mapWithIndex) -import Data.Array as A -import Data.Either (Either(..)) +import Data.Array (concat) import Data.Foldable (intercalate) -import Data.Foldable as F -import Data.Int (fromString) -import Data.Map as Map -import Data.Maybe (Maybe(..), fromJust) +import Data.Maybe (Maybe(..)) import Data.Sequence as Seq -import Data.Set as Set import Data.Tuple.Nested ((/\)) import Effect (Effect) -import Effect.Aff (launchAff_) -import Effect.Class (liftEffect) -import Gargantext.Components.App.Store as AppStore import Gargantext.Components.Bootstrap as B -import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) -import Gargantext.Components.GraphExplorer.Sidebar.DocList (docList) -import Gargantext.Components.GraphExplorer.Sidebar.Legend as Legend -import Gargantext.Components.GraphExplorer.Store as GraphStore -import Gargantext.Components.GraphExplorer.Types as GET -import Gargantext.Components.Lang (Lang(..)) -import Gargantext.Components.NgramsTable.Core as NTC +import Gargantext.Components.Bootstrap.Types (Variant(..)) +import Gargantext.Components.FacetsTable (DocumentsView(..), Rows(..), initialPagePath, loadPage, publicationDate) import Gargantext.Components.PhyloExplorer.Store as PhyloStore +import Gargantext.Components.PhyloExplorer.Types (CorpusId, DocId, FrameDoc(..), ListId) import Gargantext.Components.RandomText (words) import Gargantext.Components.Search (SearchQuery(..), SearchType(..)) import Gargantext.Config (defaultFrontends) -import Gargantext.Config.REST (AffRESTError) -import Gargantext.Data.Array (mapMaybe) -import Gargantext.Ends (Frontends) +import Gargantext.Config.REST (RESTError(..)) +import Gargantext.Ends (Frontends, url) +import Gargantext.Hooks.Loader (useLoaderEffect) import Gargantext.Hooks.Session (useSession) -import Gargantext.Hooks.Sigmax.Types as SigmaxT -import Gargantext.Sessions (Session) -import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, TabSubType(..), TabType(..), TermList(..), modeTabType) -import Gargantext.Utils (getter, nbsp) +import Gargantext.Hooks.UpdateEffect (useUpdateEffect1') +import Gargantext.Routes as Routes +import Gargantext.Sessions (Session, sessionId) +import Gargantext.Utils (getter, (?)) import Gargantext.Utils.Reactix as R2 -import Gargantext.Utils.Toestand as T2 -import Math as Math -import Partial.Unsafe (unsafePartial) +import React.SyntheticEvent as SE import Reactix as R import Reactix.DOM.HTML as H -import Record as Record import Toestand as T here :: R2.Here here = R2.here "Gargantext.Components.PhyloExplorer.Sidebar.DocList" + docListWrapper :: R2.Leaf () docListWrapper = R2.leaf docListWrapperCpt -docListWrapperCpt :: R.Memo () -docListWrapperCpt = R.memo' $ here.component "wrapper" cpt where +docListWrapperCpt :: R.Component () +docListWrapperCpt = here.component "wrapper" cpt where cpt _ _ = do -- | States -- | + session <- useSession + store <- PhyloStore.use extractedTerms <- R2.useLive' store.extractedTerms + corpusId <- R2.useLive' store.corpusId + listId <- R2.useLive' store.listId query' /\ query <- R2.useBox' Nothing -- | Helpers -- | let - toSearchQuery ids = SearchQuery + + toSearchQuery items = SearchQuery { expected: SearchDoc - , query: map (getter _.label) ids + , query: concat $ words <$> (getter _.label) <$> items } - searchQuery - = extractedTerms - # toSearchQuery - # Just - # flip T.write_ query + -- | Hooks + -- | + R.useEffect1' extractedTerms $ + T.write_ (extractedTerms # toSearchQuery >>> Just) query -- | Render - -- + -- | pure $ - docList - { searchQuery } + R.fragment + [ + case query' of + + Nothing -> + B.caveat + {} + [ + H.text "You can link a corpus to retrieve relative documents about your selection" + ] + + Just q' -> + docList + { query: q' + , session + , corpusId + , listId + , frameDoc: store.frameDoc + , frontends: defaultFrontends + } + ] --------------------------------------------------------------- +------------------------------------------------------------------- type ListProps = - ( searchQuery :: SearchQuery + ( query :: SearchQuery + , corpusId :: CorpusId + , listId :: ListId + , session :: Session + , frameDoc :: T.Box (Maybe FrameDoc) + , frontends :: Frontends ) -docList :: R2.Leaf TabsProps +docList :: R2.Leaf ListProps docList = R2.leaf docListCpt -docListCpt :: R.Component TabsProps +docListCpt :: R.Component ListProps docListCpt = here.component "main" cpt where -- | Helpers -- | @@ -108,7 +120,12 @@ docListCpt = here.component "main" cpt where _ -> pure unit -- | Component -- | - cpt { searchQuery + cpt { query + , session + , corpusId: nodeId + , listId + , frameDoc + , frontends } _ = do -- | States -- | @@ -122,8 +139,8 @@ docListCpt = here.component "main" cpt where rows' /\ rows <- R2.useBox' Nothing - showDoc' <- - R2.useLive' showDoc + frameDoc' <- + R2.useLive' frameDoc -- | Hooks -- | @@ -153,35 +170,35 @@ docListCpt = here.component "main" cpt where -- | let - -- callback :: Maybe GraphSideDoc -> DocId -> Effect Unit - -- callback - -- Nothing - -- new - -- = setGraphSideDoc new # Just # flip T.write_ showDoc - - -- callback - -- (Just (GraphSideDoc { docId })) - -- new - -- | docId == new = T.write_ Nothing showDoc - -- | otherwise = setGraphSideDoc new # Just # flip T.write_ showDoc - - -- setGraphSideDoc :: DocId -> GraphSideDoc - -- setGraphSideDoc docId = GraphSideDoc - -- { docId - -- , listId - -- , corpusId: nodeId - -- } - - -- isSelected :: Maybe GraphSideDoc -> DocumentsView -> Boolean - -- isSelected - -- (Just (GraphSideDoc { docId })) - -- (DocumentsView { id }) - -- = docId == id - - -- isSelected - -- _ - -- _ - -- = false + callback :: Maybe FrameDoc -> DocId -> Effect Unit + callback + Nothing + new + = setFrameDoc new # Just # flip T.write_ frameDoc + + callback + (Just (FrameDoc { docId })) + new + | docId == new = T.write_ Nothing frameDoc + | otherwise = setFrameDoc new # Just # flip T.write_ frameDoc + + setFrameDoc :: DocId -> FrameDoc + setFrameDoc docId = FrameDoc + { docId + , listId + , corpusId: nodeId + } + + isSelected :: Maybe FrameDoc -> DocumentsView -> Boolean + isSelected + (Just (FrameDoc { docId })) + (DocumentsView { id }) + = docId == id + + isSelected + _ + _ + = false -- | Render -- | @@ -196,26 +213,27 @@ docListCpt = here.component "main" cpt where B.caveat {} [ - H.text "No docs found in your corpus for your selected terms" + H.text "No document found in your corpus for your selected terms" ] , R2.when (not $ eq results Seq.empty) $ H.ul { className: intercalate " " - [ "graph-doc-list" + [ "phylo-doc-list" , "list-group" ] } $ Seq.toUnfoldable $ flip Seq.map results \r -> item - { frontends - , path: path' + { documentView: (r :: DocumentsView) + , callback: callback frameDoc' + , isSelected: isSelected frameDoc' (r :: DocumentsView) + , listId + , corpusId: nodeId , session - , documentView: (r :: DocumentsView) - , callback: callback showDoc' - , isSelected: isSelected showDoc' (r :: DocumentsView) + , frontends } ] @@ -224,11 +242,12 @@ docListCpt = here.component "main" cpt where type ItemProps = ( documentView :: DocumentsView - , frontends :: Frontends - , session :: Session - , path :: PagePath , callback :: DocId -> Effect Unit , isSelected :: Boolean + , corpusId :: CorpusId + , listId :: ListId + , session :: Session + , frontends :: Frontends ) item :: R2.Leaf ItemProps @@ -239,50 +258,63 @@ itemCpt = here.component "item" cpt where cpt { documentView: dv@(DocumentsView { id, title, source }) , callback , isSelected - -- , frontends - -- , path - -- , session + , listId + , corpusId + , session + , frontends } _ = do -- Computed - -- let + let -- Creating a href link - -- documentUrl id' { listId, nodeId } = - -- url frontends $ Routes.CorpusDocument (sessionId session) nodeId listId id' + route = Routes.CorpusDocument (sessionId session) corpusId listId id + href = url frontends route + -- Methods + let + onClick :: + SE.SyntheticMouseEvent + -> Effect Unit + onClick event + = R2.externalOpeningFlag event + >>= case _ of + true -> R.nothing + false -> SE.preventDefault event *> callback id -- Render pure $ - H.div + H.a { className: intercalate " " - [ "graph-doc-list__item" - , isSelected ? "graph-doc-list__item--selected" $ "" + [ "phylo-doc-list__item" + , isSelected ? "phylo-doc-list__item--selected" $ "" , "list-group-item" + , "text-decoration-none" ] - , on: { click: \_ -> callback id } + , on: { click: onClick } + , href } [ B.ripple { variant: Dark } [ H.div - { className: "graph-doc-list__item__main" } + { className: "phylo-doc-list__item__main" } [ B.div' - { className: "graph-doc-list__item__title" } + { className: "phylo-doc-list__item__title" } title , B.div' - { className: "graph-doc-list__item__source" } + { className: "phylo-doc-list__item__source" } source , B.div' - { className: "graph-doc-list__item__date" } $ + { className: "phylo-doc-list__item__date" } $ publicationDate dv ] , H.div - { className: "graph-doc-list__item__aside" } + { className: "phylo-doc-list__item__aside" } [ B.icon { name: "eye-slash" diff --git a/src/Gargantext/Components/PhyloExplorer/Sidebar/SelectionTab.purs b/src/Gargantext/Components/PhyloExplorer/Sidebar/SelectionTab.purs index 73cff1dc9319b4a57dd1dd633d731e298c107eeb..082335205c1161730e0fe7664ebd0ec1eb6be9fa 100644 --- a/src/Gargantext/Components/PhyloExplorer/Sidebar/SelectionTab.purs +++ b/src/Gargantext/Components/PhyloExplorer/Sidebar/SelectionTab.purs @@ -12,6 +12,7 @@ import Data.Tuple.Nested ((/\)) import Effect (Effect) import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..)) +import Gargantext.Components.PhyloExplorer.Sidebar.DocList (docListWrapper) import Gargantext.Components.PhyloExplorer.Store as PhyloStore import Gargantext.Components.PhyloExplorer.Types (ExtractedCount(..), ExtractedTerm(..), defaultCacheParams) import Gargantext.Hooks.FirstEffect (useFirstEffect') @@ -322,19 +323,26 @@ component = here.component "main" cpt where ] ] ] - -- , + , -- (separator) - -- R2.when (not null extractedTerms) $ - - -- H.div - -- { className: "phylo-selection-tab__separator" } - -- [ - -- B.icon - -- { name: "angle-double-down" } - -- ] - -- , + R2.when (not null extractedTerms) $ + + H.div + { className: "phylo-selection-tab__separator" } + [ + B.icon + { name: "angle-double-down" } + ] + , -- Extracted Docs - -- R2.when (not null extractedTerms) $ + R2.when (not null extractedTerms) $ + + H.div + { className: "phylo-selection-tab__extracted-docs" } + [ + docListWrapper + {} + ] ] termFontSize :: Number -> String diff --git a/src/Gargantext/Components/PhyloExplorer/Store.purs b/src/Gargantext/Components/PhyloExplorer/Store.purs index 36045ffc325f837d24bdb3e0190a224e0be84460..bf2199b5f614966a9c1e281b1e2269d4b14f5280 100644 --- a/src/Gargantext/Components/PhyloExplorer/Store.purs +++ b/src/Gargantext/Components/PhyloExplorer/Store.purs @@ -11,7 +11,7 @@ module Gargantext.Components.PhyloExplorer.Store import Gargantext.Prelude import Data.Maybe (Maybe(..)) -import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), ExtractedCount, ExtractedTerm, FrameDoc, PhyloDataSet, Source, TabView(..), Term, defaultCacheParams) +import Gargantext.Components.PhyloExplorer.Types (CorpusId, DisplayView(..), ExtractedCount, ExtractedTerm, FrameDoc, PhyloData, Source, TabView(..), Term, ListId, defaultCacheParams) import Gargantext.Types (NodeID, SidePanelState(..)) import Gargantext.Utils (getter) import Gargantext.Utils.Reactix as R2 @@ -25,8 +25,10 @@ here = R2.here "Gargantext.Components.GraphExplorer.Store" type Store = -- Data - ( phyloDataSet :: T.Box PhyloDataSet + ( phyloData :: T.Box PhyloData , phyloId :: T.Box NodeID + , corpusId :: T.Box CorpusId + , listId :: T.Box ListId , isBuilt :: T.Box Boolean -- Layout , toolBarDisplayed :: T.Box Boolean @@ -54,8 +56,10 @@ type Store = type State = -- Data - ( phyloDataSet :: PhyloDataSet + ( phyloData :: PhyloData , phyloId :: NodeID + , corpusId :: CorpusId + , listId :: ListId , isBuilt :: Boolean -- Layout , toolBarDisplayed :: Boolean diff --git a/src/Gargantext/Components/PhyloExplorer/Types.purs b/src/Gargantext/Components/PhyloExplorer/Types.purs index 7e0b0debe119dd11d68ca5f495886d1c1aebc613..aa5187fe33bc6d149ecf49390584c57a92ed0da8 100644 --- a/src/Gargantext/Components/PhyloExplorer/Types.purs +++ b/src/Gargantext/Components/PhyloExplorer/Types.purs @@ -1,6 +1,7 @@ module Gargantext.Components.PhyloExplorer.Types - ( PhyloDataSet(..) - , parsePhyloJSONSet + ( PhyloSet(..), parseToPhyloSet + , CorpusId, ListId, DocId + , PhyloData(..) , Branch(..), Period(..), Group(..) , Link(..), AncestorLink(..), BranchLink(..) , Term(..) @@ -28,18 +29,72 @@ import Data.String as String import Data.String.Extra (camelCase) import Data.Tuple as Tuple import Data.Tuple.Nested ((/\)) -import Gargantext.Components.PhyloExplorer.JSON (PhyloJSONSet(..), RawEdge(..), RawObject(..)) +import Gargantext.Components.PhyloExplorer.JSON (PhyloJSON(..), RawEdge(..), RawObject(..)) import Gargantext.Types (NodeID) import Simple.JSON as JSON - -- @NOTE #219: PureScript Date or stick to JavaScript foreign? foreign import yearToDate :: String -> Date.Date foreign import stringToDate :: String -> Date.Date foreign import utcStringToDate :: String -> Date.Date +type CorpusId = Int +type ListId = Int +type DocId = Int + +------------------------------------------------------------------ + +data PhyloSet = PhyloSet + { corpusId :: CorpusId + , listId :: ListId + , phyloData :: PhyloData + } + +derive instance Generic PhyloSet _ +derive instance Eq PhyloSet +instance Show PhyloSet where show = genericShow + +parseToPhyloSet :: PhyloJSON -> PhyloSet +parseToPhyloSet (PhyloJSON o) = PhyloSet + { corpusId : o.pd_corpusId + , listId : o.pd_listId + , phyloData : PhyloData + { ancestorLinks + , bb : parseBB p.bb + , branchLinks + , branches + , groups + , links + , name : p.name + , nbBranches : parseInt p.phyloBranches + -- @NOTE #219: remotely stringify as a Double instead of an Int (reason?) + , nbDocs : (parseFloat >>> parseInt') p.phyloDocs + , nbFoundations : parseInt p.phyloFoundations + , nbGroups : parseInt p.phyloGroups + , nbPeriods : parseInt p.phyloPeriods + , nbTerms : parseInt p.phyloTerms + , periods + , sources : parseSources p.phyloSources + , timeScale : p.phyloTimeScale + , weighted : getGlobalWeightedValue groups + } + } + + where + p = o.pd_data + + epochTS = p.phyloTimeScale == "epoch" + + ancestorLinks = parseAncestorLinks p.edges + branchLinks = parseBranchLinks p.edges + branches = parseBranches p.objects + groups = parseGroups epochTS p.objects + links = parseLinks p.edges + periods = parsePeriods epochTS p.objects + +---------------------------------------------------------------------- -data PhyloDataSet = PhyloDataSet +data PhyloData = PhyloData { ancestorLinks :: Array AncestorLink , bb :: Array Number , branchLinks :: Array BranchLink @@ -59,41 +114,9 @@ data PhyloDataSet = PhyloDataSet , weighted :: Boolean } -derive instance Generic PhyloDataSet _ -derive instance Eq PhyloDataSet -instance Show PhyloDataSet where show = genericShow - -parsePhyloJSONSet :: PhyloJSONSet -> PhyloDataSet -parsePhyloJSONSet (PhyloJSONSet o) = PhyloDataSet - { ancestorLinks - , bb : parseBB o.bb - , branchLinks - , branches - , groups - , links - , name : o.name - , nbBranches : parseInt o.phyloBranches - -- @NOTE #219: remotely stringify as a Double instead of an Int (reason?) - , nbDocs : (parseFloat >>> parseInt') o.phyloDocs - , nbFoundations : parseInt o.phyloFoundations - , nbGroups : parseInt o.phyloGroups - , nbPeriods : parseInt o.phyloPeriods - , nbTerms : parseInt o.phyloTerms - , periods - , sources : parseSources o.phyloSources - , timeScale : o.phyloTimeScale - , weighted : getGlobalWeightedValue groups - } - - where - epochTS = o.phyloTimeScale == "epoch" - - ancestorLinks = parseAncestorLinks o.edges - branchLinks = parseBranchLinks o.edges - branches = parseBranches o.objects - groups = parseGroups epochTS o.objects - links = parseLinks o.edges - periods = parsePeriods epochTS o.objects +derive instance Generic PhyloData _ +derive instance Eq PhyloData +instance Show PhyloData where show = genericShow ----------------------------------------------------------- @@ -492,9 +515,9 @@ derive newtype instance JSON.ReadForeign ExtractedCount ----------------------------------------------------------- newtype FrameDoc = FrameDoc - { docId :: NodeID - , corpusId :: NodeID - , listId :: NodeID + { docId :: DocId + , corpusId :: CorpusId + , listId :: ListId } derive instance Generic FrameDoc _ diff --git a/src/sass/_legacy/_graph.sass b/src/sass/_legacy/_graph.sass index 160a846559b7441e96b1811873a87df1508cc4ff..3b444cd347ce42dab6d2a1330d61c8cb5bd0797e 100644 --- a/src/sass/_legacy/_graph.sass +++ b/src/sass/_legacy/_graph.sass @@ -252,14 +252,13 @@ background-color: $gray-50 &--selected::before + @include fit-positions + content: "" position: absolute z-index: 1 width: 2px background-color: $info - left: 0 - top: 0 - bottom: 0 // (following list group item border radius) &--selected:first-child::before diff --git a/src/sass/_legacy/_phylo.scss b/src/sass/_legacy/_phylo.scss index 188b1706a54d77168381a484b89d7919354c9f20..ee26b28e8b13299806a82dd5aae74573e1afdea3 100644 --- a/src/sass/_legacy/_phylo.scss +++ b/src/sass/_legacy/_phylo.scss @@ -383,6 +383,19 @@ $decreasing-color: #11638F; } } } + + &__focus { + flex-grow: 1; + pointer-events: all; + position: relative; + + &__inner { + @include fit-positions(); + + position: absolute; + background-color: $body-bg; + } + } } @@ -563,7 +576,7 @@ $decreasing-color: #11638F; } &__selection { - margin: $margin-y $margin-x 0; + margin: $margin-y $margin-x; &__item { white-space: normal; @@ -611,6 +624,65 @@ $decreasing-color: #11638F; color: $gray-500; text-align: center; } + + &__extracted-docs { + margin: $margin-y $margin-x; + } +} + +.phylo-doc-list { + + &__item { + @include clickable(); + + display: flex; + align-items: flex-start; + transition: $transition-base; + + &:hover { + background-color: $gray-50; + } + + &--selected::before { + @include fit-positions(); + + content: ""; + position: absolute; + z-index: 1; + width: 2px; + background-color: $info; + } + + // (following list group item border radius) + &--selected:first-child::before { + border-top-left-radius: $list-group-border-radius; + } + &--selected:last-child::before { + border-bottom-left-radius: $list-group-border-radius; + } + + &__main { + flex-grow: 1; + padding-right: $card-spacer-x; + } + + &__title, + &__source, + &__date { + line-height: 1.3; + margin-bottom: space-x(0.5); + } + + &__source { + font-size: 15px; + color: $gray-700; + } + + &__date { + font-size: 14px; + color: $gray-600; + } + } } ////////////////////////////////////////////////////////////////