-
Notifications
You must be signed in to change notification settings - Fork 336
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: image placeholders in tiptap (#10703)
Signed-off-by: Matt Krick <[email protected]>
- Loading branch information
Showing
7 changed files
with
159 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
packages/client/shared/tiptap/extensions/ImageUploadBase.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import {Node} from '@tiptap/react' | ||
|
||
declare module '@tiptap/core' { | ||
interface Commands<ReturnType> { | ||
imageUpload: { | ||
setImageUpload: () => ReturnType | ||
} | ||
} | ||
} | ||
|
||
export const ImageUploadBase = Node.create({ | ||
name: 'imageUpload', | ||
|
||
isolating: true, | ||
|
||
defining: true, | ||
|
||
group: 'block', | ||
|
||
draggable: true, | ||
|
||
selectable: true, | ||
|
||
inline: false, | ||
|
||
parseHTML() { | ||
return [ | ||
{ | ||
tag: `div[data-type="${this.name}"]` | ||
} | ||
] | ||
}, | ||
|
||
renderHTML() { | ||
return ['div', {'data-type': this.name}] | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 81 additions & 16 deletions
97
packages/client/tiptap/extensions/imageUpload/ImageUploadView.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,91 @@ | ||
import ImageIcon from '@mui/icons-material/Image' | ||
import {NodeViewWrapper, type Editor} from '@tiptap/react' | ||
import {useCallback} from 'react' | ||
import * as Popover from '@radix-ui/react-popover' | ||
import {NodeViewWrapper, type NodeViewProps} from '@tiptap/react' | ||
import {useEffect, useRef, useState} from 'react' | ||
import useEventCallback from '../../../hooks/useEventCallback' | ||
import {ImageSelector} from './ImageSelector' | ||
|
||
interface Props { | ||
getPos(): number | ||
editor: Editor | ||
const useHideWhenTriggerHidden = (setOpen: (open: boolean) => void) => { | ||
const triggerRef = useRef(null) | ||
useEffect(() => { | ||
const observer = new IntersectionObserver( | ||
([entry]) => { | ||
if (!entry?.isIntersecting) { | ||
setOpen(false) | ||
} | ||
}, | ||
{ | ||
root: null, | ||
threshold: 0.9 // Ensure the div is completely hidden | ||
} | ||
) | ||
|
||
if (triggerRef.current) { | ||
observer.observe(triggerRef.current) | ||
} | ||
|
||
return () => { | ||
if (triggerRef.current) { | ||
observer.unobserve(triggerRef.current) | ||
} | ||
} | ||
}, []) | ||
return triggerRef | ||
} | ||
|
||
export const ImageUploadView = (props: Props) => { | ||
const {getPos, editor} = props | ||
const onClick = useCallback(() => { | ||
editor.commands.setNodeSelection(getPos()) | ||
}, [getPos, editor.commands]) | ||
export const ImageUploadView = (props: NodeViewProps) => { | ||
const {editor} = props | ||
const [open, setOpen] = useState(false) | ||
const onOpenChange = (willOpen: boolean) => { | ||
const {isEditable} = editor | ||
if (!willOpen) { | ||
if (isEditable) { | ||
editor.commands.focus() | ||
} | ||
setOpen(false) | ||
} else if (isEditable) { | ||
setOpen(true) | ||
} | ||
} | ||
const openPopover = useEventCallback(() => { | ||
setOpen(true) | ||
}) | ||
|
||
useEffect(() => { | ||
editor.storage.imageUpload.emitter.on('enter', openPopover) | ||
return () => { | ||
editor.storage.imageUpload.emitter.off('enter', openPopover) | ||
} | ||
}, []) | ||
const triggerRef = useHideWhenTriggerHidden(setOpen) | ||
|
||
return ( | ||
<NodeViewWrapper> | ||
<div className='m-0 p-0' onClick={onClick} contentEditable={false}> | ||
<div className='flex items-center rounded bg-slate-200 p-2'> | ||
<ImageIcon className='size-6' /> | ||
<span className='text-sm'>Add an image</span> | ||
</div> | ||
</div> | ||
<Popover.Root open={open} onOpenChange={onOpenChange}> | ||
<Popover.Trigger asChild> | ||
<div className='m-0 p-0' contentEditable={false} ref={triggerRef}> | ||
<div className='flex cursor-pointer items-center rounded bg-slate-200 p-2 hover:bg-slate-300'> | ||
<ImageIcon className='size-6' /> | ||
<span className='text-sm'>Add an image</span> | ||
</div> | ||
</div> | ||
</Popover.Trigger> | ||
<Popover.Portal> | ||
<Popover.Content | ||
asChild | ||
align='start' | ||
alignOffset={8} | ||
onOpenAutoFocus={(e) => { | ||
e.preventDefault() | ||
}} | ||
> | ||
{/* z-30 is for expanded reflection stacks using Zindex.DIALOG */} | ||
<div className='absolute left-0 top-0 z-30'> | ||
<ImageSelector editor={editor} /> | ||
</div> | ||
</Popover.Content> | ||
</Popover.Portal> | ||
</Popover.Root> | ||
</NodeViewWrapper> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters