Commit 48a16913 authored by arturo's avatar arturo

[graph] Node Focus

* #375: rc6.x
parent 8c72c681
Pipeline #2769 failed with stage
in 0 seconds
......@@ -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;
}
......
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)
......@@ -147,7 +147,7 @@ folderSimpleCpt = here.component "folderSimpleCpt" cpt where
route nId rootId nType sid
| rootId == nodeId = Home
| otherwise = getFolderPath nType sid nId
icon :: FolderStyle -> GT.NodeType -> String
icon FolderUp _ = "fa fa-folder-open"
......@@ -234,7 +234,7 @@ backButtonCpt = R.hooksComponent "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,7 +59,7 @@ 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
......
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
......
......@@ -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)
......
......@@ -11,7 +11,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.GraphQL.User (UserInfo)
import Gargantext.Components.Nodes.Annuaire.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contact (getUserInfoWithReload, saveUserInfo, contactInfos)
......
......@@ -13,7 +13,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.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)
......
......@@ -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
......
......@@ -10,18 +10,19 @@ import Data.Foldable (intercalate)
import Data.Int as Int
import Data.Maybe (Maybe(..))
import Data.String (null)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import FFI.Simple ((..), (.=))
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.PhyloExplorer.Resources (PubSubEvent(..))
import Gargantext.Components.PhyloExplorer.Resources as RS
import Gargantext.Components.PhyloExplorer.SideBar (sideBar)
import Gargantext.Components.PhyloExplorer.Store as PhyloStore
import Gargantext.Components.PhyloExplorer.ToolBar (toolBar)
import Gargantext.Components.PhyloExplorer.TopBar (topBar)
import Gargantext.Components.PhyloExplorer.Types (DisplayView(..), PhyloDataSet(..), ExtractedTerm, ExtractedCount, Source, Term, sortSources)
import Gargantext.Components.PhyloExplorer.Types (DisplayView, ExtractedCount, FrameDoc, PhyloDataSet(..), TabView(..), Term, sortSources)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Types (NodeID, SidePanelState(..))
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1', useUpdateEffect3')
import Gargantext.Types (SidePanelState(..))
import Gargantext.Utils (getter, (?))
import Gargantext.Utils.Reactix as R2
import Graphics.D3.Base (d3)
......@@ -33,126 +34,121 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer"
type Props =
( phyloDataSet :: PhyloDataSet
, nodeId :: NodeID
)
layout :: R2.Leaf Props
layout :: R2.Leaf ()
layout = R2.leaf layoutCpt
layoutCpt :: R.Component Props
layoutCpt :: R.Component ()
layoutCpt = here.component "layout" cpt where
cpt { phyloDataSet: (PhyloDataSet o)
, nodeId
} _ = do
-- States
---------
let defaultDisplayView = HeadingMode
let topBarPortalKey = "portal-topbar::" <> show nodeId
cpt _ _ = do
-- | States
-- |
{ isBuilt
, source
, sources
, terms
, toolBarDisplayed
, search
, result
, displayView
, isIsolineDisplayed
, sideBarDisplayed
, sideBarTabView
, extractedTerms
, selectedTerm
, selectedBranch
, selectedSource
, extractedCount
, phyloId
, phyloDataSet
, frameDoc
} <- PhyloStore.use
(PhyloDataSet o) <- R2.useLive' phyloDataSet
phyloId' <- R2.useLive' phyloId
sources' <- R2.useLive' sources
terms' <- R2.useLive' terms
displayView' <- R2.useLive' displayView
isIsolineDisplayed' <- R2.useLive' isIsolineDisplayed
sideBarDisplayed' <- R2.useLive' sideBarDisplayed
toolBarDisplayed' <- R2.useLive' toolBarDisplayed
isBuilt' <- R2.useLive' isBuilt
selectedTerm' <- R2.useLive' selectedTerm
selectedBranch' <- R2.useLive' selectedBranch
selectedSource' <- R2.useLive' selectedSource
frameDoc' <- R2.useLive' frameDoc
-- | Hooks
-- |
let topBarPortalKey = "portal-topbar::" <> show phyloId'
mTopBarHost <- R.unsafeHooksEffect $ R2.getElementById "portal-topbar"
isDisplayed /\ isReadyBox <-
R2.useBox' false
source /\ sourceBox <-
R2.useBox' ""
sources /\ sourcesBox <-
R2.useBox' (mempty :: Array Source)
terms /\ termsBox <-
R2.useBox' (mempty :: Array Term)
toolBarDisplayed /\ toolBarDisplayedBox <-
R2.useBox' false
search /\ searchBox <-
R2.useBox' ""
result /\ resultBox <-
R2.useBox' (Nothing :: Maybe Term)
displayView /\ displayViewBox <-
R2.useBox' defaultDisplayView
isIsolineDisplayed /\ isIsolineDisplayedBox <-
R2.useBox' false
sideBarDisplayed /\ sideBarDisplayedBox <-
R2.useBox' InitialClosed
extractedTerms /\ extractedTermsBox <-
R2.useBox' (mempty :: Array ExtractedTerm)
selectedTerm /\ selectedTermBox <-
R2.useBox' (Nothing :: Maybe String)
selectedBranch /\ selectedBranchBox <-
R2.useBox' (Nothing :: Maybe String)
selectedSource /\ selectedSourceBox <-
R2.useBox' (Nothing :: Maybe String)
extractedCount /\ extractedCountBox <-
R2.useBox' (Nothing :: Maybe ExtractedCount)
-- Behaviors
------------
-- | Behaviors
-- |
-- (!) do not rely on the JavaScript `Resources.js:resetSelection`,
-- as it will lead to potential circular computations
resetSelection <- pure $ const do
T.write_ Nothing selectedBranchBox
T.write_ Nothing selectedTermBox
T.write_ "" sourceBox
T.write_ Nothing selectedSourceBox
T.write_ Nothing extractedCountBox
T.write_ mempty extractedTermsBox
void $ pure $ (window .= "branchFocus") []
changeViewCallback <- pure $
flip T.write displayViewBox
>=> RS.changeDisplayView
sourceCallback <- pure \id -> do
-- (!) upcoming value from a `B.formSelect`, so simple <String> format
let mSource = RS.findSourceById sources =<< Int.fromString id
let mLabel = pure <<< getter _.label =<< mSource
resetSelection unit
T.write_ id sourceBox
T.write_ mLabel selectedSourceBox
RS.selectSource window mSource
searchCallback <- pure $
flip T.write searchBox
>=> RS.autocompleteSearch terms
>=> flip T.write_ resultBox
resultCallback <- pure $ const $
resetSelection unit
*> RS.autocompleteSubmit displayViewBox result
unselectCallback <- pure $ const $
resetSelection unit
*> RS.doubleClick
selectTermCallback <- pure $ \s -> do
resetSelection unit
mTerm <- RS.autocompleteSearch terms s
RS.autocompleteSubmit displayViewBox mTerm
-- Effects
----------
--
-- (!) same idea for Box content mutation: eg. due the reset notion
-- triggered at each selection (cf. `resetSelection`), a call via
-- `T.write` + `T.listen` will also create an infinite loop
let
resetSelection :: Unit -> Effect Unit
resetSelection _ = do
T.write_ Nothing selectedBranch
T.write_ Nothing selectedTerm
T.write_ "" source
T.write_ Nothing selectedSource
T.write_ Nothing extractedCount
T.write_ mempty extractedTerms
void $ pure $ (window .= "branchFocus") []
changeViewCallback :: DisplayView -> Effect Unit
changeViewCallback =
flip T.write displayView
>=> RS.changeDisplayView
sourceCallback :: String -> Effect Unit
sourceCallback id = do
-- (!) upcoming value from a `B.formSelect`, so simple <String> format
let mSource = RS.findSourceById sources' =<< Int.fromString id
let mLabel = pure <<< getter _.label =<< mSource
resetSelection unit
T.write_ id source
T.write_ mLabel selectedSource
RS.selectSource window mSource
searchCallback :: String -> Effect Unit
searchCallback =
flip T.write search
>=> RS.autocompleteSearch terms'
>=> flip T.write_ result
resultCallback :: Maybe Term -> Effect Unit
resultCallback mTerm =
resetSelection unit
*> RS.autocompleteSubmit displayView mTerm
unselectCallback :: Unit -> Effect Unit
unselectCallback _ =
resetSelection unit
*> RS.doubleClick
selectTermCallback :: String -> Effect Unit
selectTermCallback s = do
resetSelection unit
mTerm <- RS.autocompleteSearch terms' s
RS.autocompleteSubmit displayView mTerm
-- | Effects
-- |
-- Drawing the phylo
useFirstEffect' do
(sortSources >>> flip T.write_ sourcesBox) o.sources
(sortSources >>> flip T.write_ sources) o.sources
RS.setGlobalD3Reference window d3
RS.setGlobalDependencies window (PhyloDataSet o)
RS.drawPhylo
......@@ -163,67 +159,79 @@ layoutCpt = here.component "layout" cpt where
o.ancestorLinks
o.branchLinks
o.bb
RS.changeDisplayView displayView
T.write_ true isReadyBox
RS.changeDisplayView displayView'
T.write_ true isBuilt
-- @NOTE #219: handling global variables
-- (see `Resources.js` how they are being used)
T.write_ (window .. "terms") termsBox
T.write_ (window .. "terms") terms
-- (see `Gargantext.Components.PhyloExplorer.Resources` > JavaScript >
-- `pubsub` for detailed explanations)
useFirstEffect' do
RS.subscribe (show ExtractedTermsEvent) $ flip T.write_ extractedTermsBox
RS.subscribe (show ExtractedTermsEvent) $ flip T.write_ extractedTerms
RS.subscribe (show SelectedTermEvent) $ case _ of
res
| true == null res -> T.write_ Nothing selectedTermBox
| otherwise -> T.write_ (Just res) selectedTermBox
| true == null res -> T.write_ Nothing selectedTerm
| otherwise -> T.write_ (Just res) selectedTerm
RS.subscribe (show SelectedBranchEvent) $ case _ of
res
| true == null res -> T.write_ Nothing selectedBranchBox
| otherwise -> T.write_ (Just res) selectedBranchBox
| true == null res -> T.write_ Nothing selectedBranch
| otherwise -> T.write_ (Just res) selectedBranch
RS.subscribe (show SelectedSourceEvent) $ sourceCallback
RS.subscribe (show DisplayViewEvent) $ read >>> case _ of
Nothing -> pure unit
Just res -> T.write_ res displayViewBox
Nothing -> R.nothing
Just res -> T.write_ res displayView
RS.subscribe (show ExtractedCountEvent) $ JSON.readJSON >>> case _ of
Left _ ->
T.write_ Nothing extractedCountBox
T.write_ Nothing extractedCount
Right (res :: ExtractedCount) ->
T.write_ (Just res) extractedCountBox
T.write_ (Just res) extractedCount
R.useEffect1' isIsolineDisplayed do
R.useEffect1' isIsolineDisplayed' do
mEl <- querySelector document ".phylo-isoline"
case mEl of
Nothing -> pure unit
Nothing -> R.nothing
Just el -> do
style <- pure $ (el .. "style")
pure $ (style .= "display") $
isIsolineDisplayed ? "flex" $ "none"
isIsolineDisplayed' ? "flex" $ "none"
-- @NOTE #219: handling global variables (eg. via `window`)
-- (see `Resources.js` how they are being used)
useUpdateEffect1' displayView do
pure $ (window .= "displayView") (show displayView)
-- Render
---------
useUpdateEffect1' displayView' do
pure $ (window .= "displayView") (show displayView')
-- Educational behavior: automatically opening sidebar on first selection
useUpdateEffect3'
selectedTerm'
selectedBranch'
selectedSource'
if (sideBarDisplayed' == InitialClosed)
then
T.write_ Opened sideBarDisplayed
*> T.write_ SelectionTab sideBarTabView
else
R.nothing
-- | Render
-- |
pure $
H.div
{ className: intercalate " "
[ "phylo"
, not isDisplayed ? "phylo--preloading" $ ""
, not isBuilt' ? "phylo--preloading" $ ""
]
}
[
-- Preloading spinner
R2.if' (not isDisplayed) $
R2.if' (not isBuilt') $
H.div
{ className: "phylo__spinner-wrapper" }
......@@ -237,55 +245,56 @@ layoutCpt = here.component "layout" cpt where
[
R2.fragmentWithKey topBarPortalKey
[
R2.if' (isDisplayed) $
R2.if' (isBuilt') $
topBar
{ sources
, source
, sourceCallback
, search
{ sourceCallback
, searchCallback
, result
, resultCallback
, toolBar: toolBarDisplayedBox
, sideBar: sideBarDisplayedBox
}
]
]
,
-- Sidebar
-- Sidebar + Focus frame
H.div
{ className: "phylo__sidebar"
-- @XXX: ReactJS lack of "keep-alive" feature workaround solution
-- @link https://github.com/facebook/react/issues/12039
, style: { display: sideBarDisplayed == Opened? "block" $ "none" }
}
{ className: "phylo__frame" }
[
sideBar
{ nodeId
, docCount: o.nbDocs
, foundationCount: o.nbFoundations
, periodCount: o.nbPeriods
, termCount: o.nbTerms
, groupCount: o.nbGroups
, branchCount: o.nbBranches
, extractedTerms
, extractedCount
, selectedTerm
, selectedBranch
, selectedSource
, selectTermCallback
-- Doc focus
R2.fromMaybe_ frameDoc' \(frameDoc :: FrameDoc) ->
H.div
{ className: "phylo__focus" }
[
H.div
{ className: "phylo__focus__inner" }
[
H.text $ "hello"
]
]
,
-- Sidebar
H.div
{ className: "phylo__sidebar"
-- @XXX: ReactJS lack of "keep-alive" feature workaround solution
-- @link https://github.com/facebook/react/issues/12039
, style: { display: sideBarDisplayed' == Opened ? "block" $ "none" }
}
[
H.div
{ className: "phylo__sidebar__inner" }
[
sideBar
{ selectTermCallback }
]
]
]
,
-- Toolbar
R2.if' (toolBarDisplayed) $
R2.if' (toolBarDisplayed') $
toolBar
{ resetViewCallback: const RS.resetView
, exportCallback: const RS.exportViz
, unselectCallback: unselectCallback
, displayView
{ resetViewCallback : const RS.resetView
, exportCallback : const RS.exportViz
, unselectCallback : unselectCallback
, changeViewCallback
, isolineBox: isIsolineDisplayedBox
}
,
-- Iso Line
......
......@@ -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" }
[