Commit b4aba663 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-node-calc-parser

parents 6e088030 a969007f
...@@ -126,6 +126,20 @@ brew install yarn ...@@ -126,6 +126,20 @@ brew install yarn
For other platforms, please refer to [the yarn website](https://www.yarnpkg.com/). For other platforms, please refer to [the yarn website](https://www.yarnpkg.com/).
#### Purescript build tools
Once you have yarn installed you can install the necessary purescript build tools:
```shell
yarn global add purescript spago pulp
```
In order to use those tools you might need to add the yarn global package install location to your path. On linux this can be done by adding the following line at the end of your `.bashrc` file:
```shell
export PATH="$(yarn global bin):$PATH"
```
## Development ## Development
### Docker environment ### Docker environment
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -1138,4 +1138,572 @@ select.form-control { ...@@ -1138,4 +1138,572 @@ select.form-control {
width: 100%; width: 100%;
} }
/* fonts */
@font-face {
font-family: "Inter-Regular";
font-style: normal;
font-weight: 400;
src: url("../fonts/phylo/Inter-Regular.woff2") format("woff2"), url("../fonts/phylo/Inter-Regular.woff") format("woff");
}
@font-face {
font-family: "Inter-Bold";
font-style: normal;
font-weight: 700;
src: url("../fonts/phylo/Inter-Bold.woff2") format("woff2"), url("../fonts/phylo/Inter-Bold.woff") format("woff");
}
/* grid */
.phylo {
font-family: "Inter-Regular";
font-size: 16px;
display: grid;
grid-template-columns: repeat(15, 1fr);
grid-template-rows: 2% 7% 7% auto 1%;
grid-gap: 10px;
height: 100vh;
color: #0d1824;
}
/* ---- row 1 ---- */
.phylo-title {
grid-row: 1;
grid-column: 1/2;
align-items: center;
text-align: right;
margin-top: 0.5px;
}
.phylo-folder {
grid-row: 1;
grid-column: 2/16;
align-items: center;
}
/* -------------------- */
.phylo-corpus {
grid-row: 2/3;
grid-column: 1/2;
/*background : #3E75B3;*/
font-size: 14px;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-phylo {
grid-row: 3/4;
grid-column: 1/2;
font-size: 14px;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-corpus-info {
grid-row: 2/3;
grid-column: 2/4;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
}
.phylo-phylo-info {
grid-row: 3/4;
grid-column: 2/4;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
}
.phylo-how {
grid-row: 2/4;
grid-column: 1/2;
z-index: 2;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-isoline {
grid-row: 2/4;
grid-column: 3/15;
/*background: rgba(223,216,200,0.25); */
}
.phylo-isoline-info {
grid-row: 2/4;
grid-column: 15/16;
padding-top: 20%;
padding-bottom: 20%;
padding-left: 15px;
}
.phylo-isoline-info .btn-group {
display: initial;
}
/* -------------------- */
.phylo-scape {
grid-row: 4;
grid-column: 1/15;
/*background: #FFCC73;*/
}
.phylo-timeline {
grid-row: 4;
grid-column: 1/2;
}
.phylo-graph {
grid-row: 4;
grid-column: 15/16;
/*background: #FFCC73;*/
}
/* classes */
/* ---------- icons ---------- */
a {
color: inherit;
cursor: pointer;
}
.how {
cursor: pointer;
position: relative;
}
.tooltip {
font-family: "Inter-Regular";
font-size: 14px;
font-style: normal;
font-weight: 400;
}
i.how span {
position: absolute;
width: 300px;
color: #FFFFFF;
background: #0d1824;
height: 30px;
line-height: 30px;
text-align: center;
visibility: hidden;
border-radius: 6px;
}
i.how span:after {
content: "";
position: absolute;
top: 50%;
right: 100%;
margin-top: -8px;
width: 0;
height: 0;
border-right: 8px solid #0d1824;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
i.how:hover span {
visibility: visible;
left: 100%;
top: 50%;
margin-top: -15px;
margin-left: 15px;
z-index: 999;
}
.switch > .far + .fa,
.switch:hover > .far {
display: none;
}
.switch:hover > .far + .fa {
display: inherit;
color: #0d1824;
}
/* ---------- fonts ---------- */
.font-bold {
font-family: "Inter-Bold";
text-transform: uppercase;
}
.font-small {
color: #0d1824;
font-size: 12px;
}
.header {
/*font-weight: bold;*/
/*text-transform: uppercase;*/
font-weight: 500;
cursor: pointer;
}
.header:hover {
font-weight: bold;
}
/* ---------- input ---------- */
.button {
background-color: white;
border: 1.5px solid #0d1824;
cursor: pointer;
}
.button:hover {
background-color: #0d1824;
color: white;
}
.btn-group button {
margin-top: 1px;
margin-bottom: 1px;
display: block;
width: 40px;
}
.draw {
display: none;
}
.phylo-focus {
fill: #f8381f;
color: #f8381f;
}
.reset {
visibility: hidden;
}
.label {
visibility: hidden;
}
.heading {
visibility: hidden;
}
.export {
visibility: hidden;
}
.headed {
background-color: #0d1824;
color: white;
}
.labeled {
background-color: #0d1824;
color: white;
}
.input-file {
display: inline-block;
cursor: pointer;
}
.input-file:hover {
border-bottom: 1.5px solid #0d1824;
}
.input-name {
font-style: italic;
opacity: 0.7;
font-size: 14px;
padding-left: 6px;
padding-right: 6px;
}
/* ---------- axis ---------- */
.x-axis path {
stroke: #EBE4DD;
stroke-width: 1.5px;
}
.y-axis path {
stroke: #EBE4DD;
stroke-width: 1.5px;
}
.y-highlight {
stroke: #f3be54;
stroke-width: 1.5px;
}
.x-mark {
fill: #4A5C70;
stroke-width: 1px;
stroke: #fff;
}
.x-mark-over {
fill: #f3be54;
}
.x-mark-focus {
fill: #f8381f;
}
.tick text {
font-family: "Inter-Regular";
}
.tick text:hover {
cursor: pointer;
}
.y-label {
font-size: 10px;
font-family: "Inter-Regular";
font-weight: normal;
}
.y-label-bold {
font-size: 12px;
font-family: "Inter-Bold";
font-weight: bold;
}
.y-mark-year-inner {
fill: #4A5C70;
}
.y-mark-year-inner-highlight {
fill: #f3be54;
}
.y-mark-year-outer {
fill: #fff;
stroke: #4A5C70;
stroke-width: 1px;
}
.y-mark-year-outer-highlight {
fill: #fff;
stroke: #f3be54;
stroke-width: 3px;
}
.y-mark-month {
fill: #4A5C70;
}
/* ---------- group ---------- */
.group-outer {
stroke-width: 0.8px;
stroke: #fff;
fill: #fff;
}
.group-inner {
stroke-width: 0.8px;
stroke: #0d1824;
fill: #0d1824;
/*cursor: pointer;*/
z-index: 10;
}
.group-heading {
fill: #fff;
stroke: #B5B5B5;
}
.group-focus {
stroke: #f8381f;
}
.source-focus {
stroke: #67a9cf;
}
.group-unfocus {
stroke: #A9A9A9;
}
.group-path {
cursor: pointer;
}
/* ---------- labels ---------- */
.ngrams {
visibility: hidden;
}
.term {
cursor: pointer;
}
.term:hover {
font-weight: bold;
fill: #f8381f;
}
.term-path {
fill: none;
stroke: #F0684D;
stroke-width: 1.5px;
}
.emerging {
/*text-decoration: underline #F0684D;*/
/*fill:#5AA350;*/
/*fill: #5AA350;*/
fill: #F8381F;
}
.decreasing {
/*text-decoration: underline #74B5FF;*/
fill: #11638F;
}
.path-focus {
fill: none;
stroke: #F0684D;
stroke-width: 1.5px;
}
.path-unfocus {
stroke: #A9A9A9;
}
.path-heading {
stroke: #B5B5B5;
}
/* ---------- phylo ---------- */
.branch-hover {
fill: #f3be54;
opacity: 0.5;
}
/* elements */
#file-path {
display: none;
}
/* axis */
.axisRight {
font-family: "Inter-Regular";
font-size: 10px;
}
/* isoline */
.peak {
stroke: white;
stroke-width: 1px;
font-family: "Inter-Regular";
font-size: 14px;
text-anchor: middle;
visibility: visible;
}
.peak-over {
font-size: 18px;
stroke-width: 2px;
cursor: pointer;
stroke: #f3be54;
z-index: 100;
}
.peak-focus {
font-size: 18px;
stroke-width: 2px;
stroke: #F0684D;
}
.peak-focus-source {
font-size: 18px;
stroke-width: 2px;
stroke: #67a9cf;
}
.peak-label {
text-align: center;
font-family: "Inter-Regular";
font-size: 14px;
font-style: normal;
font-weight: 400;
color: #FFFFFF;
border-radius: 3px;
border-style: solid;
border-width: 2px;
border-color: white;
background: #0d1824;
padding: 5px;
z-index: 10;
position: absolute;
visibility: hidden;
}
.word-cloud {
font-family: "Inter-Regular";
font-size: 12px;
}
.search {
margin-left: 10px;
visibility: hidden;
position: absolute;
z-index: 7;
font-size: 14px;
background-color: transparent;
outline: 0;
border-width: 0 0 2px;
border-color: #0d1824;
}
.search-label {
visibility: hidden;
margin-left: 5px;
}
.search:focus {
border-color: #F0684D;
}
.autocomplete {
margin-left: 10px;
visibility: hidden;
position: absolute;
z-index: 7;
font-size: 14px;
background-color: transparent;
color: silver;
z-index: 1;
border: none;
}
.loading {
visibility: hidden;
}
.phylo-name {
visibility: hidden;
text-transform: capitalize;
font-weight: bold;
}
.select-source {
margin-left: 10px;
display: none;
border: 1.5px solid #0d1824;
cursor: pointer;
outline: 0;
background: transparent;
border-image: none;
outline-offset: -2px;
outline-color: transparent;
box-shadow: none;
-webkit-appearance: none;
}
option {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
/*# sourceMappingURL=sass.css.map */ /*# sourceMappingURL=sass.css.map */
{"version":3,"sourceRoot":"","sources":["../../src/sass/_menu.sass","../../src/sass/_context_menu.sass","../../src/sass/_graph.sass","../../src/sass/_login.sass","../../src/sass/_tree.sass","../../src/sass/abstract/_members.scss","../../src/sass/_code_editor.sass","../../src/sass/_styles.sass","../../src/sass/_range_slider.sass","../../src/sass/_annotation.sass","../../src/sass/_folder_view.sass"],"names":[],"mappings":"AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;;AAEF;EACI;EACA;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;;;AAEF;AACI;EACA;;;AAEJ;AACI;EACA;;;AAGJ;AACA;EACI;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;EACA;;;AAEF;EACE;;;AAME;EACE;;;AC9DN;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AClBF;EACE;EACA;EACA;;;AAEF;AAkCE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAxCA;EAZA;EACA;EAEA;EAWE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA;;AAGA;EACE;EACA;;AACN;EACE;;AACF;EACE;;AAEF;EApCA;EACA;EAEA;EAmCE;EACA;;AACF;EACE;;AACF;EACE;;AAWF;EAEE;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAEJ;EACE;;AAEA;EACE;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ACjFJ;EACE;;AACA;EACE;EACA;;;AAEJ;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;AAEE;EACE;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAEJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;;;AAGF;EACE;;;AAEJ;EACI;EACA;;;AAGF;EACE;;;AAEJ;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AC1GF;EACE;;;AAGA;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGA;EACE;;AACF;EACE;;AAEJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AACF;EACE;;AAGN;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AAIR;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAEV;EACE;;AACF;EACE;;AAEE;EACE;;AACF;EACE;;AACN;EACE;;AAEE;EACE;;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEF;EACE;;AAEA;EACE;EACA;;AAEJ;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAIF;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAGR;EACE;;AAEA;EACE;EACA;;AC5HO;ED+HP;;;ACxIO;ED2IP;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AAEF;EACE;;;AAEN;EAWE;EAGA;EACA,eAvPkB;;AAyPlB;EACE;EACA;;AC/LO;ED4MP;EACA,MAFW;EAGX;;;ACvNO;ED2NP;EACA,OAFW;EAGX;;;AAGA;EACE;;AAEA;EACE;;AAGJ;EACE;;AACF;EACE;;AACF;EACE;;AAGJ;EACE;EACA;;AAEA;EAGE;EACA,WAzSa;EA4Sb;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AACA;EACE;;AAEN;EACE;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;;AAEJ;EAEE;EACA;;;AAEF;EAGE;EAKA;EACA;EACA;EACA;EAGA;EACA;EACA;EAKA;;AAHA;EACE;;;AAKJ;EAGE;;AC3SS;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;ACgDO;ED8SP;;AAEA;EACE;;;AC1TK;ED6TP;;AAEA;EACE;;;AAON;EAIE;EAEA;EACA;EACA;EACA,QApYmC;EAqYnC;;ACxUS;ED4UP;;;ACrVO;EDwVP;;;AAEJ;EAGE;EAEA;EACA;EACA;EACA,QAvZgC;EAwZhC;;AC1VS;ED8VP;;;ACvWO;ED0WP;;;AAKF;EAIE;EACA;;AC3WO;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;AA6ZA;EACE,eA3a+B;;;AAibjC;EACE;;AAEE;EACE;;;AE/ZN;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EApCR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AA0CM;EACE;EACA;EACA;EACA;EACA;EA5CR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAkDE;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAGE;EACE;;AAEF;EACE;;AAEV;EACE;;AAEA;EACE;;;AC9FN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAII;EACE;EACA;;AAEJ;EACE;EACA;;AF4CK;EEzCP;;AAEA;EACE;;;AF6BK;EE1BP;;AAEA;EACE;;;AAGN;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;;AAIA;EACE;;AACA;EACE;EACA;;AACF;EACE;;AACA;EACE;;AACJ;EACE;;;AAER;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;AAGI;EACE;;AACF;EACE;;;AAEN;EACE;EACA;EACA;;;AAIA;EACE;;AACF;EACE;;;AAEJ;EACE;;AACA;EACE;;;AAEJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAEF;EACE;;AAEA;EACE;;AAEF;EACE;;;AAGJ;EAKE;;AAEA;EACE;;AAIE;EACE;;AACF;EACE;;AAEJ;EAGE;;AAGJ;EACE,SAtBa;EAuBb;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKE;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;;AAKN;EACE;;AFjGO;EEoGL;;;AF7GK;EEgHL;;;AAEJ;EACE,SAZa;;;AAcjB;EAKE;;AAEA;EACE;EACA,OARa;EASb,QATa;EAUb,MAToB;EAUpB,KATmB;;;AC/KvB;EACE;AACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AAGN;EACE;;;ACxBJ;EACE;;AAEA;EANE;EACA;;AAQF;EAbE;EACA;;AAeF;EAhBE;EACA;;AAkBF;EAnBE;EACA;;AAqBF;EA1BE;EACA,kBANyB;;AAkC3B;EA7BE;EACA,kBAPqB;;AAsCvB;EAhCE;EACA,kBAJoB;;;AAuCtB;EApCE;EACA,kBANyB;;AA4C3B;EAvCE;EACA,kBAPqB;;AAgDvB;EA1CE;EACA,kBAJoB;;;ACNxB;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA","file":"sass.css"} {"version":3,"sourceRoot":"","sources":["../../src/sass/_menu.sass","../../src/sass/_context_menu.sass","../../src/sass/_graph.sass","../../src/sass/_login.sass","../../src/sass/_tree.sass","../../src/sass/abstract/_members.scss","../../src/sass/_code_editor.sass","../../src/sass/_styles.sass","../../src/sass/_range_slider.sass","../../src/sass/_annotation.sass","../../src/sass/_folder_view.sass","../../src/sass/_phylo.scss"],"names":[],"mappings":"AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;;AAEF;EACI;EACA;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;;;AAEF;AACI;EACA;;;AAEJ;AACI;EACA;;;AAGJ;AACA;EACI;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;EACA;;;AAEF;EACE;;;AAME;EACE;;;AC9DN;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AClBF;EACE;EACA;EACA;;;AAEF;AAkCE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAxCA;EAZA;EACA;EAEA;EAWE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA;;AAGA;EACE;EACA;;AACN;EACE;;AACF;EACE;;AAEF;EApCA;EACA;EAEA;EAmCE;EACA;;AACF;EACE;;AACF;EACE;;AAWF;EAEE;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAEJ;EACE;;AAEA;EACE;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ACjFJ;EACE;;AACA;EACE;EACA;;;AAEJ;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;AAEE;EACE;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAEJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;;;AAGF;EACE;;;AAEJ;EACI;EACA;;;AAGF;EACE;;;AAEJ;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AC1GF;EACE;;;AAGA;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGA;EACE;;AACF;EACE;;AAEJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AACF;EACE;;AAGN;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AAIR;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAEV;EACE;;AACF;EACE;;AAEE;EACE;;AACF;EACE;;AACN;EACE;;AAEE;EACE;;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEF;EACE;;AAEA;EACE;EACA;;AAEJ;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAIF;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAGR;EACE;;AAEA;EACE;EACA;;AC5HO;ED+HP;;;ACxIO;ED2IP;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AAEF;EACE;;;AAEN;EAWE;EAGA;EACA,eAvPkB;;AAyPlB;EACE;EACA;;AC/LO;ED4MP;EACA,MAFW;EAGX;;;ACvNO;ED2NP;EACA,OAFW;EAGX;;;AAGA;EACE;;AAEA;EACE;;AAGJ;EACE;;AACF;EACE;;AACF;EACE;;AAGJ;EACE;EACA;;AAEA;EAGE;EACA,WAzSa;EA4Sb;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AACA;EACE;;AAEN;EACE;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;;AAEJ;EAEE;EACA;;;AAEF;EAGE;EAKA;EACA;EACA;EACA;EAGA;EACA;EACA;EAKA;;AAHA;EACE;;;AAKJ;EAGE;;AC3SS;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;ACgDO;ED8SP;;AAEA;EACE;;;AC1TK;ED6TP;;AAEA;EACE;;;AAON;EAIE;EAEA;EACA;EACA;EACA,QApYmC;EAqYnC;;ACxUS;ED4UP;;;ACrVO;EDwVP;;;AAEJ;EAGE;EAEA;EACA;EACA;EACA,QAvZgC;EAwZhC;;AC1VS;ED8VP;;;ACvWO;ED0WP;;;AAKF;EAIE;EACA;;AC3WO;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;AA6ZA;EACE,eA3a+B;;;AAibjC;EACE;;AAEE;EACE;;;AE/ZN;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EApCR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AA0CM;EACE;EACA;EACA;EACA;EACA;EA5CR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAkDE;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAGE;EACE;;AAEF;EACE;;AAEV;EACE;;AAEA;EACE;;;AC9FN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAII;EACE;EACA;;AAEJ;EACE;EACA;;AF4CK;EEzCP;;AAEA;EACE;;;AF6BK;EE1BP;;AAEA;EACE;;;AAGN;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;;AAIA;EACE;;AACA;EACE;EACA;;AACF;EACE;;AACA;EACE;;AACJ;EACE;;;AAER;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;AAGI;EACE;;AACF;EACE;;;AAEN;EACE;EACA;EACA;;;AAIA;EACE;;AACF;EACE;;;AAEJ;EACE;;AACA;EACE;;;AAEJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAEF;EACE;;AAEA;EACE;;AAEF;EACE;;;AAGJ;EAKE;;AAEA;EACE;;AAIE;EACE;;AACF;EACE;;AAEJ;EAGE;;AAGJ;EACE,SAtBa;EAuBb;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKE;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;;AAKN;EACE;;AFjGO;EEoGL;;;AF7GK;EEgHL;;;AAEJ;EACE,SAZa;;;AAcjB;EAKE;;AAEA;EACE;EACA,OARa;EASb,QATa;EAUb,MAToB;EAUpB,KATmB;;;AC/KvB;EACE;AACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AAGN;EACE;;;ACxBJ;EACE;;AAEA;EANE;EACA;;AAQF;EAbE;EACA;;AAeF;EAhBE;EACA;;AAkBF;EAnBE;EACA;;AAqBF;EA1BE;EACA,kBANyB;;AAkC3B;EA7BE;EACA,kBAPqB;;AAsCvB;EAhCE;EACA,kBAJoB;;;AAuCtB;EApCE;EACA,kBANyB;;AA4C3B;EAvCE;EACA,kBAPqB;;AAgDvB;EA1CE;EACA,kBAJoB;;;ACNxB;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;;;AChBF;AAEA;EACE;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAIF;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAEA;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;AAEA;EACE;EACA;AACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;AACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;AAEA;EACE;EACA;AACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;AACA;;;AAIF;AAGA;AAEA;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EAAU;EACV;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AAAA;EAEE;;;AAEF;EACE;EACA;;;AAKF;AAGA;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;AACE;AACA;EACA;EACA;;;AAGF;EACE;;;AAGF;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAEA;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;AACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAEA;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAWF;EACE;EACA;EACA;;;AAGF;AACE;AACA;AACA;EACA;;;AAGF;AACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAEA;EACE;EACA;;;AAGF;AAEA;EACE;;;AAIF;AAEA;EACE;EACA;;;AAGF;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA","file":"sass.css"}
\ No newline at end of file \ No newline at end of file
{ {
"name": "Gargantext", "name": "Gargantext",
"version": "0.0.4.7.2", "version": "0.0.4.8.5",
"scripts": { "scripts": {
"generate-purs-packages-nix": "./nix/generate-purs-packages.nix", "generate-purs-packages-nix": "./nix/generate-purs-packages.nix",
"generate-psc-packages-nix": "./nix/generate-packages-json.bash", "generate-psc-packages-nix": "./nix/generate-packages-json.bash",
...@@ -29,15 +29,20 @@ ...@@ -29,15 +29,20 @@
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.9.2", "@popperjs/core": "^2.9.2",
"@urql/core": "^2.3.3",
"aes-js": "^3.1.1", "aes-js": "^3.1.1",
"base-x": "^3.0.2", "base-x": "^3.0.2",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"bootstrap-dark": "^1.0.3", "bootstrap-dark": "^1.0.3",
"create-react-class": "^15.6.3", "create-react-class": "^15.6.3",
"d3": "^7.0.0",
"echarts": "^5.1.2", "echarts": "^5.1.2",
"echarts-for-react": "^3.0.1", "echarts-for-react": "^3.0.1",
"graphql": "^15.6.1",
"graphql-ws": "^5.5.0",
"highlightjs": "^9.16.2", "highlightjs": "^9.16.2",
"immer": "^9.0.5", "immer": "^9.0.5",
"isomorphic-unfetch": "^3.1.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"pullstate": "^1.20.6", "pullstate": "^1.20.6",
"react": "^17.0.2", "react": "^17.0.2",
......
let upstream = let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.14.4-20210826/packages.dhall sha256:eee0765aa98e0da8fc414768870ad588e7cada060f9f7c23c37385c169f74d9f https://github.com/purescript/package-sets/releases/download/psc-0.14.4-20211030/packages.dhall sha256:5cd7c5696feea3d3f84505d311348b9e90a76c4ce3684930a0ff29606d2d816c
let overrides = let overrides =
{ globals = { globals =
...@@ -206,6 +206,26 @@ let additions = ...@@ -206,6 +206,26 @@ let additions =
, repo = "https://github.com/natefaubion/purescript-convertable-options" , repo = "https://github.com/natefaubion/purescript-convertable-options"
, version = "v1.0.0" , version = "v1.0.0"
} }
, d3 =
{ dependencies =
[ "aff"
, "aff-promise"
, "dom-simple"
, "easy-ffi"
, "effect"
, "exceptions"
, "foreign"
, "functions"
, "js-date"
, "maybe"
, "prelude"
, "psci-support"
, "tuples"
, "web-dom"
]
, repo = "https://github.com/cgenie/purescript-d3"
, version = "v0.9.1"
}
} }
in upstream // overrides // additions in upstream // overrides // additions
...@@ -26,6 +26,7 @@ to generate this file without the comments in this block. ...@@ -26,6 +26,7 @@ to generate this file without the comments in this block.
, "control" , "control"
, "convertable-options" , "convertable-options"
, "css" , "css"
, "d3"
, "datetime" , "datetime"
, "dom-filereader" , "dom-filereader"
, "dom-simple" , "dom-simple"
...@@ -41,6 +42,7 @@ to generate this file without the comments in this block. ...@@ -41,6 +42,7 @@ to generate this file without the comments in this block.
, "formula" , "formula"
, "functions" , "functions"
, "globals" , "globals"
, "graphql-client"
, "http-methods" , "http-methods"
, "integers" , "integers"
, "js-timers" , "js-timers"
......
...@@ -4,6 +4,7 @@ module Gargantext.Components.DocsTable where ...@@ -4,6 +4,7 @@ module Gargantext.Components.DocsTable where
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple.Event as DE import DOM.Simple.Event as DE
import Data.Array (any)
import Data.Array as A import Data.Array as A
import Data.Either (Either) import Data.Either (Either)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
...@@ -21,33 +22,36 @@ import Data.Symbol (SProxy(..)) ...@@ -21,33 +22,36 @@ import Data.Symbol (SProxy(..))
import Data.Tuple (Tuple(..)) import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, Milliseconds(..), delay, launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Effect.Timer (setTimeout)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Category (rating) import Gargantext.Components.Category (rating)
import Gargantext.Components.Category.Types (Star(..)) import Gargantext.Components.Category.Types (Star(..))
import Gargantext.Components.DocsTable.DocumentFormCreation (documentFormCreation) import Gargantext.Components.DocsTable.DocumentFormCreation as DFC
import Gargantext.Components.DocsTable.Types (DocumentsView(..), Hyperdata(..), LocalUserScore, Query, Response(..), Year, sampleData, showSource) import Gargantext.Components.DocsTable.Types (DocumentsView(..), Hyperdata(..), LocalUserScore, Query, Response(..), Year, sampleData, showSource)
import Gargantext.Components.Nodes.Lists.Types as NT import Gargantext.Components.Nodes.Lists.Types as NT
import Gargantext.Components.Nodes.Texts.Types (SidePanelTriggers)
import Gargantext.Components.Score as GCS
import Gargantext.Components.Nodes.Texts.Types as TextsT import Gargantext.Components.Nodes.Texts.Types as TextsT
import Gargantext.Components.Reload (reloadContext, textsReloadContext)
import Gargantext.Components.Table as TT import Gargantext.Components.Table as TT
import Gargantext.Components.Table.Types as TT import Gargantext.Components.Table.Types as TT
import Gargantext.Config.REST (RESTError, logRESTError) import Gargantext.Config.REST (RESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends, url) import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.Loader (useLoader, useLoaderWithCacheAPI, HashedResponse(..)) import Gargantext.Hooks.Loader (useLoader, useLoaderWithCacheAPI, HashedResponse(..))
import Gargantext.Routes (SessionRoute(NodeAPI)) import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId, get, delete) import Gargantext.Sessions (Session, sessionId, get, delete)
import Gargantext.Types (ListId, NodeID, NodeType(..), OrderBy(..), SidePanelState(..), TabSubType, TabType, TableResult, showTabType') import Gargantext.Types (ListId, NodeID, NodeType(..), OrderBy(..), SidePanelState(..), TabSubType, TabType, TableResult, showTabType')
import Gargantext.Types as GT
import Gargantext.Utils (sortWith, (?)) import Gargantext.Utils (sortWith, (?))
import Gargantext.Utils.CacheAPI as GUC import Gargantext.Utils.CacheAPI as GUC
import Gargantext.Utils.QueryString (joinQueryStrings, mQueryParam, mQueryParamS, queryParam, queryParamS) import Gargantext.Utils.QueryString (joinQueryStrings, mQueryParam, mQueryParamS, queryParam, queryParamS)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as GUT import Gargantext.Utils.Toestand as GUT
import Gargantext.Utils.Toestand as T2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Simple.JSON as JSON import Simple.JSON as JSON
...@@ -143,19 +147,38 @@ docViewCpt = here.component "docView" cpt where ...@@ -143,19 +147,38 @@ docViewCpt = here.component "docView" cpt where
onDocumentCreationPending /\ onDocumentCreationPendingBox <- onDocumentCreationPending /\ onDocumentCreationPendingBox <-
R2.useBox' false R2.useBox' false
-- Context
mReloadContext <- R.useContext textsReloadContext
-- @toggleModalCallback -- @toggleModalCallback
toggleModal <- pure $ const $ toggleModal <- pure $ const $
T.modify_ not isDocumentModalVisibleBox T.modify_ not isDocumentModalVisibleBox
-- @onCreateDocumentEnd <AsyncProgress>
onCreateDocumentEnd <- pure $ \asyncProgress -> do
here.log2 "[DocsTables] NodeDocument task:" asyncProgress
T.write_ false onDocumentCreationPendingBox
toggleModal unit
case mReloadContext of
Nothing -> pure unit
Just b -> T2.reload b
-- @createDocumentCallback -- @createDocumentCallback
-- @WIP: remote business for document creation
createDocumentCallback <- pure $ \fdata -> launchAff_ do createDocumentCallback <- pure $ \fdata -> launchAff_ do
liftEffect $ T.write_ true onDocumentCreationPendingBox liftEffect $
T.write_ true onDocumentCreationPendingBox
delay $ Milliseconds 2000.0 eTask <- DFC.create session nodeId fdata
liftEffect $ T.write_ false onDocumentCreationPendingBox handleRESTError boxes.errors eTask
\t -> liftEffect $ launchDocumentCreationProgress
boxes
session
nodeId
t
onCreateDocumentEnd
-- Render -- Render
pure $ pure $
...@@ -206,13 +229,54 @@ docViewCpt = here.component "docView" cpt where ...@@ -206,13 +229,54 @@ docViewCpt = here.component "docView" cpt where
, hasCollapsibleBackground: false , hasCollapsibleBackground: false
} }
[ [
documentFormCreation DFC.documentFormCreation
{ callback: createDocumentCallback { callback: createDocumentCallback
, status: onDocumentCreationPending ? Deferred $ Enabled , status: onDocumentCreationPending ? Deferred $ Enabled
} }
] ]
] ]
launchDocumentCreationProgress ::
Boxes
-> Session
-> GT.ID
-> GT.AsyncTaskWithType
-> (GT.AsyncProgress -> Effect Unit)
-> Effect Unit
launchDocumentCreationProgress boxes session nodeId currentTask cbk
= void $ setTimeout 1000 $ launchAff_ $
scanDocumentCreationProgress boxes session nodeId currentTask cbk
scanDocumentCreationProgress ::
Boxes
-> Session
-> GT.ID
-> GT.AsyncTaskWithType
-> (GT.AsyncProgress -> Effect Unit)
-> Aff Unit
scanDocumentCreationProgress boxes session nodeId currentTask cbk = do
eTask <- DFC.createProgress session nodeId currentTask
handleRESTError boxes.errors eTask
\asyncProgress -> liftEffect do
let
GT.AsyncProgress { status } = asyncProgress
endingStatusList =
[ GT.IsFinished
, GT.IsKilled
, GT.IsFailure
]
hasEndingStatus s = any (_ # s # eq) endingStatusList
if (hasEndingStatus status)
then
cbk asyncProgress
else
launchDocumentCreationProgress boxes session nodeId currentTask cbk
---------------------------------------------------
type SearchBarProps = type SearchBarProps =
( query :: T.Box Query ) ( query :: T.Box Query )
......
module Gargantext.Components.DocsTable.DocumentFormCreation module Gargantext.Components.DocsTable.DocumentFormCreation
( documentFormCreation ( documentFormCreation
, FormData , FormData
, create, createProgress
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -8,18 +9,25 @@ import Gargantext.Prelude ...@@ -8,18 +9,25 @@ import Gargantext.Prelude
import DOM.Simple.Console (log3) import DOM.Simple.Console (log3)
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Foldable (foldl, intercalate) import Data.Foldable (foldl, intercalate)
import Data.Maybe (Maybe(..))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Config.REST (RESTError)
import Gargantext.Hooks.FormValidation (VForm, useFormValidation) import Gargantext.Hooks.FormValidation (VForm, useFormValidation)
import Gargantext.Hooks.FormValidation.Unboxed as FV import Gargantext.Hooks.FormValidation.Unboxed as FV
import Gargantext.Hooks.StateRecord (useStateRecord) import Gargantext.Hooks.StateRecord (useStateRecord)
import Gargantext.Routes as GR
import Gargantext.Sessions (Session, post, get)
import Gargantext.Types as GT
import Gargantext.Utils (nbsp, (?)) import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Record (merge) import Record as Record
import Record.Extra (pick) import Record.Extra (pick)
import Type.Proxy (Proxy(..))
type Props = type Props =
( callback :: Record FormData -> Effect Unit ( callback :: Record FormData -> Effect Unit
...@@ -30,7 +38,7 @@ type Props = ...@@ -30,7 +38,7 @@ type Props =
type Options = ( | FormData ) type Options = ( | FormData )
options :: Record Options options :: Record Options
options = merge {} defaultData options = Record.merge {} defaultData
documentFormCreation :: forall r. R2.OptLeaf Options Props r documentFormCreation :: forall r. R2.OptLeaf Options Props r
documentFormCreation = R2.optLeaf component options documentFormCreation = R2.optLeaf component options
...@@ -48,7 +56,7 @@ component = R.hooksComponent "documentFormCreation" cpt where ...@@ -48,7 +56,7 @@ component = R.hooksComponent "documentFormCreation" cpt where
result <- fv.try (\_ -> documentFormValidation state) result <- fv.try (\_ -> documentFormValidation state)
case result of case result of
Left err -> log3 "document form validation error" state err Left err -> log3 "documentFormCreation validation error" state err
Right _ -> props.callback state Right _ -> props.callback state
-- Render -- Render
...@@ -128,13 +136,42 @@ component = R.hooksComponent "documentFormCreation" cpt where ...@@ -128,13 +136,42 @@ component = R.hooksComponent "documentFormCreation" cpt where
[ [
B.formInput $ B.formInput $
{ placeholder: "ex: author1, author2, …" { placeholder: "ex: author1, author2, …"
} `merge` bindStateKey "authors" } `Record.merge` bindStateKey "authors"
, ,
R2.if' (fv.hasError' "authors") $ R2.if' (fv.hasError' "authors") $
H.div { className: "form-group__error" } H.div { className: "form-group__error" }
[ H.text "Please enter at least one author" ] [ H.text "Please enter at least one author" ]
] ]
] ]
,
-- Date
H.div
{ className: intercalate " "
[ "form-group"
, (fv.hasError' "date") ?
"form-group--error" $
mempty
]
}
[
H.div
{ className: "form-group__label" }
[
H.label {} [ H.text $ "Date" ]
]
,
H.div
{ className: "form-group__field" }
[
B.formInput $
{ type: "date"
} `Record.merge` bindStateKey "date"
,
R2.if' (fv.hasError' "date") $
H.div { className: "form-group__error" }
[ H.text "Please enter a valid date" ]
]
]
, ,
-- Abstract -- Abstract
H.div H.div
...@@ -143,7 +180,8 @@ component = R.hooksComponent "documentFormCreation" cpt where ...@@ -143,7 +180,8 @@ component = R.hooksComponent "documentFormCreation" cpt where
] ]
} }
[ [
H.div { className: "form-group__label" } H.div
{ className: "form-group__label" }
[ [
H.label {} [ H.text $ "Abstract" <> nbsp 1 ] H.label {} [ H.text $ "Abstract" <> nbsp 1 ]
, ,
...@@ -152,11 +190,12 @@ component = R.hooksComponent "documentFormCreation" cpt where ...@@ -152,11 +190,12 @@ component = R.hooksComponent "documentFormCreation" cpt where
[ H.text "optional" ] [ H.text "optional" ]
] ]
, ,
H.div { className: "form-group__field" } H.div
{ className: "form-group__field" }
[ [
B.formTextarea $ B.formTextarea $
{ rows: 5 { rows: 5
} `merge` bindStateKey "abstract" } `Record.merge` bindStateKey "abstract"
] ]
] ]
, ,
...@@ -178,6 +217,7 @@ type FormData = ...@@ -178,6 +217,7 @@ type FormData =
( title :: String ( title :: String
, source :: String , source :: String
, authors :: String , authors :: String
, date :: String
, abstract :: String , abstract :: String
) )
...@@ -186,6 +226,7 @@ defaultData = ...@@ -186,6 +226,7 @@ defaultData =
{ title : "" { title : ""
, source : "" , source : ""
, authors : "" , authors : ""
, date : ""
, abstract : "" , abstract : ""
} }
...@@ -196,4 +237,51 @@ documentFormValidation r = foldl append mempty rules ...@@ -196,4 +237,51 @@ documentFormValidation r = foldl append mempty rules
[ FV.nonEmpty "title" r.title [ FV.nonEmpty "title" r.title
, FV.nonEmpty "source" r.source , FV.nonEmpty "source" r.source
, FV.nonEmpty "authors" r.authors , FV.nonEmpty "authors" r.authors
, FV.date "date" r.date
] ]
---------------------------------------------------
create ::
Session
-> GT.ID
-> Record FormData
-> Aff (Either RESTError GT.AsyncTaskWithType)
create session nodeId =
rename
>>> post session request
>=> case _ of
Left err -> pure $ Left err
Right task -> pure $ Right $ GT.AsyncTaskWithType
{ task
, typ: GT.NodeDocument
}
where
request = GR.NodeAPI GT.Node (Just nodeId)
(GT.asyncTaskTypePath GT.NodeDocument)
rename = Record.rename
(Proxy :: Proxy "source")
(Proxy :: Proxy "sources")
createProgress ::
Session
-> GT.ID
-> GT.AsyncTaskWithType
-> Aff (Either RESTError GT.AsyncProgress)
createProgress
session
nodeId
(GT.AsyncTaskWithType { task: GT.AsyncTask { id } })
=
get session request
where
request = GR.NodeAPI GT.Node (Just nodeId)
(GT.asyncTaskTypePath GT.NodeDocument <> pollParams)
pollParams = "/" <> id <> "/poll?limit1"
...@@ -18,12 +18,13 @@ type GraphId = Int ...@@ -18,12 +18,13 @@ type GraphId = Int
newtype Node = Node { newtype Node = Node {
attributes :: Cluster attributes :: Cluster
, id_ :: String , children :: Array String
, label :: String , id_ :: String
, size :: Int , label :: String
, type_ :: String , size :: Int
, x :: Number , type_ :: String
, y :: Number , x :: Number
, y :: Number
} }
x_coordP = SProxy :: SProxy "x_coord" x_coordP = SProxy :: SProxy "x_coord"
......
...@@ -15,6 +15,7 @@ stEdgeToGET { _original } = _original ...@@ -15,6 +15,7 @@ stEdgeToGET { _original } = _original
stNodeToGET :: Record ST.Node -> GET.Node stNodeToGET :: Record ST.Node -> GET.Node
stNodeToGET { id, label, x, y, _original: GET.Node { attributes, size, type_ } } = GET.Node { stNodeToGET { id, label, x, y, _original: GET.Node { attributes, size, type_ } } = GET.Node {
attributes attributes
, children: []
, id_: id , id_: id
, label , label
, size , size
......
module Gargantext.Components.GraphQL where
import Gargantext.Prelude
import Affjax.RequestHeader as ARH
import Data.Argonaut.Decode (JsonDecodeError)
import Data.Bifunctor (lmap)
import Data.List.Types (NonEmptyList)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Foreign (unsafeToForeign, ForeignError)
import Gargantext.Components.GraphQL.User (User, UserInfo, UserInfoM)
import Gargantext.Sessions (Session(..))
import Gargantext.Utils.Reactix as R2
import GraphQL.Client.Args (type (==>))
import GraphQL.Client.BaseClients.Urql (UrqlClient, createClient)
import GraphQL.Client.Query (queryWithDecoder)
import GraphQL.Client.Types (class GqlQuery, Client, class QueryClient)
import Simple.JSON as JSON
import Unsafe.Coerce (unsafeCoerce)
here :: R2.Here
here = R2.here "Gargantext.Components.GraphQL"
--client :: Client AffjaxClient Schema Void Void
--client = Client $ AffjaxClient "http://localhost:8008/gql" []
-- | Run a graphQL query with a custom decoder and custom options
gqlQuery ::
forall client schema query returns a b queryOpts mutationOpts.
QueryClient client queryOpts mutationOpts =>
GqlQuery schema query returns =>
JSON.ReadForeign returns =>
--(queryOpts -> queryOpts) ->
(Client client schema a b) ->
String ->
query ->
Aff returns
gqlQuery = queryWithDecoder (unsafeToForeign >>> JSON.read >>> lmap toJsonError)
toJsonError :: NonEmptyList ForeignError -> JsonDecodeError
toJsonError = unsafeCoerce -- map ForeignErrors to JsonDecodeError as you wish
getClient :: Session -> Effect (Client UrqlClient Schema Mutation Void)
getClient (Session { token }) = createClient { headers, url: "http://localhost:8008/gql" }
where
headers = [ ARH.RequestHeader "Authorization" $ "Bearer " <> token ]
queryGql ::
forall query returns.
GqlQuery Schema query returns =>
JSON.ReadForeign returns =>
Session
-> String
-> query
-> Aff returns
queryGql session name q = do
--query client name q
client <- liftEffect $ getClient session
gqlQuery (client :: Client UrqlClient Schema Mutation Void) name q
--query_ "http://localhost:8008/gql" (Proxy :: Proxy Schema)
-- Schema
type Schema
= { user_infos :: { user_id :: Int } ==> Array UserInfo
, users :: { user_id :: Int } ==> Array User
}
type Mutation
= { update_user_info :: UserInfoM ==> Int }
module Gargantext.Components.GraphQL.AffjaxSimpleJSONClient
(AffjaxClient(..))
where
import Prelude
import Affjax (Error(..), Response, URL, defaultRequest, printError, request)
import Affjax.RequestBody as RequestBody
import Affjax.RequestHeader (RequestHeader(..))
import Affjax.ResponseFormat as ResponseFormat
import Data.Argonaut.Core (Json)
import Data.Either (Either(..))
import Data.HTTP.Method as Method
import Data.List.NonEmpty as DLN
import Data.Maybe (Maybe(..))
import Data.MediaType.Common (applicationJSON)
import Effect.Aff (Aff, error, throwError)
import Foreign (unsafeToForeign)
import GraphQL.Client.Types (class QueryClient)
import Simple.JSON as JSON
data AffjaxClient
= AffjaxClient URL (Array RequestHeader)
--
-- instance queryClient :: QueryClient AffjaxClient Unit Unit where
-- clientQuery _ (AffjaxClient url headers) name q vars = throwLeft =<< convertJsonResponse =<< queryPostForeign "query" url headers name q vars
-- clientMutation _ (AffjaxClient url headers) name q vars = throwLeft =<< convertJsonResponse =<< queryPostForeign "mutation" url headers name q vars
-- defQueryOpts = const unit
-- defMutationOpts = const unit
--
-- throwLeft :: forall r body. Either Error { body :: body | r } -> Aff body
-- throwLeft = case _ of
-- Left err -> throwError $ error $ printError err
-- Right { body } -> pure body
--
-- queryPostForeign ::
-- forall d.
-- JSON.WriteForeign d =>
-- String -> URL -> Array RequestHeader -> String -> String -> d -> Aff (Either Error (Response String))
-- queryPostForeign opStr url headers queryName q vars = do
-- request
-- defaultRequest
-- { withCredentials = true
-- , url = url
-- , method = Left Method.POST
-- --, responseFormat = ResponseFormat.json
-- , responseFormat = ResponseFormat.string
-- , content =
-- Just
-- -- $ RequestBody.Json
-- -- $ encodeJson
-- $ RequestBody.String
-- $ JSON.writeJSON
-- { query: opStr <> " " <> queryName <> " " <> q
-- , variables: vars
-- , operationName: queryName
-- }
-- , headers = headers <> [ ContentType applicationJSON ]
-- }
--
-- convertJsonResponse :: Either Error (Response String) -> Aff (Either Error (Response Json))
-- convertJsonResponse (Left err) = pure $ Left err
-- convertJsonResponse (Right res@{ body }) = pure $ case JSON.readJSON body of
-- Left err -> Left $ ResponseBodyError (DLN.head err) (res { body = unsafeToForeign body })
-- Right body' -> Right $ res { body = toJSON body' }
--
-- foreign import toJSON :: forall d. JSON.ReadForeign d => d -> Json
--
--
module Gargantext.Components.GraphQL.User where
import Gargantext.Prelude
import Data.Array as A
import Data.Lens (Lens', lens)
import Data.Maybe (Maybe(..), fromMaybe, maybe)
import GraphQL.Client.Args (NotNull(..), (=>>))
import GraphQL.Client.Variable (Var(..))
import GraphQL.Client.Variables (withVars)
import Type.Proxy (Proxy(..))
type UserInfo
= { ui_id :: Int
, ui_username :: String
, ui_email :: String
, ui_title :: Maybe String
, ui_source :: Maybe String
, ui_cwFirstName :: Maybe String
, ui_cwLastName :: Maybe String
, ui_cwOrganization :: Array String
, ui_cwLabTeamDepts :: Array String
, ui_cwOffice :: Maybe String
, ui_cwCity :: Maybe String
, ui_cwCountry :: Maybe String
, ui_cwRole :: Maybe String
, ui_cwTouchPhone :: Maybe String
, ui_cwTouchMail :: Maybe String }
type UserInfoM
= { ui_id :: NotNull Int
, ui_username :: String
, ui_email :: String
, ui_title :: String
, ui_source :: String
, ui_cwFirstName :: String
, ui_cwLastName :: String
, ui_cwOrganization :: (Array String)
, ui_cwLabTeamDepts :: (Array String)
, ui_cwOffice :: String
, ui_cwCity :: String
, ui_cwCountry :: String
, ui_cwRole :: String
, ui_cwTouchPhone :: String
, ui_cwTouchMail :: String }
userInfoQuery = { user_infos: { user_id: Var :: _ "id" Int } =>>
{ ui_id: unit
, ui_username: unit
, ui_email: unit
, ui_title: unit
, ui_source: unit
, ui_cwFirstName: unit
, ui_cwLastName: unit
, ui_cwCity: unit
, ui_cwCountry: unit
, ui_cwLabTeamDepts: unit
, ui_cwOrganization: unit
, ui_cwOffice: unit
, ui_cwRole: unit
, ui_cwTouchMail: unit
, ui_cwTouchPhone: unit }
}
_ui_cwFirstName :: Lens' UserInfo String
_ui_cwFirstName = lens getter setter
where
getter ({ ui_cwFirstName: val }) = fromMaybe "" val
setter ui val = ui { ui_cwFirstName = Just val }
_ui_cwLastName :: Lens' UserInfo String
_ui_cwLastName = lens getter setter
where
getter ({ ui_cwLastName: val }) = fromMaybe "" val
setter ui val = ui { ui_cwLastName = Just val }
_ui_cwCity :: Lens' UserInfo String
_ui_cwCity = lens getter setter
where
getter ({ ui_cwCity: val }) = fromMaybe "" val
setter ui val = ui { ui_cwCity = Just val }
_ui_cwCountry :: Lens' UserInfo String
_ui_cwCountry = lens getter setter
where
getter ({ ui_cwCountry: val }) = fromMaybe "" val
setter ui val = ui { ui_cwCountry = Just val }
_ui_cwLabTeamDepts :: Lens' UserInfo (Array String)
_ui_cwLabTeamDepts = lens getter setter
where
getter ({ ui_cwLabTeamDepts: val }) = val
setter ui val = ui { ui_cwLabTeamDepts = val }
_ui_cwLabTeamDeptsFirst :: Lens' UserInfo String
_ui_cwLabTeamDeptsFirst = lens getter setter
where
getter ({ ui_cwLabTeamDepts: val }) = fromMaybe "" $ A.head val
setter ui val = ui { ui_cwLabTeamDepts = fromMaybe [val] $ A.updateAt 0 val ui.ui_cwLabTeamDepts }
_ui_cwOffice :: Lens' UserInfo String
_ui_cwOffice = lens getter setter
where
getter ({ ui_cwOffice: val }) = fromMaybe "" val
setter ui val = ui { ui_cwOffice = Just val }
_ui_cwOrganization :: Lens' UserInfo (Array String)
_ui_cwOrganization = lens getter setter
where
getter ({ ui_cwOrganization: val }) = val
setter ui val = ui { ui_cwOrganization = val }
_ui_cwOrganizationFirst :: Lens' UserInfo String
_ui_cwOrganizationFirst = lens getter setter
where
getter ({ ui_cwOrganization: val }) = fromMaybe "" $ A.head val
setter ui val = ui { ui_cwOrganization = fromMaybe [val] $ A.updateAt 0 val ui.ui_cwOrganization }
_ui_cwRole :: Lens' UserInfo String
_ui_cwRole = lens getter setter
where
getter ({ ui_cwRole: val }) = fromMaybe "" val
setter ui val = ui { ui_cwRole = Just val }
_ui_cwTouchMail :: Lens' UserInfo String
_ui_cwTouchMail = lens getter setter
where
getter ({ ui_cwTouchMail: val }) = fromMaybe "" val
setter ui val = ui { ui_cwTouchMail = Just val }
_ui_cwTouchPhone :: Lens' UserInfo String
_ui_cwTouchPhone = lens getter setter
where
getter ({ ui_cwTouchPhone: val }) = fromMaybe "" val
setter ui val = ui { ui_cwTouchPhone = Just val }
type User
= { u_id :: Int
, u_hyperdata ::
{ shared :: Maybe
{ title :: Maybe String
, source :: Maybe String
, who :: Maybe
{ firstName :: Maybe String
, lastName :: Maybe String
}
, "where" :: Array
{ organization :: Array String }
}
}
, u_username :: String
, u_email :: String
}
showUser { u_id
, u_username
, u_email } = "[" <> show u_id <> "] " <> u_username <> " :: " <> u_email
showMUser u = maybe "" showUser u
...@@ -11,6 +11,7 @@ import Effect.Aff (Aff) ...@@ -11,6 +11,7 @@ import Effect.Aff (Aff)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.DocsTable as DT import Gargantext.Components.DocsTable as DT
import Gargantext.Components.DocsTable.Types (Year) import Gargantext.Components.DocsTable.Types (Year)
import Gargantext.Components.GraphQL.User (UserInfo)
import Gargantext.Components.NgramsTable as NT import Gargantext.Components.NgramsTable as NT
import Gargantext.Components.NgramsTable.Core as NTC import Gargantext.Components.NgramsTable.Core as NTC
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData) import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData)
...@@ -50,13 +51,13 @@ modeTabType' Books = CTabAuthors ...@@ -50,13 +51,13 @@ modeTabType' Books = CTabAuthors
modeTabType' Communication = CTabAuthors modeTabType' Communication = CTabAuthors
type TabsProps = type TabsProps =
( boxes :: Boxes ( boxes :: Boxes
, cacheState :: T.Box LTypes.CacheState , cacheState :: T.Box LTypes.CacheState
, contactData :: ContactData , defaultListId :: Int
, frontends :: Frontends , frontends :: Frontends
, nodeId :: Int , nodeId :: Int
, session :: Session , session :: Session
, sidePanel :: T.Box (Maybe (Record TextsT.SidePanel)) , sidePanel :: T.Box (Maybe (Record TextsT.SidePanel))
) )
tabs :: R2.Leaf TabsProps tabs :: R2.Leaf TabsProps
...@@ -68,21 +69,21 @@ tabsCpt = here.component "tabs" cpt where ...@@ -68,21 +69,21 @@ tabsCpt = here.component "tabs" cpt where
yearFilter <- T.useBox (Nothing :: Maybe Year) yearFilter <- T.useBox (Nothing :: Maybe Year)
pure $ Tab.tabs { activeTab, tabs: tabs' yearFilter props } pure $ Tab.tabs { activeTab, tabs: tabs' yearFilter props }
tabs' yearFilter props@{ boxes, sidePanel } = tabs' yearFilter props@{ boxes, defaultListId, sidePanel } =
[ "Documents" /\ docs [ "Documents" /\ docs
, "Patents" /\ ngramsView (viewProps Patents) , "Patents" /\ ngramsView (viewProps Patents)
, "Books" /\ ngramsView (viewProps Books) , "Books" /\ ngramsView (viewProps Books)
, "Communication" /\ ngramsView (viewProps Communication) , "Communication" /\ ngramsView (viewProps Communication)
, "Trash" /\ docs -- TODO pass-in trash mode , "Trash" /\ docs -- TODO pass-in trash mode
] where ] where
viewProps mode = Record.merge props { defaultListId: props.contactData.defaultListId viewProps mode = Record.merge props { mode }
, mode } totalRecords = 4736 -- TODO lol
totalRecords = 4736 -- TODO lol
docs = DT.docViewLayout (Record.merge { boxes, sidePanel } $ Record.merge dtCommon dtExtra) docs = DT.docViewLayout (Record.merge { boxes, sidePanel } $ Record.merge dtCommon dtExtra)
dtCommon = RX.pick props :: Record DTCommon dtCommon = RX.pick props :: Record DTCommon
dtExtra = dtExtra =
{ chart: mempty { chart: mempty
, listId: props.contactData.defaultListId --, listId: props.contactData.defaultListId
, listId: defaultListId
, mCorpusId: Nothing , mCorpusId: Nothing
, showSearch: true , showSearch: true
, tabType: TabPairing TabDocs , tabType: TabPairing TabDocs
...@@ -100,8 +101,7 @@ type DTCommon = ...@@ -100,8 +101,7 @@ type DTCommon =
) )
type NgramsViewTabsProps = type NgramsViewTabsProps =
( defaultListId :: Int ( mode :: Mode
, mode :: Mode
| TabsProps ) | TabsProps )
ngramsView :: R2.Leaf NgramsViewTabsProps ngramsView :: R2.Leaf NgramsViewTabsProps
......
...@@ -4,25 +4,27 @@ module Gargantext.Components.Nodes.Annuaire.User ...@@ -4,25 +4,27 @@ module Gargantext.Components.Nodes.Annuaire.User
) )
where where
import Gargantext.Prelude import Gargantext.Prelude
import Data.Either (Either) import Data.Either (Either)
import Data.Lens as L
import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Components.GraphQL.User (UserInfo)
import Gargantext.Components.Nodes.Annuaire.Tabs as Tabs import Gargantext.Components.Nodes.Annuaire.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contact (getUserInfoWithReload, saveUserInfo, contactInfos)
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (Contact(..), ContactData, ContactTouch(..), ContactWhere(..), ContactWho(..), HyperdataContact(..), HyperdataUser(..), _city, _country, _firstName, _labTeamDeptsJoinComma, _lastName, _mail, _office, _organizationJoinComma, _ouFirst, _phone, _role, _shared, _touch, _who, defaultContactTouch, defaultContactWhere, defaultContactWho, defaultHyperdataContact, defaultHyperdataUser) import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (Contact(..), ContactData, ContactTouch(..), ContactWhere(..), ContactWho(..), HyperdataContact(..), HyperdataUser(..), _city, _country, _firstName, _labTeamDeptsJoinComma, _lastName, _mail, _office, _organizationJoinComma, _ouFirst, _phone, _role, _shared, _touch, _who, defaultContactTouch, defaultContactWhere, defaultContactWho, defaultHyperdataContact, defaultHyperdataUser)
import Gargantext.Components.Nodes.Lists.Types as LT import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Config.REST (RESTError, logRESTError) import Gargantext.Config.REST (RESTError, logRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (WithSession, WithSessionContext, Session, get, put, sessionId) import Gargantext.Sessions (WithSession, WithSessionContext, Session, get, put, sessionId)
import Gargantext.Types (NodeType(..)) import Gargantext.Types (FrontendError, NodeType(..))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import Reactix as R import Reactix as R
...@@ -53,98 +55,10 @@ displayCpt = here.component "display" cpt ...@@ -53,98 +55,10 @@ displayCpt = here.component "display" cpt
[ H.div { className: "col-md-2" } [ H.img { src: "/images/Gargantextuel-212x300.jpg"} ] [ H.div { className: "col-md-2" } [ H.img { src: "/images/Gargantextuel-212x300.jpg"} ]
, H.div { className: "col-md-1"} [] , H.div { className: "col-md-1"} []
, H.div { className: "col-md-8"} children , H.div { className: "col-md-8"} children
]]]] ]
-- | TODO format data in better design (UI) shape
contactInfos :: HyperdataUser -> (HyperdataUser -> Effect Unit) -> Array R.Element
contactInfos h onUpdateHyperdata = item <$> contactInfoItems
where
item {label, defaultVal, lens} =
contactInfoItem { hyperdata: h
, label
, lens
, onUpdateHyperdata
, placeholder: defaultVal }
contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: HyperdataUserLens}
contactInfoItems =
[ {label: "Last Name" , defaultVal: "Empty Last Name" , lens: _shared <<< _who <<< _lastName }
, {label: "First Name" , defaultVal: "Empty First Name" , lens: _shared <<< _who <<< _firstName }
, {label: "Organisation" , defaultVal: "Empty Organisation" , lens: _shared <<< _ouFirst <<< _organizationJoinComma}
, {label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _shared <<< _ouFirst <<< _labTeamDeptsJoinComma}
, {label: "Office" , defaultVal: "Empty Office" , lens: _shared <<< _ouFirst <<< _office }
, {label: "City" , defaultVal: "Empty City" , lens: _shared <<< _ouFirst <<< _city }
, {label: "Country" , defaultVal: "Empty Country" , lens: _shared <<< _ouFirst <<< _country }
, {label: "Role" , defaultVal: "Empty Role" , lens: _shared <<< _ouFirst <<< _role }
, {label: "Phone" , defaultVal: "Empty Phone" , lens: _shared <<< _ouFirst <<< _touch <<< _phone }
, {label: "Mail" , defaultVal: "Empty Mail" , lens: _shared <<< _ouFirst <<< _touch <<< _mail }
]
type HyperdataUserLens = L.ALens' HyperdataUser String
type ContactInfoItemProps =
( hyperdata :: HyperdataUser
, label :: String
, lens :: HyperdataUserLens
, onUpdateHyperdata :: HyperdataUser -> Effect Unit
, placeholder :: String
)
contactInfoItem :: Record ContactInfoItemProps -> R.Element
contactInfoItem props = R.createElement contactInfoItemCpt props []
contactInfoItemCpt :: R.Component ContactInfoItemProps
contactInfoItemCpt = here.component "contactInfoItem" cpt
where
cpt {hyperdata, label, lens, onUpdateHyperdata, placeholder} _ = do
isEditing <- T.useBox false
isEditing' <- T.useLive T.unequal isEditing
let value = (L.view cLens hyperdata) :: String
valueRef <- R.useRef value
pure $ H.div { className: "form-group row" } [
H.span { className: "col-sm-2 col-form-label" } [ H.text label ]
, item isEditing' isEditing valueRef
]
where
cLens = L.cloneLens lens
item false isEditing valueRef =
H.div { className: "input-group col-sm-6" } [
H.input { className: "form-control"
, defaultValue: placeholder'
, disabled: 1
, type: "text" }
, H.div { className: "btn input-group-append"
, on: { click: onClick } } [
H.div { className: "input-group-text fa fa-pencil" } []
] ]
] ]
where ]
placeholder' = R.readRef valueRef
onClick _ = T.write_ true isEditing
item true isEditing valueRef =
H.div { className: "input-group col-sm-6" } [
inputWithEnter {
autoFocus: true
, className: "form-control"
, defaultValue: R.readRef valueRef
, onBlur: R.setRef valueRef
, onEnter: onClick
, onValueChanged: R.setRef valueRef
, placeholder
, type: "text"
}
, H.div { className: "btn input-group-append"
, on: { click: onClick } } [
H.div { className: "input-group-text fa fa-floppy-o" } []
]
]
where
onClick _ = do
T.write_ true isEditing
let newHyperdata = (L.over cLens (\_ -> R.readRef valueRef) hyperdata) :: HyperdataUser
onUpdateHyperdata newHyperdata
{- {-
listElement :: Array R.Element -> R.Element listElement :: Array R.Element -> R.Element
...@@ -191,16 +105,16 @@ userLayoutWithKeyCpt = here.component "userLayoutWithKey" cpt where ...@@ -191,16 +105,16 @@ userLayoutWithKeyCpt = here.component "userLayoutWithKey" cpt where
cacheState <- T.useBox LT.CacheOn cacheState <- T.useBox LT.CacheOn
useLoader { errorHandler useLoader { errorHandler
, loader: getUserWithReload , loader: getUserInfoWithReload
, path: { nodeId, reload: reload', session } , path: { nodeId, reload: reload', session }
, render: \contactData@{contactNode: Contact {name, hyperdata}} -> , render: \userInfo@{ ui_username } ->
H.ul { className: "col-md-12 list-group" } [ H.ul { className: "col-md-12 list-group" } [
display { title: fromMaybe "no name" name } display { title: fromMaybe "no name" (Just ui_username) }
(contactInfos hyperdata (onUpdateHyperdata reload)) (contactInfos userInfo (onUpdateUserInfo boxes.errors reload))
, Tabs.tabs { , Tabs.tabs {
boxes boxes
, cacheState , cacheState
, contactData , defaultListId: 424242
, frontends , frontends
, nodeId , nodeId
, session , session
...@@ -210,31 +124,26 @@ userLayoutWithKeyCpt = here.component "userLayoutWithKey" cpt where ...@@ -210,31 +124,26 @@ userLayoutWithKeyCpt = here.component "userLayoutWithKey" cpt where
} }
where where
errorHandler = logRESTError here "[userLayoutWithKey]" errorHandler = logRESTError here "[userLayoutWithKey]"
onUpdateHyperdata :: T2.ReloadS -> HyperdataUser -> Effect Unit onUpdateUserInfo :: T.Box (Array FrontendError) -> T2.ReloadS -> UserInfo -> Effect Unit
onUpdateHyperdata reload hd = do onUpdateUserInfo errors reload ui = do
launchAff_ $ do launchAff_ $ do
_ <- saveContactHyperdata session nodeId hd res <- saveUserInfo session nodeId ui
liftEffect $ T2.reload reload handleRESTError errors res $ \_ ->
liftEffect $ T2.reload reload
-- | toUrl to get data XXX --saveContactHyperdata :: Session -> Int -> HyperdataUser -> Aff (Either RESTError Int)
getContact :: Session -> Int -> Aff (Either RESTError ContactData) --saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "")
getContact session id = do
eContactNode <- get session $ Routes.NodeAPI Node (Just id) ""
-- TODO: we need a default list for the pairings
--defaultListIds <- get $ toUrl endConfigStateful Back (Children NodeList 0 1 Nothing) $ Just id
--case (head defaultListIds :: Maybe (NodePoly HyperdataList)) of
-- Just (NodePoly { id: defaultListId }) ->
-- pure {contactNode, defaultListId}
-- Nothing ->
-- throwError $ error "Missing default list"
pure $ (\contactNode -> { contactNode, defaultListId: 424242 }) <$> eContactNode
getUserWithReload :: { nodeId :: Int
, reload :: T2.Reload
, session :: Session} -> Aff (Either RESTError ContactData)
getUserWithReload {nodeId, session} = getContact session nodeId
saveContactHyperdata :: Session -> Int -> HyperdataUser -> Aff (Either RESTError Int)
saveContactHyperdata session id h = do
put session (Routes.NodeAPI Node (Just id) "") h
-- | toUrl to get data XXX
--getContact :: Session -> Int -> Aff (Either RESTError ContactData)
--getContact session id = do
-- eContactNode <- get session $ Routes.NodeAPI Node (Just id) ""
-- -- TODO: we need a default list for the pairings
-- --defaultListIds <- get $ toUrl endConfigStateful Back (Children NodeList 0 1 Nothing) $ Just id
-- --case (head defaultListIds :: Maybe (NodePoly HyperdataList)) of
-- -- Just (NodePoly { id: defaultListId }) ->
-- -- pure {contactNode, defaultListId}
-- -- Nothing ->
-- -- throwError $ error "Missing default list"
-- pure $ (\contactNode -> { contactNode, defaultListId: 424242 }) <$> eContactNode
--
module Gargantext.Components.Nodes.Annuaire.User.Contact module Gargantext.Components.Nodes.Annuaire.User.Contact
( module Gargantext.Components.Nodes.Annuaire.User.Contacts.Types ( module Gargantext.Components.Nodes.Annuaire.User.Contacts.Types
, contactInfos
, contactLayout , contactLayout
, getUserInfo
, getUserInfoWithReload
, saveContactHyperdata
, saveUserInfo
) where ) where
import Data.Either (Either) import Gargantext.Components.GraphQL.User
import Gargantext.Prelude
import Affjax.RequestBody (RequestBody(..))
import Data.Array as A
import Data.Either (Either(..))
import Data.Lens as L import Data.Lens as L
import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes) import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.GraphQL (getClient, queryGql)
import Gargantext.Components.InputWithEnter (inputWithEnter) import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (Contact'(..), ContactData', ContactTouch(..), ContactWhere(..), ContactWho(..), HyperdataContact(..), HyperdataUser(..), _city, _country, _firstName, _labTeamDeptsJoinComma, _lastName, _mail, _office, _organizationJoinComma, _ouFirst, _phone, _role, _shared, _touch, _who, defaultContactTouch, defaultContactWhere, defaultContactWho, defaultHyperdataContact, defaultHyperdataUser) import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (ContactData', HyperdataContact(..))
import Gargantext.Components.Nodes.Lists.Types as LT import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Config.REST (RESTError, logRESTError) import Gargantext.Config.REST (RESTError(..), logRESTError)
import Gargantext.Ends (Frontends) import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader) import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Prelude
import Gargantext.Routes as Routes import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, get, put, sessionId) import Gargantext.Sessions (Session, get, put, sessionId)
import Gargantext.Types (NodeType(..)) import Gargantext.Types (NodeType(..))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2 import Gargantext.Utils.Toestand as T2
import GraphQL.Client.Args (type (==>), IgnoreArg(..), OrArg(..), onlyArgs, (=>>))
import GraphQL.Client.Query (mutationOpts, mutation)
import GraphQL.Client.Variables (withVars)
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Record as Record import Record as Record
...@@ -51,36 +64,39 @@ displayCpt = here.component "display" cpt ...@@ -51,36 +64,39 @@ displayCpt = here.component "display" cpt
[ H.div { className: "col-md-2" } [ H.img { src: "/images/Gargantextuel-212x300.jpg"} ] [ H.div { className: "col-md-2" } [ H.img { src: "/images/Gargantextuel-212x300.jpg"} ]
, H.div { className: "col-md-1"} [] , H.div { className: "col-md-1"} []
, H.div { className: "col-md-8"} children , H.div { className: "col-md-8"} children
]]]] ]
]
]
]
-- | TODO format data in better design (UI) shape -- | TODO format data in better design (UI) shape
contactInfos :: HyperdataContact -> (HyperdataContact -> Effect Unit) -> Array R.Element contactInfos :: UserInfo -> (UserInfo -> Effect Unit) -> Array R.Element
contactInfos h onUpdateHyperdata = item <$> contactInfoItems where contactInfos userInfo onUpdateUserInfo = item <$> contactInfoItems where
item { label, lens, defaultVal: placeholder } = item { label, lens, defaultVal } =
contactInfoItem { label, lens, onUpdateHyperdata, placeholder, hyperdata: h } contactInfoItem { defaultVal, label, lens, onUpdateUserInfo, userInfo }
contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: HyperdataContactLens} contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: UserInfoLens}
contactInfoItems = contactInfoItems =
[ {label: "Last Name" , defaultVal: "Empty Last Name" , lens: _who <<< _lastName } [ { label: "Last Name" , defaultVal: "Empty Last Name" , lens: _ui_cwLastName }
, {label: "First Name" , defaultVal: "Empty First Name" , lens: _who <<< _firstName } , { label: "First Name" , defaultVal: "Empty First Name" , lens: _ui_cwFirstName }
, {label: "Organisation" , defaultVal: "Empty Organisation" , lens: _ouFirst <<< _organizationJoinComma} , { label: "Organisation" , defaultVal: "Empty Organisation" , lens: _ui_cwOrganizationFirst }
, {label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _ouFirst <<< _labTeamDeptsJoinComma} , { label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _ui_cwLabTeamDeptsFirst }
, {label: "Office" , defaultVal: "Empty Office" , lens: _ouFirst <<< _office } , { label: "Office" , defaultVal: "Empty Office" , lens: _ui_cwOffice }
, {label: "City" , defaultVal: "Empty City" , lens: _ouFirst <<< _city } , { label: "City" , defaultVal: "Empty City" , lens: _ui_cwCity }
, {label: "Country" , defaultVal: "Empty Country" , lens: _ouFirst <<< _country } , { label: "Country" , defaultVal: "Empty Country" , lens: _ui_cwCountry }
, {label: "Role" , defaultVal: "Empty Role" , lens: _ouFirst <<< _role } , { label: "Role" , defaultVal: "Empty Role" , lens: _ui_cwRole }
, {label: "Phone" , defaultVal: "Empty Phone" , lens: _ouFirst <<< _touch <<< _phone } , { label: "Phone" , defaultVal: "Empty Phone" , lens: _ui_cwTouchPhone }
, {label: "Mail" , defaultVal: "Empty Mail" , lens: _ouFirst <<< _touch <<< _mail } , { label: "Mail" , defaultVal: "Empty Mail" , lens: _ui_cwTouchMail }
] ]
type HyperdataContactLens = L.ALens' HyperdataContact String type UserInfoLens = L.ALens' UserInfo String
type ContactInfoItemProps = type ContactInfoItemProps =
( hyperdata :: HyperdataContact ( defaultVal :: String
, label :: String , label :: String
, lens :: HyperdataContactLens , lens :: UserInfoLens
, onUpdateHyperdata :: HyperdataContact -> Effect Unit , onUpdateUserInfo :: UserInfo -> Effect Unit
, placeholder :: String , userInfo :: UserInfo
) )
contactInfoItem :: R2.Leaf ContactInfoItemProps contactInfoItem :: R2.Leaf ContactInfoItemProps
...@@ -88,47 +104,80 @@ contactInfoItem props = R.createElement contactInfoItemCpt props [] ...@@ -88,47 +104,80 @@ contactInfoItem props = R.createElement contactInfoItemCpt props []
contactInfoItemCpt :: R.Component ContactInfoItemProps contactInfoItemCpt :: R.Component ContactInfoItemProps
contactInfoItemCpt = here.component "contactInfoItem" cpt contactInfoItemCpt = here.component "contactInfoItem" cpt
where where
cpt { hyperdata, label, lens, onUpdateHyperdata, placeholder } _ = do cpt { defaultVal, label, lens, onUpdateUserInfo, userInfo } _ = do
isEditing <- T.useBox false isEditing <- T.useBox false
isEditing' <- T.useLive T.unequal isEditing isEditing' <- T.useLive T.unequal isEditing
let value = (L.view cLens hyperdata) :: String let value = (L.view cLens userInfo) :: String
valueRef <- R.useRef value valueBox <- T.useBox value
pure $ pure $
H.div { className: "form-group row" } H.div { className: "form-group row" }
[ H.span { className: "col-sm-2 col-form-label" } [ H.text label ] [ H.span { className: "col-sm-2 col-form-label" } [ H.text label ]
, item isEditing' isEditing valueRef ] , if isEditing' then
itemEditing { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox }
else
itemNotEditing { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox }
]
where where
cLens = L.cloneLens lens cLens = L.cloneLens lens
item false isEditing valueRef =
H.div { className: "input-group col-sm-6" } type ItemProps =
[ H.input ( defaultVal :: String
{ className: "form-control", type: "text" , isEditing :: T.Box Boolean
, defaultValue: placeholder', disabled: true } , lens :: UserInfoLens
, H.div { className: "btn input-group-append", on: { click } } , onUpdateUserInfo :: UserInfo -> Effect Unit
[ H.div { className: "input-group-text fa fa-pencil" } [] ]] , userInfo :: UserInfo
where , valueBox :: T.Box String
placeholder' = R.readRef valueRef )
click _ = T.write_ true isEditing
item true isEditing valueRef = itemNotEditing :: R2.Leaf ItemProps
H.div { className: "input-group col-sm-6" } itemNotEditing props = R.createElement itemNotEditingCpt props []
[ inputWithEnter itemNotEditingCpt :: R.Component ItemProps
{ autoFocus: true itemNotEditingCpt = here.component "itemEditing" cpt where
, className: "form-control" cpt { isEditing, valueBox } _ = do
, defaultValue: R.readRef valueRef valueBox' <- T.useLive T.unequal valueBox
, onBlur: R.setRef valueRef
, onEnter: click pure $ H.div { className: "input-group col-sm-6" }
, onValueChanged: R.setRef valueRef [ H.input
, placeholder { className: "form-control", type: "text"
, type: "text" } , defaultValue: valueBox', disabled: true }
, H.div { className: "btn input-group-append", on: { click } } , H.div { className: "btn input-group-append", on: { click } }
[ H.div { className: "input-group-text fa fa-floppy-o" } [] ]] [ H.div { className: "input-group-text fa fa-pencil" } [] ]
where ]
click _ = do where
T.write_ false isEditing click _ = T.write_ true isEditing
let newHyperdata = (L.over cLens (\_ -> R.readRef valueRef) hyperdata) :: HyperdataContact
onUpdateHyperdata newHyperdata itemEditing :: R2.Leaf ItemProps
itemEditing props = R.createElement itemEditingCpt props []
itemEditingCpt :: R.Component ItemProps
itemEditingCpt = here.component "itemNotEditing" cpt where
cpt { defaultVal, isEditing, lens, onUpdateUserInfo, userInfo, valueBox } _ = do
valueBox' <- T.useLive T.unequal valueBox
pure $ H.div { className: "input-group col-sm-6" }
[ inputWithEnter
{ autoFocus: true
, className: "form-control"
, defaultValue: valueBox'
, onBlur: \v -> T.write_ v valueBox
, onEnter: click
, onValueChanged: \v -> do
here.log2 "[itemEditingCpt] value Changed: " v
T.write_ v valueBox
, placeholder: defaultVal
, type: "text" }
, H.div { className: "btn input-group-append", on: { click } }
[ H.div { className: "input-group-text fa fa-floppy-o" } [] ]
]
where
cLens = L.cloneLens lens
click _ = do
T.write_ false isEditing
value <- T.read valueBox
here.log2 "[itemEditing] value" value
let newUserInfo = (L.set cLens value userInfo) :: UserInfo
onUpdateUserInfo newUserInfo
type ReloadProps = type ReloadProps =
( boxes :: Boxes ( boxes :: Boxes
...@@ -148,6 +197,29 @@ type KeyLayoutProps = ...@@ -148,6 +197,29 @@ type KeyLayoutProps =
saveContactHyperdata :: Session -> Int -> HyperdataContact -> Aff (Either RESTError Int) saveContactHyperdata :: Session -> Int -> HyperdataContact -> Aff (Either RESTError Int)
saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "") saveContactHyperdata session id = put session (Routes.NodeAPI Node (Just id) "")
saveUserInfo :: Session -> Int -> UserInfo -> Aff (Either RESTError Int)
saveUserInfo session id ui = do
client <- liftEffect $ getClient session
res <- mutationOpts
(\m -> m)
client
"update user_info"
{ update_user_info: onlyArgs { ui_id: id
, ui_cwFirstName: ga ui.ui_cwFirstName
, ui_cwLastName: ga ui.ui_cwLastName
, ui_cwOrganization: ui.ui_cwOrganization
, ui_cwLabTeamDepts: ui.ui_cwLabTeamDepts
, ui_cwOffice: ga ui.ui_cwOffice
, ui_cwCity: ga ui.ui_cwCity
, ui_cwCountry: ga ui.ui_cwCountry
, ui_cwRole: ga ui.ui_cwRole
, ui_cwTouchPhone: ga ui.ui_cwTouchPhone
, ui_cwTouchMail: ga ui.ui_cwTouchMail } }
pure $ Right res.update_user_info
where
ga Nothing = ArgL IgnoreArg
ga (Just val) = ArgR val
type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps ) type AnnuaireLayoutProps = ( annuaireId :: Int, session :: Session | ReloadProps )
type AnnuaireKeyLayoutProps = ( annuaireId :: Int | KeyLayoutProps ) type AnnuaireKeyLayoutProps = ( annuaireId :: Int | KeyLayoutProps )
...@@ -175,27 +247,31 @@ contactLayoutWithKeyCpt = here.component "contactLayoutWithKey" cpt where ...@@ -175,27 +247,31 @@ contactLayoutWithKeyCpt = here.component "contactLayoutWithKey" cpt where
_ <- T.useLive T.unequal reload _ <- T.useLive T.unequal reload
cacheState <- T.useBox LT.CacheOn cacheState <- T.useBox LT.CacheOn
useLoader { errorHandler useLoader { errorHandler
, loader: getAnnuaireContact session annuaireId --, loader: getAnnuaireContact session annuaireId
, loader: getUserInfo session
, path: nodeId , path: nodeId
, render: \contactData@{contactNode: Contact' {name, hyperdata}} -> , render: \userInfo@{ ui_username } ->
H.ul { className: "col-md-12 list-group" } H.ul { className: "col-md-12 list-group" }
[ display { title: fromMaybe "no name" name } [ display { title: fromMaybe "no name" (Just ui_username) }
(contactInfos hyperdata (onUpdateHyperdata reload)) (contactInfos userInfo (onUpdateUserInfo reload))
, Tabs.tabs , Tabs.tabs
{ boxes { boxes
, cacheState , cacheState
, contactData , defaultListId: 424242 -- TODO
, frontends , frontends
, nodeId , nodeId
, session , session
, sidePanel: sidePanelTexts , sidePanel: sidePanelTexts
} ] } }
]
}
where where
errorHandler = logRESTError here "[contactLayoutWithKey]" errorHandler = logRESTError here "[contactLayoutWithKey]"
onUpdateHyperdata :: T2.ReloadS -> HyperdataContact -> Effect Unit onUpdateUserInfo :: T2.ReloadS -> UserInfo -> Effect Unit
onUpdateHyperdata reload hd = onUpdateUserInfo reload ui = do
launchAff_ $ launchAff_ $ do
saveContactHyperdata session nodeId hd *> liftEffect (T2.reload reload) _ <- saveUserInfo session nodeId ui
liftEffect (T2.reload reload)
getAnnuaireContact :: Session -> Int -> Int -> Aff (Either RESTError ContactData') getAnnuaireContact :: Session -> Int -> Int -> Aff (Either RESTError ContactData')
getAnnuaireContact session annuaireId id = do getAnnuaireContact session annuaireId id = do
...@@ -208,3 +284,18 @@ getAnnuaireContact session annuaireId id = do ...@@ -208,3 +284,18 @@ getAnnuaireContact session annuaireId id = do
-- Nothing -> -- Nothing ->
-- throwError $ error "Missing default list" -- throwError $ error "Missing default list"
pure $ (\contactNode -> { contactNode, defaultListId: 424242 }) <$> eContactNode pure $ (\contactNode -> { contactNode, defaultListId: 424242 }) <$> eContactNode
getUserInfoWithReload :: { nodeId :: Int
, reload :: T2.Reload
, session :: Session} -> Aff (Either RESTError UserInfo)
getUserInfoWithReload {nodeId, session} = getUserInfo session nodeId -- getContact session nodeId
getUserInfo :: Session -> Int -> Aff (Either RESTError UserInfo)
getUserInfo session id = do
{ user_infos } <- queryGql session "get user infos" $ userInfoQuery `withVars` { id }
liftEffect $ here.log2 "[getUserInfo] user infos" user_infos
pure $ case A.head user_infos of
Nothing -> Left (CustomError $ "user with id " <> show id <> " not found")
-- NOTE Contact is at G.C.N.A.U.C.Types
Just ui -> Right ui
...@@ -47,14 +47,14 @@ modeTabType' Patents = CTabAuthors ...@@ -47,14 +47,14 @@ modeTabType' Patents = CTabAuthors
modeTabType' Books = CTabAuthors modeTabType' Books = CTabAuthors
modeTabType' Communication = CTabAuthors modeTabType' Communication = CTabAuthors
type TabsProps = ( type TabsProps =
boxes :: Boxes ( boxes :: Boxes
, cacheState :: T.Box LTypes.CacheState , cacheState :: T.Box LTypes.CacheState
, contactData :: ContactData' , defaultListId :: Int
, frontends :: Frontends , frontends :: Frontends
, nodeId :: Int , nodeId :: Int
, session :: Session , session :: Session
, sidePanel :: T.Box (Maybe (Record TTypes.SidePanel)) , sidePanel :: T.Box (Maybe (Record TTypes.SidePanel))
) )
tabs :: R2.Leaf TabsProps tabs :: R2.Leaf TabsProps
...@@ -64,7 +64,7 @@ tabsCpt = here.component "tabs" cpt ...@@ -64,7 +64,7 @@ tabsCpt = here.component "tabs" cpt
where where
cpt { boxes cpt { boxes
, cacheState , cacheState
, contactData: {defaultListId} , defaultListId
, frontends , frontends
, nodeId , nodeId
, session , session
...@@ -134,7 +134,6 @@ type NgramsViewTabsProps = ( ...@@ -134,7 +134,6 @@ type NgramsViewTabsProps = (
ngramsView :: R2.Component NgramsViewTabsProps ngramsView :: R2.Component NgramsViewTabsProps
ngramsView = R.createElement ngramsViewCpt ngramsView = R.createElement ngramsViewCpt
ngramsViewCpt :: R.Component NgramsViewTabsProps ngramsViewCpt :: R.Component NgramsViewTabsProps
ngramsViewCpt = here.component "ngramsView" cpt ngramsViewCpt = here.component "ngramsView" cpt
where where
......
module Gargantext.Components.Nodes.Corpus.Phylo where module Gargantext.Components.Nodes.Corpus.Phylo
( phyloLayout
) where
import Gargantext.Prelude import Gargantext.Prelude
( pure, ($) )
-- import Gargantext.Utils.Toestand as T2 import Affjax as AX
-- import Toestand as T import Affjax.ResponseFormat as ResponseFormat
import DOM.Simple.Console (log2)
import Data.Either (Either(..))
import Data.HTTP.Method (Method(..))
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.PhyloExplorer.JSON (PhyloJSONSet)
import Gargantext.Components.PhyloExplorer.Layout (layout)
import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet, parsePhyloJSONSet)
import Gargantext.Sessions (Session) import Gargantext.Sessions (Session)
import Gargantext.Types (NodeID) import Gargantext.Types (NodeID)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Simple.JSON as JSON
import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Nodes.Corpus.Phylo" here = R2.here "Gargantext.Components.Nodes.Corpus.Phylo"
type Props = ( nodeId :: NodeID, session :: Session ) type Props =
( nodeId :: NodeID
, session :: Session
)
phyloLayout :: R2.Component Props phyloLayout :: R2.Component Props
phyloLayout = R.createElement phyloLayoutCpt phyloLayout = R.createElement phyloLayoutCpt
phyloLayoutCpt :: R.Component Props phyloLayoutCpt :: R.Component Props
phyloLayoutCpt = here.component "phyloLayout" cpt where phyloLayoutCpt = here.component "phyloLayout" cpt where
cpt { nodeId, session } content = do cpt _ _ = do
pure $ H.h1 {} [ H.text "Hello Phylo" ]
fetchedDataBox <- T.useBox (Nothing :: Maybe PhyloDataSet)
fetchedData <- T.useLive T.unequal fetchedDataBox
R.useEffectOnce' $ launchAff_ do
result <- fetchPhyloJSON
liftEffect $ case result of
Left err -> log2 "error" err
Right res -> T.write_ (Just res) fetchedDataBox
pure case fetchedData of
Nothing -> mempty
Just phyloDataSet -> layout { phyloDataSet } []
fetchPhyloJSON :: Aff (Either String PhyloDataSet)
fetchPhyloJSON =
let
-- @WIP remove dumb data
url = "http://localhost:5000/js/knowledge-phylomemy.json"
-- url = "http://localhost:5000/js/vaccines_countries_06_2021.json"
request = AX.defaultRequest
{ url = url
, method = Left GET
, responseFormat = ResponseFormat.string
}
in do
result <- request # AX.request
liftEffect $ case result of
Left err -> pure $ Left $ AX.printError err
Right response -> case JSON.readJSON response.body of
Left err -> pure $ Left $ show err
Right (res :: PhyloJSONSet) -> pure $ Right $ parsePhyloJSONSet res
...@@ -20,6 +20,7 @@ import Gargantext.Components.Nodes.Corpus.Document as D ...@@ -20,6 +20,7 @@ import Gargantext.Components.Nodes.Corpus.Document as D
import Gargantext.Components.Nodes.Corpus.Types (CorpusData, CorpusInfo(..), Hyperdata(..), getCorpusInfo) import Gargantext.Components.Nodes.Corpus.Types (CorpusData, CorpusInfo(..), Hyperdata(..), getCorpusInfo)
import Gargantext.Components.Nodes.Lists.Types as LT import Gargantext.Components.Nodes.Lists.Types as LT
import Gargantext.Components.Nodes.Texts.Types as TT import Gargantext.Components.Nodes.Texts.Types as TT
import Gargantext.Components.Reload (textsReloadContext)
import Gargantext.Components.Tab as Tab import Gargantext.Components.Tab as Tab
import Gargantext.Components.Table as Table import Gargantext.Components.Table as Table
import Gargantext.Config.REST (logRESTError) import Gargantext.Config.REST (logRESTError)
...@@ -28,6 +29,7 @@ import Gargantext.Hooks.Loader (useLoader) ...@@ -28,6 +29,7 @@ import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Sessions (WithSession, Session, getCacheState) import Gargantext.Sessions (WithSession, Session, getCacheState)
import Gargantext.Types (CTabNgramType(..), ListId, NodeID, SidePanelState(..), TabSubType(..), TabType(..)) import Gargantext.Types (CTabNgramType(..), ListId, NodeID, SidePanelState(..), TabSubType(..), TabType(..))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Toestand as T import Toestand as T
...@@ -52,11 +54,20 @@ textsLayout = R.createElement textsLayoutCpt ...@@ -52,11 +54,20 @@ textsLayout = R.createElement textsLayoutCpt
textsLayoutCpt :: R.Component Props textsLayoutCpt :: R.Component Props
textsLayoutCpt = here.component "textsLayout" cpt where textsLayoutCpt = here.component "textsLayout" cpt where
cpt { boxes, frontends, nodeId, session } children = do cpt { boxes, frontends, nodeId, session } children = do
pure $ textsLayoutWithKey { key
, boxes _ /\ reloadBox <- R2.useBox' T2.newReload
, frontends
, nodeId pure $
, session } children R.provideContext textsReloadContext (Just reloadBox)
[
textsLayoutWithKey
{ key
, boxes
, frontends
, nodeId
, session } children
]
where where
key = show nodeId key = show nodeId
-- key = show sid <> "-" <> show nodeId -- key = show sid <> "-" <> show nodeId
......
module Gargantext.Components.Nodes.Texts.Types where module Gargantext.Components.Nodes.Texts.Types where
import Data.Maybe (Maybe(..))
import Reactix as R
import Gargantext.Prelude import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Gargantext.Types (ListId, NodeID) import Gargantext.Types (ListId, NodeID)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Toestand as T
data SidePanelState = InitialClosed | Opened | Closed data SidePanelState = InitialClosed | Opened | Closed
derive instance Eq SidePanelState derive instance Eq SidePanelState
...@@ -67,3 +68,13 @@ type SidePanel = ...@@ -67,3 +68,13 @@ type SidePanel =
initialSidePanel :: Maybe (Record SidePanel) initialSidePanel :: Maybe (Record SidePanel)
initialSidePanel = Nothing initialSidePanel = Nothing
-----------------------------------------------------------------
-- @XXX: This custom context solves a wrong monolithic front design where
-- "DocsTable" component is used for many different use cases
-- Normally we would have use the classic "Gargantext.Components.Reload",
-- but we limit side-effects by using another context reference
textsReloadContext :: R.Context (Maybe (T.Box T2.Reload))
textsReloadContext = R.createContext Nothing
exports._drawPhylo = drawPhylo;
exports._drawWordCloud = drawWordCloud;
// set javascript date from a string year
function yearToDate(year) {
var d = new Date()
d.setYear(parseInt(year));
d.setMonth(0);
d.setDate(1);
return d;
}
function stringToDate(str) {
var arr = (str.replace('"','')).split('-');
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setMonth(d.getMonth() - 1);
d.setDate(parseInt(arr[2]));
return d;
}
function utcStringToDate(str) {
var arr = ((str.replace('"','')).replace(' UTC','')).split(/[\s-:]+/);
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setDate(parseInt(arr[2]));
d.setHours(parseInt(arr[3]),parseInt(arr[4]),parseInt(arr[5]))
return d;
}
function stringArrToArr(str) {
var arr = ((str.replace('["','')).replace('"]','')).split('","');
return arr;
}
function intArrToArr(int) {
var arr = ((int.replace('[','')).replace(']','')).split(',');
return arr;
}
function yearToDateHacked(w) {
var d = new Date(2020,0,0);
d.setDate(d.getDate() + (w * 7));
return d
}
function toCoord(id) {
var x = parseFloat(d3.select("#group" + id).attr("cx")),
y = parseFloat(d3.select("#group" + id).attr("cy"));
return [x,y];
}
function toXLabels(branches, groups, xMax) {
var xLabels = branches.map(function(b) {
var bId = b.bId,
xs = groups.filter(g => g.bId == bId).map(g => g.x),
inf = Math.min(...xs),
sup = Math.max(...xs);
return { x : b.x2,
label : b.label.replace(/\"/g, '').replace(/\|/g, ''),
inf : inf,
sup : sup,
bId : bId};
})
return xLabels.map(function(b,i){
var prec = 0,
succ = xMax;
if (i != 0)
prec = xLabels[i -1].sup
if (i != (xLabels.length - 1))
succ = xLabels[i + 1].inf
var w = Math.min(...[(b.x - prec) / 2,(succ - b.x) / 2]),
inf = b.x - w,
sup = b.x + w;
inf = (b.inf < inf) ? b.inf : inf + (w / 10);
sup = (b.sup > sup) ? b.sup : sup - (w / 10);
return { x : (sup + inf) / 2,
label : b.label,
inf : inf,
sup : sup,
bId : b.bId};
})
}
function xOverFlow(ticks,arr) {
ticks.each(function(t,i){
var text = d3.select(this),
chars = d3.select(this).text().split('').reverse(),
nb = chars.length,
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
line = [],
tspan = text.attr("bId",arr[i][1]).text(null).append("tspan").attr("x", 0).attr("y", -14).attr("dy", dy + "em").attr("bId","");
while ((char = chars.pop()) && (tspan.node().getComputedTextLength() < (arr[i][0] - 2))) {
line.push(char);
tspan.text(line.join(''));
}
if (line.length != nb) {
line.slice(-3)
tspan.text(line.join('') + '...')
}
})
}
var branchFocus = [];
function addMarkX(ticks,ws,ids) {
ticks.each(function(t,i){
d3.select(this)
.append("rect")
.attr("x","-" + (ws[i]/2 + 1))
.attr("y","-4")
.attr("height","8")
.attr("width",ws[i] + 1)
.attr("class","x-mark")
.attr("id", "xmark-" + ids[i])
if (branchFocus.includes("" + ids[i])) {
d3.select("#xmark-" + ids[i]).style("fill","#F0684D");
}
})
}
function setMarkYLabel(labels) {
labels.each(function(l,i){
d3.select(this).attr("dx","-5").attr("class","y-label").attr("id","y-label-" + d3.timeYear(l).getFullYear());
})
}
function addMarkY(ticks) {
ticks.each(function(d,i){
if (d3.timeYear(d) < d) {
// month
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",3).attr("class","y-mark-month");
} else {
var from = d3.timeYear(d).getFullYear();
// year
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",6).attr("class","y-mark-year-outer").attr("id","y-mark-year-outer-" + from);
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",3).attr("class","y-mark-year-inner").attr("id","y-mark-year-inner-" + from);
}
})
}
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function removeDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() - days);
return result;
}
function setYDomain(labels) {
var ts = ["week","month","day","year","epoch"];
//console.log(labels)
if (ts.includes(window.timeScale)) {
labels = labels.sort(function(d1,d2){return d1.from - d2.from;})
}
var inf = (labels[0]).from,
sup = (labels[labels.length - 1]).to;
if (window.timeScale == "week") {
inf = addDays(inf,7)
sup = addDays(sup,7)
} else if (window.timeScale == "month") {
inf = removeDays(inf,31)
sup = addDays(sup,31)
} else if (window.timeScale == "day") {
inf = removeDays(inf,1)
sup = addDays(sup,1)
} else if (window.timeScale == "year") {
inf = removeDays(inf,365)
sup = addDays(sup,365)
} else if (window.timeScale == "epoch") {
inf = inf
sup = sup
} else {
inf = new Date((inf.getFullYear() - 1),0,0);
sup = new Date((sup.getFullYear() + 1),0,0);
}
// inf = new Date((inf - 1),6,0);
// inf = new Date((1950 - 1),6,0);
// sup = new Date((sup + 1),0,0);
return [inf,sup];
}
function groupTermsBy(elements, attr) {
let grouped = {},
curr = "";
for (var i = 0; i < elements.length; i++) {
let from = elements[i].getAttribute(attr)
if (curr != from) {
grouped[from] = [[(elements[i]).getAttribute("gx"),(elements[i]).getAttribute("gy"),(elements[i]).getAttribute("bid")]];
curr = from
} else {
grouped[from].push([(elements[i]).getAttribute("gx"),(elements[i]).getAttribute("gy"),(elements[i]).getAttribute("bid")]);
}
}
return Object.values(grouped);
};
function findValueByPrefix(prefix) {
for(var i = 0; i < window.terms.length; i++) {
var object = (window.terms)[i];
if(object.label.toLowerCase().startsWith(prefix.toLowerCase()))
{
return object;
}
}
return null;
}
function drawWordCloud (groups) {
let labels = {},
count = 0;
d3.selectAll(".word-cloud").remove();
groups.forEach(function(g){
let gid = (g.getAttribute("id")).replace("group","");
let terms = d3.selectAll(".term").filter(".g-" + gid).nodes();
terms.forEach(function(t){
count ++;
if (labels[t.getAttribute("fdt")] == undefined) {
labels[t.getAttribute("fdt")] = {"freq" : 1, "label" : t.getAttribute("label")}
} else {
labels[t.getAttribute("fdt")].freq = labels[t.getAttribute("fdt")].freq + 1
}
})
});
labels = (Object.values(labels)).map(function(l){
return {"freq":(l.freq / count),"label":l.label};
}).sort(function(l1,l2){
return l2.freq - l1.freq;
})
let y = 20
let opacity = d3.scaleLinear().domain([Math.log((labels[labels.length - 1]).freq),Math.log((labels[0]).freq)]).range([0.5,1]);
labels.forEach(function(l){
y = y + 12;
window.svg3.append("text")
.attr("class","word-cloud")
.attr("x", 10)
.attr("y", y)
.style("opacity", opacity(Math.log(l.freq)))
.text(l.label);
})
}
function drawPhylo(branches, periods, groups, links, aLinks, bLinks, frame) {
/* ** draw the sources box ** */
document.querySelector("#checkSource").style.display = "inline-block";
/* ** draw the search box ** */
var inputSearch = document.getElementById("search-box");
inputSearch.style.visibility = "visible";
inputSearch.addEventListener("keyup", autocomplete);
document.getElementById("search-autocomplete").style.visibility = "visible";
document.getElementById("search-label").style.visibility = "visible";
/* ** draw the isoline ** */
d3.select('#phyloIsoLine').style("background","#EBE4DD");
var div0 = getIsolineDOMElement(),
m0 = {t:5,r:5,b:5,l:5},
w0 = div0.width,
h0 = div0.height;
var svg0 = d3
.select('#phyloIsoLine')
.append("svg")
.attr("width", w0)
.attr("height",h0)
.append("g");
var xScale0 = d3.scaleLinear().domain([0,Math.max(...branches.map(b => b.x1))]).range([2 * m0.l, w0 - 2 * m0.l]),
yScale0 = d3.scaleLinear().domain(d3.extent(branches, b => b.y)).nice().range([2 * m0.t, h0 - 2 * m0.t]);
var density =
d3.contourDensity()
.x(function(b) { return xScale0(b.x1); })
.y(function(b) { return yScale0(b.y); })
.size([w0, h0])
.thresholds(Math.round(branches.length / 2))
(branches)
function getIsolineDOMElement() {
return d3.select('#phyloIsoLine').node().getBoundingClientRect();
}
/* shadows and lights */
svg0.append("g")
.selectAll("circle")
.data(branches)
.enter()
.append("circle")
.attr("cx", b => xScale0(b.x1))
.attr("cy", b => yScale0(b.y))
.attr("r","55")
.attr("id",b => "peak-shadow" + b.bId)
.attr("visibility","visible")
.style("fill","#f5eee6");
svg0.selectAll("path")
.data(density)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", "none")
.attr("stroke", "#74B5FF")
.attr("stroke-width", (d, i) => i % 2 ? 0.25 : 1)
.attr("stroke-linejoin", "round");
var label =
d3.select("#phyloIsoLine")
.append("div")
.attr("class","peak-label");
svg0.append("g")
.selectAll("text")
.data(branches)
.enter()
.append("text")
.attr("x", b => xScale0(b.x1))
.attr("y", b => yScale0(b.y) + 4)
.attr("class","peak")
.attr("id",b => "peak-" + b.bId)
.style("fill","#0d1824")
.attr("visibility","visible")
.text("▲")
.on("mouseover", function(e, b) {
peakOver(b, b.bId);
})
.on("mouseout", function(e, b) {
peakOut(b, b.bId);
})
.on("click", function(e, b) {
peakClick(b, b.bId);
});
/* *** draw the phylo *** */
var div1 = d3.select('#phyloScape')
.node().getBoundingClientRect(),
div2 = d3.select('#phyloTimeline')
.node().getBoundingClientRect(),
m = {t:10, b:10, l:10, r:10},
w = div1.width,
h = div1.height
ydiv = div1.y;
const svg = d3
.select('#phyloScape')
.append("svg")
.attr("viewBox",[0,0,w,h]);
var xo = div2.width + m.l,
yo = 2.5 * m.t,
wo = w - xo - m.r,
ho = h - yo - m.b;
/* *** draw the graph *** */
var div3 = d3.select('#phyloGraph')
.node().getBoundingClientRect();
window.svg3 = d3
.select('#phyloGraph')
.append("svg")
.attr("width", div3.width)
.attr("height",div3.height)
.append("g");
/* labels */
var firstDate = Math.min(...groups.map(g => (g.from).getFullYear()))
var yLabels = (periods.map(p => ({y:p.y,from:p.from,to:p.to,label:(p.from).getFullYear()}))).filter(p => p.label >= firstDate);
var xLabels = toXLabels(branches,groups,frame[2]);
/* weight */
if (window.weighted == true) {
var wInf = Math.min(...groups.map(g => g.weight))
var wSup = Math.max(...groups.map(g => g.weight))
var wScale = d3.scaleLog().domain([1,wSup]).range([3,10])
}
/* scales */
var xScale = d3.scaleLinear().domain([0,frame[2]]).range([xo + m.t,wo + xo]),
yScale = d3.scaleTime().domain(setYDomain(yLabels)).range([m.t + yo, ho + yo]);
/* panel and& mask */
var mask = svg
.append("defs")
.append("svg:clipPath")
.attr("id","mask")
.append("svg:rect")
.attr("width", wo)
.attr("height",ho)
.attr("x",xo)
.attr("y",yo);
const panel = svg.append("g").attr("clip-path", "url(#mask)").attr("id","panel")
/* highlight */
xLabels.forEach(b =>
panel.append("rect")
.attr("class","branch-hover")
.attr("x", xScale(b.inf))
.attr("y", -10000)
.attr("width", xScale(b.sup) - xScale(b.inf))
.attr("height", 20000)
.attr("id","hover-" + b.bId)
.style("visibility","hidden"))
yLabels.forEach(l =>
panel.append("line")
.attr("class","y-highlight")
.attr("id","y-highlight-" + l.label)
.attr("x1", -10000)
.attr("y1", yScale(l.from))
.attr("x2", 10000)
.attr("y2", yScale(l.from))
.style("visibility","hidden"))
/* links */
function findGroup (id, xsc, ysc) {
var group = groups.find(g => g.gId == id),
x = xsc(group.x);
y = ysc(group.to);
return [x,y]
}
var linkGen = d3.linkVertical();
var groupLinks = links.map(l => ({source: findGroup(l.from, xScale, yScale), target: findGroup(l.to, xScale, yScale),from: l.from, to: l.to, label: l.label}));
var groupAncestors = aLinks.map(l => ({source: findGroup(l.from, xScale, yScale), target: findGroup(l.to, xScale, yScale),from: l.from, to: l.to, label: l.label}));
panel
.selectAll("path")
.data(groupLinks.concat(groupAncestors))
.join("path")
.attr("d", linkGen)
.attr("fill", "none")
.attr("stroke","#0d1824")
.attr("class", "group-path")
.attr("source",d => d.from)
.attr("target",d => d.to)
.attr("label", d => d.label)
// .on("click", function(){
// // console.log(this)
// })
var colors = ["#F0684D","#aa8c58","#74b5ff","#0d1824"];
/* groups */
groups.forEach(g => setGroup(g));
/* axis */
var xAxis = svg.append("g").attr("class","x-axis").attr("transform", "translate(0," + yo + ")"),
yAxis = svg.append("g").attr("class","y-axis").attr("transform", "translate(" + xo + ",0)");
setAxisX(xScale,xLabels);
setAxisY(yScale,yLabels);
/* zoom */
var zoom = d3.zoom()
.scaleExtent([1,50])
.extent([[xo,yo],[wo,ho]])
.on("zoom", function(e) {
debouncedOnZoom(e);
});
svg.call(zoom).on("dblclick.zoom",null).on("dblclick",doubleClick);
d3.select("#reset").on("click",reset);
function reset() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
function debounce(fn, wait, immediate) {
var timeout;
return function() {
var context = this
, args = arguments
, later = function() {
timeout = null;
if (immediate !== true) {
fn.apply(context, args);
}
}
, now = immediate === true && timeout === null;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (now === true) {
fn.apply(context, args);
}
};
};
var debouncedOnZoom = debounce(
onZoom
, 50
);
function onZoom(event) {
var zoomX = event.transform.rescaleX(xScale),
zoomY = event.transform.rescaleY(yScale),
zoomXLabels = xLabels
.filter(b => (zoomX(b.x) >= xo) && (zoomX(b.x) <= (wo + xo))),
zoomYLabels = yLabels
.filter(p => (zoomY(p.y) >= yo) && (zoomY(p.y) <= (ho + yo)));
setAxisX(zoomX,zoomXLabels);
setAxisY(zoomY,zoomYLabels);
panel.selectAll("circle").attr("transform", event.transform);
panel.selectAll("text").attr("transform", event.transform);
panel.selectAll("path").attr("transform", event.transform);
panel.selectAll(".branch-hover").attr("transform", event.transform);
panel.selectAll(".y-highlight").attr("transform", event.transform);
panel.selectAll(".ngrams").attr("transform", event.transform);
panel.selectAll(".term-path").attr("transform", event.transform);
panel.selectAll(".emergence").attr("transform", event.transform);
panel.selectAll(".header").attr("transform", event.transform);
showPeak()
}
/* label */
// https://observablehq.com/@d3/parallel-coordinates
d3.select("#label").on("click", function(e, l) {
showLabel(l);
});
function autocomplete(e) {
var txt = e.target.value;
if (txt.length < 1) {
document.getElementById("search-autocomplete").value = '';
return;
}
var placeholder = findValueByPrefix(txt);
if (placeholder !== null) {
document.getElementById("search-autocomplete").value = placeholder.label;
} else {
document.getElementById("search-autocomplete").value = '';
}
// press enter
if (e.keyCode === 13) {
e.preventDefault();
if (placeholder !== null) {
showLabel("search")
termClick(placeholder.label,placeholder.fdt,0,"search")
}
}
}
function showLabel(type) {
if ((document.getElementsByClassName("header"))[0].style.visibility != "hidden") {
showHeading()
}
doubleClick()
let ngrams = document.getElementsByClassName("ngrams")
let groups = document.getElementsByClassName("group-inner")
if (ngrams[0].style.visibility == "hidden") {
document.getElementById("label").classList.add("labeled")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#fff";
}
for (var i = 0; i < ngrams.length; i++){
ngrams[i].style.visibility = "visible";
}
} else {
if (type != "search") {
document.getElementById("label").classList.remove("labeled")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#61a3a9";
}
for (var i = 0; i < ngrams.length; i++){
ngrams[i].style.visibility = "hidden";
}
}
}
}
/* role & dynamic */
d3.select("#heading").on("click",showHeading);
var emergences = {};
var branchByGroup = {};
groups.forEach(function(g){
// is a term in many branches ?
for (var i = 0; i < (g.foundation).length; i++) {
var fdt = (g.foundation)[i];
if (fdt in branchByGroup) {
(branchByGroup[fdt]).push(g.bId);
} else {
branchByGroup[fdt] = [g.bId];
}
}
// is emerging ?
if ((g.role).includes(0)) {
for (var i = 0; i < (g.role).length; i++) {
if ((g.role)[i] == 0) {
var gf = (g.foundation)[i];
if (gf in emergences) {
(emergences[gf].x).push(xScale(g.x));
(emergences[gf].y).push(yScale(g.to));
} else {
emergences[gf] = {"label":g.label[i],"x":[xScale(g.x)],"y":[yScale(g.to)],"bid":g.bId}
}
}
}
}
});
var keys = Object.keys(emergences);
var freqs = (keys.map(k => window.freq[k])).filter(f => f != null);
const arraySum = (acc, curr) => acc + curr;
function rdm() {
if (Math.random() > 0.5) {
return 1;
} else {
return -1;
}
}
// var fontScale = d3.scaleLinear().domain([0,Math.max(...freqs)]).range([2,10]);
var fontScale = d3.scaleLinear().domain([0,Math.sqrt(Math.max(...freqs))]).range([2,20]);
var opacityScale = d3.scaleLinear().domain([0,1/Math.sqrt(Math.max(...freqs))]).range([0.1,1]);
keys.forEach(function(k){
let x = ((emergences[k]).x).reduce(arraySum) / ((emergences[k]).x).length;
let y = ((emergences[k]).y).reduce(arraySum) / ((emergences[k]).y).length;
let bid = Array.from(new Set(branchByGroup[k]));
var freq = 0;
// console.log(k)
if (k in window.freq) {
freq = window.freq[k];
}
panel.append("text")
.attr("x",x + (rdm() * Math.random() * 10))
.attr("y",y + (rdm() * Math.random() * 10))
.attr("fdt",k)
.attr("id","head" + k)
.attr("mem-size", fontScale(Math.sqrt(freq)))
.attr("mem-opac", opacityScale(Math.sqrt(freq)))
.attr("bid",(emergences[k]).bid)
.style("font-size", fontScale(Math.sqrt(freq)) + "px")
.style("opacity", opacityScale(1/Math.sqrt(freq)))
.attr("class","header")
.style("visibility","hidden")
.style("text-anchor", "middle")
// .style("fill",(bid.length > 1) ? "#012840" : "#CC382F")
.style("fill",(bid.length > 1) ? "#012840" : "#012840")
.text((emergences[k]).label)
.on("click",function(){
showLabel("header")
termClick((emergences[k]).label,k,k,"head");
});
});
function landingView() {
window.ldView = true;
doubleClick()
let headers = document.getElementsByClassName("header")
let groups = document.getElementsByClassName("group-inner")
if (headers[0].style.visibility == "hidden") {
document.getElementById("heading").classList.add("headed")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#f5eee6";
groups[i].classList.add("group-heading")
}
d3.selectAll(".group-path").classed("path-heading",true);
for (var i = 0; i < headers.length; i++){
headers[i].style.visibility = "visible";
}
} else {
document.getElementById("heading").classList.remove("headed")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#61a3a9";
groups[i].classList.remove("group-heading")
}
d3.selectAll(".group-path").classed("path-heading",false);
for (var i = 0; i < headers.length; i++){
headers[i].style.visibility = "hidden";
}
}
}
landingView()
function showHeading() {
if ((document.getElementsByClassName("ngrams"))[0].style.visibility != "hidden") {
showLabel("header")
}
landingView()
}
/* groups */
function textWidth(text) {
const context = document.createElement("canvas").getContext("2d");
return context.measureText(text).width;
}
function toLines(words,fdt,role,targetWidth) {
let line;
let lineWidth0 = Infinity;
const lines = [];
for (let i = 0, n = words.length; i < n; ++i) {
let lineText1 = (line ? line.text + " " : "") + words[i];
let lineFdt1 = (line ? line.fdt + " " : "") + fdt[i];
let lineRole1 = (line ? line.role + " " : "") + role[i];
let lineWidth1 = textWidth(lineText1);
if ((lineWidth0 + lineWidth1) / 2 < targetWidth + 10) {
line.width = lineWidth0 = lineWidth1;
// line.text = lineText1;
line.text.push(words[i])
line.fdt.push(fdt[i])
line.role.push(role[i])
} else {
lineWidth0 = textWidth(words[i]);
line = {width: lineWidth0, text: [words[i]], fdt: [fdt[i]], role: [role[i]]};
lines.push(line);
}
}
return lines;
}
function toTextRadius(lines,lineHeight) {
let radius = 0;
for (let i = 0, n = lines.length; i < n; ++i) {
const dy = (Math.abs(i - n / 2 + 0.5) + 2) * lineHeight;
const dx = lines[i].width / 2;
const sdy = Math.pow(dy, 2);
const sdx = Math.pow(dx, 2);
radius = Math.max(radius, Math.sqrt(sdx + sdy));
}
return radius;
}
function findFreq(fdt) {
let freq = 0;
if (window.freq[fdt] != null) {
freq = window.freq[fdt]
}
return freq;
}
function setGroupClass(g) {
var str = "group-inner" + " " + "branch-" + g.bId;
for (var i = 0; i < g.source.length; i++) {
str += " source-" + g.source[i];
}
return str;
}
function setGroup(g) {
// console.log(window.weighted)
if(window.weighted == true) {
var radius = wScale(g.weight)
} else {
var radius = 5;
}
// var radius = 5;
// var col = Math.round(Math.random() * 3) - 1
panel
.append("circle")
.attr("class","group-outer")
.attr("cx", xScale(g.x))
.attr("cy", yScale(g.to))
.attr("r" , radius + 0.5);
panel
.append("circle")
.attr("class", setGroupClass(g))
.attr("cx", xScale(g.x))
.attr("cy", yScale(g.to))
.attr("bId", g.bId)
.attr("id" , "group" + g.gId)
.attr("gid" , g.gId)
.attr("r" ,radius)
// .attr("stroke",colors[col])
.attr("stroke","#0d1824")
.style("fill", "#61a3a9")
.attr("from",(g.to).getFullYear())
// .on("mouseover",groupOver)
// .on("mouseout" ,groupOut)
/* group label */
var lineHeight = 12,
targetWidth = Math.sqrt(textWidth(g.label.join('').trim()) * radius),
lines = toLines(g.label,g.foundation,g.role,targetWidth),
textRadius = toTextRadius(lines,lineHeight)
textRatio = (radius - 0.5) / textRadius;
for (let i = 0; i < lines.length; i++) {
let words = lines[i].text,
fdt = lines[i].fdt,
roles = lines[i].role,
terms = mergeLists(words,fdt,roles)
toSpan = (acc, w) => acc + "<tspan fdt=" + w[1]
+ " class='term fdt-" + w[1] + " " + "g-" + g.gId + findRole(w[2]) + "'"
+ " gy=" + yScale(g.to)
+ " gx=" + xScale(g.x)
+ " freq=" + findFreq(w[1])
+ " label='" + w[0] + "'"
+ " gid=" + g.gId
+ " bid=" + g.bId
+ " from=" + (g.to).getFullYear()
+ ">" + w[0] + "</tspan>";
panel
.append("text")
.attr("class","ngrams")
.attr("text-anchor", "middle")
.style("font-size", 12 * textRatio + "px")
.style("visibility", "hidden")
.append("tspan")
.attr("x", xScale(g.x))
.attr("y", yScale(g.to) + (i - lines.length / 2.8) * (lineHeight * textRatio))
.html(terms.reduce(toSpan,""));
d3.selectAll(".term")
.on("click",function(){
termClick(this.textContent,this.getAttribute("fdt"),this.getAttribute("gid"),"group");
})
// .on("mouseover",function(){
// d3.selectAll(".term").classed("term-unfocus",true);
// d3.selectAll(".term").filter(".g-" + this.getAttribute("gid")).classed("term-focus",true);
// })
// .on("mouseout",function(){
// d3.selectAll(".term").classed("term-unfocus",false);
// d3.selectAll(".term").classed("term-focus",false);
// });
}
}
d3.selectAll(".header").raise();
function findRole(r) {
if (r == 0) {
return " emerging";
} else if (r == 2) {
return " decreasing";
} else {
return "";
}
}
function mergeLists(l1,l2,l3) {
let merged = [];
for (let i = 0; i < l1.length; i++) {
merged.push([l1[i],l2[i],l3[i]])
}
return merged;
}
function setAxisY(scale,labels) {
yAxis.call(d3.axisLeft(scale)
.tickFormat(function(d){
if (d3.timeYear(d) < d) {
// '%B'
return d3.timeFormat('%d %B')(d);
} else {
return d3.timeFormat('%Y')(d);
}
})
.tickSizeOuter(0));
yAxis.selectAll(".tick line").remove();
yAxis.selectAll(".tick circle").remove();
yAxis.selectAll(".tick")
.call(addMarkY)
yAxis.selectAll(".tick text")
.call(setMarkYLabel)
}
function setAxisX(scale,labels) {
xAxis.call(d3.axisTop(scale)
.tickValues(labels.map(l => l.x))
.tickFormat((l, i) => labels[i].label)
.tickSizeOuter(0));
xAxis.selectAll(".tick text")
.call(xOverFlow, labels.map(l => [scale(l.sup) - scale(l.inf),l.bId]))
.on("mouseover", tickOver)
.on("click", tickClick)
.on("mouseout" , tickOut);
xAxis.selectAll(".tick line").remove();
xAxis.selectAll(".tick rect").remove();
xAxis.selectAll(".tick")
.call(addMarkX, labels.map(l => scale(l.sup) - scale(l.inf)),labels.map(l => l.bId));
}
function showPeak() {
d3.selectAll(".peak").style("fill",function(peak,i){
var isVisible = d3
.selectAll(".branch-" + i)
.nodes()
.map(function(g){
var x = g.getBoundingClientRect().x,
y = g.getBoundingClientRect().y;
if ((x >= xo) && (x <= (wo + xo)) && (y >= (div1.y + yo - m.t)) && (y <= (div1.y + ho + yo))) {
return true;
} else {
return false;
}})
.reduce((mem,cur) => {return mem || cur;})
if (isVisible) {
d3.select("#peak-shadow" + i).attr("visibility","visible");
return "#0d1824";
} else {
d3.select("#peak-shadow" + i).attr("visibility","hidden");
return "#A9A9A9";
}
})
}
function countTerms(groups) {
var terms = [];
for (var i = 0; i < groups.length; i++) {
let gid = ((groups[i].getAttribute("id")).split("group"))[1]
d3.selectAll(".g-" + gid).nodes().forEach(e => terms.push(e.getAttribute("fdt")))
}
return (Array.from(new Set(terms))).length;
}
function countBranches(groups) {
var branches = [];
for (var i = 0; i < groups.length; i++) {
branches.push(groups[i].getAttribute("bId"));
}
return (Array.from(new Set(branches))).length;
}
function highlightGroups (groups) {
window.ldView = false;
// console.log(groups)
let paths = document.getElementsByClassName("group-path"),
gids = [];
for (var i = 0; i < groups.length; i++) {
// highlight the groups
groups[i]
.classList.add("group-focus");
groups[i]
.classList.remove("group-unfocus");
// .classed("group-unfocus", false)
// .classed("group-focus", true);
gids.push(groups[i].getAttribute("gid"))
// highlight the branches peak
let bid = groups[i].getAttribute("bId")
d3.select("#peak-" + bid)
.classed("peak-focus", true);
d3.select("#xmark-" + bid)
.style("fill", "#F0684D");
}
// facets
document.querySelector("#phyloGroups").innerHTML = groups.length;
document.querySelector("#phyloTerms").innerHTML = countTerms(groups);
document.querySelector("#phyloBranches").innerHTML = countBranches(groups);
document.querySelector("#phyloGroups").classList.add("phylo-focus");
document.querySelector("#phyloTerms").classList.add("phylo-focus");
document.querySelector("#phyloBranches").classList.add("phylo-focus");
// highlight the links
for (var i = 0; i < paths.length; i++) {
if (gids.includes((paths[i]).getAttribute("source")) && (paths[i]).getAttribute("target")) {
paths[i].classList.add("path-focus");
paths[i].classList.remove("path-unfocus");
}
}
}
function termClick (txt,idx,nodeId,typeNode) {
// remove old focus
initPath()
// catch the last transformations
if (typeNode == "group") {
var transform = d3.select("#group" + nodeId).node().getAttribute("transform");
} else if (typeNode == "head") {
var transform = d3.select("#head" + nodeId).node().getAttribute("transform");
} else {
var transform = (d3.selectAll(".header").nodes())[0].getAttribute("transform");
}
// focus
document.querySelector("#phyloPhylo").innerHTML = txt;
document.querySelector("#phyloPhylo").classList.add("phylo-focus");
document.querySelector("#phyloSearch").setAttribute("href",'https://en.wikipedia.org/w/index.php?search="' + txt + '"')
// highlight the groups
var terms = document.getElementsByClassName("fdt-" + idx),
periods = groupTermsBy(terms,"from");
var groups = [];
for (var i = 0; i < terms.length; i++) {
groups.push(d3.select("#group" + (terms[i]).getAttribute("gid")));
branchFocus.push((terms[i]).getAttribute("bid"));
}
highlightGroups(groups.map(g => g.node()));
drawWordCloud(groups.map(g => g.node()));
// highlight the cross branches links
var bids = [];
for (var i = 0; i < periods.length; i++) {
if (i != periods.length - 1) {
for (var j = 0; j < periods[i].length; j++) {
bids.push(periods[i][j][2])
var x1 = periods[i][j][0],
y1 = periods[i][j][1];
for (var k = 0; k < periods[i + 1].length; k++) {
var x2 = periods[i + 1][k][0],
y2 = periods[i + 1][k][1];
if ((periods[i][j][2] != periods[i + 1][k][2]) && (!bids.includes(periods[i + 1][k][2]))) {
// draw the links between branches
panel
.append("path")
.attr("class","term-path")
.attr("d", function(d) {
return "M" + x1 + "," + y1
+ "C" + x2 + "," + y1
+ " " + x2 + "," + y2
+ " " + x2 + "," + y2;
})
.attr("transform",transform)
.style("stroke-opacity", 0.4)
.lower();
}
bids.push(periods[i + 1][k][2])
}
}
}
}
d3.selectAll(".path-unfocus").lower();
}
function peakOver (b,i) {
var el = getIsolineDOMElement();
d3.select("#peak-" + i).classed("peak-focus",false);
d3.select("#peak-" + i).classed("peak-over",true);
label.text(b.label.replace(/"/g,''))
.style("visibility", "visible")
.style("top", (yScale0(b.y) + el.top - 18) + "px")
.style("left",(xScale0(b.x1) + el.left + 12) + "px");
branchOver(b.bId);
}
function peakOut (b,i) {
d3.select("#peak-" + i).classed("peak-over",false);
if (branchFocus.includes("" + b.bId)) {
d3.select("#peak-" + i).classed("peak-focus",true);
}
branchOut();
}
function peakClick (b,i) {
initPath()
let groups = d3.selectAll(".group-inner").filter(".branch-" + b.bId).nodes()
branchFocus.push(b.bId);
/* word cloud */
drawWordCloud(groups);
highlightGroups(groups);
/* rescale */
let tx = (groups[0]).getAttribute("cx")
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity.translate(((wo + xo) / 2) - tx, 0).scale(1));
d3.selectAll(".path-unfocus").lower();
}
function branchOver(bId) {
// headers
if (d3.select("#heading").classed("headed")) {
d3.selectAll(".header").nodes().forEach(function(header){
if (header.getAttribute("bid") == bId) {
header.style["font-size"] = "10px";
header.style["opacity"] = 1;
} else {
header.style["opacity"] = 0.3;
}
})
}
// branches
d3.select("#xmark-" + bId).style("fill","#f3be54");
d3.select("#hover-" + bId).style("visibility","visible");
}
function headerOut() {
d3.selectAll(".header").nodes().forEach(function(header){
header.style["font-size"] = header.getAttribute("mem-size") + "px";
header.style["opacity"] = header.getAttribute("mem-opac");
})
}
function branchOut(bId) {
d3.selectAll(".peak-label").style("visibility","hidden");
d3.selectAll(".branch-hover").style("visibility","hidden");
d3.selectAll(".x-mark").style("fill","#4A5C70");
for (var i = 0; i < branchFocus.length; i++) {
d3.select("#xmark-" + branchFocus[i]).style("fill","#F24C3D");
}
headerOut();
}
function tickClick() {
initPath()
let bid = this.getAttribute("bId"),
groups = d3.selectAll(".group-inner").filter(".branch-" + bid).nodes();
// draw the word cloud
branchFocus.push(bid);
drawWordCloud(groups);
// highlight the groups
highlightGroups(groups);
d3.selectAll(".path-unfocus").lower();
}
function tickOver() {
var ego = this.getAttribute("bId"),
branch = branches.find(b => b.bId == ego);
if (d3.select("#peak-" + ego).node().style.visibility != "hidden") {
branchOver(ego);
peakOver(branch,ego);
}
}
function tickOut() {
var ego = this.getAttribute("bId"),
branch = branches.find(b => b.bId == ego);
branchOut();
peakOut(branch,ego)
}
// function groupOver() {
// var from = this.getAttribute("from");
// d3.select("#y-highlight-" + from).style("visibility","visible");
// // d3.select("#y-mark-year-inner-" + from).node().setAttribute("class","y-mark-year-inner-highlight");
// // d3.select("#y-mark-year-outer-" + from).node().setAttribute("class","y-mark-year-outer-highlight");
// // d3.select("#y-label-" + from).node().setAttribute("class","y-label-bold");
// }
// function groupOut() {
// var from = this.getAttribute("from");
// d3.select("#y-highlight-" + from).style("visibility","hidden");
// // d3.select("#y-mark-year-inner-" + from).node().setAttribute("class","y-mark-year-inner");
// // d3.select("#y-mark-year-outer-" + from).node().setAttribute("class","y-mark-year-outer");
// // d3.select("#y-label-" + from).node().setAttribute("class","y-label");
// }
function initPath () {
window.highlighted = true;
window.ldView = false;
let groups = d3.selectAll(".group-inner");
(groups.nodes()).map(function(g){
if (!g.classList.contains("source-focus")) {
g.classList.add("group-unfocus");
g.classList.remove("group-focus");
}
})
d3.selectAll(".group-path")
.classed("path-unfocus",true)
.classed("path-focus",false);
d3.selectAll(".term-path").remove();
d3.selectAll(".peak").classed("peak-focus",false);
d3.selectAll(".peak").classed("peak-focus-source",false);
d3.selectAll(".x-mark").style("fill","#4A5C70");
branchFocus = [];
}
function doubleClick() {
window.highlighted = false;
headerOut();
d3.selectAll(".group-inner")
.classed("group-unfocus",false)
.classed("group-focus",false);
d3.selectAll(".group-path")
.classed("path-unfocus",false)
.classed("path-focus",false);
d3.selectAll(".term-path").remove();
document.querySelector("#phyloPhylo").innerHTML = "phylomemy";
document.querySelector("#phyloPhylo").classList.remove("phylo-focus");
document.querySelector("#phyloGroups").innerHTML = window.nbGroups;
document.querySelector("#phyloTerms").innerHTML = window.nbTerms;
document.querySelector("#phyloBranches").innerHTML = window.nbBranches;
document.querySelector("#phyloGroups").classList.remove("phylo-focus");
document.querySelector("#phyloTerms").classList.remove("phylo-focus");
document.querySelector("#phyloBranches").classList.remove("phylo-focus");
d3.selectAll(".peak").classed("peak-focus",false);
d3.selectAll(".peak").classed("peak-focus-source",false);
d3.selectAll(".x-mark").style("fill","#4A5C70");
branchFocus = [];
}
/* export */
d3.select("#export").on("click",exportViz);
function exportViz() {
const xmlns = "http://www.w3.org/2000/xmlns/";
const xlinkns = "http://www.w3.org/1999/xlink";
const svgns = "http://www.w3.org/2000/svg";
var time = new Date();
serialize(svg.node(),"phylomemy-" + Date.parse(time.toString()) + ".svg")
function serialize(graph,name) {
graph = graph.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(graph, NodeFilter.SHOW_ELEMENT, null, false);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
graph.setAttributeNS(xmlns, "xmlns", svgns);
graph.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);
var cssStyleText = getCSSStyles( graph );
appendCSS( cssStyleText, graph );
const serializer = new window.XMLSerializer;
const string = serializer.serializeToString(graph);
var svgBlob = new Blob([string], {type: "image/svg+xml"});
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = name;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
function getCSSStyles( parentElement ) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push( '#'+parentElement.id );
for (var c = 0; c < parentElement.classList.length; c++)
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
selectorTextArr.push( '.'+parentElement.classList[c] );
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if ( !contains('#'+id, selectorTextArr) )
selectorTextArr.push( '#'+id );
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if ( !contains('.'+classes[c], selectorTextArr) )
selectorTextArr.push( '.'+classes[c] );
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch( e ) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf( str ) === -1 ? false : true;
}
}
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore( styleElement, refNode );
}
}
}
module Gargantext.Components.PhyloExplorer.Draw
( drawPhylo
, highlightSource
, unhide
, setGlobalDependencies, setGlobalD3Reference
) where
import Gargantext.Prelude
import DOM.Simple (Document, Window, querySelectorAll)
import Data.Either (Either(..))
import Data.Foldable (for_)
import Data.FoldableWithIndex (forWithIndex_)
import Data.Maybe (Maybe(..), maybe)
import Effect (Effect)
import Effect.Uncurried (EffectFn1, EffectFn7, runEffectFn1, runEffectFn7)
import FFI.Simple (applyTo, getProperty, (..), (.=), (.?))
import Gargantext.Components.PhyloExplorer.Types (AncestorLink, Branch, BranchLink, GlobalTerm(..), Group(..), Link, Period, PhyloDataSet(..))
import Graphics.D3.Base (D3, D3Eff)
import Graphics.D3.Selection as D3S
import Graphics.D3.Util (ffi)
foreign import _drawPhylo :: EffectFn7
(Array Branch)
(Array Period)
(Array Group)
(Array Link)
(Array AncestorLink)
(Array BranchLink)
(Array Number)
(Unit)
drawPhylo ::
Array Branch
-> Array Period
-> Array Group
-> Array Link
-> Array AncestorLink
-> Array BranchLink
-> Array Number
-> Effect Unit
drawPhylo = runEffectFn7 _drawPhylo
foreign import _drawWordCloud :: forall a. EffectFn1 (Array a) Unit
drawWordCloud :: forall a. Array a -> Effect Unit
drawWordCloud = runEffectFn1 _drawWordCloud
-----------------------------------------------------------
orDie :: forall err a. Maybe a -> err -> Either err a
orDie (Just a) _ = Right a
orDie Nothing err = Left err
-- @XXX: FFI.Simple `(...)` throws error (JavaScript issue)
-- need to decompose computation
--
-- (?) chained prototype property issue?
applyTo_ :: forall src arg res. src -> String -> Array arg -> res
applyTo_ src name args =
let fn = getProperty name src
in applyTo fn src args
infixl 4 applyTo_ as ~~
-- @WIP: DOM.Simple lack of "ClassList" module
addClass :: forall el. el -> Array String -> Effect Unit
addClass el args = pure $ (el .. "classList") ~~ "add" $ args
removeClass :: forall el. el -> Array String -> Effect Unit
removeClass el args = pure $ (el .. "classList") ~~ "remove" $ args
-- @WIP: "Graphics.D3.Selection" lack of "filter" function
-- @WIP: "Graphics.D3.Selection" lack of "nodes" function
selectionFilter :: forall d. String -> D3S.Selection d -> D3Eff (D3S.Selection D3S.Void)
selectionFilter = ffi ["query", "selection", ""] "selection.filter(query)"
selectionNodes :: forall d el. D3S.Selection d -> D3Eff (Array el)
selectionNodes = ffi ["selection", ""] "selection.nodes()"
-----------------------------------------------------------
setGlobalDependencies :: Window -> PhyloDataSet -> Effect Unit
setGlobalDependencies w (PhyloDataSet o)
= do
_ <- pure $ (w .= "freq") {}
_ <- pure $ (w .= "nbBranches") o.nbBranches
_ <- pure $ (w .= "nbDocs") o.nbDocs
_ <- pure $ (w .= "nbFoundations") o.nbFoundations
_ <- pure $ (w .= "nbGroups") o.nbGroups
_ <- pure $ (w .= "nbPeriods") o.nbPeriods
_ <- pure $ (w .= "nbTerms") o.nbTerms
_ <- pure $ (w .= "sources") o.sources
_ <- pure $ (w .= "terms") []
_ <- pure $ (w .= "timeScale") o.timeScale
_ <- pure $ (w .= "weighted") o.weighted
(freq :: Array Int) <- pure $ w .. "freq"
(terms :: Array GlobalTerm) <- pure $ w .. "terms"
for_ o.groups \(Group g) -> do
let
f = g.foundation
l = g.label
forWithIndex_ f \idx val ->
let
idx' = show idx
val' = show val
in
-- For each entries in group.foundation array,
-- increment consequently the global window.keys array
case (freq .? val') of
Nothing -> pure $ (freq .= val') 0
Just v -> pure $ (freq .= val') (v +1)
*>
-- For each entries in group.foundation array,
-- if the global window.terms does not have it in property,
-- append an item to the global window.terms
case (terms .? val') of
Just _ -> pure unit
Nothing -> void <<< pure $ (terms .= val') $ GlobalTerm
{ label: l .. idx'
, fdt : val'
}
-- Use FFI native `Array.flat` method (not mutating its caller in this
-- context)
void do
new <- pure $ (terms ~~ "flat") []
pure $ (w .= "terms") new
-- @XXX: prevent PureScript from not injecting D3
setGlobalD3Reference :: Window -> D3 -> Effect Unit
setGlobalD3Reference window d3 = void $ pure $ (window .= "d3") d3
-----------------------------------------------------------
unhide :: Document -> String -> Effect Unit
unhide d s = do
setText s `toElements` "#phyloName"
turnVisible `toElements` "#phyloName"
turnVisible `toElements` ".reset"
turnVisible `toElements` ".label"
turnVisible `toElements` ".heading"
turnVisible `toElements` ".export"
where
toElements fn query = querySelectorAll d query >>= flip for_ fn
turnVisible el = do
style <- pure $ (el .. "style")
pure $ (style .= "visibility") "visible"
setText name el = pure $ (el .= "innerHTML") name
-----------------------------------------------------------
highlightSource :: Window -> String -> Effect Unit
highlightSource window value =
let
hasHighlight = maybe false identity (window .? "highlighted")
hasLdView = maybe false identity (window .? "ldView")
in do
groups <- D3S.rootSelectAll ".group-inner"
if hasHighlight
then
selectionFilter ".source-focus" groups
>>= selectionNodes
>>= flip for_ (flip addClass [ "group-unfocus" ])
else
pure unit
-- unselected all the groups
_ <- selectionNodes groups
>>= flip for_ (flip removeClass [ "source-focus" ])
if hasLdView
then
selectionNodes groups
>>= flip for_ (fill "#f5eee6")
else
selectionNodes groups
>>= flip for_ (fill "#fff")
_ <- D3S.rootSelectAll ".peak"
>>= D3S.classed "peak-focus-source" false
-- select the relevant ones
if (value == "unselect")
then
pure unit
else do
arr <- selectionFilter (".source-" <> value) groups
>>= selectionNodes
drawWordCloud arr
for_ arr selectNodeGroup
where
fill :: forall el. String -> el -> Effect Unit
fill hex el = do
style <- pure $ (el .. "style")
pure $ (style .= "fill") hex
selectNodeGroup :: forall el. el -> Effect Unit
selectNodeGroup el = do
removeClass el [ "group-unfocus" ]
addClass el [ "source-focus" ]
fill "#a6bddb" el
bid <- pure $ (el ~~ "getAttribute") [ "bId" ]
void $
D3S.rootSelect ("#peak-" <> bid)
>>= D3S.classed "peak-focus-source" true
module Gargantext.Components.PhyloExplorer.JSON where
import Gargantext.Prelude
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep as GR
import Data.Maybe (Maybe)
import Data.Show.Generic (genericShow)
import Gargantext.Utils.SimpleJSON (untaggedSumRep)
import Simple.JSON as JSON
type GraphData =
( bb :: String
, color :: String
, fontsize :: String
, label :: String
, labelloc :: String
, lheight :: String
, lp :: String
, lwidth :: String
, name :: String
, nodesep :: String
, overlap :: String
, phyloBranches :: String
, phyloDocs :: String
, phyloFoundations :: String
, phyloGroups :: String
, phyloPeriods :: String
, phyloSources :: String
, phyloTerms :: String
, phyloTimeScale :: String
, rank :: String
, ranksep :: String
, ratio :: String
, splines :: String
, style :: String
)
--------------------------------------------------
newtype PhyloJSONSet = PhyloJSONSet
{ _subgraph_cnt :: Int
, directed :: Boolean
, edges :: Array RawEdge
, objects :: Array RawObject
, strict :: Boolean
| GraphData
}
derive instance Generic PhyloJSONSet _
derive instance Eq PhyloJSONSet
instance Show PhyloJSONSet where show = genericShow
derive newtype instance JSON.ReadForeign PhyloJSONSet
--------------------------------------------------
type NodeData =
( height :: String
, label :: String
, name :: String
, nodeType :: String
, pos :: String
, shape :: String
, width :: String
)
data RawObject
= GroupToNode
{ _gvid :: Int
, bId :: String
, branchId :: String
, fontname :: String
, foundation :: String
, frequence :: String
, from :: String
, lbl :: String
, penwidth :: String
, role :: String
, seaLvl :: String
, source :: String
, strFrom :: Maybe String
, strTo :: Maybe String
, support :: String
, to :: String
, weight :: String
| NodeData
}
| BranchToNode
{ _gvid :: Int
, age :: String
, bId :: String
, birth :: String
, branchId :: String
, branch_x :: String
, branch_y :: String
, fillcolor :: String
, fontname :: String
, fontsize :: String
, size :: String
, style :: String
| NodeData
}
| PeriodToNode
{ _gvid :: Int
, fontsize :: String
, from :: String
, strFrom :: Maybe String
, strTo :: Maybe String
, to :: String
| NodeData
}
| Layer
{ _gvid :: Int
, nodes :: Array Int
| GraphData
}
derive instance Generic RawObject _
derive instance Eq RawObject
instance Show RawObject where show = genericShow
instance JSON.ReadForeign RawObject where
readImpl f = GR.to <$> untaggedSumRep f
--------------------------------------------------
type EdgeData =
( color :: String
, head :: Int
, pos :: String
, tail :: Int
, width :: String
)
data RawEdge
= GroupToAncestor
{ _gvid :: Int
, arrowhead :: String
, edgeType :: String
, lbl :: String
, penwidth :: String
, style :: String
| EdgeData
}
| GroupToGroup
{ _gvid :: Int
, constraint :: String
, edgeType :: String
, lbl :: String
, penwidth :: String
| EdgeData
}
| BranchToGroup
{ _gvid :: Int
, arrowhead :: String
, edgeType :: String
| EdgeData
}
| BranchToBranch
{ _gvid :: Int
, arrowhead :: String
, style :: String
| EdgeData
}
| PeriodToPeriod
{ _gvid :: Int
| EdgeData
}
derive instance Generic RawEdge _
derive instance Eq RawEdge
instance Show RawEdge where show = genericShow
instance JSON.ReadForeign RawEdge where
readImpl f = GR.to <$> untaggedSumRep f
module Gargantext.Components.PhyloExplorer.Layout
( layout
) where
import Gargantext.Prelude
import DOM.Simple (document, window)
import Data.Array as Array
import Gargantext.Components.PhyloExplorer.Draw (drawPhylo, highlightSource, setGlobalD3Reference, setGlobalDependencies, unhide)
import Gargantext.Components.PhyloExplorer.Types (PhyloDataSet(..))
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2
import Graphics.D3.Base (d3)
import Reactix as R
import Reactix.DOM.HTML as H
here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer"
type Props =
( phyloDataSet :: PhyloDataSet
)
layout :: R2.Component Props
layout = R.createElement layoutCpt
layoutCpt :: R.Component Props
layoutCpt = here.component "layout" cpt where
cpt { phyloDataSet: (PhyloDataSet o)
} _ = do
-- States
R.useEffectOnce' $ do
unhide document o.name
setGlobalD3Reference window d3
setGlobalDependencies window (PhyloDataSet o)
drawPhylo
o.branches
o.periods
o.groups
o.links
o.ancestorLinks
o.branchLinks
o.bb
-- Render
pure $
H.div
{ className: "phylo" }
[
-- <!-- row 1 -->
H.div
{ className: "phylo-title font-bold" }
[ H.text "Mèmiescape" ]
,
H.div
{ className: "phylo-folder" }
[
-- <!-- title bar (static mode) -->
H.label
{ id: "phyloName"
, className: "phylo-name"
}
[]
,
-- <!-- folder bar -->
-- H.label
-- { id: "file-label"
-- , for: "file-path"
-- , className: "input-file"
-- }
-- [ H.text "load a phylomemy →" ]
-- ,
-- H.input
-- { id: "file-path"
-- , type: "file"
-- , maxLength: "10"
-- }
-- ,
-- H.label
-- { id: "file-name"
-- , className: "input-name"
-- }
-- []
-- ,
-- H.button
-- { id: "draw"
-- , className: "button draw"
-- }
-- [ H.text "draw" ]
-- ,
-- <!-- source selector -->
R2.select
{ id: "checkSource"
, className: "select-source"
, defaultValue: ""
, on: { change: \e -> highlightSource window e.target.value }
} $
[
H.option
{ disabled: true
, value: ""
}
[ H.text "select a source ↴" ]
,
H.option
{ value: "unselect" }
[ H.text "unselect source ✕" ]
]
<>
flip Array.mapWithIndex o.sources
( \idx val ->
H.option
{ value: idx }
[ H.text val ]
)
,
-- <!-- search bar -->
H.label
{ id: "search-label"
, className: "search-label"
}
[ H.text "find a term →" ]
,
H.input
{ id: "search-box"
, type: "text"
, className: "search"
}
,
H.input
{ id: "search-autocomplete"
, text: "text"
, className: "autocomplete"
, disabled: true
, value: ""
}
]
,
-- <!-- row 2 & 3 -->
phyloCorpus {} []
,
phyloCorpusInfo
{ nbDocs : o.nbDocs
, nbFoundations : o.nbFoundations
, nbPeriods : o.nbPeriods
}
[]
,
phyloHow {} []
,
phyloPhylo {} []
,
phyloPhyloInfo
{ nbTerms : o.nbTerms
, nbGroups : o.nbGroups
, nbBranches : o.nbBranches
}
[]
,
H.div
{ id: "phyloIsoLine"
, className: "phylo-isoline"
}
[]
,
H.div
{ id: "phyloIsolineInfo"
, className: "phylo-isoline-info"
}
[
H.div
{ className: "btn-group" }
[
H.button
{ id: "reset"
, className: "button reset"
}
[
H.i
{ className: "fa fa-arrows-alt" }
[]
]
,
H.button
{ id: "label"
, className: "button label"
}
[
H.i
{ className: "fa fa-dot-circle-o" }
[]
]
,
H.button
{ id: "heading"
, className: "button heading"
}
[
H.i
{ className: "fa fa-sort-alpha-asc" }
[]
]
,
H.button
{ id: "export"
, className: "button export"
}
[
H.i
{ className: "fas fa-camera" }
[]
]
]
]
,
-- <!-- row 4 -->
H.div
{ id: "phyloScape"
, className: "phylo-scape"
}
[]
,
H.div
{ id: "phyloTimeline"
, className: "phylo-timeline"
}
[]
,
H.div
{ id: "phyloGraph"
, className: "phylo-graph"
}
[]
]
--------------------------------------------------------
phyloCorpus :: R2.Component ()
phyloCorpus = R.createElement phyloCorpusCpt
phyloCorpusCpt :: R.Component ()
phyloCorpusCpt = here.component "phyloCorpus" cpt where
cpt _ _ = do
-- Render
pure $
H.div
{ id: "phyloCorpus"
, className: "phylo-corpus"
}
[ H.text "corpus" ]
---------------------------------------------------------
phyloHow :: R2.Component ()
phyloHow = R.createElement phyloHowCpt
phyloHowCpt :: R.Component ()
phyloHowCpt = here.component "phyloHow" cpt where
cpt _ _ = do
-- Render
pure $
H.div
{ id: "phyloHow"
, className: "phylo-how"
}
[
H.a
{ id: "phyloSearch"
, href: "http://maps.gargantext.org/phylo/knowledge_visualization/memiescape/documentation.html"
, target: "_blank"
}
[
H.div
{ className: "switch" }
[
H.i
{ className: "far fa-question-circle how" }
[]
,
H.i
{ className: "fa fa-question-circle how" }
[
H.span
{ className: "tooltip" }
[ H.text "click to see how the phylomemy was built" ]
]
]
]
]
---------------------------------------------------------
phyloPhylo :: R2.Component ()
phyloPhylo = R.createElement phyloPhyloCpt
phyloPhyloCpt :: R.Component ()
phyloPhyloCpt = here.component "phyloPhylo" cpt where
cpt _ _ = do
-- Render
pure $
H.div
{ id: "phyloPhylo"
, className: "phylo-phylo"
}
[ H.text "phylomemy" ]
---------------------------------------------------------
type PhyloCorpusInfoProps =
( nbDocs :: Int
, nbFoundations :: Int
, nbPeriods :: Int
)
phyloCorpusInfo :: R2.Component PhyloCorpusInfoProps
phyloCorpusInfo = R.createElement phyloCorpusInfoCpt
phyloCorpusInfoCpt :: R.Component PhyloCorpusInfoProps
phyloCorpusInfoCpt = here.component "phyloCorpusInfo" cpt where
cpt props _ = do
-- Render
pure $
H.div
{ id: "phyloCorpusInfo"
, className: "phylo-corpus-info"
}
[
H.span
{}
[
H.b {} [ H.text $ show props.nbDocs ]
, H.text $ nbsp 1 <> "docs"
]
,
H.span
{}
[
H.b {} [ H.text $ show props.nbFoundations ]
, H.text $ nbsp 1 <> "foundations"
]
,
H.span
{}
[
H.b {} [ H.text $ show props.nbPeriods ]
, H.text $ nbsp 1 <> "periods"
]
]
---------------------------------------------------------
type PhyloPhyloInfoProps =
( nbTerms :: Int
, nbGroups :: Int
, nbBranches :: Int
)
phyloPhyloInfo :: R2.Component PhyloPhyloInfoProps
phyloPhyloInfo = R.createElement phyloPhyloInfoCpt
phyloPhyloInfoCpt :: R.Component PhyloPhyloInfoProps
phyloPhyloInfoCpt = here.component "phyloPhyloInfo" cpt where
cpt props _ = do
-- Render
pure $
H.div
{ id: "phyloPhyloInfo"
, className: "phylo-phylo-info"
}
[
H.span
{}
[
H.b
{ id: "phyloTerms" }
[ H.text $ show props.nbTerms ]
, H.text $ nbsp 1 <> "terms"
]
,
H.span
{}
[
H.b
{ id: "phyloGroups" }
[ H.text $ show props.nbGroups ]
, H.text $ nbsp 1 <> "groups"
]
,
H.span
{}
[
H.b
{ id: "phyloBranches" }
[ H.text $ show props.nbBranches ]
, H.text $ nbsp 1 <> "branches"
]
]
'use strict';
/**
* @name yearToDate
* @param {string} year
* @returns {Date}
*/
function yearToDate(year) {
var d = new Date();
d.setYear(parseInt(year));
d.setMonth(0);
d.setDate(1);
return d;
}
/**
* @name stringToDate
* @param {string} str
* @returns {Date}
*/
function stringToDate(str) {
var arr = (str.replace('"','')).split('-');
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setMonth(d.getMonth() - 1);
d.setDate(parseInt(arr[2]));
return d;
}
/**
* @name utcStringToDate
* @param {string} str
* @returns {Date}
*/
function utcStringToDate(str) {
var arr = ((str.replace('"','')).replace(' UTC','')).split(/[\s-:]+/);
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setDate(parseInt(arr[2]));
d.setHours(parseInt(arr[3]), parseInt(arr[4]), parseInt(arr[5]))
return d;
}
exports.yearToDate = yearToDate;
exports.stringToDate = stringToDate;
exports.utcStringToDate = utcStringToDate;
module Gargantext.Components.PhyloExplorer.Types
( PhyloDataSet(..)
, Branch(..), Period(..), Group(..)
, Link(..), AncestorLink(..), BranchLink(..)
, GlobalTerm(..)
, parsePhyloJSONSet
) where
import Gargantext.Prelude
import Data.Array as Array
import Data.Date as Date
import Data.Generic.Rep (class Generic)
import Data.Int as Int
import Data.Maybe (Maybe(..), maybe)
import Data.Number as Number
import Data.Show.Generic (genericShow)
import Data.String as String
import Data.Tuple as Tuple
import Data.Tuple.Nested ((/\))
import Gargantext.Components.PhyloExplorer.JSON (PhyloJSONSet(..), RawEdge(..), RawObject(..))
-- @WIP PureScript Date or stick to JavaScript foreign?
foreign import yearToDate :: String -> Date.Date
foreign import stringToDate :: String -> Date.Date
foreign import utcStringToDate :: String -> Date.Date
data PhyloDataSet = PhyloDataSet
{ ancestorLinks :: Array AncestorLink
, bb :: Array Number
, branchLinks :: Array BranchLink
, branches :: Array Branch
, groups :: Array Group
, links :: Array Link
, name :: String
, nbBranches :: Int
, nbDocs :: Int
, nbFoundations :: Int
, nbGroups :: Int
, nbPeriods :: Int
, nbTerms :: Int
, periods :: Array Period
, sources :: Array String
, timeScale :: String
, weighted :: Boolean
}
derive instance Generic PhyloDataSet _
derive instance Eq PhyloDataSet
instance Show PhyloDataSet where show = genericShow
parsePhyloJSONSet :: PhyloJSONSet -> PhyloDataSet
parsePhyloJSONSet (PhyloJSONSet o) = PhyloDataSet
{ ancestorLinks
, bb : parseBB o.bb
, branchLinks
, branches
, groups
, links
, name : o.name
, nbBranches : parseInt o.phyloBranches
, nbDocs : parseInt o.phyloDocs
, nbFoundations : parseInt o.phyloFoundations
, nbGroups : parseInt o.phyloGroups
, nbPeriods : parseInt o.phyloPeriods
, nbTerms : parseInt o.phyloTerms
, periods
, sources : parseSources o.phyloSources
, timeScale : o.phyloTimeScale
, weighted : getGlobalWeightedValue groups
}
where
epochTS = o.phyloTimeScale == "epoch"
ancestorLinks = parseAncestorLinks o.edges
branchLinks = parseBranchLinks o.edges
branches = parseBranches o.objects
groups = parseGroups epochTS o.objects
links = parseLinks o.edges
periods = parsePeriods epochTS o.objects
-----------------------------------------------------------
newtype Branch = Branch
{ bId :: Int
, gvid :: Int
, label :: String
, x1 :: String
, x2 :: Number
, y :: String
}
derive instance Generic Branch _
derive instance Eq Branch
instance Show Branch where show = genericShow
parseBranches :: Array RawObject -> Array Branch
parseBranches
= map parse
>>> Array.catMaybes
where
parse :: RawObject -> Maybe Branch
parse (BranchToNode o) = Just $ Branch
{ bId : parseInt o.bId
, gvid : o._gvid
, label : o.label
, x1 : o.branch_x
, x2 : Tuple.fst $ parsePos o.pos
, y : o.branch_y
}
parse _ = Nothing
-----------------------------------------------------------
newtype Period = Period
{ from :: Date.Date
, to :: Date.Date
, y :: Number
}
derive instance Generic Period _
derive instance Eq Period
instance Show Period where show = genericShow
parsePeriods :: Boolean -> Array RawObject -> Array Period
parsePeriods epoch
= map parse
>>> Array.catMaybes
where
parse :: RawObject -> Maybe Period
parse (PeriodToNode o) = Just $ Period
{ from : parseNodeDate o.strFrom o.from epoch
, to : parseNodeDate o.strTo o.to epoch
, y : Tuple.snd $ parsePos o.pos
}
parse _ = Nothing
-----------------------------------------------------------
newtype Group = Group
{ bId :: Int
, foundation :: Array Int -- @WIP: Array String ???
, from :: Date.Date
, gId :: Int
, label :: Array String
, role :: Array Int
, size :: Int
, source :: Array String
, to :: Date.Date
, weight :: Number
, x :: Number
, y :: Number
}
derive instance Generic Group _
derive instance Eq Group
instance Show Group where show = genericShow
parseGroups :: Boolean -> Array RawObject -> Array Group
parseGroups epoch
= map parse
>>> Array.catMaybes
where
parse :: RawObject -> Maybe Group
parse (GroupToNode o) = Just $ Group
{ bId : parseInt o.bId
, foundation : stringedArrayToArray' o.foundation
, from : parseNodeDate o.strFrom o.from epoch
, gId : o._gvid
, label : stringedArrayToArray o.lbl
, role : stringedArrayToArray_ o.role
, size : parseInt o.support
, source : parseSources o.source
, to : parseNodeDate o.strTo o.to epoch
, weight : stringedMaybeToNumber o.weight
, x : Tuple.fst $ parsePos o.pos
, y : Tuple.snd $ parsePos o.pos
}
parse _ = Nothing
-----------------------------------------------------------
newtype Link = Link
{ from :: Int
, lId :: Int
, label :: String -- @WIP: undefined in Mèmiescape v2, still needed?
, to :: Int
}
derive instance Generic Link _
derive instance Eq Link
instance Show Link where show = genericShow
parseLinks :: Array RawEdge -> Array Link
parseLinks
= Array.filter filter
>>> map parse
>>> Array.catMaybes
where
-- @WIP: necessary?
-- bc. GroupToGroup as 1-1 relation with "edgeType=link"
filter :: RawEdge -> Boolean
filter (GroupToGroup o) = o.edgeType == "link"
filter _ = false
parse :: RawEdge -> Maybe Link
parse (GroupToGroup o) = Just $ Link
{ from : o.tail
, lId : o._gvid
, label : ""
, to : o.head
}
parse _ = Nothing
-----------------------------------------------------------
newtype AncestorLink = AncestorLink
{ from :: Int
, lId :: Int
, label :: String -- @WIP: undefined in Mèmiescape v2, still needed?
, to :: Int
}
derive instance Generic AncestorLink _
derive instance Eq AncestorLink
instance Show AncestorLink where show = genericShow
parseAncestorLinks :: Array RawEdge -> Array AncestorLink
parseAncestorLinks
= Array.filter filter
>>> map parse
>>> Array.catMaybes
where
-- @WIP: necessary?
-- bc. GroupToAncestor as 1-1 relation with "edgeType=ancestorLink"
filter :: RawEdge -> Boolean
filter (GroupToAncestor o) = o.edgeType == "ancestorLink"
filter _ = false
parse :: RawEdge -> Maybe AncestorLink
parse (GroupToAncestor o) = Just $ AncestorLink
{ from : o.tail
, lId : o._gvid
, label : ""
, to : o.head
}
parse _ = Nothing
-----------------------------------------------------------
newtype BranchLink = BranchLink
{ from :: Int
, to :: Int
}
derive instance Generic BranchLink _
derive instance Eq BranchLink
instance Show BranchLink where show = genericShow
parseBranchLinks :: Array RawEdge -> Array BranchLink
parseBranchLinks
= Array.filter filter
>>> map parse
>>> Array.catMaybes
where
-- @WIP: necessary?
-- bc. BranchToGroup as 1-1 relation with "edgeType=branchLink"
filter :: RawEdge -> Boolean
filter (BranchToGroup o) = o.edgeType == "branchLink"
filter _ = false
parse :: RawEdge -> Maybe BranchLink
parse (BranchToGroup o) = Just $ BranchLink
{ from : o.tail
, to : o.head
}
parse _ = Nothing
-----------------------------------------------------------
newtype GlobalTerm = GlobalTerm
{ label :: String
, fdt :: String
}
derive instance Generic GlobalTerm _
derive instance Eq GlobalTerm
instance Show GlobalTerm where show = genericShow
-----------------------------------------------------------
parseInt :: String -> Int
parseInt s = maybe 0 identity $ Int.fromString s
parseInt' :: Number -> Int
parseInt' n = maybe 0 identity $ Int.fromNumber n
parseFloat :: String -> Number
parseFloat s = maybe 0.0 identity $ Number.fromString s
parseSources :: String -> Array String
parseSources
= String.replace (String.Pattern "[") (String.Replacement "")
>>> String.replace (String.Pattern "]") (String.Replacement "")
>>> String.split (String.Pattern ",")
>>> Array.filter (\s -> not eq 0 $ String.length s)
>>> Array.sort
parseBB :: String -> Array Number
parseBB
= String.split (String.Pattern ",")
>>> map parseFloat
parseNodeDate :: Maybe String -> String -> Boolean -> Date.Date
parseNodeDate Nothing year _ = yearToDate(year)
parseNodeDate (Just str) _ true = utcStringToDate(str)
parseNodeDate (Just str) _ false = stringToDate(str)
parsePos :: String -> Tuple.Tuple Number Number
parsePos
= String.split (String.Pattern ",")
>>> \a -> (p $ Array.index a 0) /\
(p $ Array.index a 1)
where
p = case _ of
Nothing -> 0.0
Just s -> parseFloat s
-- @WIP: why taking last value? use `any`?
getGlobalWeightedValue :: Array Group -> Boolean
getGlobalWeightedValue
= Array.last
>>> case _ of
Nothing -> false
Just (Group o) -> o.weight > 0.0
stringedMaybeToNumber :: String -> Number
stringedMaybeToNumber "Nothing" = 0.0
stringedMaybeToNumber s =
s # String.replace (String.Pattern "Just ") (String.Replacement "")
>>> parseFloat
-- | From "\"user | sentiment analysis\"" :: String
-- |
-- | To ["user", "sentiment analysis"] :: Array String
stringedArrayToArray :: String -> Array String
stringedArrayToArray str
= str # String.length
>>> (\length -> String.splitAt (length - 1) str)
>>> (\{ before } -> String.splitAt 1 before)
>>> (\{ after } -> String.split (String.Pattern "|") after)
>>> map String.trim
-- | From "\"97 | 257 | 542 | 574 | 577 | 597 | 785\"" :: String
-- |
-- | To [97, 257, 542, 574, 577, 597, 785] :: Array Int
stringedArrayToArray' :: String -> Array Int
stringedArrayToArray'
= stringedArrayToArray
>>> map parseInt
-- | From "\"3.0 | 3.0 | 3.0 | 3.0 | 1.0 | 3.0 | 3.0\"" :: String
-- |
-- | To [3, 3, 3, 3, 1, 3, 3] :: Array Int
stringedArrayToArray_ :: String -> Array Int
stringedArrayToArray_
= stringedArrayToArray
>>> map parseFloat
>>> map parseInt'
module Gargantext.Components.Reload
( reloadContext
, textsReloadContext ) where
import Data.Maybe (Maybe(..))
import Gargantext.Utils.Toestand as T2
import Reactix as R
import Toestand as T
-- | Reload Context
-- |
-- | Use with `R.provideContext` as a (nested) context
-- |
-- | https://medium.com/@NickIannelli/nested-context-the-underrated-aspect-thats-probably-missing-from-your-react-app-16e73f7d1
reloadContext :: R.Context (Maybe (T.Box T2.Reload))
reloadContext = R.createContext Nothing
-----------------------------------------------------------------
-- @XXX: This custom context solves a wrong monolithic front design where
-- "DocsTable" component is used for many different use cases
-- Normally we would have use the classic "Gargantext.Components.Reload",
-- but we limit side-effects by using another context reference
--
-- See its use in "Gargantext.Components.Nodes.Texts"
textsReloadContext :: R.Context (Maybe (T.Box T2.Reload))
textsReloadContext = R.createContext Nothing
...@@ -22,7 +22,7 @@ defaultBackends = backend' "Demo" "Public Show room" "http ...@@ -22,7 +22,7 @@ defaultBackends = backend' "Demo" "Public Show room" "http
, backend' "Organization" "Hello Word Company" "https://helloword.gargantext.org" , backend' "Organization" "Hello Word Company" "https://helloword.gargantext.org"
, backend' "Networking" "Complex Systems Community" "https://complexsystems.gargantext.org" , backend' "Networking" "Complex Systems Community" "https://complexsystems.gargantext.org"
, backend' "Networking" "Digeing European Project" "https://europa.gargantext.org" , backend' "Networking" "Digeing European Project" "https://europa.gargantext.org"
, backend' "Development" "Main SandBox" "https://dev.gargantext.org" , backend' "Development" "Main SandBox" "https://dev.sub.gargantext.org"
, backend' "Private" "Offline Bunker" "http://localhost:8008" , backend' "Private" "Offline Bunker" "http://localhost:8008"
] ]
...@@ -58,7 +58,7 @@ defaultApps = relative :| [prod, dev, demo, haskell, python, caddy] ...@@ -58,7 +58,7 @@ defaultApps = relative :| [prod, dev, demo, haskell, python, caddy]
where where
relative = frontend "/#/" "" "Relative" relative = frontend "/#/" "" "Relative"
prod = frontend "/#/" "https://v4.gargantext.org" "v4.gargantext.org" prod = frontend "/#/" "https://v4.gargantext.org" "v4.gargantext.org"
dev = frontend "/#/" "https://dev.gargantext.org" "gargantext.org (dev)" dev = frontend "/#/" "https://dev.sub.gargantext.org" "gargantext.org (dev)"
demo = frontend "/#/" "https://demo.gargantext.org" "gargantext.org (demo)" demo = frontend "/#/" "https://demo.gargantext.org" "gargantext.org (demo)"
haskell = frontend "/#/" "http://localhost:8008" "localhost.gargantext" haskell = frontend "/#/" "http://localhost:8008" "localhost.gargantext"
python = frontend "/#/" "http://localhost:8000" "localhost.python" python = frontend "/#/" "http://localhost:8000" "localhost.python"
......
...@@ -152,7 +152,7 @@ postMultipartFormData mtoken url body = do ...@@ -152,7 +152,7 @@ postMultipartFormData mtoken url body = do
, ARH.Accept applicationJSON , ARH.Accept applicationJSON
] <> ] <>
foldMap (\token -> foldMap (\token ->
[ ARH.RequestHeader "Authorization" $ "Bearer " <> token ] [ ARH.RequestHeader "Authorization" $ " " <> token ]
) mtoken ) mtoken
, content = Just $ formData fd , content = Just $ formData fd
} }
......
...@@ -195,7 +195,7 @@ sessionPath (R.ChartHash { chartType, listId, tabType } i) = ...@@ -195,7 +195,7 @@ sessionPath (R.ChartHash { chartType, listId, tabType } i) =
<> "&listType=" <> show MapTerm -- listId <> "&listType=" <> show MapTerm -- listId
<> defaultListAddMaybe listId <> defaultListAddMaybe listId
-- sessionPath (R.NodeAPI (NodeContact s a i) i) = sessionPath $ "annuaire/" <> show a <> "/contact/" <> show i -- sessionPath (R.NodeAPI (NodeContact s a i) i) = sessionPath $ "annuaire/" <> show a <> "/contact/" <> show i
sessionPath (R.NodeAPI Phylo pId p) = "phyloscape?nodeId=" <> (show $ fromMaybe 0 pId) <> p
------- misc routing stuff ------- misc routing stuff
......
...@@ -3,7 +3,7 @@ module Gargantext.Hooks.FormValidation.Boxed ...@@ -3,7 +3,7 @@ module Gargantext.Hooks.FormValidation.Boxed
, class NonEmpty, nonEmpty , class NonEmpty, nonEmpty
, class Minimum, minimum , class Minimum, minimum
, class Maximum, maximum , class Maximum, maximum
, lowercase, uppercase, email , lowercase, uppercase, email, date
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -14,7 +14,7 @@ import Data.String.Regex (test) ...@@ -14,7 +14,7 @@ import Data.String.Regex (test)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Data.Validation.Semigroup (invalid) import Data.Validation.Semigroup (invalid)
import Effect (Effect) import Effect (Effect)
import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern) import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern, datePattern)
import Toestand as T import Toestand as T
class Eq a <= Equals a where class Eq a <= Equals a where
...@@ -75,3 +75,9 @@ email field = T.read >=> case _ of ...@@ -75,3 +75,9 @@ email field = T.read >=> case _ of
input input
| (not $ test emailPattern input) -> pure $ invalid [ field /\ "email" ] | (not $ test emailPattern input) -> pure $ invalid [ field /\ "email" ]
| otherwise -> pure $ pure unit | otherwise -> pure $ pure unit
date :: Field -> T.Box String -> Effect VForm
date field = T.read >=> case _ of
input
| (not $ test datePattern input) -> pure $ invalid [ field /\ "date" ]
| otherwise -> pure $ pure unit
...@@ -8,3 +8,9 @@ ...@@ -8,3 +8,9 @@
* @type {RegExp} * @type {RegExp}
*/ */
exports.emailPattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/; exports.emailPattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
/**
* Date Pattern
* @link https://www.regextester.com/96683
* @type {RegExp}
*/
exports.datePattern = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/
module Gargantext.Hooks.FormValidation.Types module Gargantext.Hooks.FormValidation.Types
( VForm, EForm, Field ( VForm, EForm, Field
, emailPattern , emailPattern, datePattern
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -11,6 +11,7 @@ import Data.Tuple (Tuple) ...@@ -11,6 +11,7 @@ import Data.Tuple (Tuple)
import Data.Validation.Semigroup (V) import Data.Validation.Semigroup (V)
foreign import emailPattern :: Regex foreign import emailPattern :: Regex
foreign import datePattern :: Regex
-- @TODO: types for errors (`Tuple Field String`)? -- @TODO: types for errors (`Tuple Field String`)?
......
...@@ -3,7 +3,7 @@ module Gargantext.Hooks.FormValidation.Unboxed ...@@ -3,7 +3,7 @@ module Gargantext.Hooks.FormValidation.Unboxed
, class NonEmpty, nonEmpty , class NonEmpty, nonEmpty
, class Minimum, minimum , class Minimum, minimum
, class Maximum, maximum , class Maximum, maximum
, lowercase, uppercase, email , lowercase, uppercase, email, date
) where ) where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -14,7 +14,7 @@ import Data.String.Regex (test) ...@@ -14,7 +14,7 @@ import Data.String.Regex (test)
import Data.Tuple.Nested ((/\)) import Data.Tuple.Nested ((/\))
import Data.Validation.Semigroup (invalid) import Data.Validation.Semigroup (invalid)
import Effect (Effect) import Effect (Effect)
import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern) import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern, datePattern)
class Eq a <= Equals a where class Eq a <= Equals a where
equals :: Field -> a -> a -> Effect VForm equals :: Field -> a -> a -> Effect VForm
...@@ -63,3 +63,8 @@ email :: Field -> String -> Effect VForm ...@@ -63,3 +63,8 @@ email :: Field -> String -> Effect VForm
email field input email field input
| (not $ test emailPattern input) = pure $ invalid [ field /\ "email" ] | (not $ test emailPattern input) = pure $ invalid [ field /\ "email" ]
| otherwise = pure $ pure unit | otherwise = pure $ pure unit
date :: Field -> String -> Effect VForm
date field input
| (not $ test datePattern input) = pure $ invalid [ field /\ "date" ]
| otherwise = pure $ pure unit
...@@ -658,6 +658,7 @@ data AsyncTaskType = AddNode ...@@ -658,6 +658,7 @@ data AsyncTaskType = AddNode
| GraphRecompute | GraphRecompute
| ListUpload | ListUpload
| ListCSVUpload -- legacy v3 CSV upload for lists | ListCSVUpload -- legacy v3 CSV upload for lists
| NodeDocument
| Query | Query
| UpdateNgramsCharts | UpdateNgramsCharts
| UpdateNode | UpdateNode
...@@ -678,6 +679,7 @@ asyncTaskTypePath CorpusFormUpload = "add/form/async/" ...@@ -678,6 +679,7 @@ asyncTaskTypePath CorpusFormUpload = "add/form/async/"
asyncTaskTypePath GraphRecompute = "async/recompute/" asyncTaskTypePath GraphRecompute = "async/recompute/"
asyncTaskTypePath ListUpload = "add/form/async/" asyncTaskTypePath ListUpload = "add/form/async/"
asyncTaskTypePath ListCSVUpload = "csv/add/form/async/" asyncTaskTypePath ListCSVUpload = "csv/add/form/async/"
asyncTaskTypePath NodeDocument = "document/upload/async"
asyncTaskTypePath Query = "query/" asyncTaskTypePath Query = "query/"
asyncTaskTypePath UpdateNgramsCharts = "ngrams/async/charts/update/" asyncTaskTypePath UpdateNgramsCharts = "ngrams/async/charts/update/"
asyncTaskTypePath UpdateNode = "update/" asyncTaskTypePath UpdateNode = "update/"
......
...@@ -39,7 +39,7 @@ instance ( GenericTaggedSumRep a ...@@ -39,7 +39,7 @@ instance ( GenericTaggedSumRep a
) => GenericTaggedSumRep (GR.Constructor name a) where ) => GenericTaggedSumRep (GR.Constructor name a) where
genericTaggedSumRep f = do genericTaggedSumRep f = do
-- r :: { "type" :: String } <- JSON.read' f -- r :: { "type" :: String } <- JSON.read' f
-- if r."type" == name -- if r."type" == name
-- then withExcept (map $ ErrorAtProperty name) $ GR.Constructor <$> genericTaggedSumRep r -- then withExcept (map $ ErrorAtProperty name) $ GR.Constructor <$> genericTaggedSumRep r
-- else fail $ ForeignError $ "Wrong type tag " <> r."type" <> " where " <> name <> " was expected." -- else fail $ ForeignError $ "Wrong type tag " <> r."type" <> " where " <> name <> " was expected."
r :: FO.Object Foreign <- JSON.read' f r :: FO.Object Foreign <- JSON.read' f
...@@ -60,5 +60,28 @@ instance ( JSON.ReadForeign a ...@@ -60,5 +60,28 @@ instance ( JSON.ReadForeign a
-----------------------------------------------------------
-- | Applying Generics-Rep to decoding untagged JSON values
-- |
-- | https://purescript-simple-json.readthedocs.io/en/latest/generics-rep.html
class UntaggedSumRep rep where
untaggedSumRep :: Foreign -> Foreign.F rep
instance untaggedSumRepSum ::
( UntaggedSumRep a
, UntaggedSumRep b
) => UntaggedSumRep (GR.Sum a b) where
untaggedSumRep f
= GR.Inl <$> untaggedSumRep f
<|> GR.Inr <$> untaggedSumRep f
instance untaggedSumRepConstructor ::
( UntaggedSumRep a
) => UntaggedSumRep (GR.Constructor name a) where
untaggedSumRep f = GR.Constructor <$> untaggedSumRep f
instance untaggedSumRepArgument ::
( JSON.ReadForeign a
) => UntaggedSumRep (GR.Argument a) where
untaggedSumRep f = GR.Argument <$> JSON.readImpl f
/* fonts */
@font-face {
font-family: 'Inter-Regular';
font-style: normal;
font-weight: 400;
src: url("../fonts/phylo/Inter-Regular.woff2") format("woff2"),
url("../fonts/phylo/Inter-Regular.woff") format("woff");
}
@font-face {
font-family: 'Inter-Bold';
font-style: normal;
font-weight: 700;
src: url("../fonts/phylo/Inter-Bold.woff2") format("woff2"),
url("../fonts/phylo/Inter-Bold.woff") format("woff");
}
/* grid */
.phylo {
font-family: "Inter-Regular";
font-size: 16px;
display: grid;
grid-template-columns: repeat(15, 1fr);
grid-template-rows: 2% 7% 7% auto 1%;
grid-gap: 10px;
height: 100vh;
color: #0d1824;
}
/* ---- row 1 ---- */
.phylo-title {
grid-row: 1;
grid-column: 1 / 2;
align-items: center;
text-align: right;
margin-top: 0.5px;
}
.phylo-folder {
grid-row: 1;
grid-column: 2 / 16;
align-items: center;
}
/* -------------------- */
.phylo-corpus {
grid-row: 2 / 3;
grid-column: 1 / 2;
/*background : #3E75B3;*/
font-size: 14px;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-phylo {
grid-row: 3 / 4;
grid-column: 1 / 2;
font-size: 14px;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-corpus-info {
grid-row: 2 / 3;
grid-column: 2 / 4;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
}
.phylo-phylo-info {
grid-row: 3 / 4;
grid-column: 2 / 4;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
}
.phylo-how {
grid-row: 2 / 4;
grid-column: 1 / 2;
z-index: 2;
display: flex;
align-items: center;
justify-content: right;
}
.phylo-isoline {
grid-row: 2 / 4;
grid-column: 3 / 15;
/*background: rgba(223,216,200,0.25); */
}
.phylo-isoline-info {
grid-row: 2 / 4;
grid-column: 15 / 16;
padding-top: 20%;
padding-bottom: 20%;
padding-left: 15px;
.btn-group {
display: initial;
}
}
/* -------------------- */
.phylo-scape {
grid-row: 4;
grid-column: 1 / 15;
/*background: #FFCC73;*/
}
.phylo-timeline {
grid-row: 4;
grid-column: 1 / 2;
}
.phylo-graph {
grid-row: 4;
grid-column: 15 / 16;
/*background: #FFCC73;*/
}
/* classes */
/* ---------- icons ---------- */
a {
color: inherit;
cursor: pointer;
}
.how {
cursor: pointer;
position: relative;
}
.tooltip {
font-family: "Inter-Regular";
font-size: 14px;
font-style: normal;
font-weight: 400;
}
i.how span {
position: absolute;
width:300px;
color: #FFFFFF;
background: #0d1824;
height: 30px;
line-height: 30px;
text-align: center;
visibility: hidden;
border-radius: 6px;
}
i.how span:after {
content: '';
position: absolute;
top: 50%;
right: 100%;
margin-top: -8px;
width: 0; height: 0;
border-right: 8px solid #0d1824;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
i.how:hover span {
visibility: visible;
left: 100%;
top: 50%;
margin-top: -15px;
margin-left: 15px;
z-index: 999;
}
.switch > .far + .fa,
.switch:hover > .far {
display: none;
}
.switch:hover > .far + .fa {
display: inherit;
color: #0d1824;
}
/* ---------- fonts ---------- */
.font-bold {
font-family: "Inter-Bold";
text-transform: uppercase;
}
.font-small {
color: #0d1824;
font-size: 12px;
}
.header {
/*font-weight: bold;*/
/*text-transform: uppercase;*/
font-weight: 500;
cursor: pointer;
}
.header:hover {
font-weight: bold;
}
/* ---------- input ---------- */
.button {
background-color: white;
border: 1.5px solid #0d1824;
cursor: pointer;
}
.button:hover {
background-color: #0d1824;
color: white;
}
.btn-group button{
margin-top: 1px;
margin-bottom: 1px;
display: block;
width: 40px;
}
.draw {
display: none;
}
.phylo-focus {
fill: #f8381f;
color: #f8381f;
}
.reset {
visibility: hidden;
}
.label {
visibility: hidden;
}
.heading {
visibility: hidden;
}
.export {
visibility: hidden;
}
.headed {
background-color: #0d1824;
color: white;
}
.labeled {
background-color: #0d1824;
color: white;
}
.input-file {
display: inline-block;
cursor: pointer;
}
.input-file:hover {
border-bottom: 1.5px solid #0d1824;
}
.input-name {
font-style: italic;
opacity: 0.7;
font-size: 14px;
padding-left: 6px;
padding-right: 6px;
}
/* ---------- axis ---------- */
.x-axis path {
stroke: #EBE4DD;
stroke-width: 1.5px;
}
.y-axis path {
stroke: #EBE4DD;
stroke-width: 1.5px;
}
.y-highlight {
stroke: #f3be54;
stroke-width: 1.5px;
}
.x-mark {
fill: #4A5C70;
stroke-width: 1px;
stroke: #fff;
}
.x-mark-over {
fill: #f3be54;
}
.x-mark-focus {
fill: #f8381f;
}
.tick text {
font-family: "Inter-Regular";
}
.tick text:hover {
cursor: pointer;
}
.y-label {
font-size: 10px;
font-family: "Inter-Regular";
font-weight: normal;
}
.y-label-bold {
font-size: 12px;
font-family: "Inter-Bold";
font-weight: bold;
}
.y-mark-year-inner {
fill: #4A5C70;
}
.y-mark-year-inner-highlight {
fill: #f3be54;
}
.y-mark-year-outer {
fill: #fff;
stroke: #4A5C70;
stroke-width: 1px;
}
.y-mark-year-outer-highlight {
fill: #fff;
stroke: #f3be54;
stroke-width: 3px;
}
.y-mark-month {
fill: #4A5C70;
}
/* ---------- group ---------- */
.group-outer {
stroke-width: 0.8px;
stroke: #fff;
fill: #fff;
}
.group-inner {
stroke-width: 0.8px;
stroke: #0d1824;
fill: #0d1824;
/*cursor: pointer;*/
z-index: 10;
}
.group-heading {
fill: #fff;
stroke: #B5B5B5;
}
.group-focus {
stroke: #f8381f;
}
.source-focus {
stroke: #67a9cf;
}
.group-unfocus {
stroke: #A9A9A9;
}
.group-path {
cursor: pointer;
}
/* ---------- labels ---------- */
.ngrams {
visibility: hidden;
}
.term {
cursor: pointer;
}
.term:hover {
font-weight: bold;
fill: #f8381f;
}
// .term-unfocus {
// fill: #A9A9A9;
// }
// .term-focus {
// fill: black;
// }
.term-path {
fill: none;
stroke: #F0684D;
stroke-width: 1.5px;
}
.emerging {
/*text-decoration: underline #F0684D;*/
/*fill:#5AA350;*/
/*fill: #5AA350;*/
fill: #F8381F;
}
.decreasing {
/*text-decoration: underline #74B5FF;*/
fill: #11638F;
}
.path-focus {
fill: none;
stroke: #F0684D;
stroke-width: 1.5px;
}
.path-unfocus {
stroke: #A9A9A9;
}
.path-heading {
stroke: #B5B5B5;
}
/* ---------- phylo ---------- */
.branch-hover {
fill: #f3be54;
opacity: 0.5;
}
/* elements */
#file-path {
display: none;
}
/* axis */
.axisRight {
font-family: "Inter-Regular";
font-size: 10px;
}
/* isoline */
.peak {
stroke: white;
stroke-width: 1px;
font-family: "Inter-Regular";
font-size: 14px;
text-anchor: middle;
visibility: visible;
}
.peak-over {
font-size: 18px;
stroke-width: 2px;
cursor: pointer;
stroke: #f3be54;
z-index: 100;
}
.peak-focus {
font-size: 18px;
stroke-width: 2px;
stroke: #F0684D;
}
.peak-focus-source {
font-size: 18px;
stroke-width: 2px;
stroke: #67a9cf;
}
.peak-label {
text-align: center;
font-family: "Inter-Regular";
font-size: 14px;
font-style: normal;
font-weight: 400;
color: #FFFFFF;
border-radius: 3px;
border-style: solid;
border-width: 2px;
border-color: white;
background: #0d1824;
padding: 5px;
z-index: 10;
position: absolute;
visibility: hidden;
}
.word-cloud {
font-family: "Inter-Regular";
font-size: 12px;
}
.search {
margin-left: 10px;
visibility: hidden;
position: absolute;
z-index: 7;
font-size: 14px;
background-color: transparent;
outline: 0;
border-width: 0 0 2px;
border-color: #0d1824
}
.search-label {
visibility: hidden;
margin-left: 5px;
}
.search:focus {
border-color: #F0684D
}
.autocomplete {
margin-left: 10px;
visibility: hidden;
position: absolute;
z-index: 7;
font-size: 14px;
background-color: transparent;
color: silver;
z-index: 1;
border: none;
}
.loading {
visibility: hidden;
}
.phylo-name {
visibility:hidden;
text-transform: capitalize;
font-weight: bold;
}
.select-source {
margin-left: 10px;
display: none;
border: 1.5px solid #0d1824;
cursor: pointer;
outline: 0;
background: transparent;
border-image: none;
outline-offset: -2px;
outline-color: transparent;
box-shadow: none;
-webkit-appearance: none;
}
option {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
...@@ -8,3 +8,4 @@ ...@@ -8,3 +8,4 @@
@use "_range_slider.sass" @use "_range_slider.sass"
@use "_annotation.sass" @use "_annotation.sass"
@use "_folder_view.sass" @use "_folder_view.sass"
@use "_phylo.scss"
...@@ -1153,6 +1153,11 @@ ...@@ -1153,6 +1153,11 @@
dependencies: dependencies:
unzipper "^0.9.3" unzipper "^0.9.3"
"@graphql-typed-document-node/core@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.0.tgz#0eee6373e11418bfe0b5638f654df7a4ca6a3950"
integrity sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg==
"@hutson/parse-repository-url@^3.0.0": "@hutson/parse-repository-url@^3.0.0":
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340"
...@@ -1918,6 +1923,14 @@ ...@@ -1918,6 +1923,14 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@urql/core@^2.3.3":
version "2.3.3"
resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.3.3.tgz#e4777b95c31d8ad0ba21ea1f5c851cbbe1d0ac17"
integrity sha512-Bi9mafTFu0O1XZmI7/HrEk12LHZW+Fs/V1FqSJoUDgYIhARIJW6cCh3Havy1dJJ0FETxYmmQQXPf6kst+IP2qQ==
dependencies:
"@graphql-typed-document-node/core" "^3.1.0"
wonka "^4.0.14"
"@vue/compiler-core@3.1.5": "@vue/compiler-core@3.1.5":
version "3.1.5" version "3.1.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.1.5.tgz#298f905b6065d6d81ff63756f98c60876b393c87" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.1.5.tgz#298f905b6065d6d81ff63756f98c60876b393c87"
...@@ -3032,6 +3045,11 @@ commander@2.11.x: ...@@ -3032,6 +3045,11 @@ commander@2.11.x:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==
commander@7, commander@^7.0.0, commander@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^2.20.0: commander@^2.20.0:
version "2.20.3" version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
...@@ -3042,11 +3060,6 @@ commander@^6.0.0: ...@@ -3042,11 +3060,6 @@ commander@^6.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^7.0.0, commander@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^8.0.0: commander@^8.0.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362" resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362"
...@@ -3570,6 +3583,250 @@ csstype@^3.0.2: ...@@ -3570,6 +3583,250 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c"
integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==
dependencies:
internmap "1 - 2"
d3-axis@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
d3-brush@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
dependencies:
d3-dispatch "1 - 3"
d3-drag "2 - 3"
d3-interpolate "1 - 3"
d3-selection "3"
d3-transition "3"
d3-chord@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
dependencies:
d3-path "1 - 3"
"d3-color@1 - 3", d3-color@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a"
integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==
d3-contour@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-3.0.1.tgz#2c64255d43059599cd0dba8fe4cc3d51ccdd9bbd"
integrity sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==
dependencies:
d3-array "2 - 3"
d3-delaunay@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92"
integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==
dependencies:
delaunator "5"
"d3-dispatch@1 - 3", d3-dispatch@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
"d3-drag@2 - 3", d3-drag@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
dependencies:
d3-dispatch "1 - 3"
d3-selection "3"
"d3-dsv@1 - 3", d3-dsv@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
dependencies:
commander "7"
iconv-lite "0.6"
rw "1"
"d3-ease@1 - 3", d3-ease@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
d3-fetch@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
dependencies:
d3-dsv "1 - 3"
d3-force@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
dependencies:
d3-dispatch "1 - 3"
d3-quadtree "1 - 3"
d3-timer "1 - 3"
"d3-format@1 - 3", d3-format@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.0.1.tgz#e41b81b2ab79277141ec1404aa5d05001da64084"
integrity sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==
d3-geo@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e"
integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==
dependencies:
d3-array "2.5.0 - 3"
d3-hierarchy@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz#0365342d54972e38ca05e9143e0ab1c60846b3b5"
integrity sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
dependencies:
d3-color "1 - 3"
"d3-path@1 - 3", d3-path@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e"
integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==
d3-polygon@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
"d3-quadtree@1 - 3", d3-quadtree@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
d3-random@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
d3-scale-chromatic@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a"
integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==
dependencies:
d3-color "1 - 3"
d3-interpolate "1 - 3"
d3-scale@4:
version "4.0.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
dependencies:
d3-array "2.10.0 - 3"
d3-format "1 - 3"
d3-interpolate "1.2.0 - 3"
d3-time "2.1.1 - 3"
d3-time-format "2 - 4"
"d3-selection@2 - 3", d3-selection@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
d3-shape@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.0.1.tgz#9ccdfb28fd9b0d12f2d8aec234cd5c4a9ea27931"
integrity sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==
dependencies:
d3-path "1 - 3"
"d3-time-format@2 - 4", d3-time-format@4:
version "4.0.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.0.0.tgz#930ded86a9de761702344760d8a25753467f28b7"
integrity sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==
dependencies:
d3-time "1 - 3"
"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975"
integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==
dependencies:
d3-array "2 - 3"
"d3-timer@1 - 3", d3-timer@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
"d3-transition@2 - 3", d3-transition@3:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
dependencies:
d3-color "1 - 3"
d3-dispatch "1 - 3"
d3-ease "1 - 3"
d3-interpolate "1 - 3"
d3-timer "1 - 3"
d3-zoom@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
dependencies:
d3-dispatch "1 - 3"
d3-drag "2 - 3"
d3-interpolate "1 - 3"
d3-selection "2 - 3"
d3-transition "2 - 3"
d3@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/d3/-/d3-7.1.1.tgz#77b9a0c9893b13643b8e52316ec65dca3a6a115e"
integrity sha512-8zkLMwSvUAnfN9pcJDfkuxU0Nvg4RLUD0A4BZN1KxJPtlnCGzMx3xM5cRl4m8fym/Vy8rlq52tl90UF3m91OnA==
dependencies:
d3-array "3"
d3-axis "3"
d3-brush "3"
d3-chord "3"
d3-color "3"
d3-contour "3"
d3-delaunay "6"
d3-dispatch "3"
d3-drag "3"
d3-dsv "3"
d3-ease "3"
d3-fetch "3"
d3-force "3"
d3-format "3"
d3-geo "3"
d3-hierarchy "3"
d3-interpolate "3"
d3-path "3"
d3-polygon "3"
d3-quadtree "3"
d3-random "3"
d3-scale "4"
d3-scale-chromatic "3"
d3-selection "3"
d3-shape "3"
d3-time "3"
d3-time-format "4"
d3-timer "3"
d3-transition "3"
d3-zoom "3"
dargs@^7.0.0: dargs@^7.0.0:
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"
...@@ -3703,6 +3960,13 @@ defined@^1.0.0: ...@@ -3703,6 +3960,13 @@ defined@^1.0.0:
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
delaunator@5:
version "5.0.0"
resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b"
integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==
dependencies:
robust-predicates "^3.0.0"
delayed-stream@~1.0.0: delayed-stream@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
...@@ -4734,6 +4998,16 @@ graceful-fs@^4.1.2: ...@@ -4734,6 +4998,16 @@ graceful-fs@^4.1.2:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
graphql-ws@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.5.0.tgz#79f10248d23d104369eaef93acb9f887276a2c42"
integrity sha512-WQepPMGQQoqS2VsrI2I3RMLCVz3CW4/6ZqGV6ABDOwH4R62DzjxwMlwZbj6vhSI/7IM3/C911yITwgs77iO/hw==
graphql@^15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.6.1.tgz#9125bdf057553525da251e19e96dab3d3855ddfc"
integrity sha512-3i5lu0z6dRvJ48QP9kFxBkJ7h4Kso7PS8eahyTFz5Jm6CvQfLtNIE8LX9N6JLnXTuwR+sIYnXzaWp6anOg0QQw==
handlebars@^4.7.6: handlebars@^4.7.6:
version "4.7.7" version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
...@@ -5047,6 +5321,13 @@ iconv-lite@0.4.24: ...@@ -5047,6 +5321,13 @@ iconv-lite@0.4.24:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3" safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
...@@ -5131,6 +5412,11 @@ internal-slot@^1.0.3: ...@@ -5131,6 +5412,11 @@ internal-slot@^1.0.3:
has "^1.0.3" has "^1.0.3"
side-channel "^1.0.4" side-channel "^1.0.4"
"internmap@1 - 2":
version "2.0.3"
resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
invariant@^2.2.4: invariant@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
...@@ -5567,6 +5853,14 @@ isobject@^3.0.0, isobject@^3.0.1: ...@@ -5567,6 +5853,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-unfetch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f"
integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==
dependencies:
node-fetch "^2.6.1"
unfetch "^4.2.0"
isstream@~0.1.2: isstream@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
...@@ -6476,6 +6770,13 @@ node-addon-api@^3.0.2: ...@@ -6476,6 +6770,13 @@ node-addon-api@^3.0.2:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-fetch@^2.6.1:
version "2.6.5"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@^0.10.0: node-forge@^0.10.0:
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
...@@ -8255,6 +8556,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: ...@@ -8255,6 +8556,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0" hash-base "^3.0.0"
inherits "^2.0.1" inherits "^2.0.1"
robust-predicates@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
router-ips@^1.0.0: router-ips@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/router-ips/-/router-ips-1.0.0.tgz#44e00858ebebc0133d58e40b2cd8a1fbb04203f5" resolved "https://registry.yarnpkg.com/router-ips/-/router-ips-1.0.0.tgz#44e00858ebebc0133d58e40b2cd8a1fbb04203f5"
...@@ -8267,6 +8573,11 @@ run-parallel@^1.1.9: ...@@ -8267,6 +8573,11 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
rw@1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
...@@ -8289,7 +8600,7 @@ safe-regex@^1.1.0: ...@@ -8289,7 +8600,7 @@ safe-regex@^1.1.0:
dependencies: dependencies:
ret "~0.1.10" ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
...@@ -9046,6 +9357,11 @@ tr46@^1.0.1: ...@@ -9046,6 +9357,11 @@ tr46@^1.0.1:
dependencies: dependencies:
punycode "^2.1.0" punycode "^2.1.0"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
"traverse@>=0.3.0 <0.4": "traverse@>=0.3.0 <0.4":
version "0.3.9" version "0.3.9"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
...@@ -9175,6 +9491,11 @@ uncss@^0.17.3: ...@@ -9175,6 +9491,11 @@ uncss@^0.17.3:
postcss-selector-parser "6.0.2" postcss-selector-parser "6.0.2"
request "^2.88.0" request "^2.88.0"
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
unicode-canonical-property-names-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
...@@ -9608,6 +9929,11 @@ weak-lru-cache@^1.0.0: ...@@ -9608,6 +9929,11 @@ weak-lru-cache@^1.0.0:
resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.0.0.tgz#f1394721169883488c554703704fbd91cda05ddf" resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.0.0.tgz#f1394721169883488c554703704fbd91cda05ddf"
integrity sha512-135bPugHHIJLNx20guHgk4etZAbd7nou34NQfdKkJPgMuC3Oqn4cT6f7ORVvnud9oEyXJVJXPcTFsUvttGm5xg== integrity sha512-135bPugHHIJLNx20guHgk4etZAbd7nou34NQfdKkJPgMuC3Oqn4cT6f7ORVvnud9oEyXJVJXPcTFsUvttGm5xg==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^4.0.2: webidl-conversions@^4.0.2:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
...@@ -9639,6 +9965,14 @@ whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: ...@@ -9639,6 +9965,14 @@ whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^7.0.0: whatwg-url@^7.0.0:
version "7.1.0" version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
...@@ -9697,6 +10031,11 @@ widest-line@^2.0.0: ...@@ -9697,6 +10031,11 @@ widest-line@^2.0.0:
dependencies: dependencies:
string-width "^2.1.1" string-width "^2.1.1"
wonka@^4.0.14:
version "4.0.15"
resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.15.tgz#9aa42046efa424565ab8f8f451fcca955bf80b89"
integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==
word-wrap@~1.2.3: word-wrap@~1.2.3:
version "1.2.3" version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment