Commit c024c3b6 authored by arturo's avatar arturo

[docs] Add new document

* #174
* FormValidation hook
* StateRecord hook
* Bootstrap component (button, formInput, formTextarea, baseModal,
spinner, button)
* enhanced SASS members
parent 41ecf1e2
Pipeline #1930 failed with stage
......@@ -10032,6 +10032,127 @@ h3 {
background-color: #000;
}
.form-group {
position: relative;
margin-bottom: 24px;
}
.form-group__label {
padding-bottom: 4px;
}
.form-group__label label {
font-weight: 600;
margin-bottom: initial;
}
.form-group__label--sub {
color: #555555;
font-size: 14px;
}
.form-group__field .form-group__error,
.form-group__field .form-group__success {
position: absolute;
overflow: hidden;
}
.form-group__field .form-group__error--obtrusive,
.form-group__field .form-group__success--obtrusive {
position: static;
overflow: initial;
}
.form-group--error label {
color: #FF304F;
}
.form-group--error .b-form-input {
border-color: #FF304F;
}
.form-group--error .b-form-input:focus, .form-group--error .b-form-input:active {
border-color: #FF304F;
box-shadow: 0 0 0 1px #FF304F;
}
.form-group__error, .form-group__success, .form-group__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
.form-group__error {
color: #FF304F;
}
.form-group__success {
color: #015668;
}
.form-group__warning {
color: #0DE2EA;
}
.form-input--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
.b-form-input[type=password] {
letter-spacing: 8px;
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #0d0d0d;
max-height: calc(100% - 3.5rem );
overflow: auto;
}
.b-modal__header {
background-color: #6f6f6f;
}
.b-modal__header__content {
color: #FF550B;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
}
.b-button {
cursor: pointer;
position: relative;
}
.b-button:focus {
outline: 0;
}
.b-button__spinner {
position: absolute;
left: calc(50% - 10px);
top: calc(50% - 10px);
width: 20px;
height: 20px;
}
.b-button--disabled, .b-button--deferred, .b-button--idled {
cursor: default;
}
.b-button--disabled:focus, .b-button--deferred:focus, .b-button--idled:focus {
outline: 0;
}
.b-button--deferred .b-button__inner {
visibility: hidden;
}
.b-button .b-icon {
vertical-align: text-top;
}
.navbar-dark.bg-primary {
background-color: #111111 !important;
}
......
......@@ -9984,4 +9984,125 @@ h3 {
background-color: #fff;
}
.form-group {
position: relative;
margin-bottom: 24px;
}
.form-group__label {
padding-bottom: 4px;
}
.form-group__label label {
font-weight: 600;
margin-bottom: initial;
}
.form-group__label--sub {
color: #343a40;
font-size: 14px;
}
.form-group__field .form-group__error,
.form-group__field .form-group__success {
position: absolute;
overflow: hidden;
}
.form-group__field .form-group__error--obtrusive,
.form-group__field .form-group__success--obtrusive {
position: static;
overflow: initial;
}
.form-group--error label {
color: #dc3545;
}
.form-group--error .b-form-input {
border-color: #dc3545;
}
.form-group--error .b-form-input:focus, .form-group--error .b-form-input:active {
border-color: #dc3545;
box-shadow: 0 0 0 1px #dc3545;
}
.form-group__error, .form-group__success, .form-group__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
.form-group__error {
color: #dc3545;
}
.form-group__success {
color: #28a745;
}
.form-group__warning {
color: #ffc107;
}
.form-input--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
.b-form-input[type=password] {
letter-spacing: 8px;
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
color: #007bff;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
}
.b-button {
cursor: pointer;
position: relative;
}
.b-button:focus {
outline: 0;
}
.b-button__spinner {
position: absolute;
left: calc(50% - 10px);
top: calc(50% - 10px);
width: 20px;
height: 20px;
}
.b-button--disabled, .b-button--deferred, .b-button--idled {
cursor: default;
}
.b-button--disabled:focus, .b-button--deferred:focus, .b-button--idled:focus {
outline: 0;
}
.b-button--deferred .b-button__inner {
visibility: hidden;
}
.b-button .b-icon {
vertical-align: text-top;
}
/*# sourceMappingURL=bootstrap-default.css.map */
......@@ -9740,4 +9740,125 @@ h3 {
background-color: #fff;
}
.form-group {
position: relative;
margin-bottom: 24px;
}
.form-group__label {
padding-bottom: 4px;
}
.form-group__label label {
font-weight: 600;
margin-bottom: initial;
}
.form-group__label--sub {
color: #343a40;
font-size: 14px;
}
.form-group__field .form-group__error,
.form-group__field .form-group__success {
position: absolute;
overflow: hidden;
}
.form-group__field .form-group__error--obtrusive,
.form-group__field .form-group__success--obtrusive {
position: static;
overflow: initial;
}
.form-group--error label {
color: #cc330d;
}
.form-group--error .b-form-input {
border-color: #cc330d;
}
.form-group--error .b-form-input:focus, .form-group--error .b-form-input:active {
border-color: #cc330d;
box-shadow: 0 0 0 1px #cc330d;
}
.form-group__error, .form-group__success, .form-group__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
.form-group__error {
color: #cc330d;
}
.form-group__success {
color: #3e4d59;
}
.form-group__warning {
color: #6e9fa5;
}
.form-input--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
.b-form-input[type=password] {
letter-spacing: 8px;
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
color: #2f3c48;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
}
.b-button {
cursor: pointer;
position: relative;
}
.b-button:focus {
outline: 0;
}
.b-button__spinner {
position: absolute;
left: calc(50% - 10px);
top: calc(50% - 10px);
width: 20px;
height: 20px;
}
.b-button--disabled, .b-button--deferred, .b-button--idled {
cursor: default;
}
.b-button--disabled:focus, .b-button--deferred:focus, .b-button--idled:focus {
outline: 0;
}
.b-button--deferred .b-button__inner {
visibility: hidden;
}
.b-button .b-icon {
vertical-align: text-top;
}
/*# sourceMappingURL=bootstrap-greyson.css.map */
......@@ -9972,4 +9972,125 @@ h3 {
background-color: #fff;
}
.form-group {
position: relative;
margin-bottom: 24px;
}
.form-group__label {
padding-bottom: 4px;
}
.form-group__label label {
font-weight: 600;
margin-bottom: initial;
}
.form-group__label--sub {
color: #343a40;
font-size: 14px;
}
.form-group__field .form-group__error,
.form-group__field .form-group__success {
position: absolute;
overflow: hidden;
}
.form-group__field .form-group__error--obtrusive,
.form-group__field .form-group__success--obtrusive {
position: static;
overflow: initial;
}
.form-group--error label {
color: #FF4057;
}
.form-group--error .b-form-input {
border-color: #FF4057;
}
.form-group--error .b-form-input:focus, .form-group--error .b-form-input:active {
border-color: #FF4057;
box-shadow: 0 0 0 1px #FF4057;
}
.form-group__error, .form-group__success, .form-group__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
.form-group__error {
color: #FF4057;
}
.form-group__success {
color: #0074E4;
}
.form-group__warning {
color: #FC3C3C;
}
.form-input--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
.b-form-input[type=password] {
letter-spacing: 8px;
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
color: #083358;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
}
.b-button {
cursor: pointer;
position: relative;
}
.b-button:focus {
outline: 0;
}
.b-button__spinner {
position: absolute;
left: calc(50% - 10px);
top: calc(50% - 10px);
width: 20px;
height: 20px;
}
.b-button--disabled, .b-button--deferred, .b-button--idled {
cursor: default;
}
.b-button--disabled:focus, .b-button--deferred:focus, .b-button--idled:focus {
outline: 0;
}
.b-button--deferred .b-button__inner {
visibility: hidden;
}
.b-button .b-icon {
vertical-align: text-top;
}
/*# sourceMappingURL=bootstrap-herbie.css.map */
......@@ -9989,4 +9989,125 @@ h3 {
background-color: #fff;
}
.form-group {
position: relative;
margin-bottom: 24px;
}
.form-group__label {
padding-bottom: 4px;
}
.form-group__label label {
font-weight: 600;
margin-bottom: initial;
}
.form-group__label--sub {
color: #343a40;
font-size: 14px;
}
.form-group__field .form-group__error,
.form-group__field .form-group__success {
position: absolute;
overflow: hidden;
}
.form-group__field .form-group__error--obtrusive,
.form-group__field .form-group__success--obtrusive {
position: static;
overflow: initial;
}
.form-group--error label {
color: #434343;
}
.form-group--error .b-form-input {
border-color: #434343;
}
.form-group--error .b-form-input:focus, .form-group--error .b-form-input:active {
border-color: #434343;
box-shadow: 0 0 0 1px #434343;
}
.form-group__error, .form-group__success, .form-group__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
.form-group__error {
color: #434343;
}
.form-group__success {
color: #333333;
}
.form-group__warning {
color: #5f5f5f;
}
.form-input--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
.b-form-input[type=password] {
letter-spacing: 8px;
font-weight: 700;
}
.b-modal--visible {
display: block;
}
.b-modal__content {
background-color: #fff;
max-height: calc(100% - 3.5rem );
overflow: auto;
}
.b-modal__header {
background-color: rgba(0, 0, 0, 0.03);
}
.b-modal__header__content {
color: #222222;
font-size: 21px;
}
.b-modal__overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
.b-modal__overlay--collapsible {
cursor: pointer;
}
.b-spinner {
font-size: inherit;
}
.b-button {
cursor: pointer;
position: relative;
}
.b-button:focus {
outline: 0;
}
.b-button__spinner {
position: absolute;
left: calc(50% - 10px);
top: calc(50% - 10px);
width: 20px;
height: 20px;
}
.b-button--disabled, .b-button--deferred, .b-button--idled {
cursor: default;
}
.b-button--disabled:focus, .b-button--deferred:focus, .b-button--idled:focus {
outline: 0;
}
.b-button--deferred .b-button__inner {
visibility: hidden;
}
.b-button .b-icon {
vertical-align: text-top;
}
/*# sourceMappingURL=bootstrap-monotony.css.map */
{"version":3,"sourceRoot":"","sources":["../../src/sass/_menu.sass","../../src/sass/_context_menu.sass","../../src/sass/_graph.sass","../../src/sass/_login.sass","../../src/sass/_tree.sass","../../src/sass/abstract/_members.scss","../../src/sass/_code_editor.sass","../../src/sass/_styles.sass","../../src/sass/_range_slider.sass","../../src/sass/_annotation.sass","../../src/sass/_folder_view.sass"],"names":[],"mappings":"AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;;AAEF;EACI;EACA;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;;;AAEF;AACI;EACA;;;AAEJ;AACI;EACA;;;AAGJ;AACA;EACI;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;EACA;;;AAEF;EACE;;;AAME;EACE;;;AC9DN;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AClBF;EACE;EACA;EACA;;;AAEF;AAkCE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAxCA;EAZA;EACA;EAEA;EAWE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA;;AAGA;EACE;EACA;;AACN;EACE;;AACF;EACE;;AAEF;EApCA;EACA;EAEA;EAmCE;EACA;;AACF;EACE;;AACF;EACE;;AAWF;EAEE;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAEJ;EACE;;AAEA;EACE;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ACjFJ;EACE;;AACA;EACE;EACA;;;AAEJ;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;AAEE;EACE;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAEJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;;;AAGF;EACE;;;AAEJ;EACI;EACA;;;AAGF;EACE;;;AAEJ;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AC1GF;EACE;;;AAGA;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGA;EACE;;AACF;EACE;;AAEJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AACF;EACE;;AAGN;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AAIR;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAEV;EACE;;AACF;EACE;;AAEE;EACE;;AACF;EACE;;AACN;EACE;;AAEE;EACE;;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEF;EACE;;AAEA;EACE;EACA;;AAEJ;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAIF;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAGR;EACE;;AAEA;EACE;EACA;;ACrJO;EDwJP;;;ACjKO;EDoKP;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AAEF;EACE;;;AAEN;EAWE;EAGA;EACA,eAvPkB;;AAyPlB;EACE;EACA;;ACxNO;EDqOP;EACA,MAFW;EAGX;;;AChPO;EDoPP;EACA,OAFW;EAGX;;;AAGA;EACE;;AAEA;EACE;;AAGJ;EACE;;AACF;EACE;;AACF;EACE;;AAGJ;EACE;EACA;;AAEA;EAGE;EACA,WAzSa;EA4Sb;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AACA;EACE;;AAEN;EACE;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;;AAEJ;EAEE;EACA;;;AAEF;EAGE;EAKA;EACA;EACA;EACA;EAGA;EACA;EACA;EAKA;;AAHA;EACE;;;AAKJ;EAGE;;ACpUS;ED5BP;EACA;;;ACkBO;EDfP;EACA;;;ACuBO;EDuUP;;AAEA;EACE;;;ACnVK;EDsVP;;AAEA;EACE;;;AAON;EAIE;EAEA;EACA;EACA;EACA,QApYmC;EAqYnC;;ACjWS;EDqWP;;;AC9WO;EDiXP;;;AAEJ;EAGE;EAEA;EACA;EACA;EACA,QAvZgC;EAwZhC;;ACnXS;EDuXP;;;AChYO;EDmYP;;;AAKF;EAIE;EACA;;ACpYO;ED5BP;EACA;;;ACkBO;EDfP;EACA;;;AA6ZA;EACE,eA3a+B;;;AAibjC;EACE;;AAEE;EACE;;;AE/ZN;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EApCR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AA0CM;EACE;EACA;EACA;EACA;EACA;EA5CR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAkDE;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAGE;EACE;;AAEF;EACE;;AAEV;EACE;;AAEA;EACE;;;AC9FN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAII;EACE;EACA;;AAEJ;EACE;EACA;;AFmBK;EEhBP;;AAEA;EACE;;;AFIK;EEDP;;AAEA;EACE;;;AAGN;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;;AAIA;EACE;;AACA;EACE;EACA;;AACF;EACE;;AACA;EACE;;AACJ;EACE;;;AAER;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;AAGI;EACE;;AACF;EACE;;;AAEN;EACE;EACA;EACA;;;AAIA;EACE;;AACF;EACE;;;AAEJ;EACE;;AACA;EACE;;;AAEJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAEF;EACE;;AAEA;EACE;;AAEF;EACE;;;AAGJ;EAKE;;AAEA;EACE;;AAIE;EACE;;AACF;EACE;;AAEJ;EAGE;;AAGJ;EACE,SAtBa;EAuBb;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKE;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;;AAKN;EACE;;AF1HO;EE6HL;;;AFtIK;EEyIL;;;AAEJ;EACE,SAZa;;;AC9JjB;EACE;AACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AAGN;EACE;;;ACxBJ;EACE;;AAEA;EANE;EACA;;AAQF;EAbE;EACA;;AAeF;EAhBE;EACA;;AAkBF;EAnBE;EACA;;AAqBF;EA1BE;EACA,kBANyB;;AAkC3B;EA7BE;EACA,kBAPqB;;AAsCvB;EAhCE;EACA,kBAJoB;;;AAuCtB;EApCE;EACA,kBANyB;;AA4C3B;EAvCE;EACA,kBAPqB;;AAgDvB;EA1CE;EACA,kBAJoB;;;ACNxB;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA","file":"sass.css"}
\ No newline at end of file
{"version":3,"sourceRoot":"","sources":["../../src/sass/_menu.sass","../../src/sass/_context_menu.sass","../../src/sass/_graph.sass","../../src/sass/_login.sass","../../src/sass/_tree.sass","../../src/sass/abstract/_members.scss","../../src/sass/_code_editor.sass","../../src/sass/_styles.sass","../../src/sass/_range_slider.sass","../../src/sass/_annotation.sass","../../src/sass/_folder_view.sass"],"names":[],"mappings":"AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;EACE;EACA;EACA;EACA;EAEA;;;AAEF;EACI;EACA;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;;;AAEF;AACI;EACA;;;AAEJ;AACI;EACA;;;AAGJ;AACA;EACI;;;AAEJ;EACI;EACA;EACA;EACA;;;AAEJ;EACE;EACA;;;AAEF;EACE;;;AAME;EACE;;;AC9DN;EACE;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AClBF;EACE;EACA;EACA;;;AAEF;AAkCE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAxCA;EAZA;EACA;EAEA;EAWE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEF;EACE;EACA;;AAGA;EACE;EACA;;AACN;EACE;;AACF;EACE;;AAEF;EApCA;EACA;EAEA;EAmCE;EACA;;AACF;EACE;;AACF;EACE;;AAWF;EAEE;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAEJ;EACE;;AAEA;EACE;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;ACjFJ;EACE;;AACA;EACE;EACA;;;AAEJ;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;;AAEE;EACE;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAEJ;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;;;AAEN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAEJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;;;AAGF;EACE;;;AAEJ;EACI;EACA;;;AAGF;EACE;;;AAEJ;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;;;AC1GF;EACE;;;AAGA;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGA;EACE;;AACF;EACE;;AAEJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AACF;EACE;;AAGN;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AAIR;EACE;;AACF;EACE;;AACA;EACE;EACA;;AAEE;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAEV;EACE;;AACF;EACE;;AAEE;EACE;;AACF;EACE;;AACN;EACE;;AAEE;EACE;;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEF;EACE;;AAEA;EACE;EACA;;AAEJ;EACE;EACA;;AAIA;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAIF;EACE;EACA;;AAEF;EACE;;AAEF;EACE;;;AAGR;EACE;;AAEA;EACE;EACA;;AC5HO;ED+HP;;;ACxIO;ED2IP;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AAEF;EACE;;;AAEN;EAWE;EAGA;EACA,eAvPkB;;AAyPlB;EACE;EACA;;AC/LO;ED4MP;EACA,MAFW;EAGX;;;ACvNO;ED2NP;EACA,OAFW;EAGX;;;AAGA;EACE;;AAEA;EACE;;AAGJ;EACE;;AACF;EACE;;AACF;EACE;;AAGJ;EACE;EACA;;AAEA;EAGE;EACA,WAzSa;EA4Sb;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AACA;EACE;;AAEN;EACE;EACA;EACA;EACA;;AAEJ;EAEE;EACA;;;AAEJ;EAEE;EACA;;;AAEF;EAGE;EAKA;EACA;EACA;EACA;EAGA;EACA;EACA;EAKA;;AAHA;EACE;;;AAKJ;EAGE;;AC3SS;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;ACgDO;ED8SP;;AAEA;EACE;;;AC1TK;ED6TP;;AAEA;EACE;;;AAON;EAIE;EAEA;EACA;EACA;EACA,QApYmC;EAqYnC;;ACxUS;ED4UP;;;ACrVO;EDwVP;;;AAEJ;EAGE;EAEA;EACA;EACA;EACA,QAvZgC;EAwZhC;;AC1VS;ED8VP;;;ACvWO;ED0WP;;;AAKF;EAIE;EACA;;AC3WO;EDrDP;EACA;;;AC2CO;EDxCP;EACA;;;AA6ZA;EACE,eA3a+B;;;AAibjC;EACE;;AAEE;EACE;;;AE/ZN;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EApCR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AA0CM;EACE;EACA;EACA;EACA;EACA;EA5CR;EACA;EACA;EACA;EACA;EACA;EACA;EAlBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAkDE;EACE;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;;AACF;EACE;EACA;;AACF;EACE;EACA;;AAGE;EACE;;AAEF;EACE;;AAEV;EACE;;AAEA;EACE;;;AC9FN;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;;AAII;EACE;EACA;;AAEJ;EACE;EACA;;AF4CK;EEzCP;;AAEA;EACE;;;AF6BK;EE1BP;;AAEA;EACE;;;AAGN;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;;AAIA;EACE;;AACA;EACE;EACA;;AACF;EACE;;AACA;EACE;;AACJ;EACE;;;AAER;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;AAGI;EACE;;AACF;EACE;;;AAEN;EACE;EACA;EACA;;;AAIA;EACE;;AACF;EACE;;;AAEJ;EACE;;AACA;EACE;;;AAEJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAEF;EACE;;AAEA;EACE;;AAEF;EACE;;;AAGJ;EAKE;;AAEA;EACE;;AAIE;EACE;;AACF;EACE;;AAEJ;EAGE;;AAGJ;EACE,SAtBa;EAuBb;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAKE;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;;AAKN;EACE;;AFjGO;EEoGL;;;AF7GK;EEgHL;;;AAEJ;EACE,SAZa;;;AC9JjB;EACE;AACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AAGN;EACE;;;ACxBJ;EACE;;AAEA;EANE;EACA;;AAQF;EAbE;EACA;;AAeF;EAhBE;EACA;;AAkBF;EAnBE;EACA;;AAqBF;EA1BE;EACA,kBANyB;;AAkC3B;EA7BE;EACA,kBAPqB;;AAsCvB;EAhCE;EACA,kBAJoB;;;AAuCtB;EApCE;EACA,kBANyB;;AA4C3B;EAvCE;EACA,kBAPqB;;AAgDvB;EA1CE;EACA,kBAJoB;;;ACNxB;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA","file":"sass.css"}
\ No newline at end of file
......@@ -201,6 +201,11 @@ let additions =
, repo = "https://github.com/mjepronk/purescript-web-url"
, version = "v1.0.2"
}
, convertable-options =
{ dependencies = [ "console", "effect", "maybe", "record" ]
, repo = "https://github.com/natefaubion/purescript-convertable-options"
, version = "v1.0.0"
}
}
in upstream // overrides // additions
......@@ -23,6 +23,7 @@ to generate this file without the comments in this block.
, "colors"
, "console"
, "control"
, "convertable-options"
, "css"
, "datetime"
, "dom-filereader"
......@@ -76,6 +77,7 @@ to generate this file without the comments in this block.
, "spec-discovery"
, "spec-quickcheck"
, "strings"
, "strings-extra"
, "stringutils"
, "these"
, "toestand"
......@@ -89,6 +91,7 @@ to generate this file without the comments in this block.
, "unsafe-coerce"
, "uri"
, "uuid"
, "validation"
, "web-file"
, "web-html"
, "web-storage"
......
module Gargantext.Components.Bootstrap.Div (div', div_) where
import Reactix as R
import Reactix.DOM.HTML as H
-- | Shorthand for using HTML <div> without writing its text node
div' :: forall r. Record r -> String -> R.Element
div' props content = H.div props [ H.text content ]
-- | Shorthand for using HTML <div> without writing its text node nor props
div_ :: String -> R.Element
div_ content = H.div {} [ H.text content ]
module Gargantext.Components.Bootstrap.Spinner(spinner) where
import Gargantext.Prelude
import Data.Foldable (intercalate)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
type Props = ( | Options)
type Options =
( theme :: String
, className :: String
)
options :: Record Options
options =
{ theme: "border"
, className: ""
}
-- | Structural Component for the Bootstrap spinner
-- |
-- | * theme: `"border" (default) | "grow"`
-- |
-- | https://getbootstrap.com/docs/4.4/components/spinners/
spinner :: forall r. R2.OptLeaf Options Props r
spinner = R2.optLeaf component options
componentName :: String
componentName = "b-spinner"
bootstrapName :: String
bootstrapName = "spinner"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props _ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
-- Bootstrap specific classNames
, bootstrapName <> "-" <> props.theme
]
-- Render
pure $
H.div
{ className }
[]
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.Div(div', div_) as Exports
import Gargantext.Components.Bootstrap.FormInput(formInput) as Exports
import Gargantext.Components.Bootstrap.FormTextarea(formTextarea) as Exports
import Gargantext.Components.Bootstrap.Spinner(spinner) as Exports
'use strict';
exports._addClassName = function(window, className) {
window.document.body.classList.add(className);
}
exports._removeClassName = function(window, className) {
window.document.body.classList.remove(className);
}
module Gargantext.Components.Bootstrap.BaseModal (baseModal) where
import Gargantext.Prelude
import DOM.Simple (Window)
import Data.Foldable (intercalate)
import Effect (Effect)
import Effect.Uncurried (EffectFn2, runEffectFn2)
import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
foreign import _addClassName :: EffectFn2 Window String Unit
foreign import _removeClassName :: EffectFn2 Window String Unit
type Props =
( isVisibleBox :: T.Box Boolean
| Options
)
type Options =
( id :: String
, title :: String
, hasBackground :: Boolean
, hasCollapsibleBackground :: Boolean
)
options :: Record Options
options =
{ id: ""
, title: ""
, hasBackground: true
, hasCollapsibleBackground: true
}
componentName :: String
componentName = "b-modal"
vendorName :: String
vendorName = "modal"
baseModal :: forall r. R2.OptComponent Options Props r
baseModal = R2.optComponent component options
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt { isVisibleBox
, id
, title
, hasBackground
, hasCollapsibleBackground
} children = do
-- State
isVisible <- R2.useLive' isVisibleBox
-- Hooks
-- R.useEffect1' isVisible $
-- (isVisible ? addClassName $ removeClassName) window "modal-open"
-- Computed
let
className = intercalate " "
-- Component
[ componentName
, isVisible ?
componentName <> "--visible" $
componentName <> "--hidden"
-- Vendor
, vendorName
]
hasHeader = not $ eq title ""
-- Render
R.createPortal
[
H.div
{ id
, className
, role: "dialog"
, data: { show: true }
}
[
R2.if' (hasBackground) $
H.div
{ className: intercalate " "
[ componentName <> "__overlay"
, hasCollapsibleBackground ?
componentName <> "__overlay--collapsible" $
""
]
, on: { click: hasCollapsibleBackground ?
toggle isVisibleBox $
const $ pure unit
}
}
[ H.text $ nbsp 1 ]
,
H.div
{ className: "modal-dialog modal-lg"
, role: "document"
}
[
H.div
{ className: intercalate " "
[ componentName <> "__content"
, vendorName <> "-content"
]
}
[
R2.if' (hasHeader) $
H.div
{ className: intercalate " "
[ componentName <> "__header"
, vendorName <> "-header"
]
}
[
H.div
{ className: componentName <> "__header__content" }
[ H.text title ]
,
H.button
{ type: "button"
, className: "close"
, data: { dismiss: "modal" }
}
[
H.a
{ on: { click: toggle isVisibleBox }
, className: "btn fa fa-times" }
[]
]
]
,
H.div
{ className: "modal-body" }
children
]
]
]
]
<$> R2.getPortalHost
toggle :: forall event. T.Box Boolean -> event -> Effect Unit
toggle box _ = T.modify_ not box
addClassName :: Window -> String -> Effect Unit
addClassName = runEffectFn2 _addClassName
removeClassName :: Window -> String -> Effect Unit
removeClassName = runEffectFn2 _removeClassName
module Gargantext.Components.Bootstrap.FormInput (formInput) where
import Gargantext.Prelude
import Data.Foldable (elem, intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, type :: String
, placeholder :: String
, size :: String
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, type : "text"
, placeholder : ""
, size : "md"
}
-- | Structural Component for the Bootstrap input
-- |
-- | * size: `"md" (default) | "sm" | "lg"`
-- |
-- | https://getbootstrap.com/docs/4.1/components/forms/
formInput :: forall r. R2.OptLeaf Options Props r
formInput = R2.optLeaf component options
componentName :: String
componentName = "b-form-input"
bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} _ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
, componentName <> "--" <> show status
-- Bootstrap specific classNames
, bootstrapName
, bootstrapName <> "-" <> props.size
]
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
, autoComplete: "off"
}
-- | * 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
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
else pure unit
module Gargantext.Components.Bootstrap.FormTextarea (formTextarea) where
import Gargantext.Prelude
import Data.Foldable (elem, intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
type Props =
( callback :: String -> Effect Unit
, value :: String
| Options
)
type Options =
( status :: ComponentStatus
, className :: String
, placeholder :: String
, rows :: Int
)
options :: Record Options
options =
{ status : Enabled
, className : ""
, placeholder : ""
, rows : 2
}
-- | Structural Component for the Bootstrap textarea
-- |
-- | https://getbootstrap.com/docs/4.1/components/forms/
formTextarea :: forall r. R2.OptLeaf Options Props r
formTextarea = R2.optLeaf component options
componentName :: String
componentName = "b-form-textarea"
bootstrapName :: String
bootstrapName = "form-control"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
, rows
} _ = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
, componentName <> "--" <> show status
-- Bootstrap specific classNames
, bootstrapName
]
change <- pure $ onChange status callback
-- Render
pure $
H.textarea
{ className
, on: { change }
, disabled: elem status [ Disabled ]
, readOnly: elem status [ Idled ]
, placeholder: props.placeholder
, autoComplete: "off"
, rows
} []
-- | * 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
-> (String -> Effect Unit)
-> event
-> Effect Unit
onChange status callback event = do
if status == Enabled
then callback $ (unsafeCoerce event).target.value
else pure unit
module Gargantext.Components.Bootstrap.Button (button) where
import Gargantext.Prelude
import Data.Array (elem)
import Data.Foldable (intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap.Spinner (spinner)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix as R2
import React.SyntheticEvent as SE
import Reactix as R
import Reactix.DOM.HTML as H
type Props =
( callback :: Unit -> Effect Unit
| Options
)
type Options =
( status :: ComponentStatus
, size :: String
, variant :: String
, type :: String
, className :: String
, block :: Boolean
)
options :: Record Options
options =
{ status : Enabled
, size : "md"
, variant : "primary"
, type : "button"
, className : ""
, block : false
}
-- | Structural Component for the Bootstrap button
-- |
-- | * size: `"md" (default) | "sm" | "lg"`
-- |
-- | https://getbootstrap.com/docs/4.0/components/buttons/
button :: forall r. R2.OptComponent Options Props r
button = R2.optComponent component options
componentName :: String
componentName = "b-button"
bootstrapName :: String
bootstrapName = "btn"
component :: R.Component Props
component = R.hooksComponent componentName cpt where
cpt props@{ callback
, status
} children = do
-- Computed
className <- pure $ intercalate " "
-- provided custom className
[ props.className
-- BEM classNames
, componentName
, componentName <> "--" <> show status
-- Bootstrap specific classNames
, bootstrapName
, bootstrapName <> "-" <> props.variant
, bootstrapName <> "-" <> props.size
, props.block == true ?
bootstrapName <> "-block" $
mempty
]
-- @click
click <- pure $ \event -> onClick status callback event
-- Render
pure $
H.button
{ className
, on: { click }
, disabled: elem status [ Disabled, Deferred ]
, type: props.type
}
[ R2.if' (status == Deferred) $
spinner
{ className: componentName <> "__spinner"
}
, H.span
{ className: componentName <> "__inner"
}
children
]
-- | Clicked event will effectively be triggered according to the
-- | component status props
onClick :: forall event.
ComponentStatus
-> (Unit -> Effect Unit)
-> SE.SyntheticEvent_ event
-> Effect Unit
onClick status callback event = do
SE.preventDefault event
if status == Enabled
then callback unit
else pure unit
module Gargantext.Components.Bootstrap.Types
( ComponentStatus(..)
) where
import Gargantext.Prelude
import Data.Generic.Rep (class Generic)
import Data.Show.Generic (genericShow)
import Data.String.Extra (kebabCase)
-- | Component status based on UI/UX overall expression
-- |
-- | * `Enabled`: default UI/UX behavior
-- | * `Disabled`: main action of the component has been deactivated, and
-- | a UI feedback is showed to the user (eg. a disabled button is now
-- | unclikable, fade color, and disabled CTA feature)
-- | * `Deffered`: main action of the component has been deactivated, but
-- | contrary to a disabled state, the altered UX/UI bears characteristics
-- | of a short-lived state (eg. a button turns to `Deffered`, is now
-- | unclickable but presents a less inoperative style, for example a
-- | spinner is now attached in place of the CTA text)
-- | * `Muted`: on surface the component seems functional, but main action
-- | of the component has been deactivated, yet no UI nor UX feedback is
-- | particularly showed to the user accordingly
-- | * `Idled`: balance between a `Disabled` and `Muted` state, as if the
-- | component has its main feature deactivated, but told with a less
-- | strong UI/UX (eg. a input in a "read-only" mode: UI can be alter to
-- | underline the lack of its main writing feature, but without telling
-- | to the user that the input is per-se inoperative)
data ComponentStatus =
Enabled
| Disabled
| Deferred
| Idled
| Muted
derive instance Generic ComponentStatus _
derive instance Eq ComponentStatus
instance Show ComponentStatus where
show a = kebabCase $ genericShow a
......@@ -19,12 +19,16 @@ import Data.Set as Set
import Data.String as Str
import Data.Symbol (SProxy(..))
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Aff (Aff, Milliseconds(..), delay, launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.App.Data (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Category (rating)
import Gargantext.Components.Category.Types (Star(..))
import Gargantext.Components.DocsTable.DocumentFormCreation (documentFormCreation)
import Gargantext.Components.DocsTable.Types (DocumentsView(..), Hyperdata(..), LocalUserScore, Query, Response(..), Year, sampleData)
import Gargantext.Components.Nodes.Lists.Types as NT
import Gargantext.Components.Nodes.Texts.Types as TextsT
......@@ -37,7 +41,7 @@ import Gargantext.Routes (SessionRoute(NodeAPI))
import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId, get, delete)
import Gargantext.Types (ListId, NodeID, NodeType(..), OrderBy(..), SidePanelState(..), TabSubType, TabType, TableResult, showTabType')
import Gargantext.Utils (sortWith)
import Gargantext.Utils (sortWith, (?))
import Gargantext.Utils.CacheAPI as GUC
import Gargantext.Utils.QueryString (joinQueryStrings, mQueryParam, mQueryParamS, queryParam, queryParamS)
import Gargantext.Utils.Reactix as R2
......@@ -129,29 +133,82 @@ docViewCpt = here.component "docView" cpt where
, params
, query
} _ = do
-- State
cacheState' <- T.useLive T.unequal cacheState
query' <- T.useLive T.unequal query
pure $ H.div { className: "doc-table-doc-view container1" }
[ R2.row
[ chart
, if showSearch then searchBar { query } [] else H.div {} []
, H.div {className: "col-md-12"}
[ pageLayout { boxes
, cacheState
, frontends
, key: "docView-" <> (show cacheState')
, listId
, mCorpusId
, nodeId
, params
, query: query'
, session
, sidePanel
, tabType
, totalRecords
, yearFilter
} [] ] ] ]
isDocumentModalVisibleBox <- T.useBox false
onDocumentCreationPending /\ onDocumentCreationPendingBox <-
R2.useBox' false
-- @toggleModalCallback
toggleModal <- pure $ const $
T.modify_ not isDocumentModalVisibleBox
-- @createDocumentCallback
-- @WIP: remote business for document creation
createDocumentCallback <- pure $ \fdata -> launchAff_ do
liftEffect $ T.write_ true onDocumentCreationPendingBox
delay $ Milliseconds 2000.0
liftEffect $ T.write_ false onDocumentCreationPendingBox
-- Render
pure $
R.fragment
[
H.div { className: "doc-table-doc-view container1" }
[ R2.row
[ chart
, if showSearch then searchBar { query } [] else H.div {} []
, H.div
{ className: "col-md-12 row mb-3" }
[
H.div { className: "col-md-4" } []
,
H.button
{ className: "btn btn-light col-md-3"
, on: { click: toggleModal }
}
[
H.text "Add a document"
]
]
, H.div {className: "col-md-12"}
[ pageLayout { boxes
, cacheState
, frontends
, key: "docView-" <> (show cacheState')
, listId
, mCorpusId
, nodeId
, params
, query: query'
, session
, sidePanel
, tabType
, totalRecords
, yearFilter
} []
]
]
]
,
-- Document Creation Modal
B.baseModal
{ isVisibleBox: isDocumentModalVisibleBox
, title: "Add a new document"
, hasCollapsibleBackground: false
}
[
documentFormCreation
{ callback: createDocumentCallback
, status: onDocumentCreationPending ? Deferred $ Enabled
}
]
]
type SearchBarProps =
( query :: T.Box Query )
......
module Gargantext.Components.DocsTable.DocumentFormCreation
( documentFormCreation
, FormData
) where
import Gargantext.Prelude
import DOM.Simple.Console (log3)
import Data.Either (Either(..))
import Data.Foldable (foldl, intercalate)
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Hooks.FormValidation (VForm, useFormValidation)
import Gargantext.Hooks.FormValidation.Unboxed as FV
import Gargantext.Hooks.StateRecord (useStateRecord)
import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record (merge)
import Record.Extra (pick)
type Props =
( callback :: Record FormData -> Effect Unit
, status :: ComponentStatus
| Options
)
type Options = ( | FormData )
options :: Record Options
options = merge {} defaultData
documentFormCreation :: forall r. R2.OptLeaf Options Props r
documentFormCreation = R2.optLeaf component options
component :: R.Component Props
component = R.hooksComponent "documentFormCreation" cpt where
cpt props _ = do
-- Hooks
{ state, bindStateKey } <- useStateRecord (pick props :: Record FormData)
fv <- useFormValidation
-- @onSubmit: exec whole form validation and execute callback
onSubmit <- pure $ do
result <- fv.try (\_ -> documentFormValidation state)
case result of
Left err -> log3 "document form validation error" state err
Right _ -> props.callback state
-- Render
pure $
H.form
{ className: "document-form-creation" }
[
-- Title
H.div
{ className: intercalate " "
[ "form-group"
, (fv.hasError' "title") ?
"form-group--error" $
mempty
]
}
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text "Title" ]
]
,
H.div { className: "form-group__field" }
[
B.formInput $
bindStateKey "title"
,
R2.if' (fv.hasError' "title") $
H.div { className: "form-group__error" }
[ H.text "Please enter a title" ]
]
]
,
-- Source
H.div
{ className: intercalate " "
[ "form-group"
, (fv.hasError' "source") ?
"form-group--error" $
mempty
]
}
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text "Source" ]
]
,
H.div { className: "form-group__field" }
[
B.formInput $
bindStateKey "source"
,
R2.if' (fv.hasError' "source") $
H.div { className: "form-group__error" }
[ H.text "Please enter a source" ]
]
]
,
-- Authors
H.div
{ className: intercalate " "
[ "form-group"
, (fv.hasError' "authors") ?
"form-group--error" $
mempty
]
}
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text "Authors" ]
]
,
H.div { className: "form-group__field" }
[
B.formInput $
{ placeholder: "ex: author1, author2, …"
} `merge` bindStateKey "authors"
,
R2.if' (fv.hasError' "authors") $
H.div { className: "form-group__error" }
[ H.text "Please enter at least one author" ]
]
]
,
-- Abstract
H.div
{ className: intercalate " "
[ "form-group"
]
}
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text $ "Abstract" <> nbsp 1 ]
,
H.span
{ className: "form-group__label--sub" }
[ H.text "optional" ]
]
,
H.div { className: "form-group__field" }
[
B.formTextarea $
{ rows: 5
} `merge` bindStateKey "abstract"
]
]
,
-- Submit
H.div { className: "document-form-creation__submit" }
[
B.button
{ callback: \_ -> onSubmit
, status: props.status == Deferred ? Deferred $ Enabled
, variant: "primary"
, type: "submit"
, block: true
}
[ H.text "Add" ]
]
]
type FormData =
( title :: String
, source :: String
, authors :: String
, abstract :: String
)
defaultData :: Record FormData
defaultData =
{ title : ""
, source : ""
, authors : ""
, abstract : ""
}
documentFormValidation :: Record FormData -> Effect VForm
documentFormValidation r = foldl append mempty rules
where
rules =
[ FV.nonEmpty "title" r.title
, FV.nonEmpty "source" r.source
, FV.nonEmpty "authors" r.authors
]
......@@ -21,7 +21,6 @@ import Gargantext.Routes as GR
import Gargantext.Sessions (Session(..), get)
import Gargantext.Types as GT
import Gargantext.Utils ((?))
import Gargantext.Utils.Reactix (if', useBox', useLive')
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
......@@ -120,8 +119,8 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
, subTreeParams
, tree: NTree (LNode { id: targetId, name, nodeType }) ary }) _ = do
-- Hooks
action <- useLive' p.action
isExpanded /\ isExpandedBox <- useBox' false
action <- R2.useLive' p.action
isExpanded /\ isExpandedBox <- R2.useBox' false
-- Computed
let
expandCbk _ = T.modify_ not isExpandedBox
......@@ -157,7 +156,7 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
[
H.span { className: GT.fldr nodeType true } []
,
if' hasChild $
R2.if' hasChild $
if isExpanded then
H.span { className: "fa fa-chevron-down" } []
......@@ -175,7 +174,7 @@ subTreeTreeViewCpt = here.ntComponent "subTreeTreeView" cpt where
]
]
,
if' (hasChild && isExpanded) $
R2.if' (hasChild && isExpanded) $
H.div { className: "subtree__node__children" }
children
]
......
module Gargantext.Hooks.FormValidation
( module Gargantext.Hooks.FormValidation.Methods
, module Gargantext.Hooks.FormValidation.Types
) where
import Gargantext.Hooks.FormValidation.Types
( VForm, EForm, Field
, emailPattern
)
import Gargantext.Hooks.FormValidation.Methods
( useFormValidation
, append', (<!>)
)
-- (?) as `Gargantext.Hooks.FormValidation.Unboxed` and
-- `Gargantext.Hooks.FormValidation.Boxed` used same name of functions,
-- please import manually these helpers
module Gargantext.Hooks.FormValidation.Boxed
( class Equals, equals
, class NonEmpty, nonEmpty
, class Minimum, minimum
, class Maximum, maximum
, lowercase, uppercase, email
) where
import Gargantext.Prelude
import Data.String (toLower, toUpper)
import Data.String.CodeUnits (length)
import Data.String.Regex (test)
import Data.Tuple.Nested ((/\))
import Data.Validation.Semigroup (invalid)
import Effect (Effect)
import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern)
import Toestand as T
class Eq a <= Equals a where
equals :: Field -> T.Box a -> T.Box a -> Effect VForm
class NonEmpty a where
nonEmpty :: Field -> T.Box a -> Effect VForm
class Ord a <= Minimum a where
minimum :: Field -> T.Box a -> Int -> Effect VForm
class Ord a <= Maximum a where
maximum :: Field -> T.Box a -> Int -> Effect VForm
-- Regarding String field value
instance equalsString :: Equals String where
equals field box box' = do
input <- T.read box
input' <- T.read box'
case unit of
_
| (not eq input input') -> pure $ invalid [ field /\ "equals" ]
| otherwise -> pure $ pure unit
instance nonEmptyString :: NonEmpty String where
nonEmpty field = T.read >=> case _ of
input
| input == "" -> pure $ invalid [ field /\ "nonEmpty" ]
| otherwise -> pure $ pure unit
instance minimumString :: Minimum String where
minimum field box min = T.read box >>= case _ of
input
| (length input) < min -> pure $ invalid [ field /\ "minimum" ]
| otherwise -> pure $ pure unit
instance maximumString :: Maximum String where
maximum field box max = T.read box >>= case _ of
input
| (length input) > max -> pure $ invalid [ field /\ "maximum" ]
| otherwise -> pure $ pure unit
uppercase :: Field -> T.Box String -> Effect VForm
uppercase field = T.read >=> case _ of
input
| (toLower input) == input -> pure $ invalid [ field /\ "uppercase" ]
| otherwise -> pure $ pure unit
lowercase :: Field -> T.Box String -> Effect VForm
lowercase field = T.read >=> case _ of
input
| (toUpper input) == input -> pure $ invalid [ field /\ "lowercase" ]
| otherwise -> pure $ pure unit
email :: Field -> T.Box String -> Effect VForm
email field = T.read >=> case _ of
input
| (not $ test emailPattern input) -> pure $ invalid [ field /\ "email" ]
| otherwise -> pure $ pure unit
module Gargantext.Hooks.FormValidation.Methods
( useFormValidation
, append', (<!>)
) where
import Gargantext.Prelude
import Data.Array (filter, find)
import Data.Either (Either(..), isLeft)
import Data.Maybe (isJust)
import Data.Tuple (Tuple, fst, snd)
import Data.Tuple.Nested ((/\))
import Data.Validation.Semigroup (V(..), invalid, toEither)
import Effect (Effect)
import Effect.Aff (Aff, forkAff, joinFiber)
import Effect.Class (liftEffect)
import Gargantext.Utils.Reactix as R2
import Gargantext.Hooks.FormValidation.Types (Field, VForm, EForm)
import Reactix as R
import Toestand as T
type Methods =
(
try :: (Unit -> Effect VForm) -> Effect EForm
, asyncTry :: (Unit -> Aff VForm) -> Aff EForm
, tryCount :: Int
, tryCountBox :: T.Box Int
, try' :: (Unit -> Effect VForm) -> Effect EForm
, asyncTry' :: (Unit -> Aff VForm) -> Aff EForm
, hasError :: Boolean
, hasError' :: Field -> Boolean
, hasError_ :: Field -> String -> Boolean
, removeError :: Effect Unit
, removeError' :: Field -> Effect Unit
, removeError_ :: Field -> String -> Effect Unit
)
-- | ## hasError
-- |
-- | **Check if previous "try/asyncTry" contains error**
-- |
-- | variant:
-- |
-- | * constraining to a given field,
-- | * idem + provided error
-- |
-- | ***
-- |
-- | ## removeError
-- |
-- | **Remove all current error without running parallel side effects**
-- |
-- | variant:
-- |
-- | * constrained to a given field
-- |
-- | ***
-- |
-- | ## tryCount
-- |
-- | **Store number of "try/asyncTry" made (ie. "global" validation made)**
-- |
-- | variant:
-- |
-- | * boxed wrapped value
-- |
-- | ***
-- |
-- | ## try
-- |
-- | **Exec a synchronous validation, run parallel side effects, return result**
-- |
-- | variant:
-- |
-- | * Aff monad dependent validation rules
-- |
-- | ***
-- |
-- | ## try'
-- |
-- | **Exec a dynamic synchronous validation, no parallel side effects runned,
-- | returned only provided validation part (ie. not the whole result)
-- | nor returned result)**
-- |
-- | variant:
-- |
-- | * Aff monad dependent validation rules
-- |
useFormValidation :: R.Hooks (Record Methods)
useFormValidation = do
tryCount /\ tryCountBox <- R2.useBox' 0
result /\ resultBox <- R2.useBox' (V (Right mempty) :: VForm)
memoHasError <- R.useMemo1 result $ const $ hasError result
memoHasError' <- R.useMemo1 result $ const $ (_ # result # hasError')
memoHasError_ <- R.useMemo1 result $ const $ (_ # result # hasError_)
pure
{ tryCount
, tryCountBox
, hasError : memoHasError
, hasError' : memoHasError'
, hasError_ : memoHasError_
, try : try resultBox tryCountBox
, asyncTry : asyncTry resultBox tryCountBox
, try' : try' resultBox
, asyncTry' : asyncTry' resultBox
, removeError : removeError resultBox
, removeError': removeError' resultBox
, removeError_: removeError_ resultBox
}
----
hasError :: VForm -> Boolean
hasError = toEither >>> isLeft
hasError' :: VForm -> Field -> Boolean
hasError' (V (Right _)) _ = false
hasError' (V (Left err)) field = isJust $ find (fst >>> eq field) err
hasError_ :: VForm -> Field -> String -> Boolean
hasError_ (V (Right _)) _ _ = false
hasError_ (V (Left err)) field value = isJust $ find (a && b) err
where
a = fst >>> eq field
b = snd >>> eq value
----
try :: forall box1 box2.
T.ReadWrite box1 VForm
=> T.ReadWrite box2 Int
=> box1
-> box2
-> (Unit -> Effect VForm)
-> Effect EForm
try resultBox tryCountBox thunk = do
res <- thunk unit
T.write_ res resultBox
T.modify_ (_ + 1) tryCountBox
pure $ toEither res
asyncTry :: forall box1 box2.
T.ReadWrite box1 VForm
=> T.ReadWrite box2 Int
=> box1
-> box2
-> (Unit -> Aff VForm)
-> Aff EForm
asyncTry resultBox tryCountBox thunk = do
fiber <- forkAff $ thunk unit
res <- joinFiber fiber
liftEffect do
T.write_ res resultBox
T.modify_ (_ + 1) tryCountBox
pure $ toEither res
----
try' :: forall box. T.ReadWrite box VForm
=> box
-> (Unit -> Effect VForm)
-> Effect EForm
try' resultBox thunk = do
res <- thunk unit <> (T.read resultBox)
T.write_ res resultBox
pure $ toEither res
asyncTry' :: forall box. T.ReadWrite box VForm
=> box
-> (Unit -> Aff VForm)
-> Aff EForm
asyncTry' resultBox thunk = do
fiber <- forkAff $ thunk unit
res <- (joinFiber fiber) <> (liftEffect $ T.read resultBox)
liftEffect $ T.write_ res resultBox
pure $ toEither res
----
removeError :: forall box. T.ReadWrite box VForm
=> box
-> Effect Unit
removeError = T.write_ (V (Right mempty) :: VForm)
removeError' :: forall box. T.ReadWrite box VForm
=> box
-> Field
-> Effect Unit
removeError' resultBox field = T.read resultBox >>=
case _ of
V (Right _) -> pure unit
V (Left err) -> do
res <- pure $ filter (\t -> not isDuplicate field t) err
T.write_ (invalid res) resultBox
removeError_ :: forall box. T.ReadWrite box VForm
=> box
-> Field
-> String
-> Effect Unit
removeError_ resultBox field error = T.read resultBox >>=
case _ of
V (Right _) -> pure unit
V (Left err) -> do
res <- pure $ filter (\t -> not isDuplicate' field error t) err
T.write_ (invalid res) resultBox
----
isDuplicate :: Field -> Tuple Field String -> Boolean
isDuplicate field = fst >>> eq field
isDuplicate' :: Field -> String -> Tuple Field String -> Boolean
isDuplicate' field error = a && b
where
a = fst >>> eq field
b = snd >>> eq error
-- | @TODO:
-- | Really thinking that I have reinvented the wheel here...
-- |
-- | As `VForm` are used as Semigroup, appending with `<>` will result on
-- | a collection of errors
-- |
-- | This method offers the primary use of `V` (ie. an `Either`) and so breaking
-- | computation if first validation rule was lefty
-- |
-- | ```purescript
-- | let
-- | a = onError
-- | b = onErrorToo
-- | in
-- | a <> b -- Will output two errors in the final `VForm`
-- | a <!> b -- Will only output the a) error
-- | ```
append' :: Effect VForm -> Effect VForm -> Effect VForm
append' a b = a >>= \a' ->
case toEither a' of
Left _ -> a
Right _ -> a <> b
infixr 5 append' as <!>
'use strict';
/**
* Email Pattern
* - provide a custom pattern based on regexp for email validation [1]
* - regexp based on RFC 2822 simplified version (see FCT-68) [2]
* @link https://validatejs.org/#validators-email [1]
* @link https://gist.github.com/gregseth/5582254 [2]
* @type {RegExp}
*/
exports.emailPattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
module Gargantext.Hooks.FormValidation.Types
( VForm, EForm, Field
, emailPattern
) where
import Gargantext.Prelude
import Data.Either (Either)
import Data.String.Regex (Regex)
import Data.Tuple (Tuple)
import Data.Validation.Semigroup (V)
foreign import emailPattern :: Regex
-- @TODO: types for errors (`Tuple Field String`)?
type Field = String
type EForm = Either (Array (Tuple Field String)) Unit
type VForm = V (Array (Tuple Field String)) Unit
module Gargantext.Hooks.FormValidation.Unboxed
( class Equals, equals
, class NonEmpty, nonEmpty
, class Minimum, minimum
, class Maximum, maximum
, lowercase, uppercase, email
) where
import Gargantext.Prelude
import Data.String (toLower, toUpper)
import Data.String.CodeUnits (length)
import Data.String.Regex (test)
import Data.Tuple.Nested ((/\))
import Data.Validation.Semigroup (invalid)
import Effect (Effect)
import Gargantext.Hooks.FormValidation.Types (Field, VForm, emailPattern)
class Eq a <= Equals a where
equals :: Field -> a -> a -> Effect VForm
class NonEmpty a where
nonEmpty :: Field -> a -> Effect VForm
class Ord a <= Minimum a where
minimum :: Field -> a -> Int -> Effect VForm
class Ord a <= Maximum a where
maximum :: Field -> a -> Int -> Effect VForm
-- Regarding String field value
instance equalsString :: Equals String where
equals field input input'
| (not eq input input') = pure $ invalid [ field /\ "equals" ]
| otherwise = pure $ pure unit
instance nonEmptyString :: NonEmpty String where
nonEmpty field "" = pure $ invalid [ field /\ "nonEmpty" ]
nonEmpty _ _ = pure $ pure unit
instance minimumString :: Minimum String where
minimum field input min
| (length input) < min = pure $ invalid [ field /\ "minimum" ]
| otherwise = pure $ pure unit
instance maximumString :: Maximum String where
maximum field input max
| (length input) > max = pure $ invalid [ field /\ "maximum" ]
| otherwise = pure $ pure unit
uppercase :: Field -> String -> Effect VForm
uppercase field input
| (toLower input) == input = pure $ invalid [ field /\ "uppercase" ]
| otherwise = pure $ pure unit
lowercase :: Field -> String -> Effect VForm
lowercase field input
| (toUpper input) == input = pure $ invalid [ field /\ "lowercase" ]
| otherwise = pure $ pure unit
email :: Field -> String -> Effect VForm
email field input
| (not $ test emailPattern input) = pure $ invalid [ field /\ "email" ]
| otherwise = pure $ pure unit
module Gargantext.Hooks.StateRecord
( module Gargantext.Hooks.StateRecord.Unboxed
) where
import Gargantext.Hooks.StateRecord.Unboxed
module Gargantext.Hooks.StateRecord.Behaviors
( setter
, binder
, TwoWayBinding
) where
import Gargantext.Prelude
import Effect (Effect)
import Record.Unsafe (unsafeGet, unsafeSet)
import Toestand as T
type TwoWayBinding a =
( callback :: a -> Effect Unit
, value :: a
)
-- | ```purescript
-- | formInput
-- | { callback: setter stateBox "label" }
-- | ```
setter :: forall box r a.
T.ReadWrite box (Record r)
=> box
-> String
-> a
-> Effect Unit
setter stateBox field value = T.modify_ (\prev -> unsafeSet field value prev) stateBox
-- | ```purescript
-- | formInput
-- | (binder stateBox state "label" }
-- | ```
binder :: forall box r a.
T.ReadWrite box (Record r)
=> box
-> Record r
-> String
-> Record (TwoWayBinding a)
binder stateBox state field =
{ callback: \value -> setter stateBox field value
, value: unsafeGet field state
}
module Gargantext.Hooks.StateRecord.Unboxed
( useStateRecord
, useStateRecord'
) where
import Gargantext.Prelude
import Data.Eq (class EqRecord)
import Effect (Effect)
import Gargantext.Hooks.StateRecord.Behaviors (TwoWayBinding, binder, setter)
import Gargantext.Utils.Reactix as R2
import Prim.RowList (class RowToList)
import Reactix as R
import Toestand as T
type Methods r a =
-- | Every provided props will be available within the `formFields` proxy
( state :: Record r
, stateBox :: T.Box (Record r)
-- | When binded with a form input, any form fields can be handled by
-- | providing this function
, setStateKey :: String -> a -> Effect Unit
-- | API method proposing a two way data binding (such as "v-model" of
-- | VueJS)
, bindStateKey :: String -> Record (TwoWayBinding a)
)
-- | Hooks inspired from this article
-- |
-- | https://blog.logrocket.com/forms-in-react-in-2020/
-- |
-- | ```purescript
-- |
-- | r <- useStateRecord defaultValues
-- |
-- | ...
-- |
-- | B.formInput
-- | { value: r.state.prop
-- | , callback: r.setStateKey "prop"
-- | }
-- |
-- | ...
-- |
-- | -- `bindStateKey` will add both `value: ...` and `callback: ...`
-- | B.formInput $
-- | { ... } `merge` r.bindStateKey "prop"
-- |
useStateRecord :: forall a r l.
RowToList r l
=> EqRecord l r
=> Record r
-> R.Hooks (Record (Methods r a))
useStateRecord = T.useBox >=> main
-- | Variant where `stateBox :: Box (Record r)` is already instanciated and
-- | provided
useStateRecord' :: forall a r l.
T.ReadWrite (T.Box (Record r)) (Record r)
=> RowToList r l
=> EqRecord l r
=> T.Box (Record r)
-> R.Hooks (Record (Methods r a))
useStateRecord' = main
main :: forall a r l.
T.ReadWrite (T.Box (Record r)) (Record r)
=> RowToList r l
=> EqRecord l r
=> T.Box (Record r)
-> R.Hooks (Record (Methods r a))
main stateBox = do
state <- R2.useLive' stateBox
pure
{ state
, stateBox
, setStateKey : (_ # stateBox # setter)
, bindStateKey: (_ # stateBox # binder $ state)
}
......@@ -2,26 +2,25 @@ module Gargantext.Utils.Reactix where
import Prelude
import Data.Array as A
import Data.Either (hush)
import Data.Function.Uncurried (Fn1, runFn1, Fn2, runFn2)
import Data.Maybe (Maybe(..), fromJust, fromMaybe, isJust)
import Data.Nullable (Nullable, null, toMaybe)
import Data.Tuple (Tuple(..))
import Data.Tuple.Nested ((/\))
import ConvertableOptions as CO
import DOM.Simple as DOM
import DOM.Simple.Console (log2)
import DOM.Simple.Document (document)
import DOM.Simple.Element as Element
import DOM.Simple.Event as DE
import DOM.Simple.Types (class IsNode, class IsElement, DOMRect)
import Data.Array as A
import Data.Either (hush)
import Data.Function.Uncurried (Fn1, runFn1, Fn2, runFn2)
import Data.Maybe (Maybe(..), fromJust, fromMaybe)
import Data.Nullable (Nullable, null, toMaybe)
import Data.Tuple (Tuple)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Console (logShow)
import Effect.Aff (Aff, launchAff, launchAff_, killFiber)
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, mkEffectFn1, mkEffectFn2, runEffectFn1, runEffectFn2, runEffectFn3)
import Effect.Unsafe (unsafePerformEffect)
import Effect.Uncurried (EffectFn1, EffectFn3, mkEffectFn1, mkEffectFn2, runEffectFn1, runEffectFn3)
import FFI.Simple ((..), (...), (.=), defineProperty, delay, args2, args3)
import Partial.Unsafe (unsafePartial)
import React (class ReactPropFields, Children, ReactClass, ReactElement)
......@@ -42,12 +41,46 @@ import Web.HTML (window)
import Web.HTML.Window (localStorage)
import Web.Storage.Storage (Storage, getItem, setItem)
type Module = String
-- | UI Component type with only required props and children
type Component p = Record p -> Array R.Element -> R.Element
-- | UI Component type with only required props and no child
type Leaf p = Record p -> R.Element
-- | UI Component type containing optional props and children
type OptComponent options props provided = CO.Defaults (Record options) (Record provided) (Record props)
=> Record provided -> Array R.Element -> R.Element
-- | UI Component type containing optional props and no child
type OptLeaf options props provided = CO.Defaults (Record options) (Record provided) (Record props)
=> Record provided -> R.Element
component :: forall cpt p. R.IsComponent cpt p (Array R.Element)
=> cpt -> Record p -> Array R.Element -> R.Element
component cpt props children = R.createElement cpt props children
leaf :: forall cpt p. R.IsComponent cpt p (Array R.Element)
=> cpt -> Record p -> R.Element
leaf cpt props = R.createElement cpt props []
optComponent :: forall r r' cpt p.
CO.Defaults r r' (Record p)
=> R.IsComponent cpt p (Array R.Element)
=> cpt -> r -> r' -> Array R.Element -> R.Element
optComponent cpt options props children = R.createElement cpt props' children where
props' = CO.defaults options props
optLeaf :: forall r r' cpt p.
CO.Defaults r r' (Record p)
=> R.IsComponent cpt p (Array R.Element)
=> cpt -> r -> r' -> R.Element
optLeaf cpt options props = R.createElement cpt props' [] where
props' = CO.defaults options props
-----------------------------------------
type Module = String
type Here =
{ component :: forall p. String -> R.HooksComponent p -> R.Component p
, log :: forall l. l -> Effect Unit
......@@ -438,6 +471,9 @@ boundingRect els =
, width: maxx - minx
, height: maxy - miny }
--------------------------------------
-- | One-liner `if` simplifying render writing
-- | (best for one child)
if' :: Boolean -> R.Element -> R.Element
......
......@@ -24,6 +24,31 @@
/// Mixins
///--------------------------
/// UX/UI rules regarding element that can be clicked and
/// others than <a/> tags
@mixin clickable() {
cursor: pointer;
&:focus { outline: 0; }
}
@mixin unclickable() {
cursor: default;
&:focus { outline: 0; }
}
@mixin unselectable() {
user-select: none;
}
@mixin disabled() {
cursor: not-allowed;
&:focus { outline: 0; }
}
@mixin gradient($primary, $secondary, $direction) {
background: linear-gradient($direction, $primary, $secondary);
}
/// Place contextualised element (&) at the root,
/// prefixed with left handed class
/// @access public
......@@ -41,3 +66,40 @@
@content;
}
}
/// Centering an element (you'll have to change the position
/// of the element though)
@mixin centered() {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
/// Mixin from "bootstrap v4"
/// @link https://getbootstrap.com/docs/4.3/utilities/clearfix/
@mixin clearfix() {
&::after {
display: block;
content: "";
clear: both;
}
}
/// Reverse an element
@mixin reversed() {
transform: scaleX(-1);
}
/// Add led effect with the input color
@mixin led($color) {
color: $color;
text-shadow: 0 0 4px brighten($color, 90);
}
/// Extend element to fit all four positions
@mixin fit-positions() {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
@use '../abstract/_members' as main;
@use "sass:math" as math;
///
/// @TODO: create "base", "module", "component" SASS business folders
///
/// @TODO: More SASS structure (eg. exporting theme variables into component
/// SASS files)
///
/// ############################################################################
/// MODULES
/// ############################################################################
@mixin inputError() {
border-color: $danger;
&:focus, &:active {
border-color: $danger;
box-shadow: 0 0 0 1px $danger;
}
}
/// ############################################################################
/// BASE
/// ############################################################################
/// Misc
///--------------------------
///-----------------------------------------------------------------------------
.with-icon-font {
font-family: ForkAwesome, $font-family-base;
......@@ -9,10 +36,8 @@
/// Tree
///--------------------------
///-----------------------------------------------------------------------------
/// (?) @TODO: More SASS structure (eg. exporting theme variables into component
/// SASS files)
.forest-layout {
@include main.right-handed {
......@@ -60,3 +85,190 @@
border-radius: $border-radius;
background-color: $body-bg;
}
/// Form
///-----------------------------------------------------------------------------
/// We made the choice to let the "form-group" element purely declarative, as
/// reality projected use cases are far too numerous. Making structural rules
/// (ie. making specific UI Component sets) wouldn't be a best practice.
.form-group {
$self: &;
position: relative;
margin-bottom: space-x(3);
&__label {
padding-bottom: space-x(0.5);
label {
font-weight: 600;
margin-bottom: initial; // reset Bootstrap "_reboot.scss"
}
&--sub {
color: $gray-800;
font-size: 14px;
}
}
&__field {
// (?) In most cases, we found that error directive integrated within
// the "form-group__field" reach actually breaks the design when it
// pops out
// Hence the `position: absolute` rule, mostly avoiding this UI break
#{ $self }__error,
#{ $self }__success {
position: absolute;
overflow: hidden;
// (below is the reversing process rule)
&--obtrusive {
position: static;
overflow: initial;
}
}
}
}
/// Managing error presence while using <form-group> component
/// This design is based on Bootstrap 2 form management
/// @link https://getbootstrap.com/2.3.2/base-css.html#Validation%20states
.form-group--error {
label { color: $danger; }
.b-form-input { @include inputError; }
}
.form-group {
&__error,
&__success,
&__warning {
padding-top: 2px;
font-size: 12px;
padding-left: 0.75rem;
}
&__error { color: $danger; }
&__success { color: $success; }
&__warning { color: $warning; }
}
.form-input {
// Based on "vue-bootstrap" implementation of SVG icon within the input
&--address {
padding-right: calc(1.5em + 0.75rem);
background-repeat: no-repeat;
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-position: right calc(0.375em + 0.1875rem) center;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-4.198 0-8 3.403-8 7.602 0 4.198 3.469 9.21 8 16.398 4.531-7.188 8-12.2 8-16.398 0-4.199-3.801-7.602-8-7.602zm0 11c-1.657 0-3-1.343-3-3s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3z"/></svg>');
}
}
/// ############################################################################
/// COMPONENTS
/// ############################################################################
/// FormInput
///-----------------------------------------------------------------------------
.b-form-input {
&[type=password] {
letter-spacing: space-x(1);
font-weight: 700;
}
}
/// Modal
///-----------------------------------------------------------------------------
.b-modal {
$self: &;
$wrapper-margin: 1.75rem; // from "_modal.scss"
&--visible {
display: block;
}
&__content {
background-color: $modal-content-bg;
max-height: calc(100% - #{ $wrapper-margin * 2 } );
overflow: auto;
}
&__header {
background-color: $card-cap-bg;
&__content {
color: $primary;
font-size: 21px;
}
}
&__overlay {
width: 100%;
height: 100%;
background-color: mixAlpha($black, 50%);
position: absolute;
&--collapsible {
cursor: pointer;
}
}
// @at-root body.modal-open {
// overflow: hidden;
// }
}
/// Spinner
///-----------------------------------------------------------------------------
.b-spinner {
// regarding sizing, Bootstrap set a default `2rem ^ 2rem`
// (cf. "_spinners.scss")
font-size: inherit;
}
/// Button
///-----------------------------------------------------------------------------
.b-button {
$spinner-size: 20px;
$self: &;
@include clickable();
position: relative;
// (!) `translate` CSS modifiers won't work with bootstrap spinner
&__spinner {
position: absolute;
left: calc(50% - #{ math.div($spinner-size, 2) });
top: calc(50% - #{ math.div($spinner-size, 2) });
width: $spinner-size;
height: $spinner-size;
}
&--disabled,
&--deferred,
&--idled {
@include unclickable();
}
&--deferred {
#{ $self }__inner {
visibility: hidden;
}
}
// icon alignement
.b-icon {
vertical-align: text-top;
}
}
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