Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
encyclopedia-gui
Commits
e35871eb
Commit
e35871eb
authored
Feb 18, 2021
by
Lauri Himanen
Browse files
Merge branch 'enc-search' into enc-search-logic
parents
66bf2d5b
08e45aff
Changes
7
Hide whitespace changes
Inline
Side-by-side
client/css/styles.css
View file @
e35871eb
...
...
@@ -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 component
s
*/
.AutocompleteTextField
{
display
:
inline-block
;
}
.AutocompleteTextField-dropdown
{
.AutocompleteTextField-dropdown
,
.AutocompleteMultiselectTextfield-dropdown
{
font-size
:
0.9em
;
position
:
absolute
;
z-index
:
99
;
box-shadow
:
1px
1px
4px
gray
;
}
.AutocompleteTextField-dropdown
>
div
{
.AutocompleteTextField-dropdown
>
div
,
.AutocompleteMultiselectTextfield-dropdown
>
div
{
padding
:
2px
10px
2px
10px
;
cursor
:
pointer
;
background-color
:
#DDD
;
border
:
1px
solid
transparent
;
}
...
...
@@ -559,6 +549,48 @@ div.title span.unfolded::before{
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
.autocomplete-em {
...
...
client/src/search-mod/AutocompleteMultiselectTextfield.js
View file @
e35871eb
...
...
@@ -26,255 +26,199 @@
class
AutocompleteMultiselectTextfield
{
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
}
);
}
constructor
(
id
=
""
,
placeholder
=
''
,
allowEmptyInput
=
true
)
{
this
.
id
=
id
;
this
.
element
=
document
.
createElement
(
'
div
'
);
//this.element.style.display = 'inline';
this
.
element
.
className
=
`AutocompleteMultiselectTextfield
${
id
}
-autocomplete-multiselect-textfield`
;
this
.
element
.
innerHTML
=
`
<input type="text" placeholder="
${
placeholder
}
" />
<div class="AutocompleteMultiselectTextfield-selected-box"></div>
<div class="AutocompleteMultiselectTextfield-dropdown
${
this
.
id
}
-autocomplete-multiselect-dropdown"></div>
`
;
this
.
input
=
this
.
element
.
querySelector
(
'
input
'
);
this
.
selectedItemsBox
=
this
.
element
.
querySelector
(
'
.AutocompleteMultiselectTextfield-selected-box
'
);
this
.
listContainer
=
this
.
element
.
querySelector
(
'
.AutocompleteMultiselectTextfield-dropdown
'
);
this
.
selectListener
;
// state
this
.
valueList
;
// List of autocomplete (possible) values
this
.
selectedValues
=
new
Set
();
this
.
allowEmptyInput
=
allowEmptyInput
;
// event management
// When clicking on the textfield the dropdown shows up
this
.
input
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
this
.
_processInput
();
e
.
stopPropagation
();
});
/
* process input when someone
writes
i
n the text
field
:*/
this
.
elemen
t
.
addEventListener
(
"
input
"
,
(
e
)
=>
{
this
.
_processInput
(
allAcValues
);
/
/ the dropdown list is updated as the user
writes
o
n the textfield
this
.
inpu
t
.
addEventListener
(
"
input
"
,
(
e
)
=>
{
this
.
_processInput
();
});
/* 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
();
}
// The dropdown is hiden when the mouse leaves the component area
this
.
element
.
addEventListener
(
"
mouseleave
"
,
e
=>
{
this
.
_cleanList
();
});
/* react to enter key */
this
.
element
.
addEventListener
(
"
keypress
"
,
(
e
)
=>
{
if
(
e
.
keyCode
==
13
)
{
// ENTER
/* simulate a click on the "active" item:*/
this
.
_clickActive
();
}
// Handle the item removal on the selected items box
this
.
selectedItemsBox
.
addEventListener
(
"
click
"
,
e
=>
{
let
itemLabel
=
event
.
target
.
closest
(
'
span
'
);
this
.
_removeSelectedValue
(
itemLabel
.
dataset
.
value
);
//itemLabel.dispatchEvent(new Event('change'));
});
/
* react to klicking into the textfield */
this
.
element
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
this
.
_processInput
(
);
e
.
stopPropagation
(
);
/
/ Handle the item selection on the dropdown
this
.
listContainer
.
addEventListener
(
"
click
"
,
e
=>
{
let
listItem
=
event
.
target
.
closest
(
'
div
'
);
this
.
_toggleItem
(
listItem
);
});
// close lists when someone clicks in the document:*/
document
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
this
.
element
.
value
=
''
;
this
.
_
closeAllLists
(
);
this
.
listContainer
.
addEventListener
(
"
mouseover
"
,
e
=>
{
let
listItem
=
event
.
target
.
closest
(
'
div
'
);
this
.
_
setActiveListItem
(
listItem
);
});
}
getSelected
()
{
let
values
=
[];
for
(
let
item
of
this
.
items
)
{
if
(
item
.
selected
)
{
values
.
push
(
item
.
value
);
}
}
return
values
;
getValues
(){
return
Array
.
from
(
this
.
selectedValues
);
}
_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 */
let
acItemIndex
=
0
;
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
++
;
}
}
/*resetValue(){
this.input.value = '';
}*/
disable
(
bool
){
this
.
input
.
disabled
=
bool
;
}
_generateListItem
(
acText
,
selected
,
inputText
,
itemIndex
)
{
/*create a DIV element for each matching element:*/
let
listItem
=
document
.
createElement
(
"
div
"
);
let
itemCheckbox
=
document
.
createElement
(
"
input
"
);
itemCheckbox
.
type
=
"
checkbox
"
;
itemCheckbox
.
checked
=
selected
;
/* TODO: check why catching this event is necessary */
itemCheckbox
.
addEventListener
(
"
click
"
,
e
=>
{
listItem
.
click
();
e
.
stopPropagation
();
});
listItem
.
appendChild
(
itemCheckbox
);
/*make the matching letters bold:*/
if
(
inputText
&&
inputText
!=
""
){
let
pos
=
acText
.
toUpperCase
().
search
(
inputText
.
toUpperCase
());
listItem
.
appendChild
(
document
.
createTextNode
(
acText
.
substr
(
0
,
pos
)));
let
emText
=
document
.
createElement
(
"
span
"
);
emText
.
className
=
"
autocomplete-em
"
;
emText
.
innerHTML
=
acText
.
substr
(
pos
,
inputText
.
length
);
/* 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
));
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
;
}
/* clicking on the AS list item puts selects the corresponding name for searching */
listItem
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
let
checkbox
=
e
.
target
.
getElementsByTagName
(
"
input
"
)[
0
];
this
.
_toggleSelect
(
acText
,
checkbox
);
e
.
stopPropagation
();
// 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
;
});
//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
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
;
_removeSelectedValue
(
value
)
{
/* mark the active status by a style class */
listItems
[
newFocus
].
classList
.
add
(
"
autocomplete-active
"
);
this
.
selectedValues
.
delete
(
value
);
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
)
{
let
listItems
=
document
.
getElementById
(
"
autocomplete-list
"
)
.
getElementsByTagName
(
"
div
"
);
listItems
[
this
.
currentFocus
].
click
();
_toggleItem
(
listItem
)
{
const
value
=
listItem
.
textContent
;
const
present
=
this
.
selectedValues
.
has
(
value
);
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 */
let
allAcLists
=
document
.
getElementsByClassName
(
"
autocomplete-items
"
);
for
(
let
acList
of
allAcLists
)
{
acList
.
parentNode
.
removeChild
(
acList
);
_setActiveListItem
(
element
){
const
currentActiveItem
=
this
.
listContainer
.
querySelector
(
'
.autocomplete-active
'
);
if
(
currentActiveItem
)
currentActiveItem
.
classList
.
remove
(
'
autocomplete-active
'
);
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
=
''
;
}
}
...
...
client/src/search-mod/AutocompleteTextfield.js
View file @
e35871eb
...
...
@@ -31,8 +31,8 @@ class AutocompleteTextfield {
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- -->
<input type="text" placeholder="
${
placeholder
}
" />
<div class="AutocompleteTextField-dropdown
${
this
.
id
}
-autocomplete-dropdown"></div>
`
;
this
.
input
=
this
.
element
.
querySelector
(
'
input
'
);
...
...
@@ -145,14 +145,14 @@ class AutocompleteTextfield {
if
(
value
.
toUpperCase
()
===
currentInput
.
toUpperCase
())
this
.
_setActiveListItem
(
listItem
);
function
generateListItem
(
value
,
inputText
,
itemIndex
)
{
function
generateListItem
(
value
,
inputText
)
{
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
;
listItem
.
innerHTML
=
value
;
return
listItem
;
}
...
...
client/src/search-mod/FilterPanel.view.js
View file @
e35871eb
...
...
@@ -137,8 +137,7 @@ class FilterPanel {
filterMap
.
set
(
values
.
fieldId
,
values
.
value
)
}
});
console
.
log
(
'
FilterPanel getValues:
'
,
filterMap
);
//console.log('FilterPanel getValues:', filterMap);
return
filterMap
;
}
...
...
@@ -282,20 +281,18 @@ class AutocompleteField{
</div>`
;
this
.
autocomplete
=
new
AutocompleteMultiselectTextfield
(
id
);
this
.
autocomplete
.
element
.
placeholder
=
"
Search and select options
"
;
this
.
autocomplete
.
element
.
classList
.
add
(
'
textfield-filter
'
);
this
.
autocomplete
=
new
AutocompleteMultiselectTextfield
(
id
,
'
Search and select options
'
);
this
.
element
.
append
(
this
.
autocomplete
.
element
)
let
r1
=
util
.
serverReq
(
util
.
getSuggestionURL
(
this
.
fieldId
),
(
e
)
=>
{
let
names
=
JSON
.
parse
(
r1
.
response
)[
this
.
fieldId
];
this
.
autocomplete
.
a
utocomplete
(
names
);
this
.
autocomplete
.
setA
utocomplete
List
(
names
);
});
}
getValues
(){
const
values
=
this
.
autocomplete
.
getSelected
();
const
values
=
this
.
autocomplete
.
getValues
();
//
getSelected();
return
(
values
.
length
===
0
?
null
:
{
fieldId
:
this
.
fieldId
,
value
:
values
}
);
}
...
...
client/src/search-mod/MaterialList.view.js
View file @
e35871eb
...
...
@@ -68,10 +68,11 @@ class MaterialList {
this
.
_render
();
}
/*
invalidateSearch
(){
this._hide();
}*/
this
.
visible
=
false
;
this
.
_render
();
}
initSearch
(
optimadeQuery
){
...
...
client/src/search-mod/MaterialName.view.js
View file @
e35871eb
...
...
@@ -34,7 +34,6 @@ class MaterialNameBox{
`
;
this
.
materialNameField
=
new
AutocompleteTextfield
(
'
material-name
'
,
'
Start typing a material name
'
);
this
.
materialNameField
.
element
.
querySelector
(
'
input
'
).
classList
.
add
(
'
textfield-composition
'
);
// ** Pending to change