add basic implementation of hightlight.js
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"url": "https://github.com/heyscrumpy/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"lowlight": "^1.10.0",
|
||||
"prosemirror-history": "^1.0.2",
|
||||
"tiptap": "^0.8.0",
|
||||
"tiptap-commands": "^0.2.4"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { default as BlockquoteNode } from './nodes/Blockquote'
|
||||
export { default as BulletListNode } from './nodes/BulletList'
|
||||
export { default as CodeBlockNode } from './nodes/CodeBlock'
|
||||
export { default as CodeBlockHighlightNode } from './nodes/CodeBlockHighlight'
|
||||
export { default as HardBreakNode } from './nodes/HardBreak'
|
||||
export { default as HeadingNode } from './nodes/Heading'
|
||||
export { default as ImageNode } from './nodes/Image'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Node } from 'tiptap'
|
||||
import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands'
|
||||
import { Node, Plugin } from 'tiptap'
|
||||
import { wrappingInputRule, wrapIn } from 'tiptap-commands'
|
||||
|
||||
export default class BlockquoteNode extends Node {
|
||||
|
||||
|
||||
109
packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js
Normal file
109
packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Node, Plugin } from 'tiptap'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands'
|
||||
import { findBlockNodes } from 'prosemirror-utils'
|
||||
import low from 'lowlight'
|
||||
|
||||
export default class CodeBlockHighlightNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'code_block'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
group: 'block',
|
||||
code: true,
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
{ tag: 'pre', preserveWhitespace: 'full' },
|
||||
],
|
||||
toDOM: () => ['pre', ['code', 0]],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, schema }) {
|
||||
return toggleBlockType(type, schema.nodes.paragraph)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Shift-Ctrl-\\': setBlockType(type),
|
||||
}
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
textblockTypeInputRule(/^```$/, type),
|
||||
]
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
props: {
|
||||
decorations({ doc }) {
|
||||
const decorations = []
|
||||
|
||||
const blocks = findBlockNodes(doc)
|
||||
.filter(item => item.node.type.name === 'code_block')
|
||||
|
||||
const flatten = list => list.reduce(
|
||||
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
|
||||
)
|
||||
|
||||
function parseNodes(nodes, className = []) {
|
||||
return nodes.map(node => {
|
||||
|
||||
const classes = [
|
||||
...className,
|
||||
...node.properties ? node.properties.className : [],
|
||||
]
|
||||
|
||||
if (node.children) {
|
||||
return parseNodes(node.children, classes)
|
||||
}
|
||||
|
||||
return {
|
||||
text: node.value,
|
||||
classes,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
blocks.forEach(block => {
|
||||
let startPos = block.pos + 1
|
||||
const nodes = low.highlightAuto(block.node.textContent).value
|
||||
|
||||
flatten(parseNodes(nodes))
|
||||
.map(node => {
|
||||
const from = startPos
|
||||
const to = from + node.text.length
|
||||
|
||||
startPos = to
|
||||
|
||||
return {
|
||||
...node,
|
||||
from,
|
||||
to,
|
||||
}
|
||||
})
|
||||
.forEach(node => {
|
||||
const decoration = Decoration.inline(node.from, node.to, {
|
||||
class: node.classes.join(' '),
|
||||
})
|
||||
decorations.push(decoration)
|
||||
})
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user