diff --git a/src/blog/editor/BlogEditor.tsx b/src/blog/editor/BlogEditor.tsx
index f8fbbce..edf3438 100644
--- a/src/blog/editor/BlogEditor.tsx
+++ b/src/blog/editor/BlogEditor.tsx
@@ -8,6 +8,8 @@ import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'
+import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
+import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { HeadingNode, QuoteNode } from '@lexical/rich-text'
import { CodeNode, CodeHighlightNode } from '@lexical/code'
import { ListItemNode, ListNode } from '@lexical/list'
@@ -20,12 +22,13 @@ import { LinkNode, AutoLinkNode } from '@lexical/link'
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin'
import { AutoLinkPlugin } from '@lexical/react/LexicalAutoLinkPlugin'
import { HashtagNode } from '@lexical/hashtag'
+import { $generateHtmlFromNodes } from '@lexical/html'
+import type { EditorState } from 'lexical'
import { ImageNode } from '../../editor/nodes/ImageNode'
import { MentionNode } from '../../editor/nodes/MentionNode'
import ToolbarPlugin from '../../editor/plugins/ToolbarPlugin'
import MarkdownPlugin from '../../editor/plugins/MarkdownShortcutPlugin'
-import DragDropPastePlugin from '../../editor/plugins/DragDropPastePlugin'
import HashtagPlugin from '../../editor/plugins/HashtagPlugin'
import MentionsPlugin from '../../editor/plugins/MentionsPlugin'
import editorTheme from '../../editor/themes/EditorTheme'
@@ -69,9 +72,26 @@ const MATCHERS = [
interface BlogEditorProps {
placeholder?: string
+ onChange?: (html: string) => void
}
-export default function BlogEditor({ placeholder }: BlogEditorProps) {
+/**
+ * OnChange wrapper component that has access to editor context
+ */
+function OnChangeWrapper({ onChange }: { onChange?: (html: string) => void }) {
+ const [editor] = useLexicalComposerContext()
+
+ const handleChange = (editorState: EditorState) => {
+ editorState.read(() => {
+ const html = $generateHtmlFromNodes(editor)
+ onChange?.(html)
+ })
+ }
+
+ return
+}
+
+export default function BlogEditor({ placeholder, onChange }: BlogEditorProps) {
const editorConfig = {
namespace: 'BlogEditor',
theme: editorTheme,
@@ -111,6 +131,7 @@ export default function BlogEditor({ placeholder }: BlogEditorProps) {
}
ErrorBoundary={LexicalErrorBoundary}
/>
+
@@ -118,7 +139,6 @@ export default function BlogEditor({ placeholder }: BlogEditorProps) {
-
diff --git a/src/blog/editor/BlogImagePlugin.tsx b/src/blog/editor/BlogImagePlugin.tsx
index 1eed2f7..a0323cc 100644
--- a/src/blog/editor/BlogImagePlugin.tsx
+++ b/src/blog/editor/BlogImagePlugin.tsx
@@ -4,14 +4,28 @@
*/
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
+import { DRAG_DROP_PASTE } from '@lexical/rich-text'
+import { isMimeType, mediaFileReader } from '@lexical/utils'
import { useEffect, useState } from 'react'
-import { $insertNodes, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical'
+import { $insertNodes, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, createCommand } from 'lexical'
import type { LexicalCommand } from 'lexical'
import { $createImageNode, ImageNode } from '../../editor/nodes/ImageNode'
import { uploadFile, getDownloadPresignedURL } from '../api'
export const INSERT_BLOG_IMAGE_COMMAND: LexicalCommand = createCommand('INSERT_BLOG_IMAGE_COMMAND')
+const ACCEPTABLE_IMAGE_TYPES = [
+ 'image/',
+ 'image/heic',
+ 'image/heif',
+ 'image/gif',
+ 'image/webp',
+ 'image/png',
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/svg+xml',
+]
+
interface UploadingImage {
id: string
file: File
@@ -27,6 +41,7 @@ export function BlogImagePlugin() {
throw new Error('BlogImagePlugin: ImageNode not registered on editor')
}
+ // Register command to insert blog images
return editor.registerCommand(
INSERT_BLOG_IMAGE_COMMAND,
(file: File) => {
@@ -55,6 +70,28 @@ export function BlogImagePlugin() {
)
}, [editor])
+ // Handle drag and drop / paste events
+ useEffect(() => {
+ return editor.registerCommand(
+ DRAG_DROP_PASTE,
+ (files) => {
+ (async () => {
+ const filesResult = await mediaFileReader(
+ files,
+ [ACCEPTABLE_IMAGE_TYPES].flatMap((x) => x),
+ )
+ for (const { file } of filesResult) {
+ if (isMimeType(file, ACCEPTABLE_IMAGE_TYPES)) {
+ editor.dispatchCommand(INSERT_BLOG_IMAGE_COMMAND, file)
+ }
+ }
+ })()
+ return true
+ },
+ COMMAND_PRIORITY_LOW,
+ )
+ }, [editor])
+
const handleImageUpload = async (file: File, uploadId: string, localUrl: string) => {
try {
// Upload file and get fileKey
@@ -109,33 +146,6 @@ export function BlogImagePlugin() {
}
}
- // Handle paste events
- useEffect(() => {
- return editor.registerCommand(
- // @ts-ignore - PASTE_COMMAND exists
- editor.PASTE_COMMAND || 'PASTE_COMMAND',
- (event: ClipboardEvent) => {
- const items = event.clipboardData?.items
- if (!items) return false
-
- for (let i = 0; i < items.length; i++) {
- const item = items[i]
- if (item.type.indexOf('image') !== -1) {
- event.preventDefault()
- const file = item.getAsFile()
- if (file) {
- editor.dispatchCommand(INSERT_BLOG_IMAGE_COMMAND, file)
- }
- return true
- }
- }
-
- return false
- },
- COMMAND_PRIORITY_EDITOR
- )
- }, [editor])
-
return null
}