Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can we render ReactNodes as output to Codemirror extensions? #689

Open
kevinschaich opened this issue Oct 9, 2024 · 2 comments
Open

Comments

@kevinschaich
Copy link

Since this package is dedicated to integration with React, I figured this would be a good place to ask instead of Codemirror.

How can we render ReactNodes in response to extension output? A few places this would be useful are linters, autocomplete, and tooltips.

Codemirror supports very basic output for each one of these - it mostly expects string parameters and then stubs it into the generated DOM elements. But it would be very nice to be able to supply replacement components or a renderCompletion etc. to apply custom icons, formatting, styling, images, etc. - similar to the more rich functionality that exists in something like VSCode:

image
@jaywcjlove
Copy link
Member

I think your idea is very difficult to implement. I once tried using something like the following, and it was very challenging:

<CodeMirror>
    <CodeMirror.Extensions ... />
</CodeMirror>

If you find a good way to achieve this, feel free to share it here. @kevinschaich

@kevinschaich
Copy link
Author

I tried a few ways but it's very difficult to shoehorn in. You can render a second react root which I didn't particularly want to do. Here's a generalized version of what I ended up going with:

'use client'

import { Popover, PopoverAnchor, PopoverContent } from '@radix-ui/react-popover'
import { store } from '@/lib/state/store'
import { EditorSelection, EditorView, keymap } from '@uiw/react-codemirror'

export const RichContentPopover = () => {
    const { top, bottom, left, right, isRichContentPopoverOpen } = store.get()

    return (
        <Popover open={isRichContentPopoverOpen}>
            <PopoverAnchor asChild>
                <div
                    className='transition-all'
                    style={{
                        position: 'fixed',
                        left: left,
                        top: top,
                        width: right - left,
                        height: bottom - top,
                    }}
                />
            </PopoverAnchor>

            <PopoverContent side='top' align='center' className='w-fit' updatePositionStrategy='optimized'>
                ...
            </PopoverContent>
        </Popover>
    )
}

export const richContentExtension = () => {
    return keymap.of([
        {
            key: 'Mod-k',
            preventDefault: true,
            run: (v: EditorView) => {
                const selection: EditorSelection = v.state.selection

                store.setSelectionScreenCoords({
                    left: v.coordsAtPos(selection.main.from)?.left ?? 0,
                    top: v.coordsAtPos(selection.main.from)?.top ?? 0,
                    right: v.coordsAtPos(selection.main.to)?.right ?? 0,
                    bottom: v.coordsAtPos(selection.main.to)?.bottom ?? 0,
                })

                return true
            },
        },
    ])
}

It will render a full react component based on wherever your selection is at the time of pressing CMD+K.

Others could bind this to a mouse event or change event as well.

image

@jaywcjlove could be cool to wrap this somehow and include it as an optional component!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants