chore: Editor refactor (#3286)
* cleanup * add context * EventEmitter allows removal of toolbar props from extensions * Move to 'packages' of extensions Remove EmojiTrigger extension * types * iteration * fix render flashing * fix: Missing nodes in collection descriptions
This commit is contained in:
@@ -7,6 +7,7 @@ import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
||||
import * as React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import Extension from "../lib/Extension";
|
||||
import { EventType } from "../types";
|
||||
|
||||
const MAX_MATCH = 500;
|
||||
const OPEN_REGEX = /^\/(\w+)?$/;
|
||||
@@ -65,7 +66,7 @@ export default class BlockMenuTrigger extends Extension {
|
||||
new Plugin({
|
||||
props: {
|
||||
handleClick: () => {
|
||||
this.options.onClose();
|
||||
this.editor.events.emit(EventType.blockMenuClose);
|
||||
return false;
|
||||
},
|
||||
handleKeyDown: (view, event) => {
|
||||
@@ -79,9 +80,9 @@ export default class BlockMenuTrigger extends Extension {
|
||||
const { pos } = view.state.selection.$from;
|
||||
return run(view, pos, pos, OPEN_REGEX, (state, match) => {
|
||||
if (match) {
|
||||
this.options.onOpen(match[1]);
|
||||
this.editor.events.emit(EventType.blockMenuOpen, match[1]);
|
||||
} else {
|
||||
this.options.onClose();
|
||||
this.editor.events.emit(EventType.blockMenuClose);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -125,7 +126,7 @@ export default class BlockMenuTrigger extends Extension {
|
||||
decorations.push(
|
||||
Decoration.widget(parent.pos, () => {
|
||||
button.addEventListener("click", () => {
|
||||
this.options.onOpen("");
|
||||
this.editor.events.emit(EventType.blockMenuOpen, "");
|
||||
});
|
||||
return button;
|
||||
})
|
||||
@@ -176,7 +177,7 @@ export default class BlockMenuTrigger extends Extension {
|
||||
state.selection.$from.parent.type.name === "paragraph" &&
|
||||
!isInTable(state)
|
||||
) {
|
||||
this.options.onOpen(match[1]);
|
||||
this.editor.events.emit(EventType.blockMenuOpen, match[1]);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
@@ -186,7 +187,7 @@ export default class BlockMenuTrigger extends Extension {
|
||||
// /word<space>
|
||||
new InputRule(CLOSE_REGEX, (state, match) => {
|
||||
if (match) {
|
||||
this.options.onClose();
|
||||
this.editor.events.emit(EventType.blockMenuClose);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import { InputRule } from "prosemirror-inputrules";
|
||||
import { Plugin } from "prosemirror-state";
|
||||
import Extension from "../lib/Extension";
|
||||
import isInCode from "../queries/isInCode";
|
||||
import { run } from "./BlockMenuTrigger";
|
||||
|
||||
const OPEN_REGEX = /(?:^|\s):([0-9a-zA-Z_+-]+)?$/;
|
||||
const CLOSE_REGEX = /(?:^|\s):(([0-9a-zA-Z_+-]*\s+)|(\s+[0-9a-zA-Z_+-]+)|[^0-9a-zA-Z_+-]+)$/;
|
||||
|
||||
export default class EmojiTrigger extends Extension {
|
||||
get name() {
|
||||
return "emojimenu";
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
props: {
|
||||
handleClick: () => {
|
||||
this.options.onClose();
|
||||
return false;
|
||||
},
|
||||
handleKeyDown: (view, event) => {
|
||||
// Prosemirror input rules are not triggered on backspace, however
|
||||
// we need them to be evaluted for the filter trigger to work
|
||||
// correctly. This additional handler adds inputrules-like handling.
|
||||
if (event.key === "Backspace") {
|
||||
// timeout ensures that the delete has been handled by prosemirror
|
||||
// and any characters removed, before we evaluate the rule.
|
||||
setTimeout(() => {
|
||||
const { pos } = view.state.selection.$from;
|
||||
return run(view, pos, pos, OPEN_REGEX, (state, match) => {
|
||||
if (match) {
|
||||
this.options.onOpen(match[1]);
|
||||
} else {
|
||||
this.options.onClose();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// If the query is active and we're navigating the block menu then
|
||||
// just ignore the key events in the editor itself until we're done
|
||||
if (
|
||||
event.key === "Enter" ||
|
||||
event.key === "ArrowUp" ||
|
||||
event.key === "ArrowDown" ||
|
||||
event.key === "Tab"
|
||||
) {
|
||||
const { pos } = view.state.selection.$from;
|
||||
|
||||
return run(view, pos, pos, OPEN_REGEX, (state, match) => {
|
||||
// just tell Prosemirror we handled it and not to do anything
|
||||
return match ? true : null;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
inputRules() {
|
||||
return [
|
||||
// main regex should match only:
|
||||
// :word
|
||||
new InputRule(OPEN_REGEX, (state, match) => {
|
||||
if (
|
||||
match &&
|
||||
state.selection.$from.parent.type.name === "paragraph" &&
|
||||
!isInCode(state)
|
||||
) {
|
||||
this.options.onOpen(match[1]);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
// invert regex should match some of these scenarios:
|
||||
// :<space>word
|
||||
// :<space>
|
||||
// :word<space>
|
||||
// :)
|
||||
new InputRule(CLOSE_REGEX, (state, match) => {
|
||||
if (match) {
|
||||
this.options.onClose();
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ export default class Keys extends Extension {
|
||||
|
||||
keys(): Record<string, Command> {
|
||||
const onCancel = () => {
|
||||
if (this.options.onCancel) {
|
||||
this.options.onCancel();
|
||||
if (this.editor.props.onCancel) {
|
||||
this.editor.props.onCancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -32,15 +32,15 @@ export default class Keys extends Extension {
|
||||
"Mod-Escape": onCancel,
|
||||
"Shift-Escape": onCancel,
|
||||
"Mod-s": () => {
|
||||
if (this.options.onSave) {
|
||||
this.options.onSave();
|
||||
if (this.editor.props.onSave) {
|
||||
this.editor.props.onSave({ done: false });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"Mod-Enter": (state: EditorState) => {
|
||||
if (!isInCode(state) && this.options.onSaveAndExit) {
|
||||
this.options.onSaveAndExit();
|
||||
if (!isInCode(state) && this.editor.props.onSave) {
|
||||
this.editor.props.onSave({ done: true });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -52,10 +52,6 @@ export default class Keys extends Extension {
|
||||
return [
|
||||
new Plugin({
|
||||
props: {
|
||||
handleDOMEvents: {
|
||||
blur: this.options.onBlur,
|
||||
focus: this.options.onFocus,
|
||||
},
|
||||
// we can't use the keys bindings for this as we want to preventDefault
|
||||
// on the original keyboard event when handled
|
||||
handleKeyDown: (view, event) => {
|
||||
|
||||
Reference in New Issue
Block a user