Skip to content

Commit

Permalink
feat: custom image preview hook (Fixes #62, Fixes #65)
Browse files Browse the repository at this point in the history
* feat: add a hook for updating the image source before it's rendered

* Prevent double rendering in ImageEditor

* Update LexicalImageVisitor to construct HTML without fetching the image
  • Loading branch information
FinlayBP authored Sep 6, 2023
1 parent 8355ad9 commit c02f699
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 7 deletions.
20 changes: 20 additions & 0 deletions src/examples/images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,23 @@ export function JsxImage() {
</>
)
}

export function ImageWithPreviewHook() {
return (
<>
<MDXEditor
markdown={markdownWithHtmlImages}
plugins={[
imagePlugin({
disableImageResize: true,
imagePreviewHandler: async (imageSource) => Promise.resolve(`${imageSource}?grayscale`)
}),
diffSourcePlugin(),
jsxPlugin(),
toolbarPlugin({ toolbarContents: () => <DiffSourceToggleWrapper>:)</DiffSourceToggleWrapper> })
]}
onChange={console.log}
/>
</>
)
}
23 changes: 19 additions & 4 deletions src/plugins/image/ImageEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function LazyImage({
return <img className={className || undefined} src={src} title={title} ref={imageRef} draggable="false" width={width} height={height} />
}

export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorProps): JSX.Element {
export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorProps): JSX.Element | null {
const imageRef = React.useRef<null | HTMLImageElement>(null)
const buttonRef = React.useRef<HTMLButtonElement | null>(null)
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey)
Expand All @@ -76,6 +76,8 @@ export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorP
const activeEditorRef = React.useRef<LexicalEditor | null>(null)
const [isResizing, setIsResizing] = React.useState<boolean>(false)
const [disableImageResize] = imagePluginHooks.useEmitterValues('disableImageResize')
const [imagePreviewHandler] = imagePluginHooks.useEmitterValues('imagePreviewHandler')
const [imageSource, setImageSource] = React.useState<string | null>(null)

const onDelete = React.useCallback(
(payload: KeyboardEvent) => {
Expand Down Expand Up @@ -126,6 +128,18 @@ export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorP
[editor, setSelected]
)

React.useEffect(() => {
if (imagePreviewHandler) {
const callPreviewHandler = async () => {
const updatedSrc = await imagePreviewHandler(src)
setImageSource(updatedSrc)
}
callPreviewHandler().catch(console.error)
} else {
setImageSource(src)
}
}, [src, imagePreviewHandler])

React.useEffect(() => {
let isMounted = true
const unregister = mergeRegister(
Expand Down Expand Up @@ -208,7 +222,8 @@ export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorP

const draggable = $isNodeSelection(selection)
const isFocused = isSelected
return (

return imageSource !== null ? (
<React.Suspense fallback={null}>
<div className={styles.imageWrapper} data-editor-block-type="image">
<div draggable={draggable}>
Expand All @@ -218,7 +233,7 @@ export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorP
className={classNames({
[styles.focusedImage]: isFocused
})}
src={src}
src={imageSource}
title={title || ''}
imageRef={imageRef}
/>
Expand All @@ -228,5 +243,5 @@ export function ImageEditor({ src, title, nodeKey, width, height }: ImageEditorP
)}
</div>
</React.Suspense>
)
) : null
}
4 changes: 2 additions & 2 deletions src/plugins/image/LexicalImageVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const LexicalImageVisitor: LexicalExportVisitor<ImageNode, Mdast.Image> =
if (lexicalNode.getWidth() !== 'inherit') {
img.width = lexicalNode.getWidth() as number
}
img.src = lexicalNode.getSrc()

if (lexicalNode.getAltText()) {
img.alt = lexicalNode.getAltText()
}
Expand All @@ -25,7 +25,7 @@ export const LexicalImageVisitor: LexicalExportVisitor<ImageNode, Mdast.Image> =

actions.appendToParent(mdastParent, {
type: 'html',
value: img.outerHTML.replace(/>$/, '/>')
value: img.outerHTML.replace(/>$/, ` src="${lexicalNode.getSrc()}" />`)
})
} else {
actions.appendToParent(mdastParent, {
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/image/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { CAN_USE_DOM } from '../../utils/detectMac'
export * from './ImageNode'

export type ImageUploadHandler = ((image: File) => Promise<string>) | null
export type ImagePreviewHandler = ((imageSource: string) => Promise<string>) | null

/** @internal */
export const imageSystem = system(
Expand All @@ -37,6 +38,7 @@ export const imageSystem = system(
const imageAutocompleteSuggestions = r.node<string[]>([])
const disableImageResize = r.node<boolean>(false)
const imageUploadHandler = r.node<ImageUploadHandler>(null)
const imagePreviewHandler = r.node<ImagePreviewHandler>(null)

r.sub(r.pipe(insertImage, r.o.withLatestFrom(rootEditor)), ([src, theEditor]) => {
theEditor?.update(() => {
Expand Down Expand Up @@ -123,7 +125,8 @@ export const imageSystem = system(
imageUploadHandler,
imageAutocompleteSuggestions,
disableImageResize,
insertImage
insertImage,
imagePreviewHandler
}
},
[coreSystem]
Expand All @@ -133,6 +136,7 @@ interface ImagePluginParams {
imageUploadHandler?: ImageUploadHandler
imageAutocompleteSuggestions?: string[]
disableImageResize?: boolean
imagePreviewHandler?: ImagePreviewHandler
}

export const [
Expand All @@ -148,6 +152,7 @@ export const [
realm.pubKey('imageUploadHandler', params?.imageUploadHandler || null)
realm.pubKey('imageAutocompleteSuggestions', params?.imageAutocompleteSuggestions || [])
realm.pubKey('disableImageResize', Boolean(params?.disableImageResize))
realm.pubKey('imagePreviewHandler', params?.imagePreviewHandler || null)
},

init: (realm) => {
Expand Down

0 comments on commit c02f699

Please sign in to comment.