feat: Add @mention support to comments (#5001)
* Refactor, remove confusing 'packages' language * Basic notifications when mentioned in comment * fix: Incorrect trimming of comments * test
This commit is contained in:
117
shared/editor/nodes/index.ts
Normal file
117
shared/editor/nodes/index.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import Extension from "../lib/Extension";
|
||||
import Bold from "../marks/Bold";
|
||||
import Code from "../marks/Code";
|
||||
import Comment from "../marks/Comment";
|
||||
import Highlight from "../marks/Highlight";
|
||||
import Italic from "../marks/Italic";
|
||||
import Link from "../marks/Link";
|
||||
import Mark from "../marks/Mark";
|
||||
import TemplatePlaceholder from "../marks/Placeholder";
|
||||
import Strikethrough from "../marks/Strikethrough";
|
||||
import Underline from "../marks/Underline";
|
||||
import BlockMenuTrigger from "../plugins/BlockMenuTrigger";
|
||||
import ClipboardTextSerializer from "../plugins/ClipboardTextSerializer";
|
||||
import DateTime from "../plugins/DateTime";
|
||||
import Folding from "../plugins/Folding";
|
||||
import History from "../plugins/History";
|
||||
import Keys from "../plugins/Keys";
|
||||
import MaxLength from "../plugins/MaxLength";
|
||||
import PasteHandler from "../plugins/PasteHandler";
|
||||
import Placeholder from "../plugins/Placeholder";
|
||||
import PreventTab from "../plugins/PreventTab";
|
||||
import SmartText from "../plugins/SmartText";
|
||||
import TrailingNode from "../plugins/TrailingNode";
|
||||
import Attachment from "./Attachment";
|
||||
import Blockquote from "./Blockquote";
|
||||
import BulletList from "./BulletList";
|
||||
import CheckboxItem from "./CheckboxItem";
|
||||
import CheckboxList from "./CheckboxList";
|
||||
import CodeBlock from "./CodeBlock";
|
||||
import CodeFence from "./CodeFence";
|
||||
import Doc from "./Doc";
|
||||
import Embed from "./Embed";
|
||||
import Emoji from "./Emoji";
|
||||
import HardBreak from "./HardBreak";
|
||||
import Heading from "./Heading";
|
||||
import HorizontalRule from "./HorizontalRule";
|
||||
import Image from "./Image";
|
||||
import ListItem from "./ListItem";
|
||||
import Math from "./Math";
|
||||
import MathBlock from "./MathBlock";
|
||||
import Mention from "./Mention";
|
||||
import Node from "./Node";
|
||||
import Notice from "./Notice";
|
||||
import OrderedList from "./OrderedList";
|
||||
import Paragraph from "./Paragraph";
|
||||
import Table from "./Table";
|
||||
import TableCell from "./TableCell";
|
||||
import TableHeadCell from "./TableHeadCell";
|
||||
import TableRow from "./TableRow";
|
||||
import Text from "./Text";
|
||||
|
||||
type Nodes = (typeof Node | typeof Mark | typeof Extension)[];
|
||||
|
||||
/**
|
||||
* The basic set of nodes that are used in the editor. This is used for simple
|
||||
* editors that need basic formatting.
|
||||
*/
|
||||
export const basicExtensions: Nodes = [
|
||||
Doc,
|
||||
Paragraph,
|
||||
Emoji,
|
||||
Text,
|
||||
Image,
|
||||
Bold,
|
||||
Code,
|
||||
Italic,
|
||||
Underline,
|
||||
Link,
|
||||
Strikethrough,
|
||||
History,
|
||||
SmartText,
|
||||
TrailingNode,
|
||||
PasteHandler,
|
||||
Placeholder,
|
||||
MaxLength,
|
||||
DateTime,
|
||||
Keys,
|
||||
ClipboardTextSerializer,
|
||||
];
|
||||
|
||||
/**
|
||||
* The full set of nodes that are used in the editor. This is used for rich
|
||||
* editors that need advanced formatting.
|
||||
*/
|
||||
export const richExtensions: Nodes = [
|
||||
...basicExtensions,
|
||||
HardBreak,
|
||||
CodeBlock,
|
||||
CodeFence,
|
||||
CheckboxList,
|
||||
CheckboxItem,
|
||||
Blockquote,
|
||||
BulletList,
|
||||
OrderedList,
|
||||
Embed,
|
||||
ListItem,
|
||||
Attachment,
|
||||
Notice,
|
||||
Heading,
|
||||
HorizontalRule,
|
||||
Table,
|
||||
TableCell,
|
||||
TableHeadCell,
|
||||
TableRow,
|
||||
Highlight,
|
||||
TemplatePlaceholder,
|
||||
Folding,
|
||||
BlockMenuTrigger,
|
||||
Math,
|
||||
MathBlock,
|
||||
PreventTab,
|
||||
];
|
||||
|
||||
/**
|
||||
* Add commenting and mentions to a set of nodes
|
||||
*/
|
||||
export const withComments = (nodes: Nodes) => [...nodes, Mention, Comment];
|
||||
@@ -1,2 +0,0 @@
|
||||
Packages are preselected collections of extensions that form the different types
|
||||
of editors within Outline.
|
||||
@@ -1,50 +0,0 @@
|
||||
import Extension from "../lib/Extension";
|
||||
import Bold from "../marks/Bold";
|
||||
import Code from "../marks/Code";
|
||||
import Italic from "../marks/Italic";
|
||||
import Link from "../marks/Link";
|
||||
import Mark from "../marks/Mark";
|
||||
import Strikethrough from "../marks/Strikethrough";
|
||||
import Underline from "../marks/Underline";
|
||||
import Doc from "../nodes/Doc";
|
||||
import Emoji from "../nodes/Emoji";
|
||||
import Image from "../nodes/Image";
|
||||
import Mention from "../nodes/Mention";
|
||||
import Node from "../nodes/Node";
|
||||
import Paragraph from "../nodes/Paragraph";
|
||||
import Text from "../nodes/Text";
|
||||
import ClipboardTextSerializer from "../plugins/ClipboardTextSerializer";
|
||||
import DateTime from "../plugins/DateTime";
|
||||
import History from "../plugins/History";
|
||||
import Keys from "../plugins/Keys";
|
||||
import MaxLength from "../plugins/MaxLength";
|
||||
import PasteHandler from "../plugins/PasteHandler";
|
||||
import Placeholder from "../plugins/Placeholder";
|
||||
import SmartText from "../plugins/SmartText";
|
||||
import TrailingNode from "../plugins/TrailingNode";
|
||||
|
||||
const basicPackage: (typeof Node | typeof Mark | typeof Extension)[] = [
|
||||
Doc,
|
||||
Paragraph,
|
||||
Emoji,
|
||||
Text,
|
||||
Image,
|
||||
Bold,
|
||||
Code,
|
||||
Italic,
|
||||
Underline,
|
||||
Link,
|
||||
Strikethrough,
|
||||
History,
|
||||
SmartText,
|
||||
TrailingNode,
|
||||
PasteHandler,
|
||||
Placeholder,
|
||||
MaxLength,
|
||||
DateTime,
|
||||
Keys,
|
||||
ClipboardTextSerializer,
|
||||
Mention,
|
||||
];
|
||||
|
||||
export default basicPackage;
|
||||
@@ -1,60 +0,0 @@
|
||||
import Extension from "../lib/Extension";
|
||||
import Highlight from "../marks/Highlight";
|
||||
import Mark from "../marks/Mark";
|
||||
import TemplatePlaceholder from "../marks/Placeholder";
|
||||
import Attachment from "../nodes/Attachment";
|
||||
import Blockquote from "../nodes/Blockquote";
|
||||
import BulletList from "../nodes/BulletList";
|
||||
import CheckboxItem from "../nodes/CheckboxItem";
|
||||
import CheckboxList from "../nodes/CheckboxList";
|
||||
import CodeBlock from "../nodes/CodeBlock";
|
||||
import CodeFence from "../nodes/CodeFence";
|
||||
import Embed from "../nodes/Embed";
|
||||
import HardBreak from "../nodes/HardBreak";
|
||||
import Heading from "../nodes/Heading";
|
||||
import HorizontalRule from "../nodes/HorizontalRule";
|
||||
import ListItem from "../nodes/ListItem";
|
||||
import Math from "../nodes/Math";
|
||||
import MathBlock from "../nodes/MathBlock";
|
||||
import Node from "../nodes/Node";
|
||||
import Notice from "../nodes/Notice";
|
||||
import OrderedList from "../nodes/OrderedList";
|
||||
import Table from "../nodes/Table";
|
||||
import TableCell from "../nodes/TableCell";
|
||||
import TableHeadCell from "../nodes/TableHeadCell";
|
||||
import TableRow from "../nodes/TableRow";
|
||||
import BlockMenuTrigger from "../plugins/BlockMenuTrigger";
|
||||
import Folding from "../plugins/Folding";
|
||||
import PreventTab from "../plugins/PreventTab";
|
||||
import basicPackage from "./basic";
|
||||
|
||||
const fullPackage: (typeof Node | typeof Mark | typeof Extension)[] = [
|
||||
...basicPackage,
|
||||
HardBreak,
|
||||
CodeBlock,
|
||||
CodeFence,
|
||||
CheckboxList,
|
||||
CheckboxItem,
|
||||
Blockquote,
|
||||
BulletList,
|
||||
OrderedList,
|
||||
Embed,
|
||||
ListItem,
|
||||
Attachment,
|
||||
Notice,
|
||||
Heading,
|
||||
HorizontalRule,
|
||||
Table,
|
||||
TableCell,
|
||||
TableHeadCell,
|
||||
TableRow,
|
||||
Highlight,
|
||||
TemplatePlaceholder,
|
||||
Folding,
|
||||
BlockMenuTrigger,
|
||||
Math,
|
||||
MathBlock,
|
||||
PreventTab,
|
||||
];
|
||||
|
||||
export default fullPackage;
|
||||
@@ -1,13 +0,0 @@
|
||||
import Extension from "../lib/Extension";
|
||||
import Comment from "../marks/Comment";
|
||||
import Mark from "../marks/Mark";
|
||||
import Node from "../nodes/Node";
|
||||
import fullPackage from "./full";
|
||||
|
||||
const fullWithCommentsPackage: (
|
||||
| typeof Node
|
||||
| typeof Mark
|
||||
| typeof Extension
|
||||
)[] = [...fullPackage, Comment];
|
||||
|
||||
export default fullWithCommentsPackage;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Node } from "prosemirror-model";
|
||||
import { Node, Schema } from "prosemirror-model";
|
||||
import textBetween from "@shared/editor/lib/textBetween";
|
||||
import headingToSlug from "../editor/lib/headingToSlug";
|
||||
|
||||
export type Heading = {
|
||||
@@ -25,6 +26,23 @@ export type Task = {
|
||||
};
|
||||
|
||||
export default class ProsemirrorHelper {
|
||||
/**
|
||||
* Returns the node as plain text.
|
||||
*
|
||||
* @param node The node to convert.
|
||||
* @param schema The schema to use.
|
||||
* @returns The document content as plain text without formatting.
|
||||
*/
|
||||
static toPlainText(node: Node, schema: Schema) {
|
||||
const textSerializers = Object.fromEntries(
|
||||
Object.entries(schema.nodes)
|
||||
.filter(([, node]) => node.spec.toPlainText)
|
||||
.map(([name, node]) => [name, node.spec.toPlainText])
|
||||
);
|
||||
|
||||
return textBetween(node, 0, node.content.size, textSerializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any empty paragraphs from the beginning and end of the document.
|
||||
*
|
||||
@@ -34,9 +52,11 @@ export default class ProsemirrorHelper {
|
||||
const first = doc.firstChild;
|
||||
const last = doc.lastChild;
|
||||
const firstIsEmpty =
|
||||
first?.type.name === "paragraph" && !first.textContent.trim();
|
||||
first &&
|
||||
ProsemirrorHelper.toPlainText(first, doc.type.schema).trim() === "";
|
||||
const lastIsEmpty =
|
||||
last?.type.name === "paragraph" && !last.textContent.trim();
|
||||
last &&
|
||||
ProsemirrorHelper.toPlainText(last, doc.type.schema).trim() === "";
|
||||
const firstIsLast = first === last;
|
||||
|
||||
return doc.cut(
|
||||
|
||||
Reference in New Issue
Block a user