diff --git a/packages/editor/core/src/ui/menus/bubble-menu/index.tsx b/packages/editor/core/src/ui/menus/bubble-menu/index.tsx deleted file mode 100644 index 686cd9194..000000000 --- a/packages/editor/core/src/ui/menus/bubble-menu/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { BubbleMenu, BubbleMenuProps } from "@tiptap/react"; -import { FC, useState } from "react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; - -import { NodeSelector } from "./node-selector"; -import { LinkSelector } from "./link-selector"; -import { cn } from "../../../lib/utils"; - -export interface BubbleMenuItem { - name: string; - isActive: () => boolean; - command: () => void; - icon: typeof BoldIcon; -} - -type EditorBubbleMenuProps = Omit; - -export const EditorBubbleMenu: FC = (props: any) => { - 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 bubbleMenuProps: EditorBubbleMenuProps = { - ...props, - shouldShow: ({ editor }) => { - if (!editor.isEditable) { - return false; - } - if (editor.isActive("image")) { - return false; - } - return editor.view.state.selection.content().size > 0; - }, - tippyOptions: { - moveTransition: "transform 0.15s ease-out", - onHidden: () => { - setIsNodeSelectorOpen(false); - setIsLinkSelectorOpen(false); - }, - }, - }; - - const [isNodeSelectorOpen, setIsNodeSelectorOpen] = useState(false); - const [isLinkSelectorOpen, setIsLinkSelectorOpen] = useState(false); - - return ( - - {!props.editor.isActive("table") && ( - { - setIsNodeSelectorOpen(!isNodeSelectorOpen); - setIsLinkSelectorOpen(false); - }} - /> - )} - { - setIsLinkSelectorOpen(!isLinkSelectorOpen); - setIsNodeSelectorOpen(false); - }} - /> -
- {items.map((item, index) => ( - - ))} -
-
- ); -}; diff --git a/packages/editor/core/src/ui/menus/bubble-menu/link-selector.tsx b/packages/editor/core/src/ui/menus/bubble-menu/link-selector.tsx deleted file mode 100644 index 7060ff9d8..000000000 --- a/packages/editor/core/src/ui/menus/bubble-menu/link-selector.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { cn } from "../../../lib/utils"; -import { Editor } from "@tiptap/core"; -import { Check, Trash } from "lucide-react"; -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from "react"; -import isValidHttpUrl from "./utils"; - -interface LinkSelectorProps { - editor: Editor; - isOpen: boolean; - setIsOpen: Dispatch>; -} - -export const LinkSelector: FC = ({ editor, isOpen, setIsOpen }) => { - const inputRef = useRef(null); - - const onLinkSubmit = useCallback(() => { - const input = inputRef.current; - const url = input?.value; - if (url && isValidHttpUrl(url)) { - editor.chain().focus().setLink({ href: url }).run(); - setIsOpen(false); - } - }, [editor, inputRef, setIsOpen]); - - useEffect(() => { - inputRef.current && inputRef.current?.focus(); - }); - - return ( -
- - {isOpen && ( -
{ - if (e.key === "Enter") { - e.preventDefault(); - onLinkSubmit(); - } - }} - > - - {editor.getAttributes("link").href ? ( - - ) : ( - - )} -
- )} -
- ); -}; diff --git a/packages/editor/core/src/ui/menus/bubble-menu/node-selector.tsx b/packages/editor/core/src/ui/menus/bubble-menu/node-selector.tsx deleted file mode 100644 index c3003be23..000000000 --- a/packages/editor/core/src/ui/menus/bubble-menu/node-selector.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { cn } from "../../../lib/utils"; -import { Editor } from "@tiptap/core"; -import { - Check, - ChevronDown, - Heading1, - Heading2, - Heading3, - TextQuote, - ListOrdered, - TextIcon, - Code, - CheckSquare, -} from "lucide-react"; -import { Dispatch, FC, SetStateAction } from "react"; - -import { BubbleMenuItem } from "."; - -interface NodeSelectorProps { - editor: Editor; - isOpen: boolean; - setIsOpen: Dispatch>; -} - -export const NodeSelector: FC = ({ editor, isOpen, setIsOpen }) => { - const items: BubbleMenuItem[] = [ - { - name: "Text", - icon: TextIcon, - command: () => editor.chain().focus().toggleNode("paragraph", "paragraph").run(), - isActive: () => - editor.isActive("paragraph") && - !editor.isActive("bulletList") && - !editor.isActive("orderedList"), - }, - { - name: "H1", - icon: Heading1, - command: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), - isActive: () => editor.isActive("heading", { level: 1 }), - }, - { - name: "H2", - icon: Heading2, - command: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), - isActive: () => editor.isActive("heading", { level: 2 }), - }, - { - name: "H3", - icon: Heading3, - command: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), - isActive: () => editor.isActive("heading", { level: 3 }), - }, - { - name: "To-do List", - icon: CheckSquare, - command: () => editor.chain().focus().toggleTaskList().run(), - isActive: () => editor.isActive("taskItem"), - }, - { - name: "Bullet List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleBulletList().run(), - isActive: () => editor.isActive("bulletList"), - }, - { - name: "Numbered List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleOrderedList().run(), - isActive: () => editor.isActive("orderedList"), - }, - { - name: "Quote", - icon: TextQuote, - command: () => - editor.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(), - isActive: () => editor.isActive("blockquote"), - }, - { - name: "Code", - icon: Code, - command: () => editor.chain().focus().toggleCodeBlock().run(), - isActive: () => editor.isActive("codeBlock"), - }, - ]; - - const activeItem = items.filter((item) => item.isActive()).pop() ?? { - name: "Multiple", - }; - - return ( -
- - - {isOpen && ( -
- {items.map((item, index) => ( - - ))} -
- )} -
- ); -}; diff --git a/packages/editor/core/src/ui/menus/bubble-menu/utils/index.tsx b/packages/editor/core/src/ui/menus/bubble-menu/utils/index.tsx deleted file mode 100644 index b5add3f54..000000000 --- a/packages/editor/core/src/ui/menus/bubble-menu/utils/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export default function isValidHttpUrl(string: string): boolean { - let url: URL; - - try { - url = new URL(string); - } catch (_) { - return false; - } - - return url.protocol === "http:" || url.protocol === "https:"; -} diff --git a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx index b9ce6159d..14fd73a83 100644 --- a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx @@ -1,10 +1,10 @@ import { BubbleMenu, BubbleMenuProps } from "@tiptap/react"; import { FC, useState } from "react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; +import { BoldIcon } from "lucide-react"; import { NodeSelector } from "./node-selector"; import { LinkSelector } from "./link-selector"; -import { cn } from "@plane/editor-core"; +import { BoldItem, cn, CodeItem, ItalicItem, StrikeThroughItem, UnderLineItem } from "@plane/editor-core"; export interface BubbleMenuItem { name: string; @@ -17,36 +17,11 @@ type EditorBubbleMenuProps = Omit; export const EditorBubbleMenu: FC = (props: any) => { 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, - }, + BoldItem(props.editor), + ItalicItem(props.editor), + UnderLineItem(props.editor), + StrikeThroughItem(props.editor), + CodeItem(props.editor), ]; const bubbleMenuProps: EditorBubbleMenuProps = {