Files
outline/shared/editor/extensions/BlockMenu.tsx
2023-05-24 19:24:05 -07:00

100 lines
3.0 KiB
TypeScript

import { PlusIcon } from "outline-icons";
import { Plugin } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import * as React from "react";
import ReactDOM from "react-dom";
import { SuggestionsMenuType } from "../plugins/Suggestions";
import { findParentNode } from "../queries/findParentNode";
import { EventType } from "../types";
import Suggestion from "./Suggestion";
export default class BlockMenu extends Suggestion {
get defaultOptions() {
return {
type: SuggestionsMenuType.Block,
openRegex: /^\/(\w+)?$/,
closeRegex: /(^(?!\/(\w+)?)(.*)$|^\/(([\w\W]+)\s.*|\s)$|^\/((\W)+)$)/,
};
}
get name() {
return "blockmenu";
}
get plugins() {
const button = document.createElement("button");
button.className = "block-menu-trigger";
button.type = "button";
ReactDOM.render(<PlusIcon />, button);
return [
...super.plugins,
new Plugin({
props: {
decorations: (state) => {
const parent = findParentNode(
(node) => node.type.name === "paragraph"
)(state.selection);
if (!parent) {
return;
}
const isTopLevel = state.selection.$from.depth === 1;
if (!isTopLevel) {
return;
}
const decorations: Decoration[] = [];
const isEmptyNode = parent && parent.node.content.size === 0;
const isSlash = parent && parent.node.textContent === "/";
if (isEmptyNode) {
decorations.push(
Decoration.widget(
parent.pos,
() => {
button.addEventListener("click", () => {
this.editor.events.emit(EventType.SuggestionsMenuOpen, {
type: SuggestionsMenuType.Block,
query: "",
});
});
return button;
},
{
key: "block-trigger",
}
)
);
const isEmptyDoc = state.doc.textContent === "";
if (!isEmptyDoc) {
decorations.push(
Decoration.node(
parent.pos,
parent.pos + parent.node.nodeSize,
{
class: "placeholder",
"data-empty-text": this.options.dictionary.newLineEmpty,
}
)
);
}
} else if (isSlash) {
decorations.push(
Decoration.node(parent.pos, parent.pos + parent.node.nodeSize, {
class: "placeholder",
"data-empty-text": ` ${this.options.dictionary.newLineWithSlash}`,
})
);
}
return DecorationSet.create(state.doc, decorations);
},
},
}),
];
}
}