使用 mantine UI 的 CopyButton 组件注入复制按键到文章代码块中
在 anflext press 应用中使用了 tiptap 组件来编辑文章。之后发布 html 内容,代码块(html 的 pre 标记)会正常突出显示出来。
有时可能需要通过复制来提取代码使用。这时一个最简单的办法就是通过 Ctrl+C 快捷键或鼠标右键菜单来操作,不及友好。正常的网站会提供一个复制按键在代码块上,我也想实现这个实用功能。
谷歌了一下,我发现有一篇讨论这个问题的文章值得参考学习: Inject Copy Button into Code Block Using MutationObserver in a React Application 。研读了代码,学习了一下,我就用 mantine UI 的 CopyButton 组件来执行这个操作。代码类似于:
import { ActionIcon, CopyButton, MantineProvider, Tooltip, Typography } from "@mantine/core";
import { IconCheck, IconCopy } from '@tabler/icons-react';
import theme from './theme.js';
// Use the below article code and replace with CopyButton
// https://www.alexandru-barbulescu.com/posts/inject-copy-button-into-code-block-using-mutationobserver-in-a-react-application
const CopyButtonsInjector = () => {
useEffect(() => {
const pres = document.querySelectorAll('pre');
pres.forEach(pre => {
if (pre.querySelector('.copy-btn-wrapper')) return;
if (!pre.textContent?.trim()) return;
const wrapper = document.createElement('div');
wrapper.className = 'copy-btn-wrapper';
wrapper.style.position = 'absolute';
wrapper.style.top = '0.1rem';
wrapper.style.right = '0.5rem';
wrapper.style.zIndex = '10';
pre.style.position = 'relative';
pre.appendChild(wrapper);
const root = createRoot(wrapper);
root.render(
<MantineProvider theme={theme}>
<CopyButton value={pre.textContent}>
{({ copied, copy }) => (
<Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="right">
<ActionIcon color={copied ? 'teal' : 'gray'} variant="subtle" onClick={copy}>
{copied ? <IconCheck size={16} /> : <IconCopy size={16} />}
</ActionIcon>
</Tooltip>
)}
</CopyButton></MantineProvider>
);
});
});
return null;
};
export default function Article() {
// some other code
return (
<>
<CopyButtonsInjector />
<Typography dangerouslySetInnerHTML={{ __html: '<p>test only</p>' }} />
</>
);
}注意: 那个 theme 组件必须要导入进来放到 MantineProvider 的 theme 属性中。我用的布局组件中使用了 MantineProvider, 如果不这么使用的话就会报运行时错误,添加的复制按钮就显示不出来了。
但功能实现后,却发现在 dark 模式下 code 标记的背景与父 pre 标记的背景不一样,看起来不那么美观,就像这样:

我只好去调试检查了一下默认的 @mantine/core/styles.css 文件,然后手动将这两个标记的颜色设置成一样的:
// @mantine/core/styles.css:7678
// pills variant
:where([data-mantine-color-scheme='dark']) .m_d08caa0 :where(pre) {
background-color: var(--mantine-color-dark-8);
}
// Changed to
:where([data-mantine-color-scheme='dark']) .m_d08caa0 :where(pre) {
background-color: var(--mantine-color-dark-5);
}最终结果看着就好看多了:

21