Thank you for helping. I tried that approach with no success, and many other variations. I feel like there’s just a fundemental piece I’m missing. I’ve built a WYSIWYG with the same behavior as you see in the dino example, but it was done with DOM manipulation/watching, but ProseMirror wrote it the way I would if I were to do it again. While I agree with the complete modularization of ProseMirror, I’m struggling to put the pieces together for these dino nodes, let alone in my own company’s implementation.
So in part desperation, I have really avoided doing this ever, but could you kindly adjust this dino demo, and comment on what you changed and why?
This is the dino demo from the PM website, and I’ve included a data-some="thing"
attribute on the tyrannosaurus node in the pre-existing content that I want persisted when PM parses it. (In my real application, we’ll have multiple data attributes with unknown/variable values, and style attributes for those that have to work in Outlook for desktop).
<!doctype html>
<head>
<title>Dinos in the Document</title>
<meta charset="utf8">
<link rel="stylesheet" href="https://prosemirror.net/css/editor.css">
<script src="https://prosemirror.net/examples/prosemirror.js"></script>
<script src="require-pm.js"></script>
<script src="index.js" defer></script>
<base href="http://prosemirror.net/">
<style>
img.dinosaur { height: 40px; vertical-align: bottom; border: 1px solid #0ae; border-radius: 4px; background: #ddf6ff }
</style>
</head>
<body>
<div id="editor"></div>
<div id="content" style="display: none">
<p>This is your dinosaur-enabled editor. The insert menu allows you
to insert dinosaurs.</p>
<p>This paragraph <img class="dinosaur" dino-type="stegosaurus">, for example,
<img class="dinosaur" dino-type="triceratops">
is full <img class="dinosaur" dino-type="tyrannosaurus" data-some="thing"> of
dinosaurs.</p>
<p>Dinosaur nodes can be selected, copied, pasted, dragged, and so on.</p>
</div>
<script type="text/javascript">
// The supported types of dinosaurs.
const dinos = ["brontosaurus", "stegosaurus", "triceratops",
"tyrannosaurus", "pterodactyl"]
const dinoNodeSpec = {
// Dinosaurs have one attribute, their type, which must be one of
// the types defined above.
// Brontosaurs are still the default dino.
attrs: {type: {default: "brontosaurus"}},
inline: true,
group: "inline",
draggable: true,
// These nodes are rendered as images with a `dino-type` attribute.
// There are pictures for all dino types under /img/dino/.
toDOM: node => ["img", {"dino-type": node.attrs.type,
src: "/img/dino/" + node.attrs.type + ".png",
title: node.attrs.type,
class: "dinosaur"}],
// When parsing, such an image, if its type matches one of the known
// types, is converted to a dino node.
parseDOM: [{
tag: "img[dino-type]",
getAttrs: dom => {
let type = dom.getAttribute("dino-type")
return dinos.indexOf(type) > -1 ? {type} : false
}
}]
}
const {Schema, DOMParser} = require("prosemirror-model")
const {schema} = require("prosemirror-schema-basic")
const dinoSchema = new Schema({
nodes: schema.spec.nodes.addBefore("image", "dino", dinoNodeSpec),
marks: schema.spec.marks
})
let content = document.querySelector("#content")
let startDoc = DOMParser.fromSchema(dinoSchema).parse(content)
let dinoType = dinoSchema.nodes.dino
function insertDino(type) {
return function(state, dispatch) {
let {$from} = state.selection, index = $from.index()
if (!$from.parent.canReplaceWith(index, index, dinoType))
return false
if (dispatch)
dispatch(state.tr.replaceSelectionWith(dinoType.create({type})))
return true
}
}
const {MenuItem} = require("prosemirror-menu")
const {buildMenuItems} = require("prosemirror-example-setup")
// Ask example-setup to build its basic menu
let menu = buildMenuItems(dinoSchema)
// Add a dino-inserting item for each type of dino
dinos.forEach(name => menu.insertMenu.content.push(new MenuItem({
title: "Insert " + name,
label: name.charAt(0).toUpperCase() + name.slice(1),
enable(state) { return insertDino(name)(state) },
run: insertDino(name)
})))
const {EditorState} = require("prosemirror-state")
const {EditorView} = require("prosemirror-view")
const {exampleSetup} = require("prosemirror-example-setup")
window.view = new EditorView(document.querySelector("#editor"), {
state: EditorState.create({
doc: startDoc,
// Pass exampleSetup our schema and the menu we created
plugins: exampleSetup({schema: dinoSchema, menuContent: menu.fullMenu})
})
})
</script>
</body>
</html>