Skip to content
92 changes: 92 additions & 0 deletions apps/web/core/components/editor/document/editor.tsx
Original file line number Diff line number Diff line change
@@ -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";
4 changes: 2 additions & 2 deletions apps/web/core/components/editor/index.ts
Original file line number Diff line number Diff line change
@@ -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";
3 changes: 0 additions & 3 deletions apps/web/core/components/editor/lite-text-editor/index.ts

This file was deleted.

3 changes: 3 additions & 0 deletions apps/web/core/components/editor/lite-text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./editor";
export * from "./read-only-editor";
export * from "./toolbar";
1 change: 0 additions & 1 deletion apps/web/core/components/editor/rich-text-editor/index.ts

This file was deleted.

1 change: 1 addition & 0 deletions apps/web/core/components/editor/rich-text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./editor";
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
50 changes: 12 additions & 38 deletions apps/web/core/components/pages/version/editor.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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();

Expand Down Expand Up @@ -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() ?? ""}
/>
);
});
1 change: 1 addition & 0 deletions packages/editor/src/ce/extensions/document-extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type TDocumentEditorAdditionalExtensionsProps = Pick<
"disabledExtensions" | "flaggedExtensions" | "fileHandler"
> & {
embedConfig: TEmbedConfig | undefined;
isEditable: boolean;
provider?: HocuspocusProvider;
userDetails: TUserDetails;
};
Expand Down
109 changes: 109 additions & 0 deletions packages/editor/src/core/components/editors/document/editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Extensions } from "@tiptap/core";
import { forwardRef, MutableRefObject, useMemo } from "react";
// plane imports
import { cn } from "@plane/utils";
// components
import { PageRenderer } from "@/components/editors";
// constants
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
// extensions
import { HeadingListExtension, WorkItemEmbedExtension, SideMenuExtension } from "@/extensions";
// helpers
import { getEditorClassNames } from "@/helpers/common";
// hooks
import { useEditor } from "@/hooks/use-editor";
// plane editor extensions
import { DocumentEditorAdditionalExtensions } from "@/plane-editor/extensions";
// types
import { EditorRefApi, IDocumentEditorProps } from "@/types";

const DocumentEditor = (props: IDocumentEditorProps) => {
const {
bubbleMenuEnabled = false,
containerClassName,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
embedHandler,
fileHandler,
flaggedExtensions,
forwardedRef,
id,
handleEditorReady,
mentionHandler,
onChange,
user,
value,
} = props;
const extensions: Extensions = useMemo(() => {
const additionalExtensions: Extensions = [];
if (embedHandler?.issue) {
additionalExtensions.push(
WorkItemEmbedExtension({
widgetCallback: embedHandler.issue.widgetCallback,
})
);
}
additionalExtensions.push(
SideMenuExtension({
aiEnabled: !disabledExtensions?.includes("ai"),
dragDropEnabled: true,
}),
HeadingListExtension,
...DocumentEditorAdditionalExtensions({
disabledExtensions,
embedConfig: embedHandler,
flaggedExtensions,
isEditable: editable,
fileHandler,
userDetails: user ?? {
id: "",
name: "",
color: "",
},
})
);
return additionalExtensions;
}, []);

const editor = useEditor({
disabledExtensions,
editable,
editorClassName,
enableHistory: true,
extensions,
fileHandler,
flaggedExtensions,
forwardedRef,
handleEditorReady,
id,
initialValue: value,
mentionHandler,
onChange,
});

const editorContainerClassName = getEditorClassNames({
containerClassName,
});

if (!editor) return null;

return (
<PageRenderer
bubbleMenuEnabled={bubbleMenuEnabled}
displayConfig={displayConfig}
editor={editor}
editorContainerClassName={cn(editorContainerClassName, "document-editor")}
id={id}
/>
);
};

const DocumentEditorWithRef = forwardRef<EditorRefApi, IDocumentEditorProps>((props, ref) => (
<DocumentEditor {...props} forwardedRef={ref as MutableRefObject<EditorRefApi | null>} />
));

DocumentEditorWithRef.displayName = "DocumentEditorWithRef";

export { DocumentEditorWithRef };
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./collaborative-editor";
export * from "./editor";
export * from "./loader";
export * from "./page-renderer";
export * from "./read-only-editor";
Loading
Loading