首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用slate.js实现动态标记显示

使用slate.js实现动态标记显示
EN

Stack Overflow用户
提问于 2022-06-04 13:48:44
回答 1查看 550关注 0票数 5

我正在使用slate.js编写一个标记文本编辑器。我正在尝试实现以下实时呈现效果(来自泰波拉):

如你所见,

  1. 当我打字时,文本会自动变成粗体。
  2. 当我按空格键时,四个星号消失了,只有文本本身是可见的。
  3. 当我将光标聚焦回文本时,星号会再次出现(这样我就可以修改它们)。

由于MarkdownPreview的示例,我已经实现了第一项,下面是它的代码(取自板岩储存库):

代码语言:javascript
复制
import Prism from 'prismjs'
import React, { useCallback, useMemo } from 'react'
import { Slate, Editable, withReact } from 'slate-react'
import { Text, createEditor, Descendant } from 'slate'
import { withHistory } from 'slate-history'
import { css } from '@emotion/css'

// eslint-disable-next-line
;Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore

const MarkdownPreviewExample = () => {
  const renderLeaf = useCallback(props => <Leaf {...props} />, [])
  const editor = useMemo(() => withHistory(withReact(createEditor())), [])
  const decorate = useCallback(([node, path]) => {
    const ranges = []

    if (!Text.isText(node)) {
      return ranges
    }

    const getLength = token => {
      if (typeof token === 'string') {
        return token.length
      } else if (typeof token.content === 'string') {
        return token.content.length
      } else {
        return token.content.reduce((l, t) => l + getLength(t), 0)
      }
    }

    const tokens = Prism.tokenize(node.text, Prism.languages.markdown)
    let start = 0

    for (const token of tokens) {
      const length = getLength(token)
      const end = start + length

      if (typeof token !== 'string') {
        ranges.push({
          [token.type]: true,
          anchor: { path, offset: start },
          focus: { path, offset: end },
        })
      }

      start = end
    }

    return ranges
  }, [])

  return (
    <Slate editor={editor} value={initialValue}>
      <Editable
        decorate={decorate}
        renderLeaf={renderLeaf}
        placeholder="Write some markdown..."
      />
    </Slate>
  )
}

const Leaf = ({ attributes, children, leaf }) => {
  return (
    <span
      {...attributes}
      className={css`
        font-weight: ${leaf.bold && 'bold'};
        font-style: ${leaf.italic && 'italic'};
        text-decoration: ${leaf.underlined && 'underline'};
        ${leaf.title &&
          css`
            display: inline-block;
            font-weight: bold;
            font-size: 20px;
            margin: 20px 0 10px 0;
          `}
        ${leaf.list &&
          css`
            padding-left: 10px;
            font-size: 20px;
            line-height: 10px;
          `}
        ${leaf.hr &&
          css`
            display: block;
            text-align: center;
            border-bottom: 2px solid #ddd;
          `}
        ${leaf.blockquote &&
          css`
            display: inline-block;
            border-left: 2px solid #ddd;
            padding-left: 10px;
            color: #aaa;
            font-style: italic;
          `}
        ${leaf.code &&
          css`
            font-family: monospace;
            background-color: #eee;
            padding: 3px;
          `}
      `}
    >
      {children}
    </span>
  )
}

const initialValue: Descendant[] = [
  {
    type: 'paragraph',
    children: [
      {
        text:
          'Slate is flexible enough to add **decorations** that can format text based on its content. For example, this editor has **Markdown** preview decorations on it, to make it _dead_ simple to make an editor with built-in Markdown previewing.',
      },
    ],
  },
  {
    type: 'paragraph',
    children: [{ text: '## Try it out!' }],
  },
  {
    type: 'paragraph',
    children: [{ text: 'Try it out for yourself!' }],
  },
]

export default MarkdownPreviewExample

我的问题是,如何落实第二及第三项呢?我已经考虑了很长时间,但没有找到任何好的方法来实现它们。

EN

回答 1

Stack Overflow用户

发布于 2022-09-03 08:41:21

好消息:

  • 原始文本保存完好(在应用程序中的某个地方)
  • Slate.js为每个样式呈现一个新元素。感谢您发布的实时演示链接,很明显DOM正在保存您的标记的state (这意味着如果需要的话,我们可以在板岩上添加操作)。

坏消息:

  • 实现编辑器是一个令人头疼的问题。使用现有的系统并不总是像我们预期的那样直观和容易。这就是为什么我如此喜欢摩纳哥编辑项目的原因(一个免费的可编辑编辑器给我免费的VS code体验?是的,请!)

我们能做什么?

简易方式:

在我们的项目中使用摩纳哥编辑器。根据我的经验,它在编码和格式化技术文本方面更好(也许这就是VS code背后的引擎)。

不那么容易的方式:

我们可以做一个更简单的实现--一个带有原始文本的面板和另一个预览面板(顺便说一句,这正是在VS code中编辑标记文件时发生的事情)。这样,我们就可以始终呈现出反映我们最近变化的观点。首先,您可以使用这个项目来完成它,并在其中添加一些调整,以满足您的要求。

的艰难之路:

使用slate.js commands (一般概念阅读这位医生 +有用的示例)。这种方法可以让slate处理负载,但也需要您深入研究该项目。请注意,您可以注册自定义命令和查询,以满足您的需要,而无需中断工作管道(请阅读)。

疯狂的方式

尝试通过在呈现的元素之上使用自定义事件来覆盖板岩。这可能是棘手的,但如果你喜欢玩项目内部和注入价值的动态。不过,不太推荐

我的建议

创建一个将应用所需markdown style的自定义命令(例如:粗体)

使用板岩处理程序跟踪space键命中并使用您的命令

代码语言:javascript
复制
function onKeyDown(event, editor, next) {
  if (event.key == 'Enter') {
    // TODO: add markdown style 
    editor.applyMarkdownBold() // applyMarkdownBold is a made-up name for your custom command
  } else {
    return next()
  }
}

我知道-这个示例将对所有文本应用粗体样式,但是-如果将它与range selection结合起来,您将得到编辑器中相关区域所需的样式(同样- 文档用于拯救)

从这一点开始--通过按下键或通过单击添加标记字符(使用事件+查询+命令)应用特定的选择只是时间问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72500275

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档