Use Drag-and-Drop
With local images
You must invoke the addOnUISdk.app.enableDragToDocument()
method for each draggable image to implement this feature. It accepts two parameters: the HTMLElement
and an object with a previewCallback()
that returns the image URL for preview purposes, and a completionCallback()
that fetches the corresponding blob to finalize insertion into the document.
You also need to listen for "dragstart"
and "dragend"
events to manage logs or other custom behaviour when the user interacts with the images.
Example
Copied to your clipboardimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";// Wait for the SDK to be ready before rendering elements in the DOM.addOnUISdk.ready.then(async () => {// Create the image element in the DOM.const image = document.createElement("img");image.id = "image.jpg";// The image is local to the add-on.image.src = "./images/image.jpg";image.addEventListener("click", addToDocument);// Enable drag to document for the image.addOnUISdk.app.enableDragToDocument(image, {previewCallback: (element) => {// return the new URL for preview purposesreturn new URL(element.src);},completionCallback: async (element) => {// return the blob for the imagereturn [{ blob: await getBlob(element.src) }];},});// Add the image to the document.document.body.appendChild(image);});// Utility functions//Add image to the document.async function addToDocument(event) {const url = event.currentTarget.src;const blob = await getBlob(url);addOnUISdk.app.document.addImage(blob);}// Handle "dragstart" eventfunction startDrag(eventData) {console.log("The drag event has started for", eventData.element.id);}// Handle "dragend" eventfunction endDrag(eventData) {if (!eventData.dropCancelled) {console.log("The drag event has ended for", eventData.element.id);} else {console.log("The drag event was cancelled for", eventData.element.id);}}// Get the binary object for the image.async function getBlob(url) {return await fetch(url).then((response) => response.blob());}
With remote images or audio
To implement drag and drop with remotely hosted images, you similarly invoke addOnUISdk.app.enableDragToDocument()
, but you fetch the resource from its remote URL. Provide a previewCallback()
that returns the preview URL and a completionCallback()
that retrieves the image as a blob. You can then attach the same "dragstart"
and "dragend"
event handlers to log or customize interactions as needed.
To drag audio content, you must specify an additional attributes
object with a title
property. A note on how to include it is found in the following example.
Example
Copied to your clipboardimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";// Enable drag support for an elementfunction makeDraggableUsingUrl(elementId: string, previewUrl: string) {const image = document.getElementById(elementId);const dragCallbacks = {previewCallback: (image: HTMLElement) => {// Return a new URL for the remote previewreturn new URL(previewUrl);},completionCallback: async (image: HTMLElement) => {// Fetch and return the image blobconst imageBlob = await fetch(image.src).then((response) =>response.blob());return [{ blob: imageBlob }];// ⚠️ for audio content, an attributes object// with the title is mandatory. For example:// return [{ blob: audioBlob, attributes: { title: "Jazzy beats" } }];},};try {addOnUISdk.app.enableDragToDocument(image, dragCallbacks);} catch (error) {console.log("Failed to enable DragToDocument:", error);}}// Log start of the drag eventaddOnUISdk.app.on("dragstart", (eventData: DragStartEventData) => {console.log("The drag event has started for", eventData.element);});// Log end of the drag eventaddOnUISdk.app.on("dragend", (eventData: DragEndEventData) => {if (!eventData.dropCancelled) {console.log("The drag event has ended for", eventData.element);disableDragToDocument();} else {console.log("The drag event was cancelled for", eventData.element);console.log("Cancel Reason:", eventData.dropCancelReason);}});
Dragging converted documents
If the drag and drop operation is used to import a PDF document, and was specifically one converted from a Word document (.docx
) or Google Docs (.gdoc
) format, to improve the user experience, use the sourceMimeType
parameter in the attributes
object. This ensures that when users drag the converted PDF, the import consent dialog shows "Import a document" instead of "Import a PDF".
Copied to your clipboard// Enable drag support for a converted documentfunction makeDraggableConvertedDoc(elementId: string, convertedPdfBlob: Blob, originalMimeType: string) {const element = document.getElementById(elementId);const dragCallbacks = {previewCallback: (element: HTMLElement) => {// URL of image to display during drag operationreturn new URL(element.src);},completionCallback: async (element: HTMLElement) => {// Return the converted PDF blob with source mime type informationreturn [{blob: convertedPdfBlob,attributes: {title: "Converted Document",sourceMimeType: originalMimeType // "docx" or "gdoc"}}];},};addOnUISdk.app.enableDragToDocument(element, dragCallbacks);}
Best Practices and Limitations
If the content being dragged is an animated GIF, it will be added as an animated GIF to the document, as long as it fits the size criteria for animated GIF's. In the event that it doesn't fit the size criteria, an error toast will be shown to the user.
Since the Add-on SDK uses pointer event handlers to perform drag operations, you should ensure that you don't attach any pointer event handlers that prevent default or stop propagation. Adding those types of handlers will kill the built-in handlers and cause the events not to work.
You should not attach click
event listeners to drag-enabled elements in the capture phase, as the Add-on SDK attaches a cancelClickEvent
handler to drag-enabled elements to ensure that the automatic click (pointer down + pointer up automatically fires a click event) doesn't fire. Adding other handlers to this same element will trigger them on drag & drop completion.
Use Chrome devTools to check the handlers attached to the element and its ancestors to identify any that may be causing conflicts with drag and drop handlers.
There are several code samples that implement drag and drop, including the import-images-using-oauth and pix projects that you can reference.
FAQs
Q: How do I enable drag-and-drop for an element?
A: Call addOnUISdk.app.enableDragToDocument(element, callbacks)
with preview and completion callbacks.
Q: What callbacks are required?
A: Provide previewCallback()
for preview URL and completionCallback()
to return the blob for insertion.
Q: How do I handle drag events?
A: Listen for "dragstart"
and "dragend"
events using addOnUISdk.app.on()
.
Q: What's special about audio content?
A: Audio requires an attributes
object with a title
property in the completion callback return.
Q: Can I drag remote images?
A: Yes, fetch the remote URL in the completion callback and return the blob.
Q: How do I use sourceMimeType when dragging converted documents?
A: Add sourceMimeType
to the attributes object in completionCallback to show "Import a document" instead of "Import a PDF" in the consent dialog.
Q: When should I include sourceMimeType in drag operations?
A: Use it when dragging PDFs that were converted from other document types like Word (.docx) or Google Docs (.gdoc) to provide clearer user messaging.
Q: What sourceMimeType values work for drag and drop?
A: Common values include "docx" for Word documents and "gdoc" for Google Docs. Use the original document format before PDF conversion.
Q: What event handlers should I avoid?
A: Avoid pointer event handlers that prevent default or stop propagation on drag-enabled elements.