Skip to content

Commit

Permalink
feat: images can be inserted in nested editors
Browse files Browse the repository at this point in the history
Fix the image insertion regression from v2.

Fixes #281
Fixes #238
  • Loading branch information
petyosi committed Jan 6, 2024
1 parent 1a4d96f commit aa0a503
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 75 deletions.
5 changes: 4 additions & 1 deletion src/examples/_boilerplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ export const ALL_PLUGINS = [
headingsPlugin({ allowedHeadingLevels: [1, 2, 3] }),
linkPlugin(),
linkDialogPlugin(),
imagePlugin({ imageAutocompleteSuggestions: ['https://via.placeholder.com/150', 'https://via.placeholder.com/150'] }),
imagePlugin({
imageAutocompleteSuggestions: ['https://via.placeholder.com/150', 'https://via.placeholder.com/150'],
imageUploadHandler: async () => Promise.resolve('https://picsum.photos/200/300')
}),
tablePlugin(),
thematicBreakPlugin(),
frontmatterPlugin(),
Expand Down
154 changes: 80 additions & 74 deletions src/plugins/image/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { realmPlugin } from '../../RealmWithPlugins'
import { $wrapNodeInElement } from '@lexical/utils'
import { $wrapNodeInElement, mergeRegister } from '@lexical/utils'
import { Action, Cell, Signal, map, mapTo, withLatestFrom } from '@mdxeditor/gurx'
import {
$createParagraphNode,
Expand All @@ -22,8 +21,16 @@ import {
PASTE_COMMAND,
createCommand
} from 'lexical'
import { realmPlugin } from '../../RealmWithPlugins'
import { CAN_USE_DOM } from '../../utils/detectMac'
import { addComposerChild$, addExportVisitor$, addImportVisitor$, addLexicalNode$, rootEditor$ } from '../core'
import {
activeEditor$,
addComposerChild$,
addExportVisitor$,
addImportVisitor$,
addLexicalNode$,
createActiveEditorSubscription$
} from '../core'
import { ImageDialog } from './ImageDialog'
import { $createImageNode, $isImageNode, CreateImageNodeParameters, ImageNode } from './ImageNode'
import { LexicalImageVisitor } from './LexicalImageVisitor'
Expand Down Expand Up @@ -114,7 +121,7 @@ export const imageDialogState$ = Cell<InactiveImageDialogState | NewImageDialogS
{ type: 'inactive' },
(r) => {
r.sub(
r.pipe(saveImage$, withLatestFrom(rootEditor$, imageUploadHandler$, imageDialogState$)),
r.pipe(saveImage$, withLatestFrom(activeEditor$, imageUploadHandler$, imageDialogState$)),
([values, theEditor, imageUploadHandler, dialogState]) => {
const handler =
dialogState.type === 'editing'
Expand Down Expand Up @@ -152,77 +159,76 @@ export const imageDialogState$ = Cell<InactiveImageDialogState | NewImageDialogS
}
)

r.sub(rootEditor$, (editor) => {
editor?.registerCommand<InsertImagePayload>(
INSERT_IMAGE_COMMAND,
(payload) => {
const imageNode = $createImageNode(payload)
$insertNodes([imageNode])
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
}

return true
},
COMMAND_PRIORITY_EDITOR
)

r.pub(createActiveEditorSubscription$, (editor) => {
const theUploadHandler = r.getValue(imageUploadHandler$)

editor?.registerCommand<DragEvent>(
DRAGSTART_COMMAND,
(event) => {
return onDragStart(event)
},
COMMAND_PRIORITY_HIGH
)
editor?.registerCommand<DragEvent>(
DRAGOVER_COMMAND,
(event) => {
return onDragover(event)
},
COMMAND_PRIORITY_LOW
)

editor?.registerCommand<DragEvent>(
DROP_COMMAND,
(event) => {
return onDrop(event, editor, r.getValue(imageUploadHandler$))
},
COMMAND_PRIORITY_HIGH
)

if (theUploadHandler === null) {
return
}

editor?.registerCommand(
PASTE_COMMAND,
(event: ClipboardEvent) => {
let cbPayload = Array.from(event.clipboardData?.items || [])
cbPayload = cbPayload.filter((i) => /image/.test(i.type)) // Strip out the non-image bits

if (!cbPayload.length || cbPayload.length === 0) {
return false
} // If no image was present in the collection, bail.

const imageUploadHandlerValue = r.getValue(imageUploadHandler$)!

Promise.all(cbPayload.map((file) => imageUploadHandlerValue(file.getAsFile()!)))
.then((urls) => {
urls.forEach((url) => {
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
src: url,
altText: ''
})
})
})
.catch((e) => {
throw e
})
return true
},
COMMAND_PRIORITY_CRITICAL
return mergeRegister(
editor?.registerCommand<InsertImagePayload>(
INSERT_IMAGE_COMMAND,
(payload) => {
const imageNode = $createImageNode(payload)
$insertNodes([imageNode])
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd()
}

return true
},
COMMAND_PRIORITY_EDITOR
),
editor?.registerCommand<DragEvent>(
DRAGSTART_COMMAND,
(event) => {
return onDragStart(event)
},
COMMAND_PRIORITY_HIGH
),
editor?.registerCommand<DragEvent>(
DRAGOVER_COMMAND,
(event) => {
return onDragover(event)
},
COMMAND_PRIORITY_LOW
),

editor?.registerCommand<DragEvent>(
DROP_COMMAND,
(event) => {
return onDrop(event, editor, r.getValue(imageUploadHandler$))
},
COMMAND_PRIORITY_HIGH
),
...(theUploadHandler !== null
? [
editor?.registerCommand(
PASTE_COMMAND,
(event: ClipboardEvent) => {
let cbPayload = Array.from(event.clipboardData?.items || [])
cbPayload = cbPayload.filter((i) => /image/.test(i.type)) // Strip out the non-image bits

if (!cbPayload.length || cbPayload.length === 0) {
return false
} // If no image was present in the collection, bail.

const imageUploadHandlerValue = r.getValue(imageUploadHandler$)!

Promise.all(cbPayload.map((file) => imageUploadHandlerValue(file.getAsFile()!)))
.then((urls) => {
urls.forEach((url) => {
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
src: url,
altText: ''
})
})
})
.catch((e) => {
throw e
})
return true
},
COMMAND_PRIORITY_CRITICAL
)
]
: [])
)
})
}
Expand Down

0 comments on commit aa0a503

Please sign in to comment.