tabs to spaces whitespace
This commit is contained in:
@@ -4,4 +4,4 @@ This is the core package of [tiptap](https://www.npmjs.com/package/tiptap).
|
||||
[](https://www.npmjs.com/package/tiptap)
|
||||
[](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[](https://www.npmjs.com/package/tiptap)
|
||||
[](https://www.npmjs.com/package/tiptap)
|
||||
[](https://www.npmjs.com/package/tiptap)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export default {
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'editor.element': {
|
||||
immediate: true,
|
||||
handler(element) {
|
||||
if (element) {
|
||||
this.$nextTick(() => this.$el.append(element.firstChild))
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('div')
|
||||
},
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'editor.element': {
|
||||
immediate: true,
|
||||
handler(element) {
|
||||
if (element) {
|
||||
this.$nextTick(() => this.$el.append(element.firstChild))
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('div')
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import FloatingMenu from '../Utils/FloatingMenu'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menu: {
|
||||
isActive: false,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editor: {
|
||||
immediate: true,
|
||||
handler(editor) {
|
||||
if (editor) {
|
||||
this.$nextTick(() => {
|
||||
editor.registerPlugin(FloatingMenu({
|
||||
element: this.$el,
|
||||
onUpdate: menu => {
|
||||
this.menu = menu
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menu: {
|
||||
isActive: false,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editor: {
|
||||
immediate: true,
|
||||
handler(editor) {
|
||||
if (editor) {
|
||||
this.$nextTick(() => {
|
||||
editor.registerPlugin(FloatingMenu({
|
||||
element: this.$el,
|
||||
onUpdate: menu => {
|
||||
this.menu = menu
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export default {
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
})
|
||||
},
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import MenuBubble from '../Utils/MenuBubble'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menu: {
|
||||
isActive: false,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editor: {
|
||||
immediate: true,
|
||||
handler(editor) {
|
||||
if (editor) {
|
||||
this.$nextTick(() => {
|
||||
editor.registerPlugin(MenuBubble({
|
||||
element: this.$el,
|
||||
onUpdate: menu => {
|
||||
this.menu = menu
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
props: {
|
||||
editor: {
|
||||
default: null,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menu: {
|
||||
isActive: false,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editor: {
|
||||
immediate: true,
|
||||
handler(editor) {
|
||||
if (editor) {
|
||||
this.$nextTick(() => {
|
||||
editor.registerPlugin(MenuBubble({
|
||||
element: this.$el,
|
||||
onUpdate: menu => {
|
||||
this.menu = menu
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
render() {
|
||||
if (!this.editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
return this.$scopedSlots.default({
|
||||
focused: this.editor.view.focused,
|
||||
focus: this.editor.focus,
|
||||
commands: this.editor.commands,
|
||||
isActive: this.editor.isActive.bind(this.editor),
|
||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||
menu: this.menu,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ export { default as FloatingMenu } from './Components/FloatingMenu'
|
||||
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 Plugin } from './Utils/Plugin'
|
||||
|
||||
@@ -2,14 +2,14 @@ import Node from '../Utils/Node'
|
||||
|
||||
export default class Doc extends Node {
|
||||
|
||||
get name() {
|
||||
return 'doc'
|
||||
}
|
||||
get name() {
|
||||
return 'doc'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'block+',
|
||||
}
|
||||
}
|
||||
get schema() {
|
||||
return {
|
||||
content: 'block+',
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,24 +3,24 @@ import Node from '../Utils/Node'
|
||||
|
||||
export default class Paragraph extends Node {
|
||||
|
||||
get name() {
|
||||
return 'paragraph'
|
||||
}
|
||||
get name() {
|
||||
return 'paragraph'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
draggable: false,
|
||||
parseDOM: [{
|
||||
tag: 'p',
|
||||
}],
|
||||
toDOM: () => ['p', 0],
|
||||
}
|
||||
}
|
||||
get schema() {
|
||||
return {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
draggable: false,
|
||||
parseDOM: [{
|
||||
tag: 'p',
|
||||
}],
|
||||
toDOM: () => ['p', 0],
|
||||
}
|
||||
}
|
||||
|
||||
commands({ type }) {
|
||||
return () => setBlockType(type)
|
||||
}
|
||||
commands({ type }) {
|
||||
return () => setBlockType(type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ import Node from '../Utils/Node'
|
||||
|
||||
export default class Text extends Node {
|
||||
|
||||
get name() {
|
||||
return 'text'
|
||||
}
|
||||
get name() {
|
||||
return 'text'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'inline',
|
||||
}
|
||||
}
|
||||
get schema() {
|
||||
return {
|
||||
group: 'inline',
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import Paragraph from './Paragraph'
|
||||
import Text from './Text'
|
||||
|
||||
export default [
|
||||
new Doc(),
|
||||
new Text(),
|
||||
new Paragraph(),
|
||||
new Doc(),
|
||||
new Text(),
|
||||
new Paragraph(),
|
||||
]
|
||||
|
||||
@@ -1,92 +1,92 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
export default class ComponentView {
|
||||
constructor(component, {
|
||||
parent,
|
||||
node,
|
||||
view,
|
||||
getPos,
|
||||
decorations,
|
||||
editable,
|
||||
}) {
|
||||
this.parent = parent
|
||||
this.component = component
|
||||
this.node = node
|
||||
this.view = view
|
||||
this.getPos = getPos
|
||||
this.decorations = decorations
|
||||
this.editable = editable
|
||||
constructor(component, {
|
||||
parent,
|
||||
node,
|
||||
view,
|
||||
getPos,
|
||||
decorations,
|
||||
editable,
|
||||
}) {
|
||||
this.parent = parent
|
||||
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
|
||||
}
|
||||
this.dom = this.createDOM()
|
||||
this.contentDOM = this.vm.$refs.content
|
||||
}
|
||||
|
||||
createDOM() {
|
||||
const Component = Vue.extend(this.component)
|
||||
this.vm = new Component({
|
||||
parent: this.parent,
|
||||
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
|
||||
}
|
||||
createDOM() {
|
||||
const Component = Vue.extend(this.component)
|
||||
this.vm = new Component({
|
||||
parent: this.parent,
|
||||
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
|
||||
}
|
||||
updateAttrs(attrs) {
|
||||
if (!this.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), null, {
|
||||
...this.node.attrs,
|
||||
...attrs,
|
||||
})
|
||||
this.view.dispatch(transaction)
|
||||
}
|
||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), null, {
|
||||
...this.node.attrs,
|
||||
...attrs,
|
||||
})
|
||||
this.view.dispatch(transaction)
|
||||
}
|
||||
|
||||
updateContent(content) {
|
||||
if (!this.editable) {
|
||||
return
|
||||
}
|
||||
updateContent(content) {
|
||||
if (!this.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), this.node.type, { content })
|
||||
this.view.dispatch(transaction)
|
||||
}
|
||||
const transaction = this.view.state.tr.setNodeMarkup(this.getPos(), this.node.type, { content })
|
||||
this.view.dispatch(transaction)
|
||||
}
|
||||
|
||||
ignoreMutation() {
|
||||
return true
|
||||
}
|
||||
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)
|
||||
}
|
||||
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
|
||||
}
|
||||
update(node, decorations) {
|
||||
if (node.type !== this.node.type) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (node === this.node && this.decorations === decorations) {
|
||||
return true
|
||||
}
|
||||
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
|
||||
}
|
||||
this.node = node
|
||||
this.decorations = decorations
|
||||
this.vm._props.node = node
|
||||
this.vm._props.decorations = decorations
|
||||
return true
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.vm.$destroy()
|
||||
}
|
||||
destroy() {
|
||||
this.vm.$destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,278 +9,278 @@ import { inputRules } from 'prosemirror-inputrules'
|
||||
import { markIsActive, nodeIsActive, getMarkAttrs } from 'tiptap-utils'
|
||||
|
||||
import {
|
||||
ExtensionManager,
|
||||
initNodeViews,
|
||||
builtInKeymap,
|
||||
ExtensionManager,
|
||||
initNodeViews,
|
||||
builtInKeymap,
|
||||
} from '.'
|
||||
|
||||
import builtInNodes from '../Nodes'
|
||||
|
||||
export default class Editor {
|
||||
|
||||
constructor(options = {}) {
|
||||
this.setOptions(options)
|
||||
this.init()
|
||||
}
|
||||
constructor(options = {}) {
|
||||
this.setOptions(options)
|
||||
this.init()
|
||||
}
|
||||
|
||||
setOptions(options) {
|
||||
const defaultOptions = {
|
||||
editable: true,
|
||||
content: '',
|
||||
onUpdate: () => {},
|
||||
}
|
||||
setOptions(options) {
|
||||
const defaultOptions = {
|
||||
editable: true,
|
||||
content: '',
|
||||
onUpdate: () => {},
|
||||
}
|
||||
|
||||
this.options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
this.options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bus = new Vue()
|
||||
this.element = document.createElement('div')
|
||||
this.extensions = this.createExtensions()
|
||||
this.nodes = this.createNodes()
|
||||
this.marks = this.createMarks()
|
||||
this.views = this.createViews()
|
||||
this.schema = this.createSchema()
|
||||
this.plugins = this.createPlugins()
|
||||
this.keymaps = this.createKeymaps()
|
||||
this.inputRules = this.createInputRules()
|
||||
this.state = this.createState()
|
||||
this.view = this.createView()
|
||||
this.commands = this.createCommands()
|
||||
this.getActiveNodesAndMarks()
|
||||
this.emit('init')
|
||||
}
|
||||
init() {
|
||||
this.bus = new Vue()
|
||||
this.element = document.createElement('div')
|
||||
this.extensions = this.createExtensions()
|
||||
this.nodes = this.createNodes()
|
||||
this.marks = this.createMarks()
|
||||
this.views = this.createViews()
|
||||
this.schema = this.createSchema()
|
||||
this.plugins = this.createPlugins()
|
||||
this.keymaps = this.createKeymaps()
|
||||
this.inputRules = this.createInputRules()
|
||||
this.state = this.createState()
|
||||
this.view = this.createView()
|
||||
this.commands = this.createCommands()
|
||||
this.getActiveNodesAndMarks()
|
||||
this.emit('init')
|
||||
}
|
||||
|
||||
createExtensions() {
|
||||
return new ExtensionManager([
|
||||
...builtInNodes,
|
||||
...this.options.extensions,
|
||||
])
|
||||
}
|
||||
createExtensions() {
|
||||
return new ExtensionManager([
|
||||
...builtInNodes,
|
||||
...this.options.extensions,
|
||||
])
|
||||
}
|
||||
|
||||
createPlugins() {
|
||||
return this.extensions.plugins
|
||||
}
|
||||
createPlugins() {
|
||||
return this.extensions.plugins
|
||||
}
|
||||
|
||||
createKeymaps() {
|
||||
return this.extensions.keymaps({
|
||||
schema: this.schema,
|
||||
})
|
||||
}
|
||||
createKeymaps() {
|
||||
return this.extensions.keymaps({
|
||||
schema: this.schema,
|
||||
})
|
||||
}
|
||||
|
||||
createInputRules() {
|
||||
return this.extensions.inputRules({
|
||||
schema: this.schema,
|
||||
})
|
||||
}
|
||||
createInputRules() {
|
||||
return this.extensions.inputRules({
|
||||
schema: this.schema,
|
||||
})
|
||||
}
|
||||
|
||||
createCommands() {
|
||||
return this.extensions.commands({
|
||||
schema: this.schema,
|
||||
view: this.view,
|
||||
editable: this.options.editable,
|
||||
})
|
||||
}
|
||||
createCommands() {
|
||||
return this.extensions.commands({
|
||||
schema: this.schema,
|
||||
view: this.view,
|
||||
editable: this.options.editable,
|
||||
})
|
||||
}
|
||||
|
||||
createNodes() {
|
||||
return this.extensions.nodes
|
||||
}
|
||||
createNodes() {
|
||||
return this.extensions.nodes
|
||||
}
|
||||
|
||||
createMarks() {
|
||||
return this.extensions.marks
|
||||
}
|
||||
createMarks() {
|
||||
return this.extensions.marks
|
||||
}
|
||||
|
||||
createViews() {
|
||||
return this.extensions.views
|
||||
}
|
||||
createViews() {
|
||||
return this.extensions.views
|
||||
}
|
||||
|
||||
createSchema() {
|
||||
return new Schema({
|
||||
nodes: this.nodes,
|
||||
marks: this.marks,
|
||||
})
|
||||
}
|
||||
createSchema() {
|
||||
return new Schema({
|
||||
nodes: this.nodes,
|
||||
marks: this.marks,
|
||||
})
|
||||
}
|
||||
|
||||
createState() {
|
||||
return EditorState.create({
|
||||
schema: this.schema,
|
||||
doc: this.createDocument(this.options.content),
|
||||
plugins: [
|
||||
...this.plugins,
|
||||
inputRules({
|
||||
rules: this.inputRules,
|
||||
}),
|
||||
...this.keymaps,
|
||||
keymap(builtInKeymap),
|
||||
keymap(baseKeymap),
|
||||
gapCursor(),
|
||||
new Plugin({
|
||||
props: {
|
||||
editable: () => this.options.editable,
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
createState() {
|
||||
return EditorState.create({
|
||||
schema: this.schema,
|
||||
doc: this.createDocument(this.options.content),
|
||||
plugins: [
|
||||
...this.plugins,
|
||||
inputRules({
|
||||
rules: this.inputRules,
|
||||
}),
|
||||
...this.keymaps,
|
||||
keymap(builtInKeymap),
|
||||
keymap(baseKeymap),
|
||||
gapCursor(),
|
||||
new Plugin({
|
||||
props: {
|
||||
editable: () => this.options.editable,
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
createDocument(content) {
|
||||
if (typeof content === 'object') {
|
||||
return this.schema.nodeFromJSON(content)
|
||||
}
|
||||
createDocument(content) {
|
||||
if (typeof content === 'object') {
|
||||
return this.schema.nodeFromJSON(content)
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
const element = document.createElement('div')
|
||||
element.innerHTML = content.trim()
|
||||
if (typeof content === 'string') {
|
||||
const element = document.createElement('div')
|
||||
element.innerHTML = content.trim()
|
||||
|
||||
return DOMParser.fromSchema(this.schema).parse(element)
|
||||
}
|
||||
return DOMParser.fromSchema(this.schema).parse(element)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
createView() {
|
||||
const view = new EditorView(this.element, {
|
||||
state: this.state,
|
||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||||
nodeViews: initNodeViews({
|
||||
nodes: this.views,
|
||||
editable: this.options.editable,
|
||||
}),
|
||||
})
|
||||
createView() {
|
||||
const view = new EditorView(this.element, {
|
||||
state: this.state,
|
||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||||
nodeViews: initNodeViews({
|
||||
nodes: this.views,
|
||||
editable: this.options.editable,
|
||||
}),
|
||||
})
|
||||
|
||||
view.dom.style.whiteSpace = 'pre-wrap'
|
||||
view.dom.style.whiteSpace = 'pre-wrap'
|
||||
|
||||
return view
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
dispatchTransaction(transaction) {
|
||||
this.state = this.state.apply(transaction)
|
||||
this.view.updateState(this.state)
|
||||
this.getActiveNodesAndMarks()
|
||||
dispatchTransaction(transaction) {
|
||||
this.state = this.state.apply(transaction)
|
||||
this.view.updateState(this.state)
|
||||
this.getActiveNodesAndMarks()
|
||||
|
||||
if (!transaction.docChanged) {
|
||||
return
|
||||
}
|
||||
if (!transaction.docChanged) {
|
||||
return
|
||||
}
|
||||
|
||||
this.emitUpdate()
|
||||
}
|
||||
this.emitUpdate()
|
||||
}
|
||||
|
||||
emitUpdate() {
|
||||
this.options.onUpdate({
|
||||
getHTML: this.getHTML.bind(this),
|
||||
getJSON: this.getJSON.bind(this),
|
||||
state: this.state,
|
||||
})
|
||||
}
|
||||
emitUpdate() {
|
||||
this.options.onUpdate({
|
||||
getHTML: this.getHTML.bind(this),
|
||||
getJSON: this.getJSON.bind(this),
|
||||
state: this.state,
|
||||
})
|
||||
}
|
||||
|
||||
getHTML() {
|
||||
const div = document.createElement('div')
|
||||
const fragment = DOMSerializer
|
||||
.fromSchema(this.schema)
|
||||
.serializeFragment(this.state.doc.content)
|
||||
getHTML() {
|
||||
const div = document.createElement('div')
|
||||
const fragment = DOMSerializer
|
||||
.fromSchema(this.schema)
|
||||
.serializeFragment(this.state.doc.content)
|
||||
|
||||
div.appendChild(fragment)
|
||||
div.appendChild(fragment)
|
||||
|
||||
return div.innerHTML
|
||||
}
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
getJSON() {
|
||||
return this.state.doc.toJSON()
|
||||
}
|
||||
getJSON() {
|
||||
return this.state.doc.toJSON()
|
||||
}
|
||||
|
||||
setContent(content = {}, emitUpdate = false) {
|
||||
this.state = EditorState.create({
|
||||
schema: this.state.schema,
|
||||
doc: this.createDocument(content),
|
||||
plugins: this.state.plugins,
|
||||
})
|
||||
setContent(content = {}, emitUpdate = false) {
|
||||
this.state = EditorState.create({
|
||||
schema: this.state.schema,
|
||||
doc: this.createDocument(content),
|
||||
plugins: this.state.plugins,
|
||||
})
|
||||
|
||||
this.view.updateState(this.state)
|
||||
this.view.updateState(this.state)
|
||||
|
||||
if (emitUpdate) {
|
||||
this.emitUpdate()
|
||||
}
|
||||
}
|
||||
if (emitUpdate) {
|
||||
this.emitUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
clearContent(emitUpdate = false) {
|
||||
this.setContent({
|
||||
type: 'doc',
|
||||
content: [{
|
||||
type: 'paragraph',
|
||||
}],
|
||||
}, emitUpdate)
|
||||
}
|
||||
clearContent(emitUpdate = false) {
|
||||
this.setContent({
|
||||
type: 'doc',
|
||||
content: [{
|
||||
type: 'paragraph',
|
||||
}],
|
||||
}, emitUpdate)
|
||||
}
|
||||
|
||||
getActiveNodesAndMarks() {
|
||||
this.activeMarks = Object
|
||||
.entries(this.schema.marks)
|
||||
.reduce((marks, [name, mark]) => ({
|
||||
...marks,
|
||||
[name]: (attrs = {}) => markIsActive(this.state, mark, attrs),
|
||||
}), {})
|
||||
getActiveNodesAndMarks() {
|
||||
this.activeMarks = Object
|
||||
.entries(this.schema.marks)
|
||||
.reduce((marks, [name, mark]) => ({
|
||||
...marks,
|
||||
[name]: (attrs = {}) => markIsActive(this.state, mark, attrs),
|
||||
}), {})
|
||||
|
||||
this.activeMarkAttrs = Object
|
||||
.entries(this.schema.marks)
|
||||
.reduce((marks, [name, mark]) => ({
|
||||
...marks,
|
||||
[name]: getMarkAttrs(this.state, mark),
|
||||
}), {})
|
||||
this.activeMarkAttrs = Object
|
||||
.entries(this.schema.marks)
|
||||
.reduce((marks, [name, mark]) => ({
|
||||
...marks,
|
||||
[name]: getMarkAttrs(this.state, mark),
|
||||
}), {})
|
||||
|
||||
this.activeNodes = Object
|
||||
.entries(this.schema.nodes)
|
||||
.reduce((nodes, [name, node]) => ({
|
||||
...nodes,
|
||||
[name]: (attrs = {}) => nodeIsActive(this.state, node, attrs),
|
||||
}), {})
|
||||
}
|
||||
this.activeNodes = Object
|
||||
.entries(this.schema.nodes)
|
||||
.reduce((nodes, [name, node]) => ({
|
||||
...nodes,
|
||||
[name]: (attrs = {}) => nodeIsActive(this.state, node, attrs),
|
||||
}), {})
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.view.focus()
|
||||
}
|
||||
focus() {
|
||||
this.view.focus()
|
||||
}
|
||||
|
||||
emit(event, ...data) {
|
||||
this.bus.$emit(event, ...data)
|
||||
}
|
||||
emit(event, ...data) {
|
||||
this.bus.$emit(event, ...data)
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
this.bus.$on(event, callback)
|
||||
}
|
||||
on(event, callback) {
|
||||
this.bus.$on(event, callback)
|
||||
}
|
||||
|
||||
registerPlugin(plugin = null) {
|
||||
if (plugin) {
|
||||
this.state = this.state.reconfigure({
|
||||
plugins: this.state.plugins.concat([plugin]),
|
||||
})
|
||||
this.view.updateState(this.state)
|
||||
}
|
||||
}
|
||||
registerPlugin(plugin = null) {
|
||||
if (plugin) {
|
||||
this.state = this.state.reconfigure({
|
||||
plugins: this.state.plugins.concat([plugin]),
|
||||
})
|
||||
this.view.updateState(this.state)
|
||||
}
|
||||
}
|
||||
|
||||
markAttrs(type = null) {
|
||||
return this.activeMarkAttrs[type]
|
||||
}
|
||||
markAttrs(type = null) {
|
||||
return this.activeMarkAttrs[type]
|
||||
}
|
||||
|
||||
isActive(type = null, attrs = {}) {
|
||||
const types = {
|
||||
...this.activeMarks,
|
||||
...this.activeNodes,
|
||||
}
|
||||
isActive(type = null, attrs = {}) {
|
||||
const types = {
|
||||
...this.activeMarks,
|
||||
...this.activeNodes,
|
||||
}
|
||||
|
||||
if (!types[type]) {
|
||||
return false
|
||||
}
|
||||
if (!types[type]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return types[type](attrs)
|
||||
}
|
||||
return types[type](attrs)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.emit('destroy')
|
||||
destroy() {
|
||||
this.emit('destroy')
|
||||
|
||||
if (this.view) {
|
||||
this.view.destroy()
|
||||
}
|
||||
}
|
||||
if (this.view) {
|
||||
this.view.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,148 +2,148 @@ import { keymap } from 'prosemirror-keymap'
|
||||
|
||||
export default class ExtensionManager {
|
||||
|
||||
constructor(extensions = []) {
|
||||
this.extensions = extensions
|
||||
}
|
||||
constructor(extensions = []) {
|
||||
this.extensions = extensions
|
||||
}
|
||||
|
||||
get nodes() {
|
||||
return this.extensions
|
||||
.filter(extension => extension.type === 'node')
|
||||
.reduce((nodes, { name, schema }) => ({
|
||||
...nodes,
|
||||
[name]: schema,
|
||||
}), {})
|
||||
}
|
||||
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 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 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,
|
||||
}), {})
|
||||
}
|
||||
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 }))
|
||||
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,
|
||||
}))
|
||||
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))
|
||||
}
|
||||
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 }))
|
||||
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,
|
||||
}))
|
||||
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,
|
||||
]), [])
|
||||
}
|
||||
return [
|
||||
...extensionInputRules,
|
||||
...nodeMarkInputRules,
|
||||
].reduce((allInputRules, inputRules) => ([
|
||||
...allInputRules,
|
||||
...inputRules,
|
||||
]), [])
|
||||
}
|
||||
|
||||
commands({ schema, view, editable }) {
|
||||
return this.extensions
|
||||
.filter(extension => extension.commands)
|
||||
.reduce((allCommands, { name, type, commands: provider }) => {
|
||||
commands({ schema, view, editable }) {
|
||||
return this.extensions
|
||||
.filter(extension => extension.commands)
|
||||
.reduce((allCommands, { name, type, commands: provider }) => {
|
||||
|
||||
const commands = {}
|
||||
const value = provider({
|
||||
schema,
|
||||
...['node', 'mark'].includes(type) ? {
|
||||
type: schema[`${type}s`][name],
|
||||
} : {},
|
||||
})
|
||||
const commands = {}
|
||||
const value = provider({
|
||||
schema,
|
||||
...['node', 'mark'].includes(type) ? {
|
||||
type: schema[`${type}s`][name],
|
||||
} : {},
|
||||
})
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
commands[name] = attrs => value
|
||||
.forEach(callback => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return callback(attrs)(view.state, view.dispatch, view)
|
||||
})
|
||||
} else if (typeof value === 'function') {
|
||||
commands[name] = attrs => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return value(attrs)(view.state, view.dispatch, view)
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
Object.entries(value).forEach(([commandName, commandValue]) => {
|
||||
if (Array.isArray(commandValue)) {
|
||||
commands[commandName] = attrs => commandValue
|
||||
.forEach(callback => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return callback(attrs)(view.state, view.dispatch, view)
|
||||
})
|
||||
} else {
|
||||
commands[commandName] = attrs => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return commandValue(attrs)(view.state, view.dispatch, view)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
commands[name] = attrs => value
|
||||
.forEach(callback => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return callback(attrs)(view.state, view.dispatch, view)
|
||||
})
|
||||
} else if (typeof value === 'function') {
|
||||
commands[name] = attrs => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return value(attrs)(view.state, view.dispatch, view)
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
Object.entries(value).forEach(([commandName, commandValue]) => {
|
||||
if (Array.isArray(commandValue)) {
|
||||
commands[commandName] = attrs => commandValue
|
||||
.forEach(callback => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return callback(attrs)(view.state, view.dispatch, view)
|
||||
})
|
||||
} else {
|
||||
commands[commandName] = attrs => {
|
||||
if (!editable) {
|
||||
return false
|
||||
}
|
||||
view.focus()
|
||||
return commandValue(attrs)(view.state, view.dispatch, view)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...allCommands,
|
||||
...commands,
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
return {
|
||||
...allCommands,
|
||||
...commands,
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,81 +2,81 @@ import { Plugin } from 'prosemirror-state'
|
||||
|
||||
class Menu {
|
||||
|
||||
constructor({ options, editorView }) {
|
||||
this.options = {
|
||||
...{
|
||||
element: null,
|
||||
onUpdate: () => false,
|
||||
},
|
||||
...options,
|
||||
}
|
||||
this.editorView = editorView
|
||||
this.isActive = false
|
||||
this.top = 0
|
||||
constructor({ options, editorView }) {
|
||||
this.options = {
|
||||
...{
|
||||
element: null,
|
||||
onUpdate: () => false,
|
||||
},
|
||||
...options,
|
||||
}
|
||||
this.editorView = editorView
|
||||
this.isActive = false
|
||||
this.top = 0
|
||||
|
||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
||||
}
|
||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
||||
}
|
||||
|
||||
update(view, lastState) {
|
||||
const { state } = view
|
||||
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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
if (!state.selection.empty) {
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
|
||||
const currentDom = view.domAtPos(state.selection.$anchor.pos)
|
||||
const currentDom = view.domAtPos(state.selection.$anchor.pos)
|
||||
|
||||
const isActive = currentDom.node.innerHTML === '<br>'
|
||||
&& currentDom.node.tagName === 'P'
|
||||
&& currentDom.node.parentNode === view.dom
|
||||
const isActive = currentDom.node.innerHTML === '<br>'
|
||||
&& currentDom.node.tagName === 'P'
|
||||
&& currentDom.node.parentNode === view.dom
|
||||
|
||||
if (!isActive) {
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
if (!isActive) {
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
|
||||
const editorBoundings = this.options.element.offsetParent.getBoundingClientRect()
|
||||
const cursorBoundings = view.coordsAtPos(state.selection.$anchor.pos)
|
||||
const top = cursorBoundings.top - editorBoundings.top
|
||||
const editorBoundings = this.options.element.offsetParent.getBoundingClientRect()
|
||||
const cursorBoundings = view.coordsAtPos(state.selection.$anchor.pos)
|
||||
const top = cursorBoundings.top - editorBoundings.top
|
||||
|
||||
this.isActive = true
|
||||
this.top = top
|
||||
this.isActive = true
|
||||
this.top = top
|
||||
|
||||
this.sendUpdate()
|
||||
}
|
||||
this.sendUpdate()
|
||||
}
|
||||
|
||||
sendUpdate() {
|
||||
this.options.onUpdate({
|
||||
isActive: this.isActive,
|
||||
top: this.top,
|
||||
})
|
||||
}
|
||||
sendUpdate() {
|
||||
this.options.onUpdate({
|
||||
isActive: this.isActive,
|
||||
top: this.top,
|
||||
})
|
||||
}
|
||||
|
||||
hide(event) {
|
||||
if (event && event.relatedTarget) {
|
||||
return
|
||||
}
|
||||
hide(event) {
|
||||
if (event && event.relatedTarget) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isActive = false
|
||||
this.sendUpdate()
|
||||
}
|
||||
this.isActive = false
|
||||
this.sendUpdate()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
||||
}
|
||||
destroy() {
|
||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default function (options) {
|
||||
return new Plugin({
|
||||
view(editorView) {
|
||||
return new Menu({ editorView, options })
|
||||
},
|
||||
})
|
||||
return new Plugin({
|
||||
view(editorView) {
|
||||
return new Menu({ editorView, options })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,84 +2,84 @@ import { Plugin } from 'prosemirror-state'
|
||||
|
||||
class Menu {
|
||||
|
||||
constructor({ options, editorView }) {
|
||||
this.options = {
|
||||
...{
|
||||
element: null,
|
||||
onUpdate: () => false,
|
||||
},
|
||||
...options,
|
||||
}
|
||||
this.editorView = editorView
|
||||
this.isActive = false
|
||||
this.left = 0
|
||||
this.bottom = 0
|
||||
constructor({ options, editorView }) {
|
||||
this.options = {
|
||||
...{
|
||||
element: null,
|
||||
onUpdate: () => false,
|
||||
},
|
||||
...options,
|
||||
}
|
||||
this.editorView = editorView
|
||||
this.isActive = false
|
||||
this.left = 0
|
||||
this.bottom = 0
|
||||
|
||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
||||
}
|
||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
||||
}
|
||||
|
||||
update(view, lastState) {
|
||||
const { state } = view
|
||||
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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// Hide the tooltip if the selection is empty
|
||||
if (state.selection.empty) {
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, reposition it and update its content
|
||||
const { from, to } = state.selection
|
||||
// Otherwise, reposition it and update its content
|
||||
const { from, to } = state.selection
|
||||
|
||||
// These are in screen coordinates
|
||||
const start = view.coordsAtPos(from)
|
||||
const end = view.coordsAtPos(to)
|
||||
// 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.options.element.offsetParent.getBoundingClientRect()
|
||||
// The box in which the tooltip is positioned, to use as base
|
||||
const box = this.options.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)
|
||||
// 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.isActive = true
|
||||
this.left = parseInt(left - box.left, 10)
|
||||
this.bottom = parseInt(box.bottom - start.top, 10)
|
||||
this.isActive = true
|
||||
this.left = parseInt(left - box.left, 10)
|
||||
this.bottom = parseInt(box.bottom - start.top, 10)
|
||||
|
||||
this.sendUpdate()
|
||||
}
|
||||
this.sendUpdate()
|
||||
}
|
||||
|
||||
sendUpdate() {
|
||||
this.options.onUpdate({
|
||||
isActive: this.isActive,
|
||||
left: this.left,
|
||||
bottom: this.bottom,
|
||||
})
|
||||
}
|
||||
sendUpdate() {
|
||||
this.options.onUpdate({
|
||||
isActive: this.isActive,
|
||||
left: this.left,
|
||||
bottom: this.bottom,
|
||||
})
|
||||
}
|
||||
|
||||
hide(event) {
|
||||
if (event && event.relatedTarget) {
|
||||
return
|
||||
}
|
||||
hide(event) {
|
||||
if (event && event.relatedTarget) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isActive = false
|
||||
this.sendUpdate()
|
||||
}
|
||||
this.isActive = false
|
||||
this.sendUpdate()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
||||
}
|
||||
destroy() {
|
||||
this.editorView.dom.removeEventListener('blur', this.hide)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default function (options) {
|
||||
return new Plugin({
|
||||
view(editorView) {
|
||||
return new Menu({ editorView, options })
|
||||
},
|
||||
})
|
||||
return new Plugin({
|
||||
view(editorView) {
|
||||
return new Menu({ editorView, options })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { lift, selectParentNode } from 'prosemirror-commands'
|
||||
import { undoInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
const keymap = {
|
||||
'Mod-BracketLeft': lift,
|
||||
Backspace: undoInputRule,
|
||||
Escape: selectParentNode,
|
||||
'Mod-BracketLeft': lift,
|
||||
Backspace: undoInputRule,
|
||||
Escape: selectParentNode,
|
||||
}
|
||||
|
||||
export default keymap
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
export default class Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'extension'
|
||||
}
|
||||
get type() {
|
||||
return 'extension'
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return []
|
||||
}
|
||||
get plugins() {
|
||||
return []
|
||||
}
|
||||
|
||||
inputRules() {
|
||||
return []
|
||||
}
|
||||
inputRules() {
|
||||
return []
|
||||
}
|
||||
|
||||
keys() {
|
||||
return {}
|
||||
}
|
||||
keys() {
|
||||
return {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,24 +2,24 @@ import Extension from './Extension'
|
||||
|
||||
export default class Mark extends Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'mark'
|
||||
}
|
||||
get type() {
|
||||
return 'mark'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,24 +2,24 @@ import Extension from './Extension'
|
||||
|
||||
export default class Node extends Extension {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'node'
|
||||
}
|
||||
get type() {
|
||||
return 'node'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user