rte-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/editor-XH5ZEVLH.css +288 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +604 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +567 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/RichEditor.tsx","../src/hooks/useEditor.ts","../src/extensions/FontSize.ts","../src/components/Toolbar.tsx","../src/components/toolbar/ToolbarButton.tsx","../src/components/toolbar/tools.ts"],"sourcesContent":["export { default as RichEditor } from './components/RichEditor';\nexport type { RichEditorProps, ToolbarTool, ToolbarButtonProps, ToolDefinition } from './types';\n","import React from 'react';\nimport { EditorContent } from '@tiptap/react';\nimport { RichEditorProps, ToolbarTool } from '../types';\nimport { useEditor } from '../hooks/useEditor';\nimport Toolbar from './Toolbar';\nimport '../styles/editor.css';\n\nconst DEFAULT_TOOLBAR: ToolbarTool[] = [\n 'bold', 'italic', 'underline', 'strikethrough', 'divider',\n 'heading1', 'heading2', 'heading3', 'divider',\n 'bulletList', 'orderedList', 'blockquote', 'codeBlock', 'divider',\n 'link', 'image', 'table', 'divider',\n 'undo', 'redo',\n];\n\nconst RichEditor: React.FC<RichEditorProps> = ({\n value = '',\n onChange,\n toolbar = DEFAULT_TOOLBAR,\n placeholder = 'Start typing...',\n editable = true,\n className,\n style,\n toolbarClassName,\n contentClassName,\n}) => {\n const editor = useEditor({ value, onChange, placeholder, editable, toolbar });\n\n if (!editor) return null;\n\n return (\n <div\n className={['rre-editor', className].filter(Boolean).join(' ')}\n style={style}\n >\n {editable && (\n <Toolbar editor={editor} toolbar={toolbar} className={toolbarClassName} />\n )}\n <EditorContent\n editor={editor}\n className={['rre-content', contentClassName].filter(Boolean).join(' ')}\n />\n </div>\n );\n};\n\nexport default RichEditor;\n","import { useEffect } from 'react';\nimport { useEditor as useTiptapEditor } from '@tiptap/react';\nimport StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport TextStyle from '@tiptap/extension-text-style';\nimport Link from '@tiptap/extension-link';\nimport Image from '@tiptap/extension-image';\nimport Table from '@tiptap/extension-table';\nimport TableRow from '@tiptap/extension-table-row';\nimport TableCell from '@tiptap/extension-table-cell';\nimport TableHeader from '@tiptap/extension-table-header';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport FontSize from '../extensions/FontSize';\nimport { ToolbarTool } from '../types';\n\ninterface UseEditorOptions {\n value?: string;\n onChange?: (html: string) => void;\n placeholder?: string;\n editable?: boolean;\n toolbar?: ToolbarTool[];\n}\n\nexport const useEditor = ({\n value = '',\n onChange,\n placeholder = 'Start typing...',\n editable = true,\n}: UseEditorOptions) => {\n const editor = useTiptapEditor({\n extensions: [\n StarterKit,\n Underline,\n TextStyle,\n FontSize,\n Link.configure({ openOnClick: false }),\n Image,\n Table.configure({ resizable: true }),\n TableRow,\n TableCell,\n TableHeader,\n Placeholder.configure({ placeholder }),\n ],\n content: value,\n editable,\n onUpdate({ editor }) {\n onChange?.(editor.getHTML());\n },\n });\n\n useEffect(() => {\n if (editor && value !== editor.getHTML()) {\n editor.commands.setContent(value, false);\n }\n }, [value, editor]);\n\n useEffect(() => {\n if (editor) {\n editor.setEditable(editable ?? true);\n }\n }, [editable, editor]);\n\n return editor;\n};","import { Extension } from '@tiptap/core';\nimport '@tiptap/extension-text-style';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n fontSize: {\n setFontSize: (size: string) => ReturnType;\n unsetFontSize: () => ReturnType;\n };\n }\n}\n\nconst FontSize = Extension.create({\n name: 'fontSize',\n\n addOptions() {\n return {\n types: ['textStyle'],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n fontSize: {\n default: null,\n parseHTML: (element) => element.style.fontSize || null,\n renderHTML: (attributes) => {\n if (!attributes.fontSize) return {};\n return { style: `font-size: ${attributes.fontSize}` };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setFontSize:\n (fontSize: string) =>\n ({ chain }) => {\n return chain().setMark('textStyle', { fontSize }).run();\n },\n unsetFontSize:\n () =>\n ({ chain }) => {\n return chain().setMark('textStyle', { fontSize: null }).removeEmptyTextStyle().run();\n },\n };\n },\n});\n\nexport default FontSize;","import React, { useState, useRef, useEffect } from 'react';\nimport { Editor } from '@tiptap/react';\nimport { ToolbarTool } from '../types';\nimport ToolbarButton from './toolbar/ToolbarButton';\nimport { getToolDefinition } from './toolbar/tools';\n\nconst FONT_SIZES = ['12', '14', '16', '18', '20', '24', '28', '32', '36', '48'];\nconst MAX_IMAGE_SIZE = 10 * 1024 * 1024; // 10MB\n\ninterface ToolbarProps {\n editor: Editor;\n toolbar: ToolbarTool[];\n className?: string;\n}\n\ntype PopupType = 'link' | 'table' | null;\n\nconst Toolbar: React.FC<ToolbarProps> = ({ editor, toolbar, className }) => {\n const [popup, setPopup] = useState<PopupType>(null);\n const [linkUrl, setLinkUrl] = useState('');\n const [linkText, setLinkText] = useState('');\n const [tableRows, setTableRows] = useState('3');\n const [tableCols, setTableCols] = useState('3');\n const popupRef = useRef<HTMLDivElement>(null);\n const imageInputRef = useRef<HTMLInputElement>(null);\n\n const hasSelection = !editor.state.selection.empty;\n const currentFontSize = editor.getAttributes('textStyle').fontSize?.replace('px', '') || '16';\n\n // Close popup when clicking outside\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (popupRef.current && !popupRef.current.contains(e.target as Node)) {\n setPopup(null);\n }\n };\n if (popup) document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [popup]);\n\n const handleFontSize = (size: string) => {\n editor.chain().focus().setFontSize(`${size}px`).run();\n };\n\n const handleLinkOpen = () => {\n setLinkUrl(editor.getAttributes('link').href || '');\n setLinkText('');\n setPopup(popup === 'link' ? null : 'link');\n };\n\n const handleLinkSave = () => {\n if (!linkUrl) return;\n if (hasSelection) {\n editor.chain().focus().setLink({ href: linkUrl }).run();\n } else {\n if (!linkText) return;\n editor\n .chain()\n .focus()\n .insertContent(`<a href=\"${linkUrl}\">${linkText}</a>`)\n .run();\n }\n setLinkUrl('');\n setLinkText('');\n setPopup(null);\n };\n\n const handleLinkKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') handleLinkSave();\n if (e.key === 'Escape') setPopup(null);\n };\n\n const handleImageClick = () => {\n imageInputRef.current?.click();\n };\n\n const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n if (file.size > MAX_IMAGE_SIZE) {\n alert('Image is too large. Maximum size is 10MB.');\n return;\n }\n\n const reader = new FileReader();\n reader.onload = () => {\n const base64 = reader.result as string;\n editor.chain().focus().setImage({ src: base64 }).run();\n };\n reader.readAsDataURL(file);\n // Reset input so same file can be picked again\n e.target.value = '';\n };\n\n const handleTableOpen = () => {\n setPopup(popup === 'table' ? null : 'table');\n };\n\n const handleTableInsert = () => {\n const rows = Math.min(10, Math.max(1, parseInt(tableRows) || 3));\n const cols = Math.min(10, Math.max(1, parseInt(tableCols) || 3));\n editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();\n setPopup(null);\n };\n\n const handleTableKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') handleTableInsert();\n if (e.key === 'Escape') setPopup(null);\n };\n\n const handleTool = (tool: ToolbarTool) => {\n switch (tool) {\n case 'bold': editor.chain().focus().toggleBold().run(); break;\n case 'italic': editor.chain().focus().toggleItalic().run(); break;\n case 'underline': editor.chain().focus().toggleUnderline().run(); break;\n case 'strikethrough': editor.chain().focus().toggleStrike().run(); break;\n case 'heading1': editor.chain().focus().toggleHeading({ level: 1 }).run(); break;\n case 'heading2': editor.chain().focus().toggleHeading({ level: 2 }).run(); break;\n case 'heading3': editor.chain().focus().toggleHeading({ level: 3 }).run(); break;\n case 'heading4': editor.chain().focus().toggleHeading({ level: 4 }).run(); break;\n case 'heading5': editor.chain().focus().toggleHeading({ level: 5 }).run(); break;\n case 'heading6': editor.chain().focus().toggleHeading({ level: 6 }).run(); break;\n case 'bulletList': editor.chain().focus().toggleBulletList().run(); break;\n case 'orderedList': editor.chain().focus().toggleOrderedList().run(); break;\n case 'blockquote': editor.chain().focus().toggleBlockquote().run(); break;\n case 'codeBlock': editor.chain().focus().toggleCodeBlock().run(); break;\n case 'undo': editor.chain().focus().undo().run(); break;\n case 'redo': editor.chain().focus().redo().run(); break;\n }\n };\n\n const isActive = (tool: ToolbarTool): boolean => {\n switch (tool) {\n case 'bold': return editor.isActive('bold');\n case 'italic': return editor.isActive('italic');\n case 'underline': return editor.isActive('underline');\n case 'strikethrough': return editor.isActive('strike');\n case 'heading1': return editor.isActive('heading', { level: 1 });\n case 'heading2': return editor.isActive('heading', { level: 2 });\n case 'heading3': return editor.isActive('heading', { level: 3 });\n case 'heading4': return editor.isActive('heading', { level: 4 });\n case 'heading5': return editor.isActive('heading', { level: 5 });\n case 'heading6': return editor.isActive('heading', { level: 6 });\n case 'bulletList': return editor.isActive('bulletList');\n case 'orderedList': return editor.isActive('orderedList');\n case 'blockquote': return editor.isActive('blockquote');\n case 'codeBlock': return editor.isActive('codeBlock');\n case 'link': return editor.isActive('link');\n default: return false;\n }\n };\n\n const isDisabled = (tool: ToolbarTool): boolean => {\n if (tool === 'undo') return !editor.can().undo();\n if (tool === 'redo') return !editor.can().redo();\n return false;\n };\n\n const renderPopup = (tool: ToolbarTool) => {\n if (tool === 'link' && popup === 'link') {\n return (\n <div className=\"rre-popup\" ref={popupRef}>\n <input\n className=\"rre-popup-input\"\n type=\"url\"\n placeholder=\"https://example.com\"\n value={linkUrl}\n onChange={(e) => setLinkUrl(e.target.value)}\n onKeyDown={handleLinkKeyDown}\n autoFocus\n />\n {!hasSelection && (\n <input\n className=\"rre-popup-input\"\n type=\"text\"\n placeholder=\"Display text\"\n value={linkText}\n onChange={(e) => setLinkText(e.target.value)}\n onKeyDown={handleLinkKeyDown}\n />\n )}\n <button\n className=\"rre-popup-btn\"\n onMouseDown={(e) => { e.preventDefault(); handleLinkSave(); }}\n >\n Save\n </button>\n </div>\n );\n }\n\n if (tool === 'table' && popup === 'table') {\n return (\n <div className=\"rre-popup rre-popup--table\" ref={popupRef}>\n <input\n className=\"rre-popup-input rre-popup-input--num\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n value={tableRows}\n onChange={(e) => setTableRows(e.target.value)}\n onKeyDown={handleTableKeyDown}\n autoFocus\n />\n <span className=\"rre-popup-x\">×</span>\n <input\n className=\"rre-popup-input rre-popup-input--num\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n value={tableCols}\n onChange={(e) => setTableCols(e.target.value)}\n onKeyDown={handleTableKeyDown}\n />\n <button\n className=\"rre-popup-btn\"\n onMouseDown={(e) => { e.preventDefault(); handleTableInsert(); }}\n >\n Insert\n </button>\n </div>\n );\n }\n\n return null;\n };\n\n return (\n <div className={['rre-toolbar', className].filter(Boolean).join(' ')} role=\"toolbar\">\n {/* Hidden file input for image */}\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n style={{ display: 'none' }}\n onChange={handleImageChange}\n />\n\n {toolbar.map((tool, i) => {\n if (tool === 'divider') {\n return <div key={`divider-${i}`} className=\"rre-toolbar-divider\" aria-hidden=\"true\" />;\n }\n\n if (tool === 'fontSize') {\n return (\n <select\n key=\"fontSize\"\n className=\"rre-fontsize-select\"\n value={currentFontSize}\n onChange={(e) => handleFontSize(e.target.value)}\n title=\"Font size\"\n >\n {FONT_SIZES.map((size) => (\n <option key={size} value={size}>{size}px</option>\n ))}\n </select>\n );\n }\n\n if (tool === 'link') {\n const def = getToolDefinition(tool);\n if (!def) return null;\n return (\n <div key=\"link\" className=\"rre-toolbar-popup-wrapper\" onMouseDown={(e) => e.stopPropagation()}> \n <ToolbarButton\n onClick={handleLinkOpen}\n isActive={editor.isActive('link') || popup === 'link'}\n title={def.title}\n >\n <span className=\"rre-icon\" dangerouslySetInnerHTML={{ __html: def.icon }} />\n </ToolbarButton>\n {renderPopup('link')}\n </div>\n );\n }\n\n if (tool === 'image') {\n const def = getToolDefinition(tool);\n if (!def) return null;\n return (\n <ToolbarButton\n key=\"image\"\n onClick={handleImageClick}\n title={def.title}\n >\n <span className=\"rre-icon\" dangerouslySetInnerHTML={{ __html: def.icon }} />\n </ToolbarButton>\n );\n }\n\n if (tool === 'table') {\n const def = getToolDefinition(tool);\n if (!def) return null;\n return (\n<div key=\"table\" className=\"rre-toolbar-popup-wrapper\" onMouseDown={(e) => e.stopPropagation()}>\n <ToolbarButton\n onClick={handleTableOpen}\n isActive={popup === 'table'}\n title={def.title}\n >\n <span className=\"rre-icon\" dangerouslySetInnerHTML={{ __html: def.icon }} />\n </ToolbarButton>\n {renderPopup('table')}\n </div>\n );\n }\n\n const def = getToolDefinition(tool);\n if (!def) return null;\n return (\n <ToolbarButton\n key={tool}\n onClick={() => handleTool(tool)}\n isActive={isActive(tool)}\n disabled={isDisabled(tool)}\n title={def.title}\n >\n <span className=\"rre-icon\" dangerouslySetInnerHTML={{ __html: def.icon }} />\n </ToolbarButton>\n );\n })}\n </div>\n );\n};\n\nexport default Toolbar;","import React from 'react';\nimport { ToolbarButtonProps } from '../../types';\n\nconst ToolbarButton: React.FC<ToolbarButtonProps> = ({\n onClick,\n isActive = false,\n disabled = false,\n title,\n children,\n}) => {\n return (\n <button\n type=\"button\"\n onMouseDown={(e) => {\n e.preventDefault();\n onClick();\n }}\n disabled={disabled}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n className={[\n 'rre-toolbar-btn',\n isActive ? 'rre-toolbar-btn--active' : '',\n disabled ? 'rre-toolbar-btn--disabled' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {children}\n </button>\n );\n};\n\nexport default ToolbarButton;\n","import { ToolDefinition } from '../../types';\n\nexport const toolDefinitions: ToolDefinition[] = [\n { name: 'bold', title: 'Bold', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/></svg>' },\n { name: 'italic', title: 'Italic', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/></svg>' },\n { name: 'underline', title: 'Underline', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3\"/><line x1=\"4\" y1=\"21\" x2=\"20\" y2=\"21\"/></svg>' },\n { name: 'strikethrough', title: 'Strikethrough', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M17.3 4.9c-2.3-.6-4.4-1-6.2-.9-2.7 0-5.3.7-5.3 3.6 0 1.5 1.8 3.3 6.5 3.9h.1\"/><path d=\"M21.8 12H2.2\"/><path d=\"M6.7 19.1c2.3.6 4.4 1 6.2.9 2.7 0 5.3-.7 5.3-3.6 0-1.5-1.8-3.3-6.5-3.9H11\"/></svg>' },\n { name: 'heading1', title: 'Heading 1', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><path d=\"m17 12 3-2v8\"/></svg>' },\n { name: 'heading2', title: 'Heading 2', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><path d=\"M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1\"/></svg>' },\n { name: 'heading3', title: 'Heading 3', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><path d=\"M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2\"/><path d=\"M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2\"/></svg>' },\n { name: 'heading4', title: 'Heading 4', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><path d=\"M17 10v4h4\"/><path d=\"M21 10v8\"/></svg>' },\n { name: 'heading5', title: 'Heading 5', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><path d=\"M17 10h3\"/><path d=\"M17 14h2a2 2 0 0 1 0 4h-2v-4z\"/><path d=\"M17 10v4\"/></svg>' },\n { name: 'heading6', title: 'Heading 6', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M4 12h8\"/><path d=\"M4 18V6\"/><path d=\"M12 18V6\"/><circle cx=\"19\" cy=\"16\" r=\"2\"/><path d=\"M20 10c-2 2-3 3.5-3 6\"/></svg>' },\n { name: 'bulletList', title: 'Bullet List', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/></svg>' },\n { name: 'orderedList', title: 'Ordered List', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\"/><path d=\"M4 6h1v4\"/><path d=\"M4 10h2\"/><path d=\"M6 18H4c0-1 2-2 2-3s-1-1.5-2-1\"/></svg>' },\n { name: 'blockquote', title: 'Blockquote', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z\"/><path d=\"M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z\"/></svg>' },\n { name: 'codeBlock', title: 'Code Block', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/></svg>' },\n { name: 'link', title: 'Link', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/></svg>' },\n { name: 'image', title: 'Image', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>' },\n { name: 'table', title: 'Table', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/><line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"21\"/><line x1=\"15\" y1=\"3\" x2=\"15\" y2=\"21\"/></svg>' },\n { name: 'undo', title: 'Undo', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M3 7v6h6\"/><path d=\"M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13\"/></svg>' },\n { name: 'redo', title: 'Redo', icon: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M21 7v6h-6\"/><path d=\"M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13\"/></svg>' },\n];\n\nexport const getToolDefinition = (name: string): ToolDefinition | undefined =>\n toolDefinitions.find((t) => t.name === name);\n"],"mappings":"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","names":["import_react","import_react","import_extension_text_style","useTiptapEditor","StarterKit","Underline","TextStyle","Link","Image","Table","TableRow","TableCell","TableHeader","Placeholder","editor","import_react","import_jsx_runtime","_a","def","import_jsx_runtime"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
// src/components/RichEditor.tsx
|
|
2
|
+
import { EditorContent } from "@tiptap/react";
|
|
3
|
+
|
|
4
|
+
// src/hooks/useEditor.ts
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
import { useEditor as useTiptapEditor } from "@tiptap/react";
|
|
7
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
8
|
+
import Underline from "@tiptap/extension-underline";
|
|
9
|
+
import TextStyle from "@tiptap/extension-text-style";
|
|
10
|
+
import Link from "@tiptap/extension-link";
|
|
11
|
+
import Image from "@tiptap/extension-image";
|
|
12
|
+
import Table from "@tiptap/extension-table";
|
|
13
|
+
import TableRow from "@tiptap/extension-table-row";
|
|
14
|
+
import TableCell from "@tiptap/extension-table-cell";
|
|
15
|
+
import TableHeader from "@tiptap/extension-table-header";
|
|
16
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
17
|
+
|
|
18
|
+
// src/extensions/FontSize.ts
|
|
19
|
+
import { Extension } from "@tiptap/core";
|
|
20
|
+
import "@tiptap/extension-text-style";
|
|
21
|
+
var FontSize = Extension.create({
|
|
22
|
+
name: "fontSize",
|
|
23
|
+
addOptions() {
|
|
24
|
+
return {
|
|
25
|
+
types: ["textStyle"]
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
addGlobalAttributes() {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
types: this.options.types,
|
|
32
|
+
attributes: {
|
|
33
|
+
fontSize: {
|
|
34
|
+
default: null,
|
|
35
|
+
parseHTML: (element) => element.style.fontSize || null,
|
|
36
|
+
renderHTML: (attributes) => {
|
|
37
|
+
if (!attributes.fontSize) return {};
|
|
38
|
+
return { style: `font-size: ${attributes.fontSize}` };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
},
|
|
45
|
+
addCommands() {
|
|
46
|
+
return {
|
|
47
|
+
setFontSize: (fontSize) => ({ chain }) => {
|
|
48
|
+
return chain().setMark("textStyle", { fontSize }).run();
|
|
49
|
+
},
|
|
50
|
+
unsetFontSize: () => ({ chain }) => {
|
|
51
|
+
return chain().setMark("textStyle", { fontSize: null }).removeEmptyTextStyle().run();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
var FontSize_default = FontSize;
|
|
57
|
+
|
|
58
|
+
// src/hooks/useEditor.ts
|
|
59
|
+
var useEditor = ({
|
|
60
|
+
value = "",
|
|
61
|
+
onChange,
|
|
62
|
+
placeholder = "Start typing...",
|
|
63
|
+
editable = true
|
|
64
|
+
}) => {
|
|
65
|
+
const editor = useTiptapEditor({
|
|
66
|
+
extensions: [
|
|
67
|
+
StarterKit,
|
|
68
|
+
Underline,
|
|
69
|
+
TextStyle,
|
|
70
|
+
FontSize_default,
|
|
71
|
+
Link.configure({ openOnClick: false }),
|
|
72
|
+
Image,
|
|
73
|
+
Table.configure({ resizable: true }),
|
|
74
|
+
TableRow,
|
|
75
|
+
TableCell,
|
|
76
|
+
TableHeader,
|
|
77
|
+
Placeholder.configure({ placeholder })
|
|
78
|
+
],
|
|
79
|
+
content: value,
|
|
80
|
+
editable,
|
|
81
|
+
onUpdate({ editor: editor2 }) {
|
|
82
|
+
onChange == null ? void 0 : onChange(editor2.getHTML());
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (editor && value !== editor.getHTML()) {
|
|
87
|
+
editor.commands.setContent(value, false);
|
|
88
|
+
}
|
|
89
|
+
}, [value, editor]);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (editor) {
|
|
92
|
+
editor.setEditable(editable != null ? editable : true);
|
|
93
|
+
}
|
|
94
|
+
}, [editable, editor]);
|
|
95
|
+
return editor;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/components/Toolbar.tsx
|
|
99
|
+
import { useState, useRef, useEffect as useEffect2 } from "react";
|
|
100
|
+
|
|
101
|
+
// src/components/toolbar/ToolbarButton.tsx
|
|
102
|
+
import { jsx } from "react/jsx-runtime";
|
|
103
|
+
var ToolbarButton = ({
|
|
104
|
+
onClick,
|
|
105
|
+
isActive = false,
|
|
106
|
+
disabled = false,
|
|
107
|
+
title,
|
|
108
|
+
children
|
|
109
|
+
}) => {
|
|
110
|
+
return /* @__PURE__ */ jsx(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "button",
|
|
114
|
+
onMouseDown: (e) => {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
onClick();
|
|
117
|
+
},
|
|
118
|
+
disabled,
|
|
119
|
+
title,
|
|
120
|
+
"aria-label": title,
|
|
121
|
+
"aria-pressed": isActive,
|
|
122
|
+
className: [
|
|
123
|
+
"rre-toolbar-btn",
|
|
124
|
+
isActive ? "rre-toolbar-btn--active" : "",
|
|
125
|
+
disabled ? "rre-toolbar-btn--disabled" : ""
|
|
126
|
+
].filter(Boolean).join(" "),
|
|
127
|
+
children
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
var ToolbarButton_default = ToolbarButton;
|
|
132
|
+
|
|
133
|
+
// src/components/toolbar/tools.ts
|
|
134
|
+
var toolDefinitions = [
|
|
135
|
+
{ name: "bold", title: "Bold", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/></svg>' },
|
|
136
|
+
{ name: "italic", title: "Italic", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg>' },
|
|
137
|
+
{ name: "underline", title: "Underline", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"/><line x1="4" y1="21" x2="20" y2="21"/></svg>' },
|
|
138
|
+
{ name: "strikethrough", title: "Strikethrough", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M17.3 4.9c-2.3-.6-4.4-1-6.2-.9-2.7 0-5.3.7-5.3 3.6 0 1.5 1.8 3.3 6.5 3.9h.1"/><path d="M21.8 12H2.2"/><path d="M6.7 19.1c2.3.6 4.4 1 6.2.9 2.7 0 5.3-.7 5.3-3.6 0-1.5-1.8-3.3-6.5-3.9H11"/></svg>' },
|
|
139
|
+
{ name: "heading1", title: "Heading 1", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="m17 12 3-2v8"/></svg>' },
|
|
140
|
+
{ name: "heading2", title: "Heading 2", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1"/></svg>' },
|
|
141
|
+
{ name: "heading3", title: "Heading 3", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2"/><path d="M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2"/></svg>' },
|
|
142
|
+
{ name: "heading4", title: "Heading 4", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="M17 10v4h4"/><path d="M21 10v8"/></svg>' },
|
|
143
|
+
{ name: "heading5", title: "Heading 5", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><path d="M17 10h3"/><path d="M17 14h2a2 2 0 0 1 0 4h-2v-4z"/><path d="M17 10v4"/></svg>' },
|
|
144
|
+
{ name: "heading6", title: "Heading 6", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M4 12h8"/><path d="M4 18V6"/><path d="M12 18V6"/><circle cx="19" cy="16" r="2"/><path d="M20 10c-2 2-3 3.5-3 6"/></svg>' },
|
|
145
|
+
{ name: "bulletList", title: "Bullet List", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>' },
|
|
146
|
+
{ name: "orderedList", title: "Ordered List", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>' },
|
|
147
|
+
{ name: "blockquote", title: "Blockquote", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/></svg>' },
|
|
148
|
+
{ name: "codeBlock", title: "Code Block", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>' },
|
|
149
|
+
{ name: "link", title: "Link", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>' },
|
|
150
|
+
{ name: "image", title: "Image", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>' },
|
|
151
|
+
{ name: "table", title: "Table", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>' },
|
|
152
|
+
{ name: "undo", title: "Undo", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>' },
|
|
153
|
+
{ name: "redo", title: "Redo", icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13"/></svg>' }
|
|
154
|
+
];
|
|
155
|
+
var getToolDefinition = (name) => toolDefinitions.find((t) => t.name === name);
|
|
156
|
+
|
|
157
|
+
// src/components/Toolbar.tsx
|
|
158
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
159
|
+
var FONT_SIZES = ["12", "14", "16", "18", "20", "24", "28", "32", "36", "48"];
|
|
160
|
+
var MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
161
|
+
var Toolbar = ({ editor, toolbar, className }) => {
|
|
162
|
+
var _a;
|
|
163
|
+
const [popup, setPopup] = useState(null);
|
|
164
|
+
const [linkUrl, setLinkUrl] = useState("");
|
|
165
|
+
const [linkText, setLinkText] = useState("");
|
|
166
|
+
const [tableRows, setTableRows] = useState("3");
|
|
167
|
+
const [tableCols, setTableCols] = useState("3");
|
|
168
|
+
const popupRef = useRef(null);
|
|
169
|
+
const imageInputRef = useRef(null);
|
|
170
|
+
const hasSelection = !editor.state.selection.empty;
|
|
171
|
+
const currentFontSize = ((_a = editor.getAttributes("textStyle").fontSize) == null ? void 0 : _a.replace("px", "")) || "16";
|
|
172
|
+
useEffect2(() => {
|
|
173
|
+
const handleClickOutside = (e) => {
|
|
174
|
+
if (popupRef.current && !popupRef.current.contains(e.target)) {
|
|
175
|
+
setPopup(null);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
if (popup) document.addEventListener("mousedown", handleClickOutside);
|
|
179
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
180
|
+
}, [popup]);
|
|
181
|
+
const handleFontSize = (size) => {
|
|
182
|
+
editor.chain().focus().setFontSize(`${size}px`).run();
|
|
183
|
+
};
|
|
184
|
+
const handleLinkOpen = () => {
|
|
185
|
+
setLinkUrl(editor.getAttributes("link").href || "");
|
|
186
|
+
setLinkText("");
|
|
187
|
+
setPopup(popup === "link" ? null : "link");
|
|
188
|
+
};
|
|
189
|
+
const handleLinkSave = () => {
|
|
190
|
+
if (!linkUrl) return;
|
|
191
|
+
if (hasSelection) {
|
|
192
|
+
editor.chain().focus().setLink({ href: linkUrl }).run();
|
|
193
|
+
} else {
|
|
194
|
+
if (!linkText) return;
|
|
195
|
+
editor.chain().focus().insertContent(`<a href="${linkUrl}">${linkText}</a>`).run();
|
|
196
|
+
}
|
|
197
|
+
setLinkUrl("");
|
|
198
|
+
setLinkText("");
|
|
199
|
+
setPopup(null);
|
|
200
|
+
};
|
|
201
|
+
const handleLinkKeyDown = (e) => {
|
|
202
|
+
if (e.key === "Enter") handleLinkSave();
|
|
203
|
+
if (e.key === "Escape") setPopup(null);
|
|
204
|
+
};
|
|
205
|
+
const handleImageClick = () => {
|
|
206
|
+
var _a2;
|
|
207
|
+
(_a2 = imageInputRef.current) == null ? void 0 : _a2.click();
|
|
208
|
+
};
|
|
209
|
+
const handleImageChange = (e) => {
|
|
210
|
+
var _a2;
|
|
211
|
+
const file = (_a2 = e.target.files) == null ? void 0 : _a2[0];
|
|
212
|
+
if (!file) return;
|
|
213
|
+
if (file.size > MAX_IMAGE_SIZE) {
|
|
214
|
+
alert("Image is too large. Maximum size is 10MB.");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const reader = new FileReader();
|
|
218
|
+
reader.onload = () => {
|
|
219
|
+
const base64 = reader.result;
|
|
220
|
+
editor.chain().focus().setImage({ src: base64 }).run();
|
|
221
|
+
};
|
|
222
|
+
reader.readAsDataURL(file);
|
|
223
|
+
e.target.value = "";
|
|
224
|
+
};
|
|
225
|
+
const handleTableOpen = () => {
|
|
226
|
+
setPopup(popup === "table" ? null : "table");
|
|
227
|
+
};
|
|
228
|
+
const handleTableInsert = () => {
|
|
229
|
+
const rows = Math.min(10, Math.max(1, parseInt(tableRows) || 3));
|
|
230
|
+
const cols = Math.min(10, Math.max(1, parseInt(tableCols) || 3));
|
|
231
|
+
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
232
|
+
setPopup(null);
|
|
233
|
+
};
|
|
234
|
+
const handleTableKeyDown = (e) => {
|
|
235
|
+
if (e.key === "Enter") handleTableInsert();
|
|
236
|
+
if (e.key === "Escape") setPopup(null);
|
|
237
|
+
};
|
|
238
|
+
const handleTool = (tool) => {
|
|
239
|
+
switch (tool) {
|
|
240
|
+
case "bold":
|
|
241
|
+
editor.chain().focus().toggleBold().run();
|
|
242
|
+
break;
|
|
243
|
+
case "italic":
|
|
244
|
+
editor.chain().focus().toggleItalic().run();
|
|
245
|
+
break;
|
|
246
|
+
case "underline":
|
|
247
|
+
editor.chain().focus().toggleUnderline().run();
|
|
248
|
+
break;
|
|
249
|
+
case "strikethrough":
|
|
250
|
+
editor.chain().focus().toggleStrike().run();
|
|
251
|
+
break;
|
|
252
|
+
case "heading1":
|
|
253
|
+
editor.chain().focus().toggleHeading({ level: 1 }).run();
|
|
254
|
+
break;
|
|
255
|
+
case "heading2":
|
|
256
|
+
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
|
257
|
+
break;
|
|
258
|
+
case "heading3":
|
|
259
|
+
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
|
260
|
+
break;
|
|
261
|
+
case "heading4":
|
|
262
|
+
editor.chain().focus().toggleHeading({ level: 4 }).run();
|
|
263
|
+
break;
|
|
264
|
+
case "heading5":
|
|
265
|
+
editor.chain().focus().toggleHeading({ level: 5 }).run();
|
|
266
|
+
break;
|
|
267
|
+
case "heading6":
|
|
268
|
+
editor.chain().focus().toggleHeading({ level: 6 }).run();
|
|
269
|
+
break;
|
|
270
|
+
case "bulletList":
|
|
271
|
+
editor.chain().focus().toggleBulletList().run();
|
|
272
|
+
break;
|
|
273
|
+
case "orderedList":
|
|
274
|
+
editor.chain().focus().toggleOrderedList().run();
|
|
275
|
+
break;
|
|
276
|
+
case "blockquote":
|
|
277
|
+
editor.chain().focus().toggleBlockquote().run();
|
|
278
|
+
break;
|
|
279
|
+
case "codeBlock":
|
|
280
|
+
editor.chain().focus().toggleCodeBlock().run();
|
|
281
|
+
break;
|
|
282
|
+
case "undo":
|
|
283
|
+
editor.chain().focus().undo().run();
|
|
284
|
+
break;
|
|
285
|
+
case "redo":
|
|
286
|
+
editor.chain().focus().redo().run();
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const isActive = (tool) => {
|
|
291
|
+
switch (tool) {
|
|
292
|
+
case "bold":
|
|
293
|
+
return editor.isActive("bold");
|
|
294
|
+
case "italic":
|
|
295
|
+
return editor.isActive("italic");
|
|
296
|
+
case "underline":
|
|
297
|
+
return editor.isActive("underline");
|
|
298
|
+
case "strikethrough":
|
|
299
|
+
return editor.isActive("strike");
|
|
300
|
+
case "heading1":
|
|
301
|
+
return editor.isActive("heading", { level: 1 });
|
|
302
|
+
case "heading2":
|
|
303
|
+
return editor.isActive("heading", { level: 2 });
|
|
304
|
+
case "heading3":
|
|
305
|
+
return editor.isActive("heading", { level: 3 });
|
|
306
|
+
case "heading4":
|
|
307
|
+
return editor.isActive("heading", { level: 4 });
|
|
308
|
+
case "heading5":
|
|
309
|
+
return editor.isActive("heading", { level: 5 });
|
|
310
|
+
case "heading6":
|
|
311
|
+
return editor.isActive("heading", { level: 6 });
|
|
312
|
+
case "bulletList":
|
|
313
|
+
return editor.isActive("bulletList");
|
|
314
|
+
case "orderedList":
|
|
315
|
+
return editor.isActive("orderedList");
|
|
316
|
+
case "blockquote":
|
|
317
|
+
return editor.isActive("blockquote");
|
|
318
|
+
case "codeBlock":
|
|
319
|
+
return editor.isActive("codeBlock");
|
|
320
|
+
case "link":
|
|
321
|
+
return editor.isActive("link");
|
|
322
|
+
default:
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const isDisabled = (tool) => {
|
|
327
|
+
if (tool === "undo") return !editor.can().undo();
|
|
328
|
+
if (tool === "redo") return !editor.can().redo();
|
|
329
|
+
return false;
|
|
330
|
+
};
|
|
331
|
+
const renderPopup = (tool) => {
|
|
332
|
+
if (tool === "link" && popup === "link") {
|
|
333
|
+
return /* @__PURE__ */ jsxs("div", { className: "rre-popup", ref: popupRef, children: [
|
|
334
|
+
/* @__PURE__ */ jsx2(
|
|
335
|
+
"input",
|
|
336
|
+
{
|
|
337
|
+
className: "rre-popup-input",
|
|
338
|
+
type: "url",
|
|
339
|
+
placeholder: "https://example.com",
|
|
340
|
+
value: linkUrl,
|
|
341
|
+
onChange: (e) => setLinkUrl(e.target.value),
|
|
342
|
+
onKeyDown: handleLinkKeyDown,
|
|
343
|
+
autoFocus: true
|
|
344
|
+
}
|
|
345
|
+
),
|
|
346
|
+
!hasSelection && /* @__PURE__ */ jsx2(
|
|
347
|
+
"input",
|
|
348
|
+
{
|
|
349
|
+
className: "rre-popup-input",
|
|
350
|
+
type: "text",
|
|
351
|
+
placeholder: "Display text",
|
|
352
|
+
value: linkText,
|
|
353
|
+
onChange: (e) => setLinkText(e.target.value),
|
|
354
|
+
onKeyDown: handleLinkKeyDown
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ jsx2(
|
|
358
|
+
"button",
|
|
359
|
+
{
|
|
360
|
+
className: "rre-popup-btn",
|
|
361
|
+
onMouseDown: (e) => {
|
|
362
|
+
e.preventDefault();
|
|
363
|
+
handleLinkSave();
|
|
364
|
+
},
|
|
365
|
+
children: "Save"
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
] });
|
|
369
|
+
}
|
|
370
|
+
if (tool === "table" && popup === "table") {
|
|
371
|
+
return /* @__PURE__ */ jsxs("div", { className: "rre-popup rre-popup--table", ref: popupRef, children: [
|
|
372
|
+
/* @__PURE__ */ jsx2(
|
|
373
|
+
"input",
|
|
374
|
+
{
|
|
375
|
+
className: "rre-popup-input rre-popup-input--num",
|
|
376
|
+
type: "number",
|
|
377
|
+
min: "1",
|
|
378
|
+
max: "10",
|
|
379
|
+
value: tableRows,
|
|
380
|
+
onChange: (e) => setTableRows(e.target.value),
|
|
381
|
+
onKeyDown: handleTableKeyDown,
|
|
382
|
+
autoFocus: true
|
|
383
|
+
}
|
|
384
|
+
),
|
|
385
|
+
/* @__PURE__ */ jsx2("span", { className: "rre-popup-x", children: "\xD7" }),
|
|
386
|
+
/* @__PURE__ */ jsx2(
|
|
387
|
+
"input",
|
|
388
|
+
{
|
|
389
|
+
className: "rre-popup-input rre-popup-input--num",
|
|
390
|
+
type: "number",
|
|
391
|
+
min: "1",
|
|
392
|
+
max: "10",
|
|
393
|
+
value: tableCols,
|
|
394
|
+
onChange: (e) => setTableCols(e.target.value),
|
|
395
|
+
onKeyDown: handleTableKeyDown
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ jsx2(
|
|
399
|
+
"button",
|
|
400
|
+
{
|
|
401
|
+
className: "rre-popup-btn",
|
|
402
|
+
onMouseDown: (e) => {
|
|
403
|
+
e.preventDefault();
|
|
404
|
+
handleTableInsert();
|
|
405
|
+
},
|
|
406
|
+
children: "Insert"
|
|
407
|
+
}
|
|
408
|
+
)
|
|
409
|
+
] });
|
|
410
|
+
}
|
|
411
|
+
return null;
|
|
412
|
+
};
|
|
413
|
+
return /* @__PURE__ */ jsxs("div", { className: ["rre-toolbar", className].filter(Boolean).join(" "), role: "toolbar", children: [
|
|
414
|
+
/* @__PURE__ */ jsx2(
|
|
415
|
+
"input",
|
|
416
|
+
{
|
|
417
|
+
ref: imageInputRef,
|
|
418
|
+
type: "file",
|
|
419
|
+
accept: "image/*",
|
|
420
|
+
style: { display: "none" },
|
|
421
|
+
onChange: handleImageChange
|
|
422
|
+
}
|
|
423
|
+
),
|
|
424
|
+
toolbar.map((tool, i) => {
|
|
425
|
+
if (tool === "divider") {
|
|
426
|
+
return /* @__PURE__ */ jsx2("div", { className: "rre-toolbar-divider", "aria-hidden": "true" }, `divider-${i}`);
|
|
427
|
+
}
|
|
428
|
+
if (tool === "fontSize") {
|
|
429
|
+
return /* @__PURE__ */ jsx2(
|
|
430
|
+
"select",
|
|
431
|
+
{
|
|
432
|
+
className: "rre-fontsize-select",
|
|
433
|
+
value: currentFontSize,
|
|
434
|
+
onChange: (e) => handleFontSize(e.target.value),
|
|
435
|
+
title: "Font size",
|
|
436
|
+
children: FONT_SIZES.map((size) => /* @__PURE__ */ jsxs("option", { value: size, children: [
|
|
437
|
+
size,
|
|
438
|
+
"px"
|
|
439
|
+
] }, size))
|
|
440
|
+
},
|
|
441
|
+
"fontSize"
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
if (tool === "link") {
|
|
445
|
+
const def2 = getToolDefinition(tool);
|
|
446
|
+
if (!def2) return null;
|
|
447
|
+
return /* @__PURE__ */ jsxs("div", { className: "rre-toolbar-popup-wrapper", onMouseDown: (e) => e.stopPropagation(), children: [
|
|
448
|
+
/* @__PURE__ */ jsx2(
|
|
449
|
+
ToolbarButton_default,
|
|
450
|
+
{
|
|
451
|
+
onClick: handleLinkOpen,
|
|
452
|
+
isActive: editor.isActive("link") || popup === "link",
|
|
453
|
+
title: def2.title,
|
|
454
|
+
children: /* @__PURE__ */ jsx2("span", { className: "rre-icon", dangerouslySetInnerHTML: { __html: def2.icon } })
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
renderPopup("link")
|
|
458
|
+
] }, "link");
|
|
459
|
+
}
|
|
460
|
+
if (tool === "image") {
|
|
461
|
+
const def2 = getToolDefinition(tool);
|
|
462
|
+
if (!def2) return null;
|
|
463
|
+
return /* @__PURE__ */ jsx2(
|
|
464
|
+
ToolbarButton_default,
|
|
465
|
+
{
|
|
466
|
+
onClick: handleImageClick,
|
|
467
|
+
title: def2.title,
|
|
468
|
+
children: /* @__PURE__ */ jsx2("span", { className: "rre-icon", dangerouslySetInnerHTML: { __html: def2.icon } })
|
|
469
|
+
},
|
|
470
|
+
"image"
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (tool === "table") {
|
|
474
|
+
const def2 = getToolDefinition(tool);
|
|
475
|
+
if (!def2) return null;
|
|
476
|
+
return /* @__PURE__ */ jsxs("div", { className: "rre-toolbar-popup-wrapper", onMouseDown: (e) => e.stopPropagation(), children: [
|
|
477
|
+
/* @__PURE__ */ jsx2(
|
|
478
|
+
ToolbarButton_default,
|
|
479
|
+
{
|
|
480
|
+
onClick: handleTableOpen,
|
|
481
|
+
isActive: popup === "table",
|
|
482
|
+
title: def2.title,
|
|
483
|
+
children: /* @__PURE__ */ jsx2("span", { className: "rre-icon", dangerouslySetInnerHTML: { __html: def2.icon } })
|
|
484
|
+
}
|
|
485
|
+
),
|
|
486
|
+
renderPopup("table")
|
|
487
|
+
] }, "table");
|
|
488
|
+
}
|
|
489
|
+
const def = getToolDefinition(tool);
|
|
490
|
+
if (!def) return null;
|
|
491
|
+
return /* @__PURE__ */ jsx2(
|
|
492
|
+
ToolbarButton_default,
|
|
493
|
+
{
|
|
494
|
+
onClick: () => handleTool(tool),
|
|
495
|
+
isActive: isActive(tool),
|
|
496
|
+
disabled: isDisabled(tool),
|
|
497
|
+
title: def.title,
|
|
498
|
+
children: /* @__PURE__ */ jsx2("span", { className: "rre-icon", dangerouslySetInnerHTML: { __html: def.icon } })
|
|
499
|
+
},
|
|
500
|
+
tool
|
|
501
|
+
);
|
|
502
|
+
})
|
|
503
|
+
] });
|
|
504
|
+
};
|
|
505
|
+
var Toolbar_default = Toolbar;
|
|
506
|
+
|
|
507
|
+
// src/components/RichEditor.tsx
|
|
508
|
+
import "./editor-XH5ZEVLH.css";
|
|
509
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
510
|
+
var DEFAULT_TOOLBAR = [
|
|
511
|
+
"bold",
|
|
512
|
+
"italic",
|
|
513
|
+
"underline",
|
|
514
|
+
"strikethrough",
|
|
515
|
+
"divider",
|
|
516
|
+
"heading1",
|
|
517
|
+
"heading2",
|
|
518
|
+
"heading3",
|
|
519
|
+
"divider",
|
|
520
|
+
"bulletList",
|
|
521
|
+
"orderedList",
|
|
522
|
+
"blockquote",
|
|
523
|
+
"codeBlock",
|
|
524
|
+
"divider",
|
|
525
|
+
"link",
|
|
526
|
+
"image",
|
|
527
|
+
"table",
|
|
528
|
+
"divider",
|
|
529
|
+
"undo",
|
|
530
|
+
"redo"
|
|
531
|
+
];
|
|
532
|
+
var RichEditor = ({
|
|
533
|
+
value = "",
|
|
534
|
+
onChange,
|
|
535
|
+
toolbar = DEFAULT_TOOLBAR,
|
|
536
|
+
placeholder = "Start typing...",
|
|
537
|
+
editable = true,
|
|
538
|
+
className,
|
|
539
|
+
style,
|
|
540
|
+
toolbarClassName,
|
|
541
|
+
contentClassName
|
|
542
|
+
}) => {
|
|
543
|
+
const editor = useEditor({ value, onChange, placeholder, editable, toolbar });
|
|
544
|
+
if (!editor) return null;
|
|
545
|
+
return /* @__PURE__ */ jsxs2(
|
|
546
|
+
"div",
|
|
547
|
+
{
|
|
548
|
+
className: ["rre-editor", className].filter(Boolean).join(" "),
|
|
549
|
+
style,
|
|
550
|
+
children: [
|
|
551
|
+
editable && /* @__PURE__ */ jsx3(Toolbar_default, { editor, toolbar, className: toolbarClassName }),
|
|
552
|
+
/* @__PURE__ */ jsx3(
|
|
553
|
+
EditorContent,
|
|
554
|
+
{
|
|
555
|
+
editor,
|
|
556
|
+
className: ["rre-content", contentClassName].filter(Boolean).join(" ")
|
|
557
|
+
}
|
|
558
|
+
)
|
|
559
|
+
]
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
};
|
|
563
|
+
var RichEditor_default = RichEditor;
|
|
564
|
+
export {
|
|
565
|
+
RichEditor_default as RichEditor
|
|
566
|
+
};
|
|
567
|
+
//# sourceMappingURL=index.mjs.map
|