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
828ebf2c
Commit
828ebf2c
authored
Feb 12, 2021
by
Lauri Himanen
Browse files
Merge branch 'enc-search' into enc-search-logic
parents
b6185e8d
5ff199f4
Changes
6
Hide whitespace changes
Inline
Side-by-side
client/css/styles.css
View file @
828ebf2c
...
...
@@ -485,79 +485,95 @@ div.title span.unfolded::before{
cursor
:
default
;
}
#formula-box
,
#material-name-box
{
/* Formula and Material name boxes */
.FormulaBox
,
.MaterialNameBox
{
display
:
flex
;
justify-content
:
center
;
text-align
:
left
;
background-color
:
#DDD
;
padding
:
40px
0
;
}
.
textfield-composition
{
.
FormulaBox
input
,
.MaterialNameBox
input
{
padding
:
10px
;
border
:
0
;
font-size
:
0.9em
;
}
.MaterialNameBox
input
{
box-sizing
:
border-box
;
width
:
100%
;
}
.FormulaBox
input
{
box-sizing
:
border-box
;
width
:
450px
;
margin-left
:
50px
;
}
/*
.textfield-composition{
padding: 10px;
border: 0;
font-size: 0.9em;
box-sizing: border-box;
width: 100%;
}*/
.textfield-filter
{
padding
:
6px
;
border
:
1px
solid
#DDD
;
width
:
200px
;
}
.autocomplete-textfield-materialname
{
position
:
relative
;
}
.autocomplete-textfield
-strukturbericht
,
.autocomplete-textfield-structuretype
{
position
:
relative
;
.
material-name-
autocomplete-textfield
,
.material-name-autocomplete-dropdown
{
width
:
450px
;
}
.autocomplete-items
{
font-size
:
0.9em
;
position
:
absolute
;
z-index
:
99
;
}
.autocomplete-items-materialname
{
margin-left
:
50px
;
width
:
470px
;
}
.autocomplete-items
div
{
padding
:
2px
10px
2px
10px
;
cursor
:
pointer
;
background-color
:
#DDD
;
border
:
1px
solid
transparent
;
}
/* Autocomplete component */
.
a
utocomplete
-items
input
{
vertical-align
:
middle
;
.
A
utocomplete
TextField
{
display
:
inline-block
;
}
.autocomplete-items-strukturbericht
,
.autocomplete-items-structuretype
{
margin-left
:
0px
;
width
:
220px
;
.AutocompleteTextField-dropdown
{
font-size
:
0.9em
;
position
:
absolute
;
z-index
:
99
;
box-shadow
:
1px
1px
4px
gray
;
}
/*
.autocomplete-items-strukturbericht div {
.AutocompleteTextField-dropdown
>
div
{
padding
:
2px
10px
2px
10px
;
cursor
:
pointer
;
background-color
:
#DDD
;
border
:
1px
solid
transparent
;
}
*/
.autocomplete-active
{
border-color
:
#E56400
!important
;
}
/* To remove
.autocomplete-em {
font-weight: bold;
}
.autocomplete-items-strukturbericht, .autocomplete-items-structuretype{
margin-left: 0px;
width: 220px;
} */
.adding-formula-btn
,
.adding-name-btn
{
padding
:
9px
;
border
:
1px
solid
#999
;
...
...
client/src/search-mod/Autocomplete
.view
.js
→
client/src/search-mod/Autocomplete
MultiselectTextfield
.js
View file @
828ebf2c
...
...
@@ -22,201 +22,9 @@
"
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
AutocompleteMultiselect
List
{
class
AutocompleteMultiselect
Textfield
{
constructor
(
name
=
""
)
{
this
.
classPostfix
=
name
;
...
...
@@ -471,6 +279,4 @@ class AutocompleteMultiselectList {
// EXPORTS
module
.
exports
=
{
AutocompleteTextField
:
AutocompleteTextField
,
AutocompleteMultiselectList
:
AutocompleteMultiselectList
};
module
.
exports
=
AutocompleteMultiselectTextfield
;
client/src/search-mod/AutocompleteTextfield.js
0 → 100644
View file @
828ebf2c
/**
* 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
;
client/src/search-mod/FilterPanel.view.js
View file @
828ebf2c
...
...
@@ -27,7 +27,7 @@
let
util
=
require
(
'
../common/util.js
'
);
let
InfoSys
=
require
(
'
../common/InfoSys.js
'
);
let
AutocompleteMultiselect
List
=
require
(
'
./Autocomplete
.view.js
'
).
AutocompleteMultiselectList
;
let
AutocompleteMultiselect
Textfield
=
require
(
'
./Autocomplete
MultiselectTextfield.js
'
)
;
const
NOT_SELECTED_OPACITY
=
0.2
...
...
@@ -281,7 +281,8 @@ class AutocompleteField{
<span info-sys-data="
${
id
}
">
${
name
}
</span>
</div>`
;
this
.
autocomplete
=
new
AutocompleteMultiselectList
(
id
);
this
.
autocomplete
=
new
AutocompleteMultiselectTextfield
(
id
);
this
.
autocomplete
.
element
.
placeholder
=
"
Search and select options
"
;
this
.
autocomplete
.
element
.
classList
.
add
(
'
textfield-filter
'
);
this
.
element
.
append
(
this
.
autocomplete
.
element
)
...
...
@@ -290,6 +291,7 @@ class AutocompleteField{
let
names
=
JSON
.
parse
(
r1
.
response
)[
this
.
fieldId
];
this
.
autocomplete
.
autocomplete
(
names
);
});
}
getValues
(){
...
...
client/src/search-mod/MaterialName.view.js
View file @
828ebf2c
...
...
@@ -21,29 +21,27 @@
"
use strict
"
;
let
util
=
require
(
'
../common/util.js
'
);
let
AutocompleteText
F
ield
=
require
(
'
./
Autocomplete.view.js
'
).
AutocompleteText
F
ield
;
let
AutocompleteText
f
ield
=
require
(
'
./AutocompleteText
f
ield
.js
'
)
;
class
MaterialNameBox
{
constructor
()
{
this
.
element
=
document
.
createElement
(
'
div
'
);
this
.
element
.
setAttribute
(
"
id
"
,
'
material-name-box
'
);
this
.
materialNameField
=
new
AutocompleteTextField
(
'
materialname
'
);
this
.
materialNameField
.
element
.
placeholder
=
'
Start typing a material name
'
;
this
.
materialNameField
.
element
.
classList
.
add
(
'
textfield-composition
'
);
//this.materialNameField.element.classList.add("materialname-text-field");
this
.
element
.
innerHTML
=
` <div style="padding-bottom: 20px;">
<div class="materialname-placeholder"></div>
<button class="adding-name-btn" disabled>Add to query</button>
</div>
`
;
this
.
element
.
className
=
'
MaterialNameBox
'
;
this
.
element
.
innerHTML
=
` <!-- the autocomplete text field goes here -->
<button class="adding-name-btn" disabled>Add to query</button>
`
;
this
.
materialNameField
=
new
AutocompleteTextfield
(
'
material-name
'
,
'
Start typing a material name
'
);
this
.
materialNameField
.
element
.
querySelector
(
'
input
'
).
classList
.
add
(
'
textfield-composition
'
);
// ** Pending to change
this
.
element
.
prepend
(
this
.
materialNameField
.
element
);
this
.
button
=
this
.
element
.
querySelector
(
'
.adding-name-btn
'
);
this
.
button
.
addEventListener
(
"
click
"
,
(
e
)
=>
{
this
.
listener
(
this
.
materialNameField
.
element
.
v
alue
);
this
.
materialNameField
.
element
.
value
=
''
;