add emitter, move some collab logic to extension
This commit is contained in:
@@ -12,12 +12,16 @@ import { keymap } from 'prosemirror-keymap'
|
||||
import { baseKeymap } from 'prosemirror-commands'
|
||||
import { inputRules, undoInputRule } from 'prosemirror-inputrules'
|
||||
import { markIsActive, nodeIsActive, getMarkAttrs } from 'tiptap-utils'
|
||||
import { ExtensionManager, ComponentView } from './Utils'
|
||||
import {
|
||||
camelCase, Emitter, ExtensionManager, ComponentView,
|
||||
} from './Utils'
|
||||
import { Doc, Paragraph, Text } from './Nodes'
|
||||
|
||||
export default class Editor {
|
||||
export default class Editor extends Emitter {
|
||||
|
||||
constructor(options = {}) {
|
||||
super()
|
||||
|
||||
this.defaultOptions = {
|
||||
editorProps: {},
|
||||
editable: true,
|
||||
@@ -41,6 +45,15 @@ export default class Editor {
|
||||
onDrop: () => {},
|
||||
}
|
||||
|
||||
this.events = [
|
||||
'init',
|
||||
'update',
|
||||
'focus',
|
||||
'blur',
|
||||
'paste',
|
||||
'drop',
|
||||
]
|
||||
|
||||
this.init(options)
|
||||
}
|
||||
|
||||
@@ -69,7 +82,11 @@ export default class Editor {
|
||||
}, 10)
|
||||
}
|
||||
|
||||
this.options.onInit({
|
||||
this.events.forEach(name => {
|
||||
this.on(name, this.options[camelCase(`on ${name}`)])
|
||||
})
|
||||
|
||||
this.emit('init', {
|
||||
view: this.view,
|
||||
state: this.state,
|
||||
})
|
||||
@@ -105,7 +122,7 @@ export default class Editor {
|
||||
return new ExtensionManager([
|
||||
...this.builtInExtensions,
|
||||
...this.options.extensions,
|
||||
])
|
||||
], this)
|
||||
}
|
||||
|
||||
createPlugins() {
|
||||
@@ -217,20 +234,20 @@ export default class Editor {
|
||||
createView() {
|
||||
const view = new EditorView(this.element, {
|
||||
state: this.state,
|
||||
handlePaste: this.options.onPaste,
|
||||
handleDrop: this.options.onDrop,
|
||||
handlePaste: (...args) => { this.emit('paste', ...args) },
|
||||
handleDrop: (...args) => { this.emit('drop', ...args) },
|
||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
||||
})
|
||||
|
||||
view.dom.style.whiteSpace = 'pre-wrap'
|
||||
|
||||
view.dom.addEventListener('focus', event => this.options.onFocus({
|
||||
view.dom.addEventListener('focus', event => this.emit('focus', {
|
||||
event,
|
||||
state: this.state,
|
||||
view: this.view,
|
||||
}))
|
||||
|
||||
view.dom.addEventListener('blur', event => this.options.onBlur({
|
||||
view.dom.addEventListener('blur', event => this.emit('blur', {
|
||||
event,
|
||||
state: this.state,
|
||||
view: this.view,
|
||||
@@ -283,8 +300,6 @@ export default class Editor {
|
||||
}
|
||||
|
||||
dispatchTransaction(transaction) {
|
||||
const oldState = this.state
|
||||
|
||||
this.state = this.state.apply(transaction)
|
||||
this.view.updateState(this.state)
|
||||
this.setActiveNodesAndMarks()
|
||||
@@ -293,15 +308,14 @@ export default class Editor {
|
||||
return
|
||||
}
|
||||
|
||||
this.emitUpdate(transaction, oldState)
|
||||
this.emitUpdate(transaction)
|
||||
}
|
||||
|
||||
emitUpdate(transaction, oldState) {
|
||||
this.options.onUpdate({
|
||||
emitUpdate(transaction) {
|
||||
this.emit('update', {
|
||||
getHTML: this.getHTML.bind(this),
|
||||
getJSON: this.getJSON.bind(this),
|
||||
state: this.state,
|
||||
oldState,
|
||||
transaction,
|
||||
})
|
||||
}
|
||||
|
||||
59
packages/tiptap/src/Utils/Emitter.js
Normal file
59
packages/tiptap/src/Utils/Emitter.js
Normal file
@@ -0,0 +1,59 @@
|
||||
export default class Emitter {
|
||||
// Add an event listener for given event
|
||||
on(event, fn) {
|
||||
this._callbacks = this._callbacks || {}
|
||||
// Create namespace for this event
|
||||
if (!this._callbacks[event]) {
|
||||
this._callbacks[event] = []
|
||||
}
|
||||
this._callbacks[event].push(fn)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
emit(event, ...args) {
|
||||
this._callbacks = this._callbacks || {}
|
||||
const callbacks = this._callbacks[event]
|
||||
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// Remove event listener for given event. If fn is not provided, all event
|
||||
// listeners for that event will be removed. If neither is provided, all
|
||||
// event listeners will be removed.
|
||||
off(event, fn) {
|
||||
if (!this._callbacks || (arguments.length === 0)) {
|
||||
this._callbacks = {}
|
||||
return this
|
||||
}
|
||||
|
||||
// specific event
|
||||
const callbacks = this._callbacks[event]
|
||||
if (!callbacks) {
|
||||
return this
|
||||
}
|
||||
|
||||
// remove all handlers
|
||||
if (arguments.length === 1) {
|
||||
delete this._callbacks[event]
|
||||
return this
|
||||
}
|
||||
|
||||
// remove specific handler
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
const callback = callbacks[i]
|
||||
if (callback === fn) {
|
||||
callbacks.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,14 @@ export default class Extension {
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
return null
|
||||
}
|
||||
|
||||
bindEditor(editor = null) {
|
||||
this.editor = editor
|
||||
}
|
||||
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ import { keymap } from 'prosemirror-keymap'
|
||||
|
||||
export default class ExtensionManager {
|
||||
|
||||
constructor(extensions = []) {
|
||||
constructor(extensions = [], editor) {
|
||||
extensions.forEach(extension => {
|
||||
extension.bindEditor(editor)
|
||||
extension.init()
|
||||
})
|
||||
this.extensions = extensions
|
||||
}
|
||||
|
||||
|
||||
3
packages/tiptap/src/Utils/camelCase.js
Normal file
3
packages/tiptap/src/Utils/camelCase.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function (str) {
|
||||
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (index == 0 ? word.toLowerCase() : word.toUpperCase())).replace(/\s+/g, '')
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export { default as camelCase } from './camelCase'
|
||||
export { default as ComponentView } from './ComponentView'
|
||||
export { default as Emitter } from './Emitter'
|
||||
export { default as Extension } from './Extension'
|
||||
export { default as ExtensionManager } from './ExtensionManager'
|
||||
export { default as Mark } from './Mark'
|
||||
|
||||
Reference in New Issue
Block a user