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