import type { Editor } from '@tiptap/core' import { clsx } from 'clsx' import { Match, Show, Switch, createEffect, createSignal, lazy, onCleanup, onMount } from 'solid-js' import { createEditorTransaction } from 'solid-tiptap' import { Icon } from '~/components/_shared/Icon' import { Popover } from '~/components/_shared/Popover' import { useLocalize } from '~/context/localize' import { InsertLinkForm } from '../EditorToolbar/InsertLinkForm' import styles from './TextBubbleMenu.module.scss' const MiniEditor = lazy(() => import('../../Editor/MiniEditor/MiniEditor')) type BubbleMenuProps = { editor: Editor isCommonMarkup: boolean ref: (el: HTMLDivElement) => void shouldShow: boolean } export const TextBubbleMenu = (props: BubbleMenuProps) => { const { t } = useLocalize() const isActive = (name: string, attributes?: Record) => createEditorTransaction( () => props.editor, (editor) => editor?.isActive(name, attributes) ) const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false) const [listBubbleOpen, setListBubbleOpen] = createSignal(false) const [linkEditorOpen, setLinkEditorOpen] = createSignal(false) const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false) const [footNote, setFootNote] = createSignal() createEffect(() => { if (!props.shouldShow) { setFootNote() setFootnoteEditorOpen(false) setLinkEditorOpen(false) setTextSizeBubbleOpen(false) setListBubbleOpen(false) } }) const isBold = isActive('bold') const isItalic = isActive('italic') const isH1 = isActive('heading', { level: 2 }) const isH2 = isActive('heading', { level: 3 }) const isH3 = isActive('heading', { level: 4 }) const isQuote = isActive('blockquote', { 'data-type': 'quote' }) const isPunchLine = isActive('blockquote', { 'data-type': 'punchline' }) const isOrderedList = isActive('isOrderedList') const isBulletList = isActive('isBulletList') const isLink = isActive('link') const isHighlight = isActive('highlight') const isFootnote = isActive('footnote') const isIncut = isActive('article') const toggleTextSizePopup = () => { if (listBubbleOpen()) { setListBubbleOpen(false) } setTextSizeBubbleOpen((prev) => !prev) } const toggleListPopup = () => { if (textSizeBubbleOpen()) { setTextSizeBubbleOpen(false) } setListBubbleOpen((prev) => !prev) } const handleKeyDown = (event: KeyboardEvent) => { if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !props.editor.state.selection.empty) { event.preventDefault() setLinkEditorOpen(true) } } const updateCurrentFootnoteValue = createEditorTransaction( () => props.editor, (ed) => { if (!isFootnote()) { return } const value = ed.getAttributes('footnote').value setFootNote(value) } ) const handleAddFootnote = (footnote: string) => { if (footNote()) { props.editor?.chain().focus().updateFootnote({ value: footnote }).run() } else { props.editor?.chain().focus().setFootnote({ value: footnote }).run() } setFootNote() setLinkEditorOpen(false) setFootnoteEditorOpen(false) } const handleOpenFootnoteEditor = () => { updateCurrentFootnoteValue() setLinkEditorOpen(false) setFootnoteEditorOpen(true) } const handleSetPunchline = () => { if (isPunchLine()) { props.editor?.chain().focus().toggleBlockquote('punchline').run() } props.editor?.chain().focus().toggleBlockquote('quote').run() toggleTextSizePopup() } const handleSetQuote = () => { if (isQuote()) { props.editor?.chain().focus().toggleBlockquote('quote').run() } props.editor?.chain().focus().toggleBlockquote('punchline').run() toggleTextSizePopup() } onMount(() => { window.addEventListener('keydown', handleKeyDown) onCleanup(() => { window.removeEventListener('keydown', handleKeyDown) setLinkEditorOpen(false) }) }) const handleOpenLinkForm = () => { props.editor?.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run() setLinkEditorOpen(true) } const handleCloseLinkForm = () => { setLinkEditorOpen(false) props.editor?.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run() } return (
handleAddFootnote(value)} content={footNote()} onCancel={() => { setFootnoteEditorOpen(false) }} /> <> <>
{t('Headers')}
{(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )}
{t('Quotes')}
{(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )}
{t('squib')}
{(triggerRef: (el: HTMLElement) => void) => ( )}
{(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )}
{t('Add url')}
}> {(triggerRef: (el: HTMLElement) => void) => ( )} <> {(triggerRef: (el: HTMLElement) => void) => ( )}
{t('Lists')}
{(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => ( )}
) }