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

AutocompleteTextField component deep refactoring and fix the maximum size of...

AutocompleteTextField component deep refactoring and fix the maximum size of its dropdown list. Improve MaterialNameBox and FormulaBox css layouts
parent ce4e8cab
Pipeline #93371 skipped with stage
window.nomadEnv = { window.nomadEnv = {
//apiRoot: "https://nomad-lab.eu/dev/nomad/enc-search/api/encyclopedia/", //apiRoot: "https://nomad-lab.eu/dev/nomad/enc-search/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/", apiRoot: "https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/",
apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/", //apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/", //apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/",
......
...@@ -485,79 +485,95 @@ div.title span.unfolded::before{ ...@@ -485,79 +485,95 @@ div.title span.unfolded::before{
cursor: default; cursor: default;
} }
#formula-box, #material-name-box{ /* Formula and Material name boxes */
.FormulaBox, .MaterialNameBox{
display: flex;
justify-content: center;
text-align: left; text-align: left;
background-color: #DDD; background-color: #DDD;
padding: 40px 0; padding: 40px 0;
} }
.textfield-composition{ .FormulaBox input, .MaterialNameBox input{
padding: 10px; padding: 10px;
border: 0; border: 0;
font-size: 0.9em; font-size: 0.9em;
}
.MaterialNameBox input{
box-sizing: border-box;
width: 100%;
}
.FormulaBox input{
box-sizing: border-box;
width: 450px; width: 450px;
margin-left: 50px;
} }
/*
.textfield-composition{
padding: 10px;
border: 0;
font-size: 0.9em;
box-sizing: border-box;
width: 100%;
}*/
.textfield-filter{ .textfield-filter{
padding: 6px; padding: 6px;
border: 1px solid #DDD; border: 1px solid #DDD;
width: 200px; width: 200px;
} }
.autocomplete-textfield-materialname{
position: relative;
}
.autocomplete-textfield-strukturbericht, .autocomplete-textfield-structuretype { .material-name-autocomplete-textfield, .material-name-autocomplete-dropdown{
position: relative; width: 450px;
} }
.autocomplete-items{
font-size: 0.9em;
position: absolute;
z-index: 99;
}
.autocomplete-items-materialname{
margin-left: 50px;
width: 470px;
}
.autocomplete-items div { /* Autocomplete component */
padding: 2px 10px 2px 10px;
cursor: pointer;
background-color: #DDD;
border: 1px solid transparent;
}
.autocomplete-items input { .AutocompleteTextField{
vertical-align: middle; display: inline-block;
} }
.autocomplete-items-strukturbericht, .autocomplete-items-structuretype{ .AutocompleteTextField-dropdown{
margin-left: 0px; font-size: 0.9em;
width: 220px; position: absolute;
z-index: 99;
box-shadow: 1px 1px 4px gray;
} }
/* .AutocompleteTextField-dropdown > div {
.autocomplete-items-strukturbericht div {
padding: 2px 10px 2px 10px; padding: 2px 10px 2px 10px;
cursor: pointer; cursor: pointer;
background-color: #DDD; background-color: #DDD;
border: 1px solid transparent; border: 1px solid transparent;
} }
*/
.autocomplete-active { .autocomplete-active {
border-color: #E56400 !important; border-color: #E56400 !important;
} }
/* To remove
.autocomplete-em { .autocomplete-em {
font-weight: bold; font-weight: bold;
} }
.autocomplete-items-strukturbericht, .autocomplete-items-structuretype{
margin-left: 0px;
width: 220px;
} */
.adding-formula-btn, .adding-name-btn{ .adding-formula-btn, .adding-name-btn{
padding: 9px; padding: 9px;
border: 1px solid #999; border: 1px solid #999;
......
...@@ -22,201 +22,9 @@ ...@@ -22,201 +22,9 @@
"use strict"; "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 { class AutocompleteMultiselectTextfield {
constructor(name = "") { constructor(name = "") {
this.classPostfix = name; this.classPostfix = name;
...@@ -471,6 +279,4 @@ class AutocompleteMultiselectList { ...@@ -471,6 +279,4 @@ class AutocompleteMultiselectList {
// EXPORTS // EXPORTS
module.exports = { AutocompleteTextField: AutocompleteTextField, module.exports = AutocompleteMultiselectTextfield;
AutocompleteMultiselectList: AutocompleteMultiselectList
};
/**
* 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(id = "", placeholder = '', allowEmptyInput = false) {
this.id = id;
this.element = document.createElement('div');
//this.element.style.display = 'inline';
this.element.className = `AutocompleteTextField ${id}-autocomplete-textfield`;
this.element.innerHTML = `
<input type="text" placeholder="${placeholder}" /> <!-- class="autocomplete-textfield-${this.id}" /> -->
<div class="AutocompleteTextField-dropdown ${this.id}-autocomplete-dropdown"></div> <!-- autocomplete-items- -->
`;
this.input = this.element.querySelector('input');
this.listContainer = this.element.querySelector('div');
this.selectListener;
// state
this.valueList; // List of autocomplete (possible) values
this.allowEmptyInput = allowEmptyInput;
// event management
this.input.addEventListener("input", (e) => {
this._processInput();
});
// react to keyboard navigation
this.input.addEventListener("keydown", (e) => {
if (e.keyCode == 40) { // arrow DOWN
if (this._getActiveListItem())
this._setActiveListItem(this._getActiveListItem().nextSibling);
} else if (e.keyCode == 38) { // arrow UP
if (this._getActiveListItem())
this._setActiveListItem(this._getActiveListItem().previousSibling);
}
});
// react to enter key
this.input.addEventListener("keypress", e => {
if (e.keyCode == 13) { // ENTER
// simulate a click on the "active" item
this._clickOnActiveItem();//_clickActive();
}
});
// react to clicking into the textfield
this.input.addEventListener("click", (e) => {
this._processInput();
e.stopPropagation();
});
// close lists when someone clicks in the document
document.addEventListener("click", e => {
this._cleanList();// this._closeAllLists();
});
this.listContainer.addEventListener("click", e => {
let listItem = event.target.closest('div'); // (1)
this.setValue(listItem.textContent);
});
this.listContainer.addEventListener("mouseover", e => {
let listItem = event.target.closest('div'); // (1)
this._setActiveListItem(listItem);
});
}
getValue(){
return this.input.value;
}
resetValue(){
this.input.value = '';
}
disable(bool){
this.input.disabled = bool;
}
setAutocompleteList(valueList){
this.valueList = valueList;
}
setSelectListener(listener) {
this.selectListener = listener;
}
_processInput() {
const currentInput = this.input.value;
// close any already open lists of autocompleted values
this._cleanList();//this._closeAllLists();
// in case of an empty input field
if (!this.allowEmptyInput && !currentInput) {
return false;
}
// for each autocomplete value
let counter = 0;
const matchingValues = this.valueList.filter( value => {
const matching = value.toUpperCase().includes(currentInput.toUpperCase());
if (matching) counter++;
return counter <= 15 && matching;
});
this.listContainer.innerHTML = '';
matchingValues.forEach( value => {
const listItem = generateListItem(value, currentInput);
this.listContainer.append(listItem);
// check if a valid option was completely entered. if so, set the focus to the element corresponding to the input
if (value.toUpperCase() === currentInput.toUpperCase())
this._setActiveListItem(listItem);
function generateListItem(value, inputText, itemIndex) {
const listItem = document.createElement("div");
if (inputText){
const pos = value.toUpperCase().indexOf(inputText.toUpperCase()); // console.log('pos', pos)
listItem.innerHTML +=
`${value.substring(0, pos)}<strong>${value.substring(pos, pos+inputText.length)}</strong>${value.substring(pos + inputText.length)}`;
}else
listItem.innerHTML = acText;
return listItem;
}
});
}
setValue(value) {
// insert the value to the text field
this.input.value = value;
// notify listener
if (this.selectListener) this.selectListener();
// close the dropdown list of autocompleted values
this._cleanList();//this._closeAllLists();
}
_getActiveListItem(){
return this.listContainer.querySelector('.autocomplete-active');
}
_setActiveListItem(element){
const currentActiveItem = this.listContainer.querySelector('.autocomplete-active');
if (currentActiveItem) currentActiveItem.classList.remove('autocomplete-active');
element.classList.add('autocomplete-active');
}
_clickOnActiveItem() {
const activeItem = this.listContainer.querySelector('.autocomplete-active');
if (activeItem) activeItem.click();
}
_cleanList() {
this.listContainer.innerHTML = '';
}
}
// EXPORTS
module.exports = AutocompleteTextfield;
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
let util = require('../common/util.js'); let util = require('../common/util.js');
let InfoSys = require('../common/InfoSys.js'); let InfoSys = require('../common/InfoSys.js');
let AutocompleteMultiselectList = require('./Autocomplete.view.js').AutocompleteMultiselectList; let AutocompleteMultiselectTextfield = require('./AutocompleteMultiselectTextfield.js');
const NOT_SELECTED_OPACITY = 0.2 const NOT_SELECTED_OPACITY = 0.2
...@@ -281,7 +281,8 @@ class AutocompleteField{ ...@@ -281,7 +281,8 @@ class AutocompleteField{