fix: tiptap editor export fixes (#2001)

This commit is contained in:
sriram veeraghanta 2023-08-28 15:54:49 +05:30 committed by GitHub
parent b2e5760391
commit c65bbf865d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 107 deletions

View File

@ -1,7 +1,5 @@
import React, { useEffect, useState, forwardRef, useRef } from "react"; import React, { useEffect, useState, forwardRef, useRef } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// react-hook-form // react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
@ -12,9 +10,10 @@ import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
// ui // ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui"; import { Input, PrimaryButton, SecondaryButton } from "components/ui";
import { TipTapEditor } from "components/tiptap";
// types
import { IIssue, IPageBlock } from "types"; import { IIssue, IPageBlock } from "types";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
handleClose: () => void; handleClose: () => void;
@ -32,12 +31,6 @@ type FormData = {
task: string; task: string;
}; };
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
);
TiptapEditor.displayName = "TiptapEditor";
export const GptAssistantModal: React.FC<Props> = ({ export const GptAssistantModal: React.FC<Props> = ({
isOpen, isOpen,
handleClose, handleClose,
@ -140,13 +133,14 @@ export const GptAssistantModal: React.FC<Props> = ({
return ( return (
<div <div
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${isOpen ? "block" : "hidden" className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${
}`} isOpen ? "block" : "hidden"
}`}
> >
{((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && ( {((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && (
<div className="text-sm"> <div className="text-sm">
Content: Content:
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
value={htmlContent ?? `<p>${content}</p>`} value={htmlContent ?? `<p>${content}</p>`}
customClassName="-m-3" customClassName="-m-3"
@ -160,7 +154,7 @@ export const GptAssistantModal: React.FC<Props> = ({
{response !== "" && ( {response !== "" && (
<div className="page-block-section text-sm"> <div className="page-block-section text-sm">
Response: Response:
<Tiptap <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
value={`<p>${response}</p>`} value={`<p>${response}</p>`}
customClassName="-mx-3 -my-3" customClassName="-mx-3 -my-3"
@ -180,10 +174,11 @@ export const GptAssistantModal: React.FC<Props> = ({
type="text" type="text"
name="task" name="task"
register={register} register={register}
placeholder={`${content && content !== "" placeholder={`${
content && content !== ""
? "Tell AI what action to perform on this content..." ? "Tell AI what action to perform on this content..."
: "Ask AI anything..." : "Ask AI anything..."
}`} }`}
autoComplete="off" autoComplete="off"
/> />
<div className={`flex gap-2 ${response === "" ? "justify-end" : "justify-between"}`}> <div className={`flex gap-2 ${response === "" ? "justify-end" : "justify-between"}`}>
@ -219,8 +214,8 @@ export const GptAssistantModal: React.FC<Props> = ({
{isSubmitting {isSubmitting
? "Generating response..." ? "Generating response..."
: response === "" : response === ""
? "Generate response" ? "Generate response"
: "Generate again"} : "Generate again"}
</PrimaryButton> </PrimaryButton>
</div> </div>
</div> </div>

View File

@ -1,21 +1,12 @@
import React from "react"; import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// react-hook-form // react-hook-form
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
// ui // components
import { SecondaryButton } from "components/ui"; import { SecondaryButton } from "components/ui";
import { TipTapEditor } from "components/tiptap";
// types // types
import type { IIssueComment } from "types"; import type { IIssueComment } from "types";
// fetch-keys
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
);
TiptapEditor.displayName = "TiptapEditor";
const defaultValues: Partial<IIssueComment> = { const defaultValues: Partial<IIssueComment> = {
comment_json: "", comment_json: "",
@ -59,7 +50,7 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit }) => {
name="comment_html" name="comment_html"
control={control} control={control}
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => (
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={editorRef} ref={editorRef}
value={ value={

View File

@ -9,17 +9,11 @@ import useUser from "hooks/use-user";
// ui // ui
import { CustomMenu } from "components/ui"; import { CustomMenu } from "components/ui";
import { CommentReaction } from "components/issues"; import { CommentReaction } from "components/issues";
import { TipTapEditor } from "components/tiptap";
// helpers // helpers
import { timeAgo } from "helpers/date-time.helper"; import { timeAgo } from "helpers/date-time.helper";
// types // types
import type { IIssueComment } from "types"; import type { IIssueComment } from "types";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
);
TiptapEditor.displayName = "TiptapEditor";
type Props = { type Props = {
workspaceSlug: string; workspaceSlug: string;
@ -28,7 +22,12 @@ type Props = {
handleCommentDeletion: (comment: string) => void; handleCommentDeletion: (comment: string) => void;
}; };
export const CommentCard: React.FC<Props> = ({ comment, workspaceSlug, onSubmit, handleCommentDeletion }) => { export const CommentCard: React.FC<Props> = ({
comment,
workspaceSlug,
onSubmit,
handleCommentDeletion,
}) => {
const { user } = useUser(); const { user } = useUser();
const editorRef = React.useRef<any>(null); const editorRef = React.useRef<any>(null);
@ -109,7 +108,7 @@ export const CommentCard: React.FC<Props> = ({ comment, workspaceSlug, onSubmit,
onSubmit={handleSubmit(onEnter)} onSubmit={handleSubmit(onEnter)}
> >
<div> <div>
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={editorRef} ref={editorRef}
value={watch("comment_html")} value={watch("comment_html")}
@ -139,7 +138,7 @@ export const CommentCard: React.FC<Props> = ({ comment, workspaceSlug, onSubmit,
</div> </div>
</form> </form>
<div className={`${isEditing ? "hidden" : ""}`}> <div className={`${isEditing ? "hidden" : ""}`}>
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={showEditorRef} ref={showEditorRef}
value={comment.comment_html} value={comment.comment_html}

View File

@ -7,7 +7,7 @@ import useReloadConfirmations from "hooks/use-reload-confirmation";
import { useDebouncedCallback } from "use-debounce"; import { useDebouncedCallback } from "use-debounce";
// components // components
import { TextArea } from "components/ui"; import { TextArea } from "components/ui";
import Tiptap from "components/tiptap"; import { TipTapEditor } from "components/tiptap";
// types // types
import { IIssue } from "types"; import { IIssue } from "types";
@ -134,7 +134,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
if (!value) return <></>; if (!value) return <></>;
return ( return (
<Tiptap <TipTapEditor
value={ value={
!value || !value ||
value === "" || value === "" ||

View File

@ -31,18 +31,11 @@ import {
SecondaryButton, SecondaryButton,
ToggleSwitch, ToggleSwitch,
} from "components/ui"; } from "components/ui";
import { TipTapEditor } from "components/tiptap";
// icons // icons
import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline";
// types // types
import type { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types"; import type { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
// rich-text-editor
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
);
TiptapEditor.displayName = "TiptapEditor";
const defaultValues: Partial<IIssue> = { const defaultValues: Partial<IIssue> = {
project: "", project: "",
@ -369,7 +362,7 @@ export const IssueForm: FC<IssueFormProps> = ({
if (!value && !watch("description_html")) return <></>; if (!value && !watch("description_html")) return <></>;
return ( return (
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={editorRef} ref={editorRef}
debouncedUpdatesEnabled={false} debouncedUpdatesEnabled={false}

View File

@ -1,12 +1,7 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
import { SparklesIcon } from "@heroicons/react/24/outline"; import { SparklesIcon } from "@heroicons/react/24/outline";
// react-hook-form
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/pages.service";
@ -16,13 +11,12 @@ import aiService from "services/ai.service";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
import { GptAssistantModal } from "components/core"; import { GptAssistantModal } from "components/core";
// ui import { TipTapEditor } from "components/tiptap";
import { PrimaryButton, SecondaryButton, TextArea } from "components/ui"; import { PrimaryButton, SecondaryButton, TextArea } from "components/ui";
// types // types
import { ICurrentUserResponse, IPageBlock } from "types"; import { ICurrentUserResponse, IPageBlock } from "types";
// fetch-keys // fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys"; import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
type Props = { type Props = {
handleClose: () => void; handleClose: () => void;
@ -39,12 +33,6 @@ const defaultValues = {
description_html: null, description_html: null,
}; };
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
);
TiptapEditor.displayName = "TiptapEditor";
export const CreateUpdateBlockInline: React.FC<Props> = ({ export const CreateUpdateBlockInline: React.FC<Props> = ({
handleClose, handleClose,
data, data,
@ -231,9 +219,9 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
description: description:
!data.description || data.description === "" !data.description || data.description === ""
? { ? {
type: "doc", type: "doc",
content: [{ type: "paragraph" }], content: [{ type: "paragraph" }],
} }
: data.description, : data.description,
description_html: data.description_html ?? "<p></p>", description_html: data.description_html ?? "<p></p>",
}); });
@ -291,7 +279,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
render={({ field: { value, onChange } }) => { render={({ field: { value, onChange } }) => {
if (!data) if (!data)
return ( return (
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={editorRef} ref={editorRef}
value={"<p></p>"} value={"<p></p>"}
@ -311,7 +299,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
); );
return ( return (
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
ref={editorRef} ref={editorRef}
value={ value={
@ -334,8 +322,9 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
<div className="m-2 mt-6 flex"> <div className="m-2 mt-6 flex">
<button <button
type="button" type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : "" className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${
}`} iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : ""
}`}
onClick={handleAutoGenerateDescription} onClick={handleAutoGenerateDescription}
disabled={iAmFeelingLucky} disabled={iAmFeelingLucky}
> >
@ -367,8 +356,8 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
? "Updating..." ? "Updating..."
: "Update block" : "Update block"
: isSubmitting : isSubmitting
? "Adding..." ? "Adding..."
: "Add block"} : "Add block"}
</PrimaryButton> </PrimaryButton>
</div> </div>
</form> </form>

View File

@ -19,6 +19,7 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components // components
import { GptAssistantModal } from "components/core"; import { GptAssistantModal } from "components/core";
import { CreateUpdateBlockInline } from "components/pages"; import { CreateUpdateBlockInline } from "components/pages";
import { TipTapEditor } from "components/tiptap";
// ui // ui
import { CustomMenu, TextArea } from "components/ui"; import { CustomMenu, TextArea } from "components/ui";
// icons // icons
@ -38,7 +39,6 @@ import { copyTextToClipboard } from "helpers/string.helper";
import { ICurrentUserResponse, IIssue, IPageBlock, IProject } from "types"; import { ICurrentUserResponse, IIssue, IPageBlock, IProject } from "types";
// fetch-keys // fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys"; import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
type Props = { type Props = {
block: IPageBlock; block: IPageBlock;
@ -48,13 +48,6 @@ type Props = {
user: ICurrentUserResponse | undefined; user: ICurrentUserResponse | undefined;
}; };
const TiptapEditor = React.forwardRef<
ITiptapRichTextEditor,
ITiptapRichTextEditor
>((props, ref) => <Tiptap {...props} forwardedRef={ref} />);
TiptapEditor.displayName = "TiptapEditor";
export const SinglePageBlock: React.FC<Props> = ({ export const SinglePageBlock: React.FC<Props> = ({
block, block,
projectDetails, projectDetails,
@ -328,8 +321,9 @@ export const SinglePageBlock: React.FC<Props> = ({
</div> </div>
) : ( ) : (
<div <div
className={`group relative w-full rounded bg-custom-background-80 text-custom-text-200 ${snapshot.isDragging ? "bg-custom-background-100 p-4 shadow" : "" className={`group relative w-full rounded bg-custom-background-80 text-custom-text-200 ${
}`} snapshot.isDragging ? "bg-custom-background-100 p-4 shadow" : ""
}`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
> >
@ -343,8 +337,9 @@ export const SinglePageBlock: React.FC<Props> = ({
</button> </button>
<div <div
ref={actionSectionRef} ref={actionSectionRef}
className={`absolute top-4 right-2 hidden items-center gap-2 bg-custom-background-80 pl-4 group-hover:!flex ${isMenuActive ? "!flex" : "" className={`absolute top-4 right-2 hidden items-center gap-2 bg-custom-background-80 pl-4 group-hover:!flex ${
}`} isMenuActive ? "!flex" : ""
}`}
> >
{block.issue && block.sync && ( {block.issue && block.sync && (
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded py-1 px-1.5 text-xs"> <div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded py-1 px-1.5 text-xs">
@ -358,8 +353,9 @@ export const SinglePageBlock: React.FC<Props> = ({
)} )}
<button <button
type="button" type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${iAmFeelingLucky ? "cursor-wait" : "" className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${
}`} iAmFeelingLucky ? "cursor-wait" : ""
}`}
onClick={handleAutoGenerateDescription} onClick={handleAutoGenerateDescription}
disabled={iAmFeelingLucky} disabled={iAmFeelingLucky}
> >
@ -455,18 +451,19 @@ export const SinglePageBlock: React.FC<Props> = ({
{showBlockDetails {showBlockDetails
? block.description_html.length > 7 && ( ? block.description_html.length > 7 && (
<TiptapEditor <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
value={block.description_html} value={block.description_html}
customClassName="text-sm min-h-[150px]" customClassName="text-sm min-h-[150px]"
noBorder noBorder
borderOnFocus={false} borderOnFocus={false}
/> />
) : block.description_stripped.length > 0 && ( )
<p className="mt-3 text-sm font-normal text-custom-text-200 h-5 truncate"> : block.description_stripped.length > 0 && (
{block.description_stripped} <p className="mt-3 text-sm font-normal text-custom-text-200 h-5 truncate">
</p> {block.description_stripped}
)} </p>
)}
</div> </div>
</div> </div>
<GptAssistantModal <GptAssistantModal

View File

@ -3,10 +3,10 @@ import { useDebouncedCallback } from "use-debounce";
import { EditorBubbleMenu } from "./bubble-menu"; import { EditorBubbleMenu } from "./bubble-menu";
import { TiptapExtensions } from "./extensions"; import { TiptapExtensions } from "./extensions";
import { TiptapEditorProps } from "./props"; import { TiptapEditorProps } from "./props";
import { useImperativeHandle, useRef } from "react"; import { useImperativeHandle, useRef, forwardRef } from "react";
import { ImageResizer } from "./extensions/image-resize"; import { ImageResizer } from "./extensions/image-resize";
export interface ITiptapRichTextEditor { export interface ITipTapRichTextEditor {
value: string; value: string;
noBorder?: boolean; noBorder?: boolean;
borderOnFocus?: boolean; borderOnFocus?: boolean;
@ -21,7 +21,7 @@ export interface ITiptapRichTextEditor {
debouncedUpdatesEnabled?: boolean; debouncedUpdatesEnabled?: boolean;
} }
const Tiptap = (props: ITiptapRichTextEditor) => { const Tiptap = (props: ITipTapRichTextEditor) => {
const { const {
onChange, onChange,
debouncedUpdatesEnabled, debouncedUpdatesEnabled,
@ -75,8 +75,8 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md
${noBorder ? "" : "border border-custom-border-200"} ${ ${noBorder ? "" : "border border-custom-border-200"} ${
borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0" borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
} ${customClassName}`; } ${customClassName}`;
if (!editor) return null; if (!editor) return null;
editorRef.current = editor; editorRef.current = editor;
@ -98,4 +98,10 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
); );
}; };
export default Tiptap; const TipTapEditor = forwardRef<ITipTapRichTextEditor, ITipTapRichTextEditor>((props, ref) => (
<Tiptap {...props} forwardedRef={ref} />
));
TipTapEditor.displayName = "TipTapEditor";
export { TipTapEditor };

View File

@ -10,7 +10,7 @@ import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
import SettingsNavbar from "layouts/settings-navbar"; import SettingsNavbar from "layouts/settings-navbar";
// components // components
import { ActivityIcon, ActivityMessage } from "components/core"; import { ActivityIcon, ActivityMessage } from "components/core";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap"; import { TipTapEditor } from "components/tiptap";
// icons // icons
import { ArrowTopRightOnSquareIcon, ChatBubbleLeftEllipsisIcon } from "@heroicons/react/24/outline"; import { ArrowTopRightOnSquareIcon, ChatBubbleLeftEllipsisIcon } from "@heroicons/react/24/outline";
// ui // ui
@ -97,7 +97,7 @@ const ProfileActivity = () => {
</p> </p>
</div> </div>
<div className="issue-comments-section p-0"> <div className="issue-comments-section p-0">
<Tiptap <TipTapEditor
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
value={ value={
activityItem?.new_value !== "" activityItem?.new_value !== ""