]> source.dussan.org Git - gitblit.git/commitdiff
Add a "Copied" tooltip to the copy-to-clipboard button
authorFlorian Zschocke <f.zschocke+git@gmail.com>
Mon, 21 Nov 2022 00:11:52 +0000 (01:11 +0100)
committerFlorian Zschocke <f.zschocke+git@gmail.com>
Sat, 3 Dec 2022 13:33:31 +0000 (14:33 +0100)
This is not the ideal version, since the height is too low for the tooltip
used for the drop-down menus. Probably has something to do with the
container or something. But at least something is there now, even if
not the most beautiful.

src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/java/com/gitblit/wicket/pages/SummaryPage.html
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html
src/main/resources/clipboard/gitblit-ctcbtn.js [new file with mode: 0644]
src/main/resources/gitblit.css

index 4d30e049d263353c194047f3778915efde9eef39..b30aee976a3fcf7a17c0e2ece5068563bb98f64d 100644 (file)
@@ -171,6 +171,11 @@ public abstract class RepositoryPage extends RootPage {
                add(searchForm);\r
                searchForm.setTranslatedAttributes();\r
 \r
+               // load clipboard library to copy repository URL\r
+               addBottomScript("../../clipboard/clipboard.min.js");\r
+               // instantiate clipboard\r
+               addBottomScript("../../clipboard/gitblit-ctcbtn.js");\r
+\r
                // set stateless page preference\r
                setStatelessHint(true);\r
        }\r
index b6afc301ee7c0b689532691f717e120530959333..8915ecf540e5f2024f3acee8b2b2be2e5ff147c4 100644 (file)
                \r
        </wicket:fragment>\r
 \r
-       <!-- load clipboard library to copy repository URL -->\r
-       <script type="text/javascript" src="clipboard/clipboard.min.js"></script>\r
-       <!-- instantiate clipboard -->\r
-       <script>\r
-        var clipboard = new ClipboardJS('.ctcbtn');\r
-    </script>\r
-\r
 </wicket:extend>\r
 </body>\r
 </html>
\ No newline at end of file
index 1cadd7d5b35fba4eb8f14cf605df7f094dca3f3b..e4b7427abc79a60bb687e0fcae7a4238988d6da0 100644 (file)
        \r
        <wicket:fragment wicket:id="repositoryUrlFragment">\r
                <div class="btn-toolbar" style="margin: 0px;">\r
-                       <div class="btn-group repositoryUrlContainer">\r
+                       <div class="btn-group repositoryUrlContainer tooltipped tooltipped-w">\r
                                <img style="vertical-align: middle;padding: 0px 0px 1px 3px;" wicket:id="accessRestrictionIcon"></img>\r
                                <span wicket:id="menu"></span>\r
                                <div class="repositoryUrl">\r
                                        <span wicket:id="primaryUrl">[repository primary url]</span>\r
-                                       <span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span>\r
+                                       <span class="tooltipped tooltipped-n">\r
+                                               <span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span>\r
+                                       </span>\r
                                </div>\r
                                <span class="hidden-phone hidden-tablet repositoryUrlRightCap" wicket:id="primaryUrlPermission">[repository primary url permission]</span>\r
                        </div>\r
@@ -33,7 +35,7 @@
        \r
        <wicket:fragment wicket:id="applicationMenusFragment">\r
                <div class="btn-toolbar" style="margin: 4px 0px 0px 0px;">\r
-                       <div class="btn-group" wicket:id="appMenus">\r
+                       <div class="btn-group tooltipped tooltipped-w" wicket:id="appMenus">\r
                                <span wicket:id="appMenu"></span>\r
                        </div>\r
                </div>\r
@@ -87,7 +89,7 @@
     \r
     <!-- JavaScript automatic copy to clipboard -->\r
     <wicket:fragment wicket:id="clippyPanel">\r
-               <img class="ctcbtn" data-clipboard-action="copy" wicket:id="copyIcon" wicket:message="title:gb.copyToClipboard" />\r
+               <img class="ctcbtn" wicket:id="copyIcon" wicket:message="title:gb.copyToClipboard" />\r
        </wicket:fragment>\r
 \r
        <wicket:fragment wicket:id="workingCopyFragment">\r
diff --git a/src/main/resources/clipboard/gitblit-ctcbtn.js b/src/main/resources/clipboard/gitblit-ctcbtn.js
new file mode 100644 (file)
index 0000000..ddb2dda
--- /dev/null
@@ -0,0 +1,74 @@
+// Instantiate the clipboarding
+var clipboard = new ClipboardJS('.ctcbtn');
+
+clipboard.on('success', function (e) {
+  showTooltip(e.trigger, "Copied!");
+});
+
+clipboard.on('error', function (e) {
+  showTooltip(e.trigger, fallbackMessage(e.action));
+});
+
+// Attach events to buttons to clear tooltip again
+var btns = document.querySelectorAll('.ctcbtn');
+for (var i = 0; i < btns.length; i++) {
+  btns[i].addEventListener('mouseleave', clearTooltip);
+  btns[i].addEventListener('blur', clearTooltip);
+}
+
+
+function findTooltipped(elem) {
+  do {
+    if (elem.classList.contains('tooltipped')) return elem;
+    elem = elem.parentElement;
+  } while (elem != null);
+  return null;
+}
+
+// Show or hide tooltip by setting the tooltipped-active class
+// on a parent that contains tooltipped. Since the copy button
+// could be and image, or be hidden after clicking, the tooltipped
+// element might be higher in the hierarchy.
+var ttset;
+function showTooltip(elem, msg) {
+  let ttelem = findTooltipped(elem);
+  if (ttelem != null) {
+    ttelem.classList.add('tooltipped-active');
+    ttelem.setAttribute('data-tt-text', msg);
+    ttset=Date.now();
+  }
+  else {
+    console.warn("Could not find any tooltipped element for clipboard button.", elem);
+  }
+}
+
+function clearTooltip(e) {
+  let ttelem = findTooltipped(e.currentTarget);
+  if (ttelem != null) {
+    let now = Date.now();
+    if (now - ttset < 500) {
+        // Give the tooltip some time to display
+        setTimeout(function(){ttelem.classList.remove('tooltipped-active')}, 1000)
+    }
+    else {
+        ttelem.classList.remove('tooltipped-active');
+    }
+  }
+  else {
+    console.warn("Could not find any tooltipped element for clipboard button.", e.currentTarget);
+  }
+}
+
+// If the API is not supported, at least fall back to a message saying
+// that now that the text is selected, Ctrl-C can be used.
+// This is still a problem in the repo URL dropdown. When it is hidden, Ctrl-C doesn't work.
+function fallbackMessage(action) {
+  var actionMsg = "";
+  if (/Mac/i.test(navigator.userAgent)) {
+    actionMsg = "Press ⌘-C to copy";
+  }
+  else {
+    actionMsg = "Press Ctrl-C to copy";
+  }
+  return actionMsg;
+}
index f7271788ea0bfbea742a237e5b04d591b7d0b139..bd2befd3b1967d6d739eb67bab3ec837d0b945ad 100644 (file)
@@ -2408,4 +2408,174 @@ table.filestore-status {
     font-weight: 200;\r
     font-size: 1em;\r
     font-variant: normal;\r
-}
\ No newline at end of file
+}\r
+\r
+\r
+/*\r
+    Copy-to-clipboard tooltip styling from Github's primer.css\r
+   https://primer.style/css/components/tooltips\r
+   Adjusted to not hover but fade-in/out on clipboard events.\r
+*/\r
+\r
+.tooltipped {\r
+    position:relative\r
+}\r
+\r
+.tooltipped:after {\r
+    position: absolute;\r
+    z-index: 1000000;\r
+    padding: 5px 8px;\r
+\r
+    font: normal normal 11px/1.5 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";\r
+    color: #fff;\r
+    background: rgba(42, 42, 42, .8);\r
+    text-align: center;\r
+    text-decoration: none;\r
+    text-shadow: none;\r
+    text-transform: none;\r
+    letter-spacing: normal;\r
+    word-wrap: break-word;\r
+    white-space: pre;\r
+    pointer-events: none;\r
+    content: attr(data-tt-text);\r
+    border-radius: 3px;\r
+    -webkit-font-smoothing:subpixel-antialiased;\r
+\r
+    opacity: 0;\r
+    transition: 0.5s opacity;\r
+}\r
+\r
+.tooltipped:before {\r
+    position: absolute;\r
+    z-index: 1000001;\r
+\r
+    width: 0;\r
+    height: 0;\r
+    color: rgba(42, 42, 42, .8);\r
+    pointer-events: none;\r
+    content: "";\r
+    border:5px solid transparent;\r
+\r
+    opacity: 0;\r
+    transition: 0.5s opacity;\r
+}\r
+\r
+.tooltipped-active:before, .tooltipped-active:after {\r
+    opacity: 1;\r
+    text-decoration:none\r
+}\r
+\r
+.tooltipped-s:after, .tooltipped-se:after, .tooltipped-sw:after {\r
+    top: 100%;\r
+    right: 50%;\r
+    margin-top:5px\r
+}\r
+\r
+.tooltipped-s:before, .tooltipped-se:before, .tooltipped-sw:before {\r
+    top: auto;\r
+    right: 50%;\r
+    bottom: -5px;\r
+    margin-right: -5px;\r
+    border-bottom-color:rgba(42, 42, 42, .8)\r
+}\r
+\r
+.tooltipped-se:after {\r
+    right: auto;\r
+    left: 50%;\r
+    margin-left:-15px\r
+}\r
+\r
+.tooltipped-sw:after {\r
+    margin-right:-15px\r
+}\r
+\r
+.tooltipped-n:after, .tooltipped-ne:after, .tooltipped-nw:after {\r
+    right: 50%;\r
+    bottom: 100%;\r
+    margin-bottom:5px\r
+}\r
+\r
+.tooltipped-n:before, .tooltipped-ne:before, .tooltipped-nw:before {\r
+    top: -5px;\r
+    right: 50%;\r
+    bottom: auto;\r
+    margin-right: -5px;\r
+    border-top-color:rgba(42, 42, 42, .8)\r
+}\r
+\r
+.tooltipped-ne:after {\r
+    right: auto;\r
+    left: 50%;\r
+    margin-left:-15px\r
+}\r
+\r
+.tooltipped-nw:after {\r
+    margin-right:-15px\r
+}\r
+\r
+.tooltipped-s:after, .tooltipped-n:after {\r
+    -webkit-transform: translateX(50%);\r
+    -ms-transform: translateX(50%);\r
+    transform:translateX(50%)\r
+}\r
+\r
+.tooltipped-w:after {\r
+    right: 100%;\r
+    bottom: 50%;\r
+    margin-right: 5px;\r
+    -webkit-transform: translateY(50%);\r
+    -ms-transform: translateY(50%);\r
+    transform:translateY(50%)\r
+}\r
+\r
+.tooltipped-w:before {\r
+    top: 50%;\r
+    bottom: 50%;\r
+    left: -5px;\r
+    margin-top: -5px;\r
+    border-left-color:rgba(42, 42, 42, .8)\r
+}\r
+\r
+.tooltipped-e:after {\r
+    bottom: 50%;\r
+    left: 100%;\r
+    margin-left: 5px;\r
+    -webkit-transform: translateY(50%);\r
+    -ms-transform: translateY(50%);\r
+    transform:translateY(50%)\r
+}\r
+\r
+.tooltipped-e:before {\r
+    top: 50%;\r
+    right: -5px;\r
+    bottom: 50%;\r
+    margin-top: -5px;\r
+    border-right-color:rgba(42, 42, 42, .8)\r
+}\r
+\r
+\r
+.tooltipped-sticky:before, .tooltipped-sticky:after {\r
+    display:inline-block\r
+}\r
+\r
+\r
+.fullscreen-overlay-enabled.dark-theme .tooltipped:after {\r
+    color: #000;\r
+    background:rgba(200, 200, 200, .8)\r
+}\r
+\r
+.fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-s:before, .fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-se:before, .fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-sw:before {\r
+    border-bottom-color:rgba(200, 200, 200, .8)\r
+}\r
+\r
+.fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-n:before, .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-ne:before, .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-nw:before {\r
+    border-top-color:rgba(200, 200, 200, .8)\r
+}\r
+\r
+.fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-e:before {\r
+    border-right-color:rgba(200, 200, 200, .8)\r
+}\r
+\r
+.fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-w:before {\r
+    border-left-color:rgba(200, 200, 200, .8)\r
+}\r