Commit b41ffafb authored by Karen Konou's avatar Karen Konou

Merge branch 'dev' into 718-dev-subcorpus-frontend

parents fa605b8d 2e43c474
Pipeline #7254 failed with stages
in 4 minutes and 55 seconds
3ab0ce9ebc2b62ae09099e9d037e953f8ba252cf
# Fixing the nixos image saves CI time so it doesn't have to pull new
#image every time (nixos/nix updates quite often).
# image every time (nixos/nix updates quite often).
# image: nixos/nix:latest
image: nixos/nix:2.24.8
#before_script:
#- nix-env -iA nixpkgs.nix nixpkgs.cacert
#- apt-get update
#- apt-get install make xz-utils
stages:
# - deps
- check-lint
- compile
- test
......@@ -21,6 +17,17 @@ stages:
# #- node_modules/
# script:
# - nix-shell shell.nix --run 'bun install --skip-builds'
check-lint:
stage: check-lint
cache:
paths:
- /nix/store
- node_modules/
- output/
- .spago/
script:
- nix --extra-experimental-features "nix-command flakes" run .#check-lint
compile:
stage: compile
......@@ -38,8 +45,6 @@ compile:
test:
stage: test
cache:
# cache per branch name
# key: ${CI_COMMIT_REF_SLUG}
paths:
- /nix/store
- node_modules/
......@@ -51,3 +56,4 @@ test:
- nix --extra-experimental-features "nix-command flakes" run .#test-ps
- nix-collect-garbage --delete-older-than 14d
purs-tidy check "src"
if [ $? -ne 0 ]; then
echo "purs tidy check failed. Please fix the issues before committing."
exit 1
fi
......@@ -8,6 +8,7 @@
# nix-shell --show-trace --option build-fallback true --run --extra-experimental-features nix-command 'bun install'
# nix-shell --show-trace --option build-fallback true --run --extra-experimental-features nix-commandfix-bun
# nix-shell --show-trace --option build-fallback true --run --extra-experimental-features nix-command build
nix run --extra-experimental-features nix-command --extra-experimental-features flakes .#check-lint
nix run --extra-experimental-features nix-command --extra-experimental-features flakes .#install
nix run --extra-experimental-features nix-command --extra-experimental-features flakes .#build
nix run --extra-experimental-features nix-command --extra-experimental-features flakes .#build-css
......
......@@ -7,7 +7,6 @@
<!-- <link href="styles/bootstrap.min.css" rel="stylesheet"> -->
<link id="bootstrap-css" href="styles/bootstrap-default.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="styles/highlightjs-solarized-light.css" />
<style> * {margin: 0; padding: 0; list-style: none;} </style>
</head>
<body>
<div id="app"></div>
......
......@@ -5927,6 +5927,7 @@ a.close.disabled {
background-color: #fff;
margin-left: 1.75rem;
margin-right: 1.75rem;
max-width: 800px;
}
.b-modal__header {
background-color: white;
......@@ -6354,6 +6355,10 @@ a.close.disabled {
border-radius: 50px;
}
details summary {
cursor: pointer;
}
.echarts-for-react {
height: 300px;
}
......@@ -6390,7 +6395,7 @@ a:focus {
outline: 0;
}
ul {
ul, li, span {
list-style: none;
}
......@@ -10457,72 +10462,7 @@ input[type=range]:-moz-focusring {
padding-right: 20px;
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .fa-pencil {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .fa-pencil {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #181A1B;
padding: 40px 56px;
gap: 48px;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 18px;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: -9px;
right: -13px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.frame-search.card {
width: 1000px;
height: 600px;
}
......@@ -11085,6 +11025,92 @@ select.form-control {
transform: scale(1.4);
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .box-wrapper-action-rename .b-icon-button__inner {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .box-wrapper-action-rename .b-icon-button__inner {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #181A1B;
}
.node-popup-tooltip .popup-container__body.toolbox-tabs-labels {
border-bottom: 1px solid #495057;
padding: 16px 56px 0;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 20px;
padding: 0.7rem 1.5rem 0.6rem;
display: list-item;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
margin-bottom: -1px;
}
.node-popup-tooltip .popup-container__cta__button:hover, .node-popup-tooltip .popup-container__cta__button--active {
border: 1px solid #495057;
}
.node-popup-tooltip .popup-container__cta__button--active, .node-popup-tooltip .popup-container__cta__button--active:hover {
border-bottom-color: #fff;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container .toolbox-tab-content {
padding: 32px;
}
.node-popup-tooltip .text-sup {
vertical-align: super;
font-size: x-small;
margin: 0.33em;
}
.table tr td {
height: 48px;
}
......
......@@ -5739,6 +5739,7 @@ a.close.disabled {
background-color: #fff;
margin-left: 1.75rem;
margin-right: 1.75rem;
max-width: 800px;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
......@@ -6166,6 +6167,10 @@ a.close.disabled {
border-radius: 50px;
}
details summary {
cursor: pointer;
}
.echarts-for-react {
height: 300px;
}
......@@ -6202,7 +6207,7 @@ a:focus {
outline: 0;
}
ul {
ul, li, span {
list-style: none;
}
......@@ -10263,72 +10268,7 @@ input[type=range]:-moz-focusring {
padding-right: 20px;
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .fa-pencil {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .fa-pencil {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
padding: 40px 56px;
gap: 48px;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 18px;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: -9px;
right: -13px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.frame-search.card {
width: 1000px;
height: 600px;
}
......@@ -10891,6 +10831,92 @@ select.form-control {
transform: scale(1.4);
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .box-wrapper-action-rename .b-icon-button__inner {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .box-wrapper-action-rename .b-icon-button__inner {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
}
.node-popup-tooltip .popup-container__body.toolbox-tabs-labels {
border-bottom: 1px solid #DEE2E6;
padding: 16px 56px 0;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 20px;
padding: 0.7rem 1.5rem 0.6rem;
display: list-item;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
margin-bottom: -1px;
}
.node-popup-tooltip .popup-container__cta__button:hover, .node-popup-tooltip .popup-container__cta__button--active {
border: 1px solid #DEE2E6;
}
.node-popup-tooltip .popup-container__cta__button--active, .node-popup-tooltip .popup-container__cta__button--active:hover {
border-bottom-color: #fff;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container .toolbox-tab-content {
padding: 32px;
}
.node-popup-tooltip .text-sup {
vertical-align: super;
font-size: x-small;
margin: 0.33em;
}
.table tr td {
height: 48px;
}
......
......@@ -5578,6 +5578,7 @@ a.close.disabled {
background-color: #fff;
margin-left: 1.75rem;
margin-right: 1.75rem;
max-width: 800px;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
......@@ -6005,6 +6006,10 @@ a.close.disabled {
border-radius: 50px;
}
details summary {
cursor: pointer;
}
.echarts-for-react {
height: 300px;
}
......@@ -6041,7 +6046,7 @@ a:focus {
outline: 0;
}
ul {
ul, li, span {
list-style: none;
}
......@@ -10108,72 +10113,7 @@ input[type=range]:-moz-focusring {
padding-right: 20px;
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .fa-pencil {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .fa-pencil {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
padding: 40px 56px;
gap: 48px;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 18px;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: -9px;
right: -13px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.frame-search.card {
width: 1000px;
height: 600px;
}
......@@ -10736,6 +10676,92 @@ select.form-control {
transform: scale(1.4);
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .box-wrapper-action-rename .b-icon-button__inner {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .box-wrapper-action-rename .b-icon-button__inner {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
}
.node-popup-tooltip .popup-container__body.toolbox-tabs-labels {
border-bottom: 1px solid #DEE2E6;
padding: 16px 56px 0;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 20px;
padding: 0.7rem 1.5rem 0.6rem;
display: list-item;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
margin-bottom: -1px;
}
.node-popup-tooltip .popup-container__cta__button:hover, .node-popup-tooltip .popup-container__cta__button--active {
border: 1px solid #DEE2E6;
}
.node-popup-tooltip .popup-container__cta__button--active, .node-popup-tooltip .popup-container__cta__button--active:hover {
border-bottom-color: #fff;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container .toolbox-tab-content {
padding: 32px;
}
.node-popup-tooltip .text-sup {
vertical-align: super;
font-size: x-small;
margin: 0.33em;
}
.table tr td {
height: 48px;
}
......
......@@ -5803,6 +5803,7 @@ a.close.disabled {
background-color: #fff;
margin-left: 1.75rem;
margin-right: 1.75rem;
max-width: 800px;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
......@@ -6230,6 +6231,10 @@ a.close.disabled {
border-radius: 50px;
}
details summary {
cursor: pointer;
}
.echarts-for-react {
height: 300px;
}
......@@ -6266,7 +6271,7 @@ a:focus {
outline: 0;
}
ul {
ul, li, span {
list-style: none;
}
......@@ -10333,72 +10338,7 @@ input[type=range]:-moz-focusring {
padding-right: 20px;
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .fa-pencil {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .fa-pencil {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
padding: 40px 56px;
gap: 48px;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 18px;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: -9px;
right: -13px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.frame-search.card {
width: 1000px;
height: 600px;
}
......@@ -10961,6 +10901,92 @@ select.form-control {
transform: scale(1.4);
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .box-wrapper-action-rename .b-icon-button__inner {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .box-wrapper-action-rename .b-icon-button__inner {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
}
.node-popup-tooltip .popup-container__body.toolbox-tabs-labels {
border-bottom: 1px solid #DEE2E6;
padding: 16px 56px 0;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 20px;
padding: 0.7rem 1.5rem 0.6rem;
display: list-item;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
margin-bottom: -1px;
}
.node-popup-tooltip .popup-container__cta__button:hover, .node-popup-tooltip .popup-container__cta__button--active {
border: 1px solid #DEE2E6;
}
.node-popup-tooltip .popup-container__cta__button--active, .node-popup-tooltip .popup-container__cta__button--active:hover {
border-bottom-color: #fff;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container .toolbox-tab-content {
padding: 32px;
}
.node-popup-tooltip .text-sup {
vertical-align: super;
font-size: x-small;
margin: 0.33em;
}
.table tr td {
height: 48px;
}
......
......@@ -5876,6 +5876,7 @@ a.close.disabled {
background-color: #fff;
margin-left: 1.75rem;
margin-right: 1.75rem;
max-width: 800px;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
......@@ -6303,6 +6304,10 @@ a.close.disabled {
border-radius: 50px;
}
details summary {
cursor: pointer;
}
.echarts-for-react {
height: 300px;
}
......@@ -6339,7 +6344,7 @@ a:focus {
outline: 0;
}
ul {
ul, li, span {
list-style: none;
}
......@@ -10406,72 +10411,7 @@ input[type=range]:-moz-focusring {
padding-right: 20px;
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .fa-pencil {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .fa-pencil {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
padding: 40px 56px;
gap: 48px;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 18px;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: -9px;
right: -13px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.frame-search.card {
width: 1000px;
height: 600px;
}
......@@ -11034,6 +10974,92 @@ select.form-control {
transform: scale(1.4);
}
.forest-tree-node-modal {
width: fit-content;
min-width: 544px;
max-width: 100vw;
}
.node-popup-tooltip .panel-actions {
font-size: 90%;
padding-left: 20px;
}
.node-popup-tooltip .panel-actions .b-icon {
margin-right: 8px;
font-size: 11px;
}
.node-popup-tooltip .panel-actions__almost-useable {
color: #f1a50e;
}
.node-popup-tooltip .panel-actions__development-in-progress {
color: #8b2823;
}
.node-popup-tooltip .panel-actions__ok-to-use {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container {
display: flex;
flex-direction: column;
}
.node-popup-tooltip .popup-container__header .b-wad .box-wrapper-action-rename .b-icon-button__inner {
visibility: hidden;
}
.node-popup-tooltip .popup-container__header .b-wad:hover .box-wrapper-action-rename .b-icon-button__inner {
visibility: visible;
}
.node-popup-tooltip .popup-container__body {
display: flex;
justify-content: center;
background-color: #fff;
}
.node-popup-tooltip .popup-container__body.toolbox-tabs-labels {
border-bottom: 1px solid #DEE2E6;
padding: 16px 56px 0;
}
.node-popup-tooltip .popup-container__cta {
position: relative;
}
.node-popup-tooltip .popup-container__cta__button {
font-size: 20px;
padding: 0.7rem 1.5rem 0.6rem;
display: list-item;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
margin-bottom: -1px;
}
.node-popup-tooltip .popup-container__cta__button:hover, .node-popup-tooltip .popup-container__cta__button--active {
border: 1px solid #DEE2E6;
}
.node-popup-tooltip .popup-container__cta__button--active, .node-popup-tooltip .popup-container__cta__button--active:hover {
border-bottom-color: #fff;
}
.node-popup-tooltip .popup-container__cta__icon {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
}
.node-popup-tooltip .popup-container__cta--almost-useable .popup-container__cta__icon {
color: #f1a50e;
}
.node-popup-tooltip .popup-container__cta--development-in-progress .popup-container__cta__icon {
color: #8b2823;
}
.node-popup-tooltip .popup-container__cta--ok-to-use .popup-container__cta__icon {
color: #212529;
display: none;
}
.node-popup-tooltip .popup-container .toolbox-tab-content {
padding: 32px;
}
.node-popup-tooltip .text-sup {
vertical-align: super;
font-size: x-small;
margin: 0.33em;
}
.table tr td {
height: 48px;
}
......
......@@ -598,7 +598,7 @@ li .leaf:hover a.settings {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
height: 600px;
width: 1000px;
/* width: 1000px; */
}
#node-popup-tooltip .popup-container-body {
max-height: 70vh;
......
......@@ -22,12 +22,10 @@
dependencies = with pkgs; [
purs-bin.purs-0_15_16-1 # from the purescript-overlay
spago-bin.spago-0_93_37
#purescript
#spago-unstable
nodePackages.purs-tidy
watchexec
esbuild
nodejs
watchexec
pscid
nodePackages.npm
];
......@@ -37,14 +35,18 @@
npm run server
'';
build-watch = pkgs.writeShellScriptBin "build-watch" ''
setup-gitblame = pkgs.writeShellScriptBin "setup-gitblame" ''
set -e
echo "Build watch"
npm spago build -w --then browserify
if git config --get blame.ignoreRevsFile > /dev/null; then
echo "blame.ignoreRevsFile is already set."
else
git config blame.ignoreRevsFile .git-blame-ignore-revs
echo "blame.ignoreRevsFile has been set to .git-blame-ignore-revs."
fi
'';
build-zephyr = pkgs.writeShellScriptBin "build-zephyr" ''
set -e
......@@ -73,6 +75,17 @@
npm ci
'';
};
check-lint = pkgs.writeShellApplication {
name = "check-lint";
runtimeInputs = dependencies;
text = ''
set -e
echo "Checking format"
purs-tidy check "src"
'';
};
install = pkgs.writeShellApplication {
name = "install";
runtimeInputs = dependencies;
......@@ -154,10 +167,10 @@
self.packages.${system}.build
self.packages.${system}.build-css
self.packages.${system}.compile
self.packages.${system}.check-lint
self.packages.${system}.test-ps
self.packages.${system}.repl
build-watch
setup-gitblame
build-zephyr
minify-bundle
serve
......
......@@ -10,7 +10,6 @@
<link href="styles/sass.css" rel="stylesheet" type="text/css" />
<!-- <script type="text/javascript" src="/js/react-bootstrap.min.js"></script> -->
<script type="text/javascript" src="/js/react-bootstrap.js"></script>
<style> * {margin: 0; padding: 0; list-style: none;} </style>
</head>
<body>
<div id="app"></div>
......
......@@ -40,9 +40,11 @@
"http-proxy-middleware": "^3.0.3",
"immer": "~9.0.5",
"isomorphic-unfetch": "~3.1.0",
"jszip": "^3.10.1",
"markdown-it": "~13.0.1",
"minify": "^11.3.0",
"prop-types": "~15.6.2",
"purs-tidy": "^0.11.0",
"react": "~18.2.0",
"react-awesome-popover": "~6.1.1",
"react-bootstrap": "~1.5.2",
......@@ -60,6 +62,7 @@
"@babel/preset-react": "~7.24.1",
"@getgauge/cli": "~1.4.0",
"esbuild": "~0.21.1",
"husky": "^9.1.7",
"parcel": "~2.8.2",
"process": "^0.11.10",
"react-testing-library": "~8.0.1",
......@@ -5214,7 +5217,6 @@
},
"node_modules/core-util-is": {
"version": "1.0.3",
"dev": true,
"license": "MIT"
},
"node_modules/cosmiconfig": {
......@@ -7428,6 +7430,21 @@
"node": ">= 6"
}
},
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
"bin": {
"husky": "bin.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"license": "MIT",
......@@ -7456,6 +7473,11 @@
],
"license": "BSD-3-Clause"
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/immer": {
"version": "9.0.21",
"license": "MIT",
......@@ -7752,7 +7774,6 @@
},
"node_modules/isarray": {
"version": "1.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/isexe": {
......@@ -7858,6 +7879,17 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"dev": true,
......@@ -7891,6 +7923,14 @@
"node": ">=0.10.0"
}
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lightningcss": {
"version": "1.24.1",
"dev": true,
......@@ -9937,6 +9977,11 @@
"node": ">=8"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"node_modules/pandemonium": {
"version": "2.4.1",
"license": "MIT",
......@@ -10707,7 +10752,6 @@
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/progress": {
......@@ -10851,6 +10895,14 @@
"license": "MIT",
"optional": true
},
"node_modules/purs-tidy": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/purs-tidy/-/purs-tidy-0.11.0.tgz",
"integrity": "sha512-HZ8AS6J7Ka2YVl6Gr/H5NV17TU10yGYUTxVwRd5tKuwsVdFZewXSzZ/HTpWrkhdR2gxSVk0BdnpJhyu//oRc+w==",
"bin": {
"purs-tidy": "bin/index.js"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"dev": true,
......@@ -11197,7 +11249,6 @@
},
"node_modules/readable-stream": {
"version": "2.3.8",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
......@@ -12252,7 +12303,6 @@
},
"node_modules/string_decoder": {
"version": "1.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
......@@ -12878,7 +12928,6 @@
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"dev": true,
"license": "MIT"
},
"node_modules/utility-types": {
......
......@@ -6,8 +6,8 @@
"bundle": "spago bundle --module Main --outfile dist/bundle.min.js --minify --source-maps",
"build-dev": "spago bundle --module Main --outfile dist/bundle.min.js",
"watch": "watchexec -e purs -- npm run build-dev",
"css": "$npm_execpath css-themes",
"css-themes": "$npm_execpath css-default-theme && $npm_execpath css-dark-theme && $npm_execpath css-darkster-theme && $npm_execpath css-greyson-theme && $npm_execpath css-herbie-theme && $npm_execpath css-monotony-theme",
"css": "npm run css-themes",
"css-themes": "npm run css-default-theme && npm run css-dark-theme && npm run css-darkster-theme && npm run css-greyson-theme && npm run css-herbie-theme && npm run css-monotony-theme",
"css-default-theme": "sass src/sass/themes/default.scss:dist/styles/bootstrap-default.css",
"css-dark-theme": "cp node_modules/bootstrap-dark/src/bootstrap-dark.css dist/styles/bootstrap-dark.css",
"css-darkster-theme": "sass src/sass/themes/darkster.scss:dist/styles/bootstrap-darkster.css",
......@@ -20,7 +20,8 @@
"server-ssl": "ssl-serve --ssl dist",
"test": "spago test",
"dev": "concurrently \"npm run watch\" \"npm run serve\" ",
"serve": "npx parcel serve --dist-dir ./dist/dev/bundle ./dist/dev/index.html"
"serve": "npx parcel serve --dist-dir ./dist/dev/bundle ./dist/dev/index.html",
"prepare": "husky"
},
"dependencies": {
"@fontsource/crete-round": "~5.0.12",
......@@ -55,9 +56,11 @@
"http-proxy-middleware": "^3.0.3",
"immer": "~9.0.5",
"isomorphic-unfetch": "~3.1.0",
"jszip": "^3.10.1",
"markdown-it": "~13.0.1",
"minify": "^11.3.0",
"prop-types": "~15.6.2",
"purs-tidy": "^0.11.0",
"react": "~18.2.0",
"react-awesome-popover": "~6.1.1",
"react-bootstrap": "~1.5.2",
......@@ -75,6 +78,7 @@
"@babel/preset-react": "~7.24.1",
"@getgauge/cli": "~1.4.0",
"esbuild": "~0.21.1",
"husky": "^9.1.7",
"parcel": "~2.8.2",
"process": "^0.11.10",
"react-testing-library": "~8.0.1",
......
module Gargantext.AsyncTasks (
Task
module Gargantext.AsyncTasks
( Task
, TaskList
, Storage(..)
, insert
......@@ -8,8 +8,7 @@ module Gargantext.AsyncTasks (
-- , asyncTaskTTriggersAppReload
-- , asyncTaskTTriggersTreeReload
-- , asyncTaskTTriggersMainPageReload
)
where
) where
import Gargantext.Prelude
......@@ -18,7 +17,7 @@ import Data.Array as A
import Data.Either (Either(..))
import Data.Map as Map
import Data.Maybe (Maybe(..), maybe, fromMaybe)
import Data.Monoid (class Monoid)
import Data.Monoid (class Monoid)
import Data.Semigroup (class Semigroup)
import Data.Tuple (Tuple(..))
import Effect (Effect)
......@@ -33,25 +32,27 @@ import Simple.JSON as JSON
import Toestand as T
import Web.Storage.Storage as WSS
type Task = GT.WorkerTask
type TaskList = Array Task
newtype Storage = Storage (Map.Map GT.NodeID TaskList)
derive newtype instance Semigroup Storage
derive newtype instance Monoid Storage
instance JSON.ReadForeign Storage where
readImpl f = do
m <- GUJ.readMapInt f
pure $ Storage m
instance JSON.WriteForeign Storage where
writeImpl (Storage s) = JSON.writeImpl $ FO.fromFoldable arr
where
arr :: Array (Tuple String TaskList)
arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s)
arr :: Array (Tuple String TaskList)
arr = (\(Tuple k v) -> Tuple (show k) v) <$> (Map.toUnfoldable s)
modifyTaskBox :: (Storage -> Storage) -> T.Box Storage -> Effect Unit
modifyTaskBox f box = T.modify_ f box
-- modifyAsyncTasks (const newS)
-- modifyAsyncTasks (const newS)
getTasks :: GT.NodeID -> Storage -> TaskList
getTasks nodeId (Storage storage) = fromMaybe [] $ Map.lookup nodeId storage
......@@ -66,16 +67,16 @@ removeTaskFromList :: TaskList -> Task -> TaskList
removeTaskFromList ts (GT.WorkerTask { message_id }) =
A.filter (\(GT.WorkerTask { message_id: message_id' }) -> message_id /= message_id') ts
type ReductorProps = (
reloadForest :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, storage :: Storage
type ReductorProps =
( reloadForest :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, storage :: Storage
)
insert :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
insert id task storageBox = modifyTaskBox newStorage storageBox
where
newStorage (Storage s) = Storage $ Map.alter (maybe (Just [task]) (\ts -> Just $ A.nub $ A.cons task ts)) id s
newStorage (Storage s) = Storage $ Map.alter (maybe (Just [ task ]) (\ts -> Just $ A.nub $ A.cons task ts)) id s
finish :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
finish id task storage = remove id task storage
......@@ -83,8 +84,7 @@ finish id task storage = remove id task storage
remove :: GT.NodeID -> Task -> T.Box Storage -> Effect Unit
remove id task storageBox = modifyTaskBox newStorage storageBox
where
newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s
newStorage (Storage s) = Storage $ Map.alter (maybe Nothing $ (\ts -> Just $ removeTaskFromList ts task)) id s
-- AsyncTaskWithType is deprecated, but we leave these functions here,
-- becuase they're a useful reference
......@@ -112,7 +112,6 @@ remove id task storageBox = modifyTaskBox newStorage storageBox
-- asyncTaskTTriggersTreeReload :: GT.AsyncTaskWithType -> Boolean
-- asyncTaskTTriggersTreeReload (GT.AsyncTaskWithType { typ }) = asyncTaskTriggersTreeReload typ
-- With push-based notifications, it doesn't make sense to store jobs in localStorage
-- readAsyncTasks :: Effect Storage
-- readAsyncTasks = R2.loadLocalStorageState' R2.asyncTasksKey mempty
......
......@@ -26,30 +26,31 @@ type Props =
type AnnotationMenu =
( closeCallback :: Unit -> Effect Unit
, redrawMenu :: T.Box Boolean
, x :: Number
, y :: Number
, list :: Maybe TermList
, menuType :: MenuType
, setList :: TermList -> Effect Unit -- not a state hook setter
, redrawMenu :: T.Box Boolean
, x :: Number
, y :: Number
, list :: Maybe TermList
, menuType :: MenuType
, setList :: TermList -> Effect Unit -- not a state hook setter
)
annotationMenu :: R2.Leaf Props
annotationMenu = R2.leaf annotationMenuCpt
annotationMenuCpt :: R.Component Props
annotationMenuCpt = here.component "main" cpt where
annotationMenuCpt = here.component "main" cpt
where
cpt { menuRef } _ = do
-- Render
pure $
R2.fromMaybe (R.readRef menuRef) \props' ->
B.contextMenu
{ x: props'.x
, y: props'.y
, closeCallback: props'.closeCallback
} $
(addToList props') <$> [ MapTerm, CandidateTerm, StopTerm ]
{ x: props'.x
, y: props'.y
, closeCallback: props'.closeCallback
}
$ (addToList props')
<$> [ MapTerm, CandidateTerm, StopTerm ]
--------------------------------------------------------------------------
......@@ -75,33 +76,29 @@ annotationMenuCpt = here.component "main" cpt where
-- click _ = setList t
addToList :: Record AnnotationMenu -> TermList -> R.Element
addToList {list: Just t', menuType} t
addToList { list: Just t', menuType } t
| t == t' =
B.contextMenuItem
{ callback: const R.nothing
, status: Disabled
}
[
B.icon
{ name: "circle"
, className: "mr-2 disabled-term"
}
,
H.text (label t menuType)
]
B.contextMenuItem
{ callback: const R.nothing
, status: Disabled
}
[ B.icon
{ name: "circle"
, className: "mr-2 disabled-term"
}
, H.text (label t menuType)
]
addToList {menuType, setList} t =
B.contextMenuItem
addToList { menuType, setList } t =
B.contextMenuItem
{ callback: const $ setList t }
[
B.icon
{ name: "circle"
, className: "mr-2 " <> termClass t
}
,
H.text (label t menuType)
[ B.icon
{ name: "circle"
, className: "mr-2 " <> termClass t
}
, H.text (label t menuType)
]
label :: TermList -> MenuType -> String
label t NewNgram = "Add to " <> (toLower $ termListName t)
label t NewNgram = "Add to " <> (toLower $ termListName t)
label t SetTermListItem = "Change to " <> (toLower $ termListName t)
......@@ -2,8 +2,7 @@ module Gargantext.Components.Annotation.Types
( MenuType(..)
, termClass
, ModeType(..)
)
where
) where
import Gargantext.Prelude
......@@ -16,6 +15,7 @@ import Gargantext.Types (TermList(..))
---------------------------------------------------------
data MenuType = NewNgram | SetTermListItem
derive instance Generic MenuType _
instance Eq MenuType where
eq = genericEq
......@@ -24,8 +24,8 @@ instance Eq MenuType where
termClass :: TermList -> String
termClass CandidateTerm = "candidate-term"
termClass MapTerm = "graph-term"
termClass StopTerm = "stop-term"
termClass MapTerm = "graph-term"
termClass StopTerm = "stop-term"
---------------------------------------------------------
......@@ -34,11 +34,15 @@ data ModeType
| AdditionMode
derive instance Generic ModeType _
instance Eq ModeType where eq = genericEq
instance Show ModeType where show = genericShow
instance Eq ModeType where
eq = genericEq
instance Show ModeType where
show = genericShow
instance Read ModeType where
read :: String -> Maybe ModeType
read = case _ of
"EditionMode" -> Just EditionMode
"EditionMode" -> Just EditionMode
"AdditionMode" -> Just AdditionMode
_ -> Nothing
_ -> Nothing
......@@ -25,11 +25,12 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.App"
app :: R2.Leaf ()
app = R2.leaf appCpt
appCpt :: R.Component ()
appCpt = here.component "container" cpt where
appCpt = here.component "container" cpt
where
cpt _ _ = do
-- | States
-- |
......@@ -55,14 +56,18 @@ type HydrateStoreProps =
hydrateStore :: R2.Leaf HydrateStoreProps
hydrateStore = R2.leaf hydrateStoreCpt
hydrateStoreCpt :: R.Component HydrateStoreProps
hydrateStoreCpt = here.component "hydrateStore" cpt where
cpt { cacheParams
} _ = do
hydrateStoreCpt = here.component "hydrateStore" cpt
where
cpt
{ cacheParams
}
_ = do
-- | Computed
-- |
wsNotification <- RU.hook $ \_ -> NotificationsT.emptyWSNotification
(state :: Record AppStore.State) <- pure $
-- (cache options)
{ expandTableEdition: getter _.expandTableEdition cacheParams
......@@ -73,20 +78,20 @@ hydrateStoreCpt = here.component "hydrateStore" cpt where
-- | Render
-- |
pure $
AppStore.provide
state
[
mainApp
{}
]
state
[ mainApp
{}
]
--------------------------------------------------------------
mainApp :: R2.Leaf ()
mainApp = R2.leaf mainAppCpt
mainAppCpt :: R.Component ()
mainAppCpt = here.component "main" cpt where
mainAppCpt = here.component "main" cpt
where
cpt _ _ = do
boxes <- AppStore.use
-- tasks <- T.useBox Nothing -- storage for asynchronous tasks reductor
......@@ -100,7 +105,7 @@ mainAppCpt = here.component "main" cpt where
-- R.useEffectOnce' $ do
-- tasksStorage <- GAT.readAsyncTasks
-- T.write_ tasksStorage boxes.tasks
-- R.useEffectOnce' $ do
-- T.write (Just tasksReductor) tasks
R.useEffectOnce' $ do
......@@ -117,10 +122,10 @@ mainAppCpt = here.component "main" cpt where
wsProto <- Notifications.wsProtocol
h <- host
Notifications.connect ws (wsProto <> "://" <> h <> "/ws") session
-- T.write_ ws boxes.wsNotification
-- NOTE: Dummy subscription
-- let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree (-1)) "some-uuid" (\_ -> here.log "callback!")
-- Notifications.performAction ws action
-- T.write_ ws boxes.wsNotification
-- NOTE: Dummy subscription
-- let action = NotificationsT.InsertCallback (NotificationsT.UpdateTree (-1)) "some-uuid" (\_ -> here.log "callback!")
-- Notifications.performAction ws action
-- Store in the box the current backend, as
-- derived from the href, before the window gets
......@@ -130,4 +135,4 @@ mainAppCpt = here.component "main" cpt where
T.write_ mLoc boxes.backend
useHashRouter Router.router boxes.route -- Install router to window
pure $ router { boxes } -- Render router component
pure $ router { boxes } -- Render router component
......@@ -9,7 +9,6 @@ module Gargantext.Components.App.Store
, Boxes
) where
import Gargantext.Prelude
import Data.Map (Map)
......@@ -37,100 +36,99 @@ import Record as Record
import Toestand as T
import Unsafe.Coerce (unsafeCoerce)
here :: R2.Here
here = R2.here "Gargantext.Components.App.Store"
type Store =
( backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, expandTableEdition :: T.Box Boolean
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, loginRedirect :: T.Box (Maybe (Tuple String ID))
, pinnedTreeId :: T.Box (Map String Int)
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, showSearch :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsSP.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
, wsNotification :: T.Box Notifications.WSNotification
( backend :: T.Box (Maybe Backend)
, errors :: T.Box (Array FrontendError)
, expandTableEdition :: T.Box Boolean
, forestOpen :: T.Box OpenNodes
, graphVersion :: T2.ReloadS
, handed :: T.Box Handed
, lang :: T.Box Lang.LandingLang
, loginRedirect :: T.Box (Maybe (Tuple String ID))
, pinnedTreeId :: T.Box (Map String Int)
, reloadForest :: T2.ReloadS
, reloadMainPage :: T2.ReloadS
, reloadRoot :: T2.ReloadS
, route :: T.Box AppRoute
, session :: T.Box (Maybe Session)
, sessions :: T.Box Sessions
, showCorpus :: T.Box Boolean
, showLogin :: T.Box Boolean
, showTree :: T.Box Boolean
, showSearch :: T.Box Boolean
, sidePanelLists :: T.Box (Maybe (Record ListsSP.SidePanel))
, sidePanelTexts :: T.Box (Maybe (Record TextsT.SidePanel))
, sidePanelState :: T.Box SidePanelState
, tasks :: T.Box GAT.Storage
, theme :: T.Box Themes.Theme
, tileAxisXList :: T.Box (Array (Record Tile))
, tileAxisYList :: T.Box (Array (Record Tile))
, wsNotification :: T.Box Notifications.WSNotification
)
type State =
( backend :: Maybe Backend
, errors :: Array FrontendError
, expandTableEdition :: Boolean
, forestOpen :: OpenNodes
, graphVersion :: T2.Reload
, handed :: Handed
, lang :: Lang.LandingLang
, loginRedirect :: Maybe (Tuple String ID)
, pinnedTreeId :: Map String Int
, reloadForest :: T2.Reload
, reloadMainPage :: T2.Reload
, reloadRoot :: T2.Reload
, route :: AppRoute
, session :: Maybe Session
, sessions :: Sessions
, showCorpus :: Boolean
, showLogin :: Boolean
, showTree :: Boolean
, showSearch :: Boolean
, sidePanelLists :: Maybe (Record ListsSP.SidePanel)
, sidePanelTexts :: Maybe (Record TextsT.SidePanel)
, sidePanelState :: SidePanelState
, tasks :: GAT.Storage
, theme :: Themes.Theme
, tileAxisXList :: Array (Record Tile)
, tileAxisYList :: Array (Record Tile)
, wsNotification :: Notifications.WSNotification
( backend :: Maybe Backend
, errors :: Array FrontendError
, expandTableEdition :: Boolean
, forestOpen :: OpenNodes
, graphVersion :: T2.Reload
, handed :: Handed
, lang :: Lang.LandingLang
, loginRedirect :: Maybe (Tuple String ID)
, pinnedTreeId :: Map String Int
, reloadForest :: T2.Reload
, reloadMainPage :: T2.Reload
, reloadRoot :: T2.Reload
, route :: AppRoute
, session :: Maybe Session
, sessions :: Sessions
, showCorpus :: Boolean
, showLogin :: Boolean
, showTree :: Boolean
, showSearch :: Boolean
, sidePanelLists :: Maybe (Record ListsSP.SidePanel)
, sidePanelTexts :: Maybe (Record TextsT.SidePanel)
, sidePanelState :: SidePanelState
, tasks :: GAT.Storage
, theme :: Themes.Theme
, tileAxisXList :: Array (Record Tile)
, tileAxisYList :: Array (Record Tile)
, wsNotification :: Notifications.WSNotification
)
options :: Notifications.WSNotification -> Record State
options wsNotification =
{ wsNotification } `Record.merge`
{ backend : Nothing
, errors : []
, expandTableEdition : false
, forestOpen : OpenNodes $ Set.empty
, graphVersion : T2.newReload
, handed : RightHanded
, lang : Lang.LL_EN
, loginRedirect : Nothing
, pinnedTreeId : Map.empty
, reloadForest : T2.newReload
, reloadMainPage : T2.newReload
, reloadRoot : T2.newReload
, route : Home
, session : Nothing
, sessions : Sessions.empty
, showCorpus : false
, showLogin : false
, showTree : true
, showSearch : false
, sidePanelLists : ListsSP.initialSidePanel
, sidePanelTexts : TextsT.initialSidePanel
, sidePanelState : InitialClosed
, tasks : mempty
, theme : Themes.defaultTheme
, tileAxisXList : mempty
, tileAxisYList : mempty
}
{ backend: Nothing
, errors: []
, expandTableEdition: false
, forestOpen: OpenNodes $ Set.empty
, graphVersion: T2.newReload
, handed: RightHanded
, lang: Lang.LL_EN
, loginRedirect: Nothing
, pinnedTreeId: Map.empty
, reloadForest: T2.newReload
, reloadMainPage: T2.newReload
, reloadRoot: T2.newReload
, route: Home
, session: Nothing
, sessions: Sessions.empty
, showCorpus: false
, showLogin: false
, showTree: true
, showSearch: false
, sidePanelLists: ListsSP.initialSidePanel
, sidePanelTexts: TextsT.initialSidePanel
, sidePanelState: InitialClosed
, tasks: mempty
, theme: Themes.defaultTheme
, tileAxisXList: mempty
, tileAxisYList: mempty
}
context :: R.Context (Record Store)
context = R.createContext $ unsafeCoerce unit
......
......@@ -12,16 +12,14 @@ import React.DOM (div')
import Reactix as R
import Reactix.DOM.HTML as H
here :: R2.Here
here = R2.here "Gargantext.Components.AutoUpdate"
data Action = Update
type PropsRow =
( duration :: Int
, effect :: Effect Unit
, effect :: Effect Unit
)
type Props = { | PropsRow }
......@@ -31,17 +29,19 @@ type State = { intervalId :: Maybe IntervalId }
autoUpdateClass :: ReactClass { children :: Children | PropsRow }
autoUpdateClass =
React.component "AutoUpdate"
(\this -> do
pure { state: {intervalId: Nothing}
, render: pure $ div' []
, componentDidMount: do
{duration, effect} <- React.getProps this
intervalId <- setInterval duration effect
React.setState this {intervalId: Just intervalId}
, componentWillUnmount: do
{intervalId} <- React.getState this
traverse_ clearInterval intervalId
})
( \this -> do
pure
{ state: { intervalId: Nothing }
, render: pure $ div' []
, componentDidMount: do
{ duration, effect } <- React.getProps this
intervalId <- setInterval duration effect
React.setState this { intervalId: Just intervalId }
, componentWillUnmount: do
{ intervalId } <- React.getState this
traverse_ clearInterval intervalId
}
)
autoUpdateElt :: Props -> ReactElement
autoUpdateElt props = React.createElement autoUpdateClass props []
......@@ -52,18 +52,18 @@ autoUpdate props = R.createElement autoUpdateCpt props []
autoUpdateCpt :: R.Component PropsRow
autoUpdateCpt = here.component "autoUpdate" cpt
where
cpt { duration, effect } _ = do
intervalRef <- R.useRef Nothing
cpt { duration, effect } _ = do
intervalRef <- R.useRef Nothing
R.useEffect' $ do
let mInterval = R.readRef intervalRef
case mInterval of
Nothing -> do
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
Just intervalId -> do
clearInterval intervalId
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
R.useEffect' $ do
let mInterval = R.readRef intervalRef
case mInterval of
Nothing -> do
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
Just intervalId -> do
clearInterval intervalId
intervalId <- setInterval duration effect
R.setRef intervalRef $ Just intervalId
pure $ H.div {} []
pure $ H.div {} []
......@@ -14,23 +14,22 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Toestand as T
type Props =
( defaultSlot :: R.Element
, cloakSlot :: R.Element
, isDisplayed :: Boolean
( defaultSlot :: R.Element
, cloakSlot :: R.Element
, isDisplayed :: Boolean
| Options
)
type Options =
( idlingPhaseDuration :: Maybe Int -- Milliseconds
( idlingPhaseDuration :: Maybe Int -- Milliseconds
, sustainingPhaseDuration :: Maybe Int -- Milliseconds
)
options :: Record Options
options =
{ idlingPhaseDuration : Nothing
, sustainingPhaseDuration : Nothing
{ idlingPhaseDuration: Nothing
, sustainingPhaseDuration: Nothing
}
cname :: String
......@@ -111,15 +110,17 @@ cname = "b-cloak"
-- | ```
cloak :: forall r. R2.OptLeaf Options Props r
cloak = R2.optLeaf component options
component :: R.Component Props
component = R.hooksComponent cname cpt where
component = R.hooksComponent cname cpt
where
cpt props _ = do
-- State
phase /\ phaseBox <- R2.useBox' (Idle :: Phase)
-- Computed
let
canCloakBeDisplayed = elem phase [ Sustain, Wait ]
canCloakBeDisplayed = elem phase [ Sustain, Wait ]
canContentBeDisplayed = elem phase [ Display ]
-- Behaviors
......@@ -128,55 +129,46 @@ component = R.hooksComponent cname cpt where
execDisplayingPhaseOr :: (Unit -> Effect Unit) -> Effect Unit
execDisplayingPhaseOr thunk =
if props.isDisplayed
then T.write_ Display phaseBox
if props.isDisplayed then T.write_ Display phaseBox
else thunk unit
execWaitingPhase :: Unit -> Effect Unit
execWaitingPhase _ = execDisplayingPhaseOr $ const $
T.write_ Wait phaseBox
T.write_ Wait phaseBox
execSustainingPhase :: Unit -> Effect Unit
execSustainingPhase _ = execDisplayingPhaseOr $ const $
T.write_ Sustain phaseBox
execSustainingPhase _ = execDisplayingPhaseOr $ const
$ T.write_ Sustain phaseBox
<* setTimeout
(fromMaybe 0 props.sustainingPhaseDuration)
(execWaitingPhase unit)
<* setTimeout
(fromMaybe 0 props.sustainingPhaseDuration)
(execWaitingPhase unit)
execIdlingPhase :: Unit -> Effect Unit
execIdlingPhase _ = execDisplayingPhaseOr $ const $
execIdlingPhase _ = execDisplayingPhaseOr $ const
$ T.write_ Idle phaseBox
T.write_ Idle phaseBox
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
<* setTimeout
(fromMaybe 0 props.idlingPhaseDuration)
(execSustainingPhase unit)
-- Effects
useFirstEffect' $ execIdlingPhase unit
R.useEffect2' props.isDisplayed phase $
if (props.isDisplayed && phase == Wait)
then T.write_ Display phaseBox
if (props.isDisplayed && phase == Wait) then T.write_ Display phaseBox
else pure unit
-- Render
pure $
R.fragment
[
R2.when canCloakBeDisplayed props.cloakSlot
,
R2.when canContentBeDisplayed props.defaultSlot
]
[ R2.when canCloakBeDisplayed props.cloakSlot
, R2.when canContentBeDisplayed props.defaultSlot
]
data Phase =
Idle
data Phase
= Idle
| Sustain
| Wait
| Display
......
......@@ -10,22 +10,21 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options )
type Props = (| Options)
type Options =
( status :: ComponentStatus
, className :: String
, variant :: Variant
( status :: ComponentStatus
, className :: String
, variant :: Variant
)
options :: Record Options
options =
{ variant : Light
, status : Enabled
, className : ""
{ variant: Light
, status: Enabled
, className: ""
}
-- | Component for a Ripple Effect on DOMElement click (without JavaScript)
-- |
-- | ```
......@@ -42,10 +41,14 @@ componentName :: String
componentName = "b-ripple"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ variant
, status
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ variant
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -57,12 +60,11 @@ component = R.hooksComponent componentName cpt where
, componentName <> "--" <> show variant
]
-- Render
pure $
R.fragment $
[
H.div
{ className }
[]
]
<> children
pure
$ R.fragment
$
[ H.div
{ className }
[]
]
<> children
module Gargantext.Components.Bootstrap.Caveat(caveat) where
module Gargantext.Components.Bootstrap.Caveat (caveat) where
import Gargantext.Prelude
......@@ -8,16 +8,16 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options )
type Props = (| Options)
type Options =
( className :: String
, variant :: Variant
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, variant : Light
{ className: ""
, variant: Light
}
-- | Smart reference to the <alert> Bootstrap component,
......@@ -34,7 +34,8 @@ bootstrapName :: String
bootstrapName = "alert"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props children = do
-- Computed
let
......@@ -49,7 +50,6 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
children
{ className }
children
......@@ -15,14 +15,14 @@ type Props =
)
type Options =
( className :: String
, contentClassName :: String
( className :: String
, contentClassName :: String
)
options :: Record Options
options =
{ className : ""
, contentClassName : ""
{ className: ""
, contentClassName: ""
}
-- | Component simulating a native <fieldset>
......@@ -34,9 +34,13 @@ componentName :: String
componentName = "b-fieldset"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ titleSlot
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ titleSlot
}
children = do
-- Computed
let
className = intercalate " "
......@@ -55,15 +59,12 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
H.section
{ className }
[
H.div
{ className: componentName <> "__legend" }
[ titleSlot ]
,
H.div
{ className: contentClassName}
children
]
{ className }
[ H.div
{ className: componentName <> "__legend" }
[ titleSlot ]
, H.div
{ className: contentClassName }
children
]
module Gargantext.Components.Bootstrap.Preloader(preloader) where
module Gargantext.Components.Bootstrap.Preloader (preloader) where
import Gargantext.Prelude
......@@ -8,14 +8,14 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options)
type Props = (| Options)
type Options =
( className :: String
)
options :: Record Options
options =
{ className : ""
{ className: ""
}
-- | Structural Component wrapping our <Spinner.BorderTheme> within
......@@ -27,7 +27,8 @@ componentName :: String
componentName = "b-preloader"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -39,10 +40,8 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
[
spinner
{ className: componentName <> "__spinner" }
]
{ className }
[ spinner
{ className: componentName <> "__spinner" }
]
module Gargantext.Components.Bootstrap.ProgressBar(progressBar) where
module Gargantext.Components.Bootstrap.ProgressBar (progressBar) where
import Gargantext.Prelude
......@@ -8,21 +8,21 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( value :: Number
type Props =
( value :: Number
, waitingTextClass :: String
| Options
)
type Options =
( className :: String
, variant :: Variant
, variant :: Variant
)
options :: Record Options
options =
{ className : ""
, variant : Primary
{ className: ""
, variant: Primary
}
-- | Structural Component for the Bootsrap "Progress Bar"
......@@ -38,7 +38,8 @@ bootstrapName :: String
bootstrapName = "progress"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -52,28 +53,25 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
[
H.div
{ className: intercalate " "
[ "progress-bar"
, "bg-" <> show props.variant
]
, style: { width: (show props.value) <> "%" }
, role: "progress-bar"
, "aria-valuenow": show $ props.value
, "aria-valuemin": "0"
, "aria-valuemax": "100"
}
[]
,
H.div
{ className: intercalate " "
[ "progress-text"
, props.waitingTextClass
]
}
[ H.text "Waiting task..." ]
]
{ className }
[ H.div
{ className: intercalate " "
[ "progress-bar"
, "bg-" <> show props.variant
]
, style: { width: (show props.value) <> "%" }
, role: "progress-bar"
, "aria-valuenow": show $ props.value
, "aria-valuemin": "0"
, "aria-valuemax": "100"
}
[]
, H.div
{ className: intercalate " "
[ "progress-text"
, props.waitingTextClass
]
}
[ H.text "Waiting task..." ]
]
module Gargantext.Components.Bootstrap.Spinner(spinner) where
module Gargantext.Components.Bootstrap.Spinner (spinner) where
import Gargantext.Prelude
......@@ -8,16 +8,16 @@ import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options)
type Props = (| Options)
type Options =
( theme :: SpinnerTheme
( theme :: SpinnerTheme
, className :: String
)
options :: Record Options
options =
{ theme : BorderTheme
, className : ""
{ theme: BorderTheme
, className: ""
}
-- | Structural Component for the Bootstrap spinner
......@@ -33,7 +33,8 @@ bootstrapName :: String
bootstrapName = "spinner"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
component = R.hooksComponent componentName cpt
where
cpt props _ = do
-- Computed
let
......@@ -47,7 +48,6 @@ component = R.hooksComponent componentName cpt where
]
-- Render
pure $
H.div
{ className }
[]
{ className }
[]
......@@ -2,41 +2,56 @@ module Gargantext.Components.Bootstrap
( module Exports
) where
import Gargantext.Components.Bootstrap.BaseModal(baseModal) as Exports
import Gargantext.Components.Bootstrap.Button(button) as Exports
import Gargantext.Components.Bootstrap.ButtonGroup(buttonGroup) as Exports
import Gargantext.Components.Bootstrap.Caveat(caveat) as Exports
import Gargantext.Components.Bootstrap.BaseModal (baseModal) as Exports
import Gargantext.Components.Bootstrap.Button (button) as Exports
import Gargantext.Components.Bootstrap.ButtonGroup (buttonGroup) as Exports
import Gargantext.Components.Bootstrap.Caveat (caveat) as Exports
import Gargantext.Components.Bootstrap.Cloak (cloak) as Exports
import Gargantext.Components.Bootstrap.ContextMenu(contextMenu, contextMenuItem) as Exports
import Gargantext.Components.Bootstrap.Fieldset(fieldset) as Exports
import Gargantext.Components.Bootstrap.FormCheckbox(formCheckbox) as Exports
import Gargantext.Components.Bootstrap.FormInput(formInput) as Exports
import Gargantext.Components.Bootstrap.FormSelect(formSelect, formSelect') as Exports
import Gargantext.Components.Bootstrap.FormTextarea(formTextarea) as Exports
import Gargantext.Components.Bootstrap.Icon(icon) as Exports
import Gargantext.Components.Bootstrap.IconButton(iconButton) as Exports
import Gargantext.Components.Bootstrap.Preloader(preloader) as Exports
import Gargantext.Components.Bootstrap.ProgressBar(progressBar) as Exports
import Gargantext.Components.Bootstrap.Ripple(ripple) as Exports
import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports
import Gargantext.Components.Bootstrap.Tabs(tabs) as Exports
import Gargantext.Components.Bootstrap.Tooltip(tooltip, TooltipBindingProps, tooltipBind, tooltipBind', tooltipContainer) as Exports
import Gargantext.Components.Bootstrap.Wad(wad, wad', wad_) as Exports
import Gargantext.Components.Bootstrap.ContextMenu (contextMenu, contextMenuItem) as Exports
import Gargantext.Components.Bootstrap.Fieldset (fieldset) as Exports
import Gargantext.Components.Bootstrap.FormCheckbox (formCheckbox) as Exports
import Gargantext.Components.Bootstrap.FormInput (formInput) as Exports
import Gargantext.Components.Bootstrap.FormSelect (formSelect, formSelect') as Exports
import Gargantext.Components.Bootstrap.FormTextarea (formTextarea) as Exports
import Gargantext.Components.Bootstrap.Icon (icon) as Exports
import Gargantext.Components.Bootstrap.IconButton (iconButton) as Exports
import Gargantext.Components.Bootstrap.Preloader (preloader) as Exports
import Gargantext.Components.Bootstrap.ProgressBar (progressBar) as Exports
import Gargantext.Components.Bootstrap.Ripple (ripple) as Exports
import Gargantext.Components.Bootstrap.Spinner (spinner) as Exports
import Gargantext.Components.Bootstrap.Tabs (tabs) as Exports
import Gargantext.Components.Bootstrap.Tooltip (tooltip, TooltipBindingProps, tooltipBind, tooltipBind', tooltipContainer) as Exports
import Gargantext.Components.Bootstrap.Wad (wad, wad', wad_) as Exports
import Gargantext.Components.Bootstrap.Shortcut(
div', div_
, h1', h1_
, h2', h2_
, h3', h3_
, h4', h4_
, h5', h5_
, h6', h6_
, span', span_
, li', li_
, b', b_
, code', code_
, label', label_
, p', p_
, td', td_
, th', th_
import Gargantext.Components.Bootstrap.Shortcut
( div'
, div_
, h1'
, h1_
, h2'
, h2_
, h3'
, h3_
, h4'
, h4_
, h5'
, h5_
, h6'
, h6_
, span'
, span_
, li'
, li_
, b'
, b_
, code'
, code_
, label'
, label_
, p'
, p_
, td'
, td_
, th'
, th_
) as Exports
......@@ -9,7 +9,7 @@
export function _show(window, querySelector, events) {
let $modal = window.$(querySelector);
$modal.modal('show');
console.log('events', events);
//console.log('events', events);
if(events.onHide) {
$modal.on('hidden.bs.modal', events.onHide());
}
......
module Gargantext.Components.Bootstrap.BaseModal
( baseModal
, showModal, hideModal
, showModal
, hideModal
) where
import Gargantext.Prelude
......@@ -23,63 +24,63 @@ import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Bootstrap.BaseModal"
type ModalCallback = Unit -> Effect Unit
type ModalEvents =
( onHide :: ModalCallback
, onShow :: ModalCallback )
, onShow :: ModalCallback
)
foreign import _show :: EffectFn3
Window
String
(Record ModalEvents)
Unit
foreign import _show
:: EffectFn3
Window
String
(Record ModalEvents)
Unit
showModal ::
Window
showModal
:: Window
-> String
-> Record ModalEvents
-> Effect Unit
showModal = runEffectFn3 _show
foreign import _hide :: EffectFn2
Window
String
Unit
foreign import _hide
:: EffectFn2
Window
String
Unit
hideModal ::
Window
hideModal
:: Window
-> String
-> Effect Unit
hideModal = runEffectFn2 _hide
type Props =
( isVisibleBox :: T.Box Boolean
( isVisibleBox :: T.Box Boolean
| Options
)
type Options =
( modalClassName :: String
, title :: Maybe String
, hasCollapsibleBackground :: Boolean
, hasInnerScroll :: Boolean
, noHeader :: Boolean
, noBody :: Boolean -- ie. Bootstrap Body
, size :: ModalSizing
( modalClassName :: String
, title :: Maybe String
, hasCollapsibleBackground :: Boolean
, hasInnerScroll :: Boolean
, noHeader :: Boolean
, noBody :: Boolean -- ie. Bootstrap Body
, size :: ModalSizing
)
options :: Record Options
options =
{ modalClassName : ""
, title : Nothing
, hasCollapsibleBackground : true
, hasInnerScroll : false
, noHeader : false
, noBody : false
, size : MediumModalSize
{ modalClassName: ""
, title: Nothing
, hasCollapsibleBackground: true
, hasInnerScroll: false
, noHeader: false
, noBody: false
, size: MediumModalSize
}
componentName :: String
......@@ -99,16 +100,19 @@ baseModal :: forall r. R2.OptComponent Options Props r
baseModal = R2.optComponent component options
component :: R.Memo Props
component = R.memo' $ R.hooksComponent componentName cpt where
cpt props@{ isVisibleBox
, title
, hasCollapsibleBackground
, hasInnerScroll
, noHeader
, noBody
, size
} children
= do
component = R.memo' $ R.hooksComponent componentName cpt
where
cpt
props@
{ isVisibleBox
, title
, hasCollapsibleBackground
, hasInnerScroll
, noHeader
, noBody
, size
}
children = do
-- | States
-- |
isVisible <- R2.useLive' isVisibleBox
......@@ -131,19 +135,18 @@ component = R.memo' $ R.hooksComponent componentName cpt where
-- | Hooks
-- |
let modalEvents = {
onHide: \_ -> T.write_ false isVisibleBox
let
modalEvents =
{ onHide: \_ -> T.write_ false isVisibleBox
, onShow: \_ -> T.write_ true isVisibleBox
}
}
useUpdateEffect1' isVisible
if isVisible
then showModal window selector modalEvents
if isVisible then showModal window selector modalEvents
else hideModal window selector
-- | When box is true, show the modal immediately
R.useEffectOnce' $ do
if isVisible
then showModal window selector modalEvents
if isVisible then showModal window selector modalEvents
else hideModal window selector
-- | Behaviors
......@@ -154,82 +157,75 @@ component = R.memo' $ R.hooksComponent componentName cpt where
-- [ Render
-- |
R.createPortal
[
H.div
{ id: id
, className
, tabIndex: "-1"
, key: id
, data:
{ keyboard: "true"
, backdrop: hasCollapsibleBackground ?
"true" $
"static"
}
}
[
-- Overlay fixing collapsable click event
R2.when (hasCollapsibleBackground) $
H.div
{ className: componentName <> "__overlay"
, on: { click: onCloseButtonClick }
}
[]
,
H.div
{ className: intercalate " "
-- Bootstrap classNames
[ "modal-dialog"
, show size
, "modal-dialog-centered"
, hasInnerScroll ? "modal-dialog-scrollable" $ ""
-- provided custom className
, props.modalClassName
]
[ H.div
{ id: id
, className
, tabIndex: "-1"
, key: id
, data:
{ keyboard: "true"
, backdrop: hasCollapsibleBackground
? "true"
$
"static"
}
}
[
H.div
{ className: intercalate " "
[ componentName <> "__content"
, "modal-content"
]
}
[
-- Header
R2.when (not noHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, "modal-header"
]
}
[
R2.fromMaybe (title) \title' ->
H.div
{ className: componentName <> "__header__title" }
[ H.text title' ]
,
iconButton
{ name: "times"
, callback: onCloseButtonClick
, elevation: Level2
, className: componentName <> "__header__close"
}
]
,
-- Body
-- Overlay fixing collapsable click event
R2.when (hasCollapsibleBackground) $
H.div
{ className: componentName <> "__overlay"
, on: { click: onCloseButtonClick }
}
[]
, H.div
{ className: intercalate " "
[ componentName <> "__body"
, noBody ? "" $ "modal-body"
-- Bootstrap classNames
[ "modal-dialog"
, show size
, "modal-dialog-centered"
, hasInnerScroll ? "modal-dialog-scrollable" $ ""
-- provided custom className
, props.modalClassName
]
}
children
]
[ H.div
{ className: intercalate " "
[ componentName <> "__content"
, "modal-content"
]
}
[
-- Header
R2.when (not noHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, "modal-header"
]
}
[ R2.fromMaybe (title) \title' ->
H.div
{ className: componentName <> "__header__title" }
[ H.text title' ]
, iconButton
{ name: "times"
, callback: onCloseButtonClick
, elevation: Level2
, className: componentName <> "__header__close"
}
]
,
-- Body
H.div
{ className: intercalate " "
[ componentName <> "__body"
, noBody ? "" $ "modal-body"
]
}
children
]
]
]
]
]
<$> R2.getPortalHost
......@@ -27,8 +27,8 @@ import Reactix.SyntheticEvent as RE
type Props =
( closeCallback :: Unit -> Effect Unit
, x :: Number
, y :: Number
, x :: Number
, y :: Number
)
contextMenu :: R2.Component Props
......@@ -38,116 +38,114 @@ componentName :: String
componentName = "b-context-menu"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt { closeCallback
, x
, y
} children
= R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
component = R.hooksComponent componentName cpt
where
cpt
{ closeCallback
, x
, y
}
children = R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- | States
-- |
ref <- R.useRef (null :: Nullable DOM.Element)
-- | Hooks
-- |
{ enableScroll, disableScroll } <- useScrollbar
R.useLayoutEffect1 [] do
-- Mount
disableScroll
-- Unmount
pure enableScroll
-- /!\ for some reason we have to use the hook's effect with cleanup
-- function (even if empty)
R.useLayoutEffect1 (R.readRef ref) do
for_ (toMaybe $ R.readRef ref) \el -> do
let rect = Element.boundingRect el
let pos = position { x, y } rect
let style = el .. "style"
let toPixels = show >>> (_ <> "px")
void $ pure $ setProperty' style "left" [ pos.left # toPixels ]
void $ pure $ setProperty' style "top" [ pos.top # toPixels ]
R.nothing # R.thenNothing
-- | Computed
-- |
let
containerId :: String
containerId = componentName <> "-" <> uuid
containerCallback :: forall e. SE.SyntheticEvent e -> Effect Unit
containerCallback e =
let
eventTargetId :: Maybe String
eventTargetId = SE.unsafeEventTarget e # flip DOM.attr "id"
hasClickedOnContainer :: Boolean
hasClickedOnContainer = maybe false (eq containerId) eventTargetId
in
when hasClickedOnContainer $ closeCallback unit
-- | Render
-- |
R.createPortal
[
H.div
{ className: componentName
, on: { click: containerCallback }
, key: uuid
, id: containerId
}
[
H.div
{ className: componentName <> "__inner"
, data: { placement: "right", toggle: "popover" }
, ref
}
children
-- | States
-- |
ref <- R.useRef (null :: Nullable DOM.Element)
-- | Hooks
-- |
{ enableScroll, disableScroll } <- useScrollbar
R.useLayoutEffect1 [] do
-- Mount
disableScroll
-- Unmount
pure enableScroll
-- /!\ for some reason we have to use the hook's effect with cleanup
-- function (even if empty)
R.useLayoutEffect1 (R.readRef ref) do
for_ (toMaybe $ R.readRef ref) \el -> do
let rect = Element.boundingRect el
let pos = position { x, y } rect
let style = el .. "style"
let toPixels = show >>> (_ <> "px")
void $ pure $ setProperty' style "left" [ pos.left # toPixels ]
void $ pure $ setProperty' style "top" [ pos.top # toPixels ]
R.nothing # R.thenNothing
-- | Computed
-- |
let
containerId :: String
containerId = componentName <> "-" <> uuid
containerCallback :: forall e. SE.SyntheticEvent e -> Effect Unit
containerCallback e =
let
eventTargetId :: Maybe String
eventTargetId = SE.unsafeEventTarget e # flip DOM.attr "id"
hasClickedOnContainer :: Boolean
hasClickedOnContainer = maybe false (eq containerId) eventTargetId
in
when hasClickedOnContainer $ closeCallback unit
-- | Render
-- |
R.createPortal
[ H.div
{ className: componentName
, on: { click: containerCallback }
, key: uuid
, id: containerId
}
[ H.div
{ className: componentName <> "__inner"
, data: { placement: "right", toggle: "popover" }
, ref
}
children
]
]
]
<$> R2.getPortalHost
<$> R2.getPortalHost
position ::
{ x :: Number
position
:: { x :: Number
, y :: Number
}
-> DOMRect
-> { left :: Number
, top :: Number
, top :: Number
}
position mouse { width: menuWidth, height: menuHeight } = { left, top }
where
left = if isRight then mouse.x else mouse.x - menuWidth
top = if isAbove then mouse.y else mouse.y - menuHeight
isRight = screenWidth - mouse.x > menuWidth -- is there enough space to show above
isAbove = screenHeight - mouse.y > menuHeight -- is there enough space to show to the right?
screenWidth = window .. "innerWidth"
screenHeight = window .. "innerHeight"
left = if isRight then mouse.x else mouse.x - menuWidth
top = if isAbove then mouse.y else mouse.y - menuHeight
isRight = screenWidth - mouse.x > menuWidth -- is there enough space to show above
isAbove = screenHeight - mouse.y > menuHeight -- is there enough space to show to the right?
screenWidth = window .. "innerWidth"
screenHeight = window .. "innerHeight"
--------------------------------------------------------------
type ItemProps =
( callback :: Unit -> Effect Unit
( callback :: Unit -> Effect Unit
| ItemOptions
)
type ItemOptions =
( className :: String
, status :: ComponentStatus
( className :: String
, status :: ComponentStatus
)
itemOptions :: Record ItemOptions
itemOptions =
{ className : ""
, status : Enabled
{ className: ""
, status: Enabled
}
contextMenuItem :: forall r. R2.OptComponent ItemOptions ItemProps r
......@@ -157,10 +155,14 @@ itemComponentName :: String
itemComponentName = "b-context-menu-item"
itemCpt :: R.Component ItemProps
itemCpt = R.hooksComponent itemComponentName cpt where
cpt props@{ callback
, status
} children = do
itemCpt = R.hooksComponent itemComponentName cpt
where
cpt
props@
{ callback
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -174,24 +176,23 @@ itemCpt = R.hooksComponent itemComponentName cpt where
click = onClick status callback
-- Render
pure $
H.div
{ className
, on: { click }
} $
[
ripple
{ status
, variant: Dark
}
children
]
pure
$ H.div
{ className
, on: { click }
}
$
[ ripple
{ status
, variant: Dark
}
children
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick ::
ComponentStatus
onClick
:: ComponentStatus
-> (Unit -> Effect Unit)
-> RE.SyntheticEvent DE.Event
-> Effect Unit
......
module Gargantext.Components.Bootstrap.Tooltip
( tooltip
, TooltipBindingProps, tooltipBind, tooltipBind'
, TooltipBindingProps
, tooltipBind
, tooltipBind'
, tooltipContainer
) where
......@@ -19,30 +21,29 @@ import Type.Proxy (Proxy(..))
foreign import reactTooltipCpt :: R.Component Props
type Props =
( id :: String
( id :: String
| Options
)
type Options =
( effect :: TooltipEffect
, variant :: Variant
, delayHide :: Int
, delayShow :: Int
, className :: String
, position :: TooltipPosition
( effect :: TooltipEffect
, variant :: Variant
, delayHide :: Int
, delayShow :: Int
, className :: String
, position :: TooltipPosition
)
options :: Record Options
options =
{ effect : SolidEffect
, variant : Dark
, delayHide : 0
, delayShow : 0
, className : ""
, position : AutomaticPosition
{ effect: SolidEffect
, variant: Dark
, delayHide: 0
, delayShow: 0
, className: ""
, position: AutomaticPosition
}
-- | Adapter Component for React Tooltip [1]
-- |
-- |
......@@ -55,30 +56,31 @@ options =
-- |
-- |
-- | https://github.com/wwayne/react-tooltip [1]
tooltip :: forall provided.
CO.Defaults (Record Options) (Record provided) (Record Props)
tooltip
:: forall provided
. CO.Defaults (Record Options) (Record provided) (Record Props)
=> Record provided
-> Array R.Element
-> R.Element
tooltip props = R.rawCreateElement reactTooltipCpt props''
where
props' = CO.defaults options props
props'' = props'
# Record.set
(Proxy :: Proxy "effect")
(show props'.effect)
>>> Record.set
(Proxy :: Proxy "variant")
(show props'.variant)
>>> Record.rename
(Proxy :: Proxy "variant")
(Proxy :: Proxy "type")
>>> Record.set
(Proxy :: Proxy "position")
(show props'.position)
>>> Record.rename
(Proxy :: Proxy "position")
(Proxy :: Proxy "place")
props' = CO.defaults options props
props'' = props'
# Record.set
(Proxy :: Proxy "effect")
(show props'.effect)
>>> Record.set
(Proxy :: Proxy "variant")
(show props'.variant)
>>> Record.rename
(Proxy :: Proxy "variant")
(Proxy :: Proxy "type")
>>> Record.set
(Proxy :: Proxy "position")
(show props'.position)
>>> Record.rename
(Proxy :: Proxy "position")
(Proxy :: Proxy "place")
-------------------------------------------------------------
......@@ -113,11 +115,14 @@ tooltipContainer :: forall r. R2.OptLeaf Options ContainerProps r
tooltipContainer = R2.optLeaf tooltipContainerCpt options
tooltipContainerCpt :: R.Memo ContainerProps
tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt where
cpt props@{ tooltipSlot
, defaultSlot
} _
= R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt
where
cpt
props@
{ tooltipSlot
, defaultSlot
}
_ = R.unsafeHooksEffect (UUID.genUUID >>= pure <<< UUID.toString)
>>= \uuid -> do
-- Computed
let
......@@ -126,14 +131,11 @@ tooltipContainerCpt = R.memo' $ R.hooksComponent "tooltipContainer" cpt where
{ id: uuid }
pure $
R2.fragmentWithKey uuid
[
tooltip
tooltipProps
[ tooltipSlot ]
,
H.span
(tooltipBind uuid)
[ defaultSlot ]
]
[ tooltip
tooltipProps
[ tooltipSlot ]
, H.span
(tooltipBind uuid)
[ defaultSlot ]
]
......@@ -11,20 +11,20 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: Boolean -> Effect Unit
, value :: Boolean
( callback :: Boolean -> Effect Unit
, value :: Boolean
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
( status :: ComponentStatus
, className :: String
)
options :: Record Options
options =
{ status : Enabled
, className : ""
{ status: Enabled
, className: ""
}
-- | Structural Component for an <input type="checkbox">
......@@ -39,10 +39,14 @@ componentName :: String
componentName = "b-form-checkbox"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -55,27 +59,26 @@ component = R.hooksComponent componentName cpt where
change <- pure $ onChange status callback
-- Render
pure $
H.input
{ className
, on: { change }
, type: "checkbox"
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, value: props.value
, checked: props.value
}
{ className
, on: { change }
, type: "checkbox"
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, value: props.value
, checked: props.value
}
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (Boolean -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.checked
if status == Enabled then callback $ (unsafeCoerce event).target.checked
else pure unit
......@@ -11,32 +11,32 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, type :: String
( status :: ComponentStatus
, className :: String
, type :: String
, placeholder :: String
, size :: Sizing
, step :: String
, min :: String
, max :: String
, size :: Sizing
, step :: String
, min :: String
, max :: String
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, type : "text"
, placeholder : ""
, size : MediumSize
, step : ""
, min : ""
, max : ""
{ status: Enabled
, className: ""
, type: "text"
, placeholder: ""
, size: MediumSize
, step: ""
, min: ""
, max: ""
}
-- | Structural Component for the Bootstrap input
......@@ -52,10 +52,14 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -71,31 +75,30 @@ component = R.hooksComponent componentName cpt where
change <- pure $ onChange status callback
-- Render
pure $
H.input
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, type: props.type
, step: props.step
, min: props.min
, max: props.max
, autoComplete: "off"
, value: props.value
}
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, type: props.type
, step: props.step
, min: props.min
, max: props.max
, autoComplete: "off"
, value: props.value
}
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
if status == Enabled then callback $ (unsafeCoerce event).target.value
else pure unit
......@@ -15,26 +15,26 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, type :: String
( status :: ComponentStatus
, className :: String
, type :: String
, placeholder :: String
, size :: Sizing
, size :: Sizing
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, type : "text"
, placeholder : ""
, size : MediumSize
{ status: Enabled
, className: ""
, type: "text"
, placeholder: ""
, size: MediumSize
}
-- | Structural Component for the Bootstrap select
......@@ -67,10 +67,14 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} children = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
}
children = do
-- Computed
let
className = intercalate " "
......@@ -91,30 +95,29 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
R2.select
{ className
, on: { change }
, disabled: elem status [ Disabled, Idled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: props.value
}
children
{ className
, on: { change }
, disabled: elem status [ Disabled, Idled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: props.value
}
children
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange :: forall event.
ComponentStatus
onChange
:: forall event
. ComponentStatus
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
if status == Enabled then callback $ (unsafeCoerce event).target.value
else R.nothing
-----------------------------------------------------------------------
type AnyTypeProps a =
......@@ -139,24 +142,30 @@ type AnyTypeProps a =
-- | (?) Note that HTML option tags will be automatically added thanks to
-- | to the provided `list` prop. You can add additional HTML option within
-- | the `children` prop
formSelect' :: forall r a.
Show a
formSelect'
:: forall r a
. Show a
=> R2.OptComponent Options (AnyTypeProps a) r
formSelect' = R2.optComponent component' options
component' :: forall a.
Show a
component'
:: forall a
. Show a
=> R.Component (AnyTypeProps a)
component' = R.hooksComponent (componentName <> "__helper") cpt where
cpt props@{ callback
, list
, status
, value
} children = do
component' = R.hooksComponent (componentName <> "__helper") cpt
where
cpt
props@
{ callback
, list
, status
, value
}
children = do
-- Computed
let
className = intercalate " "
-- provided custom className
-- provided custom className
[ props.className
-- BEM classNames
, componentName
......@@ -173,38 +182,37 @@ component' = R.hooksComponent (componentName <> "__helper") cpt where
-- Render
pure $
R2.select
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: show value
}
(
children
<>
flip map list \raw ->
H.option
{ value: show raw }
[ H.text $ show raw ]
)
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, type: props.type
, value: show value
}
( children
<>
flip map list \raw ->
H.option
{ value: show raw }
[ H.text $ show raw ]
)
where
reader = GUS.reader list
reader = GUS.reader list
-- | * Change event will effectively be triggered according to the
-- | component status props
-- | * Also directly returns the newly input value
-- | (usage not so different from `targetValue` of ReactBasic)
onChange' :: forall event a.
Show a
onChange'
:: forall event a
. Show a
=> ComponentStatus
-> (String -> Maybe a)
-> (a -> Effect Unit)
-> event
-> Effect Unit
onChange' status reader callback event = do
if status == Enabled
then event # unsafeCoerce >>> _.target.value >>> reader >>> case _ of
if status == Enabled then event # unsafeCoerce >>> _.target.value >>> reader >>> case _ of
Nothing -> R.nothing
Just v -> callback v
Just v -> callback v
else R.nothing
......@@ -11,24 +11,24 @@ import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, placeholder :: String
, rows :: Int
( status :: ComponentStatus
, className :: String
, placeholder :: String
, rows :: Int
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, placeholder : ""
, rows : 2
{ status: Enabled
, className: ""
, placeholder: ""
, rows: 2
}
-- | Structural Component for the Bootstrap textarea
......@@ -44,11 +44,15 @@ bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, rows
} _ = do
component = R.hooksComponent componentName cpt
where
cpt
props@
{ callback
, status
, rows
}
_ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
......@@ -64,27 +68,27 @@ component = R.hooksComponent componentName cpt where
-- Render
pure $
H.textarea
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, autoComplete: "off"
, rows
} []
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]