mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-02-22 08:02:25 +01:00
* add basePath prop to Range inside slate-react custom types, calculate absolute ranges on passing them into TextComponent * code highlighting example improvements, minor markdown preview refactoring * changeset added * Revert "add basePath prop to Range inside slate-react custom types, calculate absolute ranges on passing them into TextComponent" This reverts commit afa085c289bc67ce3d27dd33b1f074ab8153efe8. * add basePath prop to Point inside slate-react custom types, resolve relative ranges on passing them to TextComponent * Update changeset * linter fixes * remove redundant checks inside renderElement function * custom types fixes for Range and Point in examples * wrap intervals and ranges extractors in useMemo hook for running them only if editor.children is changed * revert basePath changes, compare only offsets for MemoizedText decorations * use an element as a key in decorations ranges map instead of id * simplify code highlighting implementation, make code block nested * fix code-highlighting example, add toolbar code block button * remove redundant code * fix code highlighting playwright integration test
130 lines
3.2 KiB
TypeScript
130 lines
3.2 KiB
TypeScript
import Prism from 'prismjs'
|
|
import 'prismjs/components/prism-markdown'
|
|
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'
|
|
|
|
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
|