前言
最近在折腾bytemd,想要给viewer的code标签加个复制功能,本着摸着石头过河的打算,于是乎…
先看图
正文
准备工作
# 安装bytemd和react包
pnpm add bytemd @bytemd/react -S
# 代码高亮
pnpm add highlight.js -S
本文主要讲 Viewer 部分,关于Editor部分请参照官方文档
Easy to extend: ByteMD has a plugin system to extend the basic Markdown syntax, which makes it easy to add additional features such as code syntax highlight, math equation and Mermaid flowcharts. You can also write your own plugin if these ones don’t meet your needs.
根据官方文档所述,我们来写自己的插件,按照文档一步一步实现
动手
首先创建文件byteCode.tsx
import type { BytemdPlugin } from 'bytemd'
export default function byteCode(): BytemdPlugin {
return {
viewerEffect({ markdownBody }) {}
}
}
获取到markdownBody
中的code
标签
const codes = markdownBody.querySelectorAll('code')
最终要实现的框架如下
<div>
<div class="header">
<div class="header-left">
.....
</div>
<div class="header-right">
.....
</div>
</div>
<div class="body">
<code>.....</code>
</div>
</div>
创建一个最外层div
,将框架内容放入到其中
const fatherBox = document.createElement('div')
fatherBox.innerHTML =
'<div class="code-header"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><div class="code-action"><span class="code-btn-copy" id="code-btn-copy-' +
key +
'">Copy</span><span class="code-lang">' +
lan +
'</span></div></div><div class="code-block"><code id="code-content-' +
key +
'" class="code-content- ' +
code.className +
'">' +
code.innerHTML +
'</code></div>'
替换掉原本的code内容,这里分了两步
# 获取code的父级元素,删除它的子元素(code)
const parentNode: any = code.parentNode
parentNode.removeChild(code)
# 将fatherBox加入到父节点
parentNode.appendChild(fatherBox)
到这里整个code块就完成了,接下来绑定点击事件,实现复制功能
let btn: any = document.getElementById('code-btn-copy-' + key)
btn.addEventListener('click', () => {
copyToClipboard(code.innerText)
btn.innerHTML = 'Copied'
})
copyToClipboard
的内容如下,这个方法来源一行代码就能完成的事情,为什么要写两行
export const copyToClipboard = (text: any) => navigator.clipboard.writeText(text)
代码高亮
document.querySelectorAll('code.code-content-').forEach((el: any) => {
hljs.highlightElement(el)
})
使用
import { Viewer } from '@bytemd/react'
import code from '@/utils/code'
import 'highlight.js/styles/github-dark.css'
const plugins = [code()]
const Index = () => {
const value = '......'
return (
<Viewer value={value} plugins={plugins} />
)
}
到这里就大功告成了。
完整代码
byteCode.tsx
import type { BytemdPlugin } from 'bytemd'
import { copyToClipboard } from '@/utils/common'
import hljs from 'highlight.js'
export default function code(): BytemdPlugin {
return {
viewerEffect({ markdownBody }): void | (() => void) {
const codes = markdownBody.querySelectorAll('code')
codes.forEach((code, key) => {
let className: string = code.className
let lan = className.replace('language-', '').toUpperCase()
const parentNode: any = code.parentNode
parentNode.removeChild(code)
const fatherBox = document.createElement('div')
fatherBox.innerHTML =
'<div class="code-header"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><div class="code-action"><span class="code-btn-copy" id="code-btn-copy-' +
key +
'">Copy</span><span class="code-lang">' +
lan +
'</span></div></div><div class="code-block"><code id="code-content-' +
key +
'" class="code-content- ' +
code.className +
'">' +
code.innerHTML +
'</code></div>'
parentNode.appendChild(fatherBox)
let btn: any = document.getElementById('code-btn-copy-' + key)
btn.addEventListener('click', () => {
copyToClipboard(code.innerText)
btn.innerHTML = 'Copied'
})
document.querySelectorAll('code.code-content-').forEach((el: any) => {
hljs.highlightElement(el)
})
})
},
}
}
css
.code-header{
display: flex;
justify-content: space-between;
align-items: center;
background-color:#f3f4f5;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
padding: 10px;
}
.code-action{
display: flex;
align-items: center;
}
.code-btn-copy{
background-color:#4caf50;
border: none;
border-radius: 5px;
color: #fff;
cursor: pointer;
font-size: 14px;
margin-right:10px;
padding: 0 10px;
}
.code-btn-copy:hover{
background-color:#3e8e41;
}
.code-lang{
color:#7f8c8d;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
}
.code-block {
background-color: #272822;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
padding: 10px;
}
.code-block pre {
margin: 0;
}
.code-block code {
color: #F8F8F2 !important;
font-size: 1.2rem;
font-family: 'Courier New', Courier, monospace !important;
}
结语
本人后端打工仔一枚,对前端比较感兴趣,如有不正确的地方,请各位大佬多多指点?
最后,祝大家心想事成,平安喜乐。
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END