UNPKG

@vuepress/plugin-copy-code

Version:

Copy Code plugin for vuepress

78 lines (77 loc) 3.46 kB
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(); }); }); };