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

NewSearchMod.js deep refactoring (the search box was extracted as a new...

NewSearchMod.js deep refactoring (the search box was extracted as a new component - SearchBox.view.js)
parent 45be3f85
Pipeline #90007 skipped with stage
......@@ -16,11 +16,7 @@
*/
/*
This is the UI component implementing the search for material names.
The input element features a text-autocomplete functionality,
based on the data from the related "suggestions" API endpoint
*/
"use strict";
......
......@@ -27,6 +27,7 @@
let util = require('../common/util.js');
let UserGuidance = require('../common/UserGuidance.js');
let SearchBox = require('./SearchBox.view.js');
let ElementTable = require('./ElemenTable.view.js');
let MaterialList = require('./MaterialList.view.js');
let MaterialNameBox = require('./MaterialName.view.js');
......@@ -35,38 +36,18 @@ let SwitchComponent = require('../common/SwitchComponent.js');
// local utility functions
function getTagHtml(tag, isFormula){
return `<span class="search-label" data-el="${tag}" >
<img src="img/tag.svg" height="16px" class="remove-label"
style="vertical-align: bottom"/>
${isFormula ? util.getSubscriptedFormula(tag) : tag}
<img src="img/cross.svg" height="6px" class="remove-label"
style="vertical-align: middle; padding: 4px 3px 6px 5px;" />
</span>`;
}
function replaceDashes(s){
return s.split('-').join('_');
}
class NewSearchMod {
constructor() {
this.userGuidance = true; // can enabled/disabled
//this.showingSearchBox = false;
this.searchQuery = [];
this.queryTypes = []; //**** Types associated to query elements
// Types: element (E), formula (F), symbol (S) and prop names
this.searchFilters = [];
this.currentOperator = 'AND';
this.element = document.createElement('div');
this.element.setAttribute("id",'search-module');
this.element.innerHTML=
......@@ -83,17 +64,8 @@ class NewSearchMod {
<!-- <div class="search-title">Composition</div> -->
<div class="search-box" >
<div class="search-query-wrapper" >
<div class="search-query-box" style="float: left;">
</div>
<button class="clean-btn" style="float: right;">Clear all</button>
</div>
<button class="search-btn" >Search</button>
</div>
<div class="search-box-placeholder" > </div>
<input type="checkbox" id="multiples-of-formula" value="">
Include multiples of formula
<br>
......@@ -102,7 +74,6 @@ class NewSearchMod {
<br>
<div class="add-buttons" >
<div class="tab-buttons" style="width: 70%; display: inline-block">
<button class="element-add-btn" id="add-tab-selected" style="margin-left: 50px">Element</button>
......@@ -143,11 +114,24 @@ class NewSearchMod {
`;
this.filterSidePanel = this.element.querySelector('.search-filter-side');
this.searchQueryBox= this.element.getElementsByClassName("search-query-box")[0];
this.searchBox = this.element.querySelector('.search-box');
this.mainButton= this.element.querySelector('.search-btn');
this.cleanButton= this.element.querySelector('.clean-btn');
this.searchBox = new SearchBox();
this.searchBox.setBoolOperator('AND');
this.element.querySelector('.search-box-placeholder').append(this.searchBox.element);
this.searchBox.setRemoveElementListener( (element) => {
this.elementTable.deselectElement(element);
});
// When the clear all button is clicked on and
// when the search query gets blank by removing query items
this.searchBox.setCleanSearchQueryListener( () => {
this.addFormulaButton.disabled = false;
this.addMatNameButton.disabled = false;
this.addElementButton.disabled = false;
this.formulaBox.enableInput();
this.materialNameBox.enableInput();
this.elementTable.deselectAllElements();
});
this.addButtonsBox= this.element.querySelector('.add-buttons');
this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn');
this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn');
......@@ -158,30 +142,30 @@ class NewSearchMod {
let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch');
this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element);
andOrSwitch.setListener( e => {
this.currentOperator = ( e ? 'AND' : 'OR');
this.searchBox.setBoolOperator( e ? 'AND' : 'OR');
});
this.openParenthButton = this.element.querySelector('.open-parentheses');
this.closeParenthButton = this.element.querySelector('.close-parentheses');
this.openParenthButton.addEventListener( 'click', e => {
this._addParenthesesInSearchQuery(true);
this.searchBox.addParentheses(true);
});
this.closeParenthButton.addEventListener( 'click', e => {
this._addParenthesesInSearchQuery(false);
this.searchBox.addParentheses(false);
});
this.elementTable= new ElementTable();
this.elementTable.setClickListener(elementArray => {
this.addElementsInSearchQuery(elementArray);
this.searchBox.addElements(elementArray);
this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code
});
this.elementTable.setDeselectListener(e => this.removeElementORFormulaInSearchQuery(e));
this.elementTable.setDeselectListener(e => this.searchBox.removeElementORFormulaInSearchQuery(e));
this.formulaBox = new FormulaBox();
this.formulaBox.setAddFormulaListener(formula => {
if (formula.trim() !== ''){
this.addTagInSearchQuery(formula, 'F');
this.searchBox.addTag(formula, 'F');
//this.formulaBox.disable(true);
this.addMatNameButton.disabled = true;
this.materialNameBox.disableInput();
......@@ -192,7 +176,7 @@ class NewSearchMod {
this.materialNameBox = new MaterialNameBox();
this.materialNameBox.setAddMaterialNameListener( name => {
if (name.trim() !== ''){
this.addTagInSearchQuery(name, 'MN');
this.addTag(name, 'MN');
this.addElementButton.disabled = true;
this.addFormulaButton.disabled = true;
//TODO: are the following two lines needed, when the related buttons are already diasbled??
......@@ -218,19 +202,17 @@ class NewSearchMod {
this.currentTab = 'element';
this.addPanel.appendChild(this.elementTable.element);
this._events();
}
_events() {
/*
// External event - Search button press
this.mainButton.addEventListener( "click", (e) => {
//console.log("this.labels: "+JSON.stringify(this.labels));
/*
if (this.searchQuery.lenght === 0){
util.showUserMsg('No query');
}else{
......@@ -268,22 +250,12 @@ class NewSearchMod {
}
}*/
}
});
*/
this.cleanButton.addEventListener( "click", (e) => {
this.searchQuery = [];
this.queryTypes = [];
this.updateSearchQuery();
this.addFormulaButton.disabled = false;
this.addMatNameButton.disabled = false;
this.addElementButton.disabled = false;
this.formulaBox.enableInput();
this.materialNameBox.enableInput();
this.elementTable.deselectAllElements();
});
this.addButtonsBox.addEventListener( "click", (e) => {
if (e.target !== e.currentTarget) { // When the event source is a child
......@@ -339,13 +311,7 @@ class NewSearchMod {
}
});
this.searchQueryBox.addEventListener( "click", e => {
let className = e.target.className;
if (className === 'remove-label'){
let elSymbol = e.target.parentElement.getAttribute('data-el');
this.removeElementORFormulaInSearchQuery(elSymbol);
}
});
// gray-out events for the Properties panel and Composition panel
......@@ -372,8 +338,8 @@ class NewSearchMod {
sendQuery(){
console.log('sendQuery');
let searchQuery = this.getOptimadeQueryFromSearchQuery(this.searchQuery, this.queryTypes);
this.materialList.initSearch(searchQuery);
//let searchQuery = this.getOptimadeQueryFromSearchQuery(this.searchQuery, this.queryTypes);
//this.materialList.initSearch(searchQuery);
}
......@@ -646,118 +612,10 @@ class NewSearchMod {
}
}*/
_addItemInSearchQuery(item, type){
this.searchQuery.push(item);
this.queryTypes.push(type);
}
addTagInSearchQuery(tag, type){
// If the it's an element and is already in the query it's not inserted
if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return;
if ( this.searchQuery.length > 0
&& this.searchQuery[this.searchQuery.length-1] !== '(')
this._addItemInSearchQuery(this.currentOperator, 'S');
this._addItemInSearchQuery(tag, type);
this.updateSearchQuery();
//this._showSearchBox();
}
addElementsInSearchQuery(elementArray){
let index = elementArray.length;
while (index--) {
this.addTagInSearchQuery(elementArray[index], 'E');
}
return true;
}
_addParenthesesInSearchQuery(isOpen){
if ( this.searchQuery.length > 0 && isOpen)
this._addItemInSearchQuery(this.currentOperator, 'S');
this._addItemInSearchQuery( (isOpen ? '(' : ')'), 'P');
this.updateSearchQuery();
//this._showSearchBox();
}
removeElementORFormulaInSearchQuery(item){
//console.log(" removeElementORFormulaInSearchQuery item: ",item, this.searchQuery.indexOf(item));
let isMaterialName = (this.queryTypes[0] === 'MN');
// Travese the array removing the item and the bool operator related
let itemIndex = this.searchQuery.indexOf(item);
if (itemIndex >= 0){
let i, elementsToRemove;
if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the left
i = itemIndex; elementsToRemove = 2;
}else if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the right
i = itemIndex-1; elementsToRemove = 2;
}else{ // case: (item)
i = itemIndex; elementsToRemove = 1;
}
this.searchQuery.splice(i, elementsToRemove);
this.queryTypes.splice(i, elementsToRemove);
}
// Travese the array removing the unnecessary parethesis (only tested for one level nested)
if ( this.searchQuery.indexOf('(') >= 0){ // Recursion
for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed
if ( this.searchQuery[i] === '(' ){
if ( this.searchQuery[i+1] === ')'){ // '()' case
this.searchQuery.splice(i, 2);
this.queryTypes.splice(i, 2);
}else if (searchQuery[i+2] === ')'){ // '(item)' case
this.searchQuery.splice(i, 3, this.searchQuery[i+1]);
this.queryTypes.splice(i, 3, this.queryTypes[i+1]);
}
}
}
}
this.updateSearchQuery();
if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)
this.elementTable.deselectElement(item);
}
if (this.queryTypes.length === 0){
if (isMaterialName){
this.addElementButton.disabled = false;
this.addFormulaButton.disabled = false;
this.formulaBox.enableInput();
}else{ // element or formula
this.addMatNameButton.disabled = false;
}
this.materialNameBox.enableInput();
}
//console.log(" final searchQuery: ",this.searchQuery);
//}
return true;
}
updateSearchQuery(){
let html= '';
for (let i = 0; i < this.searchQuery.length; i++) {
let type = this.queryTypes[i];
if (type === 'S' || type === 'P')
html+= `<span class="search-query-symbol" > ${this.searchQuery[i]} </span>`;
else
html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false));
}
console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes);
this.searchQueryBox.innerHTML = html;
}
///********* DELETE?
showSearchResults(){
......
/**
* Copyright 2016-2020 Iker Hurtado
*
* 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.
*
*/
/*
This is the UI component implementing the search box.
It represents the search query for the user and enables its edition.
*/
"use strict";
let util = require('../common/util.js');
//
//
//// local utility functions
function getTagHtml(tag, isFormula){
return `<span class="search-label" data-el="${tag}" >
<img src="img/tag.svg" height="16px" class="remove-label"
style="vertical-align: bottom"/>
${isFormula ? util.getSubscriptedFormula(tag) : tag}
<img src="img/cross.svg" height="6px" class="remove-label"
style="vertical-align: middle; padding: 4px 3px 6px 5px;" />
</span>`;
}
class SearchBox{
constructor() {
this.searchQuery = [];
this.queryTypes = []; //**** Types associated to query elements
// Types: element (E), formula (F), symbol (S) and prop names
this.currentOperator;
this.element = document.createElement('div');
this.element.className = 'search-box';
this.element.innerHTML = `
<div class="search-query-wrapper" >
<div class="search-query-box" style="float: left;"></div>
<button class="clean-btn" style="float: right;">Clear all</button>
</div>
<button class="search-btn" >Search</button>
`;
this.searchButton = this.element.querySelector('.search-btn');
this.searchButton.addEventListener( "click", (e) => {
console.log('SEARCH event')
});
this.cleanButton = this.element.querySelector('.clean-btn');
this.cleanButton.addEventListener( "click", (e) => {
this.searchQuery = [];
this.queryTypes = [];
this.updateSearchQuery();
this.cleanSearchQueryListener();
});
this.searchQueryBox = this.element.querySelector('.search-query-box');
this.searchQueryBox.addEventListener( "click", e => {
let className = e.target.className;
if (className === 'remove-label'){
let elSymbol = e.target.parentElement.getAttribute('data-el');
this.removeElementORFormulaInSearchQuery(elSymbol);
}
});
}
setBoolOperator(operator){
this.currentOperator = operator;
}
updateSearchQuery(){
let html= '';
for (let i = 0; i < this.searchQuery.length; i++) {
let type = this.queryTypes[i];
if (type === 'S' || type === 'P')
html+= `<span class="search-query-symbol" > ${this.searchQuery[i]} </span>`;
else
html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false));
}
console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes);
this.searchQueryBox.innerHTML = html;
}
addItemInSearchQuery(item, type){
this.searchQuery.push(item);
this.queryTypes.push(type);
}
addTag(tag, type){
// If the it's an element and is already in the query it's not inserted
if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return;
if ( this.searchQuery.length > 0
&& this.searchQuery[this.searchQuery.length-1] !== '(')
this.addItemInSearchQuery(this.currentOperator, 'S');
this.addItemInSearchQuery(tag, type);
this.updateSearchQuery();
}
addElements(elementArray){
let index = elementArray.length;
while (index--) {
this.addTag(elementArray[index], 'E');
}
return true;
}
addParentheses(isOpen){
if ( this.searchQuery.length > 0 && isOpen)
this.addItemInSearchQuery(this.currentOperator, 'S');
this.addItemInSearchQuery( (isOpen ? '(' : ')'), 'P');
this.updateSearchQuery();
}
removeElementORFormulaInSearchQuery(item){
//console.log(" removeElementORFormulaInSearchQuery item: ",item, this.searchQuery.indexOf(item));
let isMaterialName = (this.queryTypes[0] === 'MN');
// Travese the array removing the item and the bool operator related
let itemIndex = this.searchQuery.indexOf(item);
if (itemIndex >= 0){
let i, elementsToRemove;
if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the left
i = itemIndex; elementsToRemove = 2;
}else if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the right
i = itemIndex-1; elementsToRemove = 2;
}else{ // case: (item)
i = itemIndex; elementsToRemove = 1;
}
this.searchQuery.splice(i, elementsToRemove);
this.queryTypes.splice(i, elementsToRemove);
}
// Travese the array removing the unnecessary parethesis (only tested for one level nested)
if ( this.searchQuery.indexOf('(') >= 0){ // Recursion
for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed
if ( this.searchQuery[i] === '(' ){
if ( this.searchQuery[i+1] === ')'){ // '()' case
this.searchQuery.splice(i, 2);
this.queryTypes.splice(i, 2);
}else if (this.searchQuery[i+2] === ')'){ // '(item)' case
this.searchQuery.splice(i, 3, this.searchQuery[i+1]);
this.queryTypes.splice(i, 3, this.queryTypes[i+1]);
}
}
}
}
this.updateSearchQuery();
if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)
this.removeElementListener(item);
}
if (this.queryTypes.length === 0){ // The search query gets blank
this.cleanSearchQueryListener();
}
return true;
}
setCleanSearchQueryListener(listener){
this.cleanSearchQueryListener = listener;
}
setRemoveElementListener(listener) {
this.removeElementListener = listener;
}
}
// EXPORTS
module.exports = SearchBox;
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