import Placeholder from '@tiptap/extension-placeholder'; import { EditorContent, useEditor, useEditorState } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import React, { useEffect, useMemo } from 'react'; import { createPortal } from 'react-dom'; import { Button } from '../shared/ui/controls'; import styles from './EditorApp.module.css'; import { useEditorI18n } from './i18n/EditorI18nContext'; import { normalizeSceneDescriptionHtml } from './sceneDescriptionHtml'; import modalStyles from './SceneDescriptionModal.module.css'; type SceneDescriptionModalProps = { initialHtml: string; onClose: () => void; onSave: (html: string) => void; }; function ToolbarIcon({ path, size = 14 }: { path: string; size?: number }) { return ( ); } function ToolButton({ active = false, disabled = false, title, onClick, children, }: { active?: boolean; disabled?: boolean; title: string; onClick: () => void; children: React.ReactNode; }) { return ( ); } export function SceneDescriptionModal({ initialHtml, onClose, onSave }: SceneDescriptionModalProps) { const { t } = useEditorI18n(); const extensions = useMemo( () => [ StarterKit.configure({ heading: { levels: [2, 3] }, codeBlock: false, link: false, }), Placeholder.configure({ placeholder: t('scene.descriptionPlaceholder'), }), ], [t], ); const editor = useEditor({ extensions, content: initialHtml || '', immediatelyRender: true, shouldRerenderOnTransaction: true, editorProps: { attributes: { class: [modalStyles.prose, 'tiptap'].join(' '), 'aria-label': t('scene.descriptionModalTitle'), }, }, }); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); const toolbarState = useEditorState({ editor, selector: ({ editor: ed }) => ({ bold: ed.isActive('bold'), italic: ed.isActive('italic'), underline: ed.isActive('underline'), bulletList: ed.isActive('bulletList'), orderedList: ed.isActive('orderedList'), h2: ed.isActive('heading', { level: 2 }), h3: ed.isActive('heading', { level: 3 }), blockquote: ed.isActive('blockquote'), }), }); const handleSave = () => { onSave(normalizeSceneDescriptionHtml(editor.getHTML())); }; return createPortal( <>
editor.chain().focus().toggleBold().run()} > B editor.chain().focus().toggleItalic().run()} > I editor.chain().focus().toggleUnderline().run()} > U
editor.chain().focus().toggleHeading({ level: 2 }).run()} > H2 editor.chain().focus().toggleHeading({ level: 3 }).run()} > H3 editor.chain().focus().toggleBlockquote().run()} >
editor.chain().focus().toggleBulletList().run()} > editor.chain().focus().toggleOrderedList().run()} >
, document.body, ); }