DropZone
A drop zone is an area into which one or multiple objects can be dragged and dropped.
install | yarn add @adobe/react-spectrum |
---|---|
version | 3.37.0 |
usage | import {DropZone} from '@adobe/react-spectrum' |
Example#
import Upload from '@spectrum-icons/illustrations/Upload';
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() => setIsFilled(true)}>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled ? 'You dropped something!' : 'Drag and drop your file'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
)
}
import Upload from '@spectrum-icons/illustrations/Upload';
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() => setIsFilled(true)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop your file'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
);
}
import Upload from '@spectrum-icons/illustrations/Upload';
function Example() {
let [
isFilled,
setIsFilled
] = React.useState(
false
);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() =>
setIsFilled(
true
)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop your file'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
);
}
The Draggable
component used above is defined below. See useDrag for more details and documentation.
Show code
import {useDrag} from 'react-aria';
function Draggable() {
let { dragProps, isDragging } = useDrag({
getItems() {
return [{
'text/plain': 'hello world',
'my-app-custom-type': JSON.stringify({ message: 'hello world' })
}];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `}
>
Drag me
</div>
);
}
import {useDrag} from 'react-aria';
function Draggable() {
let { dragProps, isDragging } = useDrag({
getItems() {
return [{
'text/plain': 'hello world',
'my-app-custom-type': JSON.stringify({
message: 'hello world'
})
}];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `}
>
Drag me
</div>
);
}
import {useDrag} from 'react-aria';
function Draggable() {
let {
dragProps,
isDragging
} = useDrag({
getItems() {
return [{
'text/plain':
'hello world',
'my-app-custom-type':
JSON.stringify(
{
message:
'hello world'
}
)
}];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `}
>
Drag me
</div>
);
}
Show CSS
.draggable {
display: inline-block;
vertical-align: top;
border: 1px solid gray;
padding: 10px;
margin-right: 20px;
margin-bottom: 20px;
border-radius: 4px;
height: fit-content;
}
.draggable.dragging {
opacity: 0.5;
}
.draggable {
display: inline-block;
vertical-align: top;
border: 1px solid gray;
padding: 10px;
margin-right: 20px;
margin-bottom: 20px;
border-radius: 4px;
height: fit-content;
}
.draggable.dragging {
opacity: 0.5;
}
.draggable {
display: inline-block;
vertical-align: top;
border: 1px solid gray;
padding: 10px;
margin-right: 20px;
margin-bottom: 20px;
border-radius: 4px;
height: fit-content;
}
.draggable.dragging {
opacity: 0.5;
}
Content#
A DropZone accepts an IllustratedMessage as a child which is comprised of three areas: an illustration, a title, and a body. Each of these sections can be populated by providing the following components to the IllustratedMessage as children: a SVG, a Heading (title), and a Content (body). A FileTrigger is commonly paired with a DropZone to allow a user to choose files from their device.
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() => setIsFilled(true)}>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled ? 'You dropped something!' : 'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
onSelect={()=> setIsFilled(true)}>
<Button variant="primary">Browse</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
)
}
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() => setIsFilled(true)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
onSelect={() => setIsFilled(true)}
>
<Button variant="primary">Browse</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
);
}
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [
isFilled,
setIsFilled
] = React.useState(
false
);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={isFilled}
onDrop={() =>
setIsFilled(
true
)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
onSelect={() =>
setIsFilled(
true
)}
>
<Button variant="primary">
Browse
</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
);
}
Accessibility#
A visual label should be provided to DropZone
using a Text
element with a label
slot. If it is not provided, then an aria-label
or aria-labelledby
prop must be passed to identify the visually hidden button to assistive technology.
Internationalization#
In order to internationalize a DropZone, a localized string should be passed to the Text
element with a label
slot or to the aria-label
prop, in addition to the replaceMessage
prop.
Events#
DropZone
supports drop operations via mouse, keyboard, and touch. You can handle all of these via the onDrop
prop. In addition, the onDropEnter
, onDropMove
, and onDropExit
events are fired as the user enter and exists the dropzone during a drag operation.
The following example uses an onDrop
handler to update the filled status stored in React state.
import {Flex} from '@adobe/react-spectrum';
import File from '@spectrum-icons/illustrations/File';
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
setFilledSrc(item.name);
} else if (item.kind === 'text' && item.types.has('text/plain')) {
setFilledSrc(await item.getText('text/plain'));
}
});
}}
>
{filledSrc
? (
<Flex
direction="column"
alignItems="center"
justifyContent="center"
gap="size-100"
>
<File />
{filledSrc}
</Flex>
)
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and drop here
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
import {Flex} from '@adobe/react-spectrum';
import File from '@spectrum-icons/illustrations/File';
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
setFilledSrc(item.name);
} else if (
item.kind === 'text' &&
item.types.has('text/plain')
) {
setFilledSrc(
await item.getText('text/plain')
);
}
});
}}
>
{filledSrc
? (
<Flex
direction="column"
alignItems="center"
justifyContent="center"
gap="size-100"
>
<File />
{filledSrc}
</Flex>
)
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and drop here
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
import {Flex} from '@adobe/react-spectrum';
import File from '@spectrum-icons/illustrations/File';
function Example() {
let [
filledSrc,
setFilledSrc
] = React.useState(
null
);
return (
<>
<Draggable />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
onDrop={async (
e
) => {
e.items.find(
async (
item
) => {
if (
item
.kind ===
'file'
) {
setFilledSrc(
item
.name
);
} else if (
item
.kind ===
'text' &&
item
.types
.has(
'text/plain'
)
) {
setFilledSrc(
await item
.getText(
'text/plain'
)
);
}
}
);
}}
>
{filledSrc
? (
<Flex
direction="column"
alignItems="center"
justifyContent="center"
gap="size-100"
>
<File />
{filledSrc}
</Flex>
)
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and
drop here
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
Props#
Name | Type | Description |
children | ReactNode | The content to display in the drop zone. |
isFilled | boolean | Whether the drop zone has been filled. |
replaceMessage | string | The message to replace the default banner message that is shown when the drop zone is filled. |
getDropOperation | (
(types: DragTypes,
, allowedOperations: DropOperation[]
)) => DropOperation | A function returning the drop operation to be performed when items matching the given types are dropped on the drop target. |
Events
Name | Type | Description |
onDropEnter | (
(e: DropEnterEvent
)) => void | Handler that is called when a valid drag enters the drop target. |
onDropMove | (
(e: DropMoveEvent
)) => void | Handler that is called when a valid drag is moved within the drop target. |
onDropExit | (
(e: DropExitEvent
)) => void | Handler that is called when a valid drag exits the drop target. |
onDrop | (
(e: DropEvent
)) => void | Handler that is called when a valid drag is dropped on the drop target. |
Layout
Name | Type | Description |
flex | Responsive<string
| number
| boolean> | When used in a flex layout, specifies how the element will grow or shrink to fit the space available. See MDN. |
flexGrow | Responsive<number> | When used in a flex layout, specifies how the element will grow to fit the space available. See MDN. |
flexShrink | Responsive<number> | When used in a flex layout, specifies how the element will shrink to fit the space available. See MDN. |
flexBasis | Responsive<number | string> | When used in a flex layout, specifies the initial main size of the element. See MDN. |
alignSelf | Responsive<'auto'
| 'normal'
| 'start'
| 'end'
| 'center'
| 'flex-start'
| 'flex-end'
| 'self-start'
| 'self-end'
| 'stretch'> | Overrides the alignItems property of a flex or grid container. See MDN. |
justifySelf | Responsive<'auto'
| 'normal'
| 'start'
| 'end'
| 'flex-start'
| 'flex-end'
| 'self-start'
| 'self-end'
| 'center'
| 'left'
| 'right'
| 'stretch'> | Specifies how the element is justified inside a flex or grid container. See MDN. |
order | Responsive<number> | The layout order for the element within a flex or grid container. See MDN. |
gridArea | Responsive<string> | When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. See MDN. |
gridColumn | Responsive<string> | When used in a grid layout, specifies the column the element should be placed in within the grid. See MDN. |
gridRow | Responsive<string> | When used in a grid layout, specifies the row the element should be placed in within the grid. See MDN. |
gridColumnStart | Responsive<string> | When used in a grid layout, specifies the starting column to span within the grid. See MDN. |
gridColumnEnd | Responsive<string> | When used in a grid layout, specifies the ending column to span within the grid. See MDN. |
gridRowStart | Responsive<string> | When used in a grid layout, specifies the starting row to span within the grid. See MDN. |
gridRowEnd | Responsive<string> | When used in a grid layout, specifies the ending row to span within the grid. See MDN. |
slot | string | null | A slot name for the component. Slots allow the component to receive props from a parent component.
An explicit |
Spacing
Name | Type | Description |
margin | Responsive<DimensionValue> | The margin for all four sides of the element. See MDN. |
marginTop | Responsive<DimensionValue> | The margin for the top side of the element. See MDN. |
marginBottom | Responsive<DimensionValue> | The margin for the bottom side of the element. See MDN. |
marginStart | Responsive<DimensionValue> | The margin for the logical start side of the element, depending on layout direction. See MDN. |
marginEnd | Responsive<DimensionValue> | The margin for the logical end side of an element, depending on layout direction. See MDN. |
marginX | Responsive<DimensionValue> | The margin for both the left and right sides of the element. See MDN. |
marginY | Responsive<DimensionValue> | The margin for both the top and bottom sides of the element. See MDN. |
Sizing
Name | Type | Description |
width | Responsive<DimensionValue> | The width of the element. See MDN. |
minWidth | Responsive<DimensionValue> | The minimum width of the element. See MDN. |
maxWidth | Responsive<DimensionValue> | The maximum width of the element. See MDN. |
height | Responsive<DimensionValue> | The height of the element. See MDN. |
minHeight | Responsive<DimensionValue> | The minimum height of the element. See MDN. |
maxHeight | Responsive<DimensionValue> | The maximum height of the element. See MDN. |
Positioning
Name | Type | Description |
position | Responsive<'static'
| 'relative'
| 'absolute'
| 'fixed'
| 'sticky'> | Specifies how the element is positioned. See MDN. |
top | Responsive<DimensionValue> | The top position for the element. See MDN. |
bottom | Responsive<DimensionValue> | The bottom position for the element. See MDN. |
left | Responsive<DimensionValue> | The left position for the element. See MDN. Consider using start instead for RTL support. |
right | Responsive<DimensionValue> | The right position for the element. See MDN. Consider using start instead for RTL support. |
start | Responsive<DimensionValue> | The logical start position for the element, depending on layout direction. See MDN. |
end | Responsive<DimensionValue> | The logical end position for the element, depending on layout direction. See MDN. |
zIndex | Responsive<number> | The stacking order for the element. See MDN. |
isHidden | Responsive<boolean> | Hides the element. |
Accessibility
Name | Type | Description |
id | string | The element's unique identifier. See MDN. |
aria-label | string | Defines a string value that labels the current element. |
aria-labelledby | string | Identifies the element (or elements) that labels the current element. |
aria-describedby | string | Identifies the element (or elements) that describes the object. |
aria-details | string | Identifies the element (or elements) that provide a detailed, extended description for the object. |
Advanced
Name | Type | Description |
UNSAFE_className | string | Sets the CSS className for the element. Only use as a last resort. Use style props instead. |
UNSAFE_style | CSSProperties | Sets inline style for the element. Only use as a last resort. Use style props instead. |
Visual options#
Filled state#
The user is responsible for both managing the filled state of a DropZone and handling the associated styling. To set the DropZone to a filled state, the user must pass the isFilled
prop.
The example below demonstrates one way of styling the filled state.
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<DraggableImage />
<DropZone
isFilled={!!filledSrc}
maxWidth="size-3000"
height="size-2400"
getDropOperation={(types) =>
(types.has('image/png') || types.has('image/jpeg'))
? 'copy'
: 'cancel'}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
if (item.type === 'image/jpeg' || item.type === 'image/png') {
setFilledSrc(URL.createObjectURL(await item.getFile()));
}
} else if (item.kind === 'text') {
setFilledSrc(await item.getText('image/jpeg'));
}
});
}}
>
{filledSrc
? <img className="images" alt="" src={filledSrc} />
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and drop photos
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<DraggableImage />
<DropZone
isFilled={!!filledSrc}
maxWidth="size-3000"
height="size-2400"
getDropOperation={(types) =>
(types.has('image/png') ||
types.has('image/jpeg'))
? 'copy'
: 'cancel'}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
if (
item.type === 'image/jpeg' ||
item.type === 'image/png'
) {
setFilledSrc(
URL.createObjectURL(await item.getFile())
);
}
} else if (item.kind === 'text') {
setFilledSrc(
await item.getText('image/jpeg')
);
}
});
}}
>
{filledSrc
? (
<img
className="images"
alt=""
src={filledSrc}
/>
)
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and drop photos
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
function Example() {
let [
filledSrc,
setFilledSrc
] = React.useState(
null
);
return (
<>
<DraggableImage />
<DropZone
isFilled={!!filledSrc}
maxWidth="size-3000"
height="size-2400"
getDropOperation={(
types
) =>
(types.has(
'image/png'
) ||
types.has(
'image/jpeg'
))
? 'copy'
: 'cancel'}
onDrop={async (
e
) => {
e.items.find(
async (
item
) => {
if (
item
.kind ===
'file'
) {
if (
item
.type ===
'image/jpeg' ||
item
.type ===
'image/png'
) {
setFilledSrc(
URL
.createObjectURL(
await item
.getFile()
)
);
}
} else if (
item
.kind ===
'text'
) {
setFilledSrc(
await item
.getText(
'image/jpeg'
)
);
}
}
);
}}
>
{filledSrc
? (
<img
className="images"
alt=""
src={filledSrc}
/>
)
: (
<IllustratedMessage>
<Upload />
<Heading>
Drag and
drop
photos
</Heading>
</IllustratedMessage>
)}
</DropZone>
</>
);
}
Show CSS
.images {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--spectrum-alias-border-radius-small);
}
.images {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--spectrum-alias-border-radius-small);
}
.images {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--spectrum-alias-border-radius-small);
}
The DraggableImage
component used above is defined below. See useDrag for more details and documentation.
Show code
function DraggableImage() {
let {dragProps, isDragging} = useDrag({
getItems() {
return [
{
'image/jpeg': 'https://i.imgur.com/Z7AzH2c.jpg'
}
];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `} >
<img
width="150px"
height="100px"
alt="Traditional Roof"
src="https://i.imgur.com/Z7AzH2c.jpg"/>
</div>
);
}
function DraggableImage() {
let { dragProps, isDragging } = useDrag({
getItems() {
return [
{
'image/jpeg': 'https://i.imgur.com/Z7AzH2c.jpg'
}
];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `}
>
<img
width="150px"
height="100px"
alt="Traditional Roof"
src="https://i.imgur.com/Z7AzH2c.jpg"
/>
</div>
);
}
function DraggableImage() {
let {
dragProps,
isDragging
} = useDrag({
getItems() {
return [
{
'image/jpeg':
'https://i.imgur.com/Z7AzH2c.jpg'
}
];
}
});
return (
<div
{...dragProps}
role="button"
tabIndex={0}
className={`draggable `}
>
<img
width="150px"
height="100px"
alt="Traditional Roof"
src="https://i.imgur.com/Z7AzH2c.jpg"
/>
</div>
);
}
Replace message#
When a DropZone is in a filled state and has an object dragged over it, a message will appear in front of the DropZone. By default, this message will say "Drop file to replace". However, users can choose to customize this message through the replaceMessage
prop. This message should describe the interaction that will occur when the object is dropped. It should also be internationalized if needed.
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
isFilled={isFilled}
maxWidth="size-3000"
replaceMessage="This is a custom message"
onDrop={() => setIsFilled(true)}>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled ? 'You dropped something!' : 'Drag and drop here'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
);
}
function Example() {
let [isFilled, setIsFilled] = React.useState(false);
return (
<>
<Draggable />
<DropZone
isFilled={isFilled}
maxWidth="size-3000"
replaceMessage="This is a custom message"
onDrop={() => setIsFilled(true)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop here'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
);
}
function Example() {
let [
isFilled,
setIsFilled
] = React.useState(
false
);
return (
<>
<Draggable />
<DropZone
isFilled={isFilled}
maxWidth="size-3000"
replaceMessage="This is a custom message"
onDrop={() =>
setIsFilled(
true
)}
>
<IllustratedMessage>
<Upload />
<Heading>
{isFilled
? 'You dropped something!'
: 'Drag and drop here'}
</Heading>
</IllustratedMessage>
</DropZone>
</>
);
}
Visual feedback#
A DropZone displays visual feedback to the user when a drag hovers over the drop target by passing the getDropOperation
function. If a drop target only supports data of specific types (e.g. images, videos, text, etc.), then it should implement the getDropOperation
prop and return 'cancel'
for types that aren't supported. This will prevent visual feedback indicating that the drop target accepts the dragged data when this is not true. Read more about getDropOperation.
In the below example, the drop zone only supports dropping JPEG images. If a JPEG is dragged over the drop zone, it will be highlighted and the operating system will display a copy cursor. If another type is dragged over the drop zone, then there is no visual feedback, indicating that a drop is not accepted.
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<Draggable />
<DraggableImage />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
getDropOperation={(types) =>
types.has('image/jpeg') ? 'copy' : 'cancel'}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
if (item.type === 'image/jpeg') {
let file = await item.getFile();
setFilledSrc({
type: file.type,
name: file.name
});
}
} else if (item.kind === 'text') {
let file = await item.getText('image/jpeg');
setFilledSrc({
type: 'image/jpeg',
name: file
});
}
});
}}
>
<IllustratedMessage>
<Upload />
<Heading>
{filledSrc
? `
: `'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
acceptedFileTypes={['image/jpeg']}
onSelect={(e) => {
let file = (Array.from(e)).find((file) =>
file.type === 'image/jpeg'
);
if (file) {
setFilledSrc({
type: file.type,
name: file.name
});
}
}}
>
<Button variant="primary">Browse</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
);
}
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [filledSrc, setFilledSrc] = React.useState(null);
return (
<>
<Draggable />
<DraggableImage />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
getDropOperation={(types) =>
types.has('image/jpeg') ? 'copy' : 'cancel'}
onDrop={async (e) => {
e.items.find(async (item) => {
if (item.kind === 'file') {
if (item.type === 'image/jpeg') {
let file = await item.getFile();
setFilledSrc({
type: file.type,
name: file.name
});
}
} else if (item.kind === 'text') {
let file = await item.getText('image/jpeg');
setFilledSrc({
type: 'image/jpeg',
name: file
});
}
});
}}
>
<IllustratedMessage>
<Upload />
<Heading>
{filledSrc
? `
: `'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
acceptedFileTypes={['image/jpeg']}
onSelect={(e) => {
let file = (Array.from(e)).find((file) =>
file.type === 'image/jpeg'
);
if (file) {
setFilledSrc({
type: file.type,
name: file.name
});
}
}}
>
<Button variant="primary">Browse</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
);
}
import {FileTrigger} from '@adobe/react-spectrum';
function Example() {
let [
filledSrc,
setFilledSrc
] = React.useState(
null
);
return (
<>
<Draggable />
<DraggableImage />
<DropZone
maxWidth="size-3000"
isFilled={!!filledSrc}
getDropOperation={(
types
) =>
types.has(
'image/jpeg'
)
? 'copy'
: 'cancel'}
onDrop={async (
e
) => {
e.items.find(
async (
item
) => {
if (
item
.kind ===
'file'
) {
if (
item
.type ===
'image/jpeg'
) {
let file =
await item
.getFile();
setFilledSrc(
{
type:
file
.type,
name:
file
.name
}
);
}
} else if (
item
.kind ===
'text'
) {
let file =
await item
.getText(
'image/jpeg'
);
setFilledSrc(
{
type:
'image/jpeg',
name:
file
}
);
}
}
);
}}
>
<IllustratedMessage>
<Upload />
<Heading>
{filledSrc
? `
: `'Drag and drop here'}
</Heading>
<Content>
<FileTrigger
acceptedFileTypes={[
'image/jpeg'
]}
onSelect={(
e
) => {
let file =
(Array
.from(
e
)).find(
(
file
) =>
file
.type ===
'image/jpeg'
);
if (
file
) {
setFilledSrc(
{
type:
file
.type,
name:
file
.name
}
);
}
}}
>
<Button variant="primary">
Browse
</Button>
</FileTrigger>
</Content>
</IllustratedMessage>
</DropZone>
</>
);
}