Drag and drop
Something that Canva users love is the ability to drag and drop content users straight into their designs. Apps can use the Apps SDK to support this behavior, ensuring that the user experience is delightfully consistent.
Content types
Apps can enable drag and drop for various types of content, including:
Additional content types may be supported in the future.
Rendering the UI
Apps need to render something in their UI that can be dragged.
This can be as simple as an HTMLDivElement
with a draggable
attribute:
<div draggable>This text can be dragged.</div>
In the App UI Kit though, we've also provided a number of card components that are designed to work seamlessly with drag and drop. The components include:
AudioCard
EmbedCard
ImageCard
TypographyCard
VideoCard
To see how to use these components, see Code samples.
Handling drag events
Apps can handle drag events by registering an onDragStart
callback and passing the drag event into either of the following methods:
startDragAtPoint
startDragAtCursor
Both methods add content to the user's design, but:
startDragAtPoint
is only compatible with design types that support absolute positions, which is all design types except for documents.startDragAtCursor
is only compatible with design types that contain streams of text, which is only the document design type.
The supported content types also depend on the method being called:
Where possible, apps should determine the context in which the app is running and either call the compatible method or make it obvious when functionality isn't available. To learn more, see Feature support.
Handling clicks
For accessibility reasons, drag and drop should not be the only way that users can add content to a design. Apps should also support click events. This behavior is built into the App UI Kit components and demonstrated below.
Code samples
import React from "react";import { AudioCard, AudioContextProvider, Rows } from "@canva/app-ui-kit";import { upload } from "@canva/asset";import { addAudioTrack, ui } from "@canva/design";import { useFeatureSupport } from "utils/use_feature_support";import styles from "styles/components.css";export function App() {const isSupported = useFeatureSupport();async function handleClick() {if (isSupported(addAudioTrack)) {const asset = await upload({type: "audio",title: "Example audio",mimeType: "audio/mp3",url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",});addAudioTrack({ref: asset.ref,});}}function handleDragStart(event: React.DragEvent<HTMLElement>) {if (isSupported(ui.startDragAtPoint)) {ui.startDragAtPoint(event, {type: "audio",title: "Example audio",resolveAudioRef: () => {return upload({type: "audio",title: "Example audio",mimeType: "audio/mp3",url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",});},});}if (isSupported(ui.startDragAtCursor)) {ui.startDragAtCursor(event, {type: "audio",title: "Example audio",resolveAudioRef: () => {return upload({type: "audio",title: "Example audio",mimeType: "audio/mp3",url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",});},});}}return (<div className={styles.scrollContainer}><Rows spacing="1u">{/* A single `AudioContextProvider` component must be an ancestor to all `AudioCard` components*/}<AudioContextProvider><AudioCardtitle="Example audio"audioPreviewUrl="https://www.canva.dev/example-assets/audio-import/audio.mp3"durationInSeconds={86}onClick={handleClick}onDragStart={handleDragStart}/></AudioContextProvider></Rows></div>);}