Last active
August 29, 2015 14:15
-
-
Save staydecent/c82a4ee2e8fc56bef034 to your computer and use it in GitHub Desktop.
Mithril autocompleter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @jsx m */ | |
'use strict'; | |
var R = require('ramda'); | |
var m = require('mithril'); | |
var autocompleter = function() { | |
var autocompleter = {}; | |
autocompleter.state = { | |
term: m.prop(""), | |
selection: m.prop(), | |
highlightIndex: m.prop(0), | |
options: m.prop([]), | |
handleInput: function(ctrl, value) { | |
autocompleter.state.term(value.toLowerCase()); | |
if (ctrl.search) { | |
ctrl.search(value.toLowerCase()); | |
} | |
}, | |
handleKeyDown: function(ctrl, e) { | |
var state = autocompleter.state; | |
if (['Up', 'Down', 'Enter'].indexOf(e.keyIdentifier) > -1) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
var max = state.options().length - 1; | |
var current = state.highlightIndex(); | |
switch (e.keyIdentifier) { | |
case "Up": | |
if (current > 0) | |
state.highlightIndex(current - 1); | |
break; | |
case "Down": | |
if (current < max) | |
state.highlightIndex(current + 1); | |
break; | |
case "Enter": | |
state.handleClick(ctrl, state.options()[state.highlightIndex()]); | |
break; | |
} | |
} | |
}, | |
handleClick: function(ctrl, item) { | |
autocompleter.state.selection(item); | |
if (ctrl.select) { | |
ctrl.select(item); | |
} | |
}, | |
filter: function(item) { | |
return autocompleter.state.term() && item.name.toLowerCase().indexOf(autocompleter.state.term()) > -1; | |
} | |
}; | |
autocompleter.view = function(ctrl) { | |
var state = autocompleter.state; | |
state.options(ctrl.data()); | |
return ( | |
<div> | |
<input | |
oninput={m.withAttr("value", state.handleInput.bind(null, ctrl))} | |
onkeydown={state.handleKeyDown.bind(null, ctrl)} | |
config={autocompleter.config(ctrl)} /> | |
<div className="settings-menu pure-menu pure-menu-open"> | |
<ul> | |
{state.options().filter(state.filter).map(optionNode.bind(null, state, ctrl))} | |
</ul> | |
</div> | |
</div> | |
); | |
}; | |
autocompleter.config = function(ctrl) { | |
return function(el, isInitd, ctx) { | |
var selectedItem = autocompleter.state.selection(); | |
if (!selectedItem) return; | |
el.value = (ctrl.prop) ? selectedItem[ctrl.prop] : selectedItem; | |
autocompleter.state.selection(null); | |
}; | |
}; | |
return autocompleter; | |
}; | |
function optionNode(state, ctrl, item, idx) { | |
var prop = (ctrl.prop) ? ctrl.prop : 'label'; | |
var cls = ['match-item', 'menu-item']; | |
if (item._service) cls.push(item._service); | |
if (state.highlightIndex() === idx) cls.push('highlight'); | |
if (item.type) { | |
contents.push(m('span.meta', item.type)); | |
} | |
return m('li', { | |
key: 'autocomplete-'+idx, | |
'data-idx': idx, | |
className: cls.join(' '), | |
onmouseover: state.highlightIndex.bind(null, idx), | |
onclick: state.handleClick.bind(null, ctrl, item) | |
}, [m('a', {title: item[prop]}, item[prop])]); | |
} | |
// User Land | |
var popover = {}; | |
popover.state = {}; | |
popover.state.init = function() { | |
this.users = m.prop([ | |
{id: 1, name: "John"}, | |
{id: 2, name: "Bob"}, | |
{id: 3, name: "Mary"}, | |
{id: 4, name: "Sam"} | |
]); | |
this.selectedUser = m.prop(); | |
this.userAC = new autocompleter(); | |
this.projects = m.prop([]); | |
this.selectedProject = m.prop(); | |
this.projectAC = new autocompleter(); | |
}; | |
popover.controller = function() { | |
popover.state.init(); | |
console.debug('popover.controller'); | |
}; | |
popover.view = function() { | |
var state = popover.state; | |
console.debug('user', state.selectedUser()); | |
console.debug('project', state.selectedProject()); | |
return m("div", [ | |
state.userAC.view({ | |
data: state.users, | |
select: state.selectedUser, | |
prop: 'name' | |
}), | |
state.projectAC.view({ | |
search: getProjects, | |
data: state.projects, | |
select: function(item) { | |
// do something with it | |
state.selectedProject(item); | |
}, | |
prop: 'name' | |
}), | |
]); | |
}; | |
// initialize | |
m.module(document.getElementById("popover"), popover); | |
function getProjects(term) { | |
if (!term) return; | |
console.debug('getProjects', term); | |
var url = 'http://en.wikipedia.org/w/api.php?action=opensearch&format=json&search='+encodeURIComponent(term); | |
m.request({ | |
method: "GET", | |
url: url | |
}).then(function(data) { | |
return R.compose(R.map(R.createMapEntry('name')))(data[1]); | |
}).then(popover.state.projects); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment