282 lines
11 KiB
JavaScript
282 lines
11 KiB
JavaScript
class SearchBox {
|
|
container
|
|
box = document.createElement('div')
|
|
list = document.createElement('div')
|
|
listItems = document.createElement('div')
|
|
selected = document.createElement('div')
|
|
searchField = document.createElement('input')
|
|
cloneListItems = this.listItems
|
|
callback
|
|
dynamic = {}
|
|
constructor(selector) {
|
|
if (typeof selector === "string") {
|
|
this.container = document.querySelector(selector)
|
|
} else {
|
|
this.container = selector;
|
|
}
|
|
if(this.container) {
|
|
if (this.container.nodeName === 'SELECT') {
|
|
this.dataRetrieve()
|
|
} else {
|
|
alert('Html element SELECT is required')
|
|
}
|
|
addEventListener('keydown', ({keyCode}) => {
|
|
if (keyCode === 27) {
|
|
this.clear()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this.box.classList.remove('open')
|
|
this.searchField.value = ''
|
|
this.listItems.querySelectorAll('.list-item').forEach(e => {
|
|
e.classList.add('visible')
|
|
e.classList.remove('hover')
|
|
e.style.display = 'block'
|
|
})
|
|
}
|
|
|
|
getNextSibling(elem, selector) {
|
|
var sibling = elem.nextElementSibling;
|
|
while (sibling) {
|
|
if (sibling.matches(selector)) return sibling;
|
|
sibling = sibling.nextElementSibling
|
|
}
|
|
|
|
}
|
|
|
|
getPreviousSibling(elem, selector) {
|
|
var sibling = elem.previousElementSibling;
|
|
if (!selector) return sibling;
|
|
while (sibling) {
|
|
if (sibling.matches(selector)) return sibling;
|
|
sibling = sibling.previousElementSibling;
|
|
}
|
|
|
|
}
|
|
|
|
keyDown() {
|
|
this.searchField.addEventListener('keydown', ({keyCode}) => {
|
|
if (keyCode === 38 || keyCode === 40) {
|
|
event.preventDefault()
|
|
const visibleList = this.listItems.querySelectorAll('.visible:not(.hover)');
|
|
const firstItem = visibleList[0];
|
|
const lastItem = this.listItems.querySelectorAll('.visible:not(.hover)')[visibleList.length - 1];
|
|
if (this.listItems.querySelectorAll('.visible.hover').length === 0) {
|
|
if (keyCode === 38 && lastItem)
|
|
lastItem.classList.add('hover')
|
|
if (keyCode === 40 && firstItem)
|
|
firstItem.classList.add('hover')
|
|
} else {
|
|
const hoverElement = this.listItems.querySelector('.visible.hover')
|
|
hoverElement.classList.remove('hover')
|
|
//up
|
|
if (keyCode === 38) {
|
|
const prev = this.getPreviousSibling(hoverElement, '.visible')
|
|
if (prev && prev.classList.contains('visible')) {
|
|
prev.classList.add('hover')
|
|
console.log(prev.offsetTop)
|
|
this.listItems.scrollTo({
|
|
top: prev.offsetTop - 160,
|
|
})
|
|
} else {
|
|
lastItem.classList.add('hover')
|
|
this.listItems.scrollTo({
|
|
top: lastItem.offsetTop - 160,
|
|
})
|
|
}
|
|
}
|
|
//down
|
|
if (keyCode === 40) {
|
|
const next = this.getNextSibling(hoverElement, '.visible')
|
|
if (next && next.classList.contains('visible')) {
|
|
next.classList.add('hover')
|
|
this.listItems.scrollTo({
|
|
top: next.offsetTop - 160
|
|
})
|
|
} else {
|
|
firstItem.classList.add('hover')
|
|
this.listItems.scrollTo({
|
|
top: firstItem.offsetTop - 160,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (keyCode === 13) {
|
|
event.preventDefault()
|
|
const currentSelected = this.listItems.querySelector('.hover');
|
|
if (currentSelected) {
|
|
const selected = this.listItems.querySelector('.selected');
|
|
if (!this.container.hasAttribute('multiple')) {
|
|
if (selected) {
|
|
selected.classList.remove('selected')
|
|
}
|
|
currentSelected.classList.add('selected')
|
|
this.selected.innerText = currentSelected.innerHTML
|
|
this.container.value = currentSelected.dataset.select
|
|
if (this.dynamic.change) {
|
|
this.dynamic.change(this.container.value)
|
|
}
|
|
} else {
|
|
this.multipleSelected(currentSelected, this.container.querySelector(`option[value="${currentSelected.dataset.select}"]`))
|
|
}
|
|
this.container.parentNode.classList.remove('form-error')
|
|
this.clear()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
multipleSelected(listItem, e) {
|
|
if (!this.selected.querySelector(`.selected-box[data-id="${e.value}"]`)) {
|
|
const sb = document.createElement('div')
|
|
const close = document.createElement('div')
|
|
sb.className = 'selected-box'
|
|
close.className = 'close'
|
|
sb.innerText = e.innerText
|
|
sb.setAttribute('data-id', e.value)
|
|
const sbExists = this.selected.querySelector('.selected-box')
|
|
if (!sbExists) {
|
|
this.selected.innerHTML = '';
|
|
}
|
|
sb.appendChild(close)
|
|
this.selected.appendChild(sb)
|
|
listItem.classList.add('selected')
|
|
e.setAttribute('selected', true)
|
|
sb.addEventListener('click', () => {
|
|
event.preventDefault()
|
|
e.removeAttribute('selected')
|
|
const listItemSelected = this.listItems.querySelector(`.list-item[data-select="${e.value}"]`)
|
|
listItemSelected.classList.remove('selected')
|
|
sb.remove()
|
|
const sbExists = this.selected.querySelector('.selected-box')
|
|
if (!sbExists) {
|
|
var s = this.container.querySelector('option:first-child')
|
|
if (s)
|
|
this.selected.innerHTML = s.innerText;
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
dataRetrieve() {
|
|
this.box.classList.add('search-box')
|
|
this.list.classList.add('list')
|
|
this.selected.classList.add('selected')
|
|
this.listItems.classList.add('list-items')
|
|
this.list.appendChild(this.searchField)
|
|
this.list.appendChild(this.listItems)
|
|
this.box.appendChild(this.selected)
|
|
this.box.appendChild(this.list)
|
|
if (this.container.hasClass('disabled')) {
|
|
this.box.classList.add('disabled')
|
|
}
|
|
this.box.addEventListener('click', e => {
|
|
if (
|
|
!event.target.classList.contains('selected-box') &&
|
|
!event.target.classList.contains('close')
|
|
) {
|
|
if (this.box.classList.contains('open')) {
|
|
this.clear()
|
|
} else {
|
|
document.querySelectorAll('.search-box').forEach(b => {
|
|
b.classList.remove('open')
|
|
})
|
|
this.box.classList.add('open')
|
|
this.searchField.focus()
|
|
}
|
|
}
|
|
})
|
|
this.keyDown()
|
|
this.updateFromOptionList()
|
|
this.searchField.addEventListener('keyup', e => {
|
|
this.listItems.querySelectorAll('.list-item').forEach((e, i) => {
|
|
if (e.innerText.toLowerCase().indexOf(this.searchField.value.toLowerCase()) !== -1 || this.searchField.value === '') {
|
|
e.style.display = 'block'
|
|
e.classList.add('visible')
|
|
} else {
|
|
e.style.display = 'none'
|
|
e.classList.remove('visible')
|
|
e.classList.remove('hover')
|
|
}
|
|
})
|
|
})
|
|
this.container.parentNode.appendChild(this.box);
|
|
}
|
|
|
|
updateFromOptionList() {
|
|
this.listItems.innerHTML = '';
|
|
this.container.querySelectorAll('option').forEach((e, i) => {
|
|
if (i === 0) {
|
|
//if (this.selected.innerText !== '')
|
|
this.selected.innerText = e.innerText
|
|
}
|
|
if (e.value) {
|
|
const listItem = document.createElement('div');
|
|
listItem.className = 'list-item visible'
|
|
listItem.setAttribute('data-select', e.value)
|
|
listItem.innerText = e.innerText;
|
|
this.listItems.appendChild(listItem)
|
|
if (e.hasAttribute('selected') && !this.container.hasAttribute('multiple')) {
|
|
this.selected.innerText = e.innerText
|
|
listItem.classList.add('selected')
|
|
this.listItems.scrollTo({
|
|
top: listItem.offsetTop - 160,
|
|
})
|
|
}
|
|
if (e.hasAttribute('selected') && this.container.hasAttribute('multiple')) {
|
|
this.multipleSelected(listItem, e)
|
|
}
|
|
listItem.addEventListener('click', l => {
|
|
if (!this.container.hasAttribute('multiple')) {
|
|
this.container.value = e.value
|
|
this.selected.innerText = e.innerText
|
|
if (this.dynamic.change) {
|
|
this.dynamic.change(this.container.value)
|
|
}
|
|
} else {
|
|
this.multipleSelected(listItem, e)
|
|
}
|
|
this.container.parentNode.classList.remove('form-error')
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
updateFromObject(object) {
|
|
const $this = this;
|
|
object.searchBox.dynamic.change = function (value) {
|
|
$this.request(object.url, `${object.queryId}=${value}`, data => {
|
|
//console.log(data);
|
|
$this.container.innerHTML = $this.container.querySelector('option:first-child').outerHTML
|
|
data.forEach(o => {
|
|
if (o[object.data.id] && o[object.data.name]) {
|
|
const option = document.createElement('option')
|
|
option.value = o[object.data.id];
|
|
option.innerText = o[object.data.name]
|
|
$this.container.appendChild(option)
|
|
}
|
|
})
|
|
if (data.length > 0) {
|
|
$this.updateFromOptionList()
|
|
$this.container.classList.remove('disabled')
|
|
$this.box.classList.remove('disabled')
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
request(url, query, callback) {
|
|
const xhr = new XMLHttpRequest()
|
|
xhr.responseType = 'json'
|
|
xhr.open('get', `${url}?${query}`)
|
|
xhr.onload = () => {
|
|
callback(xhr.response)
|
|
}
|
|
xhr.send()
|
|
}
|
|
}
|