From 95439fbbef22437f852be1cbd50ec8f37560df4d Mon Sep 17 00:00:00 2001 From: Palanikannan1437 <73993394+Palanikannan1437@users.noreply.github.com> Date: Wed, 4 Oct 2023 01:06:16 +0530 Subject: [PATCH] added comment editor with fixed menu --- packages/editor/core/src/index.ts | 4 + .../editor/core/src/lib/editor-commands.ts | 36 ++++ .../core/src/ui/menus/fixed-menu/icon.tsx | 13 -- .../core/src/ui/menus/fixed-menu/index.tsx | 112 ------------ .../core/src/ui/menus/menu-items/index.tsx | 81 +++++++++ .../editor/lite-text-editor/src/ui/index.tsx | 2 +- .../src/ui/menus/fixed-menu/index.tsx | 163 ++++++++++++------ .../src/ui/extensions/slash-command.tsx | 1 + web/components/issues/description-form.tsx | 6 +- 9 files changed, 234 insertions(+), 184 deletions(-) create mode 100644 packages/editor/core/src/lib/editor-commands.ts delete mode 100644 packages/editor/core/src/ui/menus/fixed-menu/icon.tsx delete mode 100644 packages/editor/core/src/ui/menus/fixed-menu/index.tsx create mode 100644 packages/editor/core/src/ui/menus/menu-items/index.tsx diff --git a/packages/editor/core/src/index.ts b/packages/editor/core/src/index.ts index efab12eb1..bb381b3bc 100644 --- a/packages/editor/core/src/index.ts +++ b/packages/editor/core/src/index.ts @@ -14,3 +14,7 @@ export { EditorContentWrapper } from "./ui/components/editor-content"; // hooks export { useEditor } from "./ui/hooks/useEditor"; export { useReadOnlyEditor } from "./ui/hooks/useReadOnlyEditor"; + +// helper items +export * from "./ui/menus/menu-items"; +export * from "./lib/editor-commands"; diff --git a/packages/editor/core/src/lib/editor-commands.ts b/packages/editor/core/src/lib/editor-commands.ts new file mode 100644 index 000000000..481160b90 --- /dev/null +++ b/packages/editor/core/src/lib/editor-commands.ts @@ -0,0 +1,36 @@ +import { Editor } from "@tiptap/react"; +import { UploadImage } from "../types/upload-image"; +import { startImageUpload } from "../ui/plugins/upload-image"; + +export const toggleBold = (editor: Editor) => editor?.chain().focus().toggleBold().run(); + +export const toggleItalic = (editor: Editor) => editor?.chain().focus().toggleItalic().run(); + +export const toggleUnderline = (editor: Editor) => editor?.chain().focus().toggleUnderline().run(); + +export const toggleStrike = (editor: Editor) => editor?.chain().focus().toggleStrike().run(); + +export const toggleCode = (editor: Editor) => editor?.chain().focus().toggleCode().run(); + +export const toggleBulletList = (editor: Editor) => editor?.chain().focus().toggleBulletList().run(); + +export const toggleOrderedList = (editor: Editor) => editor?.chain().focus().toggleOrderedList().run(); + +export const toggleBlockquote = (editor: Editor) => editor?.chain().focus().toggleBlockquote().run(); + +export const insertTable = (editor: Editor) => editor?.chain().focus().insertTable().run(); + +export const insertImage = (editor: Editor, uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = "image/*"; + input.onchange = async () => { + if (input.files?.length) { + const file = input.files[0]; + const pos = editor.view.state.selection.from; + startImageUpload(file, editor.view, pos, uploadFile, setIsSubmitting); + } + }; + input.click(); +}; + diff --git a/packages/editor/core/src/ui/menus/fixed-menu/icon.tsx b/packages/editor/core/src/ui/menus/fixed-menu/icon.tsx deleted file mode 100644 index c0006b3f2..000000000 --- a/packages/editor/core/src/ui/menus/fixed-menu/icon.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; - -type Props = { - iconName: string; - className?: string; -}; - -export const Icon: React.FC = ({ iconName, className = "" }) => ( - - {iconName} - -); - diff --git a/packages/editor/core/src/ui/menus/fixed-menu/index.tsx b/packages/editor/core/src/ui/menus/fixed-menu/index.tsx deleted file mode 100644 index c9b8cfcb1..000000000 --- a/packages/editor/core/src/ui/menus/fixed-menu/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Editor } from "@tiptap/react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; - -import { cn } from "../../../lib/utils"; -import { Tooltip } from "../table-menu/tooltip"; -import { Icon } from "./icon"; - -export interface BubbleMenuItem { - name: string; - isActive: () => boolean; - command: () => void; - icon: typeof BoldIcon; -} - -type EditorBubbleMenuProps = { - editor: Editor; - accessValue: string; - onAccessChange: (accessKey: string) => void; - commentAccess: { - icon: string; - key: string; - label: "Private" | "Public"; - }[] | undefined; -} - -export const FixedMenu = (props: EditorBubbleMenuProps) => { - const items: BubbleMenuItem[] = [ - { - name: "bold", - isActive: () => props.editor?.isActive("bold"), - command: () => props.editor?.chain().focus().toggleBold().run(), - icon: BoldIcon, - }, - { - name: "italic", - isActive: () => props.editor?.isActive("italic"), - command: () => props.editor?.chain().focus().toggleItalic().run(), - icon: ItalicIcon, - }, - { - name: "underline", - isActive: () => props.editor?.isActive("underline"), - command: () => props.editor?.chain().focus().toggleUnderline().run(), - icon: UnderlineIcon, - }, - { - name: "strike", - isActive: () => props.editor?.isActive("strike"), - command: () => props.editor?.chain().focus().toggleStrike().run(), - icon: StrikethroughIcon, - }, - { - name: "code", - isActive: () => props.editor?.isActive("code"), - command: () => props.editor?.chain().focus().toggleCode().run(), - icon: CodeIcon, - }, - ]; - - const handleAccessChange = (accessKey: string) => { - props.onAccessChange(accessKey); - }; - - - return ( -
-
-
- {props?.commentAccess?.map((access) => ( - - - - ))} -
- {items.map((item, index) => ( - - ))} -
-
- ); -}; diff --git a/packages/editor/core/src/ui/menus/menu-items/index.tsx b/packages/editor/core/src/ui/menus/menu-items/index.tsx new file mode 100644 index 000000000..581404b52 --- /dev/null +++ b/packages/editor/core/src/ui/menus/menu-items/index.tsx @@ -0,0 +1,81 @@ +import { BoldIcon, QuoteIcon, ImageIcon, TableIcon, ListIcon, ListOrderedIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; +import { Editor } from "@tiptap/react"; +import { UploadImage } from "../../../types/upload-image"; +import { insertImage, insertTable, toggleBlockquote, toggleBold, toggleBulletList, toggleCode, toggleItalic, toggleOrderedList, toggleStrike } from "../../../lib/editor-commands"; + +export interface EditorMenuItem { + name: string; + isActive: () => boolean; + command: () => void; + icon: typeof BoldIcon; +} + +export const BoldItem = (editor: Editor): EditorMenuItem => ({ + name: "bold", + isActive: () => editor?.isActive("bold"), + command: () => toggleBold(editor), + icon: BoldIcon, +}) + +export const ItalicItem = (editor: Editor): EditorMenuItem => ({ + name: "italic", + isActive: () => editor?.isActive("italic"), + command: () => toggleItalic(editor), + icon: ItalicIcon, +}) + +export const UnderLineItem = (editor: Editor): EditorMenuItem => ({ + name: "underline", + isActive: () => editor?.isActive("underline"), + command: () => UnderLineItem(editor), + icon: UnderlineIcon, +}) + +export const StrikeThroughItem = (editor: Editor): EditorMenuItem => ({ + name: "strike", + isActive: () => editor?.isActive("strike"), + command: () => toggleStrike(editor), + icon: StrikethroughIcon, +}) + +export const CodeItem = (editor: Editor): EditorMenuItem => ({ + name: "code", + isActive: () => editor?.isActive("code"), + command: () => toggleCode(editor), + icon: CodeIcon, +}) + +export const BulletListItem = (editor: Editor): EditorMenuItem => ({ + name: "bullet-list", + isActive: () => editor?.isActive("bulletList"), + command: () => toggleBulletList(editor), + icon: ListIcon, +}) + +export const NumberedListItem = (editor: Editor): EditorMenuItem => ({ + name: "ordered-list", + isActive: () => editor?.isActive("orderedList"), + command: () => toggleOrderedList(editor), + icon: ListOrderedIcon +}) + +export const QuoteItem = (editor: Editor): EditorMenuItem => ({ + name: "quote", + isActive: () => editor?.isActive("quote"), + command: () => toggleBlockquote(editor), + icon: QuoteIcon +}) + +export const TableItem = (editor: Editor): EditorMenuItem => ({ + name: "quote", + isActive: () => editor?.isActive("table"), + command: () => insertTable(editor), + icon: TableIcon +}) + +export const ImageItem = (editor: Editor, uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void): EditorMenuItem => ({ + name: "image", + isActive: () => editor?.isActive("image"), + command: () => insertImage(editor, uploadFile, setIsSubmitting), + icon: ImageIcon, +}) diff --git a/packages/editor/lite-text-editor/src/ui/index.tsx b/packages/editor/lite-text-editor/src/ui/index.tsx index 34edaf878..0b9386724 100644 --- a/packages/editor/lite-text-editor/src/ui/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/index.tsx @@ -83,7 +83,7 @@ const LiteTextEditor = ({ {(editable !== false) && (
- +
) } diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx index 60bc0bbda..1a6068364 100644 --- a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx @@ -1,9 +1,10 @@ import { Editor } from "@tiptap/react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; +import { BoldIcon } from "lucide-react"; -import { cn } from "@plane/editor-core"; +import { BoldItem, BulletListItem, cn, CodeItem, ImageItem, ItalicItem, NumberedListItem, QuoteItem, StrikeThroughItem, TableItem, UnderLineItem } from "@plane/editor-core"; import { Icon } from "./icon"; import { Tooltip } from "../../tooltip"; +import { UploadImage } from "../.."; export interface BubbleMenuItem { name: string; @@ -24,40 +25,31 @@ type EditorBubbleMenuProps = { label: "Private" | "Public"; }[] | undefined; } + uploadFile: UploadImage; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; } export const FixedMenu = (props: EditorBubbleMenuProps) => { - const items: BubbleMenuItem[] = [ - { - name: "bold", - isActive: () => props.editor?.isActive("bold"), - command: () => props.editor?.chain().focus().toggleBold().run(), - icon: BoldIcon, - }, - { - name: "italic", - isActive: () => props.editor?.isActive("italic"), - command: () => props.editor?.chain().focus().toggleItalic().run(), - icon: ItalicIcon, - }, - { - name: "underline", - isActive: () => props.editor?.isActive("underline"), - command: () => props.editor?.chain().focus().toggleUnderline().run(), - icon: UnderlineIcon, - }, - { - name: "strike", - isActive: () => props.editor?.isActive("strike"), - command: () => props.editor?.chain().focus().toggleStrike().run(), - icon: StrikethroughIcon, - }, - { - name: "code", - isActive: () => props.editor?.isActive("code"), - command: () => props.editor?.chain().focus().toggleCode().run(), - icon: CodeIcon, - }, + const basicMarkItems: BubbleMenuItem[] = [ + BoldItem(props.editor), + ItalicItem(props.editor), + UnderLineItem(props.editor), + StrikeThroughItem(props.editor), + ]; + + const listItems: BubbleMenuItem[] = [ + BulletListItem(props.editor), + NumberedListItem(props.editor), + ]; + + const userActionItems: BubbleMenuItem[] = [ + QuoteItem(props.editor), + CodeItem(props.editor), + ]; + + const complexItems: BubbleMenuItem[] = [ + TableItem(props.editor), + ImageItem(props.editor, props.uploadFile, props.setIsSubmitting), ]; const handleAccessChange = (accessKey: string) => { @@ -69,28 +61,91 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
-
- {props.commentAccessSpecifier && (
- {props?.commentAccessSpecifier.commentAccess?.map((access) => ( - - - - ))} -
)} - {items.map((item, index) => ( + /> + + + ))} +
)} +
+ {basicMarkItems.map((item, index) => ( + + ))} +
+
+ {listItems.map((item, index) => ( + + ))} +
+
+ {userActionItems.map((item, index) => ( + + ))} +
+
+ {complexItems.map((item, index) => (