Skip to content

Commit 8564e5f

Browse files
authored
feat: support paste upload file (#543)
* feat: support paste upload file * feat: allowPasteUpload * docs: update demo * feat: rename `allowPasteUpload` to `pastable` * test: optimize
1 parent b8a0ccd commit 8564e5f

File tree

8 files changed

+282
-30
lines changed

8 files changed

+282
-30
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ React.render(<Upload />, container);
8484
|customRequest | function | null | provide an override for the default xhr behavior for additional customization|
8585
|withCredentials | boolean | false | ajax upload with cookie send |
8686
|openFileDialogOnClick | boolean | true | useful for drag only upload as it does not trigger on enter key or click event |
87+
|pastable | boolean | false | support paste upload |
8788

8889
#### onError arguments
8990

docs/demo/paste.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: paste
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/paste.tsx"/></code>

docs/demo/pasteDirectory.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: pasteDirectory
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/pasteDirectory.tsx"/></code>

docs/examples/paste.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* eslint no-console:0 */
2+
import React from 'react';
3+
import Upload from 'rc-upload';
4+
5+
const props = {
6+
action: '/upload.do',
7+
type: 'drag',
8+
accept: '.png',
9+
pastable: true,
10+
beforeUpload(file) {
11+
console.log('beforeUpload', file.name);
12+
},
13+
onStart: file => {
14+
console.log('onStart', file.name);
15+
},
16+
onSuccess(file) {
17+
console.log('onSuccess', file);
18+
},
19+
onProgress(step, file) {
20+
console.log('onProgress', Math.round(step.percent), file.name);
21+
},
22+
onError(err) {
23+
console.log('onError', err);
24+
},
25+
style: { display: 'inline-block', width: 200, height: 200, background: '#eee' },
26+
};
27+
28+
const Test = () => {
29+
return (
30+
<div
31+
style={{
32+
margin: 100,
33+
}}
34+
>
35+
<div>
36+
<Upload {...props}>
37+
<a>开始上传</a>
38+
</Upload>
39+
</div>
40+
</div>
41+
);
42+
};
43+
44+
export default Test;

docs/examples/pasteDirectory.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint no-console:0 */
2+
import React from 'react';
3+
import Upload from 'rc-upload';
4+
5+
const props = {
6+
action: '/upload.do',
7+
type: 'drag',
8+
accept: '.png',
9+
directory: true,
10+
pastable: true,
11+
beforeUpload(file) {
12+
console.log('beforeUpload', file.name);
13+
},
14+
onStart: file => {
15+
console.log('onStart', file.name);
16+
},
17+
onSuccess(file) {
18+
console.log('onSuccess', file);
19+
},
20+
onProgress(step, file) {
21+
console.log('onProgress', Math.round(step.percent), file.name);
22+
},
23+
onError(err) {
24+
console.log('onError', err);
25+
},
26+
style: { display: 'inline-block', width: 200, height: 200, background: '#eee' },
27+
};
28+
29+
const Test = () => {
30+
return (
31+
<div
32+
style={{
33+
margin: 100,
34+
}}
35+
>
36+
<div>
37+
<Upload {...props}>
38+
<a>开始上传</a>
39+
</Upload>
40+
</div>
41+
</div>
42+
);
43+
};
44+
45+
export default Test;

src/AjaxUploader.tsx

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,41 +66,75 @@ class AjaxUploader extends Component<UploadProps> {
6666
}
6767
};
6868

69-
onFileDrop = async (e: React.DragEvent<HTMLDivElement>) => {
70-
const { multiple } = this.props;
71-
69+
onFileDropOrPaste = async (e: React.DragEvent<HTMLDivElement> | ClipboardEvent) => {
7270
e.preventDefault();
7371

7472
if (e.type === 'dragover') {
7573
return;
7674
}
7775

78-
if (this.props.directory) {
79-
const files = await traverseFileTree(
80-
Array.prototype.slice.call(e.dataTransfer.items),
81-
(_file: RcFile) => attrAccept(_file, this.props.accept),
76+
const { multiple, accept, directory } = this.props;
77+
let items: DataTransferItem[] = [];
78+
let files: File[] = [];
79+
80+
if (e.type === 'drop') {
81+
const dataTransfer = (e as React.DragEvent<HTMLDivElement>).dataTransfer;
82+
items = [...(dataTransfer.items || [])];
83+
files = [...(dataTransfer.files || [])];
84+
} else if (e.type === 'paste') {
85+
const clipboardData = (e as ClipboardEvent).clipboardData;
86+
items = [...(clipboardData.items || [])];
87+
files = [...(clipboardData.files || [])];
88+
}
89+
90+
if (directory) {
91+
files = await traverseFileTree(Array.prototype.slice.call(items), (_file: RcFile) =>
92+
attrAccept(_file, this.props.accept),
8293
);
8394
this.uploadFiles(files);
8495
} else {
85-
let files = [...e.dataTransfer.files].filter((file: RcFile) =>
86-
attrAccept(file, this.props.accept),
87-
);
96+
let acceptFiles = [...files].filter((file: RcFile) => attrAccept(file, accept));
8897

8998
if (multiple === false) {
90-
files = files.slice(0, 1);
99+
acceptFiles = files.slice(0, 1);
91100
}
92101

93-
this.uploadFiles(files);
102+
this.uploadFiles(acceptFiles);
103+
}
104+
};
105+
106+
onPrePaste = (e: ClipboardEvent) => {
107+
const { pastable } = this.props;
108+
109+
if (pastable) {
110+
this.onFileDropOrPaste(e);
94111
}
95112
};
96113

97114
componentDidMount() {
98115
this._isMounted = true;
116+
117+
const { pastable } = this.props;
118+
119+
if (pastable) {
120+
document.addEventListener('paste', this.onPrePaste);
121+
}
99122
}
100123

101124
componentWillUnmount() {
102125
this._isMounted = false;
103126
this.abort();
127+
document.removeEventListener('paste', this.onPrePaste);
128+
}
129+
130+
componentDidUpdate(prevProps: UploadProps) {
131+
const { pastable } = this.props;
132+
133+
if (pastable && !prevProps.pastable) {
134+
document.addEventListener('paste', this.onPrePaste);
135+
} else if (!pastable && prevProps.pastable) {
136+
document.removeEventListener('paste', this.onPrePaste);
137+
}
104138
}
105139

106140
uploadFiles = (files: File[]) => {
@@ -299,8 +333,8 @@ class AjaxUploader extends Component<UploadProps> {
299333
onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {},
300334
onMouseEnter,
301335
onMouseLeave,
302-
onDrop: this.onFileDrop,
303-
onDragOver: this.onFileDrop,
336+
onDrop: this.onFileDropOrPaste,
337+
onDragOver: this.onFileDropOrPaste,
304338
tabIndex: hasControlInside ? undefined : '0',
305339
};
306340
return (

src/interface.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface UploadProps
4444
input?: React.CSSProperties;
4545
};
4646
hasControlInside?: boolean;
47+
pastable?: boolean;
4748
}
4849

4950
export interface UploadProgressEvent extends Partial<ProgressEvent> {

0 commit comments

Comments
 (0)