Skip to content

Commit eb43d6e

Browse files
authored
RSP DropZone docs (#5076)
* RSP DropZone docs * dropzone docs and updated dropzone svg * remove unsed imports * add spacing * update examples * fix links * add anatomy diagram, typo fixes, updates to some examples * fix typecheck, add info about internationalization * elaborate more on internationalization * fix spacing, update image links * remove unsafe styling * remove anatomy diagram
1 parent f692175 commit eb43d6e

File tree

6 files changed

+665
-9
lines changed

6 files changed

+665
-9
lines changed

Diff for: packages/@react-spectrum/dropzone/docs/DropZone.mdx

+361
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
{/* Copyright 2023 Adobe. All rights reserved.
2+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License. You may obtain a copy
4+
of the License at http://www.apache.org/licenses/LICENSE-2.0
5+
Unless required by applicable law or agreed to in writing, software distributed under
6+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
7+
OF ANY KIND, either express or implied. See the License for the specific language
8+
governing permissions and limitations under the License. */}
9+
10+
import {Layout} from '@react-spectrum/docs';
11+
export default Layout;
12+
13+
import docs from 'docs:@react-spectrum/dropzone';
14+
import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs';
15+
import packageData from '@react-spectrum/dropzone/package.json';
16+
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
17+
import styles from '@react-spectrum/docs/src/docs.css';
18+
19+
```jsx import
20+
import {DropZone} from '@react-spectrum/dropzone';
21+
import {Heading} from '@react-spectrum/text';
22+
import {Content} from '@react-spectrum/view';
23+
import {IllustratedMessage} from '@react-spectrum/illustratedmessage';
24+
import {Text} from 'react-aria-components';
25+
import {FileTrigger} from 'react-aria-components';
26+
import {Button} from '@react-spectrum/button';
27+
```
28+
29+
---
30+
category: Drag and drop
31+
---
32+
33+
# DropZone
34+
35+
<PageDescription>{docs.exports.DropZone.description}</PageDescription>
36+
37+
<HeaderInfo
38+
packageData={packageData}
39+
componentNames={['DropZone']}
40+
/>
41+
42+
## Example
43+
44+
```tsx example
45+
import Upload from '@spectrum-icons/illustrations/Upload';
46+
47+
<DropZone
48+
maxWidth="size-3000">
49+
<IllustratedMessage>
50+
<Upload />
51+
<Heading>
52+
<Text slot="label">
53+
Drag and drop your file
54+
</Text>
55+
</Heading>
56+
</IllustratedMessage>
57+
</DropZone>
58+
```
59+
60+
61+
## Content
62+
63+
A drop zone accepts an [IllustratedMessage](IllustratedMessage.html) 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](Heading.html) (title), and a [Content](Content.html) (body). A [FileTrigger](../react-aria/FileTrigger.html) is commonly paired with a DropZone to allow a user to choose files from their device.
64+
65+
```tsx example
66+
<DropZone
67+
maxWidth="size-3000">
68+
<IllustratedMessage>
69+
<Upload />
70+
<Heading>
71+
<Text slot="label">
72+
Drag and drop here
73+
</Text>
74+
</Heading>
75+
<Content>
76+
<FileTrigger>
77+
<Button variant="primary">Browse</Button>
78+
</FileTrigger>
79+
</Content>
80+
</IllustratedMessage>
81+
</DropZone>
82+
```
83+
84+
### Accessibility
85+
86+
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.
87+
88+
### Internationalization
89+
90+
In order to internationalize a drop zone, 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.
91+
92+
## Events
93+
94+
`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.
95+
96+
The following example uses an `onDrop` handler to update the filled status stored in React state.
97+
98+
```tsx example
99+
import File from '@spectrum-icons/illustrations/File';
100+
101+
function Example() {
102+
let [isFilled, setIsFilled] = React.useState(false);
103+
let [filledSrc, setFilledSrc] = React.useState(null);
104+
105+
return (
106+
<>
107+
<Draggable />
108+
<DropZone
109+
maxWidth="size-3000"
110+
isFilled={isFilled}
111+
onDrop={async (e) => {
112+
e.items.find(async (item) => {
113+
if (item.kind === 'file') {
114+
setFilledSrc(item.name);
115+
setIsFilled(true);
116+
117+
} else if (item.kind === 'text' && item.types.has('text/plain')) {
118+
setFilledSrc(await item.getText('text/plain'));
119+
setIsFilled(true);
120+
}
121+
});
122+
}}>
123+
<IllustratedMessage>
124+
<Upload />
125+
<Heading>
126+
<Text slot="label">
127+
Drag and drop here
128+
</Text>
129+
</Heading>
130+
</IllustratedMessage>
131+
</DropZone>
132+
{isFilled &&
133+
<div className="files">
134+
<File />
135+
{filledSrc}
136+
</div>}
137+
</>
138+
);
139+
}
140+
```
141+
142+
The `Draggable` component used above is defined below. See [useDrag](../react-aria/useDrag.html) for more details and documentation.
143+
144+
<details>
145+
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>
146+
147+
```tsx example render=false export=true
148+
import {useDrag} from '@react-aria/dnd';
149+
150+
function Draggable() {
151+
let {dragProps, isDragging} = useDrag({
152+
getItems() {
153+
return [{
154+
'text/plain': 'hello world',
155+
'my-app-custom-type': JSON.stringify({message: 'hello world'})
156+
}];
157+
}
158+
});
159+
160+
return (
161+
<div {...dragProps} role="button" tabIndex={0} className={`draggable ${isDragging ? 'dragging' : ''}`}>
162+
Drag me
163+
</div>
164+
);
165+
}
166+
```
167+
</details>
168+
169+
<details>
170+
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>
171+
172+
```css
173+
.draggable {
174+
display: inline-block;
175+
vertical-align: top;
176+
border: 1px solid gray;
177+
padding: 10px;
178+
margin-right: 20px;
179+
margin-bottom: 20px;
180+
border-radius: 4px;
181+
height: fit-content;
182+
}
183+
184+
.draggable.dragging {
185+
opacity: 0.5;
186+
}
187+
188+
.files{
189+
margin-top: 20px;
190+
}
191+
```
192+
193+
</details>
194+
195+
196+
## Props
197+
198+
<PropTable component={docs.exports.DropZone} links={docs.links} />
199+
200+
## Visual options
201+
202+
### Filled state
203+
204+
The user is responsible for both managing the filled state of a drop zone and handling the associated styling. To set the drop zone to a filled state, the user must pass the `isFilled` prop.
205+
206+
The example below demonstrates one way of styling the filled state.
207+
208+
```tsx example
209+
function Example() {
210+
let [filledSrc, setFilledSrc] = React.useState(null);
211+
let [isFilled, setIsFilled] = React.useState(false);
212+
213+
return (
214+
<>
215+
<DraggableImage />
216+
<DropZone
217+
isFilled={isFilled}
218+
maxWidth="size-3000"
219+
height="size-2400"
220+
getDropOperation={(types) => (types.has('image/png') || types.has('image/jpeg')) ? 'copy' : 'cancel'}
221+
onDrop={async (e) => {
222+
e.items.find(async (item) => {
223+
if (item.kind === 'file') {
224+
if (item.type === 'image/jpeg' || item.type === 'image/png') {
225+
setFilledSrc(URL.createObjectURL(await item.getFile()));
226+
setIsFilled(true);
227+
}
228+
} else if (item.kind === 'text') {
229+
setFilledSrc(await item.getText('image/jpeg'));
230+
setIsFilled(true);
231+
}
232+
});
233+
}}>
234+
<IllustratedMessage>
235+
<Upload />
236+
<Heading>
237+
<Text slot="label">
238+
Drag and drop photos
239+
</Text>
240+
</Heading>
241+
</IllustratedMessage>
242+
{isFilled && <img className={'images'} alt="" src={filledSrc} />}
243+
</DropZone>
244+
</>
245+
);
246+
}
247+
```
248+
249+
<details>
250+
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>
251+
252+
```css
253+
.images {
254+
position: absolute;
255+
top: 0px;
256+
left: 0px;
257+
width: 100%;
258+
height: 100%;
259+
object-fit: cover;
260+
border-radius: var(--spectrum-alias-border-radius-small);
261+
}
262+
```
263+
</details>
264+
265+
The `DraggableImage` component used above is defined below. See [useDrag](../react-aria/useDrag.html) for more details and documentation.
266+
267+
<details>
268+
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>
269+
270+
```tsx example render=false export=true
271+
function DraggableImage() {
272+
let {dragProps, isDragging} = useDrag({
273+
getItems() {
274+
return [
275+
{
276+
'image/jpeg': 'https://i.imgur.com/Z7AzH2c.jpg'
277+
}
278+
];
279+
}
280+
});
281+
282+
return (
283+
<div
284+
{...dragProps}
285+
role="button"
286+
tabIndex={0}
287+
className={`draggable ${isDragging ? 'dragging' : ''}`} >
288+
<img
289+
width="150px"
290+
height="100px"
291+
alt="Traditional Roof"
292+
src="https://i.imgur.com/Z7AzH2c.jpg"/>
293+
</div>
294+
);
295+
}
296+
```
297+
</details>
298+
299+
### Replace message
300+
301+
When a drop zone is in a filled state and has an object dragged over it, a message will appear in front of the drop zone. 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.
302+
303+
304+
```tsx example
305+
function Example() {
306+
let [isFilled, setIsFilled] = React.useState(false);
307+
308+
return (
309+
<>
310+
<Draggable />
311+
<DropZone
312+
isFilled={isFilled}
313+
maxWidth="size-3000"
314+
replaceMessage="This is a custom message"
315+
onDrop={() => setIsFilled(true)}>
316+
<IllustratedMessage>
317+
<Upload />
318+
<Heading>
319+
<Text slot="label">
320+
Drag and drop here
321+
</Text>
322+
</Heading>
323+
</IllustratedMessage>
324+
</DropZone>
325+
</>
326+
);
327+
}
328+
```
329+
330+
### Visual feedback
331+
332+
A drop zone 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.](../react-aria/useDrop.html#getdropoperation)
333+
334+
```tsx example
335+
336+
function Example() {
337+
let [isFilled, setIsFilled] = React.useState(false);
338+
339+
return (
340+
<DropZone
341+
maxWidth="size-3000"
342+
isFilled={isFilled}
343+
getDropOperation={(types) => types.has('image/png') ? 'copy' : 'cancel'}
344+
onDrop={() => setIsFilled(true)}>
345+
<IllustratedMessage>
346+
<Upload />
347+
<Heading>
348+
<Text slot="label">
349+
Drag and drop here
350+
</Text>
351+
</Heading>
352+
<Content>
353+
<FileTrigger>
354+
<Button variant="primary">Browse</Button>
355+
</FileTrigger>
356+
</Content>
357+
</IllustratedMessage>
358+
</DropZone>
359+
);
360+
}
361+
```

Diff for: packages/@react-spectrum/dropzone/src/DropZone.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {useLocalizedStringFormatter} from '@react-aria/i18n';
2323
export interface SpectrumDropZoneProps extends DropZoneProps, DOMProps, StyleProps, AriaLabelingProps {
2424
/** The content to display in the drop zone. */
2525
children: ReactNode,
26-
/** Whether the dropzone has been filled. */
26+
/** Whether the drop zone has been filled. */
2727
isFilled?: boolean,
2828
/** The message to replace the default banner message that is shown when the drop zone is filled. */
2929
replaceMessage?: string
@@ -75,7 +75,7 @@ function DropZone(props: SpectrumDropZoneProps, ref: DOMRef<HTMLDivElement>) {
7575
}
7676

7777
/**
78-
* A dropzone is an area into which one or multiple objects can be dragged and dropped.
78+
* A drop zone is an area into which one or multiple objects can be dragged and dropped.
7979
*/
8080
let _DropZone = React.forwardRef(DropZone);
8181
export {_DropZone as DropZone};

0 commit comments

Comments
 (0)