blob: b28691c8e8b52afd883c25283ef022a483207fe6 [file] [log] [blame]
/**
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/**
* Shows a fixed element when a separate element begins to go out of view.
*/
class FixedHeaderController {
/**
* @param {Element} el
* @param {Element} fixedEl
*/
constructor(el, fixedEl) {
if (!el || !fixedEl) {
throw new Error('Must provide sentinel and fixed elements to constructor.');
}
/**
* The element to observe to determine whether to show the fixed element.
* @type {!Element}
* @private
*/
this._el = /** @type {!Element} */ (el);
/**
* The element to show when the other begins to go out of view.
* @type {!Element}
* @private
*/
this._fixedEl = /** @type {!Element} */ (fixedEl);
/**
* @type {!IntersectionObserver}
* @private
*/
this._intersectionObserver = new IntersectionObserver(
(entries, observer) => this.intersectionObserverCallback(entries, observer),
{
threshold: 1.0,
}
);
this._intersectionObserver.observe(this._el);
// Fixed positioning on Safari iOS is very broken, and without this hack,
// focusing on the overflow menu will cause all content to scroll.
// The -webkit-overflow-scroll CSS property is only available on mobile
// Safari, so check for it and set the appropriate style to fix this.
if (window.getComputedStyle(document.body)['-webkit-overflow-scrolling'] !== undefined) {
[document.documentElement, document.body].forEach(el => {
el.style.overflow = 'auto';
});
}
}
/**
* @param {!Array<IntersectionObserverEntry>} entries
* @param {!IntersectionObserver} observer
* @private
*/
intersectionObserverCallback(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this._fixedEl.classList.remove('UnitFixedHeader--visible');
} else {
this._fixedEl.classList.add('UnitFixedHeader--visible');
}
});
}
}
new FixedHeaderController(
document.querySelector('.js-fixedHeaderSentinel'),
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 => {
window.location.href = e.target.value;
});
}