Skip to content

Commit

Permalink
Add new behavior to directories
Browse files Browse the repository at this point in the history
  • Loading branch information
tomayac committed May 5, 2021
1 parent 8997ed1 commit 2ea28fc
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 24 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ const throwIfExistingHandleNotGood = true;
await fileSave(someBlob, options, existingHandle, throwIfExistingHandleNotGood);
```

### File operations and exceptions

The File System Access API supports exceptions, so apps can throw when problems occur (permissions
not granted, out of disk space,…), or when the user cancels the dialog. The legacy methods,
unfortunately, do not support exceptions (albeit there is an
[HTML issue](https://github.com/whatwg/html/issues/6376) open for this request). If your app depends
on exceptions, see the file
[`index.d.ts`](https://github.com/GoogleChromeLabs/browser-fs-access/blob/main/index.d.ts) for the
documentation of the `setupLegacyCleanupAndRejection` parameter.

## Browser-FS-Access in Action

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

## License and Note

Expand Down
32 changes: 23 additions & 9 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ export function fileOpen<M extends boolean | undefined = false>(options?: {
/** Allow multiple files to be selected. Defaults to false. */
multiple?: M;
/**
* Configurable cleanup and `Promise` rejector usable with legacy API for determining when (and reacting when) a user cancels the operation.
* 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.
* The method should return a function that will be called when either the user chooses to open a file or the `rejectionHandler` is called.
* 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.
* Configurable cleanup and `Promise` rejector usable with legacy API for
* determining when (and reacting if) a user cancels the operation. The
* method will be passed a reference to the internal `rejectionHandler` that
* can, e.g., be attached to/removed from the window or called after a
* timeout. The method should return a function that will be called when
* either the user chooses to open a file or the `rejectionHandler` is
* called. 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.
* ToDo: Remove this workaround once
* https://github.com/whatwg/html/issues/6376 is specified and supported.
*/
setupLegacyCleanupAndRejection?: (
rejectionHandler?: () => void
Expand All @@ -38,9 +46,15 @@ export function fileSave(
/** Suggested file description. Defaults to "". */
description?: string;
},
/** A potentially existing file handle for a file to save to. Defaults to null. */
/**
* A potentially existing file handle for a file to save to. Defaults to
* null.
*/
existingHandle?: FileSystemHandle | null,
/** Determines whether to throw (rather than open a new file save dialog) when existingHandle is no longer good. Defaults to false. */
/**
* Determines whether to throw (rather than open a new file save dialog)
* when existingHandle is no longer good. Defaults to false.
*/
throwIfExistingHandleNotGood?: boolean | false
): Promise<FileSystemHandle>;

Expand All @@ -64,9 +78,9 @@ export interface FileWithHandle extends File {
handle?: FileSystemHandle;
}

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

export interface FileSystemHandlePermissionDescriptor {
fileSystemHandle: FileSystemHandle;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 21 additions & 12 deletions src/legacy/directory-open.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,30 @@ export default async (options = {}) => {

// ToDo: Remove this workaround once
// https://github.com/whatwg/html/issues/6376 is specified and supported.
const rejectOnPageInteraction = () => {
window.removeEventListener('pointermove', rejectOnPageInteraction);
window.removeEventListener('pointerdown', rejectOnPageInteraction);
window.removeEventListener('keydown', rejectOnPageInteraction);
reject(new DOMException('The user aborted a request.', 'AbortError'));
};
let cleanupListenersAndMaybeReject;
const rejectionHandler = () => cleanupListenersAndMaybeReject(reject);
if (options.setupLegacyCleanupAndRejection) {
cleanupListenersAndMaybeReject = options.setupLegacyCleanupAndRejection(
rejectionHandler
);
} else {
// Default rejection behavior; works in most cases, but not in Chrome in non-secure contexts.
cleanupListenersAndMaybeReject = (reject) => {
window.removeEventListener('pointermove', rejectionHandler);
window.removeEventListener('pointerdown', rejectionHandler);
window.removeEventListener('keydown', rejectionHandler);
if (reject) {
reject(new DOMException('The user aborted a request.', 'AbortError'));
}
};

window.addEventListener('pointermove', rejectOnPageInteraction);
window.addEventListener('pointerdown', rejectOnPageInteraction);
window.addEventListener('keydown', rejectOnPageInteraction);
window.addEventListener('pointermove', rejectionHandler);
window.addEventListener('pointerdown', rejectionHandler);
window.addEventListener('keydown', rejectionHandler);
}

input.addEventListener('change', () => {
window.removeEventListener('pointermove', rejectOnPageInteraction);
window.removeEventListener('pointerdown', rejectOnPageInteraction);
window.removeEventListener('keydown', rejectOnPageInteraction);
cleanupListenersAndMaybeReject();
let files = Array.from(input.files);
if (!options.recursive) {
files = files.filter((file) => {
Expand Down

0 comments on commit 2ea28fc

Please sign in to comment.