[WIKI-537] refactor: document editor (#7384)

* refactor: document editor

* chore: update user prop

* fix: type warning

* chore: update value prop name

* chore: remove unnecessary exports

* hore: update initialValue type

* chore: revert initialValue type

* refactor: unnecessary string handlers
This commit is contained in:
Aaryan Khandelwal
2025-07-25 13:57:45 +05:30
committed by GitHub
parent e20bfa55d6
commit 27f74206a3
23 changed files with 244 additions and 183 deletions

View File

@@ -0,0 +1,92 @@
import React, { forwardRef } from "react";
// plane imports
import { DocumentEditorWithRef, EditorRefApi, IDocumentEditorProps, TFileHandler } from "@plane/editor";
import { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types";
import { cn } from "@plane/utils";
// components
import { EditorMentionsRoot } from "@/components/editor";
// hooks
import { useEditorConfig, useEditorMention } from "@/hooks/editor";
import { useMember } from "@/hooks/store";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed";
type DocumentEditorWrapperProps = MakeOptional<
Omit<IDocumentEditorProps, "fileHandler" | "mentionHandler" | "embedHandler" | "user">,
"disabledExtensions" | "editable" | "flaggedExtensions"
> & {
embedHandler?: Partial<IDocumentEditorProps["embedHandler"]>;
workspaceSlug: string;
workspaceId: string;
projectId?: string;
} & (
| {
editable: false;
}
| {
editable: true;
searchMentionCallback: (payload: TSearchEntityRequestPayload) => Promise<TSearchResponse>;
uploadFile: TFileHandler["upload"];
}
);
export const DocumentEditor = forwardRef<EditorRefApi, DocumentEditorWrapperProps>((props, ref) => {
const {
containerClassName,
editable,
embedHandler,
workspaceSlug,
workspaceId,
projectId,
disabledExtensions: additionalDisabledExtensions = [],
...rest
} = props;
// store hooks
const { getUserDetails } = useMember();
// editor flaggings
const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug);
// use editor mention
const { fetchMentions } = useEditorMention({
searchEntity: editable ? async (payload) => await props.searchMentionCallback(payload) : async () => ({}),
});
// editor config
const { getEditorFileHandlers } = useEditorConfig();
// issue-embed
const { issueEmbedProps } = useIssueEmbed({
projectId,
workspaceSlug,
});
return (
<DocumentEditorWithRef
ref={ref}
disabledExtensions={[...documentEditorExtensions.disabled, ...(additionalDisabledExtensions ?? [])]}
editable={editable}
flaggedExtensions={documentEditorExtensions.flagged}
fileHandler={getEditorFileHandlers({
projectId,
uploadFile: editable ? props.uploadFile : async () => "",
workspaceId,
workspaceSlug,
})}
mentionHandler={{
searchCallback: async (query) => {
const res = await fetchMentions(query);
if (!res) throw new Error("Failed in fetching mentions");
return res;
},
renderComponent: EditorMentionsRoot,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
embedHandler={{
issue: issueEmbedProps,
...embedHandler,
}}
{...rest}
containerClassName={cn("relative pl-3 pb-3", containerClassName)}
/>
);
});
DocumentEditor.displayName = "DocumentEditor";

View File

@@ -1,5 +1,5 @@
export * from "./embeds";
export * from "./lite-text-editor";
export * from "./lite-text";
export * from "./pdf";
export * from "./rich-text-editor";
export * from "./rich-text";
export * from "./sticky-editor";

View File

@@ -1,3 +0,0 @@
export * from "./lite-text-editor";
export * from "./lite-text-read-only-editor";
export * from "./toolbar";

View File

@@ -0,0 +1,3 @@
export * from "./editor";
export * from "./read-only-editor";
export * from "./toolbar";

View File

@@ -1 +0,0 @@
export * from "./rich-text-editor";

View File

@@ -0,0 +1 @@
export * from "./editor";

View File

@@ -10,7 +10,7 @@ import { EFileAssetType, TIssue } from "@plane/types";
import { Loader } from "@plane/ui";
import { getDescriptionPlaceholderI18n, getTabIndex } from "@plane/utils";
// components
import { RichTextEditor } from "@/components/editor/rich-text-editor/rich-text-editor";
import { RichTextEditor } from "@/components/editor/rich-text/editor";
// hooks
import { useEditorAsset, useProjectInbox } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";

View File

@@ -1,18 +1,14 @@
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { DocumentReadOnlyEditorWithRef, TDisplayConfig } from "@plane/editor";
import { TDisplayConfig } from "@plane/editor";
import { TPageVersion } from "@plane/types";
import { Loader } from "@plane/ui";
// components
import { EditorMentionsRoot } from "@/components/editor";
import { DocumentEditor } from "@/components/editor/document/editor";
// hooks
import { useEditorConfig } from "@/hooks/editor";
import { useMember, useWorkspace } from "@/hooks/store";
import { useWorkspace } from "@/hooks/store";
import { usePageFilters } from "@/hooks/use-page-filters";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed";
export type TVersionEditorProps = {
activeVersion: string | null;
@@ -21,23 +17,12 @@ export type TVersionEditorProps = {
export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props) => {
const { activeVersion, versionDetails } = props;
// store hooks
const { getUserDetails } = useMember();
// params
const { workspaceSlug, projectId } = useParams();
// store hooks
const { getWorkspaceBySlug } = useWorkspace();
// derived values
const workspaceDetails = getWorkspaceBySlug(workspaceSlug?.toString() ?? "");
// editor flaggings
const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug?.toString() ?? "");
// editor config
const { getReadOnlyEditorFileHandlers } = useEditorConfig();
// issue-embed
const { issueEmbedProps } = useIssueEmbed({
projectId: projectId?.toString() ?? "",
workspaceSlug: workspaceSlug?.toString() ?? "",
});
// page filters
const { fontSize, fontStyle } = usePageFilters();
@@ -89,32 +74,21 @@ export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props
</div>
);
const description = versionDetails?.description_html;
if (description === undefined || description?.trim() === "") return null;
const description = versionDetails?.description_json;
if (!description) return null;
return (
<DocumentReadOnlyEditorWithRef
<DocumentEditor
key={activeVersion ?? ""}
editable={false}
id={activeVersion ?? ""}
initialValue={description ?? "<p></p>"}
value={description}
containerClassName="p-0 pb-64 border-none"
disabledExtensions={documentEditorExtensions.disabled}
flaggedExtensions={documentEditorExtensions.flagged}
displayConfig={displayConfig}
editorClassName="pl-10"
fileHandler={getReadOnlyEditorFileHandlers({
projectId: projectId?.toString() ?? "",
workspaceId: workspaceDetails?.id ?? "",
workspaceSlug: workspaceSlug?.toString() ?? "",
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
embedHandler={{
issue: {
widgetCallback: issueEmbedProps.widgetCallback,
},
}}
projectId={projectId?.toString()}
workspaceId={workspaceDetails?.id ?? ""}
workspaceSlug={workspaceSlug?.toString() ?? ""}
/>
);
});