Skip to content

Commit

Permalink
added changed in editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Diivvuu committed Oct 12, 2024
1 parent 7fe111f commit f76f00b
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 10 deletions.
27 changes: 22 additions & 5 deletions src/app/workspace/[workspaceId]/channel/[channelId]/chat-input.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import Editor from "@/components/Editor";
import dynamic from "next/dynamic";
import Quill from "quill";
import { useRef } from "react";

export const ChatInput = () => {
return <div className="px-5 w-full">
<Editor/>
</div>;
const Editor = dynamic(() => import("@/components/Editor"), { ssr: false });

interface ChatInputProps {
placeholder: string;
}

export const ChatInput = ({ placeholder }: ChatInputProps) => {
const editorRef = useRef<Quill | null>(null);
return (
<div className="px-5 w-full">
<Editor
placeholder={placeholder}
onSubmit={() => {}}
disabled={false}
innerRef={editorRef}
variant="create"
/>
</div>
);
};
4 changes: 2 additions & 2 deletions src/app/workspace/[workspaceId]/channel/[channelId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const ChannelIdPage = () => {
return (
<div className="flex flex-col h-full">
<Header title={channel.name} />
<div className="flex-1"/>
<ChatInput/>
<div className="flex-1" />
<ChatInput placeholder={`Message # ${channel.name}`} />
</div>
);
};
Expand Down
202 changes: 199 additions & 3 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,65 @@
"use client";
import {
MutableRefObject,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react";

import Quill, { QuillOptions } from "quill";
import "quill/dist/quill.snow.css";
import { useEffect, useRef } from "react";

const Editor = () => {
import { Button } from "./ui/button";
import { PiTextAa } from "react-icons/pi";
import { MdSend } from "react-icons/md";
import { Image, Smile } from "lucide-react";
import { Hint } from "./hint";
import { Delta, Op } from "quill/core";
import { cn } from "@/lib/utils";
import { current } from "../../convex/members";

type EditorValue = {
image: File | null;
body: string;
};
//will be used to edit and create messages
interface EditorProps {
onSubmit: ({ image, body }: EditorValue) => void;
onCancel?: () => void;
placeholder?: string;
defaultValue?: Delta | Op[];
disabled?: boolean;
innerRef?: MutableRefObject<Quill | null>;
variant?: "create" | "update";
}

const Editor = ({
onCancel,
onSubmit,
placeholder = "Write Something",
defaultValue = [],
disabled = false,
innerRef,
variant = "create",
}: EditorProps) => {
// cant use ref it wont rerender, need usestate to keep on checking length of text inside the editor
const [text, setText] = useState("");
const [isToolbarVisible, setIsToolbarVisible] = useState(true);
//using ref bnecause it wont rerender the editor on every change
const containerRef = useRef<HTMLDivElement>(null);
const submitRef = useRef(onSubmit);
const placeholderRef = useRef(placeholder);
const quillRef = useRef<Quill | null>(null);
const defaultValueRef = useRef(defaultValue);
const disabledRef = useRef(disabled);

useLayoutEffect(() => {
submitRef.current = onSubmit;
placeholderRef.current = placeholder;
defaultValueRef.current = defaultValue;
disabledRef.current = disabled;
});

useEffect(() => {
if (typeof window === "undefined" || !containerRef.current) return;
Expand All @@ -13,20 +68,161 @@ const Editor = () => {
const editorContainer = container.appendChild(
container.ownerDocument.createElement("div")
);
const toolbarOptions = [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
["link", "formula"],
[{ list: "ordered" }, { list: "bullet" }, { list: "check" }],
[{ size: ["small", false, "large", "huge"] }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }],
[{ font: [] }],
[{ align: [] }],
];
const options: QuillOptions = {
theme: "snow",
placeholder: placeholderRef.current,

modules: {
toolbar: toolbarOptions,
keyboard: {
bindings: {
enter: {
key: "Enter",
handler: () => {
return; //todo submit form
},
},
shift_enter: {
key: "Enter",
shiftKey: true,
handler: () => {
quill.insertText(quill.getSelection()?.index || 0, "\n");
},
},
},
},
},
};
new Quill(editorContainer, options);
const quill = new Quill(editorContainer, options);
quillRef.current = quill;
quillRef.current.focus();

if (innerRef) {
innerRef.current = quill;
}

quill.setContents(defaultValueRef.current);
setText(quill.getText());

quill.on(Quill.events.TEXT_CHANGE, () => {
setText(quill.getText());
});

return () => {
quill.off(Quill.events.TEXT_CHANGE);
if (container) container.innerHTML = "";

if (quillRef.current) {
quillRef.current = null;
}

if (innerRef) {
innerRef.current = null;
}
};
}, []);

const isEmpty = text.replace(/<(.|\n)*?>/g, "").trim().length === 0;

const toggleToolbar = () => {
setIsToolbarVisible((current) => !current);
const toolbarElement = containerRef.current?.querySelector(".ql-toolbar");

if (toolbarElement) {
toolbarElement.classList.toggle("hidden");
}
};

return (
<div className="flex flex-col">
<div className="flex flex-col border border-slate-200 rounded-md overflow-hidden focus-within:border-slate-300 focus-within:shadow-sm transition bg-white">
<div ref={containerRef} className="h-full ql-custom" />
<div className="flex px-2 pb-2 z-[5]">
<Hint
label={isToolbarVisible ? "Hide Formatting" : "Show Formatting"}
>
<Button
disabled={disabled}
variant="ghost"
onClick={toggleToolbar}
size="iconSm"
>
<PiTextAa className="size-4" />
</Button>
</Hint>
<Hint label="Emoji">
<Button
disabled={disabled}
variant="ghost"
onClick={() => {}}
size="iconSm"
>
<Smile className="size-4" />
</Button>
</Hint>
{variant === "create" && (
<Hint label="Image">
<Button
disabled={disabled}
variant="ghost"
onClick={() => {}}
size="iconSm"
>
<Image className="size-4" />
</Button>
</Hint>
)}
{variant === "create" ? (
<Button
disabled={disabled || isEmpty}
onClick={() => {}}
className={cn(
"ml-auto",
!isEmpty
? "bg-[#007a5a] hover:bg-[#007a5a]/80 text-white"
: "bg-white hover:bg-[#007a5a]/80 text-muted-foreground"
)}
size="iconSm"
>
<MdSend className="size-4" />
</Button>
) : (
<div className="ml-auto flex items-center gap-x-2">
<Button
variant="outline"
size="sm"
onClick={() => {}}
disabled={disabled || isEmpty}
>
Cancel
</Button>
<Button
size="sm"
onClick={() => {}}
disabled={false}
className="bg-[#007a5a] hover:bg-[#007a5a]/80 text-white"
>
Save
</Button>
</div>
)}
</div>
</div>
<div className="p-2 text-[10px] text-muted-foreground flex justify-end">
<p>
<strong>Shift + Return</strong> to add a new line
</p>
</div>
</div>
);
Expand Down

0 comments on commit f76f00b

Please sign in to comment.