mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-12-04 11:49:02 +08:00
start moving everything to components
This commit is contained in:
parent
287a2bc523
commit
d5e25de018
@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<editor class="editor" :extensions="extensions">
|
||||
|
||||
<div class="editor">
|
||||
<editor-content class="editor__content" :editor="editor" />
|
||||
</div>
|
||||
|
||||
<!-- <editor class="editor" :extensions="extensions">
|
||||
|
||||
<div class="menubar" slot="menubar" slot-scope="{ nodes, marks }">
|
||||
<div v-if="nodes && marks">
|
||||
@ -135,57 +140,71 @@
|
||||
</blockquote>
|
||||
</div>
|
||||
|
||||
</editor>
|
||||
</editor> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Icon from 'Components/Icon'
|
||||
import { Editor } from 'tiptap'
|
||||
import {
|
||||
BlockquoteNode,
|
||||
BulletListNode,
|
||||
CodeBlockNode,
|
||||
HardBreakNode,
|
||||
HeadingNode,
|
||||
ListItemNode,
|
||||
OrderedListNode,
|
||||
TodoItemNode,
|
||||
TodoListNode,
|
||||
BoldMark,
|
||||
CodeMark,
|
||||
ItalicMark,
|
||||
LinkMark,
|
||||
StrikeMark,
|
||||
UnderlineMark,
|
||||
HistoryExtension,
|
||||
} from 'tiptap-extensions'
|
||||
import { Editor, EditorContent } from 'tiptap'
|
||||
// import {
|
||||
// BlockquoteNode,
|
||||
// BulletListNode,
|
||||
// CodeBlockNode,
|
||||
// HardBreakNode,
|
||||
// HeadingNode,
|
||||
// ListItemNode,
|
||||
// OrderedListNode,
|
||||
// TodoItemNode,
|
||||
// TodoListNode,
|
||||
// BoldMark,
|
||||
// CodeMark,
|
||||
// ItalicMark,
|
||||
// LinkMark,
|
||||
// StrikeMark,
|
||||
// UnderlineMark,
|
||||
// HistoryExtension,
|
||||
// } from 'tiptap-extensions'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor,
|
||||
EditorContent,
|
||||
Icon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
extensions: [
|
||||
new BlockquoteNode(),
|
||||
new BulletListNode(),
|
||||
new CodeBlockNode(),
|
||||
new HardBreakNode(),
|
||||
new HeadingNode({ maxLevel: 3 }),
|
||||
new ListItemNode(),
|
||||
new OrderedListNode(),
|
||||
new TodoItemNode(),
|
||||
new TodoListNode(),
|
||||
new BoldMark(),
|
||||
new CodeMark(),
|
||||
new ItalicMark(),
|
||||
new LinkMark(),
|
||||
new StrikeMark(),
|
||||
new UnderlineMark(),
|
||||
new HistoryExtension(),
|
||||
],
|
||||
editor: new Editor({
|
||||
extensions: [
|
||||
// new BlockquoteNode(),
|
||||
// new BulletListNode(),
|
||||
// new CodeBlockNode(),
|
||||
// new HardBreakNode(),
|
||||
// new HeadingNode({ maxLevel: 3 }),
|
||||
// new ListItemNode(),
|
||||
// new OrderedListNode(),
|
||||
// new TodoItemNode(),
|
||||
// new TodoListNode(),
|
||||
// new BoldMark(),
|
||||
// new CodeMark(),
|
||||
// new ItalicMark(),
|
||||
// new LinkMark(),
|
||||
// new StrikeMark(),
|
||||
// new UnderlineMark(),
|
||||
// new HistoryExtension(),
|
||||
],
|
||||
content: {
|
||||
type: 'doc',
|
||||
content: [{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'This is some inserted text. 👋',
|
||||
},
|
||||
],
|
||||
}],
|
||||
},
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
5
packages/_legacy-tiptap/src/index.js
Normal file
5
packages/_legacy-tiptap/src/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
export { default as Editor } from './components/editor'
|
||||
export { default as Extension } from './utils/extension'
|
||||
export { default as Node } from './utils/node'
|
||||
export { default as Mark } from './utils/mark'
|
||||
export { default as Plugin } from './utils/plugin'
|
15
packages/_legacy-tiptap/src/nodes/Doc.js
Normal file
15
packages/_legacy-tiptap/src/nodes/Doc.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Node from '../utils/node'
|
||||
|
||||
export default class DocNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'doc'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'block+',
|
||||
}
|
||||
}
|
||||
|
||||
}
|
26
packages/_legacy-tiptap/src/nodes/Paragraph.js
Normal file
26
packages/_legacy-tiptap/src/nodes/Paragraph.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { setBlockType } from 'tiptap-commands'
|
||||
import Node from '../utils/node'
|
||||
|
||||
export default class ParagraphNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'paragraph'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
draggable: false,
|
||||
parseDOM: [{
|
||||
tag: 'p',
|
||||
}],
|
||||
toDOM: () => ['p', 0],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return setBlockType(type)
|
||||
}
|
||||
|
||||
}
|
15
packages/_legacy-tiptap/src/nodes/Text.js
Normal file
15
packages/_legacy-tiptap/src/nodes/Text.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Node from '../utils/node'
|
||||
|
||||
export default class TextNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'text'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'inline',
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
packages/_legacy-tiptap/src/nodes/index.js
Normal file
9
packages/_legacy-tiptap/src/nodes/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import Doc from './Doc'
|
||||
import Paragraph from './Paragraph'
|
||||
import Text from './Text'
|
||||
|
||||
export default [
|
||||
new Doc(),
|
||||
new Text(),
|
||||
new Paragraph(),
|
||||
]
|
110
packages/_legacy-tiptap/src/utils/ExtensionManager.js
Normal file
110
packages/_legacy-tiptap/src/utils/ExtensionManager.js
Normal file
@ -0,0 +1,110 @@
|
||||
import { keymap } from 'prosemirror-keymap'
|
||||
|
||||
export default class ExtensionManager {
|
||||
|
||||
constructor(extensions = []) {
|
||||
this.extensions = extensions
|
||||
}
|
||||
|
||||
get nodes() {
|
||||
return this.extensions
|
||||
.filter(extension => extension.type === 'node')
|
||||
.reduce((nodes, { name, schema }) => ({
|
||||
...nodes,
|
||||
[name]: schema,
|
||||
}), {})
|
||||
}
|
||||
|
||||
get marks() {
|
||||
return this.extensions
|
||||
.filter(extension => extension.type === 'mark')
|
||||
.reduce((marks, { name, schema }) => ({
|
||||
...marks,
|
||||
[name]: schema,
|
||||
}), {})
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return this.extensions
|
||||
.filter(extension => extension.plugins)
|
||||
.reduce((allPlugins, { plugins }) => ([
|
||||
...allPlugins,
|
||||
...plugins,
|
||||
]), [])
|
||||
}
|
||||
|
||||
get views() {
|
||||
return this.extensions
|
||||
.filter(extension => ['node', 'mark'].includes(extension.type))
|
||||
.filter(extension => extension.view)
|
||||
.reduce((views, { name, view }) => ({
|
||||
...views,
|
||||
[name]: view,
|
||||
}), {})
|
||||
}
|
||||
|
||||
keymaps({ schema }) {
|
||||
const extensionKeymaps = this.extensions
|
||||
.filter(extension => ['extension'].includes(extension.type))
|
||||
.filter(extension => extension.keys)
|
||||
.map(extension => extension.keys({ schema }))
|
||||
|
||||
const nodeMarkKeymaps = this.extensions
|
||||
.filter(extension => ['node', 'mark'].includes(extension.type))
|
||||
.filter(extension => extension.keys)
|
||||
.map(extension => extension.keys({
|
||||
type: schema[`${extension.type}s`][extension.name],
|
||||
schema,
|
||||
}))
|
||||
|
||||
return [
|
||||
...extensionKeymaps,
|
||||
...nodeMarkKeymaps,
|
||||
].map(keys => keymap(keys))
|
||||
}
|
||||
|
||||
inputRules({ schema }) {
|
||||
const extensionInputRules = this.extensions
|
||||
.filter(extension => ['extension'].includes(extension.type))
|
||||
.filter(extension => extension.inputRules)
|
||||
.map(extension => extension.inputRules({ schema }))
|
||||
|
||||
const nodeMarkInputRules = this.extensions
|
||||
.filter(extension => ['node', 'mark'].includes(extension.type))
|
||||
.filter(extension => extension.inputRules)
|
||||
.map(extension => extension.inputRules({
|
||||
type: schema[`${extension.type}s`][extension.name],
|
||||
schema,
|
||||
}))
|
||||
|
||||
return [
|
||||
...extensionInputRules,
|
||||
...nodeMarkInputRules,
|
||||
].reduce((allInputRules, inputRules) => ([
|
||||
...allInputRules,
|
||||
...inputRules,
|
||||
]), [])
|
||||
}
|
||||
|
||||
commands({ schema, view }) {
|
||||
return this.extensions
|
||||
.filter(extension => ['node', 'mark'].includes(extension.type))
|
||||
.filter(extension => extension.command)
|
||||
.reduce((commands, { name, type, command }) => ({
|
||||
...commands,
|
||||
[name]: attrs => {
|
||||
view.focus()
|
||||
|
||||
const provider = command({
|
||||
type: schema[`${type}s`][name],
|
||||
attrs,
|
||||
schema,
|
||||
})
|
||||
const callbacks = Array.isArray(provider) ? provider : [provider]
|
||||
|
||||
callbacks.forEach(callback => callback(view.state, view.dispatch, view))
|
||||
},
|
||||
}), {})
|
||||
}
|
||||
|
||||
}
|
34
packages/_legacy-tiptap/src/utils/extension.js
Normal file
34
packages/_legacy-tiptap/src/utils/extension.js
Normal file
@ -0,0 +1,34 @@
|
||||
export default class Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'extension'
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return []
|
||||
}
|
||||
|
||||
inputRules() {
|
||||
return []
|
||||
}
|
||||
|
||||
keys() {
|
||||
return {}
|
||||
}
|
||||
|
||||
}
|
7
packages/_legacy-tiptap/src/utils/index.js
Normal file
7
packages/_legacy-tiptap/src/utils/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
export { default as buildMenuActions } from './buildMenuActions'
|
||||
export { default as builtInKeymap } from './builtInKeymap'
|
||||
export { default as ComponentView } from './ComponentView'
|
||||
export { default as initNodeViews } from './initNodeViews'
|
||||
export { default as floatingMenu } from './floatingMenu'
|
||||
export { default as menuBubble } from './menuBubble'
|
||||
export { default as ExtensionManager } from './ExtensionManager'
|
25
packages/_legacy-tiptap/src/utils/mark.js
Normal file
25
packages/_legacy-tiptap/src/utils/mark.js
Normal file
@ -0,0 +1,25 @@
|
||||
import Extension from './extension'
|
||||
|
||||
export default class Mark extends Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'mark'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}
|
25
packages/_legacy-tiptap/src/utils/node.js
Normal file
25
packages/_legacy-tiptap/src/utils/node.js
Normal file
@ -0,0 +1,25 @@
|
||||
import Extension from './extension'
|
||||
|
||||
export default class Node extends Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'node'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}
|
3
packages/_legacy-tiptap/src/utils/plugin.js
Normal file
3
packages/_legacy-tiptap/src/utils/plugin.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
|
||||
export default Plugin
|
39
packages/tiptap/src/Components/EditorContent.js
Normal file
39
packages/tiptap/src/Components/EditorContent.js
Normal file
@ -0,0 +1,39 @@
|
||||
export default {
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
render(createElement) {
|
||||
// console.log('createElement', this.editor)
|
||||
// if (this.editor) {
|
||||
// console.log('create element', this.editor.element)
|
||||
// return this.editor.element
|
||||
// // return createElement('div', {}, this.editor.element.innerHTML)
|
||||
// }
|
||||
return createElement('div')
|
||||
},
|
||||
watch: {
|
||||
'editor.element': {
|
||||
immediate: true,
|
||||
handler(element) {
|
||||
if (element) {
|
||||
this.$nextTick(() => {
|
||||
this.$el.append(element)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// console.log('on init')
|
||||
// this.editor.on('init', () => {
|
||||
// console.log('iniiiitttt!!!')
|
||||
// })
|
||||
// setTimeout(() => {
|
||||
// // this.$el.innerHTML = this.editor.element.innerHTML
|
||||
// this.$el.append(this.editor.element)
|
||||
// }, 1000);
|
||||
},
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
export { default as Editor } from './components/editor'
|
||||
export { default as Extension } from './utils/extension'
|
||||
export { default as Node } from './utils/node'
|
||||
export { default as Mark } from './utils/mark'
|
||||
export { default as Plugin } from './utils/plugin'
|
||||
export { default as Editor } from './Utils/Editor'
|
||||
export { default as EditorContent } from './Components/EditorContent'
|
||||
|
||||
export { default as Extension } from './Utils/Extension'
|
||||
export { default as Node } from './Utils/Node'
|
||||
export { default as Mark } from './Utils/Mark'
|
||||
export { default as Plugin } from './Utils/Plugin'
|
@ -1,4 +1,4 @@
|
||||
import Node from '../utils/node'
|
||||
import Node from '../Utils/Node'
|
||||
|
||||
export default class DocNode extends Node {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { setBlockType } from 'tiptap-commands'
|
||||
import Node from '../utils/node'
|
||||
import Node from '../Utils/Node'
|
||||
|
||||
export default class ParagraphNode extends Node {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Node from '../utils/node'
|
||||
import Node from '../Utils/Node'
|
||||
|
||||
export default class TextNode extends Node {
|
||||
|
||||
|
134
packages/tiptap/src/utils/Editor.js
Normal file
134
packages/tiptap/src/utils/Editor.js
Normal file
@ -0,0 +1,134 @@
|
||||
import Vue from 'vue'
|
||||
import { EditorState, Plugin } from 'prosemirror-state'
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import { Schema, DOMParser, DOMSerializer } from 'prosemirror-model'
|
||||
import { gapCursor } from 'prosemirror-gapcursor'
|
||||
import { keymap } from 'prosemirror-keymap'
|
||||
import { baseKeymap } from 'prosemirror-commands'
|
||||
import { inputRules } from 'prosemirror-inputrules'
|
||||
|
||||
import {
|
||||
// buildMenuActions,
|
||||
ExtensionManager,
|
||||
// initNodeViews,
|
||||
// menuBubble,
|
||||
// floatingMenu,
|
||||
// builtInKeymap,
|
||||
} from '../Utils'
|
||||
|
||||
import builtInNodes from '../Nodes'
|
||||
|
||||
export default class Editor {
|
||||
|
||||
constructor(options = {}) {
|
||||
console.log('construct editor')
|
||||
|
||||
this.element = document.createElement('div')
|
||||
this.bus = new Vue()
|
||||
|
||||
this.options = options
|
||||
this.extensions = this.createExtensions()
|
||||
this.nodes = this.createNodes()
|
||||
this.marks = this.createMarks()
|
||||
this.schema = this.createSchema()
|
||||
this.state = this.createState()
|
||||
this.view = this.createView()
|
||||
|
||||
console.log('emit init')
|
||||
this.emit('init')
|
||||
|
||||
console.log(this.view)
|
||||
}
|
||||
|
||||
createExtensions() {
|
||||
return new ExtensionManager([
|
||||
...builtInNodes,
|
||||
...this.options.extensions,
|
||||
])
|
||||
}
|
||||
|
||||
createNodes() {
|
||||
const { nodes } = this.extensions
|
||||
return nodes
|
||||
}
|
||||
|
||||
createMarks() {
|
||||
return []
|
||||
}
|
||||
|
||||
createSchema() {
|
||||
return new Schema({
|
||||
nodes: this.nodes,
|
||||
marks: this.marks,
|
||||
})
|
||||
}
|
||||
|
||||
createState() {
|
||||
return EditorState.create({
|
||||
schema: this.schema,
|
||||
doc: this.createDocument(this.options.content),
|
||||
})
|
||||
}
|
||||
|
||||
createDocument(content) {
|
||||
if (typeof content === 'object') {
|
||||
return this.schema.nodeFromJSON(content)
|
||||
}
|
||||
|
||||
// return DOMParser.fromSchema(this.schema).parse(this.contentNode.elm)
|
||||
}
|
||||
|
||||
createView() {
|
||||
this.element.style.whiteSpace = 'pre-wrap'
|
||||
|
||||
return new EditorView(this.element, {
|
||||
state: this.state,
|
||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||||
// nodeViews: initNodeViews({
|
||||
// nodes: this.views,
|
||||
// editable: this.editable,
|
||||
// }),
|
||||
})
|
||||
}
|
||||
|
||||
dispatchTransaction(transaction) {
|
||||
this.state = this.state.apply(transaction)
|
||||
this.view.updateState(this.state)
|
||||
// this.updateMenuActions()
|
||||
|
||||
if (!transaction.docChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
this.emitUpdate()
|
||||
}
|
||||
|
||||
emitUpdate() {
|
||||
console.log(this.getHTML())
|
||||
// this.$emit('update', {
|
||||
// getHTML: this.getHTML,
|
||||
// getJSON: this.getJSON,
|
||||
// state: this.state,
|
||||
// })
|
||||
}
|
||||
|
||||
getHTML() {
|
||||
const div = document.createElement('div')
|
||||
const fragment = DOMSerializer
|
||||
.fromSchema(this.schema)
|
||||
.serializeFragment(this.state.doc.content)
|
||||
|
||||
div.appendChild(fragment)
|
||||
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
emit(event, ...data) {
|
||||
this.bus.$emit(event, ...data)
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
this.bus.$on(event, callback)
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
export { default as buildMenuActions } from './buildMenuActions'
|
||||
export { default as builtInKeymap } from './builtInKeymap'
|
||||
export { default as ComponentView } from './ComponentView'
|
||||
export { default as initNodeViews } from './initNodeViews'
|
||||
export { default as floatingMenu } from './floatingMenu'
|
||||
export { default as menuBubble } from './menuBubble'
|
||||
// export { default as buildMenuActions } from './buildMenuActions'
|
||||
// export { default as builtInKeymap } from './builtInKeymap'
|
||||
// export { default as ComponentView } from './ComponentView'
|
||||
// export { default as initNodeViews } from './initNodeViews'
|
||||
// export { default as floatingMenu } from './floatingMenu'
|
||||
// export { default as menuBubble } from './menuBubble'
|
||||
export { default as ExtensionManager } from './ExtensionManager'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Extension from './extension'
|
||||
import Extension from './Extension'
|
||||
|
||||
export default class Mark extends Extension {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Extension from './extension'
|
||||
import Extension from './Extension'
|
||||
|
||||
export default class Node extends Extension {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user