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

Add Clipboard component for handling global on_paste event #3513

Merged
merged 3 commits into from
Jun 26, 2024

Conversation

masenf
Copy link
Collaborator

@masenf masenf commented Jun 17, 2024

Supersede #3078 to fix #3055

Creates a new rx.clipboard component with on_paste handler that will be called when pasting data into the page. If it has children components, then only those components will register the paste handler, otherwise, it gets registered at the top-level document (you can also pass targets prop as a list of ids).

The handler will be called with the data arg as list of tuples of (mime_type, data). If the data is text, it will be a regular string. If the data is associated with a different type, then it will be returned as a base64 encoded data uri. There may be multiple entries in the data list for example if the data is available in several formats (text/plain and text/html) or if several files have been pasted from the filesystem.

Sample code

import reflex as rx


class State(rx.State):
    image_uri: str = ""
    targets: list[str] = ["t4", "t3"]

    def on_paste_generic(self, data, source):
        for type, content in data:
            if type.startswith("image/"):
                self.image_uri = content
            yield rx.toast.info(f"Pasted {type} in {source}", description=content[:64])

    def on_paste(self, data):
        yield from self.on_paste_generic(data, "document")

    def on_paste_input(self, data):
        yield from self.on_paste_generic(data, "input")

    def on_paste_textarea(self, data):
        yield from self.on_paste_generic(data, "textarea")

    def handle_checked_change(self, target, checked):
        if checked:
            self.targets.append(target)
        else:
            try:
                self.targets.remove(target)
            except ValueError:
                pass


def togglable_input(id: str):
    return rx.hstack(
        rx.input(id=id),
        rx.checkbox(
            checked=State.targets.contains(id),
            on_change=State.handle_checked_change(id)
        ),
    )


def index() -> rx.Component:
    return rx.vstack(
        rx.clipboard(on_paste=State.on_paste),
        rx.clipboard(on_paste=State.on_paste_input.stop_propagation, targets=State.targets),
        rx.cond(State.image_uri, rx.image(src=State.image_uri), rx.text("No image")),
        rx.clipboard(rx.text_area(), on_paste=State.on_paste_textarea.prevent_default),
        rx.clipboard(rx.text_area(), on_paste=State.on_paste_textarea.stop_propagation),
        togglable_input("t1"),
        togglable_input("t2"),
        togglable_input("t3"),
        rx.text("Selected inputs: ", State.targets.to_string()),
    )


app = rx.App()
app.add_page(index)

@masenf masenf mentioned this pull request Jun 17, 2024
10 tasks
@masenf masenf merged commit 956bc0a into main Jun 26, 2024
47 checks passed
@masenf masenf deleted the masenf/global-paste-handler branch June 26, 2024 16:22
@dentro-innovation
Copy link

When using the new paste handler, how do I need to write the event handler so that if a file is pasted we only handle the file but do not paste in the file path as text into the input field / text area, but when we paste in pure text we display the text in the input field / text area?

When I use prevent_default the file path isn't displayed when I paste a file, but then I also can't paste in normal text.
If I don't use prevent_default, the file path is always pasted in along

@masenf
Copy link
Collaborator Author

masenf commented Jul 11, 2024

@dentro-innovation so there's not currently a way to accomplish this, but the logic in the paste handler could be update to achieve what sounds like a pretty common workflow. Can you file a follow up bug for this?

In the meantime, you might be able to get fancy with a controlled input, i haven't checked if the on_paste gets called before on_change though.

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

Successfully merging this pull request may close these issues.

Global on_paste event listener
3 participants