=
isTouchDevice={!!isTouchDevice}
isLoading={!hasServerSynced && !hasServerConnectionFailed}
tabIndex={tabIndex}
+ flaggedExtensions={flaggedExtensions}
+ disabledExtensions={disabledExtensions}
/>
);
};
diff --git a/packages/editor/src/core/components/editors/document/editor.tsx b/packages/editor/src/core/components/editors/document/editor.tsx
index bf27c48e3fa..85285adea2a 100644
--- a/packages/editor/src/core/components/editors/document/editor.tsx
+++ b/packages/editor/src/core/components/editors/document/editor.tsx
@@ -82,6 +82,7 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
initialValue: value,
mentionHandler,
onChange,
+ embedHandler,
});
const editorContainerClassName = getEditorClassNames({
@@ -98,6 +99,8 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
editorContainerClassName={cn(editorContainerClassName, "document-editor")}
id={id}
isTouchDevice={!!isTouchDevice}
+ flaggedExtensions={flaggedExtensions}
+ disabledExtensions={disabledExtensions}
/>
);
};
diff --git a/packages/editor/src/core/components/editors/document/page-renderer.tsx b/packages/editor/src/core/components/editors/document/page-renderer.tsx
index 266476788c1..7a628b3b22a 100644
--- a/packages/editor/src/core/components/editors/document/page-renderer.tsx
+++ b/packages/editor/src/core/components/editors/document/page-renderer.tsx
@@ -5,7 +5,7 @@ import { cn } from "@plane/utils";
import { DocumentContentLoader, EditorContainer, EditorContentWrapper } from "@/components/editors";
import { AIFeaturesMenu, BlockMenu, EditorBubbleMenu } from "@/components/menus";
// types
-import { TAIHandler, TDisplayConfig } from "@/types";
+import { IEditorProps, TAIHandler, TDisplayConfig } from "@/types";
type Props = {
aiHandler?: TAIHandler;
@@ -18,6 +18,8 @@ type Props = {
isLoading?: boolean;
isTouchDevice: boolean;
tabIndex?: number;
+ flaggedExtensions?: IEditorProps["flaggedExtensions"];
+ disabledExtensions?: IEditorProps["disabledExtensions"];
};
export const PageRenderer = (props: Props) => {
@@ -32,6 +34,8 @@ export const PageRenderer = (props: Props) => {
isLoading,
isTouchDevice,
tabIndex,
+ flaggedExtensions,
+ disabledExtensions,
} = props;
return (
@@ -54,7 +58,11 @@ export const PageRenderer = (props: Props) => {
{editor.isEditable && !isTouchDevice && (
{bubbleMenuEnabled &&
}
-
+
)}
diff --git a/packages/editor/src/core/components/editors/editor-wrapper.tsx b/packages/editor/src/core/components/editors/editor-wrapper.tsx
index 51c3ea156dd..7de71bf6f46 100644
--- a/packages/editor/src/core/components/editors/editor-wrapper.tsx
+++ b/packages/editor/src/core/components/editors/editor-wrapper.tsx
@@ -41,6 +41,7 @@ export const EditorWrapper: React.FC
= (props) => {
placeholder,
tabIndex,
value,
+ embedHandler,
} = props;
const editor = useEditor({
@@ -65,6 +66,7 @@ export const EditorWrapper: React.FC = (props) => {
placeholder,
tabIndex,
value,
+ embedHandler,
});
const editorContainerClassName = getEditorClassNames({
diff --git a/packages/editor/src/core/components/editors/link-view-container.tsx b/packages/editor/src/core/components/editors/link-view-container.tsx
index c686c33ab61..5e839830e05 100644
--- a/packages/editor/src/core/components/editors/link-view-container.tsx
+++ b/packages/editor/src/core/components/editors/link-view-container.tsx
@@ -1,8 +1,12 @@
import { autoUpdate, flip, hide, shift, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
import { Editor, useEditorState } from "@tiptap/react";
import { FC, useCallback, useEffect, useRef, useState } from "react";
+
// components
import { LinkView, LinkViewProps } from "@/components/links";
+import { CORE_EXTENSIONS } from "@/constants/extension";
+// components
+import { getExtensionStorage } from "@/helpers/get-extension-storage";
type Props = {
editor: Editor;
@@ -18,7 +22,7 @@ export const LinkViewContainer: FC = ({ editor, containerRef }) => {
const editorState = useEditorState({
editor,
selector: ({ editor }: { editor: Editor }) => ({
- linkExtensionStorage: editor.storage.link,
+ linkExtensionStorage: getExtensionStorage(editor, CORE_EXTENSIONS.CUSTOM_LINK),
}),
});
diff --git a/packages/editor/src/core/components/menus/block-menu.tsx b/packages/editor/src/core/components/menus/block-menu.tsx
index 4f4fb64ac69..43c0193c715 100644
--- a/packages/editor/src/core/components/menus/block-menu.tsx
+++ b/packages/editor/src/core/components/menus/block-menu.tsx
@@ -4,9 +4,12 @@ import { useCallback, useEffect, useRef } from "react";
import tippy, { Instance } from "tippy.js";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
+import { IEditorProps } from "@/types";
type Props = {
editor: Editor;
+ flaggedExtensions?: IEditorProps["flaggedExtensions"];
+ disabledExtensions?: IEditorProps["disabledExtensions"];
};
export const BlockMenu = (props: Props) => {
diff --git a/packages/editor/src/core/extensions/custom-link/extension.tsx b/packages/editor/src/core/extensions/custom-link/extension.tsx
index 39c227b60f7..f9ae0d561dd 100644
--- a/packages/editor/src/core/extensions/custom-link/extension.tsx
+++ b/packages/editor/src/core/extensions/custom-link/extension.tsx
@@ -81,6 +81,7 @@ declare module "@tiptap/core" {
export type CustomLinkStorage = {
isPreviewOpen: boolean;
posToInsert: { from: number; to: number };
+ isBubbleMenuOpen: boolean;
};
export const CustomLinkExtension = Mark.create({
diff --git a/packages/editor/src/core/extensions/extensions.ts b/packages/editor/src/core/extensions/extensions.ts
index 9ca8f06ed52..eda64824ffb 100644
--- a/packages/editor/src/core/extensions/extensions.ts
+++ b/packages/editor/src/core/extensions/extensions.ts
@@ -29,7 +29,7 @@ import {
// plane editor extensions
import { CoreEditorAdditionalExtensions } from "@/plane-editor/extensions";
// types
-import type { IEditorProps } from "@/types";
+import type { IEditorProps, TEmbedConfig } from "@/types";
// local imports
import { CustomImageExtension } from "./custom-image/extension";
import { EmojiExtension } from "./emoji/extension";
@@ -45,9 +45,11 @@ type TArguments = Pick<
| "mentionHandler"
| "placeholder"
| "tabIndex"
+ | "embedHandler"
> & {
enableHistory: boolean;
editable: boolean;
+ embedHandler?: TEmbedConfig;
};
export const CoreEditorExtensions = (args: TArguments): Extensions => {
@@ -60,6 +62,7 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
mentionHandler,
placeholder,
tabIndex,
+ embedHandler,
editable,
} = args;
@@ -115,6 +118,7 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
disabledExtensions,
flaggedExtensions,
fileHandler,
+ embedHandler,
}),
];
diff --git a/packages/editor/src/core/hooks/use-collaborative-editor.ts b/packages/editor/src/core/hooks/use-collaborative-editor.ts
index cd08f5cbda9..46f7c4d82c3 100644
--- a/packages/editor/src/core/hooks/use-collaborative-editor.ts
+++ b/packages/editor/src/core/hooks/use-collaborative-editor.ts
@@ -80,6 +80,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorHookProps) =>
);
const editor = useEditor({
+ embedHandler,
disabledExtensions,
id,
editable,
diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts
index b5c6383cd7b..22ca31143cf 100644
--- a/packages/editor/src/core/hooks/use-editor.ts
+++ b/packages/editor/src/core/hooks/use-editor.ts
@@ -32,6 +32,7 @@ export const useEditor = (props: TEditorHookProps) => {
onAssetChange,
onChange,
onEditorFocus,
+ embedHandler,
onTransaction,
placeholder,
provider,
@@ -63,6 +64,7 @@ export const useEditor = (props: TEditorHookProps) => {
mentionHandler,
placeholder,
tabIndex,
+ embedHandler,
}),
...extensions,
],
diff --git a/packages/editor/src/core/plugins/drag-handle.ts b/packages/editor/src/core/plugins/drag-handle.ts
index 607837e4e54..3943d0c31d9 100644
--- a/packages/editor/src/core/plugins/drag-handle.ts
+++ b/packages/editor/src/core/plugins/drag-handle.ts
@@ -21,6 +21,7 @@ const generalSelectors = [
".image-component",
".image-upload-component",
".editor-callout-component",
+ ".editor-embed-component",
].join(", ");
const maxScrollSpeed = 20;
@@ -105,6 +106,11 @@ export const nodeDOMAtCoords = (coords: { x: number; y: number }) => {
continue;
}
+ // Skip elements inside .editor-embed-component
+ if (elem.closest(".editor-embed-component") && !elem.matches(".editor-embed-component")) {
+ continue;
+ }
+
// apply general selector
if (elem.matches(generalSelectors)) {
return elem;
diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts
index 2d04b967708..ed26bbbdac7 100644
--- a/packages/editor/src/core/types/editor.ts
+++ b/packages/editor/src/core/types/editor.ts
@@ -2,6 +2,7 @@ import type { Content, Extensions, JSONContent, RawCommands } from "@tiptap/core
import type { MarkType, NodeType } from "@tiptap/pm/model";
import type { Selection } from "@tiptap/pm/state";
import type { EditorProps, EditorView } from "@tiptap/pm/view";
+import type { NodeViewProps as TNodeViewProps } from "@tiptap/react";
// extension types
import type { TTextAlign } from "@/extensions";
// types
@@ -48,7 +49,8 @@ export type TEditorCommands =
| "text-align"
| "callout"
| "attachment"
- | "emoji";
+ | "emoji"
+ | "external-embed";
export type TCommandExtraProps = {
image: {
@@ -138,6 +140,7 @@ export type IEditorProps = {
editorClassName?: string;
editorProps?: EditorProps;
extensions?: Extensions;
+ embedHandler?: TEmbedConfig;
flaggedExtensions: TExtensions[];
fileHandler: TFileHandler;
forwardedRef?: React.MutableRefObject;
@@ -191,3 +194,5 @@ export type EditorEvents = {
destroy: never;
ready: { height: number };
};
+
+export type NodeViewProps = TNodeViewProps;
diff --git a/packages/editor/src/core/types/hook.ts b/packages/editor/src/core/types/hook.ts
index 6e8dd1ee2b0..0376fc14db6 100644
--- a/packages/editor/src/core/types/hook.ts
+++ b/packages/editor/src/core/types/hook.ts
@@ -13,6 +13,7 @@ type TCoreHookProps = Pick<
| "handleEditorReady"
| "isTouchDevice"
| "onEditorFocus"
+ | "embedHandler"
>;
export type TEditorHookProps = TCoreHookProps &
diff --git a/packages/utils/src/string.ts b/packages/utils/src/string.ts
index 6ffb4ea0411..062a162abec 100644
--- a/packages/utils/src/string.ts
+++ b/packages/utils/src/string.ts
@@ -174,7 +174,7 @@ export const isCommentEmpty = (comment: string | undefined): boolean => {
return (
comment?.trim() === "" ||
comment === "" ||
- isEmptyHtmlString(comment ?? "", ["img", "mention-component", "image-component"])
+ isEmptyHtmlString(comment ?? "", ["img", "mention-component", "image-component", "embed-component"])
);
};