Commit 27399e65 authored by Karen Konou's avatar Karen Konou

Merge branch 'dev' into 382-dev-plane-navigation

parents 2e21ebc9 9de858ae
......@@ -6002,19 +6002,29 @@ h3 {
position: relative;
transition: color 150ms ease-in-out;
}
.b-icon-button--overlay.b-icon-button::before {
.b-icon-button__inner {
z-index: 1;
position: relative;
}
.b-icon-button::before {
left: -10px;
right: -10px;
top: -6px;
right: -6px;
bottom: -6px;
left: -6px;
content: "";
background-color: #212529;
border-radius: 5px;
z-index: -1;
position: absolute;
transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.b-icon-button--overlay::before {
content: "";
}
.b-icon-button--level-1:hover::before {
background-color: #212529;
}
.b-icon-button--level-2::before {
background-color: #212529;
}
.b-icon-button--overlay:hover.b-icon-button::before {
.b-icon-button--level-2:hover::before {
background-color: #0a0c0d;
}
.b-icon-button--enabled, .b-icon-button--muted, .b-icon-button--idled {
......@@ -6065,6 +6075,9 @@ h3 {
.b-icon-button--enabled.b-icon-button--dark:hover, .b-icon-button--muted.b-icon-button--dark:hover {
color: #b3b3b3;
}
.b-icon-button--enabled.b-icon-button:active::before, .b-icon-button--enabled.b-icon-button--active::before, .b-icon-button--muted.b-icon-button:active::before, .b-icon-button--muted.b-icon-button--active::before {
box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
}
.b-icon-button--idled:hover {
cursor: default;
}
......@@ -7414,6 +7427,7 @@ input[type=range]:-moz-focusring {
flex-grow: 1;
pointer-events: all;
position: relative;
content: layout;
}
.graph-layout__focus__inner {
top: 0;
......@@ -7467,6 +7481,55 @@ input[type=range]:-moz-focusring {
display: none;
}
.graph-doc-focus {
scrollbar-width: none;
overflow-y: scroll;
background-color: #000000;
height: 100%;
position: relative;
}
.graph-doc-focus::-webkit-scrollbar {
display: none;
}
.graph-doc-focus::before {
background: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, black 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
top: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus::after {
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, black 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
bottom: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus__header {
padding: 0.75rem 1.25rem;
font-size: 20px;
}
.right-handed .graph-doc-focus__header {
float: right;
}
.left-handed .graph-doc-focus__header {
float: left;
}
.graph-doc-focus__body {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.graph-sidebar {
scrollbar-width: none;
overflow-y: scroll;
......@@ -7590,6 +7653,22 @@ input[type=range]:-moz-focusring {
.graph-doc-list__item:hover {
background-color: #121212;
}
.graph-doc-list__item--selected::before {
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;
}
.graph-doc-list__item--selected:last-child::before {
border-bottom-left-radius: 0.25rem;
}
.graph-doc-list__item__main {
flex-grow: 1;
padding-right: 1.25rem;
......@@ -7607,6 +7686,15 @@ input[type=range]:-moz-focusring {
color: #CED4DA;
}
.graph-contact-list__item__title, .graph-contact-list__item__subtitle {
line-height: 1.3;
margin-bottom: 2px;
}
.graph-contact-list__item__subtitle {
font-size: 15px;
color: #DEE2E6;
}
.graph-toolbar {
display: flex;
padding: 8px;
......@@ -7938,23 +8026,26 @@ a:focus, a:hover {
visibility: visible;
}
.right-handed .mainleaf:hover .mainleaf__settings-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf:hover .mainleaf__settings-icon {
margin-right: 16px;
margin-right: 18px;
}
.right-handed .mainleaf__update-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf__update-icon {
margin-right: 16px;
margin-right: 18px;
}
.mainleaf__update-icon.b-icon-button--overlay::before, .mainleaf__settings-icon.b-icon-button--overlay::before {
.mainleaf__update-icon.b-icon-button::before, .mainleaf__settings-icon.b-icon-button::before {
top: -5px;
bottom: -5px;
left: -6px;
right: -6px;
}
.mainleaf__progress-bar {
width: 64px;
......@@ -8910,19 +9001,43 @@ select.form-control {
top: calc( 50% - 50px );
left: calc( 50% - 50px );
}
.phylo__frame {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
display: flex;
z-index: 1;
width: 100%;
height: calc(100vh - 56px);
justify-content: flex-end;
pointer-events: none;
}
.right-handed .phylo__frame {
flex-direction: row;
}
.left-handed .phylo__frame {
flex-direction: row-reverse;
}
.phylo__sidebar {
position: fixed;
width: 480px;
height: calc(100vh - 56px);
z-index: 1;
height: inherit;
flex-grow: 0;
pointer-events: all;
}
.right-handed .phylo__sidebar {
right: 0;
.phylo__sidebar__inner {
position: fixed;
height: inherit;
width: inherit;
}
.right-handed .phylo__sidebar__inner {
border-left: 1px solid #dee2e6;
}
.left-handed .phylo__sidebar {
left: 0;
.left-handed .phylo__sidebar__inner {
border-right: 1px solid #dee2e6;
}
......
......@@ -5956,19 +5956,29 @@ h3 {
position: relative;
transition: color 150ms ease-in-out;
}
.b-icon-button--overlay.b-icon-button::before {
.b-icon-button__inner {
z-index: 1;
position: relative;
}
.b-icon-button::before {
left: -10px;
right: -10px;
top: -6px;
right: -6px;
bottom: -6px;
left: -6px;
content: "";
background-color: #F8F9FA;
border-radius: 5px;
z-index: -1;
position: absolute;
transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.b-icon-button--overlay::before {
content: "";
}
.b-icon-button--level-1:hover::before {
background-color: #F8F9FA;
}
.b-icon-button--level-2::before {
background-color: #F8F9FA;
}
.b-icon-button--overlay:hover.b-icon-button::before {
.b-icon-button--level-2:hover::before {
background-color: #dae0e5;
}
.b-icon-button--enabled, .b-icon-button--muted, .b-icon-button--idled {
......@@ -6019,6 +6029,9 @@ h3 {
.b-icon-button--enabled.b-icon-button--dark:hover, .b-icon-button--muted.b-icon-button--dark:hover {
color: #0a0c0d;
}
.b-icon-button--enabled.b-icon-button:active::before, .b-icon-button--enabled.b-icon-button--active::before, .b-icon-button--muted.b-icon-button:active::before, .b-icon-button--muted.b-icon-button--active::before {
box-shadow: 0 0 0 0.2rem rgba(233, 236, 239, 0.5);
}
.b-icon-button--idled:hover {
cursor: default;
}
......@@ -7367,6 +7380,7 @@ input[type=range]:-moz-focusring {
flex-grow: 1;
pointer-events: all;
position: relative;
content: layout;
}
.graph-layout__focus__inner {
top: 0;
......@@ -7420,6 +7434,55 @@ input[type=range]:-moz-focusring {
display: none;
}
.graph-doc-focus {
scrollbar-width: none;
overflow-y: scroll;
background-color: #fff;
height: 100%;
position: relative;
}
.graph-doc-focus::-webkit-scrollbar {
display: none;
}
.graph-doc-focus::before {
background: linear-gradient(to top, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
top: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus::after {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
bottom: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus__header {
padding: 0.75rem 1.25rem;
font-size: 20px;
}
.right-handed .graph-doc-focus__header {
float: right;
}
.left-handed .graph-doc-focus__header {
float: left;
}
.graph-doc-focus__body {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.graph-sidebar {
scrollbar-width: none;
overflow-y: scroll;
......@@ -7543,6 +7606,22 @@ input[type=range]:-moz-focusring {
.graph-doc-list__item:hover {
background-color: #FCFCFC;
}
.graph-doc-list__item--selected::before {
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;
}
.graph-doc-list__item--selected:last-child::before {
border-bottom-left-radius: 0.25rem;
}
.graph-doc-list__item__main {
flex-grow: 1;
padding-right: 1.25rem;
......@@ -7560,6 +7639,15 @@ input[type=range]:-moz-focusring {
color: #6C757D;
}
.graph-contact-list__item__title, .graph-contact-list__item__subtitle {
line-height: 1.3;
margin-bottom: 2px;
}
.graph-contact-list__item__subtitle {
font-size: 15px;
color: #495057;
}
.graph-toolbar {
display: flex;
padding: 8px;
......@@ -7891,23 +7979,26 @@ a:focus, a:hover {
visibility: visible;
}
.right-handed .mainleaf:hover .mainleaf__settings-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf:hover .mainleaf__settings-icon {
margin-right: 16px;
margin-right: 18px;
}
.right-handed .mainleaf__update-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf__update-icon {
margin-right: 16px;
margin-right: 18px;
}
.mainleaf__update-icon.b-icon-button--overlay::before, .mainleaf__settings-icon.b-icon-button--overlay::before {
.mainleaf__update-icon.b-icon-button::before, .mainleaf__settings-icon.b-icon-button::before {
top: -5px;
bottom: -5px;
left: -6px;
right: -6px;
}
.mainleaf__progress-bar {
width: 64px;
......@@ -8863,19 +8954,43 @@ select.form-control {
top: calc( 50% - 50px );
left: calc( 50% - 50px );
}
.phylo__frame {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
display: flex;
z-index: 1;
width: 100%;
height: calc(100vh - 56px);
justify-content: flex-end;
pointer-events: none;
}
.right-handed .phylo__frame {
flex-direction: row;
}
.left-handed .phylo__frame {
flex-direction: row-reverse;
}
.phylo__sidebar {
position: fixed;
width: 480px;
height: calc(100vh - 56px);
z-index: 1;
height: inherit;
flex-grow: 0;
pointer-events: all;
}
.right-handed .phylo__sidebar {
right: 0;
.phylo__sidebar__inner {
position: fixed;
height: inherit;
width: inherit;
}
.right-handed .phylo__sidebar__inner {
border-left: 1px solid #dee2e6;
}
.left-handed .phylo__sidebar {
left: 0;
.left-handed .phylo__sidebar__inner {
border-right: 1px solid #dee2e6;
}
......
......@@ -5711,19 +5711,29 @@ h3 {
position: relative;
transition: color 150ms ease-in-out;
}
.b-icon-button--overlay.b-icon-button::before {
.b-icon-button__inner {
z-index: 1;
position: relative;
}
.b-icon-button::before {
left: -10px;
right: -10px;
top: -6px;
right: -6px;
bottom: -6px;
left: -6px;
content: "";
background-color: #F8F9FA;
border-radius: 5px;
z-index: -1;
position: absolute;
transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.b-icon-button--overlay::before {
content: "";
}
.b-icon-button--level-1:hover::before {
background-color: #F8F9FA;
}
.b-icon-button--level-2::before {
background-color: #F8F9FA;
}
.b-icon-button--overlay:hover.b-icon-button::before {
.b-icon-button--level-2:hover::before {
background-color: #dae0e5;
}
.b-icon-button--enabled, .b-icon-button--muted, .b-icon-button--idled {
......@@ -5774,6 +5784,9 @@ h3 {
.b-icon-button--enabled.b-icon-button--dark:hover, .b-icon-button--muted.b-icon-button--dark:hover {
color: #0a0c0d;
}
.b-icon-button--enabled.b-icon-button:active::before, .b-icon-button--enabled.b-icon-button--active::before, .b-icon-button--muted.b-icon-button:active::before, .b-icon-button--muted.b-icon-button--active::before {
box-shadow: 0 0 0 0.2rem rgba(233, 236, 239, 0.5);
}
.b-icon-button--idled:hover {
cursor: default;
}
......@@ -7123,6 +7136,7 @@ input[type=range]:-moz-focusring {
flex-grow: 1;
pointer-events: all;
position: relative;
content: layout;
}
.graph-layout__focus__inner {
top: 0;
......@@ -7176,6 +7190,55 @@ input[type=range]:-moz-focusring {
display: none;
}
.graph-doc-focus {
scrollbar-width: none;
overflow-y: scroll;
background-color: #fff;
height: 100%;
position: relative;
}
.graph-doc-focus::-webkit-scrollbar {
display: none;
}
.graph-doc-focus::before {
background: linear-gradient(to top, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
top: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus::after {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
bottom: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus__header {
padding: 0.75rem 1.25rem;
font-size: 20px;
}
.right-handed .graph-doc-focus__header {
float: right;
}
.left-handed .graph-doc-focus__header {
float: left;
}
.graph-doc-focus__body {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.graph-sidebar {
scrollbar-width: none;
overflow-y: scroll;
......@@ -7299,6 +7362,22 @@ input[type=range]:-moz-focusring {
.graph-doc-list__item:hover {
background-color: #FCFCFC;
}
.graph-doc-list__item--selected::before {
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;
}
.graph-doc-list__item--selected:last-child::before {
border-bottom-left-radius: 0.25rem;
}
.graph-doc-list__item__main {
flex-grow: 1;
padding-right: 1.25rem;
......@@ -7316,6 +7395,15 @@ input[type=range]:-moz-focusring {
color: #6C757D;
}
.graph-contact-list__item__title, .graph-contact-list__item__subtitle {
line-height: 1.3;
margin-bottom: 2px;
}
.graph-contact-list__item__subtitle {
font-size: 15px;
color: #495057;
}
.graph-toolbar {
display: flex;
padding: 8px;
......@@ -7647,23 +7735,26 @@ a:focus, a:hover {
visibility: visible;
}
.right-handed .mainleaf:hover .mainleaf__settings-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf:hover .mainleaf__settings-icon {
margin-right: 16px;
margin-right: 18px;
}
.right-handed .mainleaf__update-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf__update-icon {
margin-right: 16px;
margin-right: 18px;
}
.mainleaf__update-icon.b-icon-button--overlay::before, .mainleaf__settings-icon.b-icon-button--overlay::before {
.mainleaf__update-icon.b-icon-button::before, .mainleaf__settings-icon.b-icon-button::before {
top: -5px;
bottom: -5px;
left: -6px;
right: -6px;
}
.mainleaf__progress-bar {
width: 64px;
......@@ -8619,19 +8710,43 @@ select.form-control {
top: calc( 50% - 50px );
left: calc( 50% - 50px );
}
.phylo__frame {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
display: flex;
z-index: 1;
width: 100%;
height: calc(100vh - 56px);
justify-content: flex-end;
pointer-events: none;
}
.right-handed .phylo__frame {
flex-direction: row;
}
.left-handed .phylo__frame {
flex-direction: row-reverse;
}
.phylo__sidebar {
position: fixed;
width: 480px;
height: calc(100vh - 56px);
z-index: 1;
height: inherit;
flex-grow: 0;
pointer-events: all;
}
.right-handed .phylo__sidebar {
right: 0;
.phylo__sidebar__inner {
position: fixed;
height: inherit;
width: inherit;
}
.right-handed .phylo__sidebar__inner {
border-left: 1px solid #dee2e6;
}
.left-handed .phylo__sidebar {
left: 0;
.left-handed .phylo__sidebar__inner {
border-right: 1px solid #dee2e6;
}
......
......@@ -5959,19 +5959,29 @@ h3 {
position: relative;
transition: color 150ms ease-in-out;
}
.b-icon-button--overlay.b-icon-button::before {
.b-icon-button__inner {
z-index: 1;
position: relative;
}
.b-icon-button::before {
left: -10px;
right: -10px;
top: -6px;
right: -6px;
bottom: -6px;
left: -6px;
content: "";
background-color: #F8F9FA;
border-radius: 5px;
z-index: -1;
position: absolute;
transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.b-icon-button--overlay::before {
content: "";
}
.b-icon-button--level-1:hover::before {
background-color: #F8F9FA;
}
.b-icon-button--level-2::before {
background-color: #F8F9FA;
}
.b-icon-button--overlay:hover.b-icon-button::before {
.b-icon-button--level-2:hover::before {
background-color: #dae0e5;
}
.b-icon-button--enabled, .b-icon-button--muted, .b-icon-button--idled {
......@@ -6022,6 +6032,9 @@ h3 {
.b-icon-button--enabled.b-icon-button--dark:hover, .b-icon-button--muted.b-icon-button--dark:hover {
color: #0a0c0d;
}
.b-icon-button--enabled.b-icon-button:active::before, .b-icon-button--enabled.b-icon-button--active::before, .b-icon-button--muted.b-icon-button:active::before, .b-icon-button--muted.b-icon-button--active::before {
box-shadow: 0 0 0 0.2rem rgba(233, 236, 239, 0.5);
}
.b-icon-button--idled:hover {
cursor: default;
}
......@@ -7371,6 +7384,7 @@ input[type=range]:-moz-focusring {
flex-grow: 1;
pointer-events: all;
position: relative;
content: layout;
}
.graph-layout__focus__inner {
top: 0;
......@@ -7424,6 +7438,55 @@ input[type=range]:-moz-focusring {
display: none;
}
.graph-doc-focus {
scrollbar-width: none;
overflow-y: scroll;
background-color: #fff;
height: 100%;
position: relative;
}
.graph-doc-focus::-webkit-scrollbar {
display: none;
}
.graph-doc-focus::before {
background: linear-gradient(to top, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
top: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus::after {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
bottom: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus__header {
padding: 0.75rem 1.25rem;
font-size: 20px;
}
.right-handed .graph-doc-focus__header {
float: right;
}
.left-handed .graph-doc-focus__header {
float: left;
}
.graph-doc-focus__body {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.graph-sidebar {
scrollbar-width: none;
overflow-y: scroll;
......@@ -7547,6 +7610,22 @@ input[type=range]:-moz-focusring {
.graph-doc-list__item:hover {
background-color: #FCFCFC;
}
.graph-doc-list__item--selected::before {
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;
}
.graph-doc-list__item--selected:last-child::before {
border-bottom-left-radius: 0.25rem;
}
.graph-doc-list__item__main {
flex-grow: 1;
padding-right: 1.25rem;
......@@ -7564,6 +7643,15 @@ input[type=range]:-moz-focusring {
color: #6C757D;
}
.graph-contact-list__item__title, .graph-contact-list__item__subtitle {
line-height: 1.3;
margin-bottom: 2px;
}
.graph-contact-list__item__subtitle {
font-size: 15px;
color: #495057;
}
.graph-toolbar {
display: flex;
padding: 8px;
......@@ -7895,23 +7983,26 @@ a:focus, a:hover {
visibility: visible;
}
.right-handed .mainleaf:hover .mainleaf__settings-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf:hover .mainleaf__settings-icon {
margin-right: 16px;
margin-right: 18px;
}
.right-handed .mainleaf__update-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf__update-icon {
margin-right: 16px;
margin-right: 18px;
}
.mainleaf__update-icon.b-icon-button--overlay::before, .mainleaf__settings-icon.b-icon-button--overlay::before {
.mainleaf__update-icon.b-icon-button::before, .mainleaf__settings-icon.b-icon-button::before {
top: -5px;
bottom: -5px;
left: -6px;
right: -6px;
}
.mainleaf__progress-bar {
width: 64px;
......@@ -8867,19 +8958,43 @@ select.form-control {
top: calc( 50% - 50px );
left: calc( 50% - 50px );
}
.phylo__frame {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
display: flex;
z-index: 1;
width: 100%;
height: calc(100vh - 56px);
justify-content: flex-end;
pointer-events: none;
}
.right-handed .phylo__frame {
flex-direction: row;
}
.left-handed .phylo__frame {
flex-direction: row-reverse;
}
.phylo__sidebar {
position: fixed;
width: 480px;
height: calc(100vh - 56px);
z-index: 1;
height: inherit;
flex-grow: 0;
pointer-events: all;
}
.right-handed .phylo__sidebar {
right: 0;
.phylo__sidebar__inner {
position: fixed;
height: inherit;
width: inherit;
}
.right-handed .phylo__sidebar__inner {
border-left: 1px solid #dee2e6;
}
.left-handed .phylo__sidebar {
left: 0;
.left-handed .phylo__sidebar__inner {
border-right: 1px solid #dee2e6;
}
......
......@@ -5960,19 +5960,29 @@ h3 {
position: relative;
transition: color 150ms ease-in-out;
}
.b-icon-button--overlay.b-icon-button::before {
.b-icon-button__inner {
z-index: 1;
position: relative;
}
.b-icon-button::before {
left: -10px;
right: -10px;
top: -6px;
right: -6px;
bottom: -6px;
left: -6px;
content: "";
background-color: #F8F9FA;
border-radius: 5px;
z-index: -1;
position: absolute;
transition: background-color 150ms ease-in-out;
transition: background-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.b-icon-button--overlay::before {
content: "";
}
.b-icon-button--level-1:hover::before {
background-color: #F8F9FA;
}
.b-icon-button--level-2::before {
background-color: #F8F9FA;
}
.b-icon-button--overlay:hover.b-icon-button::before {
.b-icon-button--level-2:hover::before {
background-color: #dae0e5;
}
.b-icon-button--enabled, .b-icon-button--muted, .b-icon-button--idled {
......@@ -6023,6 +6033,9 @@ h3 {
.b-icon-button--enabled.b-icon-button--dark:hover, .b-icon-button--muted.b-icon-button--dark:hover {
color: #0a0c0d;
}
.b-icon-button--enabled.b-icon-button:active::before, .b-icon-button--enabled.b-icon-button--active::before, .b-icon-button--muted.b-icon-button:active::before, .b-icon-button--muted.b-icon-button--active::before {
box-shadow: 0 0 0 0.2rem rgba(233, 236, 239, 0.5);
}
.b-icon-button--idled:hover {
cursor: default;
}
......@@ -7372,6 +7385,7 @@ input[type=range]:-moz-focusring {
flex-grow: 1;
pointer-events: all;
position: relative;
content: layout;
}
.graph-layout__focus__inner {
top: 0;
......@@ -7425,6 +7439,55 @@ input[type=range]:-moz-focusring {
display: none;
}
.graph-doc-focus {
scrollbar-width: none;
overflow-y: scroll;
background-color: #fff;
height: 100%;
position: relative;
}
.graph-doc-focus::-webkit-scrollbar {
display: none;
}
.graph-doc-focus::before {
background: linear-gradient(to top, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
top: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus::after {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, white 45%);
content: "";
z-index: 1;
pointer-events: none;
position: sticky;
bottom: 0;
height: 16px;
width: 100%;
display: block;
}
.graph-doc-focus__header {
padding: 0.75rem 1.25rem;
font-size: 20px;
}
.right-handed .graph-doc-focus__header {
float: right;
}
.left-handed .graph-doc-focus__header {
float: left;
}
.graph-doc-focus__body {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.graph-sidebar {
scrollbar-width: none;
overflow-y: scroll;
......@@ -7548,6 +7611,22 @@ input[type=range]:-moz-focusring {
.graph-doc-list__item:hover {
background-color: #FCFCFC;
}
.graph-doc-list__item--selected::before {
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;
}
.graph-doc-list__item--selected:last-child::before {
border-bottom-left-radius: 0.25rem;
}
.graph-doc-list__item__main {
flex-grow: 1;
padding-right: 1.25rem;
......@@ -7565,6 +7644,15 @@ input[type=range]:-moz-focusring {
color: #6C757D;
}
.graph-contact-list__item__title, .graph-contact-list__item__subtitle {
line-height: 1.3;
margin-bottom: 2px;
}
.graph-contact-list__item__subtitle {
font-size: 15px;
color: #495057;
}
.graph-toolbar {
display: flex;
padding: 8px;
......@@ -7896,23 +7984,26 @@ a:focus, a:hover {
visibility: visible;
}
.right-handed .mainleaf:hover .mainleaf__settings-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf:hover .mainleaf__settings-icon {
margin-right: 16px;
margin-right: 18px;
}
.right-handed .mainleaf__update-icon {
margin-left: 16px;
margin-left: 18px;
}
.left-handed .mainleaf__update-icon {
margin-right: 16px;
margin-right: 18px;
}
.mainleaf__update-icon.b-icon-button--overlay::before, .mainleaf__settings-icon.b-icon-button--overlay::before {
.mainleaf__update-icon.b-icon-button::before, .mainleaf__settings-icon.b-icon-button::before {
top: -5px;
bottom: -5px;
left: -6px;
right: -6px;
}
.mainleaf__progress-bar {
width: 64px;
......@@ -8868,19 +8959,43 @@ select.form-control {
top: calc( 50% - 50px );
left: calc( 50% - 50px );
}
.phylo__frame {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
display: flex;
z-index: 1;
width: 100%;
height: calc(100vh - 56px);
justify-content: flex-end;
pointer-events: none;
}
.right-handed .phylo__frame {
flex-direction: row;
}
.left-handed .phylo__frame {
flex-direction: row-reverse;
}
.phylo__sidebar {
position: fixed;
width: 480px;
height: calc(100vh - 56px);
z-index: 1;
height: inherit;
flex-grow: 0;
pointer-events: all;
}
.right-handed .phylo__sidebar {
right: 0;
.phylo__sidebar__inner {
position: fixed;
height: inherit;
width: inherit;
}
.right-handed .phylo__sidebar__inner {
border-left: 1px solid #dee2e6;
}
.left-handed .phylo__sidebar {
left: 0;
.left-handed .phylo__sidebar__inner {
border-right: 1px solid #dee2e6;
}
......
{
"name": "Gargantext",
"version": "0.0.5.8.7",
"version": "0.0.5.8.8.2",
"scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash",
......
module Gargantext.Components.App (app) where
import Reactix as R
import Toestand as T
import Gargantext.Prelude
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (emptyApp)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Router (router)
import Gargantext.Hooks (useHashRouter)
import Gargantext.Router as Router
import Gargantext.Sessions as Sessions
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.App"
......@@ -22,8 +21,7 @@ app = R.createElement appCpt
appCpt :: R.Component ()
appCpt = here.component "app" cpt where
cpt _ _ = do
box <- T.useBox emptyApp -- global data
boxes <- T.useFocusedFields box {} -- read-write access for children
boxes <- AppStore.use
-- tasks <- T.useBox Nothing -- storage for asynchronous tasks reductor
R.useEffectOnce' $ do
void $ Sessions.load boxes.sessions
......
module Gargantext.Components.App.Data (App, Boxes, emptyApp) where
module Gargantext.Components.App.Store
( Store
, State
, options
, context
, provide
, use
-- legacy
, Boxes
) where
import Gargantext.Prelude
......@@ -16,11 +25,43 @@ import Gargantext.Sessions (Session, Sessions)
import Gargantext.Sessions as Sessions
import Gargantext.Sessions.Types (OpenNodes(..))
import Gargantext.Types (FrontendError, Handed(RightHanded), SidePanelState(..))
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Toestand as T
import Unsafe.Coerce (unsafeCoerce)
type App =
{ backend :: Maybe Backend
here :: R2.Here
here = R2.here "Gargantext.Components.App.Store"
type Store =
( backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsT.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
)
type State =
( backend :: Maybe Backend
, errors :: Array FrontendError
, forestOpen :: OpenNodes
, graphVersion :: T2.Reload
......@@ -42,10 +83,10 @@ type App =
, theme :: Themes.Theme
, tileAxisXList :: Array (Record Tile)
, tileAxisYList :: Array (Record Tile)
}
)
emptyApp :: App
emptyApp =
options :: Record State
options =
{ backend : Nothing
, errors : []
, forestOpen : OpenNodes $ Set.empty
......@@ -70,27 +111,15 @@ emptyApp =
, tileAxisYList : mempty
}
type Boxes =
{ backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsT.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
}
context :: R.Context (Record Store)
context = R.createContext $ unsafeCoerce unit
provide :: Record State -> Array R.Element -> R.Element
provide values = Stores.provideStore here.name values context
use :: R.Hooks (Record Store)
use = Stores.useStore context
------------------------------------------------------
type Boxes = Record Store
......@@ -4,7 +4,7 @@ import Gargantext.Prelude
import Data.Foldable (elem, intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..))
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), Variant(..))
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import React.SyntheticEvent as SE
......@@ -22,6 +22,7 @@ type Options =
, status :: ComponentStatus
, title :: String
, overlay :: Boolean
, elevation :: Elevation
, variant :: Variant
)
......@@ -30,7 +31,8 @@ options =
{ className : ""
, status : Enabled
, title : ""
, overlay : false
, overlay : true
, elevation : Level0
, variant : Dark
}
......@@ -50,7 +52,8 @@ component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, name } _ = do
, name
} _ = do
-- Computed
let
wrapperClassName = intercalate " "
......@@ -63,6 +66,7 @@ component = R.hooksComponent componentName cpt where
, props.overlay ?
componentName <> "--overlay" $
""
, componentName <> "--" <> show props.elevation
]
contentClassName = intercalate " "
......@@ -82,11 +86,15 @@ component = R.hooksComponent componentName cpt where
, disabled: elem status [ Disabled, Deferred ]
}
[
H.i
{ title: props.title
, className: contentClassName
}
[]
H.span
{ className: componentName <> "__inner" }
[
H.i
{ title: props.title
, className: contentClassName
}
[]
]
]
-- | Clicked event will effectively be triggered according to the
......
......@@ -5,6 +5,7 @@ module Gargantext.Components.Bootstrap.Types
, SpinnerTheme(..)
, TooltipEffect(..), TooltipPosition(..)
, Position(..)
, Elevation(..)
) where
import Gargantext.Prelude
......@@ -150,7 +151,7 @@ instance Show Position where show = kebabCase <<< genericShow
-- | Position used on React Tooltip
-- |
-- | -- | https://github.com/wwayne/react-tooltip#options
-- | https://github.com/wwayne/react-tooltip#options
data TooltipPosition
= TooltipPosition Position
| AutomaticPosition
......@@ -160,3 +161,18 @@ derive instance Eq TooltipPosition
instance Show TooltipPosition where
show (TooltipPosition a) = (kebabCase <<< genericShow) a
show AutomaticPosition = ""
----------------------------------------------------------------------
-- | Elevarion measure scale values used on various custom components
-- | and properties
-- |
-- | Example: https://material.io/design/environment/elevation.html
data Elevation
= Level0
| Level1
| Level2
derive instance Generic Elevation _
derive instance Eq Elevation
instance Show Elevation where show = kebabCase <<< genericShow
......@@ -25,7 +25,7 @@ import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Effect.Timer (setTimeout)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Category (rating)
......
......@@ -9,7 +9,7 @@ import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action.Add (AddNodeValue(..), addNode)
import Gargantext.Components.Forest.Tree.Node.Action.Contact as Contact
import Gargantext.Components.Forest.Tree.Node.Action.Delete (deleteNode, unpublishNode)
......@@ -227,7 +227,7 @@ backButtonCpt = here.component "backButton" cpt where
cpt _ _ = do
{ goToPreviousPage } <- useLinkHandler
pure $
pure $
H.button {
className: "btn btn-primary"
, on: { click: \_ -> goToPreviousPage unit }
......
......@@ -8,7 +8,7 @@ import Gargantext.Prelude
import Data.Array as A
import Data.Maybe (Maybe(..))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Position(..), TooltipPosition(..), Variant(..))
import Gargantext.Components.Forest.Tree (treeLoader)
......
......@@ -11,7 +11,7 @@ import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Forest.Tree.Node (blankNodeSpan, nodeSpan)
import Gargantext.Components.Forest.Tree.Node.Action.Add (AddNodeValue(..), addNode)
......
......@@ -16,9 +16,9 @@ import Effect (Effect)
import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), TooltipEffect(..), Variant(..))
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), TooltipEffect(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fileTypeView)
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..))
......@@ -291,6 +291,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt
"check-circle" $
"exclamation-circle"
, callback: const $ T.modify_ (not) folderOpen
, overlay: false
}
]
,
......@@ -331,7 +332,7 @@ nodeSpanCpt = here.component "nodeSpan" cpt
"Each node of the Tree can perform some actions.\n"
<> "Click here to execute one of them."
, variant: Secondary
, overlay: true
, elevation: Level1
}
,
nodePopupView
......@@ -390,6 +391,7 @@ nodeIconCpt = here.component "nodeIcon" cpt where
, callback
, status: isLeaf ? Idled $ Enabled
, variant: isSelected ? Primary $ Dark
, overlay: false
}
]
<> children
......@@ -421,6 +423,7 @@ folderIconCpt = here.component "folderIcon" cpt where
B.iconButton
{ className: "mainleaf__folder-icon"
, name: isOpened ? "caret-down" $ "caret-right"
, overlay: false
, callback
}
......
......@@ -5,7 +5,7 @@ import Gargantext.Prelude
import Data.Maybe (Maybe)
import Effect (Effect)
import Effect.Aff (Aff, launchAff)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchBar (searchBar)
import Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField (defaultSearch)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
......
module Gargantext.Components.Forest.Tree.Node.Action.Update where
import Gargantext.Components.Forest.Tree.Node.Action.Update.Types
import Gargantext.Prelude
import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (Charts(..), Granularity(..), GraphMetric(..), Method(..), PartitionMethod(..), UpdateNodeParams(..))
import DOM.Simple.Console (log3)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
......@@ -80,6 +81,10 @@ updateGraphCpt = here.component "updateGraph" cpt where
methodGraphClustering <- T.useBox Spinglass
methodGraphClustering' <- T.useLive T.unequal methodGraphClustering
let
callback :: Action -> Aff Unit
callback = dispatch >=> \_ -> dispatch ClosePopover
pure $ panel [ -- H.text "Update with"
formChoiceSafe { items: [Order1, Order2]
, default: methodGraphMetric'
......@@ -94,7 +99,7 @@ updateGraphCpt = here.component "updateGraph" cpt where
(submitButton (UpdateNode $ UpdateNodeParamsGraph { methodGraphMetric: methodGraphMetric'
, methodGraphClustering: methodGraphClustering'
}
) dispatch
) callback
)
......@@ -135,7 +140,9 @@ updatePhyloCpt = here.component "updatePhylo" cpt where
Left error -> log3 "[handleFormError]" error r
Right r' -> do
opts <- pure $ options r'
launchAff_ $ dispatch opts
launchAff_ do
dispatch opts
dispatch ClosePopover
where
options :: Phylo.UpdateData -> Action
......
......@@ -5,7 +5,7 @@ import Gargantext.Prelude
import Data.Either (Either)
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools (panel, submitButton)
import Gargantext.Config.REST (AffRESTError, RESTError)
......
......@@ -9,7 +9,7 @@ import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action.Add (addNodeView)
import Gargantext.Components.Forest.Tree.Node.Action.Contact as Contact
import Gargantext.Components.Forest.Tree.Node.Action.Delete (actionDelete)
......
......@@ -4,7 +4,7 @@ import DOM.Simple as DOM
import Data.Maybe (Maybe)
import Effect (Effect)
import Effect.Aff (Aff)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.Forest.Tree.Node.Settings (NodeAction)
import Gargantext.Prelude (Unit)
......@@ -32,5 +32,3 @@ type NodePopupS =
, name :: Name
, nodeType :: GT.NodeType
)
......@@ -7,7 +7,7 @@ import Data.Array as A
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Forest.Tree.Node.Action (Props, subTreeOut, setTreeOut)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.Forest.Tree.Node.Tools.FTree (FTree, LNode(..), NTree(..))
......
......@@ -6,7 +6,7 @@ import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Variant(..))
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), Variant(..))
import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Sessions (Session)
import Gargantext.Types as GT
......@@ -62,7 +62,7 @@ graphUpdateButtonCpt = here.component "graphUpdateButton" cpt
B.iconButton
{ className: "mainleaf__update-icon"
, variant: Secondary
, overlay: true
, elevation: Level1
, status: enabled' ? Enabled $ Disabled
, callback: const $ onClick enabled' enabled
, name: "refresh"
......
......@@ -5,6 +5,9 @@ module Gargantext.Components.GraphExplorer.Frame.DocFocus
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.GraphExplorer.Types (GraphSideDoc(..))
import Gargantext.Components.Nodes.Corpus.Document (documentMainLayout)
import Gargantext.Sessions (Session)
......@@ -19,6 +22,7 @@ here = R2.here "Gargantext.Components.GraphExplorer.Frame.DocFocus"
type Props =
( graphSideDoc :: GraphSideDoc
, session :: Session
, closeCallback :: Unit -> Effect Unit
)
docFocus :: R2.Leaf Props
......@@ -28,6 +32,7 @@ docFocusCpt :: R.Component Props
docFocusCpt = here.component "main" cpt where
cpt { graphSideDoc: GraphSideDoc { docId, listId, corpusId }
, session
, closeCallback
} _ = do
......@@ -36,11 +41,22 @@ docFocusCpt = here.component "main" cpt where
pure $
H.div
{ className: "graph-layout__focus" }
{ className: "graph-doc-focus" }
[
H.div
{ className: "graph-layout__focus__inner" }
{ className: "graph-doc-focus__header" }
[
B.iconButton
{ name: "times"
, elevation: Level2
, callback: closeCallback
}
]
,
H.div
{ className: "graph-doc-focus__body" }
[
-- print the document node
documentMainLayout
{ listId
, mCorpusId: Just corpusId
......
......@@ -12,7 +12,8 @@ import Data.Nullable (null, Nullable)
import Data.Sequence as Seq
import Data.Set as Set
import Data.Tuple (Tuple(..))
import Gargantext.Components.App.Data (Boxes)
import Effect (Effect)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.Frame.DocFocus (docFocus)
import Gargantext.Components.GraphExplorer.Resources as Graph
......@@ -24,27 +25,25 @@ import Gargantext.Components.GraphExplorer.Types (GraphSideDoc)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config (defaultFrontends)
import Gargantext.Data.Louvain as Louvain
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Sessions (Session)
import Gargantext.Types as GT
import Gargantext.Types as Types
import Gargantext.Utils ((?))
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Math as Math
import Partial.Unsafe (unsafePartial)
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Layout"
type Props =
( session :: Session
, boxes :: Boxes
, sigmaRef :: R.Ref Sigmax.Sigma
( sigmaRef :: R.Ref Sigmax.Sigma
)
layout :: R2.Leaf Props
......@@ -52,19 +51,21 @@ layout = R2.leaf layoutCpt
layoutCpt :: R.Memo Props
layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
cpt props@{ boxes
, session
, sigmaRef
} _ = do
cpt { sigmaRef
} _ = do
-- | States
-- |
{ reloadForest
} <- AppStore.use
{ showSidebar
, showDoc
, mMetaData
, showControls
, graphId
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
session <- useSession
showSidebar' <- R2.useLive' showSidebar
showDoc' <- R2.useLive' showDoc
......@@ -103,6 +104,12 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
-- T.write_ Graph.Init controls.graphStage
-- T.write_ Types.InitialClosed controls.sidePanelState
-- | Computed
-- |
let
closeDoc :: Unit -> Effect Unit
closeDoc _ = T.write_ Nothing showDoc
-- | Render
-- |
......@@ -128,12 +135,19 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
-- Doc focus
R2.fromMaybe_ showDoc' \(graphSideDoc :: GraphSideDoc) ->
docFocus
{ session
, graphSideDoc
}
H.div
{ className: "graph-layout__focus" }
[
H.div
{ className: "graph-layout__focus__inner" }
[
docFocus
{ session
, graphSideDoc
, closeCallback: closeDoc
}
]
]
,
-- Sidebar
H.div
......@@ -155,8 +169,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
Just metaData ->
GES.sidebar
{ boxes
, frontends: defaultFrontends
{ frontends: defaultFrontends
, metaData
, session
}
......@@ -173,7 +186,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
}
[
Controls.controls
{ reloadForest: boxes.reloadForest
{ reloadForest: reloadForest
, session
, sigmaRef
}
......@@ -186,8 +199,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
}
[
graphView
{ boxes: props.boxes
, elRef: graphRef
{ elRef: graphRef
, sigmaRef
}
]
......@@ -196,8 +208,7 @@ layoutCpt = R.memo' $ here.component "explorerWriteGraph" cpt where
--------------------------------------------------------------
type GraphProps =
( boxes :: Boxes
, elRef :: R.Ref (Nullable Element)
( elRef :: R.Ref (Nullable Element)
, sigmaRef :: R.Ref Sigmax.Sigma
)
......@@ -205,8 +216,7 @@ graphView :: R2.Leaf GraphProps
graphView = R2.leaf graphViewCpt
graphViewCpt :: R.Memo GraphProps
graphViewCpt = R.memo' $ here.component "graphView" cpt where
cpt { boxes
, elRef
cpt { elRef
, sigmaRef
} _ = do
-- | States
......@@ -219,7 +229,7 @@ graphViewCpt = R.memo' $ here.component "graphView" cpt where
, showEdges
, showLouvain
, graph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
edgeConfluence' <- R2.useLive' edgeConfluence
edgeWeight' <- R2.useLive' edgeWeight
......@@ -254,8 +264,7 @@ graphViewCpt = R.memo' $ here.component "graphView" cpt where
pure $
Graph.drawGraph
{ boxes
, elRef
{ elRef
, forceAtlas2Settings: Graph.forceAtlas2Settings
, sigmaRef
, sigmaSettings: Graph.sigmaSettings
......@@ -314,10 +323,10 @@ convert (GET.GraphData r) = Tuple r.metaData $ SigmaxT.Graph {nodes, edges}
-- | See sigmajs/plugins/sigma.renderers.customShapes/shape-library.js
modeGraphType :: Types.Mode -> String
modeGraphType Types.Authors = "square"
modeGraphType Types.Institutes = "equilateral"
modeGraphType Types.Sources = "star"
modeGraphType Types.Terms = "def"
modeGraphType Types.Authors = "square"
modeGraphType Types.Institutes = "equilateral"
modeGraphType Types.Sources = "star"
modeGraphType Types.Terms = "def"
--------------------------------------------------------------
......
......@@ -12,7 +12,7 @@ import DOM.Simple.Types (Element)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.Themes (darksterTheme)
......@@ -22,7 +22,6 @@ import Gargantext.Hooks.Sigmax.Sigma as Sigma
import Gargantext.Hooks.Sigmax.Types as SigmaxTypes
import Gargantext.Utils (getter)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Reactix as R
import Record (merge)
import Toestand as T
......@@ -31,8 +30,7 @@ here :: R2.Here
here = R2.here "Gargantext.Components.Graph"
type Props sigma forceatlas2 =
( boxes :: Boxes
, elRef :: R.Ref (Nullable Element)
( elRef :: R.Ref (Nullable Element)
, forceAtlas2Settings :: forceatlas2
, sigmaRef :: R.Ref Sigmax.Sigma
, sigmaSettings :: sigma
......@@ -48,11 +46,12 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
-- |
cpt { elRef
, sigmaRef
, boxes
, forceAtlas2Settings: fa2
, transformedGraph
} _ = do
boxes <- AppStore.use
{ showEdges
, graphStage
, graph
......@@ -60,14 +59,13 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
, selectedNodeIds
, multiSelectEnabled
, hyperdataGraph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
showEdges' <- R2.useLive' showEdges
graphStage' <- R2.useLive' graphStage
graph' <- R2.useLive' graph
startForceAtlas' <- R2.useLive' startForceAtlas
hyperdataGraph' <- R2.useLive' hyperdataGraph
selectedNodeIds' <- R2.useLive' selectedNodeIds
-- | Hooks
-- |
......@@ -144,9 +142,12 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
_ -> pure unit
-- Stage ready
-- (?) Probably this can be optimized to re-mark selected nodes only when
-- they changed → done on #375
R.useEffect1' selectedNodeIds' case graphStage' of
--
-- @TODO Probably this can be optimized to re-mark selected nodes only when
-- they changed → one solution could be to list every effects subject
-- to a graph transformation (eg. "showLouvain", "edgeConfluence",
-- etc) // drawback: don't forget to modify the effect white-list
R.useEffect' case graphStage' of
GET.Ready -> do
let tEdgesMap = SigmaxTypes.edgesGraphMap transformedGraph
......@@ -157,7 +158,7 @@ drawGraphCpt = R.memo' $ here.component "graph" cpt where
Sigmax.updateEdges sigma tEdgesMap
Sigmax.updateNodes sigma tNodesMap
let edgesState = not $ SigmaxTypes.edgeStateHidden showEdges'
here.log2 "[graphCpt] edgesState" edgesState
-- here.log2 "[graphCpt] edgesState" edgesState
Sigmax.setEdges sigma edgesState
_ -> pure unit
......
module Gargantext.Components.GraphExplorer.Sidebar.ContactList
( contactListWrapper
) where
import Gargantext.Prelude
import Data.Array (concat, head)
import Data.Foldable (intercalate)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Sequence as Seq
import Data.Set as Set
import Data.Tuple.Nested ((/\))
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.FacetsTable (ContactsView(..), Rows(..), initialPagePath, loadPage)
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types (CorpusId, ListId)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.RandomText (words)
import Gargantext.Components.Search (HyperdataRowContact(..), SearchQuery(..), SearchType(..))
import Gargantext.Config (defaultFrontends)
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.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId)
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar.ContactList"
type Props =
( metaData :: GET.MetaData
)
contactListWrapper :: R2.Leaf Props
contactListWrapper = R2.leaf contactListWrapperCpt
contactListWrapperCpt :: R.Component Props
contactListWrapperCpt = here.component "wrapper" cpt where
cpt { metaData: GET.MetaData metaData
} _ = do
-- | States
-- |
session <- useSession
{ graph
, selectedNodeIds
} <- GraphStore.use
graph' <- R2.useLive' graph
selectedNodeIds' <- R2.useLive' selectedNodeIds
query' /\ query <- R2.useBox' Nothing
-- | Helpers
-- |
let
frontends = defaultFrontends
nodesMap = SigmaxT.nodesGraphMap graph'
toSearchQuery ids = SearchQuery
{ expected: SearchContact
, query: concat $ toQuery <$> Set.toUnfoldable ids
}
toQuery id = case Map.lookup id nodesMap of
Nothing -> []
Just n -> words n.label
-- | Hooks
-- |
R.useEffect1' selectedNodeIds' $
T.write_ (selectedNodeIds' # toSearchQuery >>> Just) query
-- | Render
-- |
pure $
R.fragment
[
case (head metaData.corpusId) /\ query' of
(Just corpusId) /\ (Just q') ->
contactList
{ frontends
, query: q'
, session
, corpusId
, listId: metaData.list.listId
}
_ /\ _ ->
B.caveat
{}
[
H.text "You can link an annuaire to retrieve relative contacts about your selection"
]
]
-------------------------------------------------------------------
type ListProps =
( query :: SearchQuery
, corpusId :: CorpusId
, listId :: ListId
, frontends :: Frontends
, session :: Session
)
contactList :: R2.Leaf ListProps
contactList = R2.leaf contactListCpt
contactListCpt :: R.Component ListProps
contactListCpt = here.component "main" cpt where
-- | Helpers
-- |
errorHandler err = do
here.warn2 "[pageLayout] RESTError" err
case err of
ReadJSONError err' ->
here.warn2 "[pageLayout] ReadJSONError" $ show err'
_ -> pure unit
-- | Component
-- |
cpt { frontends
, query
, session
, corpusId: nodeId
, listId
} _ = do
-- | States
-- |
path' /\ path
<- R2.useBox' $ initialPagePath { nodeId, listId, query, session }
state' /\ state <-
R2.useBox' Nothing
rows' /\ rows <-
R2.useBox' Nothing
-- | Hooks
-- |
useLoaderEffect
{ errorHandler
, state
, loader: loadPage
, path: path'
}
-- | Effects
-- |
-- (on query change, reload fetched docs)
useUpdateEffect1' query $
flip T.write_ path $ initialPagePath { nodeId, listId, query, session }
-- (on fetch success, extract existing docs)
useUpdateEffect1' state' case state' of
Nothing -> T.write_ (Just Seq.empty) rows
Just r -> case r of
Contacts { contacts } -> T.write_ (Just contacts) rows
_ -> T.write_ (Just Seq.empty) rows
-- | Render
-- |
pure $
R2.fromMaybe_ rows' \results ->
R.fragment
[
R2.if' (results == Seq.empty) $
B.caveat
{}
[
H.text "No contact found in your corpus for your selected terms"
]
,
R2.if' (not $ eq results Seq.empty) $
H.ul
{ className: intercalate " "
[ "graph-contact-list"
, "list-group"
]
} $
Seq.toUnfoldable $ flip Seq.map results \r ->
item
{ frontends
, session
, contactView: (r :: ContactsView)
}
]
---------------------------------------------------------
type ItemProps =
( contactView :: ContactsView
, frontends :: Frontends
, session :: Session
)
item :: R2.Leaf ItemProps
item = R2.leaf itemCpt
itemCpt :: R.Component ItemProps
itemCpt = here.component "item" cpt where
cpt { contactView: ContactsView
{ id
, annuaireId
, hyperdata: HyperdataRowContact
{ firstname
, lastname
, labs
}
}
, frontends
, session
} _ = do
-- Computed
let
-- Creating a href link
contactUrl id'
= url frontends $ Routes.ContactPage (sessionId session) annuaireId id'
-- Render
pure $
H.div
{ className: intercalate " "
[ "graph-contact-list__item"
, "list-group-item"
]
}
[
H.a
{ className: "graph-contact-list__item__title"
, target: "_blank"
, href: contactUrl id
}
[
H.text $ firstname
,
H.text $ nbsp 1
,
H.text $ lastname
]
,
B.div'
{ className: "graph-contact-list__item__subtitle" }
labs
]
module Gargantext.Components.GraphExplorer.Sidebar.DocList
( docList
( docListWrapper
) where
import Gargantext.Prelude
import Data.Array (concat, head)
import Data.Foldable (intercalate)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Sequence as Seq
import Data.Set as Set
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (Variant(..))
import Gargantext.Components.FacetsTable (DocumentsView(..), PagePath, Rows(..), initialPagePath, loadPage, publicationDate)
import Gargantext.Components.GraphExplorer.Types (GraphSideCorpus(..), GraphSideDoc(..), DocId)
import Gargantext.Components.Search (SearchQuery)
import Gargantext.Components.FacetsTable (DocumentsView(..), Rows(..), initialPagePath, loadPage, publicationDate)
import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types (CorpusId, DocId, GraphSideDoc(..), ListId)
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Components.RandomText (words)
import Gargantext.Components.Search (SearchQuery(..), SearchType(..))
import Gargantext.Config.REST (RESTError(..))
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Sessions (Session)
import Gargantext.Utils ((?))
......@@ -28,18 +35,90 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar.DocList"
type TabsProps =
( frontends :: Frontends
, query :: SearchQuery
type Props =
( metaData :: GET.MetaData
)
docListWrapper :: R2.Leaf Props
docListWrapper = R2.leaf docListWrapperCpt
docListWrapperCpt :: R.Component Props
docListWrapperCpt = here.component "wrapper" cpt where
cpt { metaData: GET.MetaData metaData
} _ = do
-- | States
-- |
session <- useSession
{ showDoc
, graph
, selectedNodeIds
} <- GraphStore.use
graph' <- R2.useLive' graph
selectedNodeIds' <- R2.useLive' selectedNodeIds
query' /\ query <- R2.useBox' Nothing
-- | Helpers
-- |
let
nodesMap = SigmaxT.nodesGraphMap graph'
toSearchQuery ids = SearchQuery
{ expected: SearchDoc
, query: concat $ toQuery <$> Set.toUnfoldable ids
}
toQuery id = case Map.lookup id nodesMap of
Nothing -> []
Just n -> words n.label
-- | Hooks
-- |
R.useEffect1' selectedNodeIds' $
T.write_ (selectedNodeIds' # toSearchQuery >>> Just) query
-- | Render
-- |
pure $
R.fragment
[
case (head metaData.corpusId) /\ query' of
(Just corpusId) /\ (Just q') ->
docList
{ query: q'
, session
, corpusId
, listId: metaData.list.listId
, showDoc
}
_ /\ _ ->
B.caveat
{}
[
H.text "You can link a corpus to retrieve relative documents about your selection"
]
]
-------------------------------------------------------------------
type ListProps =
( query :: SearchQuery
, corpusId :: CorpusId
, listId :: ListId
, session :: Session
, graphSideCorpus :: GraphSideCorpus
, showDoc :: T.Box (Maybe GraphSideDoc)
)
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
-- |
......@@ -51,13 +130,10 @@ docListCpt = here.component "main" cpt where
_ -> pure unit
-- | Component
-- |
cpt { frontends
, query
cpt { query
, session
, graphSideCorpus: GraphSideCorpus
{ corpusId: nodeId
, listId
}
, corpusId: nodeId
, listId
, showDoc
} _ = do
-- | States
......@@ -146,7 +222,7 @@ 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.if' (not $ eq results Seq.empty) $
......@@ -160,10 +236,7 @@ docListCpt = here.component "main" cpt where
Seq.toUnfoldable $ flip Seq.map results \r ->
item
{ frontends
, path: path'
, session
, documentView: (r :: DocumentsView)
{ documentView: (r :: DocumentsView)
, callback: callback showDoc'
, isSelected: isSelected showDoc' (r :: DocumentsView)
}
......@@ -174,9 +247,6 @@ docListCpt = here.component "main" cpt where
type ItemProps =
( documentView :: DocumentsView
, frontends :: Frontends
, session :: Session
, path :: PagePath
, callback :: DocId -> Effect Unit
, isSelected :: Boolean
)
......@@ -189,9 +259,6 @@ itemCpt = here.component "item" cpt where
cpt { documentView: dv@(DocumentsView { id, title, source })
, callback
, isSelected
-- , frontends
-- , path
-- , session
} _ = do
-- Computed
-- let
......@@ -199,13 +266,13 @@ itemCpt = here.component "item" cpt where
-- documentUrl id' { listId, nodeId } =
-- url frontends $ Routes.CorpusDocument (sessionId session) nodeId listId id'
-- Render
pure $
H.div
{ className: intercalate " "
[ "graph-doc-list__item"
, isSelected ? "graph-doc-list__item--selected" $ ""
, "list-group-item"
]
, on: { click: \_ -> callback id }
......
......@@ -5,7 +5,7 @@ module Gargantext.Components.GraphExplorer.Sidebar
import Gargantext.Prelude
import Control.Parallel (parTraverse)
import Data.Array (concat, head, last, mapWithIndex)
import Data.Array (last, mapWithIndex)
import Data.Array as A
import Data.Either (Either(..))
import Data.Foldable (intercalate)
......@@ -19,17 +19,16 @@ import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
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.ContactList (contactListWrapper)
import Gargantext.Components.GraphExplorer.Sidebar.DocList (docListWrapper)
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.RandomText (words)
import Gargantext.Components.Search (SearchType(..), SearchQuery(..))
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Data.Array (mapMaybe)
import Gargantext.Ends (Frontends)
......@@ -38,7 +37,6 @@ import Gargantext.Sessions (Session)
import Gargantext.Types (CTabNgramType, FrontendError(..), NodeID, TabSubType(..), TabType(..), TermList(..), modeTabType)
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Gargantext.Utils.Toestand as T2
import Math as Math
import Partial.Unsafe (unsafePartial)
......@@ -51,8 +49,7 @@ here :: R2.Here
here = R2.here "Gargantext.Components.GraphExplorer.Sidebar"
type Props =
( boxes :: Boxes
, metaData :: GET.MetaData
( metaData :: GET.MetaData
, session :: Session
, frontends :: Frontends
)
......@@ -65,7 +62,7 @@ sidebarCpt = here.component "sidebar" cpt where
cpt props _ = do
-- States
{ sideTab
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
sideTab' <- R2.useLive' sideTab
......@@ -128,7 +125,7 @@ sideTabDataCpt = here.component "sideTabData" cpt where
-- States
{ selectedNodeIds
, graph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
selectedNodeIds' <- R2.useLive' selectedNodeIds
graph' <- R2.useLive' graph
......@@ -171,12 +168,7 @@ sideTabDataCpt = here.component "sideTabData" cpt where
sideBarTabSeparator
,
docListWrapper
{ frontends: props.frontends
, metaData: props.metaData
, nodesMap: SigmaxT.nodesGraphMap graph'
, searchType: SearchDoc
, selectedNodeIds: selectedNodeIds'
, session: props.session
{ metaData: props.metaData
}
]
]
......@@ -188,11 +180,11 @@ sideTabCommunity = R2.leaf sideTabCommunityCpt
sideTabCommunityCpt :: R.Component Props
sideTabCommunityCpt = here.component "sideTabCommunity" cpt where
cpt props@{ frontends } _ = do
cpt props _ = do
-- States
{ selectedNodeIds
, graph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
selectedNodeIds' <- R2.useLive' selectedNodeIds
graph' <- R2.useLive' graph
......@@ -234,13 +226,8 @@ sideTabCommunityCpt = here.component "sideTabCommunity" cpt where
,
sideBarTabSeparator
,
docListWrapper
{ frontends
, metaData: props.metaData
, nodesMap: SigmaxT.nodesGraphMap graph'
, searchType: SearchContact
, selectedNodeIds: selectedNodeIds'
, session: props.session
contactListWrapper
{ metaData: props.metaData
}
]
]
......@@ -274,7 +261,7 @@ selectedNodesCpt = here.component "selectedNodes" cpt where
-- States
{ selectedNodeIds
, graph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
selectedNodeIds' <- R2.useLive' selectedNodeIds
graph' <- R2.useLive' graph
......@@ -353,7 +340,7 @@ neighborhoodCpt = R.memo' $ here.component "neighborhood" cpt where
-- States
{ selectedNodeIds
, graph
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
selectedNodeIds' <-
R2.useLive' selectedNodeIds
......@@ -482,21 +469,21 @@ updateTermButton = R2.component updateTermButtonCpt
updateTermButtonCpt :: R.Component UpdateTermButtonProps
updateTermButtonCpt = here.component "updateTermButton" cpt where
cpt { boxes:
{ errors
, reloadForest
}
, variant
cpt { variant
, metaData
, nodesMap
, rType
, session
} children = do
-- States
{ errors
, reloadForest
} <- AppStore.use
{ removedNodeIds
, selectedNodeIds
, graphId
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
selectedNodeIds' <- R2.useLive' selectedNodeIds
graphId' <- R2.useLive' graphId
......@@ -612,80 +599,6 @@ sendPatch termList session (GET.MetaData metaData) node = do
patch_list :: NTC.Replace TermList
patch_list = NTC.Replace { new: termList, old: MapTerm }
---------------------------------------------------------
type DocListWrapper =
( frontends :: Frontends
, metaData :: GET.MetaData
, nodesMap :: SigmaxT.NodesMap
, searchType :: SearchType
, selectedNodeIds :: SigmaxT.NodeIds
, session :: Session
)
docListWrapper :: R2.Leaf DocListWrapper
docListWrapper = R2.leaf docListWrapperCpt
docListWrapperCpt :: R.Component DocListWrapper
docListWrapperCpt = here.component "docListWrapper" cpt where
cpt { frontends
, metaData: GET.MetaData metaData
, nodesMap
, searchType
, selectedNodeIds
, session
} _ = do
-- States
{ showDoc
} <- Stores.useStore GraphStore.context
query /\ queryBox <- R2.useBox' Nothing
-- Helpers
let
toSearchQuery ids = SearchQuery
{ expected: searchType
, query: concat $ toQuery <$> Set.toUnfoldable ids
}
toQuery id = case Map.lookup id nodesMap of
Nothing -> []
Just n -> words n.label
toGraphSideCorpus corpusId = GET.GraphSideCorpus
{ corpusId
, corpusLabel: metaData.title
, listId : metaData.list.listId
}
-- Hooks
R.useEffect1' selectedNodeIds $
T.write_ (selectedNodeIds # toSearchQuery >>> Just) queryBox
-- Render
pure $
R.fragment
[
case (head metaData.corpusId) /\ query of
(Just corpusId) /\ (Just query') ->
docList
{ frontends
, query: query'
, session
, graphSideCorpus: toGraphSideCorpus corpusId
, showDoc
}
_ /\ _ ->
B.caveat
{}
[
H.text "You can link a corpus to your Graph to retrieve relative documents when selecting nodes"
]
]
------------------------------------------------------------------------
......
......@@ -4,6 +4,7 @@ module Gargantext.Components.GraphExplorer.Store
, options
, context
, provide
, use
) where
import Gargantext.Prelude
......@@ -120,3 +121,6 @@ context = R.createContext $ unsafeCoerce unit
provide :: Record State -> Array R.Element -> R.Element
provide values = Stores.provideStore here.name values context
use :: R.Hooks (Record Store)
use = Stores.useStore context
......@@ -24,7 +24,6 @@ import Gargantext.Sessions (Session)
import Gargantext.Types as GT
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Reactix.DOM.HTML as H
......@@ -66,7 +65,7 @@ controlsCpt = R.memo' $ here.component "controls" cpt where
, sideTab
, mouseSelectorSize
, labelSize
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
forceAtlasState' <- R2.useLive' forceAtlasState
graph' <- R2.useLive' graph
......
......@@ -9,7 +9,6 @@ import Gargantext.Components.GraphExplorer.Topbar.Search (nodeSearchControl)
import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Stores as Stores
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
......@@ -29,7 +28,7 @@ component = here.component "topBar" cpt where
, selectedNodeIds
, showControls
, showSidebar
} <- Stores.useStore GraphStore.context
} <- GraphStore.use
graph' <- R2.useLive' graph
showControls' <- R2.useLive' showControls
......
......@@ -17,6 +17,7 @@ import Gargantext.Components.GraphQL.User (User, UserInfo, UserInfoM)
import Gargantext.Ends (Backend(..))
import Gargantext.Sessions (Session(..))
import Gargantext.Utils.Reactix as R2
import Gargnatext.Components.GraphQL.Contact (AnnuaireContact)
import GraphQL.Client.Args (type (==>))
import GraphQL.Client.BaseClients.Urql (UrqlClient, createClient)
import GraphQL.Client.Query (queryWithDecoder)
......@@ -64,8 +65,8 @@ queryGql session name q = do
--query client name q
client <- liftEffect $ getClient session
gqlQuery (client :: Client UrqlClient Schema Mutation Void) name q
--query_ "http://localhost:8008/gql" (Proxy :: Proxy Schema)
--query_ "http://localhost:8008/gql" (Proxy :: Proxy Schema)
-- Schema
type Schema
......@@ -75,6 +76,7 @@ type Schema
, user_infos :: { user_id :: Int } ==> Array UserInfo
, users :: { user_id :: Int } ==> Array User
, tree :: { root_id :: Int } ==> TreeFirstLevel
, annuaire_contacts :: { contact_id :: Int } ==> Array AnnuaireContact
}
type Mutation
......
module Gargnatext.Components.GraphQL.Contact
( AnnuaireContact
, annuaireContactQuery
-- Lenses
, _ac_firstName
, _ac_lastName
) where
import Gargantext.Prelude
import Data.Lens (Lens', lens)
import Data.Maybe (Maybe(..), fromMaybe)
import GraphQL.Client.Args (Args, (=>>))
import GraphQL.Client.Variable (Var(..))
type AnnuaireContact
= { ac_id :: Int
, ac_firstName :: Maybe String
, ac_lastName :: Maybe String
}
type AnnuaireContactQuery
= { annuaire_contacts :: Args
{ contact_id :: Var "id" Int }
{ ac_id :: Unit
, ac_firstName :: Unit
, ac_lastName :: Unit
}
}
annuaireContactQuery :: AnnuaireContactQuery
annuaireContactQuery
= { annuaire_contacts:
{ contact_id: Var :: _ "id" Int } =>>
{ ac_id: unit
, ac_firstName: unit
, ac_lastName: unit
}
}
------------------------------------------------------------------------
_ac_firstName :: Lens' AnnuaireContact String
_ac_firstName = lens getter setter
where
getter ({ ac_firstName: val }) = fromMaybe "" val
setter rec val = rec { ac_firstName = Just val }
_ac_lastName :: Lens' AnnuaireContact String
_ac_lastName = lens getter setter
where
getter ({ ac_lastName: val }) = fromMaybe "" val
setter rec val = rec { ac_lastName = Just val }
module Gargantext.Components.GraphQL.Endpoints where
import Gargantext.Components.GraphQL.Node
import Gargantext.Components.GraphQL.User
import Gargantext.Components.GraphQL.Tree
import Gargantext.Prelude
import Gargantext.Components.GraphQL.Node (Node, nodeParentQuery)
import Gargantext.Components.GraphQL.Tree (TreeFirstLevel, treeFirstLevelQuery)
import Gargantext.Components.GraphQL.User (UserInfo, userInfoQuery)
import Data.Array as A
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Unit (unit)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.Components.GraphQL (getClient, queryGql)
import Gargantext.Components.GraphQL (queryGql)
import Gargantext.Components.GraphQL.IMT as GQLIMT
import Gargantext.Components.GraphQL.Task as GQLT
import Gargantext.Config.REST (AffRESTError, RESTError(..))
import Gargantext.Config.REST (RESTError(..), AffRESTError)
import Gargantext.Sessions (Session)
import Gargantext.Types (AsyncTaskWithType(..), AsyncTask(..), AsyncTaskType(..), NodeType)
import Gargantext.Types (NodeType)
import Gargantext.Utils.Reactix as R2
import GraphQL.Client.Args (onlyArgs, (=>>))
import GraphQL.Client.Query (mutation)
import Gargnatext.Components.GraphQL.Contact (AnnuaireContact, annuaireContactQuery)
import GraphQL.Client.Variables (withVars)
import Simple.JSON as JSON
here :: R2.Here
here = R2.here "Gargantext.Components.GraphQL.Endpoints"
......@@ -59,9 +56,17 @@ getUserInfo session id = do
-- NOTE Contact is at G.C.N.A.U.C.Types
Just ui -> Right ui
getAnnuaireContact :: Session -> Int -> AffRESTError AnnuaireContact
getAnnuaireContact session id = do
{ annuaire_contacts } <- queryGql session "get annuaire contact" $
annuaireContactQuery `withVars` { id }
liftEffect $ here.log2 "[getAnnuaireContact] data" annuaire_contacts
pure $ case A.head annuaire_contacts of
Nothing -> Left (CustomError $ "contact id=" <> show id <> " not found")
Just r -> Right r
getTreeFirstLevel :: Session -> Int -> AffRESTError TreeFirstLevel
getTreeFirstLevel session id = do
{ tree } <- queryGql session "get tree first level" $ treeFirstLevelQuery `withVars` { id }
liftEffect $ here.log2 "[getTreeFirstLevel] tree first level" tree
pure $ Right tree -- TODO: error handling
......@@ -143,4 +143,3 @@ showUser { u_id
, u_username
, u_email } = "[" <> show u_id <> "] " <> u_username <> " :: " <> u_email
showMUser u = maybe "" showUser u
......@@ -25,7 +25,7 @@ import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.NgramsTable.Components as NTC
import Gargantext.Components.NgramsTable.Core (Action(..), CoreAction(..), CoreState, Dispatch, NgramsElement(..), NgramsPatch(..), NgramsTable, NgramsTerm, PageParams, PatchMap(..), Versioned(..), VersionedNgramsTable, VersionedWithCountNgramsTable, _NgramsElement, _NgramsRepoElement, _NgramsTable, _children, _list, _ngrams, _ngrams_repo_elements, _ngrams_scores, _occurrences, _root, addNewNgramA, applyNgramsPatches, applyPatchSet, chartsAfterSync, commitPatch, convOrderBy, coreDispatch, filterTermSize, fromNgramsPatches, ngramsRepoElementToNgramsElement, ngramsTermText, normNgram, patchSetFromMap, replace, setTermListA, singletonNgramsTablePatch, syncResetButtons, toVersioned)
import Gargantext.Components.NgramsTable.Loader (useLoaderWithCacheAPI)
......
......@@ -8,7 +8,7 @@ import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.DocsTable as DT
import Gargantext.Components.DocsTable.Types (Year)
import Gargantext.Components.GraphQL.User (UserInfo)
......
......@@ -7,17 +7,21 @@ module Gargantext.Components.Nodes.Annuaire.User
import Gargantext.Prelude
import Data.Either (Either(..))
import Data.Lens as L
import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.GraphQL.User (UserInfo)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.GraphQL (getClient)
import Gargantext.Components.GraphQL.Endpoints (getUserInfo)
import Gargantext.Components.GraphQL.User (UserInfo, _ui_cwCity, _ui_cwCountry, _ui_cwFirstName, _ui_cwLabTeamDeptsFirst, _ui_cwLastName, _ui_cwOffice, _ui_cwOrganizationFirst, _ui_cwRole, _ui_cwTouchMail, _ui_cwTouchPhone)
import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Components.Nodes.Annuaire.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contact (getUserInfoWithReload, saveUserInfo, contactInfos)
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (Contact(..), ContactData, ContactTouch(..), ContactWhere(..), ContactWho(..), HyperdataContact(..), HyperdataUser(..), _city, _country, _firstName, _labTeamDeptsJoinComma, _lastName, _mail, _office, _organizationJoinComma, _ouFirst, _phone, _role, _shared, _touch, _who, defaultContactTouch, defaultContactWhere, defaultContactWho, defaultHyperdataContact, defaultHyperdataUser)
import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Config.REST (logRESTError)
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader)
......@@ -25,6 +29,8 @@ import Gargantext.Sessions (Session(..), WithSession, WithSessionContext, sessio
import Gargantext.Types (FrontendError)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import GraphQL.Client.Args (IgnoreArg(..), OrArg(..), onlyArgs)
import GraphQL.Client.Query (mutation)
import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
......@@ -143,3 +149,150 @@ userLayoutWithKeyCpt = here.component "userLayoutWithKey" cpt where
-- -- throwError $ error "Missing default list"
-- pure $ (\contactNode -> { contactNode, defaultListId: 424242 }) <$> eContactNode
--
------------------------------------------------------------
-- | TODO format data in better design (UI) shape
contactInfos :: UserInfo -> (UserInfo -> Effect Unit) -> Array R.Element
contactInfos userInfo onUpdateUserInfo = item <$> contactInfoItems where
item { label, lens, defaultVal } =
contactInfoItem { defaultVal, label, lens, onUpdateUserInfo, userInfo }
contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: UserInfoLens}
contactInfoItems =
[ { label: "Last Name" , defaultVal: "Empty Last Name" , lens: _ui_cwLastName }
, { label: "First Name" , defaultVal: "Empty First Name" , lens: _ui_cwFirstName }
, { label: "Organisation" , defaultVal: "Empty Organisation" , lens: _ui_cwOrganizationFirst }
, { label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _ui_cwLabTeamDeptsFirst }
, { label: "Office" , defaultVal: "Empty Office" , lens: _ui_cwOffice }
, { label: "City" , defaultVal: "Empty City" , lens: _ui_cwCity }
, { label: "Country" , defaultVal: "Empty Country" , lens: _ui_cwCountry }
, { label: "Role" , defaultVal: "Empty Role" , lens: _ui_cwRole }
, { label: "Phone" , defaultVal: "Empty Phone" , lens: _ui_cwTouchPhone }
, { label: "Mail" , defaultVal: "Empty Mail" , lens: _ui_cwTouchMail }
]
type UserInfoLens = L.ALens' UserInfo String
type ContactInfoItemProps =
( defaultVal :: String
, label :: String
, lens :: UserInfoLens
, onUpdateUserInfo :: UserInfo -> Effect Unit
, userInfo :: UserInfo
)
contactInfoItem :: R2.Leaf ContactInfoItemProps
contactInfoItem = R2.leafComponent contactInfoItemCpt
contactInfoItemCpt :: R.Component ContactInfoItemProps
contactInfoItemCpt = here.component "contactInfoItem" cpt
where
cpt { defaultVal, label, lens, onUpdateUserInfo, userInfo } _ = do
isEditing <- T.useBox false
isEditing' <- T.useLive T.unequal isEditing
let value = (L.view cLens userInfo) :: String
valueBox <- T.useBox value
pure $
H.div { className: "form-group row" }
[ H.span { className: "col-sm-2 col-form-label" } [ H.text label ]
, if isEditing' then
itemEditing { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox }
else
itemNotEditing { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox }
]
where
cLens = L.cloneLens lens
type ItemProps =
( defaultVal :: String
, isEditing :: T.Box Boolean
, lens :: UserInfoLens
, onUpdateUserInfo :: UserInfo -> Effect Unit
, userInfo :: UserInfo
, valueBox :: T.Box String
)
itemNotEditing :: R2.Leaf ItemProps
itemNotEditing = R2.leafComponent itemNotEditingCpt
itemNotEditingCpt :: R.Component ItemProps
itemNotEditingCpt = here.component "itemEditing" cpt where
cpt { isEditing, valueBox } _ = do
valueBox' <- T.useLive T.unequal valueBox
pure $ H.div { className: "input-group col-sm-6" }
[ H.input
{ className: "form-control", type: "text"
, defaultValue: valueBox', disabled: true }
, H.div { className: "btn input-group-append", on: { click } }
[ H.div { className: "input-group-text fa fa-pencil" } [] ]
]
where
click _ = T.write_ true isEditing
itemEditing :: R2.Leaf ItemProps
itemEditing = R2.leafComponent itemEditingCpt
itemEditingCpt :: R.Component ItemProps
itemEditingCpt = here.component "itemNotEditing" cpt where
cpt { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox } _ = do
valueBox' <- T.useLive T.unequal valueBox
pure $ H.div { className: "input-group col-sm-6" }
[ inputWithEnter
{ autoFocus: true
, className: "form-control"
, defaultValue: valueBox'
, onBlur: \v -> T.write_ v valueBox
, onEnter: click
, onValueChanged: \v -> do
here.log2 "[itemEditingCpt] value Changed: " v
T.write_ v valueBox
, placeholder: defaultVal
, type: "text" }
, H.div { className: "btn input-group-append", on: { click } }
[ H.div { className: "input-group-text fa fa-floppy-o" } [] ]
]
where
cLens = L.cloneLens lens
click _ = do
T.write_ false isEditing
value <- T.read valueBox
here.log2 "[itemEditing] value" value
let newUserInfo = (L.set cLens value userInfo) :: UserInfo
onUpdateUserInfo newUserInfo
-- saveContactHyperdata :: Session -> Int -> HyperdataContact -> AffRESTError Int
-- saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "")
saveUserInfo :: Session -> Int -> UserInfo -> AffRESTError Int
saveUserInfo session id ui = do
let token = getToken session
client <- liftEffect $ getClient session
res <- mutation
client
"update user_info"
{ update_user_info: onlyArgs { token: token
, ui_id: id
, ui_cwFirstName: ga ui.ui_cwFirstName
, ui_cwLastName: ga ui.ui_cwLastName
, ui_cwOrganization: ui.ui_cwOrganization
, ui_cwLabTeamDepts: ui.ui_cwLabTeamDepts
, ui_cwOffice: ga ui.ui_cwOffice
, ui_cwCity: ga ui.ui_cwCity
, ui_cwCountry: ga ui.ui_cwCountry
, ui_cwRole: ga ui.ui_cwRole
, ui_cwTouchPhone: ga ui.ui_cwTouchPhone
, ui_cwTouchMail: ga ui.ui_cwTouchMail } }
pure $ Right res.update_user_info
where
ga Nothing = ArgL IgnoreArg
ga (Just val) = ArgR val
getToken (Session { token }) = token
getUserInfoWithReload :: { nodeId :: Int
, reload :: T2.Reload
, session :: Session
} -> AffRESTError UserInfo
getUserInfoWithReload {nodeId, session} = getUserInfo session nodeId -- getContact session nodeId
......@@ -7,7 +7,7 @@ import Data.Generic.Rep (class Generic)
import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.DocsTable as DT
import Gargantext.Components.DocsTable.Types (Year)
import Gargantext.Components.NgramsTable as NT
......
......@@ -10,7 +10,7 @@ import Data.Show.Generic (genericShow)
import Effect (Effect)
import Effect.Aff (throwError)
import Effect.Exception (error)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.CodeEditor as CE
import Gargantext.Components.FolderView as FV
import Gargantext.Components.InputWithEnter (inputWithEnter)
......
......@@ -7,7 +7,7 @@ import Data.Maybe (Maybe(..), fromMaybe)
import Data.Ord.Generic (genericCompare)
import Data.Show.Generic (genericShow)
import Effect (Effect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Charts.Options.Type (EChartsInstance, MouseEvent)
import Gargantext.Components.Nodes.Corpus.Chart.Histo (histo)
import Gargantext.Components.Nodes.Corpus.Chart.Metrics (metrics)
......
......@@ -3,7 +3,7 @@ module Gargantext.Components.Nodes.Corpus.Chart.Types where
import Data.Maybe (Maybe)
import Data.Tuple (Tuple)
import Effect (Effect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Charts.Options.Type (EChartsInstance, MouseEvent)
import Gargantext.Prelude (Unit)
import Gargantext.Sessions (Session)
......
......@@ -6,7 +6,7 @@ import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Node (NodePoly(..))
import Gargantext.Components.Nodes.Corpus (fieldsCodeEditor, loadCorpusWithReload, saveCorpus)
import Gargantext.Components.Nodes.Corpus.Types (Hyperdata(..))
......
......@@ -7,7 +7,7 @@ import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Nodes.Corpus (fieldsCodeEditor)
import Gargantext.Components.Nodes.Corpus.Chart.Predefined as P
import Gargantext.Components.Nodes.Dashboard.Types as DT
......
......@@ -7,23 +7,22 @@ import Gargantext.Prelude
import DOM.Simple (document, querySelector)
import Data.Maybe (Maybe(..), isJust)
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
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 (PhyloDataSet)
import Gargantext.Config.REST (logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Sessions (Session)
import Gargantext.Hooks.Session (useSession)
import Gargantext.Types (NodeID)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
type MainProps =
( nodeId :: NodeID
, session :: Session
, boxes :: Boxes
)
here :: R2.Here
......@@ -34,14 +33,13 @@ phyloLayout = R2.leaf phyloLayoutCpt
phyloLayoutCpt :: R.Component MainProps
phyloLayoutCpt = here.component "main" cpt where
cpt { nodeId, session } _ = do
cpt { nodeId } _ = do
-- | States
-- |
session <- useSession
state' /\ state <- R2.useBox' Nothing
-- | Computed
-- |
let
......@@ -49,8 +47,8 @@ phyloLayoutCpt = here.component "main" cpt where
errorHandler = logRESTError here "[phylo]"
handler (phyloDataSet :: PhyloDataSet) =
layout
{ nodeId
hydrateStore
{ phyloId: nodeId
, phyloDataSet
}
......@@ -65,6 +63,7 @@ phyloLayoutCpt = here.component "main" cpt where
, state
}
-- @XXX: Runtime odd behavior
-- cannot use the `useEffect` + its cleanup function within the
-- same `Effect`, otherwise the below cleanup example will be
......@@ -126,3 +125,40 @@ phyloLayoutCpt = here.component "main" cpt where
, defaultSlot:
R2.fromMaybe_ state' handler
}
--------------------------------------------------------
type HydrateStoreProps =
( phyloDataSet :: PhyloDataSet
, phyloId :: NodeID
)
hydrateStore :: R2.Leaf HydrateStoreProps
hydrateStore = R2.leaf hydrateStoreCpt
hydrateStoreCpt :: R.Component HydrateStoreProps
hydrateStoreCpt = here.component "layout" cpt where
cpt { phyloDataSet
, phyloId
} _ = do
-- | Computed
-- |
let
state :: Record PhyloStore.State
state =
-- Data
{ phyloDataSet
, phyloId
-- (default options)
} `Record.merge` PhyloStore.options
-- | Render
-- |
pure $
PhyloStore.provide
state
[
layout
{}
]
......@@ -10,7 +10,7 @@ import Data.Maybe (Maybe(..), isJust, maybe)
import Data.Sequence as Seq
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphExplorer.API as GraphAPI
import Gargantext.Components.GraphExplorer.Layout (convert, layout)
......@@ -18,9 +18,9 @@ import Gargantext.Components.GraphExplorer.Store as GraphStore
import Gargantext.Components.GraphExplorer.Types as GET
import Gargantext.Config.REST (logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.Sigmax as Sigmax
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Sessions (Session)
import Gargantext.Utils.Range as Range
import Gargantext.Utils.Reactix as R2
import Reactix as R
......@@ -29,27 +29,28 @@ import Record as Record
type Props =
( key :: String
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
( graphId :: GET.GraphId
)
here :: R2.Here
here = R2.here "Gargantext.Components.Nodes.Corpus.Graph"
graphLayout :: R2.Leaf Props
graphLayout :: R2.Leaf ( key :: String | Props )
graphLayout = R2.leaf graphLayoutCpt
graphLayoutCpt :: R.Component Props
graphLayoutCpt :: R.Component ( key :: String | Props )
graphLayoutCpt = here.component "explorerLayout" cpt where
cpt props@{ boxes: { graphVersion }, graphId, session } _ = do
cpt { graphId } _ = do
-- | States
-- |
{ graphVersion
} <- AppStore.use
session <- useSession
graphVersion' <- R2.useLive' graphVersion
state' /\ state <- R2.useBox' Nothing
-- | Hooks
-- |
useLoaderEffect
......@@ -118,37 +119,32 @@ graphLayoutCpt = here.component "explorerLayout" cpt where
where
errorHandler = logRESTError here "[explorerLayout]"
handler loaded@(GET.HyperdataGraph { graph: hyperdataGraph }) =
initGraph { graph
, hyperdataGraph: loaded
, mMetaData
, session
, boxes: props.boxes
, graphId
}
hydrateStore
{ graph
, hyperdataGraph: loaded
, mMetaData
, graphId
}
where
Tuple mMetaData graph = convert hyperdataGraph
--------------------------------------------------------
type InitGraphProps =
type HydrateStoreProps =
( mMetaData :: Maybe GET.MetaData
, graph :: SigmaxT.SGraph
, hyperdataGraph :: GET.HyperdataGraph
, session :: Session
, boxes :: Boxes
, graphId :: GET.GraphId
)
initGraph :: R2.Leaf InitGraphProps
initGraph = R2.leaf initGraphCpt
hydrateStore:: R2.Leaf HydrateStoreProps
hydrateStore = R2.leaf hydrateStoreCpt
initGraphCpt :: R.Component InitGraphProps
initGraphCpt = here.component "initGraph" cpt where
cpt { boxes
, mMetaData
hydrateStoreCpt :: R.Component HydrateStoreProps
hydrateStoreCpt = here.component "hydrateStore" cpt where
cpt { mMetaData
, graph
, graphId
, session
, hyperdataGraph
} _ = do
-- | Computed
......@@ -186,15 +182,11 @@ initGraphCpt = here.component "initGraph" cpt where
-- | Render
-- |
pure $
GraphStore.provide
state
[
layout
{ session
, boxes
, sigmaRef
}
{ sigmaRef }
]
......@@ -5,7 +5,7 @@ import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype)
import Effect (Effect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Data.Landing (BlockText(..), BlockTexts(..), Button(..), LandingData(..))
import Gargantext.Components.FolderView as FV
import Gargantext.Components.Lang (LandingLang(..))
......
......@@ -4,7 +4,7 @@ import Gargantext.Prelude
import Effect (Effect)
import Effect.Aff (launchAff_)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.NgramsTable.Loader (clearCache)
import Gargantext.Components.Node (NodePoly(..))
import Gargantext.Components.Nodes.Corpus (loadCorpusWithChild)
......
......@@ -6,7 +6,7 @@ import Data.Array as A
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Tuple.Nested ((/\))
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.NgramsTable as NT
import Gargantext.Components.NgramsTable.Core as NTC
import Gargantext.Components.Nodes.Corpus.Chart.Metrics (metrics)
......
......@@ -7,7 +7,7 @@ import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.Tuple.Nested ((/\))
import Effect.Aff (launchAff_)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Charts.Options.ECharts (dispatchAction)
import Gargantext.Components.Charts.Options.Type (EChartsInstance, EChartActionData)
import Gargantext.Components.DocsTable as DT
......@@ -108,7 +108,7 @@ textsLayoutWithKeyCpt = here.component "textsLayoutWithKey" cpt
let NodePoly { name, date, hyperdata } = corpusNode
R.fragment
[ Table.tableHeaderWithRenameLayout {
[ Table.tableHeaderWithRenameLayout {
cacheState
, name
, date
......
......@@ -4,33 +4,31 @@ module Gargantext.Components.PhyloExplorer.DetailsTab
import Gargantext.Prelude
import Gargantext.Components.PhyloExplorer.Store as PhyloStore
import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet(..))
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( key :: String
here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer.SideBar.DetailsTab"
, docCount :: Int
, foundationCount :: Int
, periodCount :: Int
, termCount :: Int
, groupCount :: Int
, branchCount :: Int
)
detailsTab :: R2.Leaf Props
detailsTab :: R2.Leaf ( key :: String )
detailsTab = R2.leaf detailsTabCpt
componentName :: String
componentName = "Gargantext.Components.PhyloExplorer.SideBar.DetailsTab"
detailsTabCpt :: R.Component Props
detailsTabCpt = R.hooksComponent componentName cpt where
cpt props _ =
detailsTabCpt :: R.Component ( key :: String )
detailsTabCpt = here.component "" cpt where
cpt _ _ = do
-- | States
-- |
store <- PhyloStore.use
(PhyloDataSet o) <- R2.useLive' store.phyloDataSet
-- Render
-- | Render
-- |
pure $
H.div
......@@ -40,21 +38,21 @@ detailsTabCpt = R.hooksComponent componentName cpt where
H.ul
{ className: "phylo-details-tab__counter" }
[
detailsCount props.docCount "docs"
detailsCount o.nbDocs "docs"
,
detailsCount props.foundationCount "foundations"
detailsCount o.nbFoundations "foundations"
,
detailsCount props.periodCount "periods"
detailsCount o.nbPeriods "periods"
]
,
H.ul
{ className: "phylo-details-tab__counter" }
[
detailsCount props.termCount "terms"
detailsCount o.nbTerms "terms"
,
detailsCount props.groupCount "groups"
detailsCount o.nbGroups "groups"
,
detailsCount props.branchCount "branches"
detailsCount o.nbBranches "branches"
]
,
H.hr
......
module Gargantext.Components.PhyloExplorer.Sidebar.DocList
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.Foldable (intercalate)
import Data.Foldable as F
import Data.Int (fromString)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromJust)
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.PhyloExplorer.Store as PhyloStore
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.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.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Math as Math
import Partial.Unsafe (unsafePartial)
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
cpt _ _ = do
-- | States
-- |
store <- PhyloStore.use
extractedTerms <- R2.useLive' store.extractedTerms
query' /\ query <- R2.useBox' Nothing
-- | Helpers
-- |
let
toSearchQuery ids = SearchQuery
{ expected: SearchDoc
, query: map (getter _.label) ids
}
searchQuery
= extractedTerms
# toSearchQuery
# Just
# flip T.write_ query
-- | Render
--
pure $
docList
{ searchQuery }
--------------------------------------------------------------
type ListProps =
( searchQuery :: SearchQuery
)
docList :: R2.Leaf TabsProps
docList = R2.leaf docListCpt
docListCpt :: R.Component TabsProps
docListCpt = here.component "main" cpt where
-- | Helpers
-- |
errorHandler err = do
here.warn2 "[pageLayout] RESTError" err
case err of
ReadJSONError err' ->
here.warn2 "[pageLayout] ReadJSONError" $ show err'
_ -> pure unit
-- | Component
-- |
cpt { searchQuery
} _ = do
-- | States
-- |
path' /\ path
<- R2.useBox' $ initialPagePath { nodeId, listId, query, session }
state' /\ state <-
R2.useBox' Nothing
rows' /\ rows <-
R2.useBox' Nothing
showDoc' <-
R2.useLive' showDoc
-- | Hooks
-- |
useLoaderEffect
{ errorHandler
, state
, loader: loadPage
, path: path'
}
-- | Effects
-- |
-- (on query change, reload fetched docs)
useUpdateEffect1' query $
flip T.write_ path $ initialPagePath { nodeId, listId, query, session }
-- (on fetch success, extract existing docs)
useUpdateEffect1' state' case state' of
Nothing -> T.write_ (Just Seq.empty) rows
Just r -> case r of
Docs { docs } -> T.write_ (Just docs) rows
_ -> T.write_ (Just Seq.empty) rows
-- | Computed
-- |
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
-- | Render
-- |
pure $
R2.fromMaybe_ rows' \results ->
R.fragment
[
R2.if' (results == Seq.empty) $
B.caveat
{}
[
H.text "No docs found in your corpus for your selected terms"
]
,
R2.if' (not $ eq results Seq.empty) $
H.ul
{ className: intercalate " "
[ "graph-doc-list"
, "list-group"
]
} $
Seq.toUnfoldable $ flip Seq.map results \r ->
item
{ frontends
, path: path'
, session
, documentView: (r :: DocumentsView)
, callback: callback showDoc'
, isSelected: isSelected showDoc' (r :: DocumentsView)
}
]
---------------------------------------------------------
type ItemProps =
( documentView :: DocumentsView
, frontends :: Frontends
, session :: Session
, path :: PagePath
, callback :: DocId -> Effect Unit
, isSelected :: Boolean
)
item :: R2.Leaf ItemProps
item = R2.leaf itemCpt
itemCpt :: R.Component ItemProps
itemCpt = here.component "item" cpt where
cpt { documentView: dv@(DocumentsView { id, title, source })
, callback
, isSelected
-- , frontends
-- , path
-- , session
} _ = do
-- Computed
-- let
-- Creating a href link
-- documentUrl id' { listId, nodeId } =
-- url frontends $ Routes.CorpusDocument (sessionId session) nodeId listId id'
-- Render
pure $
H.div
{ className: intercalate " "
[ "graph-doc-list__item"
, isSelected ? "graph-doc-list__item--selected" $ ""
, "list-group-item"
]
, on: { click: \_ -> callback id }
}
[
B.ripple
{ variant: Dark }
[
H.div
{ className: "graph-doc-list__item__main" }
[
B.div'
{ className: "graph-doc-list__item__title" }
title
,
B.div'
{ className: "graph-doc-list__item__source" }
source
,
B.div'
{ className: "graph-doc-list__item__date" } $
publicationDate dv
]
,
H.div
{ className: "graph-doc-list__item__aside" }
[
B.icon
{ name: "eye-slash"
, className: intercalate " "
[ "text-info"
, isSelected ? "visible" $ "hidden"
]
}
]
]
]
......@@ -12,7 +12,8 @@ import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), Variant(..))
import Gargantext.Components.PhyloExplorer.Types (ExtractedTerm(..), ExtractedCount(..))
import Gargantext.Components.PhyloExplorer.Store as PhyloStore
import Gargantext.Components.PhyloExplorer.Types (ExtractedCount(..), ExtractedTerm(..))
import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
......@@ -21,12 +22,6 @@ import Toestand as T
type Props =
( key :: String
, extractedTerms :: Array ExtractedTerm
, extractedCount :: Maybe ExtractedCount
, selectedTerm :: Maybe String
, selectedBranch :: Maybe String
, selectedSource :: Maybe String
, selectTermCallback :: String -> Effect Unit
)
......@@ -39,17 +34,18 @@ componentName = "Gargantext.Components.PhyloExplorer.SideBar.SelectionTab"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt { selectTermCallback
, extractedTerms
, extractedCount
, selectedTerm
, selectedBranch
, selectedSource
} _ = do
-- | State
-- |
store <- PhyloStore.use
-- State
--------
extractedTerms <- R2.useLive' store.extractedTerms
extractedCount <- R2.useLive' store.extractedCount
selectedTerm <- R2.useLive' store.selectedTerm
selectedBranch <- R2.useLive' store.selectedBranch
selectedSource <- R2.useLive' store.selectedSource
showMore /\ showMoreBox <- R2.useBox' false
showMore' /\ showMore <- R2.useBox' false
let
haveSelection
......@@ -63,18 +59,17 @@ component = R.hooksComponent componentName cpt where
truncateResults
= termCount > maxTruncateResult
&& not showMore
&& not showMore'
-- Effects
----------
-- | Effects
-- |
-- reset "show more" button to hidding mode on selected terms change
R.useEffect1' extractedTerms $
T.write_ false showMoreBox
-- Render
---------
T.write_ false showMore
-- | Render
-- |
pure $
H.div
......@@ -293,7 +288,7 @@ component = R.hooksComponent componentName cpt where
B.button
{ variant: ButtonVariant Light
, callback: \_ -> T.modify_ not showMoreBox
, callback: \_ -> T.modify_ not showMore
, block: true
, className: "phylo-selection-tab__selection__show-more"
}
......@@ -303,6 +298,19 @@ component = R.hooksComponent componentName cpt where
]
]
]
-- ,
-- (separator)
-- R2.if' (not null extractedTerms) $
-- H.div
-- { className: "phylo-selection-tab__separator" }
-- [
-- B.icon