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 ]
redrawMenu
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)
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
s <- Sel.getSelection
--log2 "[maybeShowMenu] s" s
case s of
Just sel -> do
case Sel.selectionToString sel of
"" -> pure unit
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
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
case s of
Just sel -> do
case Sel.selectionToString sel of
"" -> hideMenu
sel' -> do
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,80 +25,61 @@ 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
divs = map (\c -> if cat == c
then
H.div { className : icon c (cat == c) } []
else
H.div { className : icon c (cat == c)
, on: { click: onClick c}
} []
) (caroussel' cat)
caroussel' :: Category -> Array Category
caroussel' Trash = A.take 2 categories
caroussel' c = A.take 3 $ A.drop (cat2score c - 1 ) categories
onClick c = \_-> do
setLocalCategories $ Map.insert r._id c
void $ launchAff $ putCategories session nodeId $ CategoryQuery {nodeIds: [r._id], category: c}
cpt { category, nodeId, row: DocumentsView r, session, setLocalCategories } _ = do
pure $ H.div {className:"flex"} divs
where
divs = map (\c -> if category == c
then
H.div { className : icon c (category == c) } []
else
H.div { className : icon c (category == c)
, on: { click: onClick c}
} []
) (caroussel' category)
caroussel' :: Category -> Array Category
caroussel' Trash = A.take 2 categories
caroussel' c = A.take 3 $ A.drop (cat2score c - 1 ) categories
onClick c = \_-> do
setLocalCategories $ Map.insert r._id c
void $ launchAff $ putCategories session nodeId $ CategoryQuery {nodeIds: [r._id], category: c}
icon :: Category -> Boolean -> String
icon cat b = btn b $ "glyphicon glyphicon-" <> (color $ size b $ icon' cat b)
......
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)
......@@ -39,18 +40,20 @@ thisModule = "Gargantext.Components.FacetsTable"
------------------------------------------------------------------------
type Props =
( chart :: R.Element
, container :: Record T.TableContainerProps -> R.Element
, frontends :: Frontends
, listId :: Int
, nodeId :: Int
, query :: SearchQuery
, session :: Session
( chart :: R.Element
, container :: Record T.TableContainerProps -> R.Element
, frontends :: Frontends
, listId :: Int
, nodeId :: Int
, query :: SearchQuery
, session :: Session
, totalRecords :: Int
)
-- | 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
, handed :: Handed
, reload :: R.State Int
, route :: AppRoute
, sessions :: Sessions
, showLogin :: R.Setter Boolean
, backend :: R.State (Maybe Backend)
type Props = (
appReload :: ReloadS
, asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
, backend :: R.State (Maybe Backend)
, frontends :: Frontends
, handed :: Handed
, route :: AppRoute
, sessions :: Sessions
, showLogin :: R.Setter Boolean
, 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"
......@@ -81,9 +101,111 @@ plus handed showLogin backend = H.div {className: if handed == RightHanded
--, H.div { "type": "", className: "fa fa-plus-circle fa-lg"} []
--, H.div { "type": "", className: "fa fa-minus-circle fa-lg"} []
]
]
]
-- 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
el = R.hooksComponentWithModule thisModule "nodeMainSpan" cpt
cpt props@{id, mCurrentRoute, name, nodeType, tasks: { onTaskFinish, tasks }} _ = do
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
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
, session
}
) tasks
, barType: Pie
, nodeId: id
, onFinish: onTaskFinish id t
, session
}
) $ 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
......@@ -119,51 +148,50 @@ nodeMainSpan isLeaf p@{ dispatch, folderOpen, frontends, handed, session } = R.c
]
where
SettingsBox {show: showBox} = settingsBox nodeType
onPopoverClose popoverRef _ = Popover.setOpen popoverRef false
name' {name, nodeType} = if nodeType == GT.NodeUser
then show session
else name
chevronIcon isLeaf handed' nodeType folderOpen'@(open /\ _) =
if isLeaf
then H.div {} []
else
H.a { className: "chevron-icon"
, onClick: R2.effToggler folderOpen'
}
[ 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 /\ _) =
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
mNodePopupView props@{id, nodeType} onPopoverClose =
nodePopupView { dispatch
, handed : props.handed
, id
, name: name' props
, nodeType
, onPopoverClose
, session
}
chevronIcon true handed' nodeType (open /\ setOpen) = H.div {} []
chevronIcon false handed' nodeType (open /\ setOpen) =
H.a { className: "chevron-icon"
, on: { click: \_ -> setOpen $ not }
}
[ 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 (open /\ setOpen) =
H.a { className: "folder-icon"
, onClick: R2.effToggler folderOpen'
, on: { click: \_ -> setOpen $ not }
} [
H.i {className: GT.fldr nodeType open} []
]
H.i {className: GT.fldr nodeType open} []
]
popOverIcon = H.a { className: "settings fa fa-cog"
, title : "Each node of the Tree can perform some actions.\n"
<> "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
......
......@@ -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
......
......@@ -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.
......@@ -21,7 +21,7 @@ thisModule = "Gargantext.Components.Modal"
type Props = ( setVisible :: R.Setter Boolean )
modal :: Record Props -> Array R.Element -> R.Element
modal :: R2.Component Props
modal = R.createElement modalCpt
modalCpt :: R.Component Props
......
This diff is collapsed.
This diff is collapsed.
......@@ -28,6 +28,7 @@ import Gargantext.Types (ChartType(..), TabType)
import Gargantext.Utils.CacheAPI as GUC
import Gargantext.Utils.Reactix as R2
thisModule :: String
thisModule = "Gargantext.Components.Nodes.Corpus.Chart.Pie"
newtype ChartMetrics = ChartMetrics {
......
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 file is just a wrapper so that webpack will call our main function
require('../output/Main').main();
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