-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
98 lines (73 loc) · 2.89 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import _ from 'lodash'
type Accept = boolean | '*' | string | string[]
type MimeType = 'audio' | 'video' | 'image'
export interface FileExplorerGetFileOptions {
accept?: string
acceptVideo?: Accept
acceptImage?: Accept
acceptAudio?: Accept
}
const defaultAudio = ['mp3', 'wav', 'mpeg']
const defaultAudioMimes = defaultAudio.map(ext => `audio/${ext}`)
const defaultVideo = ['mp4', 'm4a', 'fmp4', 'mkv', 'flv', 'webm']
const defaultVideoMimes = defaultVideo.map(ext => `video/${ext}`)
const defaultImage = ['png', 'jpeg', 'jpg', 'bmp', 'gif']
const defaultImageMimes = defaultImage.map(ext => `image/${ext}`)
function toMime(txt: string, type: MimeType): string {
const prefix = `${type}/`
if (txt.includes(prefix)) return txt
return `${prefix}${txt.replace(prefix, '')}`
}
function getAudioMimes(acceptAudio: Accept): string[] {
if (_.isBoolean(acceptAudio)) return defaultAudioMimes
if (_.isString(acceptAudio)) return [toMime(acceptAudio, 'audio')]
return _.map(acceptAudio, acceptItem => toMime(acceptItem, 'audio'))
}
function getVideoMimes(acceptVideo: Accept): string[] {
if (_.isBoolean(acceptVideo)) return defaultVideoMimes
if (_.isString(acceptVideo)) return [toMime(acceptVideo, 'video')]
return _.map(acceptVideo, acceptItem => toMime(acceptItem, 'video'))
}
function getImageMimes(acceptImage: Accept): string[] {
if (_.isBoolean(acceptImage)) return defaultImageMimes
if (_.isString(acceptImage)) return [toMime(acceptImage, 'image')]
return _.map(acceptImage, acceptItem => toMime(acceptItem, 'image'))
}
function getFile(options?: FileExplorerGetFileOptions): Promise<File> {
let { accept = '', acceptVideo = false, acceptImage = false, acceptAudio = false } = options || {}
let formats: string[] = []
if (acceptVideo) formats = formats.concat(getVideoMimes(acceptVideo))
if (acceptImage) formats = formats.concat(getImageMimes(acceptVideo))
if (acceptAudio) formats = formats.concat(getAudioMimes(acceptAudio))
accept += formats.join(',')
const input = document.createElement('input')
input.type = 'file'
input.accept = accept || '*'
input.className = 'web-file-input-element'
input.style.display = 'none'
document.body.appendChild(input)
return new Promise((resolve, reject) => {
const listener = async event => {
if (!event.target) return close(event, reject)
const { files } = event.target as { files: FileList }
if (!files || !files[0]) return close(event, reject)
const file = files[0]
close(event, () => resolve(file))
}
/**
* there no reliable way to monitor cancel event.
* onBlur can be, but it is called on wrong moment and multiple times
*/
input.addEventListener('change', listener)
input.click()
function close(event, cb) {
input.remove()
input.removeEventListener('change', listener)
event.target.value = ''
cb()
}
})
}
export default {
getFile,
}