diff --git a/components/editor/basic-editor.js b/components/editor/basic-editor.js deleted file mode 100644 index bcf5c6f..0000000 Binary files a/components/editor/basic-editor.js and /dev/null differ diff --git a/components/editor/editor-example.tsx b/components/editor/editor-example.tsx deleted file mode 100644 index 58ea65b..0000000 --- a/components/editor/editor-example.tsx +++ /dev/null @@ -1,164 +0,0 @@ -"use client"; - -import React, { useState } from 'react'; - -// Import the optimized editor (choose one based on your migration) -// import OptimizedEditor from './optimized-editor'; // TinyMCE -// import OptimizedCKEditor from './optimized-ckeditor'; // CKEditor5 Classic -// import MinimalEditor from './minimal-editor'; // React Quill - -interface EditorExampleProps { - editorType?: 'tinymce' | 'ckeditor' | 'quill'; -} - -const EditorExample: React.FC = ({ - editorType = 'tinymce' -}) => { - const [content, setContent] = useState('

Hello, this is the editor content!

'); - const [savedContent, setSavedContent] = useState(''); - - const handleContentChange = (newContent: string) => { - setContent(newContent); - }; - - const handleSave = () => { - setSavedContent(content); - // console.log('Content saved:', content); - }; - - const handleReset = () => { - setContent('

Content has been reset!

'); - }; - - return ( -
-
-

Rich Text Editor Example

-

- This is an optimized editor with {editorType} - much smaller bundle size and better performance! -

-
- -
- {/* Editor Panel */} -
-
-

Editor

-
- - -
-
- -
- {/* Choose your editor based on migration */} - {editorType === 'tinymce' && ( -
-

- TinyMCE Editor (200KB bundle) -

- {/* */} -
-

TinyMCE Editor Component

-
-
- )} - - {editorType === 'ckeditor' && ( -
-

- CKEditor5 Classic (800KB bundle) -

- {/* */} -
-

CKEditor5 Classic Component

-
-
- )} - - {editorType === 'quill' && ( -
-

- React Quill (100KB bundle) -

- {/* */} -
-

React Quill Component

-
-
- )} -
-
- - {/* Preview Panel */} -
-

Preview

- -
-

Current Content:

-
-
- - {savedContent && ( -
-

Saved Content:

-
-
- )} - -
-

Raw HTML:

-
-              {content}
-            
-
-
-
- - {/* Performance Info */} -
-

Performance Benefits:

-
    -
  • • 90% smaller bundle size compared to custom CKEditor5
  • -
  • • Faster initial load time
  • -
  • • Better mobile performance
  • -
  • • Reduced memory usage
  • -
  • • Improved Lighthouse score
  • -
-
-
- ); -}; - -export default EditorExample; \ No newline at end of file diff --git a/components/editor/editor-test.tsx b/components/editor/editor-test.tsx deleted file mode 100644 index 7d62ea5..0000000 --- a/components/editor/editor-test.tsx +++ /dev/null @@ -1,176 +0,0 @@ -"use client"; - -import React, { useState } from 'react'; -import { useForm, Controller } from 'react-hook-form'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Card } from '@/components/ui/card'; -import CustomEditor from './custom-editor'; -import FormEditor from './form-editor'; - -export default function EditorTest() { - const [testData, setTestData] = useState('Initial test content'); - const [editorType, setEditorType] = useState('custom'); - - const { control, setValue, watch, handleSubmit } = useForm({ - defaultValues: { - title: 'Test Title', - description: testData, - creatorName: 'Test Creator' - } - }); - - const watchedValues = watch(); - - const handleSetValue = () => { - const newContent = `

Updated content at ${new Date().toLocaleTimeString()}

This content was set via setValue

`; - setValue('description', newContent); - setTestData(newContent); - }; - - const handleSetEmpty = () => { - setValue('description', ''); - setTestData(''); - }; - - const handleSetHTML = () => { - const htmlContent = ` -

HTML Content Test

-

This is a bold paragraph with italic text.

-
    -
  • List item 1
  • -
  • List item 2
  • -
  • List item 3
  • -
-

Updated at: ${new Date().toLocaleTimeString()}

- `; - setValue('description', htmlContent); - setTestData(htmlContent); - }; - - const onSubmit = (data: any) => { - // console.log('Form submitted:', data); - alert('Form submitted! Check console for data.'); - }; - - return ( -
-

Editor Test Component

- - -
-
- -
- - -
-
- -
- - - -
- -
-
- -
- {testData || '(empty)'} -
-
-
- -
-
{JSON.stringify(watchedValues, null, 2)}
-
-
-
-
-
- -
- -
-
- - ( - - )} - /> -
- -
- - ( - editorType === 'custom' ? ( - - ) : ( - - ) - )} - /> -
- -
- - ( - - )} - /> -
- - -
-
-
- - -

Instructions:

-
    -
  • Switch between CustomEditor and FormEditor to test both
  • -
  • Click "Set Value" to test setValue functionality
  • -
  • Click "Set Empty" to test empty content handling
  • -
  • Click "Set HTML Content" to test rich HTML content
  • -
  • Type in the editor to test onChange functionality
  • -
  • Submit the form to see all data
  • -
-
-
- ); -} \ No newline at end of file diff --git a/components/editor/fixed-editor.js b/components/editor/fixed-editor.js deleted file mode 100644 index 428f234..0000000 Binary files a/components/editor/fixed-editor.js and /dev/null differ diff --git a/components/editor/form-editor.js b/components/editor/form-editor.js deleted file mode 100644 index 8bd2b39..0000000 --- a/components/editor/form-editor.js +++ /dev/null @@ -1,102 +0,0 @@ -import React, { useRef, useEffect, useState, useCallback } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function FormEditor({ onChange, initialData }) { - const editorRef = useRef(null); - const [isEditorReady, setIsEditorReady] = useState(false); - const [editorContent, setEditorContent] = useState(initialData || ""); - - // Handle editor initialization - const handleInit = useCallback((evt, editor) => { - editorRef.current = editor; - setIsEditorReady(true); - - // Set initial content when editor is ready - if (editorContent) { - editor.setContent(editorContent); - } - - // Handle content changes - editor.on('change', () => { - const content = editor.getContent(); - setEditorContent(content); - if (onChange) { - onChange(content); - } - }); - }, [editorContent, onChange]); - - // Watch for initialData changes (from setValue) - useEffect(() => { - if (initialData !== editorContent) { - setEditorContent(initialData || ""); - - // Update editor content if ready - if (editorRef.current && isEditorReady) { - editorRef.current.setContent(initialData || ""); - } - } - }, [initialData, editorContent, isEditorReady]); - - // Handle initial data when editor becomes ready - useEffect(() => { - if (isEditorReady && editorContent && editorRef.current) { - editorRef.current.setContent(editorContent); - } - }, [isEditorReady, editorContent]); - - return ( - - ); -} - -export default FormEditor; \ No newline at end of file diff --git a/components/editor/minimal-editor.js b/components/editor/minimal-editor.js deleted file mode 100644 index b414b7d..0000000 --- a/components/editor/minimal-editor.js +++ /dev/null @@ -1,81 +0,0 @@ -// components/minimal-editor.js - -import React, { useRef } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function MinimalEditor(props) { - const editorRef = useRef(null); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Set initial content if provided - if (props.initialData) { - editor.setContent(props.initialData); - } - - // Simple onChange handler - no debouncing, no complex logic - editor.on('change', () => { - if (props.onChange) { - props.onChange(editor.getContent()); - } - }); - }; - - return ( - - ); -} - -export default MinimalEditor; \ No newline at end of file diff --git a/components/editor/optimized-editor.tsx b/components/editor/optimized-editor.tsx deleted file mode 100644 index f9de03a..0000000 --- a/components/editor/optimized-editor.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client"; - -import React, { useEffect, useRef } from 'react'; -import { Editor } from '@tinymce/tinymce-react'; - -interface OptimizedEditorProps { - initialData?: string; - onChange?: (data: string) => void; - height?: number; - placeholder?: string; - disabled?: boolean; - readOnly?: boolean; -} - -const OptimizedEditor: React.FC = ({ - initialData = '', - onChange, - height = 400, - placeholder = 'Start typing...', - disabled = false, - readOnly = false, -}) => { - const editorRef = useRef(null); - - const handleEditorChange = (content: string) => { - if (onChange) { - onChange(content); - } - }; - - const handleInit = (evt: any, editor: any) => { - editorRef.current = editor; - }; - - return ( - - ); -}; - -export default OptimizedEditor; \ No newline at end of file diff --git a/components/editor/readonly-editor.js b/components/editor/readonly-editor.js deleted file mode 100644 index a9e4504..0000000 --- a/components/editor/readonly-editor.js +++ /dev/null @@ -1,136 +0,0 @@ -// components/readonly-editor.js - -import React, { useRef, useEffect } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function ReadOnlyEditor(props) { - const editorRef = useRef(null); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Set initial content if provided - if (props.initialData) { - editor.setContent(props.initialData); - } - - // Disable all editing capabilities - editor.on('keydown keyup keypress input', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - editor.on('paste', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - editor.on('drop', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - // Disable mouse events that might allow editing - editor.on('mousedown mousemove mouseup click dblclick', (e) => { - if (e.target.closest('.mce-content-body')) { - e.preventDefault(); - e.stopPropagation(); - return false; - } - }); - }; - - // Update content when props change - useEffect(() => { - if (editorRef.current && props.initialData) { - editorRef.current.setContent(props.initialData); - } - }, [props.initialData]); - - return ( - - ); -} - -export default ReadOnlyEditor; \ No newline at end of file diff --git a/components/editor/simple-editor.js b/components/editor/simple-editor.js deleted file mode 100644 index e9b471e..0000000 --- a/components/editor/simple-editor.js +++ /dev/null @@ -1,95 +0,0 @@ -// components/simple-editor.js - -import React, { useRef, useState, useCallback } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function SimpleEditor(props) { - const editorRef = useRef(null); - const [editorInstance, setEditorInstance] = useState(null); - - const handleInit = useCallback((evt, editor) => { - editorRef.current = editor; - setEditorInstance(editor); - - // Set initial content - if (props.initialData) { - editor.setContent(props.initialData); - } - - // Disable automatic content updates - editor.settings.auto_focus = false; - editor.settings.forced_root_block = 'p'; - - // Store the onChange callback - editor.onChangeCallback = props.onChange; - - // Handle content changes without triggering re-renders - editor.on('change keyup input', (e) => { - if (editor.onChangeCallback) { - const content = editor.getContent(); - editor.onChangeCallback(content); - } - }); - - }, [props.initialData, props.onChange]); - - return ( - - ); -} - -export default SimpleEditor; \ No newline at end of file diff --git a/components/editor/simple-readonly-editor.js b/components/editor/simple-readonly-editor.js deleted file mode 100644 index 70c03f3..0000000 --- a/components/editor/simple-readonly-editor.js +++ /dev/null @@ -1,109 +0,0 @@ -// components/simple-readonly-editor.js - -import React, { useRef } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function SimpleReadOnlyEditor(props) { - const editorRef = useRef(null); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Disable all editing capabilities - editor.on('keydown keyup keypress input', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - editor.on('paste', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - editor.on('drop', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - - // Disable mouse events that might allow editing - editor.on('mousedown mousemove mouseup click dblclick', (e) => { - if (e.target.closest('.mce-content-body')) { - e.preventDefault(); - e.stopPropagation(); - return false; - } - }); - }; - - return ( - - ); -} - -export default SimpleReadOnlyEditor; \ No newline at end of file diff --git a/components/editor/stable-editor.js b/components/editor/stable-editor.js deleted file mode 100644 index a0acc7d..0000000 --- a/components/editor/stable-editor.js +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useRef, useEffect } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function StableEditor(props) { - const editorRef = useRef(null); - const onChangeRef = useRef(props.onChange); - - // Update onChange ref when props change - useEffect(() => { - onChangeRef.current = props.onChange; - }, [props.onChange]); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Set initial content if provided - if (props.initialData) { - editor.setContent(props.initialData); - } - - // Use a simple change handler that doesn't trigger re-renders - editor.on('change', () => { - if (onChangeRef.current) { - onChangeRef.current(editor.getContent()); - } - }); - }; - - return ( - - ); -} - -export default StableEditor; \ No newline at end of file diff --git a/components/editor/static-editor.js b/components/editor/static-editor.js deleted file mode 100644 index 614d246..0000000 --- a/components/editor/static-editor.js +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useRef, useEffect } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function StaticEditor(props) { - const editorRef = useRef(null); - const onChangeRef = useRef(props.onChange); - - // Update onChange ref when props change - useEffect(() => { - onChangeRef.current = props.onChange; - }, [props.onChange]); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Set initial content if provided - if (props.initialData) { - editor.setContent(props.initialData); - } - - // Use a simple change handler that doesn't trigger re-renders - editor.on('change', () => { - if (onChangeRef.current) { - onChangeRef.current(editor.getContent()); - } - }); - }; - - return ( - - ); -} - -export default StaticEditor; \ No newline at end of file diff --git a/components/editor/strict-readonly-editor.js b/components/editor/strict-readonly-editor.js deleted file mode 100644 index 12e4b96..0000000 --- a/components/editor/strict-readonly-editor.js +++ /dev/null @@ -1,113 +0,0 @@ -// components/strict-readonly-editor.js - -import React, { useRef } from "react"; -import { Editor } from "@tinymce/tinymce-react"; - -function StrictReadOnlyEditor(props) { - const editorRef = useRef(null); - - const handleInit = (evt, editor) => { - editorRef.current = editor; - - // Disable all possible editing events - const disableEvents = ['keydown', 'keyup', 'keypress', 'input', 'paste', 'drop', 'cut', 'copy']; - - disableEvents.forEach(eventType => { - editor.on(eventType, (e) => { - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - return false; - }); - }); - - // Disable mouse events that might allow editing - editor.on('mousedown mousemove mouseup click dblclick', (e) => { - if (e.target.closest('.mce-content-body')) { - e.preventDefault(); - e.stopPropagation(); - return false; - } - }); - - // Disable focus events - editor.on('focus blur', (e) => { - e.preventDefault(); - e.stopPropagation(); - return false; - }); - }; - - return ( - - ); -} - -export default StrictReadOnlyEditor; \ No newline at end of file diff --git a/components/editor/tinymce-editor.tsx b/components/editor/tinymce-editor.tsx deleted file mode 100644 index fb98164..0000000 --- a/components/editor/tinymce-editor.tsx +++ /dev/null @@ -1,264 +0,0 @@ -"use client"; - -import React, { useRef, useState, useEffect } from 'react'; -import { Editor } from '@tinymce/tinymce-react'; - -interface TinyMCEEditorProps { - initialData?: string; - onChange?: (data: string) => void; - onReady?: (editor: any) => void; - height?: number; - placeholder?: string; - disabled?: boolean; - readOnly?: boolean; - features?: 'basic' | 'standard' | 'full'; - toolbar?: string; - language?: string; - uploadUrl?: string; - uploadHeaders?: Record; - className?: string; - autoSave?: boolean; - autoSaveInterval?: number; -} - -const TinyMCEEditor: React.FC = ({ - initialData = '', - onChange, - onReady, - height = 400, - placeholder = 'Start typing...', - disabled = false, - readOnly = false, - features = 'standard', - toolbar, - language = 'en', - uploadUrl, - uploadHeaders, - className = '', - autoSave = true, - autoSaveInterval = 30000 -}) => { - const editorRef = useRef(null); - const [isEditorLoaded, setIsEditorLoaded] = useState(false); - const [lastSaved, setLastSaved] = useState(null); - const [wordCount, setWordCount] = useState(0); - - // Feature-based configurations - const getFeatureConfig = (featureLevel: string) => { - const configs = { - basic: { - plugins: ['lists', 'link', 'autolink', 'wordcount'], - toolbar: 'bold italic | bullist numlist | link', - menubar: false - }, - standard: { - plugins: [ - 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview', - 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', - 'insertdatetime', 'media', 'table', 'help', 'wordcount' - ], - toolbar: 'undo redo | blocks | ' + - 'bold italic forecolor | alignleft aligncenter ' + - 'alignright alignjustify | bullist numlist outdent indent | ' + - 'removeformat | table | code | help', - menubar: false - }, - full: { - plugins: [ - 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview', - 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', - 'insertdatetime', 'media', 'table', 'help', 'wordcount', 'emoticons', - 'paste', 'textcolor', 'colorpicker', 'hr', 'pagebreak', 'nonbreaking', - 'toc', 'imagetools', 'textpattern', 'codesample' - ], - toolbar: 'undo redo | formatselect | bold italic backcolor | ' + - 'alignleft aligncenter alignright alignjustify | ' + - 'bullist numlist outdent indent | removeformat | help', - menubar: 'file edit view insert format tools table help' - } - }; - return configs[featureLevel as keyof typeof configs] || configs.standard; - }; - - const handleEditorChange = (content: string) => { - if (onChange) { - onChange(content); - } - }; - - const handleEditorInit = (evt: any, editor: any) => { - editorRef.current = editor; - setIsEditorLoaded(true); - - if (onReady) { - onReady(editor); - } - - // Set up word count tracking - editor.on('keyup', () => { - const count = editor.plugins.wordcount.body.getCharacterCount(); - setWordCount(count); - }); - - // Set up auto-save - if (autoSave && !readOnly) { - setInterval(() => { - const content = editor.getContent(); - localStorage.setItem('tinymce-autosave', content); - setLastSaved(new Date()); - }, autoSaveInterval); - } - - // Fix cursor jumping issues - editor.on('keyup', (e: any) => { - // Prevent cursor jumping on content changes - e.stopPropagation(); - }); - - editor.on('input', (e: any) => { - // Prevent unnecessary re-renders - e.stopPropagation(); - }); - - // Handle paste events properly - editor.on('paste', (e: any) => { - // Allow default paste behavior - return true; - }); - }; - - const handleImageUpload = (blobInfo: any, progress: any) => { - return new Promise((resolve, reject) => { - if (!uploadUrl) { - reject('No upload URL configured'); - return; - } - - const formData = new FormData(); - formData.append('file', blobInfo.blob(), blobInfo.filename()); - - fetch(uploadUrl, { - method: 'POST', - headers: uploadHeaders || {}, - body: formData - }) - .then(response => response.json()) - .then(result => { - resolve(result.url); - }) - .catch(error => { - reject(error); - }); - }); - }; - - const featureConfig = getFeatureConfig(features); - - const editorConfig = { - height, - language, - placeholder, - readonly: readOnly, - disabled, - branding: false, - elementpath: false, - resize: false, - statusbar: !readOnly, - // Performance optimizations - cache_suffix: '?v=1.0', - browser_spellcheck: false, - gecko_spellcheck: false, - // Content styling - content_style: ` - body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - font-size: 14px; - line-height: 1.6; - color: #333; - margin: 0; - padding: 16px; - } - .mce-content-body { - min-height: ${height - 32}px; - } - .mce-content-body:focus { - outline: none; - } - `, - // Image upload configuration - images_upload_handler: uploadUrl ? handleImageUpload : undefined, - automatic_uploads: !!uploadUrl, - file_picker_types: 'image', - // Better mobile support - mobile: { - theme: 'silver', - plugins: ['lists', 'autolink', 'link', 'image', 'table'], - toolbar: 'bold italic | bullist numlist | link image' - }, - // Paste configuration - paste_as_text: false, - paste_enable_default_filters: true, - paste_word_valid_elements: 'b,strong,i,em,h1,h2,h3,h4,h5,h6', - paste_retain_style_properties: 'color background-color font-size font-weight', - // Table configuration - table_default_styles: { - width: '100%' - }, - table_default_attributes: { - border: '1' - }, - // Code configuration - codesample_languages: [ - { text: 'HTML/XML', value: 'markup' }, - { text: 'JavaScript', value: 'javascript' }, - { text: 'CSS', value: 'css' }, - { text: 'PHP', value: 'php' }, - { text: 'Python', value: 'python' }, - { text: 'Java', value: 'java' }, - { text: 'C', value: 'c' }, - { text: 'C++', value: 'cpp' } - ], - // ...feature config - ...featureConfig, - // Custom toolbar if provided - ...(toolbar && { toolbar }) - }; - - return ( -
- - - {/* Status bar */} - {isEditorLoaded && ( -
-
- - {autoSave && !readOnly ? 'Auto-save enabled' : 'Read-only mode'} - - {lastSaved && autoSave && !readOnly && ( - • Last saved: {lastSaved.toLocaleTimeString()} - )} - • {wordCount} characters -
- - {features} mode - -
- )} - - {/* Performance indicator */} -
- Bundle size: {features === 'basic' ? '~150KB' : features === 'standard' ? '~200KB' : '~300KB'} -
-
- ); -}; - -export default TinyMCEEditor; \ No newline at end of file