cleanup prosemirror example
This commit is contained in:
parent
32b8fac37f
commit
31d6ef6296
@ -31,7 +31,6 @@ export default class ProsemirrorBinding {
|
||||
const updatedProps = {
|
||||
dispatchTransaction: function (tr) {
|
||||
// TODO: remove
|
||||
const time = performance.now()
|
||||
const newState = prosemirror.state.apply(tr)
|
||||
mux(() => {
|
||||
updateYFragment(yDomFragment, newState, mapping)
|
||||
@ -41,11 +40,9 @@ export default class ProsemirrorBinding {
|
||||
} else {
|
||||
prosemirror.updateState(newState)
|
||||
}
|
||||
console.info('time for Yjs update: ', performance.now() - time)
|
||||
}
|
||||
}
|
||||
prosemirror.setProps(updatedProps)
|
||||
|
||||
yDomFragment.observeDeep(events => {
|
||||
if (events.length === 0) {
|
||||
return
|
||||
|
73
examples/prosemirror/PlaceholderPlugin.js
Normal file
73
examples/prosemirror/PlaceholderPlugin.js
Normal file
@ -0,0 +1,73 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
import {Plugin} from 'prosemirror-state'
|
||||
import {Decoration, DecorationSet} from 'prosemirror-view'
|
||||
import {schema} from 'prosemirror-schema-basic'
|
||||
|
||||
const findPlaceholder = (state, id) => {
|
||||
let decos = PlaceholderPlugin.getState(state)
|
||||
let found = decos.find(null, null, spec => spec.id === id)
|
||||
return found.length ? found[0].from : null
|
||||
}
|
||||
|
||||
export const startImageUpload = (view, file) => {
|
||||
// A fresh object to act as the ID for this upload
|
||||
let id = {}
|
||||
|
||||
// Replace the selection with a placeholder
|
||||
let tr = view.state.tr
|
||||
if (!tr.selection.empty) tr.deleteSelection()
|
||||
tr.setMeta(PlaceholderPlugin, {add: {id, pos: tr.selection.from}})
|
||||
view.dispatch(tr)
|
||||
|
||||
uploadFile(file).then(url => {
|
||||
let pos = findPlaceholder(view.state, id)
|
||||
// If the content around the placeholder has been deleted, drop
|
||||
// the image
|
||||
if (pos == null) return
|
||||
// Otherwise, insert it at the placeholder's position, and remove
|
||||
// the placeholder
|
||||
view.dispatch(view.state.tr
|
||||
.replaceWith(pos, pos, schema.nodes.image.create({src: url}))
|
||||
.setMeta(PlaceholderPlugin, {remove: {id}}))
|
||||
}, () => {
|
||||
// On failure, just clean up the placeholder
|
||||
view.dispatch(tr.setMeta(PlaceholderPlugin, {remove: {id}}))
|
||||
})
|
||||
}
|
||||
|
||||
// This is just a dummy that loads the file and creates a data URL.
|
||||
// You could swap it out with a function that does an actual upload
|
||||
// and returns a regular URL for the uploaded file.
|
||||
function uploadFile (file) {
|
||||
let reader = new FileReader()
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onload = () => resolve(reader.result)
|
||||
reader.onerror = () => reject(reader.error)
|
||||
// Some extra delay to make the asynchronicity visible
|
||||
setTimeout(() => reader.readAsDataURL(file), 1500)
|
||||
})
|
||||
}
|
||||
|
||||
export const PlaceholderPlugin = new Plugin({
|
||||
state: {
|
||||
init () { return DecorationSet.empty },
|
||||
apply (tr, set) {
|
||||
// Adjust decoration positions to changes made by the transaction
|
||||
set = set.map(tr.mapping, tr.doc)
|
||||
// See if the transaction adds or removes any placeholders
|
||||
let action = tr.getMeta(this)
|
||||
if (action && action.add) {
|
||||
let widget = document.createElement('placeholder')
|
||||
let deco = Decoration.widget(action.add.pos, widget, {id: action.add.id})
|
||||
set = set.add(tr.doc, [deco])
|
||||
} else if (action && action.remove) {
|
||||
set = set.remove(set.find(null, null, spec => spec.id === action.remove.id))
|
||||
}
|
||||
return set
|
||||
}
|
||||
},
|
||||
props: {
|
||||
decorations (state) { return this.getState(state) }
|
||||
}
|
||||
})
|
21
examples/prosemirror/README.md
Normal file
21
examples/prosemirror/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
# Prosemirror Example
|
||||
|
||||
### Run basic websockets server
|
||||
|
||||
```sh
|
||||
node /provider/websocket/server.js
|
||||
```
|
||||
|
||||
### Bundle Prosemirror Example
|
||||
|
||||
This example requires external modules and needs to be bundled before shipping it to the browser.
|
||||
|
||||
```sh
|
||||
cd /examples/prosemirror/
|
||||
# bundle prosemirror example
|
||||
npx rollup -wc
|
||||
# serve example
|
||||
npx serve .
|
||||
```
|
||||
|
@ -8,88 +8,12 @@ import {EditorView} from 'prosemirror-view'
|
||||
import {Schema, DOMParser, Mark, Fragment, Node, Slice} from 'prosemirror-model'
|
||||
import {schema} from 'prosemirror-schema-basic'
|
||||
import {exampleSetup} from 'prosemirror-example-setup'
|
||||
import {Plugin} from 'prosemirror-state'
|
||||
import {Decoration, DecorationSet} from 'prosemirror-view'
|
||||
|
||||
let placeholderPlugin = new Plugin({
|
||||
state: {
|
||||
init() { return DecorationSet.empty },
|
||||
apply(tr, set) {
|
||||
// Adjust decoration positions to changes made by the transaction
|
||||
set = set.map(tr.mapping, tr.doc)
|
||||
// See if the transaction adds or removes any placeholders
|
||||
let action = tr.getMeta(this)
|
||||
if (action && action.add) {
|
||||
let widget = document.createElement("placeholder")
|
||||
let deco = Decoration.widget(action.add.pos, widget, {id: action.add.id})
|
||||
set = set.add(tr.doc, [deco])
|
||||
} else if (action && action.remove) {
|
||||
set = set.remove(set.find(null, null,
|
||||
spec => spec.id == action.remove.id))
|
||||
}
|
||||
return set
|
||||
}
|
||||
},
|
||||
props: {
|
||||
decorations(state) { return this.getState(state) }
|
||||
}
|
||||
})
|
||||
|
||||
function findPlaceholder(state, id) {
|
||||
let decos = placeholderPlugin.getState(state)
|
||||
let found = decos.find(null, null, spec => spec.id == id)
|
||||
return found.length ? found[0].from : null
|
||||
}
|
||||
|
||||
document.querySelector("#image-upload").addEventListener("change", e => {
|
||||
if (view.state.selection.$from.parent.inlineContent && e.target.files.length)
|
||||
startImageUpload(view, e.target.files[0])
|
||||
view.focus()
|
||||
})
|
||||
|
||||
function startImageUpload(view, file) {
|
||||
// A fresh object to act as the ID for this upload
|
||||
let id = {}
|
||||
|
||||
// Replace the selection with a placeholder
|
||||
let tr = view.state.tr
|
||||
if (!tr.selection.empty) tr.deleteSelection()
|
||||
tr.setMeta(placeholderPlugin, {add: {id, pos: tr.selection.from}})
|
||||
view.dispatch(tr)
|
||||
|
||||
uploadFile(file).then(url => {
|
||||
let pos = findPlaceholder(view.state, id)
|
||||
// If the content around the placeholder has been deleted, drop
|
||||
// the image
|
||||
if (pos == null) return
|
||||
// Otherwise, insert it at the placeholder's position, and remove
|
||||
// the placeholder
|
||||
view.dispatch(view.state.tr
|
||||
.replaceWith(pos, pos, schema.nodes.image.create({src: url}))
|
||||
.setMeta(placeholderPlugin, {remove: {id}}))
|
||||
}, () => {
|
||||
// On failure, just clean up the placeholder
|
||||
view.dispatch(tr.setMeta(placeholderPlugin, {remove: {id}}))
|
||||
})
|
||||
}
|
||||
|
||||
// This is just a dummy that loads the file and creates a data URL.
|
||||
// You could swap it out with a function that does an actual upload
|
||||
// and returns a regular URL for the uploaded file.
|
||||
function uploadFile (file) {
|
||||
let reader = new FileReader()
|
||||
return new Promise((accept, fail) => {
|
||||
reader.onload = () => accept(reader.result)
|
||||
reader.onerror = () => fail(reader.error)
|
||||
// Some extra delay to make the asynchronicity visible
|
||||
setTimeout(() => reader.readAsDataURL(file), 1500)
|
||||
})
|
||||
}
|
||||
import { PlaceholderPlugin, startImageUpload } from './PlaceholderPlugin.js'
|
||||
|
||||
const view = new EditorView(document.querySelector('#editor'), {
|
||||
state: EditorState.create({
|
||||
doc: DOMParser.fromSchema(schema).parse(document.querySelector('#content')),
|
||||
plugins: exampleSetup({schema}).concat(placeholderPlugin)
|
||||
plugins: exampleSetup({schema}).concat(PlaceholderPlugin)
|
||||
})
|
||||
})
|
||||
|
||||
@ -110,3 +34,10 @@ window.Node = Node
|
||||
window.Schema = Schema
|
||||
window.Slice = Slice
|
||||
window.prosemirrorBinding = prosemirrorBinding
|
||||
|
||||
document.querySelector('#image-upload').addEventListener('change', e => {
|
||||
if (view.state.selection.$from.parent.inlineContent && e.target.files.length) {
|
||||
startImageUpload(view, e.target.files[0])
|
||||
}
|
||||
view.focus()
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user