Switch to comboboxes

This commit is contained in:
Tom MacWright
2013-02-01 17:54:55 -05:00
parent 2957d799f1
commit eb999c47c6
6 changed files with 184 additions and 121 deletions
+7 -9
View File
@@ -140,21 +140,19 @@
<body>
<div id='foo'><input /></div>
<div id='bar'><input /></div>
<div id="iD"></div><script>
<input />
<div id="iD"><script>
var options = d3.range(0, 50).map(function(i) {
return {
value: i * 10,
title: i * 10
value: '' + (i * 10),
title: '' + (i * 10)
};
});
d3.select('#foo').call(d3.combobox()
.data(function(selection, cb) {
cb(options);
}));
.data(options));
d3.select('#bar').call(d3.combobox()
.data(function(selection, cb) {
cb(options);
}));
.data(options));
</script></body>
<script type="text/javascript">
+22 -24
View File
@@ -563,6 +563,10 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
margin-top: -1px;
}
.input-wrap-position {
position: relative;
}
.tag-row input {
width: 50%;
border-left: 0;
@@ -580,18 +584,6 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
border-left: 1px solid #ccc;
}
.input-wrap::after {
content: '';
position: absolute;
right: 9px;
top: 14px;
height: 0;
width: 0;
border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.tag-row button {
position: absolute;
top: 5px;
@@ -1435,11 +1427,9 @@ a.success-action {
.preset-input input {
width: 100%;
}
div.combobox {
.combobox {
width:155px;
z-index: 9999;
display: none;
@@ -1451,7 +1441,7 @@ div.combobox {
border: 1px solid #ccc;
}
div.combobox a {
.combobox a {
height: 25px;
line-height: 25px;
cursor: pointer;
@@ -1462,18 +1452,26 @@ div.combobox a {
white-space: nowrap;
}
div.combobox a:hover,
div.combobox a.selected {
.combobox a:hover,
.combobox a.selected {
background: #e1e8ff;
color: #154dff;
}
div.combobox a:first-child {
.combobox a:first-child {
border-top: 0;
}
div.combobox-carat {
cursor: pointer;
padding:0 5px;
vertical-align:middle;
.combobox-carat::after {
display:block;
content: '';
cursor:url(../img/cursor-pointer.png) 6 1, auto;
position: absolute;
right: 10px;
top: 5px;
height: 0;
width: 0;
border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
+1
View File
@@ -18,6 +18,7 @@
<script src='js/lib/ohauth.js'></script>
<script src='js/lib/jxon.js'></script>
<script src='js/lib/d3.typeahead.js'></script>
<script src='js/lib/d3.combobox.js'></script>
<script src='js/lib/d3.geo.tile.js'></script>
<script src='js/lib/d3.size.js'></script>
<script src='js/lib/d3.trigger.js'></script>
+17 -12
View File
@@ -11,14 +11,14 @@ iD.ui.inspector = function() {
var entity = selection.datum();
var iwrap = selection.append('div')
.attr('class','inspector content');
.attr('class','inspector content'),
head = iwrap.append('div')
.attr('class', 'head inspector-inner fillL'),
h2 = head.append('h2');
var head = iwrap.append('div')
.attr('class', 'head inspector-inner fillL');
var h2 = head.append('h2');
h2.append('span')
.attr('class', 'icon big icon-pre-text big-' + entity.geometry(graph));
var name = h2.append('input')
.attr('placeholder', 'name')
.property('value', function() {
@@ -150,14 +150,18 @@ iD.ui.inspector = function() {
var inputs = row.append('div')
.attr('class', 'input-wrap');
inputs.append('input')
inputs.append('span')
.attr('class', 'key-wrap')
.append('input')
.property('type', 'text')
.attr('class', 'key')
.attr('maxlength', 255)
.property('value', function(d) { return d.key; })
.on('change', function(d) { d.key = this.value; event.change(); });
inputs.append('input')
inputs.append('span')
.attr('class', 'input-wrap-position')
.append('input')
.property('type', 'text')
.attr('class', 'value')
.attr('maxlength', 255)
@@ -253,7 +257,7 @@ iD.ui.inspector = function() {
geometry = entity.geometry(graph),
row = d3.select(this),
key = row.selectAll('.key'),
value = row.selectAll('.value');
value = row.selectAll('.input-wrap-position');
function sort(value, data) {
var sameletter = [],
@@ -278,14 +282,15 @@ iD.ui.inspector = function() {
});
}, 500)));
value.call(d3.typeahead()
.data(_.debounce(function(_, callback) {
var valueinput = value.select('input');
value.call(d3.combobox()
.fetcher(_.debounce(function(_, __, callback) {
taginfo.values({
key: key.property('value'),
geometry: geometry,
query: value.property('value')
query: valueinput.property('value')
}, function(err, data) {
if (!err) callback(sort(value.property('value'), data));
if (!err) callback(sort(valueinput.property('value'), data));
});
}, 500)));
}
+6 -8
View File
@@ -76,14 +76,12 @@ iD.ui.preset = function() {
});
break;
case 'select':
i = this.append('select');
var options = d.values.slice();
options.unshift('');
i.selectAll('option')
.data(options)
.enter()
.append('option')
.text(String);
var w = this.append('span').attr('class', 'input-wrap-position');
i = w.append('input');
w.call(d3.combobox()
.data([''].concat(d.values.slice()).map(function(o) {
return { value: o, title: o };
})));
break;
}
if (i) {
+131 -68
View File
@@ -1,16 +1,21 @@
d3.combobox = function() {
var event = d3.dispatch('accept'),
autohighlight = false,
autofilter = false,
input,
container,
data;
container, input, shown = false, data = [];
var fetcher = function(val, data, cb) {
cb(data.filter(function(d) {
return d.title
.toString()
.toLowerCase()
.indexOf(val.toLowerCase()) !== -1;
}));
};
var typeahead = function(selection) {
var hidden, idx = autohighlight ? 0 : -1;
var rect = selection.select('input').node().getBoundingClientRect();
var idx = -1,
rect = selection.select('input')
.node()
.getBoundingClientRect();
input = selection.select('input');
container = selection
@@ -25,104 +30,174 @@ d3.combobox = function() {
});
carat = selection
.insert('div', ':first-child')
.insert('a', ':first-child')
.attr('class', 'combobox-carat')
.text('+')
.style({
position: 'absolute',
left: (rect.width - 20) + 'px',
left: rect.width + 'px',
top: '0px'
})
.on('click', function() {
update();
show();
});
.on('mousedown', stop)
.on('click', click);
selection
.on('keyup.typeahead', key);
function stop() {
// prevent the form element from blurring. it blurs
// on mousedown
d3.event.stopPropagation();
d3.event.preventDefault();
}
hidden = false;
function click() {
d3.event.preventDefault();
d3.event.stopPropagation();
update();
show();
// focus the node so that a click outside of the
// combo box will hide it
input.node().focus();
}
function hide() {
idx = autohighlight ? 0 : -1;
hidden = true;
function blur() {
// hide the combobox whenever the input element
// loses focus
slowHide();
}
function show() {
container.style('display', 'block');
shown = true;
}
function hide() {
idx = -1;
container.style('display', 'none');
shown = false;
}
function slowHide() {
if (autohighlight && container.select('a.selected').node()) {
select(container.select('a.selected').datum());
event.accept();
}
window.setTimeout(hide, 150);
}
selection
.on('focus.typeahead', show)
.on('blur.typeahead', slowHide);
function key() {
var len = container.selectAll('a').data().length;
if (d3.event.keyCode === 40) {
idx = Math.min(idx + 1, len - 1);
return highlight();
} else if (d3.event.keyCode === 38) {
idx = Math.max(idx - 1, 0);
return highlight();
} else if (d3.event.keyCode === 13) {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
event.accept();
hide();
} else {
update();
function keydown() {
if (!shown) return;
switch (d3.event.keyCode) {
// down arrow
case 40:
next();
d3.event.preventDefault();
break;
// up arrow
case 38:
prev();
d3.event.preventDefault();
break;
// escape, tab
case 9:
case 13:
d3.event.preventDefault();
break;
}
d3.event.stopPropagation();
}
function keyup() {
switch (d3.event.keyCode) {
// escape
case 27:
hide();
break;
// escape, tab
case 9:
case 13:
if (!shown) return;
accept();
break;
default:
update();
d3.event.preventDefault();
}
d3.event.stopPropagation();
}
function accept() {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
hide();
}
function next() {
var len = container.selectAll('a').data().length;
idx = Math.min(idx + 1, len - 1);
highlight();
}
function prev() {
idx = Math.max(idx - 1, 0);
highlight();
}
function highlight() {
container
.selectAll('a')
.classed('selected', function(d, i) { return i == idx; });
var height = container.node().offsetHeight,
top = container.select('a.selected').node().offsetTop,
selectedHeight = container.select('a.selected').node().offsetHeight;
if ((top + selectedHeight) < height) {
container.node().scrollTop = 0;
} else {
container.node().scrollTop = top;
}
}
function update() {
function run(data) {
container.style('display', function() {
return data.length ? 'block' : 'none';
});
function render(data) {
if (data.length) show();
else hide();
var options = container
.selectAll('a')
.selectAll('a.combobox-option')
.data(data, function(d) { return d.value; });
options.enter()
.append('a')
.text(function(d) { return d.value; })
.attr('class', 'combobox-option')
.attr('title', function(d) { return d.title; })
.on('click', select);
options.exit().remove();
options
.classed('selected', function(d, i) { return i == idx; });
.classed('selected', function(d, i) { return i == idx; })
.order();
}
if (typeof data === 'function') data(selection, run);
else run(data);
fetcher.apply(selection, [
selection.select('input').property('value'),
data, render]);
}
// select the choice given as d
function select(d) {
input
.property('value', d.value)
.trigger('change');
container.style('display', 'none');
event.accept(d);
hide();
}
input
.on('blur.typeahead', blur)
.on('keydown.typeahead', keydown)
.on('keyup.typeahead', keyup);
};
typeahead.fetcher = function(_) {
if (!arguments.length) return fetcher;
fetcher = _;
return typeahead;
};
typeahead.data = function(_) {
@@ -131,17 +206,5 @@ d3.combobox = function() {
return typeahead;
};
typeahead.autofilter = function(_) {
if (!arguments.length) return autofilter;
autofilter = _;
return typeahead;
};
typeahead.autohighlight = function(_) {
if (!arguments.length) return autohighlight;
autohighlight = _;
return typeahead;
};
return d3.rebind(typeahead, event, 'on');
};