Commit 445858fc authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into 228-doc-annotation-multiterms

parents c3987256 95d4771f
......@@ -12,14 +12,21 @@
bundle.js
# tempfiles
.#*
# webpack splays purescript modules in dist. we don't want these to be
# added, but we do want static assets to be added
/dist/*
!/dist/styles/
/dist/styles/*map
!/dist/examples/
!/dist/fonts/
!/dist/images/
!/dist/js/
# css source maps
/dist/styles/*map
# # webpack splays purescript modules in dist. we don't want these to be
# # added, but we do want static assets to be added
# /dist/*
# !/dist/styles/
# /dist/styles/*map
# !/dist/examples/
# !/dist/fonts/
# !/dist/images/
# !/dist/js/
# # css source maps
# /dist/styles/*map
# production builds
/prod
/dce-output
/app.js
# docker
/.bash_history
/.lesshst
FROM debian:stable-slim
ENV CWD=/opt/app HOME=/opt/app TERM=xterm
RUN apt-get update && apt-get install -y bash curl inotify-tools git build-essential libtinfo5
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn
RUN curl -L https://github.com/coot/zephyr/releases/download/v0.3.2/Linux.tar.gz | tar zx -C /usr/bin --strip-components=1 zephyr/zephyr
WORKDIR /opt/app
EXPOSE 5000/tcp
CMD ["bash"]
\ No newline at end of file
......@@ -14,14 +14,47 @@ granted access to a [backend](https://gitlab.iscpif.fr/gargantext/haskell-gargan
This software is free software, developed by the CNRS Complex Systems
Institute of Paris Île-de-France (ISC-PIF) and its partners.
## Dependencies
## Getting set up
There are two approaches to working with the build:
1. Use our docker setup
2. Install our dependencies yourself
The javascript ecosystem kind of assumes if you're on linux, you're
running on debian or ubuntu. I haven't yet managed to get garg to
build on alpine linux, for example. If you're on an oddball system, I
*strongly* recommend you just use the docker setup.
### Docker setup
You will need docker and docker-compose installed.
First, Source our environment file:
```shell
source ./env.sh
```
WARNING: you must `source ./env.sh` before using the docker
container. If you don't do that, the container will write files as
root and you'll need root powers to get ownership back!
Now build the docker image:
```shell
docker-compose build frontend
```
That's it, skip ahead to "Development".
### Manual setup
The build requires the following system dependencies preinstalled:
* NodeJS (11+)
* Yarn (Recent)
### NodeJS
#### NodeJS
On debian testing, debian unstable or ubuntu:
......@@ -55,7 +88,7 @@ brew install node
For other platforms, please refer to [the nodejs website](https://nodejs.org/en/download/).
### Yarn (javascript package manager)
#### Yarn (javascript package manager)
On debian or ubuntu:
......@@ -75,63 +108,89 @@ For other platforms, please refer to [the yarn website](https://www.yarnpkg.com/
## Development
Once you have node and yarn installed, you may install deps with:
### Docker environment
Are you using the docker setup? Run this:
```shell
source ./env.sh
```
This enables the docker container to run as the current user so any
files it writes will be readable by you. It also creates a `darn`
shell alias (short for `docker yarn`) for running yarn commands inside
the docker container.
### Basic tasks
Now we must install our javascript and purescript dependencies:
```shell
yarn install -D && yarn install-ps
darn install -D && darn install-ps # for docker setup
yarn install -D && yarn install-ps # for manual setup
```
You will likely want to check your work in a browser. We provide a
local development webserver that serves on port 5000 for this purpose:
```shell
yarn server
darn server # for docker setup
yarn server # for manual setup
```
To generate a new browser bundle to test:
```shell
yarn build
darn build # for docker setup
yarn build # for manual setup
```
If you are rapidly iterating and just want to type check your code:
```shell
yarn compile
darn compile # for docker setup
yarn compile # for manual setup
```
You may access a purescript repl if you want to explore:
```shell
yarn repl
darn repl # for docker setup
yarn repl # for manual setup
```
If you need to reinstall dependencies such as after a git pull or branch switch:
```shell
yarn install -D && yarn install-ps # both javascript and purescript
darn install -D && darn install-ps # for docker setup
yarn install -D && yarn install-ps # for manual setup
```
If something goes wrong building after a deps update, you may clean
build artifacts and try again:
```shell
yarn clean-js # clean javascript, very useful
yarn clean-ps # clean purescript, should never be required, possible purescript bug
yarn clean # clean both purescript and javascript
# for docker setup
darn clean-js # clean javascript, very useful
darn clean-ps # clean purescript, should never be required, possible purescript bug
darn clean # clean both purescript and javascript
# for manual setup
yarn clean-js
yarn clean-ps
yarn clean
```
If you edit the SASS, you'll need to rebuild the CSS:
```shell
yarn sass
darn css # for docker setup
yarn css # for manual setup
```
<!-- A `purs ide` connection will be available on port 9002 while the -->
<!-- development server is running. -->
A guide to getting set up with the IDE integration is coming soon, I hope.
of this document.
### Note to contributors
......@@ -139,20 +198,6 @@ Please follow CONTRIBUTING.md
### How do I?
#### Change which backend to connect to?
Edit `Config.purs`. Find the function `endConfig'` just after the
imports and edit `back`. The definitions are not far below, just after
the definitions of the various `front` options.
Example (using `demo.gargantext.org` as backend):
```
endConfig' :: ApiVersion -> EndConfig
endConfig' v = { front : frontRelative
, back : backDemo v }
```
#### Add a javascript dependency?
Add it to `package.json`, under `dependencies` if it is needed at
......@@ -173,13 +218,13 @@ works. It's written in dhall, so you can use comments and such.
You will then need to rebuild the package set:
```shell
yarn rebuild-set
yarn rebuild-set # or darn rebuild-set
```
#### Upgrade the base package set local is based on to latest?
```shell
yarn rebase-set && yarn rebuild-set
yarn rebase-set && yarn rebuild-set # or darn rebase-set && darn rebuild-set
```
This will occasionally result in swearing when you go on to build.
......
......@@ -3,29 +3,16 @@
<head>
<meta charset="utf-8"/>
<title>CNRS GarganText</title>
<!-- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> -->
<!--<link href="https://use.fontawesome.com/releases/v5.0.8/styles/all.css" rel="stylesheet">-->
<link rel="stylesheet" href="icons/forkawesome.css">
<!--<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous">-->
<link href="styles/login.min.css" rel="stylesheet">
<link href="styles/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/context-menu.css"/>
<link rel="stylesheet" type="text/css" href="styles/menu.css"/>
<link rel="stylesheet" type="text/css" href="styles/highlightjs-solarized-light.css"/>
<link href="styles/Graph.css" rel="stylesheet" type="text/css" />
<link href="styles/Login.css" rel="stylesheet" type="text/css" />
<link href="styles/Tree.css" rel="stylesheet" type="text/css" />
<link href="styles/CodeEditor.css" rel="stylesheet" type="text/css" />
<link href="styles/Styles.css" rel="stylesheet" type="text/css" />
<link href="styles/range-slider.css" rel="stylesheet" type="text/css" />
<style>
* {margin: 0; padding: 0; list-style: none;}
</style>
<link href="styles/sass.css" rel="stylesheet" type="text/css" />
<style> * {margin: 0; padding: 0; list-style: none;} </style>
</head>
<body>
<div id="app" class ="container-fluid"></div>
<div id="portal"></div>
<script src="bundle.js"></script>
<script src="js/bootstrap-native.min.js"></script>
</body>
</html>
.code-editor-heading {
display: flex;
}
.code-editor-heading .renameable {
flex-grow: 2;
}
.code-editor-heading .renameable .text {
padding-right: 10px;
}
.code-editor-heading .buttons-right {
display: flex;
justify-content: flex-end;
}
.code-editor .toolbar {
display: flex;
justify-content: flex-start;
width: 100%;
}
.code-editor .editor {
display: flex;
width: 100%;
}
.code-editor .editor .code-area {
flex-grow: 1;
max-height: 200px;
min-width: 25%;
overflow: auto;
}
.code-editor .editor .code-area .code-container {
background-color: #fafafa;
box-sizing: border-box;
position: relative;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
font-size: 12px;
font-variant-ligatures: common-ligatures;
line-height: 1.5;
overflow: hidden;
padding: 0px;
text-align: left;
}
.code-editor .editor .code-area .code-container textarea {
border: 0px;
color: inherit;
position: absolute;
left: 0px;
top: 0px;
resize: none;
height: 100%;
overflow: hidden;
width: 100%;
-webkit-text-fill-color: transparent;
box-sizing: inherit;
display: inherit;
margin: 0px;
padding: 10px;
overflow-wrap: break-word;
white-space: pre-wrap;
word-break: keep-all;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-variant-ligatures: inherit;
font-weight: inherit;
letter-spacing: inherit;
line-height: inherit;
text-indent: inherit;
text-rendering: inherit;
text-transform: inherit;
}
.code-editor .editor .code-area .code-container pre {
background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%;
border: 0px none;
color: #000;
pointer-events: none;
position: relative;
box-sizing: inherit;
display: inherit;
margin: 0px;
padding: 10px;
overflow-wrap: break-word;
white-space: pre-wrap;
word-break: keep-all;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-variant-ligatures: inherit;
font-weight: inherit;
letter-spacing: inherit;
line-height: inherit;
text-indent: inherit;
text-rendering: inherit;
text-transform: inherit;
}
.code-editor .editor .v-divider {
border-left: 1px solid gray;
cursor: sw-resize;
height: 100%;
margin-left: 5px;
margin-right: 5px;
width: 1px;
}
.code-editor .editor .html {
flex-grow: 2;
margin-left: 25px;
padding-left: 25px;
}
.code-editor .editor .html.language-haskell {
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
white-space: pre;
}
.code-editor .editor .html.language-python {
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
white-space: pre;
}
.code-editor .editor .html.language-json {
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
white-space: pre;
}
.code-editor .editor .html.language-md ul li {
list-style: disc !important;
}
.code-editor .editor .html.language-md ol li {
list-style: decimal !important;
}
/*# sourceMappingURL=CodeEditor.css.map */
#graph-explorer #toolbar {
display: flex;
flex-direction: column;
}
#graph-explorer #toolbar ul {
display: flex;
flex-direction: row;
margin: 0;
}
#graph-explorer #toolbar ul li {
display: flex;
max-width: 200px;
}
#graph-explorer #toggle-container {
position: fixed;
z-index: 999;
right: 25%;
top: 10px;
width: 50%;
}
#graph-explorer #toggle-container .container-fluid {
padding-top: 90px;
}
#graph-explorer #controls-container {
position: fixed;
z-index: 999;
backdrop-filter: blur(4px);
background: rgba(255, 255, 255, 0.75);
overflow: auto;
left: 0;
right: 0;
top: 60px;
}
#graph-explorer .graph-tree {
position: absolute;
max-height: 600px;
top: 170px;
background-color: #fff;
z-index: 1;
}
#graph-explorer .lefthanded .graph-tree {
left: 80%;
}
#graph-explorer .righthanded .graph-tree {
left: 0%;
}
#graph-explorer #graph-view {
height: 95%;
}
#graph-explorer #sp-container {
position: absolute;
max-height: 600px;
top: 170px;
border: 1px white solid;
background-color: white;
width: 28%;
z-index: 15;
}
#graph-explorer #sp-container #myTab {
marginBottom: 18px;
marginTop: 18px;
}
#graph-explorer #sp-container #myTabContent {
borderBottom: 1px solid black;
paddingBottom: 19px;
}
#graph-explorer #sp-container #horizontal-checkbox ul {
display: inline;
float: left;
}
#graph-explorer .lefthanded #sp-container {
left: 0%;
}
#graph-explorer .righthanded #sp-container {
left: 70%;
}
#graph-explorer #tree {
position: absolute;
z-index: 1;
}
.input-with-autocomplete .completions {
position: fixed;
max-height: 300px;
overflow-y: scroll;
width: 300px;
top: 100px;
}
/*# sourceMappingURL=Graph.css.map */
#dafixedtop {
z-index: 999;
}
.logoSmall {
line-height: 15px;
height: 10px;
padding: 10px 10px;
}
#logo-designed {
border: 15px;
}
#logo-designed img {
height: 150px;
border: 3px solid white;
}
#page-wrapper {
margin-top: 96px;
}
#user-page-header {
border-bottom: 1px solid black;
}
#user-page-info {
margin-top: 38px;
}
.tableHeader {
color: white;
}
#toolbar {
display: inline;
}
#toolbar ul li {
display: inline;
margin-right: 19px;
}
#toolbar ul li form {
display: inline;
}
#horizontal-checkbox ul li {
display: inline;
float: left;
margin-top: 12px;
margin-right: 21px;
}
li#rename #rename-a {
display: none;
position: absolute;
left: 125px;
}
#node-popup-tooltip {
background-color: white;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
#node-popup-tooltip:hover {
border: none;
text-decoration: none;
}
#node-popup-tooltip .popup-container {
display: flex;
flex-direction: colum;
}
#node-popup-tooltip .popup-container .panel {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
margin-bottom: 0px;
}
#node-popup-tooltip .popup-container .panel .glyphicon-pencil {
color: black;
}
#node-popup-tooltip .popup-container .panel .panel-body {
display: flex;
justify-content: center;
background-color: white;
border: none;
}
#node-popup-tooltip .popup-container .panel .panel-body .spacer {
margin: 10px;
}
#node-popup-tooltip .popup-container .frame-search.panel {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
width: 1300px;
}
#create-node-tooltip {
position: absolute;
left: 96px;
top: -64px;
background-color: white;
z-index: 1000;
}
#create-node-tooltip .panel-body input {
min-width: 200px;
}
#file-type-tooltip {
position: absolute;
left: 96px;
top: -64px;
background-color: white;
z-index: 1000;
}
#file-type-tooltip .panel-body select {
min-width: 200px;
}
.glyphitem {
top: 0;
display: inline-block;
float: right;
opacity: 0.6;
padding-right: 5px;
cursor: pointer;
transition: transform 0.1s ease-out 0s;
font-size: 15px;
text-align: center;
}
.glyphitem:hover {
display: inline-block;
opacity: 1;
transform: scale(1.4);
}
#sp-container {
-webkit-transition: width 2s;
transition: width 2s;
}
.nooverflow {
max-width: 300px;
height: 24px;
overflow: hidden;
text-overflow: ellipsis;
}
.nooverflow:hover {
overflow: visible;
height: auto;
}
#graph-tree .tree {
margin-top: 27px;
}
.nopadding {
padding: 0 !important;
margin: 0 !important;
}
.row-no-padding > [class*=col-] {
padding-left: 0 !important;
padding-right: 0 !important;
}
.tab-pane .reload-btn {
padding-right: 6px;
}
.flex {
display: flex;
}
.flex-end {
display: flex;
justify-content: flex-end;
}
.flex-center {
display: flex;
flew-wrap: wrap;
justify-content: center;
}
.flex-space-between, .flex-center {
display: flex;
justify-content: space-between;
}
a:focus, a:hover {
cursor: pointer;
}
/*# sourceMappingURL=Login.css.map */
#page-wrapper .cache-toggle {
cursor: pointer;
}
.simple-layout {
height: 100%;
}
.simple-layout .spinner {
position: absolute;
left: 50%;
top: 50%;
}
/*# sourceMappingURL=Styles.css.map */
#page-wrapper
.cache-toggle
cursor: pointer
.simple-layout
height: 100%
.spinner
position: absolute
left: 50%
top: 50%
body > .tree ul > li:first-child::before {
top: 12px;
}
li .leaf {
display: flex;
flex-direction: row;
}
li .leaf .folder-icon {
padding: 0 2 0 2;
}
li .leaf a.settings {
cursor: pointer;
display: block;
padding: 0 2 0 2;
text-decoration: none;
visibility: hidden;
z-index: 1;
}
li .leaf:hover a.settings {
visibility: visible;
}
.tree {
margin-top: 20px;
}
.tree ul li {
position: relative;
}
.tree ul li::after {
content: " ";
height: 1px;
position: absolute;
top: 12px;
}
.tree ul li::before {
bottom: -12px;
content: " ";
height: 7px;
position: absolute;
top: 5px;
}
.tree ul li:not(:first-child):last-child::before {
display: none;
}
.tree ul li:only-child::before {
bottom: 7px;
content: " ";
display: list-item;
position: absolute;
width: 1px;
top: 5px;
}
.tree ul li.with-children::after {
background-color: #000;
}
.tree ul li.with-children::before {
background-color: #000;
}
.tree .lefthanded .leaf {
justify-content: flex-end;
}
.tree .lefthanded ul {
margin-right: 5px;
}
.tree .lefthanded ul li {
margin-right: 10px;
padding-right: 5px;
}
.tree .lefthanded ul li.with-children::after {
right: -10px;
width: 5px;
}
.tree .lefthanded ul li.with-children::before {
right: -10px;
width: 1px;
}
.tree .righthanded .leaf {
justify-content: flex-start;
}
.tree .righthanded ul {
margin-left: 5px;
}
.tree .righthanded ul li {
margin-left: 10px;
padding-left: 5px;
}
.tree .righthanded ul li.with-children::after {
left: -10px;
width: 5px;
}
.tree .righthanded ul li.with-children::before {
left: -10px;
width: 1px;
}
.tree .righthanded ul li.with-children:only::before {
background-color: #000;
right: 10px;
}
.tree .file-dropped {
background-color: #d8dfe5;
}
.tree .node-actions {
padding-left: 5px;
}
.tree .node-actions .update-button.enabled {
cursor: pointer;
}
.tree .node-actions .update-button.disabled {
cursor: wait;
}
.tree .node {
margin-top: 1px;
}
.tree .node.node-type-valid .text {
color: blue;
text-decoration: underline;
}
.progress-pie {
background: rgba(51, 122, 183, 0.1);
border-radius: 100%;
height: calc(var(--size, 14) * 1px);
overflow: hidden;
position: relative;
width: calc(var(--size, 14) * 1px);
}
.progress-pie .progress-pie-segment {
--a: calc(var(--over50, 0) * -100%);
--b: calc((1 + var(--over50, 0)) * 100%);
--degrees: calc((var(--offset, 0) / 100) * 360);
-webkit-clip-path: polygon(var(--a) var(--a), var(--b) var(--a), var(--b) var(--b), var(--a) var(--b));
clip-path: polygon(var(--a) var(--a), var(--b) var(--a), var(--b) var(--b), var(--a) var(--b));
height: 100%;
position: absolute;
transform: translate(0, -50%) rotate(90deg) rotate(calc(var(--degrees) * 1deg));
transform-origin: 50% 100%;
width: 100%;
z-index: calc(1 + var(--over50));
}
.progress-pie .progress-pie-segment:after, .progress-pie .progress-pie-segment:before {
background: var(--bg, #337ab7);
content: "";
height: 100%;
position: absolute;
width: 100%;
}
.progress-pie .progress-pie-segment:before {
--degrees: calc((var(--value, 45) / 100) * 360);
transform: translate(0, 100%) rotate(calc(var(--degrees) * 1deg));
transform-origin: 50% 0%;
}
.progress-pie .progress-pie-segment:after {
opacity: var(--over50, 0);
}
#node-popup-tooltip .tree .node {
margin-top: 5px;
}
#node-popup-tooltip .tree .children .node {
padding-left: 15px;
}
/*# sourceMappingURL=Tree.css.map */
.context-menu {
position : absolute;
left : 96px;
top:-64px;
background-color: white;
z-index: 1000;
}
.context-menu a.list-group-item {
cursor: pointer;
}
.search-bar-container {
margin-top: 11px;
text-align: center;
max-width: 800px;
height: 33px;
float: left;
display: grid;
grid-template-columns: auto auto auto;
}
.search-bar-toggle {
padding: 2px;
margin: 2px;
border: 0;
}
.search-bar.closed {
display: none;
}
/* styles for menu.html template (navbar etc) */
#dafixedtop .navbar-text, #graphsfixedtop .navbar-text {
margin: 0 ;
padding-top: 15px;
padding-bottom: 15px;
float: none;
}
#corporatop.nav-tabs > li {
padding-left:1;
padding-right:1;
}
#corporatop.nav-tabs > li > a {
padding-top: 8;
padding-bottom: 8;
line-height: .85;
margin-bottom: -5px;
}
.spacing-class { margin-right : 10px;}
.exportbtn {
/* border: 1px solid #333 ; */
margin-top:17px ; /* valigns with bootstrap h2 */
}
.btn .glyphicon {
/* glyphicons are always rendered too high within bootstrap buttons */
vertical-align:middle
}
/* graph name => editable input => submitName() => minimsg */
.editable {
color: grey ;
}
#graphname-edit {
color: white;
background-color: transparent;
border: none;
max-width: 8em;
}
.minimsg {
font-size: .7em ;
padding: 7p0x 9px;
}
.minimsg * {
line-height: 100%;
}
{"version":3,"sourceRoot":"","sources":["../../src/sass/range-slider.sass"],"names":[],"mappings":"AAAA;EACE;AACA;EACA;;AAEA;EACE;;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","file":"range-slider.css"}
\ No newline at end of file
This diff is collapsed.
{"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/_code_editor.sass","../../src/sass/_styles.sass","../../src/sass/_range_slider.sass"],"names":[],"mappings":"AAAA;AAEA;EACI;EACA;EACA;EACA;;;AAEJ;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;;;AC7CF;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;ACjBA;EACE;EACA;;AAEA;EACE;EACA;EACA;;AACA;EACE;EACA;;AAEN;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF;EAtCA;EACA;EAEA;EAqCE;EACA;;AACF;EACE;;AACF;EACE;;AAEF;EACE;;AAEF;EAlDA;EACA;EAEA;EAiDE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA;;AAGA;EACE;EACA;;AACN;EACE;;AACF;EACE;;AAEF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ACrFJ;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;AACA;EACE;EACA;;;AAEJ;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;AAEE;EACE;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAGJ;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACA;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;EACA;;AACA;EACE;;AACN;EACE;EACA;EACA;EACA;;;AAGN;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;;;AAEJ;EACI;EACA;;;AAGF;EACE;;;AAEJ;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AC1KF;EACE;;;AAGA;EACE;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAER;EACE;;AAEE;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;EACA;;;AAGR;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;;;AAIF;EACE;;AAEA;EACE;;;AC/HR;EACE;;AAEA;EACE;;AACA;EACE;;AACJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AACF;EACE;EACA;;AACA;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;EArDR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AA2DM;EACE;EACA;EACA;EACA;EACA;EA7DR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAmEE;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAGE;EACE;;AAEF;EACE;;;ACtGV;EACE;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAGE;EACE;EACA;;AAEF;EACE;EACA;;;AAEV;EACE;;AACA;EACE;EACA;EACA;;;AAKE;EACE;EACA;;AACF;EACE;;AACA;EACE;EACA;;AACJ;EACE;;AACA;EACE;;AACJ;EACE;;;AAER;EACE;;;AChDF;EACE;AACA;EACA;;AAEA;EACE;;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","file":"sass.css"}
\ No newline at end of file
version: "3.5"
services:
frontend:
build:
context: .
dockerfile: "Dockerfile.dev"
stdin_open: true
tty: true
user: $UID:$GID
ports:
- 5000:5000
volumes:
- .:/opt/app
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
- /etc/shadow:/etc/shadow:ro
\ No newline at end of file
export UID=$(id -u)
export GID=$(id -g)
alias darn="docker-compose run frontend yarn"
<!doctype>
<html>
<head>
<meta charset="utf-8"/>
<title>CNRS GarganText</title>
<link rel="stylesheet" href="dist/icons/forkawesome.css">
<link href="dist/styles/login.min.css" rel="stylesheet">
<link href="dist/styles/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="dist/styles/highlightjs-solarized-light.css"/>
<link href="dist/styles/sass.css" rel="stylesheet" type="text/css" />
<style> * {margin: 0; padding: 0; list-style: none;} </style>
</head>
<body>
<div id="app" class ="container-fluid"></div>
<div id="portal"></div>
<script src="app.js"></script>
</body>
</html>
{
"name": "Gargantext",
"version": "0.0.1.91.7",
"version": "0.0.2.2.1",
"scripts": {
"rebase-set": "spago package-set-upgrade && spago psc-package-insdhall",
"rebuild-set": "spago psc-package-insdhall",
"install-ps": "psc-package install",
"compile": "pulp --psc-package build",
"build": "pulp --psc-package browserify --no-check-main -t dist/bundle.js",
"sass": "sass dist/styles/",
"compile": "pulp build",
"build": "pulp browserify -t dist/bundle.js",
"css": "sass src/sass/sass.sass:dist/styles/sass.css",
"docs": "pulp docs -- --format html",
"repl": "pulp --psc-package repl",
"repl": "pulp repl",
"clean": "rm -Rf output node_modules",
"clean-js": "rm -Rf node_modules",
"clean-ps": "rm -Rf output",
"test": "pulp test --no-check-main",
"server": "serve dist"
"test": "pulp test",
"server": "serve dist",
"prod": "yarn prod:compile && yarn prod:dce && yarn prod:bundle && yarn prod:pack",
"prod:compile": "pulp build -- -g corefn",
"prod:dce": "zephyr -f Main.main",
"prod:bundle": "pulp browserify --skip-compile -o dce-output -t app.js",
"prod:pack": "parcel build index.html -d prod --public-url . --no-source-maps"
},
"dependencies": {
"aes-js": "^3.1.1",
......@@ -32,8 +37,11 @@
"sigma": "git://github.com/poorscript/sigma.js#garg"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/preset-react": "^7.12.7",
"parcel": "^1.12.4",
"psc-package": "^4.0.1",
"pulp": "^14.0.0",
"pulp": "^15.0.0",
"purescript": "^0.13.8",
"purescript-language-server": "^0.12.9",
"react-testing-library": "^6.1.2",
......
../dist/images
\ No newline at end of file
15747
\ No newline at end of file
......@@ -5,9 +5,11 @@ import Data.Argonaut.Parser (jsonParser)
import Data.Array as A
import Data.Either (Either(..))
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Maybe (Maybe(..), maybe, fromMaybe)
import Data.Tuple (snd)
import DOM.Simple.Console (log2)
import Effect (Effect)
import Reactix as R
import Web.Storage.Storage as WSS
import Gargantext.Prelude
......@@ -19,7 +21,8 @@ import Gargantext.Utils.Reactix as R2
localStorageKey :: String
localStorageKey = "garg-async-tasks"
type Storage = Map.Map Int (Array GT.AsyncTaskWithType)
type Storage = Map.Map GT.NodeID (Array GT.AsyncTaskWithType)
empty :: Storage
empty = Map.empty
......@@ -37,6 +40,45 @@ getAsyncTasks = R2.getls >>= WSS.getItem localStorageKey >>= handleMaybe
parse s = GU.mapLeft (log2 "Error parsing serialised sessions:") (jsonParser s)
decode j = GU.mapLeft (log2 "Error decoding serialised sessions:") (decodeJson j)
getTasks :: Record ReductorProps -> GT.NodeID -> Array GT.AsyncTaskWithType
getTasks { storage } nodeId = fromMaybe [] $ Map.lookup nodeId storage
removeTaskFromList :: Array GT.AsyncTaskWithType -> GT.AsyncTaskWithType -> Array GT.AsyncTaskWithType
removeTaskFromList ts (GT.AsyncTaskWithType { task: GT.AsyncTask { id: id' } }) =
A.filter (\(GT.AsyncTaskWithType { task: GT.AsyncTask { id: id'' } }) -> id' /= id'') ts
type ReductorProps = (
appReload :: GT.ReloadS
, treeReload :: GT.ReloadS
, storage :: Storage
)
type Reductor = R2.Reductor (Record ReductorProps) Action
type ReductorAction = Action -> Effect Unit
type OnFinish = Effect Unit
useTasks :: GT.ReloadS -> GT.ReloadS -> R.Hooks Reductor
useTasks appReload treeReload = R2.useReductor act initializer unit
where
act :: R2.Actor (Record ReductorProps) Action
act a s = action s a
initializer _ = do
storage <- getAsyncTasks
pure { appReload, treeReload, storage }
data Action =
Insert GT.NodeID GT.AsyncTaskWithType
| Finish GT.NodeID GT.AsyncTaskWithType
| Remove GT.NodeID GT.AsyncTaskWithType
action :: Record ReductorProps -> Action -> Effect (Record ReductorProps)
action p@{ treeReload, storage } (Insert nodeId t) = do
_ <- snd treeReload $ (_ + 1)
let newStorage = Map.alter (maybe (Just [t]) (\ts -> Just $ A.cons t ts)) nodeId storage
pure $ p { storage = newStorage }
action p (Finish nodeId t) = do
action p (Remove nodeId t)
action p@{ appReload, storage } (Remove nodeId t) = do
_ <- snd appReload $ (_ + 1)
let newStorage = Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts t)) nodeId storage
pure $ p { storage = newStorage }
......@@ -11,22 +11,23 @@
-- | 2. We will need a more ambitious search algorithm for skipgrams.
module Gargantext.Components.Annotation.AnnotatedField where
import Prelude
import Data.List ( List(..), (:) )
import Data.Maybe ( Maybe(..), maybe )
import Data.Tuple ( Tuple(..) )
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ( (/\) )
import DOM.Simple.Console (log2)
--import DOM.Simple.Console (log2)
import DOM.Simple.Event as DE
import Effect ( Effect )
import Reactix as R
import Reactix.DOM.HTML as HTML
import Reactix.SyntheticEvent as E
import Gargantext.Prelude
import Gargantext.Types (CTabNgramType(..), TermList)
import Gargantext.Components.Annotation.Utils ( termBootstrapClass )
import Gargantext.Components.NgramsTable.Core (NgramsTable, NgramsTerm, findNgramTermList, highlightNgrams, normNgram)
import Gargantext.Components.Annotation.Menu ( AnnotationMenu, annotationMenu, MenuType(..) )
import Gargantext.Components.Annotation.Menu ( annotationMenu, MenuType(..) )
import Gargantext.Utils.Selection as Sel
thisModule :: String
......@@ -49,107 +50,63 @@ annotatedField p = R.createElement annotatedFieldComponent p []
annotatedFieldComponent :: R.Component Props
annotatedFieldComponent = R.hooksComponentWithModule thisModule "annotatedField" cpt
where
cpt {ngrams,setTermList,text} _ = do
mMenu@(_ /\ setMenu) <- R.useState' Nothing
cpt {ngrams,setTermList,text: fieldText} _ = do
(_ /\ setRedrawMenu) <- R.useState' false
menuRef <- R.useRef Nothing
let wrapperProps = { className: "annotated-field-wrapper" }
onSelect :: Maybe (Tuple NgramsTerm TermList) -> MouseEvent -> Effect Unit
onSelect Nothing event = do
maybeShowMenu setMenu menuRef setTermList ngrams event
onSelect (Just (Tuple ngram list)) event = do
redrawMenu = setRedrawMenu not
hideMenu = do
R.setRef menuRef Nothing
redrawMenu
showMenu { event, getList, menuType, ngram } = do
let x = E.clientX event
y = E.clientY event
-- n = normNgram CTabTerms text
list = getList ngram
setList t = do
R.setRef menuRef Nothing
setTermList ngram (Just list) t
--setMenu (const Nothing)
menu = Just {
x
setTermList ngram list t
hideMenu
E.preventDefault event
--range <- Sel.getRange sel 0
--log2 "[showMenu] selection range" $ Sel.rangeToTuple range
let menu = Just
{ x
, y
, list: Just list
, menuType: SetTermListItem
, onClose: \_ -> R.setRef menuRef Nothing
, list
, menuType
, onClose: hideMenu
, setList
}
R.setRef menuRef menu
setMenu $ const menu
mapCompile (Tuple t l) = {text: t, list: l, onSelect}
compiled = map mapCompile $ compile ngrams text
runs =
HTML.div { className: "annotated-field-runs" } $ map annotateRun compiled
--pure $ HTML.div wrapperProps [maybeAddMenu mMenu runs]
pure $ HTML.div wrapperProps [ addMenu { menuRef }, runs ]
type AddMenuProps = (
menuRef :: R.Ref (Maybe AnnotationMenu)
)
addMenu :: Record AddMenuProps -> R.Element
addMenu p = R.createElement addMenuCpt p []
addMenuCpt :: R.Component AddMenuProps
addMenuCpt = R.hooksComponentWithModule thisModule "addMenu" cpt
where
cpt { menuRef } _ = do
(mMenu /\ setmMenu) <- R.useState' (Nothing :: Maybe AnnotationMenu)
redrawMenu
R.useEffect' $ do
let m = R.readRef menuRef
--log2 "[addMenu] menuRef" m
--log2 "[addMenu] mMenu" mMenu
setmMenu $ const m
pure $ case mMenu of
Nothing -> HTML.div {} []
Just menu -> annotationMenu setmMenu menu
-- forall e. IsMouseEvent e => R.Setter (Maybe AnnotationMenu) -> R.Setter ? -> ? -> e -> Effect Unit
maybeShowMenu setMenu menuRef setTermList ngrams event = do
onSelect :: Maybe (Tuple NgramsTerm TermList) -> MouseEvent -> Effect Unit
onSelect (Just (Tuple ngram list)) event =
showMenu { event, getList: const (Just list), menuType: SetTermListItem, ngram }
onSelect Nothing event = do
s <- Sel.getSelection
--log2 "[maybeShowMenu] s" s
case s of
Just sel -> do
case Sel.selectionToString sel of
"" -> pure unit
"" -> hideMenu
sel' -> do
let x = E.clientX event
y = E.clientY event
n = normNgram CTabTerms sel'
list = findNgramTermList ngrams n
setList t = do
setTermList n list t
R.setRef menuRef Nothing
--setMenu (const Nothing)
E.preventDefault event
range <- Sel.getRange sel 0
--log2 "[maybeShowMenu] selection range" $ Sel.rangeToTuple range
let menu = Just {
x
, y
, list
, menuType: NewNgram
, onClose: \_ -> R.setRef menuRef Nothing
, setList
}
R.setRef menuRef menu
setMenu $ const $ menu
Nothing -> pure unit
-- Nothing -> do
-- R.setRef menuRef Nothing
maybeAddMenu
:: R.State (Maybe AnnotationMenu)
-> R.Element
-> R.Element
maybeAddMenu (Just props /\ setMenu) e = annotationMenu setMenu props <> e
maybeAddMenu _ e = e
showMenu { event, getList: findNgramTermList ngrams, menuType: NewNgram, ngram: normNgram CTabTerms sel' }
Nothing -> hideMenu
wrap (text /\ list) = {text, list, onSelect}
pure $ HTML.div wrapperProps
[ maybe (HTML.div {} []) annotationMenu $ R.readRef menuRef
, HTML.div { className: "annotated-field-runs" }
$ annotateRun
<$> wrap
<$> compile ngrams fieldText
]
compile :: NgramsTable -> Maybe String -> Array (Tuple String (List (Tuple NgramsTerm TermList)))
compile ngrams = maybe [] (highlightNgrams CTabTerms ngrams)
......@@ -178,4 +135,3 @@ annotatedRunComponent = R.staticComponent "AnnotatedRun" cpt
where
className = "annotation-run bg-" <> termBootstrapClass list
......@@ -30,16 +30,16 @@ type Props =
type AnnotationMenu = {
x :: Number
, y :: Number
, onClose :: Unit -> Effect Unit
, onClose :: Effect Unit
| Props
}
-- | An Annotation Menu is parameterised by a Maybe Termlist of the
-- | TermList the currently selected text belongs to
annotationMenu :: R.Setter (Maybe AnnotationMenu) -> AnnotationMenu -> R.Element
annotationMenu setMenu { x,y,list,menuType, onClose,setList } =
CM.contextMenu { x,y, onClose, setMenu } [
R.createElement annotationMenuCpt {list,menuType,setList} []
annotationMenu :: AnnotationMenu -> R.Element
annotationMenu {x, y, list, menuType, onClose, setList} =
CM.contextMenu {x, y, onClose} [
R.createElement annotationMenuCpt {list, menuType, setList} []
]
annotationMenuCpt :: R.Component Props
......
This diff is collapsed.
......@@ -25,72 +25,53 @@ import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
import Reactix as R
import Reactix.DOM.HTML as H
------------------------------------------------------------------------
import Gargantext.Prelude
import Gargantext.Components.Category.Types
import Gargantext.Components.DocsTable.Types (DocumentsView(..), LocalCategories)
import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.Loader (useLoaderWithCacheAPI, HashedResponse(..))
import Gargantext.Utils.Reactix as R2
import Gargantext.Routes as Routes
import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Sessions (Session, sessionId, get, delete, put)
import Gargantext.Types (NodeType(..), OrderBy(..), TableResult, TabType, showTabType')
import Gargantext.Types (NodeID, NodeType(..), OrderBy(..), TableResult, TabType, showTabType')
import Gargantext.Utils.CacheAPI as GUC
thisModule :: String
thisModule = "Gargantext.Components.Category"
------------------------------------------------------------------------
data Category = Trash | UnRead | Checked | Topic | Favorite
categories :: Array Category
categories = [Trash, UnRead, Checked, Topic, Favorite]
derive instance genericFavorite :: Generic Category _
instance showCategory :: Show Category where
show = genericShow
instance eqCategory :: Eq Category where
eq = genericEq
instance decodeJsonCategory :: DecodeJson Category where
decodeJson json = do
obj <- decodeJson json
pure $ decodeCategory obj
instance encodeJsonCategory :: EncodeJson Category where
encodeJson cat = encodeJson (cat2score cat)
favCategory :: Category -> Category
favCategory Favorite = Topic
favCategory _ = Favorite
trashCategory :: Category -> Category
trashCategory _ = Trash
-- TODO: ?
--trashCategory Trash = UnRead
decodeCategory :: Int -> Category
decodeCategory 0 = Trash
decodeCategory 1 = UnRead
decodeCategory 2 = Checked
decodeCategory 3 = Topic
decodeCategory 4 = Favorite
decodeCategory _ = UnRead
cat2score :: Category -> Int
cat2score Trash = 0
cat2score UnRead = 1
cat2score Checked = 2
cat2score Topic = 3
cat2score Favorite = 4
type CarousselProps = (
category :: Category
, nodeId :: NodeID
, row :: DocumentsView
, session :: Session
, setLocalCategories :: R.Setter LocalCategories
)
-- caroussel :: Category -> R.Element
caroussel session nodeId setLocalCategories r cat = H.div {className:"flex"} divs
caroussel :: R2.Component CarousselProps
caroussel = R.createElement carousselCpt
carousselCpt :: R.Component CarousselProps
carousselCpt = R.hooksComponentWithModule thisModule "caroussel" cpt
where
cpt { category, nodeId, row: DocumentsView r, session, setLocalCategories } _ = do
pure $ H.div {className:"flex"} divs
where
divs = map (\c -> if cat == c
divs = map (\c -> if category == c
then
H.div { className : icon c (cat == c) } []
H.div { className : icon c (category == c) } []
else
H.div { className : icon c (cat == c)
H.div { className : icon c (category == c)
, on: { click: onClick c}
} []
) (caroussel' cat)
) (caroussel' category)
caroussel' :: Category -> Array Category
caroussel' Trash = A.take 2 categories
......
module Gargantext.Components.Category.Types where
import Data.Argonaut (class DecodeJson, class EncodeJson, decodeJson, encodeJson)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Eq (genericEq)
import Data.Generic.Rep.Show (genericShow)
import Gargantext.Prelude
------------------------------------------------------------------------
data Category = Trash | UnRead | Checked | Topic | Favorite
categories :: Array Category
categories = [Trash, UnRead, Checked, Topic, Favorite]
derive instance genericFavorite :: Generic Category _
instance showCategory :: Show Category where
show = genericShow
instance eqCategory :: Eq Category where
eq = genericEq
instance decodeJsonCategory :: DecodeJson Category where
decodeJson json = do
obj <- decodeJson json
pure $ decodeCategory obj
instance encodeJsonCategory :: EncodeJson Category where
encodeJson cat = encodeJson (cat2score cat)
favCategory :: Category -> Category
favCategory Favorite = Topic
favCategory _ = Favorite
trashCategory :: Category -> Category
trashCategory _ = Trash
-- TODO: ?
--trashCategory Trash = UnRead
decodeCategory :: Int -> Category
decodeCategory 0 = Trash
decodeCategory 1 = UnRead
decodeCategory 2 = Checked
decodeCategory 3 = Topic
decodeCategory 4 = Favorite
decodeCategory _ = UnRead
cat2score :: Category -> Int
cat2score Trash = 0
cat2score UnRead = 1
cat2score Checked = 2
cat2score Topic = 3
cat2score Favorite = 4
......@@ -21,22 +21,22 @@ import Reactix.DOM.HTML as HTML
import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.ContextMenu.ContextMenu"
type Props t = (
x :: Number
, y :: Number
, onClose :: Unit -> Effect Unit
, setMenu :: R.Setter (Maybe t)
, onClose :: Effect Unit
)
contextMenu :: forall t. Record (Props t) -> Array R.Element -> R.Element
contextMenu :: forall t. R2.Component (Props t)
contextMenu = R.createElement contextMenuCpt
contextMenuCpt :: forall t. R.Component (Props t)
contextMenuCpt = R.hooksComponentWithModule thisModule "contextMenu" cpt
where
cpt menu@{ x, y, onClose, setMenu } children = do
cpt menu@{ x, y, onClose } children = do
host <- R2.getPortalHost
root <- R.useRef null
rect /\ setRect <- R.useState $ \_ -> Nothing
......@@ -45,7 +45,7 @@ contextMenuCpt = R.hooksComponentWithModule thisModule "contextMenu" cpt
(\r -> setRect (\_ -> Just (Element.boundingRect r)))
(toMaybe $ R.readRef root)
pure $ pure unit
R.useLayoutEffect2 root rect (contextMenuEffect onClose setMenu root)
R.useLayoutEffect2 root rect (contextMenuEffect onClose root)
let cs = [
HTML.div { className: "popover-content" }
[ HTML.div { className: "panel panel-default" }
......@@ -57,27 +57,28 @@ contextMenuCpt = R.hooksComponentWithModule thisModule "contextMenu" cpt
pure $ R.createPortal [ elems root menu rect $ cs ] host
elems ref menu (Just rect) = HTML.div
{ ref
, key: "context-menu"
, className: "context-menu"
, style: position menu rect
, data: {toggle: "popover", placement: "right"}
}
elems ref _ _ = HTML.div
{ ref
, key: "context-menu"
, className: "context-menu"
, data: {toggle: "popover", placement: "right"}
}
contextMenuEffect
:: forall t.
(Unit -> Effect Unit)
-> R.Setter (Maybe t)
Effect Unit
-> R.Ref (Nullable DOM.Element)
-> Effect (Effect Unit)
contextMenuEffect onClose setMenu rootRef =
contextMenuEffect onClose rootRef =
case R.readNullableRef rootRef of
Just root -> do
let onClick = documentClickHandler onClose setMenu root
let onScroll = documentScrollHandler setMenu
let onClick = documentClickHandler onClose root
let onScroll = documentScrollHandler onClose
DOM.addEventListener document "click" onClick
DOM.addEventListener document "scroll" onScroll
pure $ do
......@@ -85,18 +86,14 @@ contextMenuEffect onClose setMenu rootRef =
DOM.removeEventListener document "scroll" onScroll
Nothing -> pure R.nothing
documentClickHandler :: forall t. (Unit -> Effect Unit) -> R.Setter (Maybe t) -> DOM.Element -> Callback DE.MouseEvent
documentClickHandler onClose hide menu =
documentClickHandler :: Effect Unit -> DOM.Element -> Callback DE.MouseEvent
documentClickHandler onClose menu =
R2.named "hideMenuOnClickOutside" $ callback $ \e ->
if Element.contains menu (DE.target e)
then pure unit
else do
hide (const Nothing)
onClose unit
documentScrollHandler :: forall t. R.Setter (Maybe t) -> Callback DE.MouseEvent
documentScrollHandler hide =
R2.named "hideMenuOnScroll" $ callback $ \e -> hide (const Nothing)
when (Element.contains menu (DE.target e)) onClose
documentScrollHandler :: Effect Unit -> Callback DE.MouseEvent
documentScrollHandler onClose =
R2.named "hideMenuOnScroll" $ callback $ \e -> onClose
position :: forall t. Record (Props t) -> DOMRect -> { left :: Number, top :: Number }
position mouse {width: menuWidth, height: menuHeight} = {left, top}
......
This diff is collapsed.
module Gargantext.Components.DocsTable.Types where
import Data.Argonaut (class DecodeJson, class EncodeJson, decodeJson, jsonEmptyObject, (.:), (:=), (~>))
import Data.Map (Map)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Tuple (Tuple(..))
import Gargantext.Prelude
import Gargantext.Components.Category.Types (Category(..), decodeCategory)
data Action
= MarkCategory Int Category
newtype DocumentsView
= DocumentsView
{ _id :: Int
, category :: Category
, date :: Int
, ngramCount :: Maybe Int
, score :: Maybe Int
, source :: String
, title :: String
, url :: String
}
{-
derive instance genericDocumentsView :: Generic DocumentsView _
instance showDocumentsView :: Show DocumentsView where
show = genericShow
instance decodeJsonSearchType :: Argonaut.DecodeJson SearchType where
decodeJson = genericSumDecodeJson
instance encodeJsonSearchType :: Argonaut.EncodeJson SearchType where
encodeJson = genericSumEncodeJson
-}
instance decodeDocumentsView :: DecodeJson DocumentsView where
decodeJson json = do
obj <- decodeJson json
_id <- obj .: "id"
category <- obj .: "category"
date <- obj .: "date"
ngramCount <- obj .: "ngramCount"
score <- obj .: "score"
source <- obj .: "source"
title <- obj .: "title"
url <- obj .: "url"
pure $ DocumentsView { _id, category, date, ngramCount, score, source, title, url }
instance encodeDocumentsView :: EncodeJson DocumentsView where
encodeJson (DocumentsView dv) =
"id" := dv._id
~> "category" := dv.category
~> "date" := dv.date
~> "ngramCount" := dv.ngramCount
~> "score" := dv.score
~> "source" := dv.source
~> "title" := dv.title
~> "url" := dv.url
~> jsonEmptyObject
newtype Response = Response
{ cid :: Int
, hyperdata :: Hyperdata
, category :: Category
, ngramCount :: Maybe Int
, score :: Maybe Int
, title :: String
}
newtype Hyperdata = Hyperdata
{ title :: String
, source :: String
, pub_year :: Int
}
instance decodeHyperdata :: DecodeJson Hyperdata where
decodeJson json = do
obj <- decodeJson json
pub_year <- obj .: "publication_year"
source <- obj .: "source"
title <- obj .: "title"
pure $ Hyperdata { title,source, pub_year}
instance decodeResponse :: DecodeJson Response where
decodeJson json = do
obj <- decodeJson json
category <- obj .: "category"
cid <- obj .: "id"
hyperdata <- obj .: "hyperdata"
ngramCount <- obj .: "ngramCount"
score <- obj .: "score"
title <- obj .: "title"
pure $ Response { category: decodeCategory category, cid, hyperdata, ngramCount, score, title }
type LocalCategories = Map Int Category
type Query = String
---------------------------------------------------------
sampleData' :: DocumentsView
sampleData' = DocumentsView { _id : 1
, url : ""
, date : 2010
, title : "title"
, source : "source"
, category : UnRead
, ngramCount : Just 1
, score: Just 1 }
sampleData :: Array DocumentsView
--sampleData = replicate 10 sampleData'
sampleData = map (\(Tuple t s) -> DocumentsView { _id : 1
, url : ""
, date : 2017
, title: t
, source: s
, category : UnRead
, ngramCount : Just 10
, score: Just 1 }) sampleDocuments
sampleDocuments :: Array (Tuple String String)
sampleDocuments = [Tuple "Macroscopic dynamics of the fusion process" "Journal de Physique Lettres",Tuple "Effects of static and cyclic fatigue at high temperature upon reaction bonded silicon nitride" "Journal de Physique Colloques",Tuple "Reliability of metal/glass-ceramic junctions made by solid state bonding" "Journal de Physique Colloques",Tuple "High temperature mechanical properties and intergranular structure of sialons" "Journal de Physique Colloques",Tuple "SOLUTIONS OF THE LANDAU-VLASOV EQUATION IN NUCLEAR PHYSICS" "Journal de Physique Colloques",Tuple "A STUDY ON THE FUSION REACTION 139La + 12C AT 50 MeV/u WITH THE VUU EQUATION" "Journal de Physique Colloques",Tuple "Atomic structure of \"vitreous\" interfacial films in sialon" "Journal de Physique Colloques",Tuple "MICROSTRUCTURAL AND ANALYTICAL CHARACTERIZATION OF Al2O3/Al-Mg COMPOSITE INTERFACES" "Journal de Physique Colloques",Tuple "Development of oxidation resistant high temperature NbTiAl alloys and intermetallics" "Journal de Physique IV Colloque",Tuple "Determination of brazed joint constitutive law by inverse method" "Journal de Physique IV Colloque",Tuple "Two dimensional estimates from ocean SAR images" "Nonlinear Processes in Geophysics",Tuple "Comparison Between New Carbon Nanostructures Produced by Plasma with Industrial Carbon Black Grades" "Journal de Physique III",Tuple "<i>Letter to the Editor:</i> SCIPION, a new flexible ionospheric sounder in Senegal" "Annales Geophysicae",Tuple "Is reducibility in nuclear multifragmentation related to thermal scaling?" "Physics Letters B",Tuple "Independence of fragment charge distributions of the size of heavy multifragmenting sources" "Physics Letters B",Tuple "Hard photons and neutral pions as probes of hot and dense nuclear matter" "Nuclear Physics A",Tuple "Surveying the nuclear caloric curve" "Physics Letters B",Tuple "A hot expanding source in 50 A MeV Xe+Sn central reactions" "Physics Letters B"]
......@@ -22,7 +22,8 @@ import Prelude
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Components.Category (Category(..), CategoryQuery(..), favCategory, decodeCategory, putCategories)
import Gargantext.Components.Category (CategoryQuery(..), putCategories)
import Gargantext.Components.Category.Types (Category(..), decodeCategory, favCategory)
import Gargantext.Components.Search
import Gargantext.Components.Table as T
import Gargantext.Ends (url, Frontends)
......@@ -50,7 +51,9 @@ type Props =
)
-- | Tracks the ids of documents to delete and that have been deleted
type Deletions = { pending :: Set Int, deleted :: Set Int }
type Deletions = { pending :: Set Int
, deleted :: Set Int
}
initialDeletions :: Deletions
initialDeletions = { pending: mempty, deleted: mempty }
......@@ -108,7 +111,7 @@ docViewCpt = R.hooksComponentWithModule thisModule "docView" cpt
else
snd path $ const ipp
pure $ H.div { className: "container1" }
pure $ H.div { className: "facets-doc-view container1" }
[ R2.row
[ chart
, H.div { className: "col-md-12" }
......@@ -330,7 +333,10 @@ pageCpt = R.hooksComponentWithModule thisModule "page" cpt
, maybeStricken delete [ H.text source ]
, maybeStricken delete [ H.text authors ]
-- , maybeStricken $ intercalate [comma] (pairUrl <$> pairs)
, H.input { type: "checkbox", checked: isChecked id, on: { click: toggleClick } }
, H.input { defaultChecked: isChecked id
, on: { click: toggleClick }
, type: "checkbox"
}
]
, delete: true }
where
......
module Gargantext.Components.Footer where
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Prelude
import Gargantext.Sessions as Sessions
thisModule :: String
thisModule = "Gargantext.Components.Footer"
---------------------------------------------------------------------------
type FooterProps =
(
session :: Sessions.Session
)
footer :: Record FooterProps -> R.Element
footer props = R.createElement footerCpt props []
footerCpt :: R.Component FooterProps
footerCpt = R.hooksComponentWithModule thisModule "footer" cpt
where
cpt { session } _ = do
pure $ H.div
{ className: "container" }
[ H.hr {}
, H.footer {} []
]
module Gargantext.Components.Forest where
import DOM.Simple.Console (log)
import Data.Maybe (Maybe(..))
import Data.Array as A
import Data.Tuple (fst)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Set as Set
import Data.Tuple (fst, snd)
import Data.Tuple.Nested ((/\))
import DOM.Simple.Console (log, log2)
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree (treeView)
import Gargantext.Components.TopBar (topBar)
import Gargantext.Ends (Frontends, Backend(..))
import Gargantext.Prelude
import Gargantext.Routes (AppRoute)
import Gargantext.Sessions (Session(..), Sessions, OpenNodes, unSessions)
import Gargantext.Types (Reload, Handed(..))
import Gargantext.Types (Reload, ReloadS, Handed(..))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
thisModule :: String
thisModule = "Gargantext.Components.Forest"
type Props =
( frontends :: Frontends
type Props = (
appReload :: ReloadS
, asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
, backend :: R.State (Maybe Backend)
, frontends :: Frontends
, handed :: Handed
, reload :: R.State Int
, route :: AppRoute
, sessions :: Sessions
, showLogin :: R.Setter Boolean
, backend :: R.State (Maybe Backend)
, treeReloadRef :: R.Ref (Maybe ReloadS)
)
forest :: Record Props -> R.Element
forest props = R.createElement forestCpt props []
forest :: R2.Component Props
forest = R.createElement forestCpt
forestCpt :: R.Component Props
forestCpt = R.hooksComponentWithModule thisModule "forest" cpt where
cpt { frontends, handed, reload: extReload, route, sessions, showLogin, backend} _ = do
cpt { appReload
, asyncTasksRef
, backend
, frontends
, handed
, route
, sessions
, showLogin
, treeReloadRef } _ = do
-- NOTE: this is a hack to reload the tree view on demand
reload <- R.useState' (0 :: Reload)
asyncTasks <- GAT.useTasks appReload reload
openNodes <- R2.useLocalStorageState R2.openNodesKey (Set.empty :: OpenNodes)
asyncTasks <- R2.useLocalStorageState GAT.localStorageKey GAT.empty
R2.useCache
( frontends
-- TODO If `treeReloadRef` is set, `reload` state should be updated
R.useEffect' $ do
R.setRef asyncTasksRef $ Just asyncTasks
case R.readRef treeReloadRef of
Nothing -> R.setRef treeReloadRef $ Just reload
Just _ -> pure unit
R2.useCache (
frontends
/\ route
/\ sessions
/\ fst openNodes
/\ fst extReload
/\ fst appReload
/\ fst reload
/\ fst asyncTasks
/\ (fst asyncTasks).storage
/\ handed
)
(cpt' openNodes asyncTasks reload showLogin backend)
cpt' openNodes asyncTasks reload showLogin backend (frontends /\ route /\ sessions /\ _ /\ _ /\ _ /\ _ /\ handed) = do
(cpt' openNodes asyncTasks appReload reload showLogin backend)
cpt' openNodes asyncTasks appReload reload showLogin backend (frontends /\ route /\ sessions /\ _ /\ _ /\ _ /\ _ /\ handed) = do
pure $ R2.row $ [plus handed showLogin backend] <> trees
where
trees = tree <$> unSessions sessions
tree s@(Session {treeId}) =
treeView { root: treeId
treeView { appReload
, asyncTasks
, frontends
, handed
, mCurrentRoute: Just route
, openNodes
, reload
, root: treeId
, session: s
}
} []
plus :: Handed -> R.Setter Boolean -> R.State (Maybe Backend) -> R.Element
plus handed showLogin backend = H.div {className: if handed == RightHanded
then "flex-start" -- TODO we should use lefthanded SASS class here
else "flex-end"
} [
plus handed showLogin backend = H.div { className: handedClass } [
H.button { title: "Add or remove connections to the server(s)."
, on: {click}
, className: "btn btn-default"
......@@ -85,5 +105,107 @@ plus handed showLogin backend = H.div {className: if handed == RightHanded
-- TODO same as the one in the Login Modal (same CSS)
-- [ H.i { className: "material-icons md-36"} [] ]
where
handedClass = if handed == RightHanded then
"flex-start" -- TODO we should use lefthanded SASS class here
else
"flex-end"
click _ = (snd backend) (const Nothing)
*> showLogin (const true)
-------------------------
type ForestLayoutProps = (
appReload :: ReloadS
, asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
, backend :: R.State (Maybe Backend)
, frontends :: Frontends
, handed :: R.State Handed
, route :: AppRoute
, sessions :: Sessions
, showLogin :: R.Setter Boolean
, treeReloadRef :: R.Ref (Maybe ReloadS)
)
forestLayout :: R2.Component ForestLayoutProps
forestLayout props = R.createElement forestLayoutCpt props
forestLayoutCpt :: R.Component ForestLayoutProps
forestLayoutCpt = R.hooksComponentWithModule thisModule "forestLayout" cpt
where
cpt props@{ handed } children = do
pure $ R.fragment [ topBar { handed } [], forestLayoutMain props children ]
-- a component, for which first child element is placed inside the top bar
-- while the remaining ones are put into the main view
forestLayoutWithTopBar :: R2.Component ForestLayoutProps
forestLayoutWithTopBar props = R.createElement forestLayoutWithTopBarCpt props
forestLayoutWithTopBarCpt :: R.Component ForestLayoutProps
forestLayoutWithTopBarCpt = R.hooksComponentWithModule thisModule "forestLayoutWithTopBar" cpt
where
cpt props@{ handed } children = do
let { head: topBarChild, tail: mainChildren } =
fromMaybe { head: H.div {} [], tail: [] } $ A.uncons children
pure $ R.fragment [
topBar { handed } [ topBarChild ]
, forestLayoutMain props mainChildren
]
forestLayoutMain :: R2.Component ForestLayoutProps
forestLayoutMain props = R.createElement forestLayoutMainCpt props
forestLayoutMainCpt :: R.Component ForestLayoutProps
forestLayoutMainCpt = R.hooksComponentWithModule thisModule "forestLayoutMain" cpt
where
cpt props children = do
pure $ forestLayoutRaw props [
mainPage {} children
]
forestLayoutRaw :: R2.Component ForestLayoutProps
forestLayoutRaw props = R.createElement forestLayoutRawCpt props
forestLayoutRawCpt :: R.Component ForestLayoutProps
forestLayoutRawCpt = R.hooksComponentWithModule thisModule "forestLayoutRaw" cpt
where
cpt { appReload
, asyncTasksRef
, backend
, frontends
, handed
, route
, sessions
, showLogin
, treeReloadRef } children = do
let ordering =
case fst handed of
LeftHanded -> A.reverse
RightHanded -> identity
pure $ R2.row $ ordering ([
H.div { className: "col-md-2", style: { paddingTop: "60px" } } [
forest { appReload
, asyncTasksRef
, backend
, frontends
, handed: fst handed
, route
, sessions
, showLogin
, treeReloadRef } []
]
] <> children)
mainPage :: R2.Component ()
mainPage = R.createElement mainPageCpt
mainPageCpt :: R.Component ()
mainPageCpt = R.hooksComponentWithModule thisModule "mainPage" cpt
where
cpt {} children = do
pure $ H.div {className: "col-md-10"} [
H.div {id: "page-wrapper"} [
H.div {className: "container-fluid"} children
]
]
This diff is collapsed.
......@@ -3,13 +3,18 @@ module Gargantext.Components.Forest.Tree.Node where
import Data.Array (reverse)
import Data.Maybe (Maybe(..))
import Data.Nullable (null)
import Data.Tuple (snd)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff)
import Effect.Class (liftEffect)
import React.SyntheticEvent as E
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Prelude
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox)
import Gargantext.Components.Forest.Tree.Node.Action (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileType(..), UploadFileBlob(..))
......@@ -17,7 +22,6 @@ import Gargantext.Components.Forest.Tree.Node.Action.Upload (DroppedFile(..), fi
import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView)
import Gargantext.Components.Forest.Tree.Node.Box.Types (CommonProps)
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (asyncProgressBar, BarType(..))
import Gargantext.Components.Forest.Tree.Node.Tools.Task (Tasks)
import Gargantext.Components.Forest.Tree.Node.Tools.Sync (nodeActionsGraph, nodeActionsNodeList)
import Gargantext.Components.Forest.Tree.Node.Tools (nodeLink)
import Gargantext.Components.GraphExplorer.API as GraphAPI
......@@ -25,7 +29,6 @@ import Gargantext.Components.Lang (Lang(EN))
import Gargantext.Components.Nodes.Corpus (loadCorpusWithChild)
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Prelude (Unit, bind, const, discard, map, pure, show, unit, void, ($), (<>), (==), identity)
import Gargantext.Routes as Routes
import Gargantext.Version as GV
import Gargantext.Sessions (Session, sessionId)
......@@ -34,66 +37,93 @@ import Gargantext.Types as GT
import Gargantext.Utils.Popover as Popover
import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.Forest.Tree.Node"
-- Main Node
type NodeMainSpanProps =
( id :: ID
type NodeMainSpanProps = (
appReload :: GT.ReloadS
, asyncTasks :: GAT.Reductor
, folderOpen :: R.State Boolean
, frontends :: Frontends
, id :: ID
, isLeaf :: IsLeaf
, mCurrentRoute :: Maybe Routes.AppRoute
, name :: Name
, nodeType :: GT.NodeType
, tasks :: Record Tasks
, setPopoverRef :: R.Ref (Maybe (Boolean -> Effect Unit))
| CommonProps
)
type IsLeaf = Boolean
nodeMainSpan :: IsLeaf
-> Record NodeMainSpanProps
-> R.Element
nodeMainSpan isLeaf p@{ dispatch, folderOpen, frontends, handed, session } = R.createElement el p []
nodeSpan :: R2.Component NodeMainSpanProps
nodeSpan = R.createElement nodeSpanCpt
nodeSpanCpt :: R.Component NodeMainSpanProps
nodeSpanCpt = R.hooksComponentWithModule thisModule "nodeSpan" cpt
where
cpt props children = do
pure $ H.div {} ([ nodeMainSpan props [] ] <> children)
nodeMainSpan :: R2.Component NodeMainSpanProps
nodeMainSpan = R.createElement nodeMainSpanCpt
nodeMainSpanCpt :: R.Component NodeMainSpanProps
nodeMainSpanCpt = R.hooksComponentWithModule thisModule "nodeMainSpan" cpt
where
el = R.hooksComponentWithModule thisModule "nodeMainSpan" cpt
cpt props@{id, mCurrentRoute, name, nodeType, tasks: { onTaskFinish, tasks }} _ = do
cpt props@{ appReload
, asyncTasks: (asyncTasks /\ dispatchAsyncTasks)
, dispatch
, folderOpen
, frontends
, handed
, id
, isLeaf
, mCurrentRoute
, name
, nodeType
, session
, setPopoverRef
} _ = do
-- only 1 popup at a time is allowed to be opened
droppedFile <- R.useState' (Nothing :: Maybe DroppedFile)
isDragOver <- R.useState' false
popoverRef <- R.useRef null
R.useEffect' $ do
R.setRef setPopoverRef $ Just $ Popover.setOpen popoverRef
let ordering =
case handed of
GT.LeftHanded -> reverse
GT.RightHanded -> identity
let isSelected = mCurrentRoute == Routes.nodeTypeAppRoute nodeType (sessionId session) id
pure $ H.span (dropProps droppedFile isDragOver)
$ ordering
[ folderIcon nodeType folderOpen
, chevronIcon isLeaf handed nodeType folderOpen
, nodeLink { frontends
, id
, handed
, folderOpen
, isSelected: mCurrentRoute
== Routes.nodeTypeAppRoute
nodeType
(sessionId session) id
, id
, isSelected
, name: name' props
, nodeType
, session
, handed
}
, fileTypeView { dispatch, droppedFile, id, isDragOver, nodeType }
, H.div {} (map (\t -> asyncProgressBar { asyncTask: t
, barType: Pie
, corpusId: id
, onFinish: const $ onTaskFinish t
, nodeId: id
, onFinish: onTaskFinish id t
, session
}
) tasks
) $ GAT.getTasks asyncTasks id
)
, if nodeType == GT.NodeUser
then GV.versionView {session}
......@@ -110,7 +140,6 @@ nodeMainSpan isLeaf p@{ dispatch, folderOpen, frontends, handed, session } = R.c
]
else H.div {} []
, nodeActions { id
, nodeType
, refreshTree: const $ dispatch RefreshTree
......@@ -120,31 +149,40 @@ nodeMainSpan isLeaf p@{ dispatch, folderOpen, frontends, handed, session } = R.c
]
where
onTaskFinish id t _ = do
dispatchAsyncTasks $ GAT.Finish id t
snd appReload $ (_ + 1)
SettingsBox {show: showBox} = settingsBox nodeType
onPopoverClose popoverRef _ = Popover.setOpen popoverRef false
name' {name, nodeType} = if nodeType == GT.NodeUser
then show session
else name
name' {name, nodeType} = if nodeType == GT.NodeUser then show session else name
mNodePopupView props@{id, nodeType} onPopoverClose =
nodePopupView { dispatch
, handed : props.handed
, id
, name: name' props
, nodeType
, onPopoverClose
, session
}
chevronIcon isLeaf handed' nodeType folderOpen'@(open /\ _) =
if isLeaf
then H.div {} []
else
chevronIcon true handed' nodeType (open /\ setOpen) = H.div {} []
chevronIcon false handed' nodeType (open /\ setOpen) =
H.a { className: "chevron-icon"
, onClick: R2.effToggler folderOpen'
, on: { click: \_ -> setOpen $ not }
}
[ H.i {
className: if open
[ H.i { className: if open
then "fa fa-chevron-down"
else if handed' == GT.RightHanded
then "fa fa-chevron-right"
else "fa fa-chevron-left"
} [] ]
folderIcon nodeType folderOpen'@(open /\ _) =
folderIcon nodeType (open /\ setOpen) =
H.a { className: "folder-icon"
, onClick: R2.effToggler folderOpen'
, on: { click: \_ -> setOpen $ not }
} [
H.i {className: GT.fldr nodeType open} []
]
......@@ -154,16 +192,6 @@ nodeMainSpan isLeaf p@{ dispatch, folderOpen, frontends, handed, session } = R.c
<> "Click here to execute one of them."
} []
mNodePopupView props@{id, nodeType} onPopoverClose =
nodePopupView { id
, dispatch
, name: name' props
, nodeType
, onPopoverClose
, session
, handed : props.handed
}
dropProps droppedFile isDragOver =
{ className: "leaf " <> (dropClass droppedFile isDragOver)
, on: { drop: dropHandler droppedFile
......
......@@ -127,6 +127,7 @@ settingsBox Team =
, NodeFrameWrite
, NodeFrameCalc
, NodeFrameNotebook
, Team
]
, Share
, Delete
......
......@@ -6,6 +6,7 @@ import Data.Set (Set)
import Data.Set as Set
import Data.String as S
import Data.String.CodeUnits as DSCU
import Data.Tuple (snd)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff, launchAff_)
......@@ -24,6 +25,7 @@ import Gargantext.Utils (toggleSet)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.ReactTooltip as ReactTooltip
thisModule :: String
thisModule = "Gargantext.Components.Forest.Tree.Node.Tools"
------------------------------------------------------------------------
......@@ -247,7 +249,7 @@ checkboxes :: forall a
-> R.Element
checkboxes xs (val /\ set) =
H.fieldset {} $ map (\a -> H.div {} [ H.input { type: "checkbox"
, checked: Set.member a val
, defaultChecked: Set.member a val
, on: { click: \_ -> set
$ const
$ toggleSet a val
......@@ -282,11 +284,19 @@ nodeLink p = R.createElement nodeLinkCpt p []
nodeLinkCpt :: R.Component NodeLinkProps
nodeLinkCpt = R.hooksComponentWithModule thisModule "nodeLink" cpt
where
cpt { frontends, id, isSelected, name, nodeType, session, handed, folderOpen} _ = do
cpt { folderOpen: (_ /\ setFolderOpen)
, frontends
, handed
, id
, isSelected
, name
, nodeType
, session
} _ = do
popoverRef <- R.useRef null
pure $
H.div { onClick: R2.effToggler folderOpen }
H.div { on: { click: onClick } }
[ H.a { data: { for: tooltipId
, tip: true
}
......@@ -305,6 +315,13 @@ nodeLinkCpt = R.hooksComponentWithModule thisModule "nodeLink" cpt
]
where
-- NOTE Don't toggle tree if it is not selected
-- click on closed -> open
-- click on open -> ?
onClick _ = if isSelected then
setFolderOpen (const true)
else
setFolderOpen (const true)
tooltipId = "node-link-" <> show id
-- END node link
......
......@@ -16,15 +16,16 @@ import Gargantext.Sessions (Session, get)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar"
data BarType = Bar | Pie
type Props =
( asyncTask :: GT.AsyncTaskWithType
type Props = (
asyncTask :: GT.AsyncTaskWithType
, barType :: BarType
, corpusId :: GT.ID
, nodeId :: GT.ID
, onFinish :: Unit -> Effect Unit
, session :: Session
)
......@@ -38,7 +39,7 @@ asyncProgressBarCpt = R.hooksComponentWithModule thisModule "asyncProgressBar" c
where
cpt props@{ asyncTask: (GT.AsyncTaskWithType {task: GT.AsyncTask {id}})
, barType
, corpusId
, nodeId
, onFinish
} _ = do
(progress /\ setProgress) <- R.useState' 0.0
......@@ -104,13 +105,14 @@ queryProgress :: Record Props -> Aff GT.AsyncProgress
queryProgress { asyncTask: GT.AsyncTaskWithType { task: GT.AsyncTask {id}
, typ
}
, corpusId
, nodeId
, session
} = get session (p typ)
where
-- TODO refactor path
p GT.UpdateNode = NodeAPI GT.Node (Just corpusId) $ path <> id <> "/poll?limit=1"
p _ = NodeAPI GT.Corpus (Just corpusId) $ path <> id <> "/poll?limit=1"
p GT.UpdateNode = NodeAPI GT.Node (Just nodeId) $ path <> id <> "/poll?limit=1"
p GT.UpdateNgramsCharts = NodeAPI GT.Node (Just nodeId) $ path <> id <> "/poll?limit=1"
p _ = NodeAPI GT.Corpus (Just nodeId) $ path <> id <> "/poll?limit=1"
path = GT.asyncTaskTypePath typ
-- TODO wait route: take the result if failure then message
module Gargantext.Components.Forest.Tree.Node.Tools.Task
where
import Data.Array as A
import Data.Map as Map
import Data.Maybe (Maybe(..), maybe)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Gargantext.AsyncTasks as GAT
import Gargantext.Prelude (Unit, discard, identity, ($), (+))
import Gargantext.Types (Reload)
import Gargantext.Types as GT
import Reactix as R
type Tasks =
( onTaskAdd :: GT.AsyncTaskWithType -> Effect Unit
, onTaskFinish :: GT.AsyncTaskWithType -> Effect Unit
, tasks :: Array GT.AsyncTaskWithType
)
tasksStruct :: Int
-> R.State GAT.Storage
-> R.State Reload
-> Record Tasks
tasksStruct id (asyncTasks /\ setAsyncTasks) (_ /\ setReload) =
{ onTaskAdd, onTaskFinish, tasks }
where
tasks = maybe [] identity $ Map.lookup id asyncTasks
onTaskAdd t = do
setReload (_ + 1)
setAsyncTasks $ Map.alter (maybe (Just [t])
$ (\ts -> Just $ A.cons t ts)) id
onTaskFinish t = do
setReload (_ + 1)
setAsyncTasks $ Map.alter (maybe Nothing $ (\ts -> Just $ GAT.removeTaskFromList ts t)) id
......@@ -20,6 +20,7 @@ import Reactix as R
import Reactix.DOM.HTML as RH
import Record as Record
import Gargantext.AsyncTasks as GAT
import Gargantext.Components.Forest (forest)
import Gargantext.Components.Graph as Graph
import Gargantext.Components.GraphExplorer.Controls as Controls
......@@ -40,20 +41,21 @@ import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.GraphExplorer"
type LayoutProps =
( frontends :: Frontends
type LayoutProps = (
asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
, backend :: R.State (Maybe Backend)
, frontends :: Frontends
, graphId :: GET.GraphId
, handed :: Types.Handed
, mCurrentRoute :: AppRoute
, session :: Session
, sessions :: Sessions
, showLogin :: R.State Boolean
, backend :: R.State (Maybe Backend)
)
type Props =
( graph :: SigmaxT.SGraph
, graphVersion :: R.State Int
, graphVersion :: Types.ReloadS
, hyperdataGraph :: GET.HyperdataGraph
, mMetaData :: Maybe GET.MetaData
| LayoutProps
......@@ -70,7 +72,7 @@ explorerLayoutCpt = R.hooksComponentWithModule thisModule "explorerLayout" cpt
graphVersion <- R.useState' 0
pure $ explorerLayoutView graphVersion props
explorerLayoutView :: R.State Int -> Record LayoutProps -> R.Element
explorerLayoutView :: Types.ReloadS -> Record LayoutProps -> R.Element
explorerLayoutView graphVersion p = R.createElement el p []
where
el = R.hooksComponentWithModule thisModule "explorerLayoutView" cpt
......@@ -90,7 +92,9 @@ explorer props = R.createElement explorerCpt props []
explorerCpt :: R.Component Props
explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
where
cpt props@{ frontends
cpt props@{ asyncTasksRef
, backend
, frontends
, graph
, graphId
, graphVersion
......@@ -101,7 +105,6 @@ explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
, session
, sessions
, showLogin
, backend
} _ = do
let startForceAtlas = maybe true (\(GET.MetaData { startForceAtlas }) -> startForceAtlas) mMetaData
......@@ -114,6 +117,7 @@ explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
graphRef <- R.useRef null
graphVersionRef <- R.useRef (fst graphVersion)
treeReload <- R.useState' 0
treeReloadRef <- R.useRef $ Just treeReload
controls <- Controls.useGraphControls { forceAtlasS
, graph
, graphId
......@@ -154,14 +158,17 @@ explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
[ inner handed
[ rowControls [ Controls.controls controls ]
, R2.row $ mainLayout handed $
tree { frontends
tree { asyncTasksRef
, backend
, frontends
, handed
, mCurrentRoute
, reload: treeReload
, sessions
, show: fst controls.showTree
, showLogin: snd showLogin
, backend}
, treeReloadRef
}
/\
RH.div { ref: graphRef, id: "graph-view", className: "col-md-12" } []
/\
......@@ -208,9 +215,17 @@ explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
tree :: Record TreeProps -> R.Element
tree { show: false } = RH.div { id: "tree" } []
tree { frontends, handed, mCurrentRoute: route, reload, sessions, showLogin, backend} =
tree { asyncTasksRef, backend, frontends, handed, mCurrentRoute: route, reload, sessions, showLogin, treeReloadRef } =
RH.div {className: "col-md-2 graph-tree"} [
forest { frontends, handed, reload, route, sessions, showLogin, backend}
forest { appReload: reload
, asyncTasksRef
, backend
, frontends
, handed
, route
, sessions
, showLogin
, treeReloadRef } []
]
mSidebar :: Maybe GET.MetaData
......@@ -222,26 +237,28 @@ explorerCpt = R.hooksComponentWithModule thisModule "explorer" cpt
type TreeProps =
(
frontends :: Frontends
asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
, backend :: R.State (Maybe Backend)
, frontends :: Frontends
, handed :: Types.Handed
, mCurrentRoute :: AppRoute
, reload :: R.State Int
, reload :: Types.ReloadS
, sessions :: Sessions
, show :: Boolean
, showLogin :: R.Setter Boolean
, backend :: R.State (Maybe Backend)
, treeReloadRef :: R.Ref (Maybe Types.ReloadS)
)
type MSidebarProps =
( frontends :: Frontends
, graph :: SigmaxT.SGraph
, graphId :: GET.GraphId
, graphVersion :: R.State Int
, graphVersion :: Types.ReloadS
, removedNodeIds :: R.State SigmaxT.NodeIds
, showSidePanel :: R.State GET.SidePanelState
, selectedNodeIds :: R.State SigmaxT.NodeIds
, session :: Session
, treeReload :: R.State Int
, treeReload :: Types.ReloadS
)
type GraphProps = (
......@@ -349,7 +366,7 @@ modeGraphType Types.Sources = "star"
modeGraphType Types.Terms = "def"
getNodes :: Session -> R.State Int -> GET.GraphId -> Aff GET.HyperdataGraph
getNodes :: Session -> Types.ReloadS -> GET.GraphId -> Aff GET.HyperdataGraph
getNodes session (graphVersion /\ _) graphId =
get session $ NodeAPI Types.Graph
(Just graphId)
......
......@@ -17,7 +17,7 @@ controlsToggleButton :: Record Props -> R.Element
controlsToggleButton props = R.createElement controlsToggleButtonCpt props []
controlsToggleButtonCpt :: R.Component Props
controlsToggleButtonCpt = R.hooksComponentWithModule thisModule "graphControlsToggleButton" cpt
controlsToggleButtonCpt = R.hooksComponentWithModule thisModule "controlsToggleButton" cpt
where
cpt {state} _ = do
let (open /\ setOpen) = state
......
......@@ -32,7 +32,7 @@ import Gargantext.Data.Array (mapMaybe)
import Gargantext.Ends (Frontends)
import Gargantext.Hooks.Sigmax.Types as SigmaxT
import Gargantext.Sessions (Session)
import Gargantext.Types (CTabNgramType, TabSubType(..), TabType(..), TermList(..), modeTabType)
import Gargantext.Types (CTabNgramType, ReloadS, TabSubType(..), TabType(..), TermList(..), modeTabType)
import Gargantext.Utils.Reactix as R2
import Partial.Unsafe (unsafePartial)
......@@ -42,13 +42,13 @@ type Props =
( frontends :: Frontends
, graph :: SigmaxT.SGraph
, graphId :: Int
, graphVersion :: R.State Int
, graphVersion :: ReloadS
, metaData :: GET.MetaData
, removedNodeIds :: R.State SigmaxT.NodeIds
, selectedNodeIds :: R.State SigmaxT.NodeIds
, session :: Session
, showSidePanel :: R.State GET.SidePanelState
, treeReload :: R.State Int
, treeReload :: ReloadS
)
sidebar :: Record Props -> R.Element
......@@ -110,7 +110,7 @@ sideTab (Opened SideTabData) props =
[ RH.span {} [ RH.text text ]
, RH.input { type: "checkbox"
, className: "checkbox"
, checked: true
, defaultChecked: true
, title: "Mark as completed" } ]
......@@ -204,7 +204,7 @@ type DeleteNodes =
, nodes :: Array (Record SigmaxT.Node)
, session :: Session
, termList :: TermList
, treeReload :: R.State Int
, treeReload :: ReloadS
)
deleteNodes :: Record DeleteNodes -> Effect Unit
......@@ -223,7 +223,10 @@ deleteNode :: TermList
-> GET.MetaData
-> Record SigmaxT.Node
-> Aff NTC.VersionedNgramsPatches
deleteNode termList session (GET.MetaData metaData) node = NTC.putNgramsPatches coreParams versioned
deleteNode termList session (GET.MetaData metaData) node = do
ret <- NTC.putNgramsPatches coreParams versioned
task <- NTC.postNgramsChartsAsync coreParams -- TODO add task
pure ret
where
nodeId :: Int
nodeId = unsafePartial $ fromJust $ fromString node.id
......
......@@ -10,6 +10,7 @@ import Reactix as R
import Gargantext.Components.LoadingSpinner (loadingSpinner)
import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.Loader"
type Props path loaded =
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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