@vuepress/plugin-copy-code
Version:
Copy Code plugin for vuepress
78 lines (77 loc) • 3.46 kB
JavaScript
import { useLocaleConfig } from '@vuepress/helper/client';
import { useClipboard, useEventListener } from '@vueuse/core';
import { nextTick, onMounted, watch } from 'vue';
import { usePageData } from 'vuepress/client';
const timeoutIdMap = new Map();
const MOBILE_USERAGENT_REGEXP = /\b(?:Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini)/i;
const isMobile = () => typeof window !== 'undefined' &&
window.navigator &&
'userAgent' in window.navigator &&
MOBILE_USERAGENT_REGEXP.test(navigator.userAgent);
export const setupCopyCode = () => {
const { copy } = useClipboard({ legacy: true });
const locale = useLocaleConfig(__COPY_CODE_LOCALES__);
const page = usePageData();
const insertCopyButton = (codeBlockElement) => {
if (!codeBlockElement.hasAttribute('copy-code-registered')) {
const copyElement = document.createElement('button');
copyElement.type = 'button';
copyElement.classList.add('copy-code-button');
copyElement.innerHTML = '<div class="copy-icon" />';
copyElement.setAttribute('aria-label', locale.value.copy);
copyElement.setAttribute('data-copied', locale.value.copied);
if (codeBlockElement.parentElement)
codeBlockElement.parentElement.insertBefore(copyElement, codeBlockElement);
codeBlockElement.setAttribute('copy-code-registered', '');
}
};
const appendCopyButton = () => {
nextTick().then(() => setTimeout(() => {
__COPY_CODE_SELECTOR__.forEach((item) => {
document.querySelectorAll(item).forEach(insertCopyButton);
});
}, __COPY_CODE_DELAY__));
};
const copyContent = (codeContainer, codeContent, button) => {
let { innerText: text = '' } = codeContent;
if (
// is shell
/language-(shellscript|shell|bash|sh|zsh)/.test(codeContainer.classList.toString()))
text = text.replace(/^ *(\$|>) /gm, '');
copy(text).then(() => {
button.classList.add('copied');
clearTimeout(timeoutIdMap.get(button));
const timeoutId = setTimeout(() => {
button.classList.remove('copied');
button.blur();
timeoutIdMap.delete(button);
}, __COPY_CODE_DURATION__);
timeoutIdMap.set(button, timeoutId);
});
};
onMounted(() => {
const enabled = !isMobile() || __COPY_CODE_SHOW_IN_MOBILE__;
if (enabled)
appendCopyButton();
useEventListener('click', (event) => {
const el = event.target;
if (el.matches('div[class*="language-"] > button.copy')) {
const codeContainer = el.parentElement;
const preBlock = el.nextElementSibling;
if (preBlock)
copyContent(codeContainer, preBlock, el);
}
else if (el.matches('div[class*="language-"] div.copy-icon')) {
const buttonElement = el.parentElement;
const codeContainer = buttonElement.parentElement;
const preBlock = buttonElement.nextElementSibling;
if (preBlock)
copyContent(codeContainer, preBlock, buttonElement);
}
});
watch(() => page.value.path, () => {
if (enabled)
appendCopyButton();
});
});
};