content/static: add copy on click to unit page header

Adds copy on click code to unit page.

Change-Id: I2a7e350e002bfc5459cc9204d92690a245a4f32f
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/259201
Trust: Jamal Carvalho <jamal@golang.org>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/content/static/css/unit_header.css b/content/static/css/unit_header.css
index 24de709..8a9247c 100644
--- a/content/static/css/unit_header.css
+++ b/content/static/css/unit_header.css
@@ -23,12 +23,13 @@
 }
 .UnitHeader-breadcrumbItem {
   color: var(--gray-4);
+  display: inline-flex;
   font-size: 0.875rem;
   line-height: 1.5rem;
 }
 .UnitHeader-breadcrumbItem:not(:last-child)::after {
   content: '>';
-  padding: 0.5rem;
+  padding: 0 0.5rem;
 }
 .UnitHeader-heading {
   display: flex;
diff --git a/content/static/html/helpers/_unit_header.tmpl b/content/static/html/helpers/_unit_header.tmpl
index 14bd702..7d429a6 100644
--- a/content/static/html/helpers/_unit_header.tmpl
+++ b/content/static/html/helpers/_unit_header.tmpl
@@ -19,7 +19,17 @@
               <a href="{{.Href}}">{{.Body}}</a>
             </span>
           {{end}}
-          <span class="UnitHeader-breadcrumbItem">{{.Current}}</span>
+          <span class="UnitHeader-breadcrumbItem">
+            {{.Current}}
+            {{if .CopyData}}
+              <button class="CopyToClipboardButton js-copyToClipboard"
+                  title="Copy path to clipboard"
+                  data-to-copy="{{.CopyData}}"
+                  tabindex="-1">
+                <img class="CopyToClipboardButton-image" src="/static/img/copy-click.svg" alt="">
+              </button>
+            {{end}}
+          </span>
         {{end}}
       </div>
       <div class="UnitHeader-heading">
diff --git a/content/static/js/unit_fixed_header.js b/content/static/js/unit_fixed_header.js
index a0b2ee1..b28691c 100644
--- a/content/static/js/unit_fixed_header.js
+++ b/content/static/js/unit_fixed_header.js
@@ -76,6 +76,73 @@
   document.querySelector('.js-fixedHeader')
 );
 
+/**
+ * This class decorates an element to copy arbitrary data attached via a data-
+ * attribute to the clipboard.
+ */
+class CopyToClipboardController {
+  /**
+   * The element that will trigger copying text to the clipboard. The text is
+   * expected to be within its data-to-copy attribute.
+   * @param {!Element} el
+   */
+  constructor(el) {
+    /**
+     * @type {!Element}
+     * @private
+     */
+    this._el = el;
+
+    /**
+     * The data to be copied to the clipboard.
+     * @type {string}
+     * @private
+     */
+    this._data = el.dataset['toCopy'];
+
+    el.addEventListener('click', e => this.handleCopyClick(/** @type {!Event} */ (e)));
+  }
+
+  /**
+   * Handles when the primary element is clicked.
+   * @param {!Event} e
+   * @private
+   */
+  handleCopyClick(e) {
+    e.preventDefault();
+    const TOOLTIP_SHOW_DURATION_MS = 1000;
+
+    // This API is not available on iOS.
+    if (!navigator.clipboard) {
+      this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);
+      return;
+    }
+    navigator.clipboard
+      .writeText(this._data)
+      .then(() => {
+        this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);
+      })
+      .catch(() => {
+        this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);
+      });
+  }
+
+  /**
+   * Shows the given text in a tooltip for a specified amount of time, in milliseconds.
+   * @param {string} text
+   * @param {number} durationMs
+   * @private
+   */
+  showTooltipText(text, durationMs) {
+    this._el.setAttribute('data-tooltip', text);
+    setTimeout(() => this._el.setAttribute('data-tooltip', ''), durationMs);
+  }
+}
+
+document.querySelectorAll('.js-copyToClipboard').forEach(el => {
+  new CopyToClipboardController(el);
+});
+
 const overflowSelect = document.querySelector('.js-overflowSelect');
 if (overflowSelect) {
   overflowSelect.addEventListener('change', e => {