nfp_sites/heimaerbest/app/combobox.js

144 lines
3.8 KiB
JavaScript
Raw Permalink Normal View History

2023-11-09 09:44:15 +00:00
const m = require('mithril')
const util = require('./util')
let activeBox = null
let boxIndex = 1
document.body.addEventListener('click', function() {
activeBox = null
m.redraw()
})
const Combobox = {
oninit: function(vnode) {
this.filtered = []
this.open = false
this.input = null
this.id = boxIndex++
this.onbeforeupdate(vnode)
this.focus = this.onFocus.bind(this, vnode)
},
onbeforeupdate: function(vnode) {
if (!vnode.attrs.value) {
this.filtered = vnode.attrs.items || []
return
}
let val = vnode.attrs.value.toLocaleLowerCase()
this.filtered = vnode.attrs.items.filter(item => {
return item.toLocaleLowerCase().indexOf(val) >= 0
})
},
onInput: function(vnode, e) {
this.smartOpen(vnode)
if (vnode.attrs.oninput) {
vnode.attrs.oninput(e)
}
},
onDone: function(vnode) {
if (vnode.attrs.ondone) {
vnode.attrs.ondone()
}
},
onFocus: function(vnode) {
this.input.focus()
},
selectText: function(vnode, text) {
this.input.value = text
this.onInput(vnode, { target: this.input })
activeBox = null
this.onDone(vnode)
return false
},
onKeyPress: function(vnode, e) {
if (e.key === 'ArrowDown') {
if (e.target.dataset.type === 'input' && e.target.nextElementSibling && e.target.nextElementSibling.childNodes) {
e.target.nextElementSibling.childNodes[0].focus()
} else if (e.target.dataset.type === 'item') {
if (e.target.nextElementSibling) {
e.target.nextElementSibling.focus()
} else {
this.input.focus()
}
}
return false
}
if (e.key === 'ArrowUp') {
if (e.target.dataset.type === 'input' && e.target.nextElementSibling && e.target.nextElementSibling.lastChild) {
e.target.nextElementSibling.scrollTop = e.target.nextElementSibling.scrollHeight
e.target.nextElementSibling.lastChild.focus()
} else if (e.target.dataset.type === 'item') {
if (e.target.previousElementSibling) {
e.target.previousElementSibling.focus()
} else {
this.input.focus()
}
}
return false
}
if (e.key === 'Enter') {
let newVal = ''
if (e.target.dataset.type === 'input' && this.filtered.length) {
if (e.target.value && e.target.value !== this.filtered[0]) {
return this.selectText(vnode, this.filtered[0])
}
this.onDone(vnode)
return false
}
if (e.target.dataset.type === 'item') {
return this.selectText(vnode, e.target.dataset.value)
}
}
},
smartOpen: function(vnode) {
if (this.input.value && this.input.value === this.filtered[0]) {
activeBox = null
} else {
activeBox = this.id
}
m.redraw()
},
view: function(vnode) {
closeBox = false
return m('div.form-item.combobox', {
class: vnode.attrs.class,
onclick: util.cancelPropagation,
}, [
m('label', vnode.attrs.label),
m('input', {
'data-type': 'input',
oncreate: (e) => { this.input = e.dom },
onkeydown: (e) => this.onKeyPress(vnode, e),
type: vnode.attrs.type || 'text',
value: vnode.attrs.value,
onfocus: () => this.smartOpen(vnode),
placeholder: vnode.attrs.placeholder || '',
oninput: this.onInput.bind(this, vnode),
}),
activeBox === this.id
? m('div.combobox-list', [
this.filtered.slice(0, 50).map(item => {
return m('div.combobox-list-item', {
'data-type': 'item',
'data-value': item,
onkeydown: (e) => this.onKeyPress(vnode, e),
onclick: (e) => this.selectText(vnode, item),
tabindex: '0',
}, item)
})
])
: null,
])
}
}
module.exports = Combobox