Skip to content

Commit 0f74a09

Browse files
committed
FileUploadInput: abandon middleware, render children, update examples
1 parent e730629 commit 0f74a09

File tree

18 files changed

+335
-207
lines changed

18 files changed

+335
-207
lines changed

src/examples/src/config.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ import ActionsDialog from './widgets/dialog/ActionsDialog';
6161
import BasicEmailInput from './widgets/email-input/Basic';
6262
import BasicFileUploadInput from './widgets/file-upload-input/Basic';
6363
import DisabledFileUploadInput from './widgets/file-upload-input/Disabled';
64-
import LabelledFileUploadInput from './widgets/file-upload-input/Labels';
64+
import LabelledFileUploadInput from './widgets/file-upload-input/Labelled';
6565
import MultipleFileUploadInput from './widgets/file-upload-input/Multiple';
66+
import NoDropFileUploadInput from './widgets/file-upload-input/NoDrop';
6667
import BasicFileUploader from './widgets/file-uploader/Basic';
6768
import DisabledFileUploader from './widgets/file-uploader/Disabled';
6869
import MultipleFileUploader from './widgets/file-uploader/Multiple';
@@ -723,12 +724,19 @@ export const config = {
723724
{
724725
title: 'Multiple FileUploadInput',
725726
filename: 'Multiple',
726-
module: MultipleFileUploadInput
727+
module: MultipleFileUploadInput,
728+
description:
729+
'Demonstrates using child `content` property to render information about the uploaded files that is available to the `onValue` callback.'
727730
},
728731
{
729-
title: 'FileUploadInput with custom labels',
730-
filename: 'Labels',
732+
title: 'FileUploadInput with label',
733+
filename: 'Labelled',
731734
module: LabelledFileUploadInput
735+
},
736+
{
737+
title: 'FileUploadInput with no DnD',
738+
filename: 'NoDrop',
739+
module: NoDropFileUploadInput
732740
}
733741
]
734742
},
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { create, tsx } from '@dojo/framework/core/vdom';
2+
import icache from '@dojo/framework/core/middleware/icache';
3+
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
4+
import Example from '../../Example';
5+
6+
const factory = create({ icache });
7+
8+
export default factory(function Labelled({ middleware: { icache } }) {
9+
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);
10+
11+
function onValue(files: File[]) {
12+
icache.set('selectedFiles', files);
13+
}
14+
15+
return (
16+
<Example>
17+
<FileUploadInput accept="image/*" onValue={onValue}>
18+
{{
19+
label: 'Upload a profile image'
20+
}}
21+
</FileUploadInput>
22+
<div>Selected file: {selectedFiles.length ? selectedFiles[0].name : 'none'}</div>
23+
</Example>
24+
);
25+
});

src/examples/src/widgets/file-upload-input/Labels.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { create, tsx } from '@dojo/framework/core/vdom';
2+
import icache from '@dojo/framework/core/middleware/icache';
23
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
34
import Example from '../../Example';
4-
import icache from '@dojo/framework/core/middleware/icache';
5+
6+
import * as css from './multiple.m.css';
57

68
const factory = create({ icache });
79

@@ -14,29 +16,34 @@ export default factory(function Multiple({ middleware: { icache } }) {
1416

1517
return (
1618
<Example>
17-
<FileUploadInput onValue={onValue} multiple />
18-
{selectedFiles.length > 0 && (
19-
<table>
20-
<thead>
21-
<th>Name</th>
22-
<th>Modified</th>
23-
<th>Type</th>
24-
<th>Bytes</th>
25-
</thead>
26-
<tbody>
27-
{selectedFiles.map(function(file) {
28-
return (
29-
<tr key={file.name}>
30-
<td>{file.name}</td>
31-
<td>{new Date(file.lastModified).toLocaleString()}</td>
32-
<td>{file.type}</td>
33-
<td>{String(file.size)}</td>
34-
</tr>
35-
);
36-
})}
37-
</tbody>
38-
</table>
39-
)}
19+
<FileUploadInput onValue={onValue} multiple>
20+
{{
21+
content: selectedFiles.length ? (
22+
<table classes={[css.table]}>
23+
<thead>
24+
<th>Name</th>
25+
<th>Modified</th>
26+
<th>Type</th>
27+
<th>Bytes</th>
28+
</thead>
29+
<tbody>
30+
{selectedFiles.map(function(file) {
31+
return (
32+
<tr key={file.name}>
33+
<td>{file.name}</td>
34+
<td>{new Date(file.lastModified).toLocaleString()}</td>
35+
<td>{file.type}</td>
36+
<td>{String(file.size)}</td>
37+
</tr>
38+
);
39+
})}
40+
</tbody>
41+
</table>
42+
) : (
43+
''
44+
)
45+
}}
46+
</FileUploadInput>
4047
</Example>
4148
);
4249
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { create, tsx } from '@dojo/framework/core/vdom';
2+
import icache from '@dojo/framework/core/middleware/icache';
3+
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
4+
import Example from '../../Example';
5+
6+
const factory = create({ icache });
7+
8+
export default factory(function NoDrop({ middleware: { icache } }) {
9+
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);
10+
11+
function onValue(files: File[]) {
12+
icache.set('selectedFiles', files);
13+
}
14+
15+
return (
16+
<Example>
17+
<FileUploadInput allowDnd={false} onValue={onValue} />
18+
<div>Selected file: {selectedFiles.length ? selectedFiles[0].name : 'none'}</div>
19+
</Example>
20+
);
21+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.table {
2+
width: 100%;
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const table: string;

src/file-upload-input/README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
# @dojo/widgets/file-upload-input
22

3-
Dojo's `FileUploadInput` provides an interface for managing file uploads using `<input type="file">`. This is a
4-
controlled component that only provides file selection. The `FileUploader` widget provides more full-featured file
5-
upload functionality. If you require more customization than `FileUploader` provides you can build a custom file
6-
uploader widget based on `FileUploadInput`. You can provide a callback function to the `onValue` property to receive
7-
a `File` array whenever files are selected.
3+
Dojo's `FileUploadInput` provides an interface for managing file uploads supporting both `<input type="file">` and the
4+
HTML Drag and Drop API. This is a controlled widget that only provides file selection. The `FileUploader` widget
5+
provides more full-featured file upload functionality. If you require more customization than `FileUploader` provides
6+
you can build a custom file uploader widget based on `FileUploadInput`. You can provide a callback function to the
7+
`onValue` property to receive a `File` array whenever files are selected.
88

99
## Features
1010

1111
- Single or multiple file upload
1212
- Add files from OS-provided file selection dialog
1313
- Add files with drag and drop
14-
- Validation
1514

1615
### Keyboard features
1716

1817
- Trigger file selection dialog with keyboard
1918

2019
### i18n features
2120

22-
- Localized version of default labels for the button and DnD can be provided in nls resources
21+
- Localized version of labels for the button and DnD can be provided in nls resources

src/file-upload-input/design.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# DOM structure
2+
3+
The only way to launch the operating system's file selection dialog is with the native `<input type="file">` element.
4+
This element renders a button in the UI which is not very customizable. A hidden input can still be used to open the
5+
dialog but you have to call its `click` method.
6+
7+
The overlay `<div>` provides a visual indicator that a drag operation is in progress.
8+
9+
# Drag and Drop
10+
11+
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
12+
13+
DOM events are used directly because writing reactive middleware for this use-case ends up either very specific and not
14+
widely useful, or if attempts are made to provide a general-purpose API the logic becomes very convoluted. Dealing with
15+
a conditionally rendered overlay becomes a hassle as well.
16+
17+
The `dragenter` event must be listened for to detect when a drag enters the target area. At this point the overlay
18+
should be rendered and it must have no children and must be the highest layer. If `dragenter` and `dragleave` event
19+
listeners are added to an element with visible children then spurious enter/leave events are constantly triggered as
20+
the cursor moves over children (even letters in text).
21+
22+
- `dragenter`: listened for on the root element at all times that DnD is allowed
23+
- makes the overlay visible to indicate DnD is active
24+
- `dragover`: this event must be listened for, but nothing needs to be done with it other than `event.preventDefault()`
25+
(the default action for DragEvents is to cancel the drag operation)
26+
- is listened for on the root since it bubbles from the overlay
27+
- `dragleave`: listened for on the overlay since it is unreliable on the root element
28+
- when this event fires the overlay is hidden
29+
- `drop`: listened for on the root since it bubbles from the overlay
30+
- get the files and update to indicate DnD is no longer active

0 commit comments

Comments
 (0)