fix(theme): keep the copy button out of code text selection

close #5120
pull/5464/head
ChisakaKanako 1 week ago
parent 19145a2fc7
commit 1fac4f18e1

@ -96,9 +96,9 @@ describe('node/markdown/plugins/containers', () => {
<p>content</p>
</div>
<details class="details custom-block"><summary>Click me to toggle the code</summary>
<div class="language-js" dir="ltr"><button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span><pre><code class="language-js">console.log('hi')
<div class="language-js" dir="ltr"><pre><code class="language-js">console.log('hi')
</code></pre>
</div></details>
<button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span></div></details>
"
`)
})
@ -335,11 +335,11 @@ describe('node/markdown/plugins/containers', () => {
].join('\n')
expect(await render(src)).toMatchInlineSnapshot(`
"<div class="vp-code-group"><div class="tabs"><input type="radio" name="group-0" id="tab-1" checked><label data-title="config.js" for="tab-1">config.js</label><input type="radio" name="group-0" id="tab-2" ><label data-title="config.ts" for="tab-2">config.ts</label></div><div class="blocks">
<div class="language-js active" dir="ltr"><button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span><pre><code class="language-js">const a = 1
<div class="language-js active" dir="ltr"><pre><code class="language-js">const a = 1
</code></pre>
</div><div class="language-ts" dir="ltr"><button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">ts</span><pre><code class="language-ts">const a: number = 1
<button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span></div><div class="language-ts" dir="ltr"><pre><code class="language-ts">const a: number = 1
</code></pre>
</div></div></div>
<button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">ts</span></div></div></div>
"
`)
})
@ -497,9 +497,9 @@ describe('node/markdown/plugins/containers (github alerts)', () => {
<ul>
<li>list item</li>
</ul>
<div class="language-js" dir="ltr"><button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span><pre><code class="language-js">const a = 1
<div class="language-js" dir="ltr"><pre><code class="language-js">const a = 1
</code></pre>
</div></div>
<button title="Copy code" data-copied="Copied" class="copy"></button><span class="lang">js</span></div></div>
"
`)
})

@ -7,11 +7,33 @@ const ignoredNodes = ['.vp-copy-ignore', '.diff.remove'].join(', ')
export function useCopyCode() {
if (inBrowser) {
const timeoutIdMap: WeakMap<HTMLElement, number> = new WeakMap()
let selectingBlock: HTMLElement | null = null
const blockOf = (node: Node | null) =>
node?.parentElement?.closest<HTMLElement>('div[class*="language-"]') ??
null
document.addEventListener('selectionchange', () => {
const selection = document.getSelection()
const block =
selection && !selection.isCollapsed
? blockOf(selection.anchorNode) || blockOf(selection.focusNode)
: null
if (selectingBlock && selectingBlock !== block) {
selectingBlock.classList.remove('selecting')
}
if (block) {
block.classList.add('selecting')
}
selectingBlock = block
})
window.addEventListener('click', (e) => {
const el = e.target as HTMLElement
if (el.matches('div[class*="language-"] > button.copy')) {
const parent = el.parentElement
const sibling = el.nextElementSibling?.nextElementSibling // <pre> tag
const sibling = parent?.querySelector('pre')
if (!parent || !sibling) {
return
}

@ -506,13 +506,16 @@
top: 0.75rem;
inset-inline-end: 0.75rem;
z-index: 3;
pointer-events: none;
border: 1px solid var(--vp-code-copy-code-border-color);
border-radius: 0.25rem;
width: 2.5rem;
height: 2.5rem;
background-color: var(--vp-code-copy-code-bg);
opacity: 0;
visibility: hidden;
cursor: pointer;
user-select: none;
background-image: var(--vp-icon-copy);
background-position: 50%;
background-size: 1.25rem;
@ -520,12 +523,36 @@
transition:
border-color 0.25s,
background-color 0.25s,
opacity 0.25s;
opacity 0.25s,
visibility 0s 0.25s;
}
.vp-doc [class*='language-']:hover > button.copy,
.vp-doc [class*='language-']:focus-within > button.copy,
.vp-doc [class*='language-'] > button.copy:focus {
opacity: 1;
visibility: visible;
pointer-events: auto;
transition:
border-color 0.25s,
background-color 0.25s,
opacity 0.25s,
visibility 0s;
}
.vp-doc [class*='language-'].selecting > button.copy:not(:focus),
.vp-doc [class*='language-'].selecting > span.lang {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.vp-doc [class*='language-'].selecting > button.copy:not(:focus) {
transition:
border-color 0.25s,
background-color 0.25s,
opacity 0.25s,
visibility 0s 0.25s;
}
.vp-doc [class*='language-'] > button.copy:hover,

@ -43,9 +43,9 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
return (
`<div class="language-${lang}${active}" dir="ltr">` +
fence(...args) +
`<button title="${tooltipText}" data-copied="${copiedText}" class="copy"></button>` +
`<span class="lang">${label}</span>` +
fence(...args) +
'</div>'
)
}

Loading…
Cancel
Save