Skip to content

Instantly share code, notes, and snippets.

@staydecent
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save staydecent/c82a4ee2e8fc56bef034 to your computer and use it in GitHub Desktop.
Save staydecent/c82a4ee2e8fc56bef034 to your computer and use it in GitHub Desktop.
Mithril autocompleter
/** @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