forked from github/plane
108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
|
import React from "react";
|
||
|
// editor
|
||
|
import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef } from "@plane/lite-text-editor";
|
||
|
// types
|
||
|
import { IUserLite } from "@plane/types";
|
||
|
// components
|
||
|
import { IssueCommentToolbar } from "@/components/editor";
|
||
|
// constants
|
||
|
import { EIssueCommentAccessSpecifier } from "@/constants/issue";
|
||
|
// helpers
|
||
|
import { cn } from "@/helpers/common.helper";
|
||
|
import { isEmptyHtmlString } from "@/helpers/string.helper";
|
||
|
// hooks
|
||
|
import { useMember, useMention, useUser } from "@/hooks/store";
|
||
|
// services
|
||
|
import { FileService } from "@/services/file.service";
|
||
|
|
||
|
interface LiteTextEditorWrapperProps extends Omit<ILiteTextEditor, "fileHandler" | "mentionHandler"> {
|
||
|
workspaceSlug: string;
|
||
|
workspaceId: string;
|
||
|
projectId: string;
|
||
|
accessSpecifier?: EIssueCommentAccessSpecifier;
|
||
|
handleAccessChange?: (accessKey: EIssueCommentAccessSpecifier) => void;
|
||
|
showAccessSpecifier?: boolean;
|
||
|
showSubmitButton?: boolean;
|
||
|
isSubmitting?: boolean;
|
||
|
}
|
||
|
|
||
|
const fileService = new FileService();
|
||
|
|
||
|
export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapperProps>((props, ref) => {
|
||
|
const {
|
||
|
containerClassName,
|
||
|
workspaceSlug,
|
||
|
workspaceId,
|
||
|
projectId,
|
||
|
accessSpecifier,
|
||
|
handleAccessChange,
|
||
|
showAccessSpecifier = false,
|
||
|
showSubmitButton = true,
|
||
|
isSubmitting = false,
|
||
|
...rest
|
||
|
} = props;
|
||
|
// store hooks
|
||
|
const { currentUser } = useUser();
|
||
|
const {
|
||
|
getUserDetails,
|
||
|
project: { getProjectMemberIds },
|
||
|
} = useMember();
|
||
|
// derived values
|
||
|
const projectMemberIds = getProjectMemberIds(projectId);
|
||
|
const projectMemberDetails = projectMemberIds?.map((id) => getUserDetails(id) as IUserLite);
|
||
|
// use-mention
|
||
|
const { mentionHighlights, mentionSuggestions } = useMention({
|
||
|
workspaceSlug,
|
||
|
projectId,
|
||
|
members: projectMemberDetails,
|
||
|
user: currentUser ?? undefined,
|
||
|
});
|
||
|
|
||
|
const isEmpty =
|
||
|
props.initialValue === "" ||
|
||
|
props.initialValue?.trim() === "" ||
|
||
|
props.initialValue === "<p></p>" ||
|
||
|
isEmptyHtmlString(props.initialValue ?? "");
|
||
|
|
||
|
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
|
||
|
return !!ref && typeof ref === "object" && "current" in ref;
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
<div className="border border-custom-border-200 rounded p-3 space-y-3">
|
||
|
<LiteTextEditorWithRef
|
||
|
ref={ref}
|
||
|
fileHandler={{
|
||
|
upload: fileService.getUploadFileFunction(workspaceSlug),
|
||
|
delete: fileService.getDeleteImageFunction(workspaceId),
|
||
|
restore: fileService.getRestoreImageFunction(workspaceId),
|
||
|
cancel: fileService.cancelUpload,
|
||
|
}}
|
||
|
mentionHandler={{
|
||
|
highlights: mentionHighlights,
|
||
|
suggestions: mentionSuggestions,
|
||
|
}}
|
||
|
{...rest}
|
||
|
containerClassName={cn(containerClassName, "relative")}
|
||
|
/>
|
||
|
<IssueCommentToolbar
|
||
|
accessSpecifier={accessSpecifier}
|
||
|
executeCommand={(key) => {
|
||
|
if (isMutableRefObject<EditorRefApi>(ref)) {
|
||
|
ref.current?.executeMenuItemCommand(key);
|
||
|
}
|
||
|
}}
|
||
|
handleAccessChange={handleAccessChange}
|
||
|
handleSubmit={(e) => rest.onEnterKeyPress?.(e)}
|
||
|
isCommentEmpty={isEmpty}
|
||
|
isSubmitting={isSubmitting}
|
||
|
showAccessSpecifier={showAccessSpecifier}
|
||
|
editorRef={isMutableRefObject<EditorRefApi>(ref) ? ref : null}
|
||
|
showSubmitButton={showSubmitButton}
|
||
|
/>
|
||
|
</div>
|
||
|
);
|
||
|
});
|
||
|
|
||
|
LiteTextEditor.displayName = "LiteTextEditor";
|