mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 01:02:58 +00:00
Support proper tooltips on undo redo buttons
This commit is contained in:
42
css/app.css
42
css/app.css
@@ -349,3 +349,45 @@ div.typeahead a.active {
|
||||
-webkit-tap-highlight-color:rgba(0,0,0,0);
|
||||
-webkit-touch-callout:none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: 1030;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
font-size: 11px;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tooltip.in {
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
max-width: 200px;
|
||||
padding: 3px 8px;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #000000;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.tooltip.bottom .tooltip-arrow {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-bottom-color: #000000;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
<script src='js/lib/d3.geo.tile.js'></script>
|
||||
<script src='js/lib/d3.size.js'></script>
|
||||
<script src='js/lib/d3.keybinding.js'></script>
|
||||
<script src='js/lib/d3-compat.js'></script>
|
||||
<script src='js/lib/bootstrap-tooltip.js'></script>
|
||||
|
||||
<script src='js/id/id.js'></script>
|
||||
<script src='js/id/util.js'></script>
|
||||
|
||||
14
js/id/id.js
14
js/id/id.js
@@ -46,13 +46,17 @@ window.iD = function(container) {
|
||||
.attr({ id: 'undo', 'class': 'mini' })
|
||||
.property('disabled', true)
|
||||
.html('←<small></small>')
|
||||
.on('click', history.undo);
|
||||
.on('click', history.undo)
|
||||
.call(bootstrap.tooltip()
|
||||
.placement('bottom'));
|
||||
|
||||
undo_buttons.append('button')
|
||||
.attr({ id: 'redo', 'class': 'mini' })
|
||||
.property('disabled', true)
|
||||
.html('→<small></small>')
|
||||
.on('click', history.redo);
|
||||
.on('click', history.redo)
|
||||
.call(bootstrap.tooltip()
|
||||
.placement('bottom'));
|
||||
|
||||
bar.append('input')
|
||||
.attr({ type: 'text', placeholder: 'find a place', id: 'geocode-location' })
|
||||
@@ -137,13 +141,11 @@ window.iD = function(container) {
|
||||
|
||||
bar.select('#undo')
|
||||
.property('disabled', !undo)
|
||||
.select('small')
|
||||
.text(undo);
|
||||
.attr('data-original-title', undo);
|
||||
|
||||
bar.select('#redo')
|
||||
.property('disabled', !redo)
|
||||
.select('small')
|
||||
.text(redo);
|
||||
.attr('data-original-title', redo);
|
||||
});
|
||||
|
||||
window.onresize = function() {
|
||||
|
||||
166
js/lib/bootstrap-tooltip.js
vendored
Normal file
166
js/lib/bootstrap-tooltip.js
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
(function(exports) {
|
||||
|
||||
var bootstrap = (typeof exports.bootstrap === "object") ?
|
||||
exports.bootstrap :
|
||||
(exports.bootstrap = {});
|
||||
|
||||
bootstrap.tooltip = function() {
|
||||
|
||||
var tooltip = function(selection) {
|
||||
selection.each(setup);
|
||||
},
|
||||
animation = d3.functor(false),
|
||||
html = d3.functor(false),
|
||||
title = function() {
|
||||
var title = this.getAttribute("data-original-title");
|
||||
if (title) {
|
||||
return title;
|
||||
} else {
|
||||
title = this.getAttribute("title");
|
||||
this.removeAttribute("title");
|
||||
this.setAttribute("data-original-title", title);
|
||||
}
|
||||
return title;
|
||||
},
|
||||
over = "mouseenter.tooltip",
|
||||
out = "mouseleave.tooltip",
|
||||
placements = "top left bottom right".split(" "),
|
||||
placement = d3.functor("top");
|
||||
|
||||
tooltip.title = function(_) {
|
||||
if (arguments.length) {
|
||||
title = d3.functor(_);
|
||||
return tooltip;
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
};
|
||||
|
||||
tooltip.html = function(_) {
|
||||
if (arguments.length) {
|
||||
html = d3.functor(_);
|
||||
return tooltip;
|
||||
} else {
|
||||
return html;
|
||||
}
|
||||
};
|
||||
|
||||
tooltip.placement = function(_) {
|
||||
if (arguments.length) {
|
||||
placement = d3.functor(_);
|
||||
return tooltip;
|
||||
} else {
|
||||
return placement;
|
||||
}
|
||||
};
|
||||
|
||||
tooltip.show = function(selection) {
|
||||
selection.each(show);
|
||||
};
|
||||
|
||||
tooltip.hide = function(selection) {
|
||||
selection.each(hide);
|
||||
};
|
||||
|
||||
tooltip.toggle = function(selection) {
|
||||
selection.each(toggle);
|
||||
};
|
||||
|
||||
tooltip.destroy = function(selection) {
|
||||
selection
|
||||
.on(over, null)
|
||||
.on(out, null)
|
||||
.attr("title", function() {
|
||||
return this.getAttribute("data-original-title") || this.getAttribute("title");
|
||||
})
|
||||
.attr("data-original-title", null)
|
||||
.select(".tooltip")
|
||||
.remove();
|
||||
};
|
||||
|
||||
function setup() {
|
||||
var root = d3.select(this),
|
||||
animate = animation.apply(this, arguments),
|
||||
tip = root.append("div")
|
||||
.attr("class", "tooltip");
|
||||
|
||||
if (animate) {
|
||||
tip.classed("fade", true);
|
||||
}
|
||||
|
||||
// TODO "inside" checks?
|
||||
|
||||
tip.append("div")
|
||||
.attr("class", "tooltip-arrow");
|
||||
tip.append("div")
|
||||
.attr("class", "tooltip-inner");
|
||||
|
||||
var place = placement.apply(this, arguments);
|
||||
tip.classed(place, true);
|
||||
|
||||
root.on(over, show);
|
||||
root.on(out, hide);
|
||||
}
|
||||
|
||||
function show() {
|
||||
var root = d3.select(this),
|
||||
content = title.apply(this, arguments),
|
||||
tip = root.select(".tooltip")
|
||||
.classed("in", true),
|
||||
markup = html.apply(this, arguments),
|
||||
innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content),
|
||||
place = placement.apply(this, arguments),
|
||||
outer = getPosition(root.node()),
|
||||
inner = getPosition(tip.node()),
|
||||
pos;
|
||||
|
||||
switch (place) {
|
||||
case "top":
|
||||
pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
|
||||
break;
|
||||
case "right":
|
||||
pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
|
||||
break;
|
||||
case "left":
|
||||
pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
|
||||
break;
|
||||
case "bottom":
|
||||
pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y + outer.h};
|
||||
break;
|
||||
}
|
||||
|
||||
tip.style(pos ?
|
||||
{left: ~~pos.x + "px", top: ~~pos.y + "px"} :
|
||||
{left: null, top: null});
|
||||
|
||||
this.tooltipVisible = true;
|
||||
}
|
||||
|
||||
function hide() {
|
||||
d3.select(this).select(".tooltip")
|
||||
.classed("in", false);
|
||||
|
||||
this.tooltipVisible = false;
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (this.tooltipVisible) {
|
||||
hide.apply(this, arguments);
|
||||
} else {
|
||||
show.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
};
|
||||
|
||||
function getPosition(node) {
|
||||
return {
|
||||
x: node.offsetLeft,
|
||||
y: node.offsetTop,
|
||||
w: node.offsetWidth,
|
||||
h: node.offsetHeight
|
||||
};
|
||||
}
|
||||
|
||||
})(this);
|
||||
48
js/lib/d3-compat.js
vendored
Normal file
48
js/lib/d3-compat.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
(function() {
|
||||
|
||||
// get a reference to the d3.selection prototype,
|
||||
// and keep a reference to the old d3.selection.on
|
||||
var d3_selectionPrototype = d3.selection.prototype,
|
||||
d3_on = d3_selectionPrototype.on;
|
||||
|
||||
// our shims are organized by event:
|
||||
// "desired-event": ["shimmed-event", wrapperFunction]
|
||||
var shims = {
|
||||
"mouseenter": ["mouseover", relatedTarget],
|
||||
"mouseleave": ["mouseout", relatedTarget]
|
||||
};
|
||||
|
||||
// rewrite the d3.selection.on function to shim the events with wrapped
|
||||
// callbacks
|
||||
d3_selectionPrototype.on = function(evt, callback, useCapture) {
|
||||
var bits = evt.split("."),
|
||||
type = bits.shift(),
|
||||
shim = shims[type];
|
||||
if (shim) {
|
||||
evt = bits.length ? [shim[0], bits].join(".") : shim[0];
|
||||
if (typeof callback === "function") {
|
||||
callback = shim[1](callback);
|
||||
}
|
||||
return d3_on.call(this, evt, callback, useCapture);
|
||||
} else {
|
||||
return d3_on.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
function relatedTarget(callback) {
|
||||
return function() {
|
||||
var related = d3.event.relatedTarget;
|
||||
if (this === related || childOf(this, related)) {
|
||||
return undefined;
|
||||
}
|
||||
return callback.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function childOf(p, c) {
|
||||
if (p === c) return false;
|
||||
while (c && c !== p) c = c.parentNode;
|
||||
return c === p;
|
||||
}
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user