144 lines
3.8 KiB
JavaScript
144 lines
3.8 KiB
JavaScript
|
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
|