实现一个掘金markdown编辑器(bytemd)的主题切换功能

465 阅读1分钟

废话

距离上一次写文章已经过了大半年了,期间经历过想润回老家,刚好七月底公司裁员,休息了一个月,然后找到了一家上海的公司,空闲时候又把之前的博客网站拿过来折腾了一下。

上效果

动画.gif

思路

思路很简单,通过点击menu向编辑区添加主题文字,然后在预览区将主题文字相关的dom设置display:none不显示,同时动态添加link标签引入样式

贴代码自取

//这个是我自己cdn存的主题列表
import { themeList as themeMap } from './utils';
/**
 *
 * @param {*} options
 * @param {Array} options.themeList 主题列表 {title:'主题名称', href: '样式链接'}
 * @param {Function} options.onThemeChange 主题回调 参数为当前选中的主题样式css链接
 * @returns
 */
function themePlugin(options = {}) {
  if (options.onThemeChange && typeof options.onThemeChange !== 'function') {
    console.error('onThemeChange 必须为函数');
  }
  return {
    viewerEffect({ markdownBody }) {
      const themeHTag = markdownBody.getElementsByTagName('h2')[0];
      if (themeHTag && /theme|highlight/.test(themeHTag.innerText)) {
        for (let i = 0; i < markdownBody.childNodes.length; i++) {
          markdownBody.childNodes[i] && markdownBody.childNodes[i].style && (markdownBody.childNodes[i].style.display = 'none');
          if (markdownBody.childNodes[i] === themeHTag) {
            break;
          }
        }
      }
    },
    actions: [
      {
        title: 'Markdown 主题',
        icon: `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M6 2H2.66667C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667H6C6.36819 6.66667 6.66667 6.36819 6.66667 6V2.66667C6.66667 2.29848 6.36819 2 6 2Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path>
        <path d="M6 9.3335H2.66667C2.29848 9.3335 2 9.63197 2 10.0002V13.3335C2 13.7017 2.29848 14.0002 2.66667 14.0002H6C6.36819 14.0002 6.66667 13.7017 6.66667 13.3335V10.0002C6.66667 9.63197 6.36819 9.3335 6 9.3335Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path>
        <path d="M13.3334 2H10C9.63185 2 9.33337 2.29848 9.33337 2.66667V6C9.33337 6.36819 9.63185 6.66667 10 6.66667H13.3334C13.7016 6.66667 14 6.36819 14 6V2.66667C14 2.29848 13.7016 2 13.3334 2Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path>
        <path d="M13.3334 9.3335H10C9.63185 9.3335 9.33337 9.63197 9.33337 10.0002V13.3335C9.33337 13.7017 9.63185 14.0002 10 14.0002H13.3334C13.7016 14.0002 14 13.7017 14 13.3335V10.0002C14 9.63197 13.7016 9.3335 13.3334 9.3335Z" stroke="#1D2129" stroke-width="1.33" stroke-linejoin="round"></path>
        </svg>`,
        handler: {
          type: 'dropdown',
          actions: (options.themeList || themeMap).map((item) => ({
            title: item.title,
            cheatsheet: `---
            theme: ${item.title}
            ---`,
            handler: {
              type: 'action',
              click({ editor }) {
                const headerPart = /^-{3}[A-Za-z0-9_:\s-]*-{3}\n*/.exec(editor.getValue());
                if (headerPart) {
                  editor.setValue(editor.getValue().replace(headerPart[0], headerPart[0].replace(/theme:\s[\w]*\n/, `theme: ${item.title}\n`)));
                } else {
                  editor.setValue(`---\ntheme: ${item.title}\n---\n\n${editor.getValue()}`);
                }
                loadCSS(item.href);
                options.onThemeChange && options.onThemeChange(item.href);
              }
            }
          }))
        }
      }
    ]
  };
}

export default themePlugin;

function loadCSS(href) {
  let link = document.getElementById('markdown-style');
  if (!link) {
    link = document.createElement('link');
    link.setAttribute('id', 'markdown-style');
    link.rel = 'stylesheet';
  }

  link.href = href;
  document.head.appendChild(link);
}