remove legacy code
This commit is contained in:
@@ -1,330 +0,0 @@
|
|||||||
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 {
|
|
||||||
|
|
||||||
props: {
|
|
||||||
doc: {
|
|
||||||
type: Object,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
extensions: {
|
|
||||||
type: Array,
|
|
||||||
required: false,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
editable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
watchDoc: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
const allExtensions = new ExtensionManager([
|
|
||||||
...builtInNodes,
|
|
||||||
...this.extensions,
|
|
||||||
])
|
|
||||||
const { nodes, marks, views } = allExtensions
|
|
||||||
|
|
||||||
return {
|
|
||||||
state: null,
|
|
||||||
view: null,
|
|
||||||
plugins: [],
|
|
||||||
allExtensions,
|
|
||||||
schema: null,
|
|
||||||
nodes,
|
|
||||||
marks,
|
|
||||||
views,
|
|
||||||
keymaps: [],
|
|
||||||
commands: {},
|
|
||||||
menuActions: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
|
|
||||||
doc: {
|
|
||||||
deep: true,
|
|
||||||
handler() {
|
|
||||||
if (this.watchDoc) {
|
|
||||||
this.setContent(this.doc, true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
render(createElement) {
|
|
||||||
const slots = []
|
|
||||||
|
|
||||||
Object
|
|
||||||
.entries(this.$scopedSlots)
|
|
||||||
.forEach(([name, slot]) => {
|
|
||||||
if (name === 'content') {
|
|
||||||
this.contentNode = slot({})
|
|
||||||
slots.push(this.contentNode)
|
|
||||||
} else if (name === 'menubar') {
|
|
||||||
this.menubarNode = slot({
|
|
||||||
nodes: this.menuActions ? this.menuActions.nodes : null,
|
|
||||||
marks: this.menuActions ? this.menuActions.marks : null,
|
|
||||||
focused: this.view ? this.view.focused : false,
|
|
||||||
focus: this.focus,
|
|
||||||
})
|
|
||||||
slots.push(this.menubarNode)
|
|
||||||
} else if (name === 'menububble') {
|
|
||||||
this.menububbleNode = slot({
|
|
||||||
nodes: this.menuActions ? this.menuActions.nodes : null,
|
|
||||||
marks: this.menuActions ? this.menuActions.marks : null,
|
|
||||||
focused: this.view ? this.view.focused : false,
|
|
||||||
focus: this.focus,
|
|
||||||
})
|
|
||||||
slots.push(this.menububbleNode)
|
|
||||||
} else if (name === 'floatingMenu') {
|
|
||||||
this.floatingMenuNode = slot({
|
|
||||||
nodes: this.menuActions ? this.menuActions.nodes : null,
|
|
||||||
marks: this.menuActions ? this.menuActions.marks : null,
|
|
||||||
focused: this.view ? this.view.focused : false,
|
|
||||||
focus: this.focus,
|
|
||||||
})
|
|
||||||
slots.push(this.floatingMenuNode)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return createElement('div', {
|
|
||||||
class: 'vue-editor',
|
|
||||||
}, slots)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
initEditor() {
|
|
||||||
this.schema = this.createSchema()
|
|
||||||
this.plugins = this.createPlugins()
|
|
||||||
this.keymaps = this.createKeymaps()
|
|
||||||
this.inputRules = this.createInputRules()
|
|
||||||
this.state = this.createState()
|
|
||||||
this.clearSlot()
|
|
||||||
this.view = this.createView()
|
|
||||||
this.commands = this.createCommands()
|
|
||||||
this.updateMenuActions()
|
|
||||||
this.$emit('init', {
|
|
||||||
view: this.view,
|
|
||||||
state: this.state,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createSchema() {
|
|
||||||
return new Schema({
|
|
||||||
nodes: this.nodes,
|
|
||||||
marks: this.marks,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createPlugins() {
|
|
||||||
return this.allExtensions.plugins
|
|
||||||
},
|
|
||||||
|
|
||||||
createKeymaps() {
|
|
||||||
return this.allExtensions.keymaps({
|
|
||||||
schema: this.schema,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createInputRules() {
|
|
||||||
return this.allExtensions.inputRules({
|
|
||||||
schema: this.schema,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createCommands() {
|
|
||||||
return this.allExtensions.commands({
|
|
||||||
schema: this.schema,
|
|
||||||
view: this.view,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createState() {
|
|
||||||
return EditorState.create({
|
|
||||||
schema: this.schema,
|
|
||||||
doc: this.getDocument(),
|
|
||||||
plugins: [
|
|
||||||
...this.plugins,
|
|
||||||
...this.getPlugins(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getDocument() {
|
|
||||||
if (this.doc) {
|
|
||||||
return this.schema.nodeFromJSON(this.doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return DOMParser.fromSchema(this.schema).parse(this.contentNode.elm)
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSlot() {
|
|
||||||
this.contentNode.elm.innerHTML = ''
|
|
||||||
},
|
|
||||||
|
|
||||||
getPlugins() {
|
|
||||||
const plugins = [
|
|
||||||
inputRules({
|
|
||||||
rules: this.inputRules,
|
|
||||||
}),
|
|
||||||
...this.keymaps,
|
|
||||||
keymap(builtInKeymap),
|
|
||||||
keymap(baseKeymap),
|
|
||||||
gapCursor(),
|
|
||||||
new Plugin({
|
|
||||||
props: {
|
|
||||||
editable: () => this.editable,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
if (this.menububbleNode) {
|
|
||||||
plugins.push(menuBubble(this.menububbleNode))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.floatingMenuNode) {
|
|
||||||
plugins.push(floatingMenu(this.floatingMenuNode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugins
|
|
||||||
},
|
|
||||||
|
|
||||||
createView() {
|
|
||||||
this.contentNode.elm.style.whiteSpace = 'pre-wrap'
|
|
||||||
|
|
||||||
return new EditorView(this.contentNode.elm, {
|
|
||||||
state: this.state,
|
|
||||||
dispatchTransaction: this.dispatchTransaction,
|
|
||||||
nodeViews: initNodeViews({
|
|
||||||
parent: this,
|
|
||||||
nodes: this.views,
|
|
||||||
editable: this.editable,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyEditor() {
|
|
||||||
if (this.view) {
|
|
||||||
this.view.destroy()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMenuActions() {
|
|
||||||
this.menuActions = buildMenuActions({
|
|
||||||
schema: this.schema,
|
|
||||||
state: this.view.state,
|
|
||||||
commands: this.commands,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchTransaction(transaction) {
|
|
||||||
this.state = this.state.apply(transaction)
|
|
||||||
this.view.updateState(this.state)
|
|
||||||
this.updateMenuActions()
|
|
||||||
|
|
||||||
if (!transaction.docChanged) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emitUpdate()
|
|
||||||
},
|
|
||||||
|
|
||||||
getHTML() {
|
|
||||||
const div = document.createElement('div')
|
|
||||||
const fragment = DOMSerializer
|
|
||||||
.fromSchema(this.schema)
|
|
||||||
.serializeFragment(this.state.doc.content)
|
|
||||||
|
|
||||||
div.appendChild(fragment)
|
|
||||||
|
|
||||||
return div.innerHTML
|
|
||||||
},
|
|
||||||
|
|
||||||
getJSON() {
|
|
||||||
return this.state.doc.toJSON()
|
|
||||||
},
|
|
||||||
|
|
||||||
emitUpdate() {
|
|
||||||
this.$emit('update', {
|
|
||||||
getHTML: this.getHTML,
|
|
||||||
getJSON: this.getJSON,
|
|
||||||
state: this.state,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getDocFromContent(content) {
|
|
||||||
if (typeof content === 'object') {
|
|
||||||
return this.schema.nodeFromJSON(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof content === 'string') {
|
|
||||||
const element = document.createElement('div')
|
|
||||||
element.innerHTML = content.trim()
|
|
||||||
|
|
||||||
return DOMParser.fromSchema(this.schema).parse(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
setContent(content = {}, emitUpdate = false) {
|
|
||||||
this.state = EditorState.create({
|
|
||||||
schema: this.state.schema,
|
|
||||||
doc: this.getDocFromContent(content),
|
|
||||||
plugins: this.state.plugins,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.view.updateState(this.state)
|
|
||||||
|
|
||||||
if (emitUpdate) {
|
|
||||||
this.emitUpdate()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clearContent(emitUpdate = false) {
|
|
||||||
this.setContent({
|
|
||||||
type: 'doc',
|
|
||||||
content: [{
|
|
||||||
type: 'paragraph',
|
|
||||||
}],
|
|
||||||
}, emitUpdate)
|
|
||||||
},
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.view.focus()
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.initEditor()
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.destroyEditor()
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
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'
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import Node from '../utils/node'
|
|
||||||
|
|
||||||
export default class DocNode extends Node {
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return 'doc'
|
|
||||||
}
|
|
||||||
|
|
||||||
get schema() {
|
|
||||||
return {
|
|
||||||
content: 'block+',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import Node from '../utils/node'
|
|
||||||
|
|
||||||
export default class TextNode extends Node {
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
get schema() {
|
|
||||||
return {
|
|
||||||
group: 'inline',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import Doc from './Doc'
|
|
||||||
import Paragraph from './Paragraph'
|
|
||||||
import Text from './Text'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
new Doc(),
|
|
||||||
new Text(),
|
|
||||||
new Paragraph(),
|
|
||||||
]
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
export default class ComponentView {
|
|
||||||
constructor(component, {
|
|
||||||
node,
|
|
||||||
view,
|
|
||||||
getPos,
|
|
||||||
decorations,
|
|
||||||
editable,
|
|
||||||
}) {
|
|
||||||
this.component = component
|
|
||||||
this.node = node
|
|
||||||
this.view = view
|
|
||||||
this.getPos = getPos
|
|
||||||
this.decorations = decorations
|
|
||||||
this.editable = editable
|
|
||||||
|
|
||||||
this.dom = this.createDOM()
|
|
||||||
this.contentDOM = this.vm.$refs.content
|
|
||||||
}
|
|
||||||
|
|
||||||
createDOM() {
|
|
||||||
const Component = Vue.extend(this.component)
|
|
||||||
this.vm = new Component({
|
|
||||||
propsData: {
|
|
||||||
node: this.node,
|
|
||||||
view: this.view,
|
|
||||||
getPos: this.getPos,
|
|
||||||
decorations: this.decorations,
|
|
||||||
editable: this.editable,
|
|
||||||
updateAttrs: attrs => this.updateAttrs(attrs),
|
|
||||||
updateContent: content => this.updateContent(content),
|
|
||||||
},
|
|
||||||
}).$mount()
|
|
||||||
return this.vm.$el
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAttrs(attrs) {
|
|
||||||
if (!this.editable) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), null, {
|
|
||||||
...this.node.attrs,
|
|
||||||
...attrs,
|
|
||||||
})
|
|
||||||
this.view.dispatch(transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateContent(content) {
|
|
||||||
if (!this.editable) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), this.node.type, { content })
|
|
||||||
this.view.dispatch(transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
ignoreMutation() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
stopEvent(event) {
|
|
||||||
// TODO: find a way to pass full extensions to ComponentView
|
|
||||||
// so we could check for schema.draggable
|
|
||||||
// for now we're allowing all drag events for node views
|
|
||||||
return !/drag/.test(event.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
update(node, decorations) {
|
|
||||||
if (node.type !== this.node.type) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === this.node && this.decorations === decorations) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
this.node = node
|
|
||||||
this.decorations = decorations
|
|
||||||
this.vm._props.node = node
|
|
||||||
this.vm._props.decorations = decorations
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.vm.$destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
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))
|
|
||||||
},
|
|
||||||
}), {})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { markIsActive, nodeIsActive, getMarkAttrs } from 'tiptap-utils'
|
|
||||||
|
|
||||||
export default function ({ schema, state, commands }) {
|
|
||||||
|
|
||||||
const nodes = Object.entries(schema.nodes)
|
|
||||||
.map(([name]) => {
|
|
||||||
const active = (attrs = {}) => nodeIsActive(state, schema.nodes[name], attrs)
|
|
||||||
const command = commands[name] ? commands[name] : () => {}
|
|
||||||
return { name, active, command }
|
|
||||||
})
|
|
||||||
.reduce((actions, { name, active, command }) => ({
|
|
||||||
...actions,
|
|
||||||
[name]: {
|
|
||||||
active,
|
|
||||||
command,
|
|
||||||
},
|
|
||||||
}), {})
|
|
||||||
|
|
||||||
const marks = Object.entries(schema.marks)
|
|
||||||
.map(([name]) => {
|
|
||||||
const active = () => markIsActive(state, schema.marks[name])
|
|
||||||
const attrs = getMarkAttrs(state, schema.marks[name])
|
|
||||||
const command = commands[name] ? commands[name] : () => {}
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
active,
|
|
||||||
attrs,
|
|
||||||
command,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.reduce((actions, {
|
|
||||||
name, active, attrs, command,
|
|
||||||
}) => ({
|
|
||||||
...actions,
|
|
||||||
[name]: {
|
|
||||||
active,
|
|
||||||
attrs,
|
|
||||||
command,
|
|
||||||
},
|
|
||||||
}), {})
|
|
||||||
|
|
||||||
return {
|
|
||||||
nodes,
|
|
||||||
marks,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { lift, selectParentNode } from 'prosemirror-commands'
|
|
||||||
import { undoInputRule } from 'prosemirror-inputrules'
|
|
||||||
|
|
||||||
const keymap = {
|
|
||||||
'Mod-BracketLeft': lift,
|
|
||||||
Backspace: undoInputRule,
|
|
||||||
Escape: selectParentNode,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default keymap
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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 {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { Plugin } from 'prosemirror-state'
|
|
||||||
|
|
||||||
class Toolbar {
|
|
||||||
|
|
||||||
constructor({ node, editorView }) {
|
|
||||||
this.editorView = editorView
|
|
||||||
this.node = node
|
|
||||||
this.element = this.node.elm
|
|
||||||
this.element.style.visibility = 'hidden'
|
|
||||||
this.element.style.opacity = 0
|
|
||||||
|
|
||||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
update(view, lastState) {
|
|
||||||
const { state } = view
|
|
||||||
|
|
||||||
// Don't do anything if the document/selection didn't change
|
|
||||||
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.selection.empty) {
|
|
||||||
this.hide()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentDom = view.domAtPos(state.selection.$anchor.pos)
|
|
||||||
const isActive = currentDom.node.innerHTML === '<br>'
|
|
||||||
&& currentDom.node.tagName === 'P'
|
|
||||||
&& currentDom.node.parentNode === view.dom
|
|
||||||
|
|
||||||
if (!isActive) {
|
|
||||||
this.hide()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const editorBoundings = this.element.offsetParent.getBoundingClientRect()
|
|
||||||
const cursorBoundings = view.coordsAtPos(state.selection.$anchor.pos)
|
|
||||||
const top = cursorBoundings.top - editorBoundings.top
|
|
||||||
|
|
||||||
this.element.style.top = `${top}px`
|
|
||||||
this.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.element.style.visibility = 'visible'
|
|
||||||
this.element.style.opacity = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
hide(event) {
|
|
||||||
if (event && event.relatedTarget) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.style.visibility = 'hidden'
|
|
||||||
this.element.style.opacity = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (node) {
|
|
||||||
return new Plugin({
|
|
||||||
view(editorView) {
|
|
||||||
return new Toolbar({ editorView, node })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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,21 +0,0 @@
|
|||||||
import ComponentView from './ComponentView'
|
|
||||||
|
|
||||||
export default function initNodeViews({ nodes, editable }) {
|
|
||||||
const nodeViews = {}
|
|
||||||
|
|
||||||
Object.keys(nodes).forEach(nodeName => {
|
|
||||||
nodeViews[nodeName] = (node, view, getPos, decorations) => {
|
|
||||||
const component = nodes[nodeName]
|
|
||||||
|
|
||||||
return new ComponentView(component, {
|
|
||||||
node,
|
|
||||||
view,
|
|
||||||
getPos,
|
|
||||||
decorations,
|
|
||||||
editable,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nodeViews
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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 () => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import { Plugin } from 'prosemirror-state'
|
|
||||||
|
|
||||||
class Toolbar {
|
|
||||||
|
|
||||||
constructor({ node, editorView }) {
|
|
||||||
this.editorView = editorView
|
|
||||||
this.node = node
|
|
||||||
this.element = this.node.elm
|
|
||||||
this.element.style.visibility = 'hidden'
|
|
||||||
this.element.style.opacity = 0
|
|
||||||
|
|
||||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
update(view, lastState) {
|
|
||||||
const { state } = view
|
|
||||||
|
|
||||||
// Don't do anything if the document/selection didn't change
|
|
||||||
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide the tooltip if the selection is empty
|
|
||||||
if (state.selection.empty) {
|
|
||||||
this.hide()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, reposition it and update its content
|
|
||||||
this.show()
|
|
||||||
const { from, to } = state.selection
|
|
||||||
|
|
||||||
// These are in screen coordinates
|
|
||||||
const start = view.coordsAtPos(from)
|
|
||||||
const end = view.coordsAtPos(to)
|
|
||||||
|
|
||||||
// The box in which the tooltip is positioned, to use as base
|
|
||||||
const box = this.element.offsetParent.getBoundingClientRect()
|
|
||||||
|
|
||||||
// Find a center-ish x position from the selection endpoints (when
|
|
||||||
// crossing lines, end may be more to the left)
|
|
||||||
const left = Math.max((start.left + end.left) / 2, start.left + 3)
|
|
||||||
this.element.style.left = `${left - box.left}px`
|
|
||||||
this.element.style.bottom = `${box.bottom - start.top}px`
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.element.style.visibility = 'visible'
|
|
||||||
this.element.style.opacity = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
hide(event) {
|
|
||||||
if (event && event.relatedTarget) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.style.visibility = 'hidden'
|
|
||||||
this.element.style.opacity = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (node) {
|
|
||||||
return new Plugin({
|
|
||||||
view(editorView) {
|
|
||||||
return new Toolbar({ editorView, node })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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 () => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { Plugin } from 'prosemirror-state'
|
|
||||||
|
|
||||||
export default Plugin
|
|
||||||
Reference in New Issue
Block a user