Use clippie module to copy to clipboard (#23801)
Externalize clipboard copying to the [clippie](https://github.com/silverwind/clippie) module which I feel I can maintain outside this repo for shared benefit with my other projects. The module is feature-equivalent to the previous code and has one improvement where it sets `aria-hidden` on the fallback textarea, preventing screen readers from picking it up. Also it support `Array` of `content` as well to copy multiple items at once, in case it's ever needed.
This commit is contained in:
parent
fcb9ef8788
commit
f5593d08dc
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -19,6 +19,7 @@
|
||||||
"add-asset-webpack-plugin": "2.0.1",
|
"add-asset-webpack-plugin": "2.0.1",
|
||||||
"ansi-to-html": "0.7.2",
|
"ansi-to-html": "0.7.2",
|
||||||
"asciinema-player": "3.2.0",
|
"asciinema-player": "3.2.0",
|
||||||
|
"clippie": "3.1.4",
|
||||||
"css-loader": "6.7.3",
|
"css-loader": "6.7.3",
|
||||||
"dropzone": "6.0.0-beta.2",
|
"dropzone": "6.0.0-beta.2",
|
||||||
"easymde": "2.18.0",
|
"easymde": "2.18.0",
|
||||||
|
@ -2762,6 +2763,11 @@
|
||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clippie": {
|
||||||
|
"version": "3.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/clippie/-/clippie-3.1.4.tgz",
|
||||||
|
"integrity": "sha512-jrW6sG1zcTEQr5MtCXJzszNmHWV9Fkaco8sAqFeuOApNFP/lRFcUi4JABMmxBJwFZLIvbw2BY3G5E+BjBqZMdQ=="
|
||||||
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"add-asset-webpack-plugin": "2.0.1",
|
"add-asset-webpack-plugin": "2.0.1",
|
||||||
"ansi-to-html": "0.7.2",
|
"ansi-to-html": "0.7.2",
|
||||||
"asciinema-player": "3.2.0",
|
"asciinema-player": "3.2.0",
|
||||||
|
"clippie": "3.1.4",
|
||||||
"css-loader": "6.7.3",
|
"css-loader": "6.7.3",
|
||||||
"dropzone": "6.0.0-beta.2",
|
"dropzone": "6.0.0-beta.2",
|
||||||
"easymde": "2.18.0",
|
"easymde": "2.18.0",
|
||||||
|
|
|
@ -1,48 +1,9 @@
|
||||||
import {showTemporaryTooltip} from '../modules/tippy.js';
|
import {showTemporaryTooltip} from '../modules/tippy.js';
|
||||||
import {toAbsoluteUrl} from '../utils.js';
|
import {toAbsoluteUrl} from '../utils.js';
|
||||||
|
import {clippie} from 'clippie';
|
||||||
|
|
||||||
const {copy_success, copy_error} = window.config.i18n;
|
const {copy_success, copy_error} = window.config.i18n;
|
||||||
|
|
||||||
export async function copyToClipboard(content) {
|
|
||||||
if (content instanceof Blob) {
|
|
||||||
const item = new ClipboardItem({[content.type]: content});
|
|
||||||
await navigator.clipboard.write([item]);
|
|
||||||
} else { // text
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(content);
|
|
||||||
} catch {
|
|
||||||
return fallbackCopyToClipboard(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to use if navigator.clipboard doesn't exist. Achieved via creating
|
|
||||||
// a temporary textarea element, selecting the text, and using document.execCommand
|
|
||||||
function fallbackCopyToClipboard(text) {
|
|
||||||
if (!document.execCommand) return false;
|
|
||||||
|
|
||||||
const tempTextArea = document.createElement('textarea');
|
|
||||||
tempTextArea.value = text;
|
|
||||||
|
|
||||||
// avoid scrolling
|
|
||||||
tempTextArea.style.top = 0;
|
|
||||||
tempTextArea.style.left = 0;
|
|
||||||
tempTextArea.style.position = 'fixed';
|
|
||||||
|
|
||||||
document.body.appendChild(tempTextArea);
|
|
||||||
|
|
||||||
tempTextArea.select();
|
|
||||||
|
|
||||||
// if unsecure (not https), there is no navigator.clipboard, but we can still
|
|
||||||
// use document.execCommand to copy to clipboard
|
|
||||||
const success = document.execCommand('copy');
|
|
||||||
|
|
||||||
document.body.removeChild(tempTextArea);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For all DOM elements with [data-clipboard-target] or [data-clipboard-text],
|
// For all DOM elements with [data-clipboard-target] or [data-clipboard-text],
|
||||||
// this copy-to-clipboard will work for them
|
// this copy-to-clipboard will work for them
|
||||||
export function initGlobalCopyToClipboardListener() {
|
export function initGlobalCopyToClipboardListener() {
|
||||||
|
@ -61,7 +22,7 @@ export function initGlobalCopyToClipboardListener() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
(async() => {
|
(async() => {
|
||||||
const success = await copyToClipboard(text);
|
const success = await clippie(text);
|
||||||
showTemporaryTooltip(target, success ? copy_success : copy_error);
|
showTemporaryTooltip(target, success ? copy_success : copy_error);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import {copyToClipboard} from './clipboard.js';
|
import {clippie} from 'clippie';
|
||||||
import {showTemporaryTooltip} from '../modules/tippy.js';
|
import {showTemporaryTooltip} from '../modules/tippy.js';
|
||||||
import {convertImage} from '../utils.js';
|
import {convertImage} from '../utils.js';
|
||||||
|
|
||||||
const {i18n} = window.config;
|
const {i18n} = window.config;
|
||||||
|
|
||||||
async function doCopy(content, btn) {
|
async function doCopy(content, btn) {
|
||||||
const success = await copyToClipboard(content);
|
const success = await clippie(content);
|
||||||
showTemporaryTooltip(btn, success ? i18n.copy_success : i18n.copy_error);
|
showTemporaryTooltip(btn, success ? i18n.copy_success : i18n.copy_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import $ from 'jquery';
|
||||||
import {svg} from '../svg.js';
|
import {svg} from '../svg.js';
|
||||||
import {invertFileFolding} from './file-fold.js';
|
import {invertFileFolding} from './file-fold.js';
|
||||||
import {createTippy} from '../modules/tippy.js';
|
import {createTippy} from '../modules/tippy.js';
|
||||||
import {copyToClipboard} from './clipboard.js';
|
import {clippie} from 'clippie';
|
||||||
import {toAbsoluteUrl} from '../utils.js';
|
import {toAbsoluteUrl} from '../utils.js';
|
||||||
|
|
||||||
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
|
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
|
||||||
|
@ -190,7 +190,7 @@ export function initRepoCodeView() {
|
||||||
currentTarget.closest('tr').outerHTML = blob;
|
currentTarget.closest('tr').outerHTML = blob;
|
||||||
});
|
});
|
||||||
$(document).on('click', '.copy-line-permalink', async (e) => {
|
$(document).on('click', '.copy-line-permalink', async (e) => {
|
||||||
const success = await copyToClipboard(toAbsoluteUrl(e.currentTarget.getAttribute('data-url')));
|
const success = await clippie(toAbsoluteUrl(e.currentTarget.getAttribute('data-url')));
|
||||||
if (!success) return;
|
if (!success) return;
|
||||||
document.querySelector('.code-line-button')?._tippy?.hide();
|
document.querySelector('.code-line-button')?._tippy?.hide();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue