Skip to content

Commit 2ea28fc

Browse files
committed
Add new behavior to directories
1 parent 8997ed1 commit 2ea28fc

File tree

4 files changed

+59
-24
lines changed

4 files changed

+59
-24
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ const throwIfExistingHandleNotGood = true;
127127
await fileSave(someBlob, options, existingHandle, throwIfExistingHandleNotGood);
128128
```
129129

130+
### File operations and exceptions
131+
132+
The File System Access API supports exceptions, so apps can throw when problems occur (permissions
133+
not granted, out of disk space,…), or when the user cancels the dialog. The legacy methods,
134+
unfortunately, do not support exceptions (albeit there is an
135+
[HTML issue](https://github.com/whatwg/html/issues/6376) open for this request). If your app depends
136+
on exceptions, see the file
137+
[`index.d.ts`](https://github.com/GoogleChromeLabs/browser-fs-access/blob/main/index.d.ts) for the
138+
documentation of the `setupLegacyCleanupAndRejection` parameter.
139+
130140
## Browser-FS-Access in Action
131141

132142
You can see the module in action in the [Excalidraw](https://excalidraw.com/) drawing app.
@@ -148,11 +158,13 @@ issue reports, and the Windows build fix.
148158
Directory operations were made consistent regarding `webkitRelativePath`
149159
and parallelized and sped up significantly by
150160
[@RReverser](https://github.com/RReverser).
151-
The TypeScript type annotations were provided by
161+
The TypeScript type annotations were initially provided by
152162
[@nanaian](https://github.com/nanaian).
153163
Dealing correctly with cross-origin iframes was contributed by
154164
[@nikhilbghodke](https://github.com/nikhilbghodke) and
155165
[@kbariotis](https://github.com/kbariotis).
166+
The exception handling of the legacy methods was contributed by
167+
[@jmrog](https://github.com/jmrog).
156168

157169
## License and Note
158170

index.d.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ export function fileOpen<M extends boolean | undefined = false>(options?: {
1111
/** Allow multiple files to be selected. Defaults to false. */
1212
multiple?: M;
1313
/**
14-
* Configurable cleanup and `Promise` rejector usable with legacy API for determining when (and reacting when) a user cancels the operation.
15-
* The method will be called a reference to the internal `rejectionHandler` that can, e.g., be attached to/removed from the window or called after a timeout.
16-
* The method should return a function that will be called when either the user chooses to open a file or the `rejectionHandler` is called.
17-
* In the latter case, the returned function will also be passed a reference to the `reject` callback for the `Promise` returned by `fileOpen`, so that developers may reject the `Promise` when desired at that time.
14+
* Configurable cleanup and `Promise` rejector usable with legacy API for
15+
* determining when (and reacting if) a user cancels the operation. The
16+
* method will be passed a reference to the internal `rejectionHandler` that
17+
* can, e.g., be attached to/removed from the window or called after a
18+
* timeout. The method should return a function that will be called when
19+
* either the user chooses to open a file or the `rejectionHandler` is
20+
* called. In the latter case, the returned function will also be passed a
21+
* reference to the `reject` callback for the `Promise` returned by
22+
* `fileOpen`, so that developers may reject the `Promise` when desired at
23+
* that time.
24+
* ToDo: Remove this workaround once
25+
* https://github.com/whatwg/html/issues/6376 is specified and supported.
1826
*/
1927
setupLegacyCleanupAndRejection?: (
2028
rejectionHandler?: () => void
@@ -38,9 +46,15 @@ export function fileSave(
3846
/** Suggested file description. Defaults to "". */
3947
description?: string;
4048
},
41-
/** A potentially existing file handle for a file to save to. Defaults to null. */
49+
/**
50+
* A potentially existing file handle for a file to save to. Defaults to
51+
* null.
52+
*/
4253
existingHandle?: FileSystemHandle | null,
43-
/** Determines whether to throw (rather than open a new file save dialog) when existingHandle is no longer good. Defaults to false. */
54+
/**
55+
* Determines whether to throw (rather than open a new file save dialog)
56+
* when existingHandle is no longer good. Defaults to false.
57+
*/
4458
throwIfExistingHandleNotGood?: boolean | false
4559
): Promise<FileSystemHandle>;
4660

@@ -64,9 +78,9 @@ export interface FileWithHandle extends File {
6478
handle?: FileSystemHandle;
6579
}
6680

67-
// The following typings implement the relevant parts of the File System Access API.
68-
// This can be removed once the specification reaches the Candidate phase and is
69-
// implemented as part of microsoft/TSJS-lib-generator.
81+
// The following typings implement the relevant parts of the File System Access
82+
// API. This can be removed once the specification reaches the Candidate phase
83+
// and is implemented as part of microsoft/TSJS-lib-generator.
7084

7185
export interface FileSystemHandlePermissionDescriptor {
7286
fileSystemHandle: FileSystemHandle;

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/legacy/directory-open.mjs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,30 @@ export default async (options = {}) => {
2929

3030
// ToDo: Remove this workaround once
3131
// https://github.com/whatwg/html/issues/6376 is specified and supported.
32-
const rejectOnPageInteraction = () => {
33-
window.removeEventListener('pointermove', rejectOnPageInteraction);
34-
window.removeEventListener('pointerdown', rejectOnPageInteraction);
35-
window.removeEventListener('keydown', rejectOnPageInteraction);
36-
reject(new DOMException('The user aborted a request.', 'AbortError'));
37-
};
32+
let cleanupListenersAndMaybeReject;
33+
const rejectionHandler = () => cleanupListenersAndMaybeReject(reject);
34+
if (options.setupLegacyCleanupAndRejection) {
35+
cleanupListenersAndMaybeReject = options.setupLegacyCleanupAndRejection(
36+
rejectionHandler
37+
);
38+
} else {
39+
// Default rejection behavior; works in most cases, but not in Chrome in non-secure contexts.
40+
cleanupListenersAndMaybeReject = (reject) => {
41+
window.removeEventListener('pointermove', rejectionHandler);
42+
window.removeEventListener('pointerdown', rejectionHandler);
43+
window.removeEventListener('keydown', rejectionHandler);
44+
if (reject) {
45+
reject(new DOMException('The user aborted a request.', 'AbortError'));
46+
}
47+
};
3848

39-
window.addEventListener('pointermove', rejectOnPageInteraction);
40-
window.addEventListener('pointerdown', rejectOnPageInteraction);
41-
window.addEventListener('keydown', rejectOnPageInteraction);
49+
window.addEventListener('pointermove', rejectionHandler);
50+
window.addEventListener('pointerdown', rejectionHandler);
51+
window.addEventListener('keydown', rejectionHandler);
52+
}
4253

4354
input.addEventListener('change', () => {
44-
window.removeEventListener('pointermove', rejectOnPageInteraction);
45-
window.removeEventListener('pointerdown', rejectOnPageInteraction);
46-
window.removeEventListener('keydown', rejectOnPageInteraction);
55+
cleanupListenersAndMaybeReject();
4756
let files = Array.from(input.files);
4857
if (!options.recursive) {
4958
files = files.filter((file) => {

0 commit comments

Comments
 (0)