Class approach
This commit is contained in:
parent
55bda8a1bd
commit
80fb1813cf
178
src/App.tsx
178
src/App.tsx
@ -2,103 +2,105 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import './App.css'
|
import './App.css'
|
||||||
import EditorJS, { OutputData } from '@editorjs/editorjs'
|
import EditorJS, { OutputData } from '@editorjs/editorjs'
|
||||||
|
|
||||||
interface EditorWrapper {
|
class EditorJSWrapper {
|
||||||
instance: EditorJS | null;
|
private editor: EditorJS | null = null;
|
||||||
addBlock: (text: string) => Promise<void>;
|
private holder: HTMLDivElement | null = null;
|
||||||
clear: () => Promise<void>;
|
private onChange!: (data: OutputData) => void;
|
||||||
getData: () => Promise<OutputData>;
|
private static instance: EditorJSWrapper | null = null;
|
||||||
holderRef: React.RefObject<HTMLDivElement | null>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useEditorJS(onChange: (data: OutputData) => void): EditorWrapper {
|
constructor(onChange: (data: OutputData) => void) {
|
||||||
const [editor, setEditor] = useState<EditorJS | null>(null);
|
if (EditorJSWrapper.instance) {
|
||||||
const holderRef = useRef<HTMLDivElement | null>(null);
|
return EditorJSWrapper.instance;
|
||||||
const isDestroyed = useRef(false);
|
|
||||||
|
|
||||||
// Initialize editor
|
|
||||||
useEffect(() => {
|
|
||||||
if (!holderRef.current || editor || isDestroyed.current) return;
|
|
||||||
|
|
||||||
const instance = new EditorJS({
|
|
||||||
holder: holderRef.current,
|
|
||||||
placeholder: 'Start writing...',
|
|
||||||
onChange: async () => {
|
|
||||||
try {
|
|
||||||
const content = await instance.save();
|
|
||||||
onChange(content);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error saving editor content:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setEditor(instance);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isDestroyed.current = true;
|
|
||||||
const cleanup = async () => {
|
|
||||||
if (instance) {
|
|
||||||
try {
|
|
||||||
await instance.destroy();
|
|
||||||
setEditor(null);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error('Error destroying editor:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
void cleanup();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const wrapper: EditorWrapper = {
|
|
||||||
instance: editor,
|
|
||||||
holderRef,
|
|
||||||
addBlock: async (text: string) => {
|
|
||||||
if (!editor) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentData = await editor.save();
|
|
||||||
const newBlock = {
|
|
||||||
id: String(Date.now()),
|
|
||||||
type: 'paragraph',
|
|
||||||
data: {
|
|
||||||
text
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await editor.blocks.insert('paragraph', { text });
|
|
||||||
|
|
||||||
const updatedData = await editor.save();
|
|
||||||
onChange(updatedData);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error adding block:', err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clear: async () => {
|
|
||||||
if (!editor) return;
|
|
||||||
try {
|
|
||||||
await editor.clear();
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error clearing editor:', err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getData: async () => {
|
|
||||||
if (!editor) throw new Error('Editor not initialized');
|
|
||||||
return editor.save();
|
|
||||||
}
|
}
|
||||||
};
|
this.onChange = onChange;
|
||||||
|
EditorJSWrapper.instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
return wrapper;
|
initialize(holder: HTMLDivElement) {
|
||||||
|
if (this.editor || !holder) return;
|
||||||
|
|
||||||
|
// Ensure any existing instance is destroyed first
|
||||||
|
if (EditorJSWrapper.instance?.editor) {
|
||||||
|
void EditorJSWrapper.instance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.holder = holder;
|
||||||
|
try {
|
||||||
|
this.editor = new EditorJS({
|
||||||
|
holder: holder,
|
||||||
|
placeholder: 'Start writing...',
|
||||||
|
onChange: async () => {
|
||||||
|
try {
|
||||||
|
const content = await this.editor!.save();
|
||||||
|
this.onChange(content);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error saving editor content:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing editor:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addBlock(text: string) {
|
||||||
|
if (!this.editor) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.editor.blocks.insert('paragraph', { text });
|
||||||
|
const updatedData = await this.editor.save();
|
||||||
|
this.onChange(updatedData);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error adding block:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
if (!this.editor) return;
|
||||||
|
try {
|
||||||
|
await this.editor.clear();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error clearing editor:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData(): Promise<OutputData> {
|
||||||
|
if (!this.editor) throw new Error('Editor not initialized');
|
||||||
|
return this.editor.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroy() {
|
||||||
|
try {
|
||||||
|
if (this.editor && typeof this.editor.destroy === 'function') {
|
||||||
|
await this.editor.destroy();
|
||||||
|
this.editor = null;
|
||||||
|
this.holder = null;
|
||||||
|
EditorJSWrapper.instance = null;
|
||||||
|
}
|
||||||
|
} catch (error: unknown) {
|
||||||
|
console.error('Error destroying editor:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [editorData, setEditorData] = useState<OutputData | null>(null);
|
const [editorData, setEditorData] = useState<OutputData | null>(null);
|
||||||
const [inputText, setInputText] = useState<string>("");
|
const [inputText, setInputText] = useState<string>("");
|
||||||
|
const [editor] = useState(() => new EditorJSWrapper((data) => setEditorData(data)));
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const editor = useEditorJS((data) => {
|
useEffect(() => {
|
||||||
setEditorData(data);
|
if (editorRef.current) {
|
||||||
});
|
editor.initialize(editorRef.current);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
const cleanup = async () => {
|
||||||
|
await editor.destroy();
|
||||||
|
};
|
||||||
|
void cleanup();
|
||||||
|
};
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
const handleAdd = async () => {
|
const handleAdd = async () => {
|
||||||
if (!inputText.trim()) return;
|
if (!inputText.trim()) return;
|
||||||
@ -121,7 +123,7 @@ function App() {
|
|||||||
<div className="flex flex-row w-full h-full gap-8 items-center px-8">
|
<div className="flex flex-row w-full h-full gap-8 items-center px-8">
|
||||||
<div className="flex-1 flex justify-center">
|
<div className="flex-1 flex justify-center">
|
||||||
<div style={{ backgroundColor: '#FFFFFF' }} className="min-h-[500px] min-w-[500px] rounded shadow-xl text-gray-900 p-6">
|
<div style={{ backgroundColor: '#FFFFFF' }} className="min-h-[500px] min-w-[500px] rounded shadow-xl text-gray-900 p-6">
|
||||||
<div style={{ margin: '20px' }} ref={editor.holderRef} />
|
<div style={{ margin: '20px' }} ref={editorRef} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col justify-center items-center gap-4">
|
<div className="flex-1 flex flex-col justify-center items-center gap-4">
|
||||||
|
Loading…
Reference in New Issue
Block a user