feat: Add image/png to the Clipboard write API#24470
Open
Artur- wants to merge 7 commits into
Open
Conversation
Adds writeImage(Component) — fire-and-forget and observed flavours — on
ClipboardBinding, plus image(Component) on ClipboardContent for the
multi-format path. The source can be any rasterisable image
({@code image/png}, {@code image/jpeg}, {@code image/svg+xml}, ...) with
intrinsic dimensions; cross-origin sources need
{@code crossorigin="anonymous"} on the {@code <img>} plus matching CORS
headers, otherwise the canvas is tainted and the write fails.
Internals:
- ImageBlobInput extends Action.Input<Object>; its toJs yields the
source <img> element verbatim.
- WriteToClipboardAction gains a third (image) slot; the rendered
helper call is writePayload($0(event), $1(event), $2(event)).
- The TS helper writeClipboardPayload takes an HTMLImageElement third
argument and re-encodes it via imageToPngBlob (canvas + toBlob).
The resulting Promise<Blob> is fed directly to ClipboardItem so the
navigator.clipboard.write call stays synchronous inside the user
gesture — Safari otherwise loses activation on the first await.
Tests cover the image/png slot and the multi-format case with all three
slots together.
…ardAction Restores the original 2-arg (text, html) and 4-arg observed constructors that main had, and adds matching 1-arg (image) and 3-arg observed image constructors. The 3-arg / 5-arg multi-format constructors stay as the underlying implementation that the dedicated overloads delegate into via this(...). Callers in the typical text-only, html-only, or image-only shapes no longer need to pass null placeholders for the unused slots. ClipboardBinding's write* methods, SignalInput/LiteralInput Javadoc snippets, SignalInputTest and TriggerWriteToClipboardView are all adjusted to use the dedicated overloads.
ClipboardContent is now a passive data holder with public slot getters; WriteToClipboardAction gains constructors that accept a ClipboardContent and read its slots. The text+html and image dedicated constructors delegate into private 3-arg / 5-arg primitives, so the previously public multi-format Action.Input constructors disappear from the API surface. ClipboardBinding.write(content[, callbacks]) now constructs the action as new WriteToClipboardAction(content[, …]). The two existing test cases that called the input-based 3-arg constructor directly now go through ClipboardContent.
Adds writeImage(DownloadHandler) and its observed counterpart on ClipboardBinding so server-defined image bytes can be copied to the clipboard without the caller having to add a hidden Image to the page themselves. The overload appends a display:none <img> child to the trigger host, bound to the handler via the same setAttribute path Image.setSrc(DownloadHandler) uses, then routes it through ImageBlobInput. ImageBlobInput gains an Element-accepting constructor so the binding can hand it the freshly built <img> element without wrapping it in a Component. The browser begins fetching the image as soon as the binding is set up, so the bytes are typically decoded before the user clicks. If the click races the load, ImageBlobInput's canvas converter falls back to the <img>'s load event before drawing.
Adds three IT scenarios to TriggerWriteToClipboardView/IT:
- writeImage(Image) with an in-DOM data-URL <img>
- write(ClipboardContent.text + image) packing both into one
ClipboardItem
- writeImage(DownloadHandler) with a server-served PNG generated
at view-class load via ImageIO
The recording shim in the IT awaits Promise<Blob> entries from the
ClipboardItem and normalises them to {type, size}, so the assertions
can inspect the resulting image/png blob without dealing with binary
content.
Restructures TriggerWriteToClipboardView into headed sections, each
with a one-line description of what should land in the clipboard so a
manual tester can paste into an external app and verify. Adds:
- visible 32x32 image sources (was 4x4; too small to see)
- a distinct blue image for writeImage(DownloadHandler), so a
pasted result makes it obvious which button was used
- a "Copy text + html + image" button exercising all three slots in
one ClipboardItem, with a matching IT case
|
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.



Summary
image/pngwhen a click (or any trigger) fires, alongside the existingtext/plainandtext/htmlslots thatClipboardBindingalready supports.<img>-rooted component already on the page, or aDownloadHandlerthat serves bytes from the server.ClipboardItem.Details
Promise<Blob>directly toClipboardItemwithout awaiting it, sonavigator.clipboard.writeis called synchronously inside the user gesture. TheDownloadHandlerflavour 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.ClipboardContentgetters are public on the surface, returning internalAction.Input<?>types. This is the price forWriteToClipboardAction(ClipboardContent)working across package boundaries;Action.Inputis already documented "for internal use only" so the leak is contained.ImageBlobInputrejects non-<img>sources at bind time rather than letting the canvas converter produce an opaque error in the browser later.<img>plus the multi-format path) and a distinct blue PNG served by theDownloadHandlercase. 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
com.vaadin.flow.component.clipboard.ClipboardContent
com.vaadin.flow.component.trigger.internal.ImageBlobInput
com.vaadin.flow.component.trigger.internal.WriteToClipboardAction
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 areprivateand not part of the public surface.WriteToClipboardActionandImageBlobInputlive incom.vaadin.flow.component.trigger.internal, documented "For internal use only. May be renamed or removed in a future release."