plane/web/components/editor/lite-text-editor/lite-text-editor.tsx

107 lines
3.5 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?.trim() === "" ||
props.initialValue === "<p></p>" ||
(isEmptyHtmlString(props.initialValue ?? "") && !props.initialValue?.includes("mention-component"));
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";