@hashicorp/design-system-components
Version:
Helios Design System Components
171 lines (159 loc) • 5.92 kB
JavaScript
import { modifier } from 'ember-modifier';
import { assert, warn } from '@ember/debug';
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
const getTextToCopy = text => {
let textToCopy = '';
if (typeof text === 'string') {
textToCopy = text;
} else if (
// context: https://github.com/hashicorp/design-system/pull/1564
typeof text === 'number' || typeof text === 'bigint') {
textToCopy = text.toString();
} else {
assert(`\`hds-clipboard\` modifier - \`text\` argument must be a string or number - provided: ${typeof text}`);
}
return textToCopy;
};
const getTargetElement = target => {
let targetElement;
if (typeof target === 'string') {
targetElement = document.querySelector(target);
if (!targetElement) {
console.error('`hds-clipboard` modifier - `target` selector provided does not point to an existing DOM node, check your selector string', targetElement);
return;
}
} else if (target instanceof HTMLElement) {
targetElement = target;
} else {
if (target instanceof NodeList) {
assert('`hds-clipboard` modifier - `target` argument must be a string or a DOM node - provided: a list of DOM nodes');
} else {
assert(`\`hds-clipboard\` modifier - \`target\` argument must be a string or a DOM node - provided: ${typeof target}`);
}
}
return targetElement;
};
const getTextToCopyFromTargetElement = targetElement => {
let textToCopy = '';
if (targetElement instanceof HTMLElement) {
if (targetElement instanceof HTMLInputElement ||
// targetElement.nodeName === 'INPUT' ||
targetElement instanceof HTMLTextAreaElement ||
// targetElement.nodeName === 'TEXTAREA' ||
targetElement instanceof HTMLSelectElement // targetElement.nodeName === 'SELECT'
) {
textToCopy = targetElement.value;
} else {
// Hide any screen reader only text from the innerText calculation
const srOnlyTexts = targetElement.querySelectorAll('.sr-only');
srOnlyTexts.forEach(el => {
el.setAttribute('style', 'display: none;');
});
// simplest approach
textToCopy = targetElement.innerText;
// Restore visibility of screen reader only text
srOnlyTexts.forEach(el => {
el.removeAttribute('style');
});
// approach based on text selection (left for backup just in case)
// var selection = window.getSelection();
// var range = document.createRange();
// selection.removeAllRanges();
// range.selectNodeContents(targetElement);
// selection.addRange(range);
// textToCopy = selection.toString();
// selection.removeAllRanges();
}
}
return textToCopy;
};
const writeTextToClipboard = async textToCopy => {
// finally copy the text to the clipboard using the Clipboard API
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API
try {
// notice: the "clipboard-write" permission is granted automatically to pages when they are in the active tab
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write
await navigator.clipboard.writeText(textToCopy);
// DEBUG uncomment this for easy debugging
// console.log('success', textToCopy);
return true;
} catch {
// if it is not a secure context, use the polyfill
// to test that this works in a non-secure context, access the port through your IP address (ie. XXX.XXX.X.XXX:4200/)
if (!navigator.clipboard) {
try {
const clipboard = await import('clipboard-polyfill');
await clipboard.writeText(textToCopy);
return true;
} catch (error) {
warn(`copy action failed, unable to use clipboard-polyfill: ${JSON.stringify(error)}`, {
id: 'hds-clipboard.write-text-to-clipboard.catch-error'
});
return false;
}
}
return false;
}
};
const copyToClipboard = async (text, target) => {
let textToCopy = '';
if (text !== undefined) {
textToCopy = getTextToCopy(text);
} else if (target) {
const targetElement = getTargetElement(target);
if (targetElement) {
textToCopy = getTextToCopyFromTargetElement(targetElement);
}
} else {
assert('`hds-clipboard` modifier - either a `text` or a `target` argument is required');
}
const success = await writeTextToClipboard(textToCopy);
return success;
};
// Notice: we use a function-based modifier here instead of a class-based one
// because it's quite simple in its logic, and doesn't require injecting services
// see: https://github.com/ember-modifier/ember-modifier#function-based-modifiers
var hdsClipboard = modifier((element, _positional, named) => {
assert('`hds-clipboard` modifier - the modifier must be applied to an element', element);
const {
text,
target,
onSuccess,
onError
} = named;
const onClick = async event => {
const trigger = event.currentTarget;
const success = await copyToClipboard(text, target);
// fire the `onSuccess/onError` callbacks (if provided)
if (success) {
if (typeof onSuccess === 'function') {
onSuccess({
trigger,
text,
target
});
}
} else {
if (typeof onError === 'function') {
onError({
trigger,
text,
target
});
}
}
};
// add the "onClick" event listener to the element
// eslint-disable-next-line @typescript-eslint/no-misused-promises
element.addEventListener('click', onClick);
// this (teardown) function is run when the element is removed
return () => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
element.removeEventListener('click', onClick);
};
});
export { copyToClipboard, hdsClipboard as default, getTargetElement, getTextToCopy, getTextToCopyFromTargetElement, writeTextToClipboard };
//# sourceMappingURL=hds-clipboard.js.map