UNPKG

@hashicorp/design-system-components

Version:
171 lines (159 loc) 5.92 kB
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