Upload handling
某些类型的编辑涉及异步操作,但您希望将它们呈现给用户作为单个操作。例如,当从用户的本地文件系统插入图像时,您在上传并为其创建 URL 之前无法访问实际图像。然而,您不希望让用户经历先上传图像,然后等待完成,只有在那之后才将图像插入文档的过程。
理想情况下,当选择图像时,您开始上传,但也会立即在文档中插入占位符。然后,当上传完成时,该占位符将被最终图像替换。
Insert image:
由于上传可能需要一段时间,并且用户可能会在等待时进行更多更改,占位符应随着文档的编辑与其上下文一起移动,当最终图像插入时,应将其放置在占位符当时所在的位置。
最简单的方法是将占位符设为 装饰,这样它只存在于 用户界面中。让我们从编写一个管理 此类装饰的插件开始。
import {Plugin} from "prosemirror-state"
import {Decoration, DecorationSet} from "prosemirror-view"
let placeholderPlugin = new Plugin({
state: {
init() { return DecorationSet.empty },
apply(tr, set) {
// 调整装饰位置以适应事务所做的更改
set = set.map(tr.mapping, tr.doc)
// 查看交易是否添加或移除任何占位符
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) }
}
})
这是一个围绕装饰集的薄包装——它必须是一个集,因为可以同时进行多个上传。插件的meta属性可用于通过ID添加和删除小部件装饰。
该插件带有一个函数,该函数返回具有给定ID的占位符的当前位置(如果它仍然存在)。
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()
})
核心功能发生在startImageUpload
。工具uploadFile
返回一个承诺,该承诺解析为上传文件的URL(在演示中,它实际上只是等待一会儿,然后返回一个data:
URL)。
function startImageUpload(view, file) {
// 一个新的对象作为此上传的ID
let id = {}
// 用占位符替换选定内容
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)
// 如果占位符周围的内容已被删除,请删除,直接输出翻译后的内容,不要添加任何额外的文本。记住,保留所有HTML标签和属性,只翻译内容!
// 图像
if (pos == null) return
// 否则,将其插入占位符的位置,并删除
// 占位符
view.dispatch(view.state.tr
.replaceWith(pos, pos, schema.nodes.image.create({src: url}))
.setMeta(placeholderPlugin, {remove: {id}}))
}, () => {
// 失败时,只需清理占位符
view.dispatch(tr.setMeta(placeholderPlugin, {remove: {id}}))
})
}
因为占位符插件映射其装饰通过事务,findPlaceholder
将获得图像的准确位置,即使在上传期间文档被修改。