luci-base, themes: add tooltip helpers & styles

Add the required JS and CSS infrastructure to support rich hover/focus
tooltips for element.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2018-10-18 16:14:35 +02:00
parent 31bce2455f
commit 7f613be500
5 changed files with 93 additions and 13 deletions

View file

@ -2191,6 +2191,58 @@ function cbi_update_table(table, data, placeholder) {
});
}
var tooltipDiv = null, tooltipTimeout = null;
function showTooltip(ev) {
if (!matchesElem(ev.target, '[data-tooltip]'))
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
var rect = ev.target.getBoundingClientRect(),
x = rect.left + window.pageXOffset,
y = rect.top + rect.height + window.pageYOffset;
tooltipDiv.className = 'cbi-tooltip';
tooltipDiv.innerHTML = '▲ ';
tooltipDiv.firstChild.data += ev.target.getAttribute('data-tooltip');
if (ev.target.hasAttribute('data-tooltip-style'))
tooltipDiv.classList.add(ev.target.getAttribute('data-tooltip-style'));
if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
y -= (tooltipDiv.offsetHeight + ev.target.offsetHeight);
tooltipDiv.firstChild.data = '▼ ' + tooltipDiv.firstChild.data.substr(2);
}
tooltipDiv.style.top = y + 'px';
tooltipDiv.style.left = x + 'px';
tooltipDiv.style.opacity = 1;
}
function hideTooltip(ev) {
if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv)
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
tooltipDiv.style.opacity = 0;
tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
}
document.addEventListener('DOMContentLoaded', function() {
tooltipDiv = document.body.appendChild(E('div', { 'class': 'cbi-tooltip' }));
document.addEventListener('mouseover', showTooltip, true);
document.addEventListener('mouseout', hideTooltip, true);
document.addEventListener('focus', showTooltip, true);
document.addEventListener('blur', hideTooltip, true);
document.querySelectorAll('.table').forEach(cbi_update_table);
});

View file

@ -1168,7 +1168,8 @@ footer {
.btn.info,
.alert-message.info,
.btn.info:hover,
.alert-message.info:hover {
.alert-message.info:hover,
.cbi-tooltip.error, .cbi-tooltip.success, .cbi-tooltip.info {
color: #fff;
}
@ -1180,25 +1181,26 @@ footer {
.btn.danger,
.alert-message.danger,
.btn.error,
.alert-message.error {
.alert-message.error,
.cbi-tooltip.error {
background: linear-gradient(to bottom, #ee5f5b, #c43c35) repeat-x;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.btn.success, .alert-message.success {
.btn.success, .alert-message.success, .cbi-tooltip.success {
background: linear-gradient(to bottom, #62c462, #57a957) repeat-x;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.btn.info, .alert-message.info {
.btn.info, .alert-message.info, .cbi-tooltip.info {
background: linear-gradient(to bottom, #5bc0de, #339bb9) repeat-x;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.alert-message.notice {
.alert-message.notice, .cbi-tooltip.notice {
background: linear-gradient(to bottom, #efefef, #fefefe) repeat-x;
text-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25);
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
@ -1491,8 +1493,13 @@ select + .cbi-button {
position: absolute;
z-index: 1000;
left: -1000px;
box-shadow: 0 0 2px #ccc;
border-radius: 3px;
background: #fff;
white-space: pre;
padding: 2px 5px;
opacity: 0;
transition: opacity .25s ease-out;
transition: opacity .25s ease-in;
}
.cbi-tooltip-container:hover .cbi-tooltip:not(:empty) {

View file

@ -156,12 +156,17 @@ a img {
color: #000;
}
.alert-message.notice {
.alert-message, .cbi-tooltip.error {
background: #fee;
color: #a22;
}
.alert-message.notice, .cbi-tooltip.notice {
background: linear-gradient(#ccc 0%, #eee 100%);
color: #4a6b7c;
}
.alert-message.warning {
.alert-message.warning, .cbi-tooltip.warning {
background: linear-gradient(#dda 0%, #dd8 100%);
color: #c00;
}
@ -1354,6 +1359,10 @@ td.cbi-value-error {
position: absolute;
z-index: 1000;
left: -1000px;
border-radius: 3px;
background: #fff;
padding: 2px 5px;
white-space: pre;
opacity: 0;
transition: opacity .25s ease-out;
pointer-events: none;

View file

@ -1481,6 +1481,10 @@ small {
position: absolute;
z-index: 1000;
left: -1000px;
border-radius: 3px;
background: #fff;
padding: 2px 5px;
white-space: pre;
opacity: 0;
transition: opacity .25s ease-out;
pointer-events: none;

View file

@ -231,18 +231,22 @@ hr {
padding: .5em;
border-radius: 3px;
border: 1px solid #a22;
color: #a22;
background: #fee;
margin: 0 0 .5em 0;
}
.alert-message.notice {
.alert-message, .cbi-tooltip.error {
border-color: #a22;
background: #fee;
color: #a22;
}
.alert-message.notice, .cbi-tooltip.notice {
border-color: #15a;
background: #e6f6ff;
color: #15a;
}
.alert-message.warning {
.alert-message.warning, .cbi-tooltip.warning {
border-color: #ed5;
background: #fe9;
color: #650;
@ -1173,6 +1177,10 @@ select + .cbi-button {
position: absolute;
z-index: 1000;
left: -1000px;
border-radius: 3px;
background: #fff;
padding: 2px 5px;
white-space: pre;
opacity: 0;
transition: opacity .25s ease-out;
pointer-events: none;