feat: Add image/png to the Clipboard write API (#24470) (CP: 25.2)#24551
Merged
Conversation
## Summary - Lets a Vaadin app copy an image to the system clipboard as `image/png` when a click (or any trigger) fires, alongside the existing `text/plain` and `text/html` slots that `ClipboardBinding` already supports. - Two image sources are supported: an `<img>`-rooted component already on the page, or a `DownloadHandler` that serves bytes from the server. - Supports image-only writes and multi-format writes that pack text, HTML, and an image into a single `ClipboardItem`. ## Details - **Safari activation, two places to watch.** The TS helper hands the canvas-produced `Promise<Blob>` directly to `ClipboardItem` without awaiting it, so `navigator.clipboard.write` is called synchronously inside the user gesture. The `DownloadHandler` flavour also preloads the bytes via a hidden `<img>` attached at bind time, on purpose: fetching at click time would push the promise resolution past Safari's transient activation window and the write would abort silently. - **`ClipboardContent` getters are public on the surface, returning internal `Action.Input<?>` types.** This is the price for `WriteToClipboardAction(ClipboardContent)` working across package boundaries; `Action.Input` is already documented "for internal use only" so the leak is contained. - **`ImageBlobInput` rejects non-`<img>` sources at bind time** rather than letting the canvas converter produce an opaque error in the browser later. - **Tests:** unit coverage on the new slot, the multi-format case, and the no-op stand-in for unset slots; IT coverage end-to-end via a 32×32 red PNG generated at view load (data-URL `<img>` plus the multi-format path) and a distinct blue PNG served by the `DownloadHandler` case. The IT view is sectioned and labelled so it doubles as a manual smoke-test page. ## API Changes: feature/clipboard-image vs origin/main 4 classes affected, 1 class added, 5 methods added, 4 constructors added. ### com.vaadin.flow.component.clipboard.ClipboardBinding ```java // Added public void writeImage(Component source) // copy component's root <img> as image/png public void writeImage(Component source, SerializableConsumer<@nullable String> onCopied, SerializableConsumer<PromiseAction.Error> onError) public void writeImage(DownloadHandler handler) // serve image bytes via a hidden <img> bound to the handler public void writeImage(DownloadHandler handler, SerializableConsumer<@nullable String> onCopied, SerializableConsumer<PromiseAction.Error> onError) ``` ### com.vaadin.flow.component.clipboard.ClipboardContent ```java // Added public ClipboardContent image(Component source) // set the image/png slot to a component's root <img> public Action.@nullable Input<String> getTextInput() // visibility raised from package-private public Action.@nullable Input<String> getHtmlInput() // visibility raised from package-private public Action.@nullable Input<?> getImageInput() // new slot accessor ``` ### com.vaadin.flow.component.trigger.internal.ImageBlobInput ```java // Added public class ImageBlobInput extends Action.Input<Object> // internal; renders the source <img> for the clipboard write helper public ImageBlobInput(Component source) // throws IllegalArgumentException if source's root element is not <img> public ImageBlobInput(Element source) // throws IllegalArgumentException if source is not <img> protected JsFunction toJs(Trigger trigger) ``` ### com.vaadin.flow.component.trigger.internal.WriteToClipboardAction ```java // Added public WriteToClipboardAction(Action.Input<?> imageInput) // dedicated image fire-and-forget public WriteToClipboardAction(Action.Input<?> imageInput, SerializableConsumer<@nullable String> onCopied, SerializableConsumer<PromiseAction.Error> onError) // dedicated image observed public WriteToClipboardAction(ClipboardContent content) // multi-format fire-and-forget public WriteToClipboardAction(ClipboardContent content, SerializableConsumer<@nullable String> onCopied, SerializableConsumer<PromiseAction.Error> onError) // multi-format observed ``` Note: The existing `Action.Input`-based constructors `(text, html)` and `(text, html, onCopied, onError)` keep their main signatures. The 3-arg and 5-arg multi-format input constructors are `private` and not part of the public surface. `WriteToClipboardAction` and `ImageBlobInput` live in `com.vaadin.flow.component.trigger.internal`, documented "For internal use only. May be renamed or removed in a future release." --------- Co-authored-by: Mikhail Shabarov <61410877+mshabarov@users.noreply.github.com> Co-authored-by: mikhail <mikhail@vaadin.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Artur-
approved these changes
Jun 9, 2026
Collaborator
Author
|
This ticket/PR has been released with Vaadin 25.2.0-beta2. |
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



This PR cherry-picks changes from the original PR #24470 to branch 25.2.
Original PR description