forked from github/plane
107 lines
3.5 KiB
TypeScript
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";
|