diff --git a/components/editor/basic-editor.js b/components/editor/basic-editor.js
new file mode 100644
index 0000000..bcf5c6f
Binary files /dev/null and b/components/editor/basic-editor.js differ
diff --git a/components/editor/custom-editor.js b/components/editor/custom-editor.js
index d2a16e4..95886a7 100644
--- a/components/editor/custom-editor.js
+++ b/components/editor/custom-editor.js
@@ -5,36 +5,166 @@ import { CKEditor } from "@ckeditor/ckeditor5-react";
import Editor from "@/vendor/ckeditor5/build/ckeditor";
function CustomEditor(props) {
+ const maxHeight = props.maxHeight || 600;
+
return (
-
{
- const data = editor.getData();
- console.log({ event, editor, data });
- props.onChange(data);
- }}
- config={{
- toolbar: [
- "heading",
- "fontsize",
- "bold",
- "italic",
- "link",
- "numberedList",
- "bulletedList",
- "undo",
- "redo",
- "alignment",
- "outdent",
- "indent",
- "blockQuote",
- "insertTable",
- "codeBlock",
- "sourceEditing",
- ],
- }}
- />
+
+ {
+ const data = editor.getData();
+ console.log({ event, editor, data });
+ props.onChange(data);
+ }}
+ config={{
+ toolbar: [
+ "heading",
+ "fontsize",
+ "bold",
+ "italic",
+ "link",
+ "numberedList",
+ "bulletedList",
+ "undo",
+ "redo",
+ "alignment",
+ "outdent",
+ "indent",
+ "blockQuote",
+ "insertTable",
+ "codeBlock",
+ "sourceEditing",
+ ],
+ content_style: `
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ font-size: 14px;
+ line-height: 1.6;
+ color: #111 !important;
+ background: #fff !important;
+ margin: 0;
+ padding: 1rem;
+ }
+ p {
+ margin: 0.5em 0;
+ }
+ h1, h2, h3, h4, h5, h6 {
+ margin: 1em 0 0.5em 0;
+ color: inherit !important;
+ }
+ ul, ol {
+ margin: 0.5em 0;
+ padding-left: 2em;
+ }
+ blockquote {
+ margin: 1em 0;
+ padding: 0.5em 1em;
+ border-left: 4px solid #d1d5db;
+ background-color: #f9fafb;
+ color: inherit !important;
+ }
+ `,
+ height: props.height || 400,
+ removePlugins: ["Title"],
+ mobile: {
+ theme: "silver",
+ },
+ }}
+ />
+
+
);
}
diff --git a/components/editor/editor-example.tsx b/components/editor/editor-example.tsx
new file mode 100644
index 0000000..5b054dd
--- /dev/null
+++ b/components/editor/editor-example.tsx
@@ -0,0 +1,164 @@
+"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
+
+
+
+ {savedContent && (
+
+ )}
+
+
+
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
new file mode 100644
index 0000000..7c5ed2d
--- /dev/null
+++ b/components/editor/editor-test.tsx
@@ -0,0 +1,176 @@
+"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)}
+
+
+
+
+
+
+
+
+
+ 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
new file mode 100644
index 0000000..428f234
Binary files /dev/null and b/components/editor/fixed-editor.js differ
diff --git a/components/editor/form-editor.js b/components/editor/form-editor.js
new file mode 100644
index 0000000..8bd2b39
--- /dev/null
+++ b/components/editor/form-editor.js
@@ -0,0 +1,102 @@
+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
new file mode 100644
index 0000000..b414b7d
--- /dev/null
+++ b/components/editor/minimal-editor.js
@@ -0,0 +1,81 @@
+// 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
new file mode 100644
index 0000000..82a5508
--- /dev/null
+++ b/components/editor/optimized-editor.tsx
@@ -0,0 +1,105 @@
+"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;
diff --git a/components/editor/readonly-editor.js b/components/editor/readonly-editor.js
new file mode 100644
index 0000000..a9e4504
--- /dev/null
+++ b/components/editor/readonly-editor.js
@@ -0,0 +1,136 @@
+// 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
new file mode 100644
index 0000000..e9b471e
--- /dev/null
+++ b/components/editor/simple-editor.js
@@ -0,0 +1,95 @@
+// 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
new file mode 100644
index 0000000..70c03f3
--- /dev/null
+++ b/components/editor/simple-readonly-editor.js
@@ -0,0 +1,109 @@
+// 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
new file mode 100644
index 0000000..a0acc7d
--- /dev/null
+++ b/components/editor/stable-editor.js
@@ -0,0 +1,93 @@
+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
new file mode 100644
index 0000000..614d246
--- /dev/null
+++ b/components/editor/static-editor.js
@@ -0,0 +1,93 @@
+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
new file mode 100644
index 0000000..12e4b96
--- /dev/null
+++ b/components/editor/strict-readonly-editor.js
@@ -0,0 +1,113 @@
+// 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
new file mode 100644
index 0000000..ca67548
--- /dev/null
+++ b/components/editor/tinymce-editor.tsx
@@ -0,0 +1,309 @@
+"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;
diff --git a/components/editor/view-editor.js b/components/editor/view-editor.js
index 2dae74e..31e7db9 100644
--- a/components/editor/view-editor.js
+++ b/components/editor/view-editor.js
@@ -3,17 +3,261 @@ import { CKEditor } from "@ckeditor/ckeditor5-react";
import Editor from "@/vendor/ckeditor5/build/ckeditor";
function ViewEditor(props) {
+ const maxHeight = props.maxHeight || 600; // Default max height 600px
+
return (
-
+
+
+
+
);
}
export default ViewEditor;
+
+// import React from "react";
+// import { CKEditor } from "@ckeditor/ckeditor5-react";
+// import Editor from "ckeditor5-custom-build";
+
+// function ViewEditor(props) {
+// const maxHeight = props.maxHeight || 600;
+
+// return (
+//
+//
+//
+//
+// );
+// }
+
+// export default ViewEditor;
diff --git a/components/form/article/create-article-form.tsx b/components/form/article/create-article-form.tsx
index 1f6b448..e7a4270 100644
--- a/components/form/article/create-article-form.tsx
+++ b/components/form/article/create-article-form.tsx
@@ -524,13 +524,21 @@ export default function CreateArticleForm() {
Single Article
- Content Rewrite
+ {/* Content Rewrite */}
{selectedWritingType === "single" ? (
{
setDiseData(data);
+ // setValue("title", data?.title ?? "", {
+ // shouldValidate: true,
+ // shouldDirty: true,
+ // });
+ // setValue("slug", generateSlug(data?.title ?? ""), {
+ // shouldValidate: true,
+ // shouldDirty: true,
+ // });
setValue(
"description",
data?.articleBody ? data?.articleBody : ""
@@ -664,7 +672,10 @@ export default function CreateArticleForm() {
"!rounded-lg bg-white !border-1 !border-gray-200 dark:!border-stone-500",
}}
classNamePrefix="select"
- onChange={onChange}
+ value={value}
+ onChange={(selected) => {
+ onChange(selected);
+ }}
closeMenuOnSelect={false}
components={animatedComponents}
isClearable={true}
@@ -683,60 +694,7 @@ export default function CreateArticleForm() {
)}
Tags
- {/* (
-