[WIKI-484] chore: update editor packages (#7265)

This commit is contained in:
Aaryan Khandelwal 2025-07-21 19:25:49 +05:30 committed by GitHub
parent 4c3af7f8a1
commit 5c22a6cecc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 483 additions and 567 deletions

View file

@ -26,8 +26,8 @@
"@hocuspocus/server": "^2.15.0",
"@plane/editor": "*",
"@plane/types": "*",
"@tiptap/core": "2.10.4",
"@tiptap/html": "2.11.0",
"@tiptap/core": "^2.22.3",
"@tiptap/html": "^2.22.3",
"axios": "^1.8.3",
"compression": "1.8.1",
"cors": "^2.8.5",

View file

@ -29,7 +29,8 @@
"@babel/helpers": "7.26.10",
"@babel/runtime": "7.26.10",
"chokidar": "3.6.0",
"tar-fs": "3.0.9"
"tar-fs": "3.0.9",
"prosemirror-view": "1.40.0"
},
"packageManager": "yarn@1.22.22"
}

View file

@ -45,33 +45,31 @@
"@plane/types": "*",
"@plane/ui": "*",
"@plane/utils": "*",
"@tiptap/core": "2.10.4",
"@tiptap/extension-blockquote": "2.10.4",
"@tiptap/extension-character-count": "2.11.0",
"@tiptap/extension-collaboration": "2.11.0",
"@tiptap/core": "^2.22.3",
"@tiptap/extension-blockquote": "^2.22.3",
"@tiptap/extension-character-count": "^2.22.3",
"@tiptap/extension-collaboration": "^2.22.3",
"@tiptap/extension-emoji": "^2.22.3",
"@tiptap/extension-image": "2.11.0",
"@tiptap/extension-list-item": "2.11.0",
"@tiptap/extension-mention": "2.11.0",
"@tiptap/extension-placeholder": "2.11.0",
"@tiptap/extension-task-item": "2.11.0",
"@tiptap/extension-task-list": "2.11.0",
"@tiptap/extension-text-align": "2.11.0",
"@tiptap/extension-text-style": "2.11.0",
"@tiptap/extension-underline": "2.11.0",
"@tiptap/html": "2.11.0",
"@tiptap/pm": "2.11.0",
"@tiptap/react": "2.11.0",
"@tiptap/starter-kit": "2.11.0",
"@tiptap/suggestion": "2.11.0",
"class-variance-authority": "^0.7.0",
"@tiptap/extension-image": "^2.22.3",
"@tiptap/extension-list-item": "^2.22.3",
"@tiptap/extension-mention": "^2.22.3",
"@tiptap/extension-placeholder": "^2.22.3",
"@tiptap/extension-task-item": "^2.22.3",
"@tiptap/extension-task-list": "^2.22.3",
"@tiptap/extension-text-align": "^2.22.3",
"@tiptap/extension-text-style": "^2.22.3",
"@tiptap/extension-underline": "^2.22.3",
"@tiptap/html": "^2.22.3",
"@tiptap/pm": "^2.22.3",
"@tiptap/react": "^2.22.3",
"@tiptap/starter-kit": "^2.22.3",
"@tiptap/suggestion": "^2.22.3",
"highlight.js": "^11.8.0",
"jsx-dom-cjs": "^8.0.3",
"linkifyjs": "^4.1.3",
"lowlight": "^3.0.0",
"lucide-react": "^0.469.0",
"prosemirror-codemark": "^0.4.2",
"prosemirror-utils": "^1.2.2",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.10",
"uuid": "^10.0.0",

View file

@ -10,14 +10,14 @@ import { EAttributeNames, TCalloutBlockAttributes } from "./types";
// utils
import { updateStoredBackgroundColor } from "./utils";
type Props = NodeViewProps & {
export type CustomCalloutNodeViewProps = NodeViewProps & {
node: NodeViewProps["node"] & {
attrs: TCalloutBlockAttributes;
};
updateAttributes: (attrs: Partial<TCalloutBlockAttributes>) => void;
};
export const CustomCalloutBlock: React.FC<Props> = (props) => {
export const CustomCalloutBlock: React.FC<CustomCalloutNodeViewProps> = (props) => {
const { editor, node, updateAttributes } = props;
// states
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false);

View file

@ -1,6 +1,6 @@
import { findParentNodeClosestToPos, Predicate, ReactNodeViewRenderer } from "@tiptap/react";
// extensions
import { CustomCalloutBlock } from "@/extensions";
import { CustomCalloutBlock, CustomCalloutNodeViewProps } from "@/extensions/callout";
// helpers
import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary";
// config
@ -63,6 +63,8 @@ export const CustomCalloutExtension = CustomCalloutExtensionConfig.extend({
},
addNodeView() {
return ReactNodeViewRenderer(CustomCalloutBlock);
return ReactNodeViewRenderer((props) => (
<CustomCalloutBlock {...props} node={props.node as CustomCalloutNodeViewProps["node"]} />
));
},
});

View file

@ -1,6 +1,6 @@
import { ReactNodeViewRenderer } from "@tiptap/react";
// extensions
import { CustomCalloutBlock } from "@/extensions";
import { CustomCalloutBlock, CustomCalloutNodeViewProps } from "@/extensions/callout";
// config
import { CustomCalloutExtensionConfig } from "./extension-config";
@ -9,6 +9,8 @@ export const CustomCalloutReadOnlyExtension = CustomCalloutExtensionConfig.exten
draggable: false,
addNodeView() {
return ReactNodeViewRenderer(CustomCalloutBlock);
return ReactNodeViewRenderer((props) => (
<CustomCalloutBlock {...props} node={props.node as CustomCalloutNodeViewProps["node"]} />
));
},
});

View file

@ -8,7 +8,7 @@ import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-par
// types
import type { TFileHandler, TReadOnlyFileHandler } from "@/types";
// local imports
import { CustomImageNodeView } from "./components/node-view";
import { CustomImageNodeView, CustomImageNodeViewProps } from "./components/node-view";
import { CustomImageExtensionConfig } from "./extension-config";
import { getImageComponentImageFileMap } from "./utils";
@ -116,7 +116,9 @@ export const CustomImageExtension = (props: Props) => {
},
addNodeView() {
return ReactNodeViewRenderer(CustomImageNodeView);
return ReactNodeViewRenderer((props) => (
<CustomImageNodeView {...props} node={props.node as CustomImageNodeViewProps["node"]} />
));
},
});
};

View file

@ -122,6 +122,5 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
);
}
// @ts-expect-error tiptap types are incorrect
return extensions;
};

View file

@ -4,7 +4,7 @@ import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-par
// types
import type { TFileHandler, TReadOnlyFileHandler } from "@/types";
// local imports
import { CustomImageNodeView } from "../custom-image/components/node-view";
import { CustomImageNodeView, CustomImageNodeViewProps } from "../custom-image/components/node-view";
import { ImageExtensionConfig } from "./extension-config";
export type ImageExtensionStorage = {
@ -47,7 +47,9 @@ export const ImageExtension = (props: Props) => {
// render custom image node
addNodeView() {
return ReactNodeViewRenderer(CustomImageNodeView);
return ReactNodeViewRenderer((props) => (
<CustomImageNodeView {...props} node={props.node as CustomImageNodeViewProps["node"]} />
));
},
});
};

View file

@ -4,7 +4,7 @@ import { TMentionHandler } from "@/types";
// extension config
import { CustomMentionExtensionConfig } from "./extension-config";
// node view
import { MentionNodeView } from "./mention-node-view";
import { MentionNodeView, MentionNodeViewProps } from "./mention-node-view";
// utils
import { renderMentionsDropdown } from "./utils";
@ -20,7 +20,9 @@ export const CustomMentionExtension = (props: TMentionHandler) => {
},
addNodeView() {
return ReactNodeViewRenderer(MentionNodeView);
return ReactNodeViewRenderer((props) => (
<MentionNodeView {...props} node={props.node as MentionNodeViewProps["node"]} />
));
},
}).configure({
suggestion: {

View file

@ -4,17 +4,18 @@ import { TMentionExtensionOptions } from "./extension-config";
// extension types
import { EMentionComponentAttributeNames, TMentionComponentAttributes } from "./types";
type Props = NodeViewProps & {
export type MentionNodeViewProps = NodeViewProps & {
node: NodeViewProps["node"] & {
attrs: TMentionComponentAttributes;
};
};
export const MentionNodeView = (props: Props) => {
export const MentionNodeView: React.FC<MentionNodeViewProps> = (props) => {
const {
extension,
node: { attrs },
} = props;
return (
<NodeViewWrapper className="mention-component inline w-fit">
{(extension.options as TMentionExtensionOptions).renderComponent({

View file

@ -98,6 +98,5 @@ export const CoreReadOnlyEditorExtensions = (props: Props): Extensions => {
);
}
// @ts-expect-error tiptap types are incorrect
return extensions;
};

View file

@ -40,10 +40,6 @@ const Command = Extension.create<SlashCommandOptions>({
return false;
}
if (editor.isActive(CORE_EXTENSIONS.TABLE)) {
return false;
}
return true;
},
},

View file

@ -1,4 +1,4 @@
import { Editor, Range } from "@tiptap/core";
import type { Editor, Range } from "@tiptap/core";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
@ -14,21 +14,16 @@ export const setText = (editor: Editor, range?: Range) => {
export const toggleHeading = (editor: Editor, level: 1 | 2 | 3 | 4 | 5 | 6, range?: Range) => {
if (range) editor.chain().focus().deleteRange(range).setNode(CORE_EXTENSIONS.HEADING, { level }).run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleHeading({ level }).run();
};
export const toggleBold = (editor: Editor, range?: Range) => {
// @ts-expect-error tiptap types are incorrect
if (range) editor.chain().focus().deleteRange(range).toggleBold().run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleBold().run();
};
export const toggleItalic = (editor: Editor, range?: Range) => {
// @ts-expect-error tiptap types are incorrect
if (range) editor.chain().focus().deleteRange(range).toggleItalic().run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleItalic().run();
};
@ -67,16 +62,12 @@ export const toggleCodeBlock = (editor: Editor, range?: Range) => {
};
export const toggleOrderedList = (editor: Editor, range?: Range) => {
// @ts-expect-error tiptap types are incorrect
if (range) editor.chain().focus().deleteRange(range).toggleOrderedList().run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleOrderedList().run();
};
export const toggleBulletList = (editor: Editor, range?: Range) => {
// @ts-expect-error tiptap types are incorrect
if (range) editor.chain().focus().deleteRange(range).toggleBulletList().run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleBulletList().run();
};
@ -86,9 +77,7 @@ export const toggleTaskList = (editor: Editor, range?: Range) => {
};
export const toggleStrike = (editor: Editor, range?: Range) => {
// @ts-expect-error tiptap types are incorrect
if (range) editor.chain().focus().deleteRange(range).toggleStrike().run();
// @ts-expect-error tiptap types are incorrect
else editor.chain().focus().toggleStrike().run();
};

View file

@ -13,9 +13,7 @@ import {
const RICH_TEXT_EDITOR_EXTENSIONS = CoreEditorExtensionsWithoutProps;
const DOCUMENT_EDITOR_EXTENSIONS = [...CoreEditorExtensionsWithoutProps, ...DocumentEditorExtensionsWithoutProps];
// editor schemas
// @ts-expect-error tiptap types are incorrect
const richTextEditorSchema = getSchema(RICH_TEXT_EDITOR_EXTENSIONS);
// @ts-expect-error tiptap types are incorrect
const documentEditorSchema = getSchema(DOCUMENT_EDITOR_EXTENSIONS);
/**
@ -57,7 +55,6 @@ export const convertBase64StringToBinaryData = (document: string): ArrayBuffer =
*/
export const getBinaryDataFromRichTextEditorHTMLString = (descriptionHTML: string): Uint8Array => {
// convert HTML to JSON
// @ts-expect-error tiptap types are incorrect
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", RICH_TEXT_EDITOR_EXTENSIONS);
// convert JSON to Y.Doc format
const transformedData = prosemirrorJSONToYDoc(richTextEditorSchema, contentJSON, "default");
@ -73,7 +70,6 @@ export const getBinaryDataFromRichTextEditorHTMLString = (descriptionHTML: strin
*/
export const getBinaryDataFromDocumentEditorHTMLString = (descriptionHTML: string): Uint8Array => {
// convert HTML to JSON
// @ts-expect-error tiptap types are incorrect
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", DOCUMENT_EDITOR_EXTENSIONS);
// convert JSON to Y.Doc format
const transformedData = prosemirrorJSONToYDoc(documentEditorSchema, contentJSON, "default");
@ -102,7 +98,6 @@ export const getAllDocumentFormatsFromRichTextEditorBinaryData = (
const type = yDoc.getXmlFragment("default");
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, richTextEditorSchema).toJSON();
// convert to HTML
// @ts-expect-error tiptap types are incorrect
const contentHTML = generateHTML(contentJSON, RICH_TEXT_EDITOR_EXTENSIONS);
return {
@ -132,7 +127,6 @@ export const getAllDocumentFormatsFromDocumentEditorBinaryData = (
const type = yDoc.getXmlFragment("default");
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, documentEditorSchema).toJSON();
// convert to HTML
// @ts-expect-error tiptap types are incorrect
const contentHTML = generateHTML(contentJSON, DOCUMENT_EDITOR_EXTENSIONS);
return {

View file

@ -65,7 +65,9 @@ const isScrollable = (node: HTMLElement | SVGElement) => {
});
};
const getScrollParent = (node: HTMLElement | SVGElement) => {
const getScrollParent = (node: HTMLElement | SVGElement | null): Element | null => {
if (!node) return null;
if (scrollParentCache.has(node)) {
return scrollParentCache.get(node);
}
@ -137,32 +139,7 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
let isDraggedOutsideWindow: "top" | "bottom" | boolean = false;
let isMouseInsideWhileDragging = false;
let currentScrollSpeed = 0;
const handleClick = (event: MouseEvent, view: EditorView) => {
handleNodeSelection(event, view, false, options);
};
const handleDragStart = (event: DragEvent, view: EditorView) => {
const { listType: listTypeFromDragStart } = handleNodeSelection(event, view, true, options) ?? {};
if (listTypeFromDragStart) {
listType = listTypeFromDragStart;
}
isDragging = true;
lastClientY = event.clientY;
scroll();
};
const handleDragEnd = <TEvent extends DragEvent | FocusEvent>(event: TEvent, view?: EditorView) => {
event.preventDefault();
isDragging = false;
isMouseInsideWhileDragging = false;
if (scrollAnimationFrame) {
cancelAnimationFrame(scrollAnimationFrame);
scrollAnimationFrame = null;
}
view?.dom.classList.remove("dragging");
};
let dragHandleElement: HTMLElement | null = null;
function scroll() {
if (!isDragging) {
@ -199,7 +176,32 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
scrollAnimationFrame = requestAnimationFrame(scroll);
}
let dragHandleElement: HTMLElement | null = null;
const handleClick = (event: MouseEvent, view: EditorView) => {
handleNodeSelection(event, view, false, options);
};
const handleDragStart = (event: DragEvent, view: EditorView) => {
const { listType: listTypeFromDragStart } = handleNodeSelection(event, view, true, options) ?? {};
if (listTypeFromDragStart) {
listType = listTypeFromDragStart;
}
isDragging = true;
lastClientY = event.clientY;
scroll();
};
const handleDragEnd = <TEvent extends DragEvent | FocusEvent>(event: TEvent, view?: EditorView) => {
event.preventDefault();
isDragging = false;
isMouseInsideWhileDragging = false;
if (scrollAnimationFrame) {
cancelAnimationFrame(scrollAnimationFrame);
scrollAnimationFrame = null;
}
view?.dom.classList.remove("dragging");
};
// drag handle view actions
const showDragHandle = () => dragHandleElement?.classList.remove("drag-handle-hidden");
const hideDragHandle = () => {

883
yarn.lock

File diff suppressed because it is too large Load diff