chore: Cleanup editor menu handlers (#5174

* wip

* wip

* refactor
This commit is contained in:
Tom Moor
2023-04-10 18:50:21 -04:00
committed by GitHub
parent 70f3a998a4
commit 60dbad765a
15 changed files with 502 additions and 572 deletions

View File

@@ -1,19 +1,32 @@
import nameToEmoji from "gemoji/name-to-emoji.json";
import Token from "markdown-it/lib/token";
import { InputRule } from "prosemirror-inputrules";
import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model";
import { EditorState, TextSelection, Plugin } from "prosemirror-state";
import { run } from "../extensions/BlockMenuTrigger";
import {
NodeSpec,
Node as ProsemirrorNode,
NodeType,
Schema,
} from "prosemirror-model";
import { EditorState, TextSelection } from "prosemirror-state";
import Suggestion from "../extensions/Suggestion";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import isInCode from "../queries/isInCode";
import { SuggestionsMenuType } from "../plugins/Suggestions";
import emojiRule from "../rules/emoji";
import { Dispatch, EventType } from "../types";
import Node from "./Node";
import { Dispatch } from "../types";
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 Emoji extends Suggestion {
get type() {
return "node";
}
get defaultOptions() {
return {
type: SuggestionsMenuType.Emoji,
openRegex: /(?:^|\s):([0-9a-zA-Z_+-]+)?$/,
closeRegex: /(?:^|\s):(([0-9a-zA-Z_+-]*\s+)|(\s+[0-9a-zA-Z_+-]+)|[^0-9a-zA-Z_+-]+)$/,
enabledInTable: true,
};
}
export default class Emoji extends Node {
get name() {
return "emoji";
}
@@ -63,58 +76,7 @@ export default class Emoji extends Node {
return [emojiRule];
}
get plugins() {
return [
new Plugin({
props: {
handleClick: () => {
this.editor.events.emit(EventType.emojiMenuClose);
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.editor.events.emit(EventType.emojiMenuOpen, match[1]);
} else {
this.editor.events.emit(EventType.emojiMenuClose);
}
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
match ? true : null
);
}
return false;
},
},
}),
];
}
commands({ type }: { type: NodeType }) {
commands({ type }: { type: NodeType; schema: Schema }) {
return (attrs: Record<string, string>) => (
state: EditorState,
dispatch: Dispatch
@@ -135,50 +97,6 @@ export default class Emoji extends Node {
};
}
inputRules({ type }: { type: NodeType }): InputRule[] {
return [
new InputRule(/^:([a-zA-Z0-9_+-]+):$/, (state, match, start, end) => {
const [okay, markup] = match;
const { tr } = state;
if (okay) {
tr.replaceWith(
start - 1,
end,
type.create({
"data-name": markup,
markup,
})
);
}
return tr;
}),
// main regex should match only:
// :word
new InputRule(OPEN_REGEX, (state, match) => {
if (
match &&
state.selection.$from.parent.type.name === "paragraph" &&
!isInCode(state)
) {
this.editor.events.emit(EventType.emojiMenuOpen, 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.editor.events.emit(EventType.emojiMenuClose);
}
return null;
}),
];
}
toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) {
const name = node.attrs["data-name"];
if (name) {