给掘金代码块换个“皮肤”

前言

最近在折腾bytemd,想要给viewer的code标签加个复制功能,本着摸着石头过河的打算,于是乎…
先看图

image.png

正文

准备工作

# 安装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;
}

结语

本人后端打工仔一枚,对前端比较感兴趣,如有不正确的地方,请各位大佬多多指点?

最后,祝大家心想事成,平安喜乐。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYjxtEVZ' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片