forked from opentiny/tiny-vue
feat(rich-text-editor): support code highlight (#440)
* feat(rich-text-editor): support code highlight * feat(rich-text-editor): support code highlight * style(rich-text-editor): code style adjust * style(rich-text-editor): code style adjust --------- Co-authored-by: 常浩-BJS21325 <changhao01@youdao>
This commit is contained in:
parent
b2cd6ab80f
commit
a592dd9f67
|
@ -0,0 +1,41 @@
|
|||
export default function (NodeViewContent, nodeViewProps, NodeViewWrapper) {
|
||||
return {
|
||||
name: 'CodeHighlight',
|
||||
components: {
|
||||
NodeViewWrapper,
|
||||
NodeViewContent,
|
||||
},
|
||||
props: nodeViewProps,
|
||||
data() {
|
||||
return {
|
||||
languages: this.extension.options.lowlight.listLanguages(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedLanguage: {
|
||||
get() {
|
||||
return this.node.attrs.language
|
||||
},
|
||||
set(language) {
|
||||
this.updateAttributes({ language })
|
||||
},
|
||||
},
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<NodeViewWrapper class="code-block">
|
||||
<select contenteditable="false" v-model={this.selectedLanguage}>
|
||||
<option value="null">
|
||||
auto
|
||||
</option>
|
||||
<option disabled>
|
||||
—
|
||||
</option>
|
||||
{this.languages.map((item, index) => <option value={item} key={index}> {item} </option>)}
|
||||
</select>
|
||||
<pre><code><NodeViewContent /></code></pre>
|
||||
</NodeViewWrapper>
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
import { handleChange, setLink, handleMove, handleClickOutside, removeClickOutside, handleClick, shouldShow } from './index'
|
||||
import Codehighlight from './code-highlight'
|
||||
export const api = ['state', 'setLink', 'handleChange', 'box', 'handleMove', 'handleClickOutside', 'removeClickOutside', 'handleClick', 'shouldShow']
|
||||
export const renderless = (
|
||||
props,
|
||||
{ computed, onMounted, onBeforeUnmount, reactive, ref },
|
||||
{ vm, emit, parent },
|
||||
{ useEditor, Collaboration, Y, WebrtcProvider, StarterKit, Table, TableCell, TableHeader, TableRow, Color, TextStyle, Image, Highlight, Link, Underline, Subscript, Superscript, TaskItem, TaskList, TextAlign, Paragraph, mergeAttributes }
|
||||
{ useEditor, Collaboration, Y, WebrtcProvider, StarterKit, Table, TableCell, TableHeader, TableRow, Color, TextStyle, Image, Highlight, Link, Underline, Subscript, Superscript, TaskItem, TaskList, TextAlign, Paragraph, mergeAttributes, CodeBlockLowlight, lowlight, VueNodeViewRenderer, NodeViewContent, nodeViewProps, NodeViewWrapper }
|
||||
) => {
|
||||
const ydoc = new Y.Doc()
|
||||
const provider = new WebrtcProvider('tiny-examsple-document', ydoc)
|
||||
|
@ -72,11 +73,18 @@ export const renderless = (
|
|||
types: ['heading', 'paragraph'],
|
||||
}),
|
||||
CustomParagraph,
|
||||
CodeBlockLowlight.extend({
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(Codehighlight(NodeViewContent, nodeViewProps, NodeViewWrapper))
|
||||
},
|
||||
})
|
||||
.configure({ lowlight }),
|
||||
],
|
||||
content: 'Example Tesxt',
|
||||
autofocus: true,
|
||||
editable: true,
|
||||
injectCSS: false,
|
||||
|
||||
})
|
||||
|
||||
const box = ref(null)
|
||||
|
|
|
@ -214,12 +214,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.tiny-rich-text-editor {
|
||||
.bubble-menu {
|
||||
|
||||
button {
|
||||
background-color: #f1f3f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 富文本编辑器的主题区域,需要对高度进行调整,达到融为一体
|
||||
.small-box {
|
||||
|
@ -239,6 +241,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.tiny-rich-text-editor {
|
||||
.ProseMirror {
|
||||
outline: none !important;
|
||||
}
|
||||
|
@ -400,3 +403,90 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror {
|
||||
>*+* {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
position: relative;
|
||||
|
||||
select {
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: 0.5rem;
|
||||
border: 0;
|
||||
background: gray;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #0d0d0d;
|
||||
color: #fff;
|
||||
font-family: "JetBrainsMono", monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
code {
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
background: none;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-regexp,
|
||||
.hljs-link,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #f98181;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-meta,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params {
|
||||
color: #fbbc88;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #b9f18d;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #faf594;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #70cff8;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^2.1.6",
|
||||
"@tiptap/extension-collaboration": "^2.0.4",
|
||||
"@tiptap/extension-color": "^2.0.4",
|
||||
"@tiptap/extension-highlight": "^2.0.3",
|
||||
|
@ -25,12 +26,16 @@
|
|||
"@tiptap/extension-task-item": "^2.0.4",
|
||||
"@tiptap/extension-task-list": "^2.0.4",
|
||||
"@tiptap/extension-text-align": "^2.0.4",
|
||||
"@tiptap/extension-paragraph": "^2.1.6",
|
||||
"@tiptap/pm": "^2.0.4",
|
||||
"@tiptap/starter-kit": "^2.0.4",
|
||||
"@tiptap/vue-3": "^2.0.4",
|
||||
"y-prosemirror": "^1.2.1",
|
||||
"y-webrtc": "^10.2.5",
|
||||
"yjs": "^13.6.7",
|
||||
"@tiptap/extension-code-block-lowlight": "^2.0.4",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lowlight": "^2.9.0",
|
||||
"@opentiny/vue-common": "workspace:~",
|
||||
"@opentiny/vue-renderless": "workspace:~",
|
||||
"@opentiny/vue-theme": "workspace:~"
|
||||
|
|
|
@ -314,7 +314,15 @@ import {
|
|||
iconRichTextUnderline,
|
||||
iconRichTextUndo
|
||||
} from '@opentiny/vue-icon'
|
||||
import { useEditor, EditorContent, BubbleMenu, VueNodeViewRenderer } from '@tiptap/vue-3'
|
||||
import {
|
||||
useEditor,
|
||||
EditorContent,
|
||||
BubbleMenu,
|
||||
VueNodeViewRenderer,
|
||||
NodeViewContent,
|
||||
nodeViewProps,
|
||||
NodeViewWrapper
|
||||
} from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
// 段落包
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
|
@ -343,6 +351,17 @@ import TaskItem from '@tiptap/extension-task-item'
|
|||
import TaskList from '@tiptap/extension-task-list'
|
||||
// textalign
|
||||
import TextAlign from '@tiptap/extension-text-align'
|
||||
// code high light
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
||||
import css from 'highlight.js/lib/languages/css'
|
||||
import js from 'highlight.js/lib/languages/javascript'
|
||||
import ts from 'highlight.js/lib/languages/typescript'
|
||||
import html from 'highlight.js/lib/languages/xml'
|
||||
import { lowlight } from 'lowlight'
|
||||
lowlight.registerLanguage('html', html)
|
||||
lowlight.registerLanguage('css', css)
|
||||
lowlight.registerLanguage('js', js)
|
||||
lowlight.registerLanguage('ts', ts)
|
||||
// collaboration 包
|
||||
import Collaboration from '@tiptap/extension-collaboration'
|
||||
import * as Y from 'yjs'
|
||||
|
@ -432,7 +451,13 @@ export default defineComponent({
|
|||
TaskList,
|
||||
TextAlign,
|
||||
Paragraph,
|
||||
mergeAttributes
|
||||
mergeAttributes,
|
||||
CodeBlockLowlight,
|
||||
lowlight,
|
||||
VueNodeViewRenderer,
|
||||
NodeViewContent,
|
||||
nodeViewProps,
|
||||
NodeViewWrapper
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue