import { createEffect, onCleanup, onMount, Show } from 'solid-js' import { createEditorTransaction, createTiptapEditor, useEditorHTML, useEditorIsEmpty, useEditorIsFocused } from 'solid-tiptap' import { useEditorContext } from '../../context/editor' import { Document } from '@tiptap/extension-document' import { Text } from '@tiptap/extension-text' import { Paragraph } from '@tiptap/extension-paragraph' import { Bold } from '@tiptap/extension-bold' import { Button } from '../_shared/Button' import { useLocalize } from '../../context/localize' import { Icon } from '../_shared/Icon' import { Popover } from '../_shared/Popover' import { Italic } from '@tiptap/extension-italic' import { Modal } from '../Nav/Modal' import { hideModal, showModal } from '../../stores/ui' import { Blockquote } from '@tiptap/extension-blockquote' import { CustomImage } from './extensions/CustomImage' import { UploadModalContent } from './UploadModalContent' import { imageProxy } from '../../utils/imageProxy' import { clsx } from 'clsx' import styles from './SimplifiedEditor.module.scss' import { Placeholder } from '@tiptap/extension-placeholder' import { InsertLinkForm } from './InsertLinkForm' import { Link } from '@tiptap/extension-link' type Props = { initialContent?: string onSubmit: (text: string) => void placeholder: string submitButtonText?: string quoteEnabled?: boolean imageEnabled?: boolean setClear?: boolean smallHeight?: boolean submitByEnter?: boolean submitByShiftEnter?: boolean } const SimplifiedEditor = (props: Props) => { const { t } = useLocalize() const editorElRef: { current: HTMLDivElement } = { current: null } const { actions: { setEditor } } = useEditorContext() const editor = createTiptapEditor(() => ({ element: editorElRef.current, editorProps: { attributes: { class: styles.simplifiedEditorField } }, extensions: [ Document, Text, Paragraph, Bold, Italic, Link.configure({ openOnClick: false }), Blockquote.configure({ HTMLAttributes: { class: styles.blockQuote } }), CustomImage.configure({ HTMLAttributes: { class: styles.uploadedImage } }), Placeholder.configure({ emptyNodeClass: styles.emptyNode, placeholder: props.placeholder }) ], content: props.initialContent ?? null })) setEditor(editor) const isEmpty = useEditorIsEmpty(() => editor()) const isFocused = useEditorIsFocused(() => editor()) const isActive = (name: string) => createEditorTransaction( () => editor(), (ed) => { return ed && ed.isActive(name) } ) const html = useEditorHTML(() => editor()) const isBold = isActive('bold') const isItalic = isActive('italic') const isLink = isActive('link') const isBlockquote = isActive('blockquote') const renderImage = (src: string) => { editor() .chain() .focus() .setImage({ src: imageProxy(src) }) .run() hideModal() } const handleClear = () => { editor().commands.clearContent(true) } createEffect(() => { if (props.setClear) { editor().commands.clearContent(true) } }) const handleKeyDown = async (event) => { if (isEmpty() || !editor().isFocused) { return } if ( event.code === 'Enter' && ((props.submitByEnter && !event.shiftKey) || (props.submitByShiftEnter && event.shiftKey)) ) { event.preventDefault() props.onSubmit(html()) handleClear() } if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !editor().state.selection.empty) { event.preventDefault() showModal('editorInsertLink') } } onMount(() => { window.addEventListener('keydown', handleKeyDown) }) onCleanup(() => { window.removeEventListener('keydown', handleKeyDown) }) const handleInsertLink = () => !editor().state.selection.empty && showModal('editorInsertLink') return (