Commit e35871eb authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Merge branch 'enc-search' into enc-search-logic

parents 66bf2d5b 08e45aff
...@@ -522,36 +522,26 @@ div.title span.unfolded::before{ ...@@ -522,36 +522,26 @@ div.title span.unfolded::before{
}*/ }*/
.textfield-filter{
padding: 6px;
border: 1px solid #DDD;
width: 200px;
}
.material-name-autocomplete-textfield, .material-name-autocomplete-dropdown{
width: 450px;
}
/* Autocomplete component */ /* Autocomplete components */
.AutocompleteTextField{ .AutocompleteTextField{
display: inline-block; display: inline-block;
} }
.AutocompleteTextField-dropdown{ .AutocompleteTextField-dropdown, .AutocompleteMultiselectTextfield-dropdown{
font-size: 0.9em; font-size: 0.9em;
position: absolute; position: absolute;
z-index: 99; z-index: 99;
box-shadow: 1px 1px 4px gray;
} }
.AutocompleteTextField-dropdown > div { .AutocompleteTextField-dropdown > div, .AutocompleteMultiselectTextfield-dropdown > div {
padding: 2px 10px 2px 10px; padding: 2px 10px 2px 10px;
cursor: pointer; cursor: pointer;
background-color: #DDD;
border: 1px solid transparent; border: 1px solid transparent;
} }
...@@ -559,6 +549,48 @@ div.title span.unfolded::before{ ...@@ -559,6 +549,48 @@ div.title span.unfolded::before{
border-color: #E56400 !important; border-color: #E56400 !important;
} }
.AutocompleteMultiselectTextfield-selected-box{
padding: 3px;
}
.selectedItemLabel{
display: inline-block;
border-left: 1px solid #E56400;border-right: 1px solid #E56400;
color: #E56400;
/*box-shadow: 1px 1px 2px #BBB;*/
border-radius: 3px;
padding: 1px 4px;
margin-right: 8px; margin-top: 3px;
cursor: pointer;
}
/* Material name autocomplete */
.material-name-autocomplete-textfield{
width: 450px;
}
.material-name-autocomplete-dropdown{
background-color: #DDD;
box-shadow: 1px 1px 4px gray;
}
/* Structure type autocomplete */
.structure_type-autocomplete-multiselect-textfield input[type="text"]{
padding: 6px;
border: 1px solid #DDD;
width: 200px;
}
.structure_type-autocomplete-multiselect-dropdown{
width: 210px;
background-color: white;
box-shadow: 0 1px 2px #E56400;
}
/* To remove /* To remove
.autocomplete-em { .autocomplete-em {
......
...@@ -26,255 +26,199 @@ ...@@ -26,255 +26,199 @@
class AutocompleteMultiselectTextfield { class AutocompleteMultiselectTextfield {
constructor(name = "") { constructor(id = "", placeholder = '', allowEmptyInput = true) {
this.classPostfix = name; this.id = id;
this.element = document.createElement('input'); this.element = document.createElement('div');
this.element.type = 'text'; //this.element.style.display = 'inline';
this.element.className = 'autocomplete-multiselectlist-' + this.classPostfix; this.element.className = `AutocompleteMultiselectTextfield ${id}-autocomplete-multiselect-textfield`;
this.currentFocus = -1;
this.selectListener = undefined; this.element.innerHTML = `
<input type="text" placeholder="${placeholder}" />
/* the items member variable keeps track of the selected options <div class="AutocompleteMultiselectTextfield-selected-box"></div>
by storing a (sorted) list of all possible values and their selection state. <div class="AutocompleteMultiselectTextfield-dropdown ${this.id}-autocomplete-multiselect-dropdown"></div>
A list entry looks like {"value": "A1", "selected": false} `;
*/
this.items = []; this.input = this.element.querySelector('input');
} this.selectedItemsBox = this.element.querySelector('.AutocompleteMultiselectTextfield-selected-box');
this.listContainer = this.element.querySelector('.AutocompleteMultiselectTextfield-dropdown');
replaceElement(oldElement) {
oldElement.parentElement.replaceChild(this.element, oldElement); this.selectListener;
}
// state
setSelectListener(listener) { this.valueList; // List of autocomplete (possible) values
this.selectListener = listener; this.selectedValues = new Set();
} this.allowEmptyInput = allowEmptyInput;
autocomplete(allAcValues) { // event management
/* the autocomplete function takes an array of possible autocomplete values.
in the following we will use 'ac' as abbrevation for 'autocomplete' // When clicking on the textfield the dropdown shows up
*/ this.input.addEventListener("click", (e) => {
this._processInput();
/* store possible values and initialize them as not selected */ e.stopPropagation();
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:*/ // the dropdown list is updated as the user writes on the textfield
this.element.addEventListener("input", (e) => { this.input.addEventListener("input", (e) => {
this._processInput(allAcValues); this._processInput();
}); });
/* react to keyboard navigation */ // The dropdown is hiden when the mouse leaves the component area
this.element.addEventListener("keydown", (e) => { this.element.addEventListener("mouseleave", e => {
if (e.keyCode == 40) { // arrow DOWN this._cleanList();
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 */ // Handle the item removal on the selected items box
this.element.addEventListener("keypress", (e) => { this.selectedItemsBox.addEventListener("click", e => {
if (e.keyCode == 13) { // ENTER let itemLabel = event.target.closest('span');
/* simulate a click on the "active" item:*/ this._removeSelectedValue(itemLabel.dataset.value);
this._clickActive(); //itemLabel.dispatchEvent(new Event('change'));
}
}); });
/* react to klicking into the textfield */ // Handle the item selection on the dropdown
this.element.addEventListener("click", (e) => { this.listContainer.addEventListener("click", e => {
this._processInput(); let listItem = event.target.closest('div');
e.stopPropagation(); this._toggleItem(listItem);
}); });
// close lists when someone clicks in the document:*/
document.addEventListener("click", (e) => { this.listContainer.addEventListener("mouseover", e => {
this.element.value = ''; let listItem = event.target.closest('div');
this._closeAllLists(); this._setActiveListItem(listItem);
}); });
} }
getSelected() {
let values = []; getValues(){
for (let item of this.items) { return Array.from(this.selectedValues);
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 = '';
this._closeAllLists();
}
});
listContainer.addEventListener("keypress", (e) => {
if (e.keyCode == 13) { // ENTER
/* simulate a click on the "active" item:*/
this._clickActive();
}
});
/* show all items matching the input text */ /*resetValue(){
let acItemIndex = 0; this.input.value = '';
for (let item of this.items) { }*/
/*check if the item contains the same letters as the text field value:*/
let acValue = item.value;
let acSelected = item.selected;
let pos = 0;
if (currentInput) {
pos = acValue.toUpperCase().search(currentInput.toUpperCase());
}
/* if there is no input text given, pos = 0 and thus an item is generated */
if (pos >= 0){
let listItem = this._generateListItem(acValue,
acSelected,
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++;
}
}
disable(bool){
this.input.disabled = bool;
} }
_generateListItem(acText, selected, inputText, itemIndex) {
/*create a DIV element for each matching element:*/ setAutocompleteList(valueList){
let listItem = document.createElement("div"); this.valueList = valueList;
let itemCheckbox = document.createElement("input"); }
itemCheckbox.type = "checkbox";
itemCheckbox.checked = selected;
/* TODO: check why catching this event is necessary */ setSelectListener(listener) {
itemCheckbox.addEventListener("click", e => { this.selectListener = listener;
listItem.click(); }
e.stopPropagation();
});
listItem.appendChild(itemCheckbox); _processInput() {
/*make the matching letters bold:*/ const currentInput = this.input.value;
if (inputText && inputText != ""){ // close any already open lists of autocompleted values
let pos = acText.toUpperCase().search(inputText.toUpperCase()); this._cleanList();//this._closeAllLists();
listItem.appendChild(document.createTextNode(acText.substr(0, pos)));
let emText = document.createElement("span"); // in case of an empty input field
emText.className = "autocomplete-em"; if (!this.allowEmptyInput && !currentInput) {
emText.innerHTML = acText.substr(pos, inputText.length); return false;
/* TODO: check why catching this event is necessary */
emText.addEventListener("click", e => {
listItem.click();
e.stopPropagation();
});
listItem.appendChild(emText);
listItem.appendChild(document.createTextNode(acText.substr(pos + inputText.length)));
} else {
listItem.appendChild(document.createTextNode(acText));
} }
/* clicking on the AS list item puts selects the corresponding name for searching */ // for each autocomplete value
listItem.addEventListener("click", (e) => { let counter = 0;
let checkbox = e.target.getElementsByTagName("input")[0]; const matchingValues = this.valueList.filter( value => {
this._toggleSelect(acText, checkbox); const matching = value.toUpperCase().includes(currentInput.toUpperCase());
e.stopPropagation(); if (matching) counter++;
return counter <= 15 && matching;
}); });
//console.log('matchingValues', matchingValues)
this.listContainer.innerHTML = '';
matchingValues.forEach( value => {
const listItem = generateListItem(value, currentInput, this.selectedValues.has(value));
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, present) {
const listItem = document.createElement("div");
let innerHTML = `<input type="checkbox" data-value="${value}" ${present ? 'checked' : ''}>`;
if (inputText){
const pos = value.toUpperCase().indexOf(inputText.toUpperCase()); // console.log('pos', pos)
innerHTML +=
`${value.substring(0, pos)}<strong>${value.substring(pos, pos+inputText.length)}</strong>${value.substring(pos + inputText.length)}`;
}else
innerHTML += value;
listItem.innerHTML = innerHTML;
return listItem;
}
/* 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();
// }
_toggleSelect(value, checkbox) {
let newSelected;
for (let item of this.items) {
if (item.value == value) {
newSelected = !item.selected;
item.selected = newSelected;
break;
}
}
checkbox.checked = newSelected;
} }
_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 _removeSelectedValue(value) {
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 */ this.selectedValues.delete(value);
listItems[newFocus].classList.add("autocomplete-active"); this.selectedItemsBox.querySelector('span[data-value="'+value+'"]').remove();
// check if the dropdown is unfolded and uncheck the checkbox if so
const itemCheckbox = this.listContainer.querySelector('input[data-value="'+value+'"]');
if (itemCheckbox) itemCheckbox.checked = false;
} }
_clickActive() {
if (this.currentFocus > -1) { _toggleItem(listItem) {
let listItems = document.getElementById("autocomplete-list") const value = listItem.textContent;
.getElementsByTagName("div"); const present = this.selectedValues.has(value);
listItems[this.currentFocus].click();
listItem.querySelector('input').checked = !present;
if (present){
this.selectedValues.delete(value);
this.selectedItemsBox.querySelector('span[data-value="'+value+'"]').remove();
}else{
this.selectedValues.add(value);
this.selectedItemsBox.append(createSelectedItemLabel(value));
}
//console.log('_addValue: this.selectedValues', this.selectedValues)
if (this.selectListener) this.selectListener(value);
function createSelectedItemLabel(value){
const label = document.createElement('span');
label.className = 'selectedItemLabel';
label.dataset.value = value;
label.innerHTML = `${value} ❌`;
return label;
} }
} }
_closeAllLists() {
/*close all autocomplete lists in the document */ _setActiveListItem(element){
let allAcLists = document.getElementsByClassName("autocomplete-items"); const currentActiveItem = this.listContainer.querySelector('.autocomplete-active');
for (let acList of allAcLists) { if (currentActiveItem) currentActiveItem.classList.remove('autocomplete-active');
acList.parentNode.removeChild(acList); element.classList.add('autocomplete-active');
}
/* Not being used for now
_getActiveListItem(){
return this.listContainer.querySelector('.autocomplete-active');
} }
this.currentFocus = -1;
_clickOnActiveItem() {
const activeItem = this.listContainer.querySelector('.autocomplete-active');
if (activeItem) activeItem.click();
}*/
_cleanList() {
this.listContainer.innerHTML = '';
} }
} }
......
...@@ -31,8 +31,8 @@ class AutocompleteTextfield { ...@@ -31,8 +31,8 @@ class AutocompleteTextfield {
this.element.className = `AutocompleteTextField ${id}-autocomplete-textfield`; this.element.className = `AutocompleteTextField ${id}-autocomplete-textfield`;
this.element.innerHTML = ` this.element.innerHTML = `
<input type="text" placeholder="${placeholder}" /> <!-- class="autocomplete-textfield-${this.id}" /> --> <input type="text" placeholder="${placeholder}" />
<div class="AutocompleteTextField-dropdown ${this.id}-autocomplete-dropdown"></div> <!-- autocomplete-items- --> <div class="AutocompleteTextField-dropdown ${this.id}-autocomplete-dropdown"></div>
`; `;
this.input = this.element.querySelector('input'); this.input = this.element.querySelector('input');
...@@ -145,14 +145,14 @@ class AutocompleteTextfield { ...@@ -145,14 +145,14 @@ class AutocompleteTextfield {
if (value.toUpperCase() === currentInput.toUpperCase()) if (value.toUpperCase() === currentInput.toUpperCase())
this._setActiveListItem(listItem); this._setActiveListItem(listItem);
function generateListItem(value, inputText, itemIndex) { function generateListItem(value, inputText) {
const listItem = document.createElement("div"); const listItem = document.createElement("div");
if (inputText){ if (inputText){
const pos = value.toUpperCase().indexOf(inputText.toUpperCase()); // console.log('pos', pos) const pos = value.toUpperCase().indexOf(inputText.toUpperCase()); // console.log('pos', pos)
listItem.innerHTML += listItem.innerHTML +=
`${value.substring(0, pos)}<strong>${value.substring(pos, pos+inputText.length)}</strong>${value.substring(pos + inputText.length)}`; `${value.substring(0, pos)}<strong>${value.substring(pos, pos+inputText.length)}</strong>${value.substring(pos + inputText.length)}`;
}else }else
listItem.innerHTML = acText; listItem.innerHTML = value;
return listItem; return listItem;
} }
......
...@@ -137,8 +137,7 @@ class FilterPanel { ...@@ -137,8 +137,7 @@ class FilterPanel {
filterMap.set(values.fieldId, values.value) filterMap.set(values.fieldId, values.value)
} }
}); });
console.log('FilterPanel getValues:', filterMap); //console.log('FilterPanel getValues:', filterMap);