Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: split file handles into seperate function #126

Merged
merged 1 commit into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,13 @@ input.addEventListener("change", async (event) => {
Convert [FileSystemFileHandle](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle) items to File objects:

```js
import { fromEvent } from "file-selector";
import { fromFileHandles } from "file-selector";

const handles = await window.showOpenFilePicker({ multiple: true });
const files = await fromEvent(handles);
const files = await fromFileHandles(handles);
console.log(files);
```

> [!NOTE]
> The above is experimental and subject to change.

## Browser Support

Most browser support basic File selection with drag 'n' drop or file input:
Expand Down
40 changes: 21 additions & 19 deletions src/file-selector.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FileWithPath } from "./file.js";
import { fromEvent } from "./file-selector.js";
import { fromEvent, fromFileHandles } from "./file-selector.js";

it("returns a Promise", async () => {
const evt = new Event("test");
Expand Down Expand Up @@ -46,27 +46,29 @@ it("should return an empty array if the evt {target} has no {files} prop", async
expect(files).toHaveLength(0);
});

it("should return files if the arg is a list of FileSystemFileHandle", async () => {
const name = "test.json";
const [mockFile, mockHandle] = createFileSystemFileHandle(
name,
{ ping: true },
{
type: "application/json",
},
);
describe("fromFileHandles", () => {
it("retrieves files from file handles", async () => {
const name = "test.json";
const [mockFile, mockHandle] = createFileSystemFileHandle(
name,
{ ping: true },
{
type: "application/json",
},
);

const files = await fromEvent([mockHandle]);
expect(files).toHaveLength(1);
expect(files.every((file) => file instanceof File)).toBe(true);
const files = await fromFileHandles([mockHandle]);
expect(files).toHaveLength(1);
expect(files.every((file) => file instanceof File)).toBe(true);

const [file] = files as FileWithPath[];
const [file] = files as FileWithPath[];

expect(file.name).toBe(mockFile.name);
expect(file.type).toBe(mockFile.type);
expect(file.size).toBe(mockFile.size);
expect(file.lastModified).toBe(mockFile.lastModified);
expect(file.path).toBe(`./${name}`);
expect(file.name).toBe(mockFile.name);
expect(file.type).toBe(mockFile.type);
expect(file.size).toBe(mockFile.size);
expect(file.lastModified).toBe(mockFile.lastModified);
expect(file.path).toBe(`./${name}`);
});
});

it("should return an empty array if the passed event is not a DragEvent", async () => {
Expand Down
38 changes: 26 additions & 12 deletions src/file-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ const FILES_TO_IGNORE = [
* NOTE: If some of the items are folders,
* everything will be flattened and placed in the same list but the paths will be kept as a {path} property.
*
* EXPERIMENTAL: A list of https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle objects can also be passed as an arg
* and a list of File objects will be returned.
*
* @param evt
*/
export async function fromEvent(
Expand All @@ -23,11 +20,6 @@ export async function fromEvent(
return getDataTransferFiles(evt.dataTransfer, evt.type);
} else if (isChangeEvt(evt)) {
return getInputFiles(evt);
} else if (
Array.isArray(evt) &&
evt.every((item) => "getFile" in item && typeof item.getFile === "function")
) {
return getFsHandleFiles(evt);
}
return [];
}
Expand All @@ -52,10 +44,32 @@ function getInputFiles(event: Event): FileWithPath[] {
return Array.from(event.target.files).map((file) => toFileWithPath(file));
}

// Ee expect each handle to be https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle
async function getFsHandleFiles(handles: any[]) {
const files = await Promise.all(handles.map((h) => h.getFile()));
return files.map((file) => toFileWithPath(file));
/**
* Retrieves files from an array of `FileSystemHandle` objects from the File System API.
*
* @param handles The array of handles to convert.
* @returns A promise that resolves to an array of files retrieved from the handles.
*
* @example
* ```js
* const handles = await window.showOpenFilePicker({ multiple: true });
* const files = await fromFileHandles(handles);
* ```
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File_System_API|MDN - File System API}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle|MDN - `FileSystemHandle`}
*/
export function fromFileHandles(
handles: FileSystemFileHandle[],
): Promise<FileWithPath[]> {
return Promise.all(handles.map((handle) => fromFileHandle(handle)));
}

async function fromFileHandle(
handle: FileSystemFileHandle,
): Promise<FileWithPath> {
const file = await handle.getFile();
return toFileWithPath(file);
}

async function getDataTransferFiles(dataTransfer: DataTransfer, type: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { fromEvent } from "./file-selector.js";
export { fromEvent, fromFileHandles } from "./file-selector.js";
export { FileWithPath } from "./file.js";
Loading