test/node_modules/@vuepress/plugin-copy-code/lib/client/composables/useCopyCode.js
2024-08-13 09:27:52 +08:00

81 lines
3.3 KiB
JavaScript

import { useLocaleConfig, wait } from '@vuepress/helper/client';
import { useClipboard, useEventListener, useMediaQuery } from '@vueuse/core';
import { computed, nextTick, watch } from 'vue';
import { usePageData } from 'vuepress/client';
import '../styles/copy-code.css';
const SHELL_RE = /language-(shellscript|shell|bash|sh|zsh)/;
export const useCopyCode = ({ delay = 500, duration = 2000, locales, selector, showInMobile, ignoreSelector = [], transform, }) => {
if (__VUEPRESS_SSR__)
return;
/**
* On small-screen devices, the copy button is not displayed by default in order to prevent
* it from obstructing content, as the `:hover` effect can be triggered by `touch` events.
*/
const is419 = useMediaQuery('(max-width: 419px)');
const enabled = computed(() => !is419.value || showInMobile);
const locale = useLocaleConfig(locales);
const page = usePageData();
const insertCopyButton = (codeBlockElement) => {
if (codeBlockElement.hasAttribute('copy-code-registered'))
return;
const copyElement = document.createElement('button');
copyElement.type = 'button';
copyElement.classList.add('vp-copy-code-button');
copyElement.setAttribute('aria-label', locale.value.copy);
copyElement.setAttribute('data-copied', locale.value.copied);
codeBlockElement.parentElement?.insertBefore(copyElement, codeBlockElement);
codeBlockElement.setAttribute('copy-code-registered', '');
};
const appendCopyButton = async () => {
document.body.classList.toggle('copy-code-disabled', !enabled.value);
if (!enabled.value)
return;
await nextTick();
await wait(delay);
document
.querySelectorAll(selector.join(','))
.forEach(insertCopyButton);
};
watch(() => [page.value.path, enabled.value], appendCopyButton, {
immediate: true,
});
const { copy } = useClipboard({ legacy: true });
const timeoutIdMap = new WeakMap();
const copyContent = (codeContainer, codeContent, button) => {
const clone = codeContent.cloneNode(true);
if (ignoreSelector.length) {
clone
.querySelectorAll(ignoreSelector.join(','))
.forEach((node) => node.remove());
}
if (transform)
transform(clone);
let text = clone.textContent || '';
if (SHELL_RE.test(codeContainer.className))
text = text.replace(/^ *(\$|>) /gm, '');
copy(text).then(() => {
if (duration <= 0)
return;
button.classList.add('copied');
clearTimeout(timeoutIdMap.get(button));
const timeoutId = setTimeout(() => {
button.classList.remove('copied');
button.blur();
timeoutIdMap.delete(button);
}, duration);
timeoutIdMap.set(button, timeoutId);
});
};
useEventListener('click', (event) => {
const el = event.target;
if (enabled.value &&
el.matches('div[class*="language-"] > button.vp-copy-code-button')) {
const codeContainer = el.parentElement;
const preBlock = el.nextElementSibling;
if (!codeContainer || !preBlock)
return;
copyContent(codeContainer, preBlock, el);
}
});
};