-
Notifications
You must be signed in to change notification settings - Fork 0
/
showOpenFilePicker.js
152 lines (123 loc) · 3.78 KB
/
showOpenFilePicker.js
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/** @type {import('./showOpenFilePicker.d.ts').Ponyfills} */
export const { showOpenFilePicker, showSaveFilePicker } = typeof document === 'object' ? (() => {
if (globalThis.showOpenFilePicker) return globalThis.showOpenFilePicker
const mapOfFiles = new WeakMap()
const prototypeOfFileSystemHandle = FileSystemHandle.prototype
const prototypeOfFileSystemFileHandle = FileSystemFileHandle.prototype
const input = document.createElement('input')
const a = document.createElement('a')
const getFileHandle = file => {
const fileHandle = create(prototypeOfFileSystemFileHandle)
mapOfFiles.set(fileHandle, file)
return fileHandle
}
const getAcceptType = type => values(Object(type?.accept)).join(',')
const resolveFilePicker = (resolve, reject) => {
input.click()
input.addEventListener('change', () => {
resolve([...input.files].map(getFileHandle))
input.value = ''
}, { once: true })
input.addEventListener('cancel', () => {
reject(new DOMException('The user aborted a request.'))
}, { once: true })
}
const { create, defineProperties, getOwnPropertyDescriptors, values } = Object
const { name, kind, ...descriptorsOfFileSystemHandle } = getOwnPropertyDescriptors(prototypeOfFileSystemHandle)
const { getFile, ...descriptorsOfFileSystemFileHandle } = getOwnPropertyDescriptors(prototypeOfFileSystemFileHandle)
input.type = 'file'
defineProperties(prototypeOfFileSystemHandle, {
...descriptorsOfFileSystemHandle,
...getOwnPropertyDescriptors({
get name() {
return mapOfFiles.get(this)?.name ?? name.call(this)
},
get kind() {
return mapOfFiles.has(this) ? 'file' : kind.call(this)
},
}),
})
defineProperties(prototypeOfFileSystemFileHandle, {
...descriptorsOfFileSystemFileHandle,
...getOwnPropertyDescriptors({
async getFile() {
return await mapOfFiles.get(this) || getFile.call(this)
},
async createWritable() {
const stream = new FileSystemWritableFileStream()
mapOfFiles.set(stream, mapOfFiles.get(this))
return stream
},
}),
})
class FileSystemWritableFileStream extends WritableStream {
constructor() {
_.set(super({
write: async (chunk) => {
const file = mapOfFiles.get(this)
_.set(this, new File([
file,
chunk instanceof Blob || chunk instanceof Uint8Array || typeof chunk === 'string'
? chunk
: ArrayBuffer.isView(chunk) || chunk instanceof ArrayBuffer
? new Uint8Array(chunk)
: chunk instanceof DataView
? new Uint8Array(chunk.buffer)
: new Uint8Array(new TextEncoder().encode(String(chunk))),
], file.name, {
type: file.type,
lastModified: file.lastModified,
}))
},
}), new File([], 'Untitled.txt', { type: 'text/plain' }))
}
async write(data) {
const writer = this.getWriter()
await writer.write(data)
writer.releaseLock()
}
async close() {
const file = mapOfFiles.get(this)
const url = URL.createObjectURL(file)
document.documentElement.append(a)
a.href = url
a.download = file.name
a.type = file.type
a.click()
a.remove()
URL.revokeObjectURL(url)
}
}
return {
showOpenFilePicker(options = null) {
input.multiple = Boolean(options?.multiple)
input.accept = [].concat(options?.types ?? []).map(getAcceptType).join(',')
return new Promise(resolveFilePicker)
},
async showSaveFilePicker(options = null) {
const accept = [].concat(
Object.entries(
Object(
[].concat(options?.types ?? [])[0]?.accept
)
)
)[0] || ["text/plain", [".txt"]]
return getFileHandle(
new File(
[],
options?.suggestedName ?? 'Untitled' + (accept?.[1]?.[0] || '.txt'),
{
type: accept?.[0] || 'text/plain'
}
)
)
},
}
})() : {
async showOpenFilePicker() {
return []
},
async showSaveFilePicker() {
return []
},
}