diff --git a/apps/space/core/components/editor/index.ts b/apps/space/core/components/editor/index.ts index de164c8376d..69e2006e346 100644 --- a/apps/space/core/components/editor/index.ts +++ b/apps/space/core/components/editor/index.ts @@ -1,5 +1,4 @@ export * from "./embeds"; export * from "./lite-text-editor"; -export * from "./lite-text-read-only-editor"; export * from "./rich-text-editor"; export * from "./toolbar"; diff --git a/apps/space/core/components/editor/lite-text-editor.tsx b/apps/space/core/components/editor/lite-text-editor.tsx index 6342d934479..ebbfaecbec4 100644 --- a/apps/space/core/components/editor/lite-text-editor.tsx +++ b/apps/space/core/components/editor/lite-text-editor.tsx @@ -1,7 +1,7 @@ import React from "react"; // plane imports -import { EditorRefApi, ILiteTextEditorProps, LiteTextEditorWithRef, TFileHandler } from "@plane/editor"; -import { MakeOptional } from "@plane/types"; +import { type EditorRefApi, type ILiteTextEditorProps, LiteTextEditorWithRef, type TFileHandler } from "@plane/editor"; +import type { MakeOptional } from "@plane/types"; import { cn } from "@plane/utils"; // components import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor"; @@ -9,28 +9,34 @@ import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor"; import { getEditorFileHandlers } from "@/helpers/editor.helper"; import { isCommentEmpty } from "@/helpers/string.helper"; -interface LiteTextEditorWrapperProps - extends MakeOptional< - Omit, - "disabledExtensions" | "flaggedExtensions" - > { +type LiteTextEditorWrapperProps = MakeOptional< + Omit, + "disabledExtensions" | "flaggedExtensions" +> & { anchor: string; - workspaceId: string; isSubmitting?: boolean; showSubmitButton?: boolean; - uploadFile: TFileHandler["upload"]; -} + workspaceId: string; +} & ( + | { + editable: false; + } + | { + editable: true; + uploadFile: TFileHandler["upload"]; + } + ); export const LiteTextEditor = React.forwardRef((props, ref) => { const { anchor, containerClassName, - workspaceId, - isSubmitting = false, - showSubmitButton = true, - uploadFile, disabledExtensions, + editable, flaggedExtensions, + isSubmitting = false, + showSubmitButton = true, + workspaceId, ...rest } = props; function isMutableRefObject(ref: React.ForwardedRef): ref is React.MutableRefObject { @@ -46,9 +52,10 @@ export const LiteTextEditor = React.forwardRef "", workspaceId, })} mentionHandler={{ diff --git a/apps/space/core/components/editor/lite-text-read-only-editor.tsx b/apps/space/core/components/editor/lite-text-read-only-editor.tsx deleted file mode 100644 index 34662b7fc37..00000000000 --- a/apps/space/core/components/editor/lite-text-read-only-editor.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; -// plane imports -import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditorProps, LiteTextReadOnlyEditorWithRef } from "@plane/editor"; -import { MakeOptional } from "@plane/types"; -import { cn } from "@plane/utils"; -// components -import { EditorMentionsRoot } from "@/components/editor"; -// helpers -import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper"; -// store hooks -import { useMember } from "@/hooks/store"; - -type LiteTextReadOnlyEditorWrapperProps = MakeOptional< - Omit, - "disabledExtensions" | "flaggedExtensions" -> & { - anchor: string; - workspaceId: string; -}; - -export const LiteTextReadOnlyEditor = React.forwardRef( - ({ anchor, workspaceId, disabledExtensions, flaggedExtensions, ...props }, ref) => { - const { getMemberById } = useMember(); - - return ( - , - getMentionedEntityDetails: (id: string) => ({ - display_name: getMemberById(id)?.member__display_name ?? "", - }), - }} - {...props} - // overriding the customClassName to add relative class passed - containerClassName={cn(props.containerClassName, "relative p-2")} - /> - ); - } -); - -LiteTextReadOnlyEditor.displayName = "LiteTextReadOnlyEditor"; diff --git a/apps/space/core/components/issues/peek-overview/comment/add-comment.tsx b/apps/space/core/components/issues/peek-overview/comment/add-comment.tsx index d746d776622..b68a7ab96e0 100644 --- a/apps/space/core/components/issues/peek-overview/comment/add-comment.tsx +++ b/apps/space/core/components/issues/peek-overview/comment/add-comment.tsx @@ -75,6 +75,7 @@ export const AddComment: React.FC = observer((props) => { control={control} render={({ field: { value, onChange } }) => ( { if (currentUser) handleSubmit(onSubmit)(e); }} diff --git a/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx b/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx index 9adebeb2db4..db7c79454a5 100644 --- a/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -8,7 +8,7 @@ import { EditorRefApi } from "@plane/editor"; import { TIssuePublicComment } from "@plane/types"; import { getFileURL } from "@plane/utils"; // components -import { LiteTextEditor, LiteTextReadOnlyEditor } from "@/components/editor"; +import { LiteTextEditor } from "@/components/editor"; import { CommentReactions } from "@/components/issues/peek-overview"; // helpers import { timeAgo } from "@/helpers/date-time.helper"; @@ -102,6 +102,7 @@ export const CommentCard: React.FC = observer((props) => { name="comment_html" render={({ field: { onChange, value } }) => ( = observer((props) => {
- ; + readOnlyEditorRef: React.RefObject; showAccessSpecifier: boolean; workspaceId: string; workspaceSlug: string; @@ -67,7 +67,8 @@ export const CommentCardDisplay: React.FC = observer((props) => { )}
)} - void; workspaceId: string; workspaceSlug: string; @@ -75,6 +75,7 @@ export const CommentCardEditForm: React.FC = observer((props) => { }} > = observer((props) => { disabled = false, projectId, } = props; - const readOnlyEditorRef = useRef(null); // states const [isEditing, setIsEditing] = useState(false); + // refs + const readOnlyEditorRef = useRef(null); // derived values const workspaceId = comment?.workspace; diff --git a/apps/web/core/components/comments/comment-create.tsx b/apps/web/core/components/comments/comment-create.tsx index 450814f519d..4a83385add0 100644 --- a/apps/web/core/components/comments/comment-create.tsx +++ b/apps/web/core/components/comments/comment-create.tsx @@ -113,6 +113,7 @@ export const CommentCreate: FC = observer((props) => { control={control} render={({ field: { value, onChange } }) => (

"} diff --git a/apps/web/core/components/editor/lite-text/editor.tsx b/apps/web/core/components/editor/lite-text/editor.tsx index 088676358ee..d1e2017905e 100644 --- a/apps/web/core/components/editor/lite-text/editor.tsx +++ b/apps/web/core/components/editor/lite-text/editor.tsx @@ -1,18 +1,14 @@ import React, { useState } from "react"; -// plane constants +// plane imports import { EIssueCommentAccessSpecifier } from "@plane/constants"; -// plane editor -import { EditorRefApi, ILiteTextEditorProps, LiteTextEditorWithRef, TFileHandler } from "@plane/editor"; -// i18n +import { type EditorRefApi, type ILiteTextEditorProps, LiteTextEditorWithRef, type TFileHandler } from "@plane/editor"; import { useTranslation } from "@plane/i18n"; -// components -import { MakeOptional } from "@plane/types"; +import type { MakeOptional } from "@plane/types"; import { cn, isCommentEmpty } from "@plane/utils"; +// components import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor"; -// helpers // hooks import { useEditorConfig, useEditorMention } from "@/hooks/editor"; -// store hooks import { useMember } from "@/hooks/store"; // plane web hooks import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; @@ -20,11 +16,10 @@ import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { WorkspaceService } from "@/plane-web/services"; const workspaceService = new WorkspaceService(); -interface LiteTextEditorWrapperProps - extends MakeOptional< - Omit, - "disabledExtensions" | "flaggedExtensions" - > { +type LiteTextEditorWrapperProps = MakeOptional< + Omit, + "disabledExtensions" | "flaggedExtensions" +> & { workspaceSlug: string; workspaceId: string; projectId?: string; @@ -35,15 +30,23 @@ interface LiteTextEditorWrapperProps isSubmitting?: boolean; showToolbarInitially?: boolean; showToolbar?: boolean; - uploadFile: TFileHandler["upload"]; issue_id?: string; parentClassName?: string; -} +} & ( + | { + editable: false; + } + | { + editable: true; + uploadFile: TFileHandler["upload"]; + } + ); export const LiteTextEditor = React.forwardRef((props, ref) => { const { t } = useTranslation(); const { containerClassName, + editable, workspaceSlug, workspaceId, projectId, @@ -57,7 +60,6 @@ export const LiteTextEditor = React.forwardRef - await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", { + await workspaceService.searchEntity(workspaceSlug, { ...payload, - project_id: projectId?.toString() ?? "", - issue_id: issue_id, + project_id: projectId, + issue_id, }), }); // editor config @@ -94,10 +96,11 @@ export const LiteTextEditor = React.forwardRef "", workspaceId, workspaceSlug, })} @@ -107,8 +110,10 @@ export const LiteTextEditor = React.forwardRef , - getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), + renderComponent: EditorMentionsRoot, + getMentionedEntityDetails: (id) => ({ + display_name: getUserDetails(id)?.display_name ?? "", + }), }} placeholder={placeholder} containerClassName={cn(containerClassName, "relative")} diff --git a/apps/web/core/components/editor/lite-text/index.ts b/apps/web/core/components/editor/lite-text/index.ts index a53feeb8277..f73ee92ef6e 100644 --- a/apps/web/core/components/editor/lite-text/index.ts +++ b/apps/web/core/components/editor/lite-text/index.ts @@ -1,3 +1,2 @@ export * from "./editor"; -export * from "./read-only-editor"; export * from "./toolbar"; diff --git a/apps/web/core/components/editor/lite-text/read-only-editor.tsx b/apps/web/core/components/editor/lite-text/read-only-editor.tsx deleted file mode 100644 index e37b0c30ee7..00000000000 --- a/apps/web/core/components/editor/lite-text/read-only-editor.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from "react"; -// plane imports -import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditorProps, LiteTextReadOnlyEditorWithRef } from "@plane/editor"; -import { MakeOptional } from "@plane/types"; -// components -import { cn } from "@plane/utils"; -import { EditorMentionsRoot } from "@/components/editor"; -// helpers -// hooks -import { useEditorConfig } from "@/hooks/editor"; -// store hooks -import { useMember } from "@/hooks/store"; -// plane web hooks -import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; - -type LiteTextReadOnlyEditorWrapperProps = MakeOptional< - Omit, - "disabledExtensions" | "flaggedExtensions" -> & { - workspaceId: string; - workspaceSlug: string; - projectId?: string; -}; - -export const LiteTextReadOnlyEditor = React.forwardRef( - ({ workspaceId, workspaceSlug, projectId, disabledExtensions: additionalDisabledExtensions, ...props }, ref) => { - // store hooks - const { getUserDetails } = useMember(); - - // editor flaggings - const { liteText: liteTextEditorExtensions } = useEditorFlagging(workspaceSlug?.toString()); - // editor config - const { getReadOnlyEditorFileHandlers } = useEditorConfig(); - - return ( - , - getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), - }} - {...props} - // overriding the containerClassName to add relative class passed - containerClassName={cn(props.containerClassName, "relative p-2")} - /> - ); - } -); - -LiteTextReadOnlyEditor.displayName = "LiteTextReadOnlyEditor"; diff --git a/apps/web/core/components/editor/rich-text/editor.tsx b/apps/web/core/components/editor/rich-text/editor.tsx index 7349bfe48db..735d81e2313 100644 --- a/apps/web/core/components/editor/rich-text/editor.tsx +++ b/apps/web/core/components/editor/rich-text/editor.tsx @@ -1,14 +1,12 @@ import React, { forwardRef } from "react"; // plane imports -import { EditorRefApi, IRichTextEditorProps, RichTextEditorWithRef, TFileHandler } from "@plane/editor"; -import { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types"; -// components +import { type EditorRefApi, type IRichTextEditorProps, RichTextEditorWithRef, type TFileHandler } from "@plane/editor"; +import type { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types"; import { cn } from "@plane/utils"; +// components import { EditorMentionsRoot } from "@/components/editor"; -// helpers // hooks import { useEditorConfig, useEditorMention } from "@/hooks/editor"; -// store hooks import { useMember } from "@/hooks/store"; // plane web hooks import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; @@ -38,7 +36,7 @@ export const RichTextEditor = forwardRef , - getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), + renderComponent: EditorMentionsRoot, + getMentionedEntityDetails: (id) => ({ + display_name: getUserDetails(id)?.display_name ?? "", + }), }} {...rest} containerClassName={cn("relative pl-3 pb-3", containerClassName)} diff --git a/apps/web/core/components/editor/sticky-editor/editor.tsx b/apps/web/core/components/editor/sticky-editor/editor.tsx index f91cdd498f9..81149e41bf4 100644 --- a/apps/web/core/components/editor/sticky-editor/editor.tsx +++ b/apps/web/core/components/editor/sticky-editor/editor.tsx @@ -14,7 +14,10 @@ import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { StickyEditorToolbar } from "./toolbar"; interface StickyEditorWrapperProps - extends Omit { + extends Omit< + ILiteTextEditorProps, + "disabledExtensions" | "editable" | "flaggedExtensions" | "fileHandler" | "mentionHandler" + > { workspaceSlug: string; workspaceId: string; projectId?: string; @@ -67,6 +70,7 @@ export const StickyEditor = React.forwardRef { // file size const { maxFileSize } = useFileSize(); - const getReadOnlyEditorFileHandlers = useCallback( - (args: Pick): TReadOnlyFileHandler => { - const { projectId, workspaceId, workspaceSlug } = args; + const getEditorFileHandlers = useCallback( + (args: TArgs): TFileHandler => { + const { projectId, uploadFile, workspaceId, workspaceSlug } = args; return { + assetsUploadStatus: assetsUploadPercentage, + cancel: fileService.cancelUpload, checkIfAssetExists: async (assetId: string) => { const res = await fileService.checkIfAssetExists(workspaceSlug, assetId); return res?.exists ?? false; }, + delete: async (src: string) => { + if (src?.startsWith("http")) { + await fileService.deleteOldWorkspaceAsset(workspaceId, src); + } else { + await fileService.deleteNewAsset( + getEditorAssetSrc({ + assetId: src, + projectId, + workspaceSlug, + }) ?? "" + ); + } + }, getAssetDownloadSrc: async (path) => { if (!path) return ""; if (path?.startsWith("http")) { @@ -68,47 +82,16 @@ export const useEditorConfig = () => { await fileService.restoreNewAsset(workspaceSlug, src); } }, - }; - }, - [] - ); - - const getEditorFileHandlers = useCallback( - (args: TArgs): TFileHandler => { - const { projectId, uploadFile, workspaceId, workspaceSlug } = args; - - return { - ...getReadOnlyEditorFileHandlers({ - projectId, - workspaceId, - workspaceSlug, - }), - assetsUploadStatus: assetsUploadPercentage, upload: uploadFile, - delete: async (src: string) => { - if (src?.startsWith("http")) { - await fileService.deleteOldWorkspaceAsset(workspaceId, src); - } else { - await fileService.deleteNewAsset( - getEditorAssetSrc({ - assetId: src, - projectId, - workspaceSlug, - }) ?? "" - ); - } - }, - cancel: fileService.cancelUpload, validation: { maxFileSize, }, }; }, - [assetsUploadPercentage, getReadOnlyEditorFileHandlers, maxFileSize] + [assetsUploadPercentage, maxFileSize] ); return { getEditorFileHandlers, - getReadOnlyEditorFileHandlers, }; }; diff --git a/packages/editor/src/ce/extensions/core/index.ts b/packages/editor/src/ce/extensions/core/index.ts index 9ffc978c3f0..e121e50b065 100644 --- a/packages/editor/src/ce/extensions/core/index.ts +++ b/packages/editor/src/ce/extensions/core/index.ts @@ -1,2 +1 @@ export * from "./extensions"; -export * from "./read-only-extensions"; diff --git a/packages/editor/src/ce/extensions/core/read-only-extensions.ts b/packages/editor/src/ce/extensions/core/read-only-extensions.ts deleted file mode 100644 index 4f9306da302..00000000000 --- a/packages/editor/src/ce/extensions/core/read-only-extensions.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Extensions } from "@tiptap/core"; -// types -import type { IReadOnlyEditorProps } from "@/types"; - -export type TCoreReadOnlyEditorAdditionalExtensionsProps = Pick< - IReadOnlyEditorProps, - "disabledExtensions" | "flaggedExtensions" ->; - -export const CoreReadOnlyEditorAdditionalExtensions = ( - props: TCoreReadOnlyEditorAdditionalExtensionsProps -): Extensions => { - const {} = props; - return []; -}; diff --git a/packages/editor/src/ce/extensions/rich-text/extensions.tsx b/packages/editor/src/ce/extensions/rich-text-extensions.tsx similarity index 100% rename from packages/editor/src/ce/extensions/rich-text/extensions.tsx rename to packages/editor/src/ce/extensions/rich-text-extensions.tsx diff --git a/packages/editor/src/ce/extensions/rich-text/read-only-extensions.tsx b/packages/editor/src/ce/extensions/rich-text/read-only-extensions.tsx deleted file mode 100644 index 0b7cbc7306d..00000000000 --- a/packages/editor/src/ce/extensions/rich-text/read-only-extensions.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { AnyExtension, Extensions } from "@tiptap/core"; -// types -import { IReadOnlyEditorProps, TExtensions } from "@/types"; - -export type TRichTextReadOnlyEditorAdditionalExtensionsProps = Pick< - IReadOnlyEditorProps, - "disabledExtensions" | "flaggedExtensions" | "fileHandler" ->; - -/** - * Registry entry configuration for extensions - */ -export type TRichTextReadOnlyEditorAdditionalExtensionsRegistry = { - /** Determines if the extension should be enabled based on disabled extensions */ - isEnabled: (disabledExtensions: TExtensions[]) => boolean; - /** Returns the extension instance(s) when enabled */ - getExtension: (props: TRichTextReadOnlyEditorAdditionalExtensionsProps) => AnyExtension | undefined; -}; - -const extensionRegistry: TRichTextReadOnlyEditorAdditionalExtensionsRegistry[] = []; - -export const RichTextReadOnlyEditorAdditionalExtensions = (props: TRichTextReadOnlyEditorAdditionalExtensionsProps) => { - const { disabledExtensions } = props; - - const extensions: Extensions = extensionRegistry - .filter((config) => config.isEnabled(disabledExtensions)) - .map((config) => config.getExtension(props)) - .filter((extension): extension is AnyExtension => extension !== undefined); - - return extensions; -}; diff --git a/packages/editor/src/core/components/editors/index.ts b/packages/editor/src/core/components/editors/index.ts index 03ada4f7261..6248f407d95 100644 --- a/packages/editor/src/core/components/editors/index.ts +++ b/packages/editor/src/core/components/editors/index.ts @@ -4,4 +4,3 @@ export * from "./rich-text"; export * from "./editor-container"; export * from "./editor-content"; export * from "./editor-wrapper"; -export * from "./read-only-editor-wrapper"; diff --git a/packages/editor/src/core/components/editors/lite-text/editor.tsx b/packages/editor/src/core/components/editors/lite-text/editor.tsx index 66913057b8b..df89521ae68 100644 --- a/packages/editor/src/core/components/editors/lite-text/editor.tsx +++ b/packages/editor/src/core/components/editors/lite-text/editor.tsx @@ -19,7 +19,7 @@ const LiteTextEditor: React.FC = (props) => { return resolvedExtensions; }, [externalExtensions, disabledExtensions, onEnterKeyPress]); - return ; + return ; }; const LiteTextEditorWithRef = forwardRef((props, ref) => ( diff --git a/packages/editor/src/core/components/editors/lite-text/index.ts b/packages/editor/src/core/components/editors/lite-text/index.ts index b2ba8682a3c..8b1fd904bb0 100644 --- a/packages/editor/src/core/components/editors/lite-text/index.ts +++ b/packages/editor/src/core/components/editors/lite-text/index.ts @@ -1,2 +1 @@ export * from "./editor"; -export * from "./read-only-editor"; diff --git a/packages/editor/src/core/components/editors/lite-text/read-only-editor.tsx b/packages/editor/src/core/components/editors/lite-text/read-only-editor.tsx deleted file mode 100644 index 75e02791de2..00000000000 --- a/packages/editor/src/core/components/editors/lite-text/read-only-editor.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { forwardRef } from "react"; -// components -import { ReadOnlyEditorWrapper } from "@/components/editors"; -// types -import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditorProps } from "@/types"; - -const LiteTextReadOnlyEditorWithRef = forwardRef((props, ref) => ( - } /> -)); - -LiteTextReadOnlyEditorWithRef.displayName = "LiteReadOnlyEditorWithRef"; - -export { LiteTextReadOnlyEditorWithRef }; diff --git a/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx b/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx deleted file mode 100644 index b6abd1a6a50..00000000000 --- a/packages/editor/src/core/components/editors/read-only-editor-wrapper.tsx +++ /dev/null @@ -1,56 +0,0 @@ -// components -import { EditorContainer, EditorContentWrapper } from "@/components/editors"; -// constants -import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; -// helpers -import { getEditorClassNames } from "@/helpers/common"; -// hooks -import { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; -// types -import { IReadOnlyEditorProps } from "@/types"; - -export const ReadOnlyEditorWrapper = (props: IReadOnlyEditorProps) => { - const { - containerClassName, - disabledExtensions, - displayConfig = DEFAULT_DISPLAY_CONFIG, - editorClassName = "", - extensions, - fileHandler, - flaggedExtensions, - forwardedRef, - id, - initialValue, - mentionHandler, - } = props; - - const editor = useReadOnlyEditor({ - disabledExtensions, - editorClassName, - extensions, - fileHandler, - flaggedExtensions, - forwardedRef, - initialValue, - mentionHandler, - }); - - const editorContainerClassName = getEditorClassNames({ - containerClassName, - }); - - if (!editor) return null; - - return ( - -
- -
-
- ); -}; diff --git a/packages/editor/src/core/components/editors/rich-text/editor.tsx b/packages/editor/src/core/components/editors/rich-text/editor.tsx index 8544dcc8323..02c0db55d6c 100644 --- a/packages/editor/src/core/components/editors/rich-text/editor.tsx +++ b/packages/editor/src/core/components/editors/rich-text/editor.tsx @@ -5,7 +5,7 @@ import { EditorBubbleMenu } from "@/components/menus"; // extensions import { SideMenuExtension } from "@/extensions"; // plane editor imports -import { RichTextEditorAdditionalExtensions } from "@/plane-editor/extensions/rich-text/extensions"; +import { RichTextEditorAdditionalExtensions } from "@/plane-editor/extensions/rich-text-extensions"; // types import { EditorRefApi, IRichTextEditorProps } from "@/types"; diff --git a/packages/editor/src/core/extensions/callout/extension.tsx b/packages/editor/src/core/extensions/callout/extension.tsx index 0629a0f7704..9bc26df7240 100644 --- a/packages/editor/src/core/extensions/callout/extension.tsx +++ b/packages/editor/src/core/extensions/callout/extension.tsx @@ -1,6 +1,6 @@ import { findParentNodeClosestToPos, Predicate, ReactNodeViewRenderer } from "@tiptap/react"; // extensions -import { CustomCalloutBlock, CustomCalloutNodeViewProps } from "@/extensions/callout"; +import { CustomCalloutBlock, CustomCalloutNodeViewProps } from "@/extensions/callout/block"; // helpers import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary"; // config diff --git a/packages/editor/src/core/extensions/callout/index.ts b/packages/editor/src/core/extensions/callout/index.ts index 57915d313f6..2ce32da8ba5 100644 --- a/packages/editor/src/core/extensions/callout/index.ts +++ b/packages/editor/src/core/extensions/callout/index.ts @@ -1,3 +1 @@ -export * from "./block"; export * from "./extension"; -export * from "./read-only-extension"; diff --git a/packages/editor/src/core/extensions/callout/read-only-extension.tsx b/packages/editor/src/core/extensions/callout/read-only-extension.tsx deleted file mode 100644 index c1279ff8807..00000000000 --- a/packages/editor/src/core/extensions/callout/read-only-extension.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { ReactNodeViewRenderer } from "@tiptap/react"; -// extensions -import { CustomCalloutBlock, CustomCalloutNodeViewProps } from "@/extensions/callout"; -// config -import { CustomCalloutExtensionConfig } from "./extension-config"; - -export const CustomCalloutReadOnlyExtension = CustomCalloutExtensionConfig.extend({ - selectable: false, - draggable: false, - - addNodeView() { - return ReactNodeViewRenderer((props) => ( - - )); - }, -}); diff --git a/packages/editor/src/core/extensions/custom-image/extension.tsx b/packages/editor/src/core/extensions/custom-image/extension.tsx index da82c6a6f12..8aefd80d03d 100644 --- a/packages/editor/src/core/extensions/custom-image/extension.tsx +++ b/packages/editor/src/core/extensions/custom-image/extension.tsx @@ -6,14 +6,14 @@ import { ACCEPTED_IMAGE_MIME_TYPES } from "@/constants/config"; import { isFileValid } from "@/helpers/file"; import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary"; // types -import type { TFileHandler, TReadOnlyFileHandler } from "@/types"; +import type { TFileHandler } from "@/types"; // local imports import { CustomImageNodeView, CustomImageNodeViewProps } from "./components/node-view"; import { CustomImageExtensionConfig } from "./extension-config"; import { getImageComponentImageFileMap } from "./utils"; type Props = { - fileHandler: TFileHandler | TReadOnlyFileHandler; + fileHandler: TFileHandler; isEditable: boolean; }; diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 3cee3b10f03..0a61fd244fb 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -2,7 +2,7 @@ import { ReactNodeViewRenderer } from "@tiptap/react"; // helpers import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary"; // types -import type { TFileHandler, TReadOnlyFileHandler } from "@/types"; +import type { TFileHandler } from "@/types"; // local imports import { CustomImageNodeView, CustomImageNodeViewProps } from "../custom-image/components/node-view"; import { ImageExtensionConfig } from "./extension-config"; @@ -12,7 +12,7 @@ export type ImageExtensionStorage = { }; type Props = { - fileHandler: TFileHandler | TReadOnlyFileHandler; + fileHandler: TFileHandler; }; export const ImageExtension = (props: Props) => { diff --git a/packages/editor/src/core/extensions/index.ts b/packages/editor/src/core/extensions/index.ts index c3a8e5d5c7a..48692c09181 100644 --- a/packages/editor/src/core/extensions/index.ts +++ b/packages/editor/src/core/extensions/index.ts @@ -17,7 +17,6 @@ export * from "./headings-list"; export * from "./horizontal-rule"; export * from "./keymap"; export * from "./quote"; -export * from "./read-only-extensions"; export * from "./side-menu"; export * from "./text-align"; export * from "./utility"; diff --git a/packages/editor/src/core/extensions/read-only-extensions.ts b/packages/editor/src/core/extensions/read-only-extensions.ts deleted file mode 100644 index 3099fa88559..00000000000 --- a/packages/editor/src/core/extensions/read-only-extensions.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Extensions } from "@tiptap/core"; -import CharacterCount from "@tiptap/extension-character-count"; -import TaskItem from "@tiptap/extension-task-item"; -import TaskList from "@tiptap/extension-task-list"; -import TextStyle from "@tiptap/extension-text-style"; -import TiptapUnderline from "@tiptap/extension-underline"; -import { Markdown } from "tiptap-markdown"; -// extensions -import { - CustomQuoteExtension, - CustomHorizontalRule, - CustomLinkExtension, - CustomTypographyExtension, - CustomCodeBlockExtension, - CustomCodeInlineExtension, - TableHeader, - TableCell, - TableRow, - Table, - CustomMentionExtension, - CustomTextAlignExtension, - CustomCalloutReadOnlyExtension, - CustomColorExtension, - UtilityExtension, - ImageExtension, -} from "@/extensions"; -// plane editor extensions -import { CoreReadOnlyEditorAdditionalExtensions } from "@/plane-editor/extensions"; -// types -import type { IReadOnlyEditorProps } from "@/types"; -// local imports -import { CustomImageExtension } from "./custom-image/extension"; -import { EmojiExtension } from "./emoji/extension"; -import { CustomStarterKitExtension } from "./starter-kit"; - -type Props = Pick; - -export const CoreReadOnlyEditorExtensions = (props: Props): Extensions => { - const { disabledExtensions, fileHandler, flaggedExtensions, mentionHandler } = props; - - const extensions = [ - CustomStarterKitExtension({ - enableHistory: false, - }), - EmojiExtension, - CustomQuoteExtension, - CustomHorizontalRule, - CustomLinkExtension, - CustomTypographyExtension, - TiptapUnderline, - TextStyle, - TaskList.configure({ - HTMLAttributes: { - class: "not-prose pl-2 space-y-2", - }, - }), - TaskItem.configure({ - HTMLAttributes: { - class: "relative pointer-events-none", - }, - nested: true, - }), - CustomCodeBlockExtension, - CustomCodeInlineExtension, - Markdown.configure({ - html: true, - transformCopiedText: false, - }), - Table, - TableHeader, - TableCell, - TableRow, - CustomMentionExtension(mentionHandler), - CharacterCount, - CustomColorExtension, - CustomTextAlignExtension, - CustomCalloutReadOnlyExtension, - UtilityExtension({ - disabledExtensions, - fileHandler, - isEditable: false, - }), - ...CoreReadOnlyEditorAdditionalExtensions({ - disabledExtensions, - flaggedExtensions, - }), - ]; - - if (!disabledExtensions.includes("image")) { - extensions.push( - ImageExtension({ - fileHandler, - }), - CustomImageExtension({ - fileHandler, - isEditable: false, - }) - ); - } - - return extensions; -}; diff --git a/packages/editor/src/core/extensions/utility.ts b/packages/editor/src/core/extensions/utility.ts index a1b138a6d26..ba9e031b027 100644 --- a/packages/editor/src/core/extensions/utility.ts +++ b/packages/editor/src/core/extensions/utility.ts @@ -10,7 +10,7 @@ import { FilePlugins } from "@/plugins/file/root"; import { MarkdownClipboardPlugin } from "@/plugins/markdown-clipboard"; // types -import type { IEditorProps, TEditorAsset, TFileHandler, TReadOnlyFileHandler } from "@/types"; +import type { IEditorProps, TEditorAsset, TFileHandler } from "@/types"; type TActiveDropbarExtensions = CORE_EXTENSIONS.MENTION | CORE_EXTENSIONS.EMOJI | TAdditionalActiveDropbarExtensions; declare module "@tiptap/core" { @@ -38,7 +38,7 @@ export interface UtilityExtensionStorage { } type Props = Pick & { - fileHandler: TFileHandler | TReadOnlyFileHandler; + fileHandler: TFileHandler; isEditable: boolean; }; diff --git a/packages/editor/src/core/helpers/editor-ref.ts b/packages/editor/src/core/helpers/editor-ref.ts index 1b9843df975..520e840bf59 100644 --- a/packages/editor/src/core/helpers/editor-ref.ts +++ b/packages/editor/src/core/helpers/editor-ref.ts @@ -1,22 +1,26 @@ import { HocuspocusProvider } from "@hocuspocus/provider"; import { Editor } from "@tiptap/core"; +import { DOMSerializer } from "@tiptap/pm/model"; import * as Y from "yjs"; +// components +import { getEditorMenuItems } from "@/components/menus"; // constants import { CORE_EXTENSIONS } from "@/constants/extension"; import { CORE_EDITOR_META } from "@/constants/meta"; // types -import { EditorReadOnlyRefApi } from "@/types"; +import type { EditorRefApi, TEditorCommands } from "@/types"; // local imports import { getParagraphCount } from "./common"; import { getExtensionStorage } from "./get-extension-storage"; -import { scrollSummary } from "./scroll-to-node"; +import { insertContentAtSavedSelection } from "./insert-content-at-cursor-position"; +import { scrollSummary, scrollToNodeViaDOMCoordinates } from "./scroll-to-node"; type TArgs = { editor: Editor | null; provider: HocuspocusProvider | undefined; }; -export const getEditorRefHelpers = (args: TArgs): EditorReadOnlyRefApi => { +export const getEditorRefHelpers = (args: TArgs): EditorRefApi => { const { editor, provider } = args; return { @@ -51,5 +55,147 @@ export const getEditorRefHelpers = (args: TArgs): EditorReadOnlyRefApi => { setEditorValue: (content, emitUpdate = false) => { editor?.commands.setContent(content, emitUpdate, { preserveWhitespace: true }); }, + blur: () => editor?.commands.blur(), + emitRealTimeUpdate: (message) => provider?.sendStateless(message), + executeMenuItemCommand: (props) => { + const { itemKey } = props; + const editorItems = getEditorMenuItems(editor); + + const getEditorMenuItem = (itemKey: TEditorCommands) => editorItems.find((item) => item.key === itemKey); + + const item = getEditorMenuItem(itemKey); + if (item) { + item.command(props); + } else { + console.warn(`No command found for item: ${itemKey}`); + } + }, + getCurrentCursorPosition: () => editor?.state.selection.from, + getSelectedText: () => { + if (!editor) return null; + + const { state } = editor; + const { from, to, empty } = state.selection; + + if (empty) return null; + + const nodesArray: string[] = []; + state.doc.nodesBetween(from, to, (node, _pos, parent) => { + if (parent === state.doc && editor) { + const serializer = DOMSerializer.fromSchema(editor.schema); + const dom = serializer.serializeNode(node); + const tempDiv = document.createElement("div"); + tempDiv.appendChild(dom); + nodesArray.push(tempDiv.innerHTML); + } + }); + const selection = nodesArray.join(""); + return selection; + }, + insertText: (contentHTML, insertOnNextLine) => { + if (!editor) return; + const { from, to, empty } = editor.state.selection; + if (empty) return; + if (insertOnNextLine) { + // move cursor to the end of the selection and insert a new line + editor.chain().focus().setTextSelection(to).insertContent("
").insertContent(contentHTML).run(); + } else { + // replace selected text with the content provided + editor.chain().focus().deleteRange({ from, to }).insertContent(contentHTML).run(); + } + }, + isEditorReadyToDiscard: () => + !!editor && getExtensionStorage(editor, CORE_EXTENSIONS.UTILITY)?.uploadInProgress === false, + isMenuItemActive: (props) => { + const { itemKey } = props; + const editorItems = getEditorMenuItems(editor); + + const getEditorMenuItem = (itemKey: TEditorCommands) => editorItems.find((item) => item.key === itemKey); + const item = getEditorMenuItem(itemKey); + if (!item) return false; + + return item.isActive(props); + }, + listenToRealTimeUpdate: () => provider && { on: provider.on.bind(provider), off: provider.off.bind(provider) }, + onDocumentInfoChange: (callback) => { + const handleDocumentInfoChange = () => { + if (!editor) return; + callback({ + characters: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.characters?.() : 0, + paragraphs: getParagraphCount(editor?.state), + words: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.words?.() : 0, + }); + }; + + // Subscribe to update event emitted from character count extension + editor?.on("update", handleDocumentInfoChange); + // Return a function to unsubscribe to the continuous transactions of + // the editor on unmounting the component that has subscribed to this + // method + return () => { + editor?.off("update", handleDocumentInfoChange); + }; + }, + onHeadingChange: (callback) => { + const handleHeadingChange = () => { + if (!editor) return; + const headings = getExtensionStorage(editor, CORE_EXTENSIONS.HEADINGS_LIST)?.headings; + if (headings) { + callback(headings); + } + }; + + // Subscribe to update event emitted from headers extension + editor?.on("update", handleHeadingChange); + // Return a function to unsubscribe to the continuous transactions of + // the editor on unmounting the component that has subscribed to this + // method + return () => { + editor?.off("update", handleHeadingChange); + }; + }, + onStateChange: (callback) => { + // Subscribe to editor state changes + editor?.on("transaction", callback); + + // Return a function to unsubscribe to the continuous transactions of + // the editor on unmounting the component that has subscribed to this + // method + return () => { + editor?.off("transaction", callback); + }; + }, + scrollToNodeViaDOMCoordinates(behavior, pos) { + const resolvedPos = pos ?? editor?.state.selection.from; + if (!editor || !resolvedPos) return; + scrollToNodeViaDOMCoordinates(editor, resolvedPos, behavior); + }, + setEditorValueAtCursorPosition: (content) => { + if (editor?.state.selection) { + insertContentAtSavedSelection(editor, content); + } + }, + setFocusAtPosition: (position) => { + if (!editor || editor.isDestroyed) { + console.error("Editor reference is not available or has been destroyed."); + return; + } + try { + const docSize = editor.state.doc.content.size; + const safePosition = Math.max(0, Math.min(position, docSize)); + editor + .chain() + .insertContentAt(safePosition, [{ type: CORE_EXTENSIONS.PARAGRAPH }]) + .focus() + .run(); + } catch (error) { + console.error("An error occurred while setting focus at position:", error); + } + }, + setProviderDocument: (value) => { + const document = provider?.document; + if (!document) return; + Y.applyUpdate(document, value); + }, }; }; diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts index 954ef61b84d..96f5ba24d98 100644 --- a/packages/editor/src/core/hooks/use-editor.ts +++ b/packages/editor/src/core/hooks/use-editor.ts @@ -1,23 +1,16 @@ -import { DOMSerializer } from "@tiptap/pm/model"; import { useEditorState, useEditor as useTiptapEditor } from "@tiptap/react"; import { useImperativeHandle, useEffect } from "react"; -import * as Y from "yjs"; -// components -import { getEditorMenuItems } from "@/components/menus"; // constants import { CORE_EXTENSIONS } from "@/constants/extension"; // extensions import { CoreEditorExtensions } from "@/extensions"; // helpers -import { getParagraphCount } from "@/helpers/common"; import { getEditorRefHelpers } from "@/helpers/editor-ref"; import { getExtensionStorage } from "@/helpers/get-extension-storage"; -import { insertContentAtSavedSelection } from "@/helpers/insert-content-at-cursor-position"; -import { scrollToNodeViaDOMCoordinates } from "@/helpers/scroll-to-node"; // props import { CoreEditorProps } from "@/props"; // types -import type { TEditorCommands, TEditorHookProps } from "@/types"; +import type { TEditorHookProps } from "@/types"; export const useEditor = (props: TEditorHookProps) => { const { @@ -124,155 +117,7 @@ export const useEditor = (props: TEditorHookProps) => { onAssetChange(assets); }, [assetsList?.assets, onAssetChange]); - useImperativeHandle( - forwardedRef, - () => ({ - ...getEditorRefHelpers({ editor, provider }), - blur: () => editor?.commands.blur(), - emitRealTimeUpdate: (message) => provider?.sendStateless(message), - executeMenuItemCommand: (props) => { - const { itemKey } = props; - const editorItems = getEditorMenuItems(editor); - - const getEditorMenuItem = (itemKey: TEditorCommands) => editorItems.find((item) => item.key === itemKey); - - const item = getEditorMenuItem(itemKey); - if (item) { - item.command(props); - } else { - console.warn(`No command found for item: ${itemKey}`); - } - }, - getCurrentCursorPosition: () => editor?.state.selection.from, - getSelectedText: () => { - if (!editor) return null; - - const { state } = editor; - const { from, to, empty } = state.selection; - - if (empty) return null; - - const nodesArray: string[] = []; - state.doc.nodesBetween(from, to, (node, _pos, parent) => { - if (parent === state.doc && editor) { - const serializer = DOMSerializer.fromSchema(editor.schema); - const dom = serializer.serializeNode(node); - const tempDiv = document.createElement("div"); - tempDiv.appendChild(dom); - nodesArray.push(tempDiv.innerHTML); - } - }); - const selection = nodesArray.join(""); - return selection; - }, - insertText: (contentHTML, insertOnNextLine) => { - if (!editor) return; - const { from, to, empty } = editor.state.selection; - if (empty) return; - if (insertOnNextLine) { - // move cursor to the end of the selection and insert a new line - editor.chain().focus().setTextSelection(to).insertContent("
").insertContent(contentHTML).run(); - } else { - // replace selected text with the content provided - editor.chain().focus().deleteRange({ from, to }).insertContent(contentHTML).run(); - } - }, - isEditorReadyToDiscard: () => - !!editor && getExtensionStorage(editor, CORE_EXTENSIONS.UTILITY)?.uploadInProgress === false, - isMenuItemActive: (props) => { - const { itemKey } = props; - const editorItems = getEditorMenuItems(editor); - - const getEditorMenuItem = (itemKey: TEditorCommands) => editorItems.find((item) => item.key === itemKey); - const item = getEditorMenuItem(itemKey); - if (!item) return false; - - return item.isActive(props); - }, - listenToRealTimeUpdate: () => provider && { on: provider.on.bind(provider), off: provider.off.bind(provider) }, - onDocumentInfoChange: (callback) => { - const handleDocumentInfoChange = () => { - if (!editor) return; - callback({ - characters: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.characters?.() : 0, - paragraphs: getParagraphCount(editor?.state), - words: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.words?.() : 0, - }); - }; - - // Subscribe to update event emitted from character count extension - editor?.on("update", handleDocumentInfoChange); - // Return a function to unsubscribe to the continuous transactions of - // the editor on unmounting the component that has subscribed to this - // method - return () => { - editor?.off("update", handleDocumentInfoChange); - }; - }, - onHeadingChange: (callback) => { - const handleHeadingChange = () => { - if (!editor) return; - const headings = getExtensionStorage(editor, CORE_EXTENSIONS.HEADINGS_LIST)?.headings; - if (headings) { - callback(headings); - } - }; - - // Subscribe to update event emitted from headers extension - editor?.on("update", handleHeadingChange); - // Return a function to unsubscribe to the continuous transactions of - // the editor on unmounting the component that has subscribed to this - // method - return () => { - editor?.off("update", handleHeadingChange); - }; - }, - onStateChange: (callback) => { - // Subscribe to editor state changes - editor?.on("transaction", callback); - - // Return a function to unsubscribe to the continuous transactions of - // the editor on unmounting the component that has subscribed to this - // method - return () => { - editor?.off("transaction", callback); - }; - }, - scrollToNodeViaDOMCoordinates(behavior, pos) { - const resolvedPos = pos ?? editor?.state.selection.from; - if (!editor || !resolvedPos) return; - scrollToNodeViaDOMCoordinates(editor, resolvedPos, behavior); - }, - setEditorValueAtCursorPosition: (content) => { - if (editor?.state.selection) { - insertContentAtSavedSelection(editor, content); - } - }, - setFocusAtPosition: (position) => { - if (!editor || editor.isDestroyed) { - console.error("Editor reference is not available or has been destroyed."); - return; - } - try { - const docSize = editor.state.doc.content.size; - const safePosition = Math.max(0, Math.min(position, docSize)); - editor - .chain() - .insertContentAt(safePosition, [{ type: CORE_EXTENSIONS.PARAGRAPH }]) - .focus() - .run(); - } catch (error) { - console.error("An error occurred while setting focus at position:", error); - } - }, - setProviderDocument: (value) => { - const document = provider?.document; - if (!document) return; - Y.applyUpdate(document, value); - }, - }), - [editor, provider] - ); + useImperativeHandle(forwardedRef, () => getEditorRefHelpers({ editor, provider }), [editor, provider]); if (!editor) { return null; diff --git a/packages/editor/src/core/hooks/use-read-only-editor.ts b/packages/editor/src/core/hooks/use-read-only-editor.ts deleted file mode 100644 index 43e9c958134..00000000000 --- a/packages/editor/src/core/hooks/use-read-only-editor.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useEditor as useTiptapEditor } from "@tiptap/react"; -import { useImperativeHandle, useEffect } from "react"; -// extensions -import { CoreReadOnlyEditorExtensions } from "@/extensions"; -// helpers -import { getEditorRefHelpers } from "@/helpers/editor-ref"; -// props -import { CoreReadOnlyEditorProps } from "@/props"; -// types -import type { TReadOnlyEditorHookProps } from "@/types"; - -export const useReadOnlyEditor = (props: TReadOnlyEditorHookProps) => { - const { - disabledExtensions, - editorClassName = "", - editorProps = {}, - extensions = [], - fileHandler, - flaggedExtensions, - forwardedRef, - handleEditorReady, - initialValue, - mentionHandler, - provider, - } = props; - - const editor = useTiptapEditor({ - editable: false, - immediatelyRender: false, - shouldRerenderOnTransaction: false, - content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "

", - parseOptions: { preserveWhitespace: true }, - editorProps: { - ...CoreReadOnlyEditorProps({ - editorClassName, - }), - ...editorProps, - }, - onCreate: async () => { - handleEditorReady?.(true); - }, - extensions: [ - ...CoreReadOnlyEditorExtensions({ - disabledExtensions, - fileHandler, - flaggedExtensions, - mentionHandler, - }), - ...extensions, - ], - onDestroy: () => { - handleEditorReady?.(false); - }, - }); - - // for syncing swr data on tab refocus etc - useEffect(() => { - if (initialValue === null || initialValue === undefined) return; - if (editor && !editor.isDestroyed) editor?.commands.setContent(initialValue, false, { preserveWhitespace: true }); - }, [editor, initialValue]); - - useImperativeHandle(forwardedRef, () => getEditorRefHelpers({ editor, provider })); - - if (!editor) { - return null; - } - - return editor; -}; diff --git a/packages/editor/src/core/plugins/file/root.ts b/packages/editor/src/core/plugins/file/root.ts index 693ac6964ba..bf9ca0260e7 100644 --- a/packages/editor/src/core/plugins/file/root.ts +++ b/packages/editor/src/core/plugins/file/root.ts @@ -1,14 +1,14 @@ import { Editor } from "@tiptap/core"; import { Plugin } from "@tiptap/pm/state"; // types -import { TFileHandler, TReadOnlyFileHandler } from "@/types"; +import { TFileHandler } from "@/types"; // local imports import { TrackFileDeletionPlugin } from "./delete"; import { TrackFileRestorationPlugin } from "./restore"; type TArgs = { editor: Editor; - fileHandler: TFileHandler | TReadOnlyFileHandler; + fileHandler: TFileHandler; isEditable: boolean; }; diff --git a/packages/editor/src/core/props/props.tsx b/packages/editor/src/core/props.ts similarity index 88% rename from packages/editor/src/core/props/props.tsx rename to packages/editor/src/core/props.ts index ee0b9e50007..939ca77176d 100644 --- a/packages/editor/src/core/props/props.tsx +++ b/packages/editor/src/core/props.ts @@ -2,11 +2,11 @@ import { EditorProps } from "@tiptap/pm/view"; // plane utils import { cn } from "@plane/utils"; -export type TCoreEditorProps = { +type TArgs = { editorClassName: string; }; -export const CoreEditorProps = (props: TCoreEditorProps): EditorProps => { +export const CoreEditorProps = (props: TArgs): EditorProps => { const { editorClassName } = props; return { diff --git a/packages/editor/src/core/props/index.ts b/packages/editor/src/core/props/index.ts deleted file mode 100644 index eaa89f0591d..00000000000 --- a/packages/editor/src/core/props/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./props"; -export * from "./read-only"; diff --git a/packages/editor/src/core/props/read-only.tsx b/packages/editor/src/core/props/read-only.tsx deleted file mode 100644 index ea5bf09f3a7..00000000000 --- a/packages/editor/src/core/props/read-only.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { EditorProps } from "@tiptap/pm/view"; -// plane utils -import { cn } from "@plane/utils"; -// props -import { TCoreEditorProps } from "@/props"; - -export const CoreReadOnlyEditorProps = (props: TCoreEditorProps): EditorProps => { - const { editorClassName } = props; - - return { - attributes: { - class: cn( - "prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none", - editorClassName - ), - }, - }; -}; diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts index 8c1903cf2dc..da00d960769 100644 --- a/packages/editor/src/core/types/config.ts +++ b/packages/editor/src/core/types/config.ts @@ -1,17 +1,14 @@ // plane imports import { TWebhookConnectionQueryParams } from "@plane/types"; -export type TReadOnlyFileHandler = { +export type TFileHandler = { + assetsUploadStatus: Record; // blockId => progress percentage + cancel: () => void; checkIfAssetExists: (assetId: string) => Promise; + delete: (assetSrc: string) => Promise; getAssetDownloadSrc: (path: string) => Promise; getAssetSrc: (path: string) => Promise; restore: (assetSrc: string) => Promise; -}; - -export type TFileHandler = TReadOnlyFileHandler & { - assetsUploadStatus: Record; // blockId => progress percentage - cancel: () => void; - delete: (assetSrc: string) => Promise; upload: (blockId: string, file: File) => Promise; validation: { /** diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index d511bb7f122..39744b3abfe 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -15,8 +15,6 @@ import type { TExtensions, TFileHandler, TMentionHandler, - TReadOnlyFileHandler, - TReadOnlyMentionHandler, TRealtimeConfig, TServerHandler, TUserDetails, @@ -83,9 +81,12 @@ export type TDocumentInfo = { words: number; }; -// editor refs -export type EditorReadOnlyRefApi = { +export type EditorRefApi = { + blur: () => void; clearEditor: (emitUpdate?: boolean) => void; + emitRealTimeUpdate: (action: TDocumentEventsServer) => void; + executeMenuItemCommand: (props: TCommandWithPropsWithItemKey) => void; + getCurrentCursorPosition: () => number | undefined; getDocument: () => { binary: Uint8Array | null; html: string; @@ -94,15 +95,6 @@ export type EditorReadOnlyRefApi = { getDocumentInfo: () => TDocumentInfo; getHeadings: () => IMarking[]; getMarkDown: () => string; - scrollSummary: (marking: IMarking) => void; - setEditorValue: (content: string, emitUpdate?: boolean) => void; -}; - -export interface EditorRefApi extends EditorReadOnlyRefApi { - blur: () => void; - emitRealTimeUpdate: (action: TDocumentEventsServer) => void; - executeMenuItemCommand: (props: TCommandWithPropsWithItemKey) => void; - getCurrentCursorPosition: () => number | undefined; getSelectedText: () => string | null; insertText: (contentHTML: string, insertOnNextLine?: boolean) => void; isEditorReadyToDiscard: () => boolean; @@ -111,12 +103,14 @@ export interface EditorRefApi extends EditorReadOnlyRefApi { onDocumentInfoChange: (callback: (documentInfo: TDocumentInfo) => void) => () => void; onHeadingChange: (callback: (headings: IMarking[]) => void) => () => void; onStateChange: (callback: () => void) => () => void; + scrollSummary: (marking: IMarking) => void; // eslint-disable-next-line no-undef scrollToNodeViaDOMCoordinates: (behavior?: ScrollBehavior, position?: number) => void; + setEditorValue: (content: string, emitUpdate?: boolean) => void; setEditorValueAtCursorPosition: (content: string) => void; setFocusAtPosition: (position: number) => void; setProviderDocument: (value: Uint8Array) => void; -} +}; // editor props export interface IEditorProps { @@ -125,6 +119,7 @@ export interface IEditorProps { containerClassName?: string; displayConfig?: TDisplayConfig; disabledExtensions: TExtensions[]; + editable: boolean; editorClassName?: string; extensions?: Extensions; flaggedExtensions: TExtensions[]; @@ -147,13 +142,11 @@ export type ILiteTextEditorProps = IEditorProps; export type IRichTextEditorProps = IEditorProps & { dragDropEnabled?: boolean; - editable: boolean; }; export interface ICollaborativeDocumentEditorProps extends Omit { aiHandler?: TAIHandler; - editable: boolean; embedHandler: TEmbedConfig; realtimeConfig: TRealtimeConfig; serverHandler?: TServerHandler; @@ -162,33 +155,11 @@ export interface ICollaborativeDocumentEditorProps export interface IDocumentEditorProps extends Omit { aiHandler?: TAIHandler; - editable: boolean; embedHandler: TEmbedConfig; user?: TUserDetails; value: Content; } -// read only editor props -export interface IReadOnlyEditorProps - extends Pick< - IEditorProps, - | "containerClassName" - | "disabledExtensions" - | "flaggedExtensions" - | "displayConfig" - | "editorClassName" - | "extensions" - | "handleEditorReady" - | "id" - | "initialValue" - > { - fileHandler: TReadOnlyFileHandler; - forwardedRef?: React.MutableRefObject; - mentionHandler: TReadOnlyMentionHandler; -} - -export type ILiteTextReadOnlyEditorProps = IReadOnlyEditorProps; - export interface EditorEvents { beforeCreate: never; create: never; diff --git a/packages/editor/src/core/types/hook.ts b/packages/editor/src/core/types/hook.ts index fa014ceb905..a1dd6ac1dd7 100644 --- a/packages/editor/src/core/types/hook.ts +++ b/packages/editor/src/core/types/hook.ts @@ -2,7 +2,7 @@ import type { HocuspocusProvider } from "@hocuspocus/provider"; import type { Content } from "@tiptap/core"; import type { EditorProps } from "@tiptap/pm/view"; // local imports -import type { ICollaborativeDocumentEditorProps, IEditorProps, IReadOnlyEditorProps } from "./editor"; +import type { ICollaborativeDocumentEditorProps, IEditorProps } from "./editor"; type TCoreHookProps = Pick< IEditorProps, @@ -47,7 +47,3 @@ export type TCollaborativeEditorHookProps = TCoreHookProps & | "tabIndex" > & Pick; - -export type TReadOnlyEditorHookProps = TCoreHookProps & - Pick & - Pick; diff --git a/packages/editor/src/core/types/mention.ts b/packages/editor/src/core/types/mention.ts index 529d64bde18..f8519194145 100644 --- a/packages/editor/src/core/types/mention.ts +++ b/packages/editor/src/core/types/mention.ts @@ -18,11 +18,8 @@ export type TMentionSection = { export type TMentionComponentProps = Pick; -export type TReadOnlyMentionHandler = { - renderComponent: (props: TMentionComponentProps) => React.ReactNode; +export type TMentionHandler = { getMentionedEntityDetails?: (entity_identifier: string) => { display_name: string } | undefined; -}; - -export type TMentionHandler = TReadOnlyMentionHandler & { + renderComponent: (props: TMentionComponentProps) => React.ReactNode; searchCallback?: (query: string) => Promise; }; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 2cfc6891588..10d7bb41137 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -11,7 +11,6 @@ export { CollaborativeDocumentEditorWithRef, DocumentEditorWithRef, LiteTextEditorWithRef, - LiteTextReadOnlyEditorWithRef, RichTextEditorWithRef, } from "@/components/editors";