luci-base, themes: add tooltip helpers & styles
authorJo-Philipp Wich <jo@mein.io>
Thu, 18 Oct 2018 14:14:35 +0000 (16:14 +0200)
committerJo-Philipp Wich <jo@mein.io>
Mon, 5 Nov 2018 10:01:45 +0000 (11:01 +0100)
Add the required JS and CSS infrastructure to support rich hover/focus
tooltips for element.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/cbi.js
themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css
themes/luci-theme-freifunk-generic/htdocs/luci-static/freifunk-generic/cascade.css
themes/luci-theme-material/htdocs/luci-static/material/cascade.css
themes/luci-theme-openwrt/htdocs/luci-static/openwrt.org/cascade.css

index b1fc26c74da7699a0ad3056e8549a005b2b8a26b..b92b86f52333c6341a772efe57b5adfb775ccd97 100644 (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);
 });
index a30dc45b72eed4524c8f0e24ff6d51b23ade108a..690fd2ad4246e9e7b1e0381a4c9487098d7cff87 100644 (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) {
index 3e1b1cd2f13e9f8305dce4b7cf4b1977e5633c23..b9ac0f033d06dab5fe1eb43062fbcab892a78f0d 100644 (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;
index 6961bfe2d762d94a8b3e24dda96dc846ea577513..a4d71eeb673847568837896eb03aa7d04224c4ea 100644 (file)
@@ -291,7 +291,7 @@ header {
 }
 
 header > .fill > .container {
-    padding-top: 0.25rem; 
+    padding-top: 0.25rem;
     padding-right: 1rem;
     padding-bottom: 0.25rem;
     display: flex;
@@ -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;
index 5becfc5ba59ad1d44bc6af3c969d08060f9d9b7a..94d6b5729644068c1cb552a35f7568757d7992f6 100644 (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;