feat: add new editor

This commit is contained in:
Kush Thaker 2024-09-10 23:56:36 +05:30
parent a5e2aec4b1
commit e8d1cf8843
4 changed files with 131 additions and 11 deletions

View file

@ -31,7 +31,7 @@ import ComboboxWithCreate from "@repo/ui/shadcn/combobox";
import { StoredSpace } from "@repo/db/schema";
import { useKeyPress } from "@/lib/useKeyPress";
import { useFormStatus } from "react-dom";
import { usePendingJob } from "@/contexts/PendingJobContext";
import EditorJSEditor from "@/components/newEditor/editor";
function Menu() {
useKeyPress("a", () => {
@ -219,7 +219,7 @@ function DialogContentMenu({ setDialogClose }: { setDialogClose: () => void }) {
setSelectedSpaces([]);
return cont;
};
const { pendingJobs, setPendingJobs } = usePendingJob();
const formSubmit = () => {
toast.promise(handleSubmit(content, selectedSpaces), {
loading: (
@ -228,14 +228,8 @@ function DialogContentMenu({ setDialogClose }: { setDialogClose: () => void }) {
Creating memory...
</span>
),
success: (data) => {
setPendingJobs(pendingJobs + 1);
return "Memory queued";
},
error: (error) => {
setPendingJobs(pendingJobs - 1);
return `Memory creation failed: ${error}`;
},
success: (data) => "Memory queued",
error: (error) => `Memory creation failed: ${error}`,
richColors: true,
});
};
@ -341,7 +335,13 @@ function DialogContentMenu({ setDialogClose }: { setDialogClose: () => void }) {
function EditorDialog({ setContent }: { setContent: (e: string) => void }) {
return (
<DialogContent className="max-w-[98vw] h-[98vh] overflow-hidden px-0 py-0 z-[100]">
<Editor setContent={(e) => setContent(e)} />
<div id="editorjs" className="h-full">
<EditorJSEditor
setContent={(e) => setContent(e)}
data={{}} // Add an empty object or initial data
holder="editorjs" // Add a unique ID for the editor container
/>
</div>
</DialogContent>
);
}

View file

@ -0,0 +1,105 @@
import React, { useEffect, useRef } from "react";
import EditorJS, { ToolConstructable } from "@editorjs/editorjs";
//@ts-ignore
import CheckList from "@editorjs/checklist";
//@ts-ignore
import Code from "@editorjs/code";
import Delimiter from "@editorjs/delimiter";
//@ts-ignore
import Embed from "@editorjs/embed";
import ImageTool from "@editorjs/image";
//@ts-ignore
import InlineCode from "@editorjs/inline-code";
import List from "@editorjs/list";
import Quote from "@editorjs/quote";
//@ts-ignore
import Table from "@editorjs/table";
//@ts-ignore
import Paragraph from "@editorjs/paragraph";
import Header from "@editorjs/header";
//@ts-ignore
import Raw from "@editorjs/raw";
//@ts-ignore
import LinkTool from "@editorjs/link";
interface EditorJSEditorProps {
setContent: (content: string) => void;
data: any;
holder: string;
}
const EDITOR_TOOLS = {
code: Code,
header: {
class: Header as unknown as ToolConstructable,
shortcut: "CMD+H",
inlineToolbar: true,
config: {
placeholder: "Enter a Header",
levels: [2, 3, 4],
defaultLevel: 2,
},
},
paragraph: {
class: Paragraph,
// shortcut: 'CMD+P',
inlineToolbar: true,
},
checklist: CheckList,
inlineCode: InlineCode,
table: Table,
list: List,
quote: Quote,
delimiter: Delimiter,
raw: Raw,
linkTool: {
class: LinkTool,
config: {
endpoint: "http://localhost:3000/", //Currently, the plugin's design supports the 'title', 'image', and 'description' fields. They should have the following format in the response:
},
},
image: {
class: ImageTool as unknown as ToolConstructable,
config: {
endpoints: {
byFile: "http://localhost:8008/uploadFile", // Your backend file uploader endpoint
byUrl: "http://localhost:8008/fetchUrl", // Your endpoint that provides uploading by Url
},
},
},
};
const EditorJSEditor: React.FC<EditorJSEditorProps> = ({
setContent,
holder,
data,
}) => {
const editorRef = useRef<EditorJS | null>(null);
// const [editorData, setEditorData] = useState<OutputData | undefined>(
// initialData,
// );
useEffect(() => {
if (!editorRef.current) {
editorRef.current = new EditorJS({
holder: holder,
placeholder: "Start writing here....",
tools: {
...EDITOR_TOOLS,
},
onChange: async () => {
const content = await editorRef.current?.save();
setContent(JSON.stringify(content));
},
});
}
return () => {
if (editorRef.current && editorRef.current.destroy) {
editorRef.current.destroy();
}
};
}, [setContent, holder]);
return <div className="prose prose-lg dark:prose-invert max-w-full" />;
};
export default EditorJSEditor;

View file

View file

@ -17,6 +17,20 @@
"update-prod-db": "wrangler d1 execute prod-d1-supermemory --remote"
},
"dependencies": {
"@editorjs/checklist": "^1.6.0",
"@editorjs/code": "^2.9.0",
"@editorjs/delimiter": "^1.4.2",
"@editorjs/editorjs": "^2.30.5",
"@editorjs/embed": "^2.7.4",
"@editorjs/header": "^2.8.7",
"@editorjs/image": "^2.9.3",
"@editorjs/inline-code": "^1.5.1",
"@editorjs/link": "^2.6.2",
"@editorjs/list": "^1.10.0",
"@editorjs/paragraph": "^2.11.6",
"@editorjs/quote": "^2.7.2",
"@editorjs/raw": "^2.5.0",
"@editorjs/table": "^2.4.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.1.0",
@ -32,6 +46,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"drizzle-kit": "0.21.2",
"drizzle-orm": "0.30.0",
"lowlight": "^3.1.0",
"million": "^3.1.11",