import { BubbleMenu, BubbleMenuProps, isNodeSelection } from '@tiptap/react'; import { FC, useState } from 'react'; import { IconBold, IconCode, IconItalic, IconStrikethrough, IconUnderline, IconMessage } from '@tabler/icons-react'; import clsx from 'clsx'; import classes from './bubble-menu.module.css'; import { ActionIcon, rem, Tooltip } from '@mantine/core'; import { ColorSelector } from './color-selector'; import { NodeSelector } from './node-selector'; import { draftCommentIdAtom, showCommentPopupAtom } from '@/features/comment/atoms/comment-atom'; import { useAtom } from 'jotai'; import { v4 as uuidv4 } from 'uuid'; export interface BubbleMenuItem { name: string; isActive: () => boolean; command: () => void; icon: typeof IconBold; } type EditorBubbleMenuProps = Omit; export const EditorBubbleMenu: FC = (props) => { const [, setShowCommentPopup] = useAtom(showCommentPopupAtom); const [, setDraftCommentId] = useAtom(draftCommentIdAtom); const items: BubbleMenuItem[] = [ { name: 'bold', isActive: () => props.editor.isActive('bold'), command: () => props.editor.chain().focus().toggleBold().run(), icon: IconBold, }, { name: 'italic', isActive: () => props.editor.isActive('italic'), command: () => props.editor.chain().focus().toggleItalic().run(), icon: IconItalic, }, { name: 'underline', isActive: () => props.editor.isActive('underline'), command: () => props.editor.chain().focus().toggleUnderline().run(), icon: IconUnderline, }, { name: 'strike', isActive: () => props.editor.isActive('strike'), command: () => props.editor.chain().focus().toggleStrike().run(), icon: IconStrikethrough, }, { name: 'code', isActive: () => props.editor.isActive('code'), command: () => props.editor.chain().focus().toggleCode().run(), icon: IconCode, }, ]; const commentItem: BubbleMenuItem = { name: 'comment', isActive: () => props.editor.isActive('comment'), command: () => { const commentId = uuidv4(); props.editor.chain().focus().setCommentDecoration().run(); setDraftCommentId(commentId); setShowCommentPopup(true); }, icon: IconMessage, }; const bubbleMenuProps: EditorBubbleMenuProps = { ...props, shouldShow: ({ state, editor }) => { const { selection } = state; const { empty } = selection; if (editor.isActive('image') || empty || isNodeSelection(selection)) { return false; } return true; }, tippyOptions: { moveTransition: 'transform 0.15s ease-out', onHidden: () => { setIsNodeSelectorOpen(false); setIsColorSelectorOpen(false); setIsLinkSelectorOpen(false); }, }, }; const [isNodeSelectorOpen, setIsNodeSelectorOpen] = useState(false); const [isColorSelectorOpen, setIsColorSelectorOpen] = useState(false); const [isLinkSelectorOpen, setIsLinkSelectorOpen] = useState(false); return ( { setIsNodeSelectorOpen(!isNodeSelectorOpen); setIsColorSelectorOpen(false); setIsLinkSelectorOpen(false); }} /> {items.map((item, index) => ( ))} { setIsColorSelectorOpen(!isColorSelectorOpen); setIsNodeSelectorOpen(false); setIsLinkSelectorOpen(false); }} /> ); };