-
Notifications
You must be signed in to change notification settings - Fork 249
Migration Guide: Super Editor = 0.2.x to Super Editor 0.3.0
super_editor
v0.3.0
introduces major breaking changes so that the logical editor forces all changes through a single pipeline, allowing for undo/redo, tagging, content conversions, and more.
The following describes how to migrate from super_editor
v0.2.x
to v0.3.0
.
At a high level, any place where a DocumentEditor
was used in v0.2.x
, a new Editor
object should be used in v0.3.0
.
In v0.2.x
a SuperEditor
only required a DocumentEditor
:
void initState() {
super.initState();
_myDocument = createDocument();
_myEditor = DocumentEditor(document: _myDocument);
}
Widget build(BuildContext context) {
return SuperEditor(
editor: _myEditor,
);
}
In v0.3.0
a SuperEditor
requires an Editor
, a Document
, and a DocumentComposer
.
Creating a new Editor
requires a MutableDocument
, and a MutableDocumentComposer
.
The Editor
has a lot of possible configuration for the typical use-case. Therefore, a top-level method is made available to create a typical Editor
.
void initState() {
super.initState();
_myDocument = createDocument();
_myComposer = MutableDocumentComposer();
_myEditor = createDefaultDocumentEditor(document: _myDocument, composer: _myComposer);
}
Widget build(BuildContext context) {
return SuperEditor(
editor: _myEditor,
document: _myDocument,
composer: _myComposer,
);
}
The concept of editor commands existed before v0.3.0
, but in v0.2.x
those commands could mutate anything at any time. The primary change in v0.3.0
is that all changes need to happen within a single pipeline.
A command in v0.2.x
would look something like:
class MyCommand implements EditorCommand {
MyCommand({
// args
});
// properties
@override
void execute(Document document, DocumentEditorTransaction transaction) {
// command behavior here
}
}
Also, some commands in v0.2.x
might be implemented in a functional way like this:
editor.executeCommand(
EditorCommandFunction(
(doc, transaction) {
// command behavior here
},
),
);
In v0.3.0
, a change should typically include an EditRequest
and an EditCommand
. There's no longer any support for an functional command.
A request/command pair in v0.3.0
should look something like this:
class MyRequest implements EditRequest {
MyRequest({
// args
});
// properties
}
class MyCommand implements EditCommand {
MyCommand({
// args
});
// properties
@override
void execute(EditContext context, CommandExecutor executor) {
// command behavior here
}
}
In most situations, an editor behavior should include both an EditRequest
and an EditCommand
.
An EditRequest
represents a desire for a change. An Editor
maps a given EditRequest
to some EditCommand
.
class MyRequest implements EditRequest {
const MyRequest({
// args
});
// properties
}
An EditCommand
mutates things to cause a desired change.
class MyCommand implements EditCommand {
const MyCommand({
// args
});
// properties
@override
void execute(EditContext context, CommandExecutor executor) {
// Most commands will require access to the mutable version of the Document, and the
// mutable version of the DocumentComposer.
//
// You can add your own `Editable`s to the `Editor`'s `EditContext` and access them
// just like this.
final document = context.find<MutableDocument>(Editor.documentKey);
final composer = context.find<MutableComposer>(Editor.composerKey);
// Mutate the document however you want.
// Mutate the composer however you want.
// After making desired changes, report those changes so that reactions and listeners
// can see what changed.
executor.logChanges([
DocumentEdit(
NodeChangeEvent(someNodeId),
),
SelectionChangeEvent(...),
]);
// You can also run other commands from this command, to re-use atomic behavior.
executor.executeCommand(
MyOtherCommand(...),
);
}
}
Map EditRequest
s to EditCommand
s in the Editor
constructor:
Editor(
requestHandlers: [
// ...
(request) => request is MyRequest
? MyCommand(
request.thing1,
request.thing2,
)
: null,
// ...
],
);