bustle / mobiledoc-kit

A toolkit for building WYSIWYG editors with Mobiledoc
https://bustle.github.io/mobiledoc-kit/demo/
MIT License
1.55k stars 150 forks source link

Allow to update editor content #527

Open StephanHoyer opened 7 years ago

StephanHoyer commented 7 years ago

I just need to update the editor content but preserve the selection state when possible

This is my current solution which works

var range = editor.range;
editor.destroy();
editor = new Mobiledoc.Editor({
  html: content,
  autofocus: true
});
editor.render(options.el);
editor.setRange(range);

@disordinary proposed me to use post insert and html parser to do this. So I did

var range = editor.range;
editor.run(function(postEditor) {
  var postRange = new Range(editor.post.headPosition(), editor.post.tailPosition());
  postEditor.deleteRange(postRange);
  postEditor.insertPost(editor.post.headPosition(), htmlParser.parse(content));
  postEditor.setRange(range);
});

This works partly, setting selection crashes.

Would be great to have one method that does this, like

editor.resetPost(htmlParser.parse(content))

I just created a non-working fiddle that tries to demonstrate the behaviour.

bantic commented 7 years ago

@StephanHoyer Thanks for this question. A Mobiledoc Range holds references (via its head and tail Positions) to existing sections in its internal Post data structure. That's why you can't use use previously-created Range to restore a selection with a new mobiledoc — that Range refers to sections from a previous instance of a Post, and the Editor will choke when trying to use them to rebuild the selection.

There is one place that something like this happens already in mobiledoc-kit, though: in undo/redo when we restore a previous state (called a Snapshot in the code, which is the combination of the mobiledoc and the range). In that case, because the Range's sections won't be present in the new instance of a Post, the Editor stores the snapshot with its section offset indexes instead. When restoring the Snapshot later, a new Range is created by looking up the sections at those indexes. The code is here: restore a Snapshot range and store the Snapshot range.

The Snapshot code isn't directly exposed by Mobiledoc (yet). A workaround that would work (although it would fairly heavily use private API) would be to pop a snapshot off of the editor's _editHistory._undoStack, create a new range using snapshot.getRange(newPost), and then restore it with postEditor.setRange(newRange). You could also mimic the logic used by Snapshot — find the section indexes for your current range's head and tail, then reset the post in the editor, then create a new range using the sections at those head/tail indexes.

Can you describe your use case? Why do you need to reset the contents of the editor? Perhaps there's another option where you can modify the existing content of the editor instead of resetting it. I would be in favor of adding a first-class method for "serializing" a Range so that it is somewhat portable and could then be re-applied to a new post. It won't always make sense to apply one posts's range to another post (if there are fewer sections in the second post, e.g.), but it seems useful to be able to at least have a way of storing that information in a simpler format.

StephanHoyer commented 7 years ago

Thanks for the answer.

We need this because we have our own undo/redo-stack. In order to set to a former version we need to reset the post.