Commit 5e855ad0 authored by Iker Hurtado's avatar Iker Hurtado
Browse files

Manual merge/integration of "new-search" branch into master + Several new search improvements (1)

The improvements are specified in a Gitlab document: https://gitlab.mpcdf.mpg.de/nomad-lab/encyclopedia-general/-/wikis/A2-new-search-tasks
parent 7bb5dd1e
Pipeline #89753 skipped with stage
......@@ -347,26 +347,41 @@ div.title span.unfolded::before{
/******+ SEARCH BAR *********/
#search-module{
width: 62%; margin: 10px auto;
}
/*width: 62%; margin: 10px auto;*/
display: flex;
margin-top: 10px;
justify-content: space-evenly;/*center;*/
.searchline{
}
.search-option {
padding: 10px 0px;
font-size: 0.85em;
.search-title{
color: #E56400; font-size: 15pt; font-weight: bold;
text-align: center;
}
.search-option>input {
margin-top:-0px;
vertical-align: middle;
.search-filter-side{
background-color: #DDD;
width: 240px;
padding: 10px 16px;
}
.search-box{
width: 742px;
display: flex;
justify-content: center; /*space-between;*/
}
.search-query-wrapper{
width: 85%;
flex-grow: 14;
background-color: white; border: 2px solid #E56400;
padding: 8px 6px;
}
.clean-btn{
padding: 6px;
background-color: white;
......@@ -378,12 +393,15 @@ div.title span.unfolded::before{
}
.search-btn{
width: 94px;
/*width: 94px;*/
display: block;
flex-grow: 1;
background-color: #E56400;
color: white;
padding: 16px;
}
.search-query-symbol{
font-size: 1.3em;
font-weight: bold;
......@@ -395,25 +413,28 @@ div.title span.unfolded::before{
width: 100%;
margin: 40px 0 0;
padding: 20px 0;
text-align: center;
}
.bool-buttons button{
border: 1px solid #BBB;
color: #BBB;
cursor: inherit;
color: #777;
border: 1px solid #777;
background-color: #EEE;
text-align: center;
}
.tab-buttons button{
display: inline-block;
background-color: #EEE;
width: 115px;
padding: 10px 30px;
margin: 0 10px;
font-size: .9em;
color: #777;
border: 1px solid #777;
text-align: center;
}
.tab-buttons button#add-tab-selected{
......@@ -429,17 +450,24 @@ div.title span.unfolded::before{
}
.triangle-container{
width: 115px;
display: inline-block;
margin: 0px 10px;
text-align: center;
}
.triangle{
display: inline-block;
width: 0; height: 0;
width: 0;
height: 0;
margin: -10px 0px;
border-right: 30px solid transparent;
border-top: 30px solid transparent;
border-left: 30px solid transparent;
border-bottom: 30px solid #DDD;
}
.add-panel button{
background-color: #EEE;
color: #777;
......@@ -452,70 +480,88 @@ div.title span.unfolded::before{
cursor: default;
}
#formula-box{
text-align: center;
#formula-box, #material-name-box{
text-align: left;
background-color: #DDD;
padding: 40px 0;
}
.formula-text-field, .material-name-text-field{
.textfield-composition{
padding: 10px;
border: 0;
font-size: 0.9em;
width: 60%;
width: 450px;
margin-left: 50px;
}
.adding-formula-btn, .adding-material-name-btn{
padding: 9px;
border: 1px solid #999;
.textfield-filter{
padding: 6px;
border: 1px solid #DDD;
width: 200px;
}
#search-properties-box{
background-color: #DDD;
padding: 4px;
height: 330px;
.autocomplete-textfield-materialname{
position: relative;
}
.props-box-tabs-wrapper{
background-color: #EEE;
height: 100%;
.autocomplete-textfield-strukturbericht, .autocomplete-textfield-structuretype {
position: relative;
}
.properties-box-tabs div{
padding: 20px;
.autocomplete-items{
font-size: 0.9em;
position: absolute;
z-index: 99;
}
border-bottom: 1px solid #DDD;
background-color: #EEE;
.autocomplete-items-materialname{
margin-left: 50px;
width: 470px;
}
.autocomplete-items div {
padding: 2px 10px 2px 10px;
cursor: pointer;
text-align: center;
background-color: #DDD;
border: 1px solid transparent;
}
.autocomplete-items input {
vertical-align: middle;
}
.properties-box-tabs div.props-tab-selected{
font-weight: bold;
color: #E56400;
background-color: #DDD;
.autocomplete-items-strukturbericht, .autocomplete-items-structuretype{
margin-left: 0px;
width: 220px;
}
/*
.autocomplete-items-strukturbericht div {
padding: 2px 10px 2px 10px;
cursor: pointer;
background-color: #DDD;
border: 1px solid transparent;
.props-tab-panel{
padding: 10px 60px;
font-size: 0.8em;
display: none;
}
*/
.autocomplete-active {
border-color: #E56400 !important;
}
.properties-box-enter-button{
text-align: right;
.autocomplete-em {
font-weight: bold;
}
.properties-box-enter-button button{
margin: 8px 20px;
.adding-formula-btn, .adding-name-btn{
padding: 9px;
border: 1px solid #999;
}
.field{
margin-bottom: 16px;
.filter-quantity-box{
margin-bottom: 12px;
padding: 0px 0px 4px 4px ;
}
.field-title{
......@@ -524,6 +570,13 @@ div.title span.unfolded::before{
font-weight: bold;
}
/* equivalent to view-box */
.filter-section-box{
margin: 16px 8px;
background-color: white;
}
input[type='range'] {
margin: 0;
-webkit-appearance: none;
......@@ -732,6 +785,12 @@ span.page{ display: inline-block; }
span.nextButton{ display: inline-block; cursor: pointer;}
.data-container table{
margin: auto;
width: 96%;
}
/*#paginationWg .left { float: left }
#paginationWg .right { float: right }*/
......
......@@ -32,7 +32,7 @@ let FlaggingFormPopup = require('./common/FlaggingFormPopup.js');
let PubSub = require('./common/PubSub.js');
let Router = require('./common/Router.js');
let MaterialMod = require('./material-mod/MaterialMod.js');
let SearchModule = require('./search-mod/SearchMod.js');
let SearchModule = require('./search-mod/NewSearchMod.js');//require('./search-mod/SearchMod.js');
let UserGuidance = require('./common/UserGuidance.js');
let DataStore = require('./material-mod/DataStore.js');
......
/**
* Copyright 2019-2019 Georg Huhs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
Basic class for extending a text field with autocomplete functionality
*/
"use strict";
class AutocompleteTextField {
constructor(name = "", allowEmptyInput = false) {
this.classPostfix = name;
this.allowEmptyInput = allowEmptyInput;
this.element = document.createElement('input');
this.element.type = 'text';
this.element.className = 'autocomplete-textfield-' + this.classPostfix;
this.currentFocus = -1;
this.selectListener = undefined;
}
replaceElement(oldElement) {
oldElement.parentElement.replaceChild(this.element, oldElement);
}
setSelectListener(listener) {
this.selectListener = listener;
}
autocomplete(allAcValues) {
/* the autocomplete function takes an array of possible autocomplete values.
in the following we will use 'ac' as abbrevation for 'autocomplete'
*/
/* process input when someone writes in the text field:*/
this.element.addEventListener("input", (e) => {
this._processInput(allAcValues);
});
/* react to keyboard navigation */
this.element.addEventListener("keydown", (e) => {
if (e.keyCode == 40) { // arrow DOWN
this._setActive(this.currentFocus + 1);
} else if (e.keyCode == 38) { // arrow UP
this._setActive(this.currentFocus - 1);
}
});
/* react to enter key */
this.element.addEventListener("keypress", (e) => {
if (e.keyCode == 13) { // ENTER
/* simulate a click on the "active" item:*/
this._clickActive();
}
});
/* react to klicking into the textfield */
this.element.addEventListener("click", (e) => {
this._processInput(allAcValues);
e.stopPropagation();
});
// close lists when someone clicks in the document:*/
document.addEventListener("click", (e) => {
this._closeAllLists();
});
}
_processInput(allAcValues) {
let currentInput = this.element.value;
/*close any already open lists of autocompleted values*/
this._closeAllLists();
// in case of an empty input field
if (!this.allowEmptyInput && !currentInput) {
return false;
}
/*create a DIV element that will contain the items (values):*/
let listContainer = document.createElement("DIV");
listContainer.setAttribute("id", "autocomplete-list");
listContainer.classList.add("autocomplete-items");
listContainer.classList.add("autocomplete-items-"+ this.classPostfix);
/*append the DIV element as a child of the autocomplete container:*/
this.element.parentNode.appendChild(listContainer);
/*for each item in the array...*/
let acItemIndex = 0;
for (var i = 0; i < allAcValues.length; i++) {
/*check if the item contains the same letters as the text field value:*/
let acValue = allAcValues[i];
let pos = 0;
if (currentInput) {
pos = acValue.toUpperCase().search(currentInput.toUpperCase());
}
if (pos >= 0){
let listItem = this._generateListItem(acValue,
currentInput,
acItemIndex)
listContainer.appendChild(listItem);
/* check if a valid option was completely entered.
if so, set the focus to the element corresponding to the input */
if (acValue.toUpperCase() === currentInput.toUpperCase()) {
this._setActive(acItemIndex);
}
acItemIndex++;
}
}
}
_generateListItem(acText, inputText, itemIndex) {
/*create a DIV element for each matching element:*/
let listItem = document.createElement("DIV");
/*make the matching letters bold:*/
if (inputText){
let pos = acText.toUpperCase().search(inputText.toUpperCase());
listItem.innerHTML = acText.substr(0, pos);
listItem.innerHTML += "<strong>";
listItem.innerHTML += acText.substr(pos, inputText.length);
listItem.innerHTML += "</strong>";
listItem.innerHTML += acText.substr(pos + inputText.length);
} else {
listItem.innerHTML = acText;
}
/* clicking on the as list item puts selects the corresponding name for searching */
listItem.addEventListener("click", (e) => {
/*insert the value for the autocomplete text field:*/
this._setText(acText);
});
/* hovering puts the focus on the related list item */
listItem.addEventListener("mouseover", (e) => {
this._setActive(itemIndex);
});
return listItem;
}
_setText(value) {
/*insert the value for the autocomplete text field:*/
this.element.value = value;
/* notify listener */
if (this.selectListener) {
this.selectListener();
}
/*close the list of autocompleted values,
(or any other open lists of autocompleted values)*/
this._closeAllLists();
}
_setActive(index) {
let listItems = document.getElementById("autocomplete-list")
.getElementsByTagName("div");
/* remove the active status from all list items */
Array.from(listItems).forEach(item => {
item.classList.remove("autocomplete-active");
});
/* ensure to stay in the list
out of boundary indices are mapped to the closest border */
let newFocus = Math.max(0, index);
newFocus = Math.min(newFocus, listItems.length-1);
this.currentFocus = newFocus;
/* mark the active status by a style class */
listItems[newFocus].classList.add("autocomplete-active");
}
_clickActive() {
if (this.currentFocus > -1) {
let listItems = document.getElementById("autocomplete-list")
.getElementsByTagName("div");
listItems[this.currentFocus].click();
}
}
_closeAllLists() {
/*close all autocomplete lists in the document */
let allAcLists = document.getElementsByClassName("autocomplete-items");
for (let acList of allAcLists) {
acList.parentNode.removeChild(acList);
}
this.currentFocus = -1;
}
}
class AutocompleteMultiselectList {
constructor(name = "") {
this.classPostfix = name;
this.element = document.createElement('input');
this.element.type = 'text';
this.element.className = 'autocomplete-multiselectlist-' + this.classPostfix;
this.currentFocus = -1;
this.selectListener = undefined;
/* the items member variable keeps track of the selected options
by storing a (sorted) list of all possible values and their selection state.
A list entry looks like {"value": "A1", "selected": false}
*/
this.items = [];
}
replaceElement(oldElement) {
oldElement.parentElement.replaceChild(this.element, oldElement);
}
setSelectListener(listener) {
this.selectListener = listener;
}
autocomplete(allAcValues) {
/* the autocomplete function takes an array of possible autocomplete values.
in the following we will use 'ac' as abbrevation for 'autocomplete'
*/
/* store possible values and initialize them as not selected */
for (var i = 0; i < allAcValues.length; i++) {
this.items.push({value: allAcValues[i],
selected: false}
);
}
/* process input when someone writes in the text field:*/
this.element.addEventListener("input", (e) => {
this._processInput(allAcValues);
});
/* react to keyboard navigation */
this.element.addEventListener("keydown", (e) => {
if (e.keyCode == 40) { // arrow DOWN
this._setActive(this.currentFocus + 1);
} else if (e.keyCode == 38) { // arrow UP
this._setActive(this.currentFocus - 1);
} else if (e.keyCode == 27) { // ESC key
this.element.value = '';
this._closeAllLists();
}
});
/* react to enter key */
this.element.addEventListener("keypress", (e) => {
if (e.keyCode == 13) { // ENTER
/* simulate a click on the "active" item:*/
this._clickActive();
}
});
/* react to klicking into the textfield */
this.element.addEventListener("click", (e) => {
this._processInput();
e.stopPropagation();
});
// close lists when someone clicks in the document:*/
document.addEventListener("click", (e) => {
this.element.value = '';
this._closeAllLists();
});
}
getSelected() {
let values = [];
for (let item of this.items) {
if (item.selected) {
values.push(item.value);
}
}
return values;
}
_processInput() {
let currentInput = this.element.value;
/*close any already open lists of autocompleted values*/
this._closeAllLists();
/*create a DIV element that will contain the items (values):*/
let listContainer = document.createElement("DIV");
listContainer.setAttribute("id", "autocomplete-list");
listContainer.classList.add("autocomplete-items");
listContainer.classList.add("autocomplete-items-"+ this.classPostfix);
/*append the DIV element as a child of the autocomplete container:*/
this.element.parentNode.appendChild(listContainer);
/* keyboard interaction */
listContainer.setAttribute("tabindex", "0");
listContainer.addEventListener("keydown", (e) => {
if (e.keyCode == 40) { // arrow DOWN
this._setActive(this.currentFocus + 1);
e.preventDefault();
} else if (e.keyCode == 38) { // arrow UP
this._setActive(this.currentFocus - 1);
e.preventDefault();
} else if (e.keyCode == 27) { // ESC key
this.element.value = '';