CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-file-selector

Convert DataTransfer object to a list of File objects

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

File Selector

File Selector is a TypeScript utility library that converts DataTransfer objects or file input elements to a list of File objects. It provides a unified interface for handling files from drag-and-drop events, file input change events, and FileSystemFileHandle items, with built-in browser compatibility handling.

Package Information

  • Package Name: file-selector
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install file-selector

Core Imports

import { fromEvent, FileWithPath } from "file-selector";

For CommonJS:

const { fromEvent, FileWithPath } = require("file-selector");

Basic Usage

import { fromEvent } from "file-selector";

// Handle drag-and-drop events
document.addEventListener("drop", async (evt) => {
  const files = await fromEvent(evt);
  console.log(files);
});

// Handle file input changes
const input = document.getElementById("myInput");
input.addEventListener("change", async (evt) => {
  const files = await fromEvent(evt);
  console.log(files);
});

// Handle FileSystemFileHandle arrays (experimental)
const handles = await window.showOpenFilePicker({ multiple: true });
const files = await fromEvent(handles);
console.log(files);

Architecture

File Selector is built around a single main function with several key components:

  • Main API: fromEvent() function provides unified interface for multiple input types
  • File Enhancement: Internal utilities add path information and MIME type detection to File objects
  • Browser Compatibility: Graceful degradation and polyfills for cross-browser support
  • Type Safety: Full TypeScript integration with enhanced File interface
  • Asynchronous Processing: Promise-based API for handling complex file system operations

Capabilities

File Event Processing

Converts various file input sources to a standardized array of File objects with path information.

/**
 * Convert a DragEvent's DataTransfer object, file input change event,
 * or FileSystemFileHandle array to a list of File objects
 * 
 * @param evt - Event or any - Can be DragEvent, input change event, or array of FileSystemFileHandle
 * @returns Promise resolving to array of FileWithPath or DataTransferItem objects
 */
async function fromEvent(evt: Event | any): Promise<(FileWithPath | DataTransferItem)[]>;

The fromEvent function handles multiple input types:

  • DragEvent: Processes drag-and-drop operations, including folder drops
  • Input Change Event: Handles file input element changes
  • FileSystemFileHandle Array: Experimental support for modern File System API

Key Features:

  • Automatically flattens nested directory structures
  • Filters out system files (.DS_Store, Thumbs.db)
  • Maintains file path information
  • Provides fallbacks for older browsers
  • Handles MIME type detection based on file extensions

Enhanced File Interface

Extended File interface with additional path properties for better file handling.

interface FileWithPath extends File {
  /** File path (may be absolute path on Electron) */
  readonly path?: string;
  /** Optional FileSystemFileHandle for modern File System API */
  readonly handle?: FileSystemFileHandle;
  /** Relative path for the file, always populated */
  readonly relativePath?: string;
}

The FileWithPath interface provides:

  • Standard File properties (name, size, type, lastModified)
  • Path information for nested directory structures
  • FileSystemFileHandle for modern browser APIs
  • Consistent relative path handling across platforms

Internal Processing Features

The package includes several internal processing capabilities:

  • MIME Type Detection: Automatic file type detection based on extensions using a comprehensive map of 1200+ file types
  • Path Processing: Smart path resolution that handles relative paths, webkit directory uploads, and cross-platform compatibility
  • File Filtering: Automatic filtering of system files (.DS_Store, Thumbs.db)
  • Directory Flattening: Recursive processing of nested directory structures into flat file arrays

Types

interface FileWithPath extends File {
  readonly path?: string;
  readonly handle?: FileSystemFileHandle;
  readonly relativePath?: string;
}

// Standard browser interfaces that the package works with:

interface DataTransferItem {
  readonly kind: string;
  readonly type: string;
  getAsFile(): File | null;
  getAsString(callback: (data: string) => void): void;
  webkitGetAsEntry?(): FileSystemEntry | null;
}

interface FileSystemFileHandle {
  getFile(): Promise<File>;
}

interface FileSystemEntry {
  readonly isDirectory: boolean;
  readonly isFile: boolean;
  readonly name: string;
  readonly fullPath: string;
}

Browser Compatibility

The package provides broad browser support with graceful degradation:

Basic File Selection:

  • File API (widely supported)
  • Drag Event (widely supported)
  • DataTransfer (widely supported)
  • <input type="file"> (universal support)

Advanced Features:

  • FileSystem API (limited support, Chrome-based browsers)
  • DataTransferItem.webkitGetAsEntry() (WebKit browsers)
  • FileSystemFileHandle (modern browsers with File System API)

Legacy Support:

  • IE11 compatibility with polyfills
  • Fallback for missing DataTransfer.items
  • Cross-browser file system entry handling

Error Handling

The package handles errors gracefully:

  • Returns empty array for unrecognized input types
  • Handles null/undefined DataTransfer objects
  • Manages FileSystem API availability and errors
  • Provides fallbacks for unsupported browser features
  • Handles secure context requirements for advanced APIs

Usage Examples

Drag and Drop with Folder Support

import { fromEvent, FileWithPath } from "file-selector";

const dropZone = document.getElementById("drop-zone");

dropZone.addEventListener("dragover", (evt) => {
  evt.preventDefault();
});

dropZone.addEventListener("drop", async (evt) => {
  evt.preventDefault();
  
  const files = await fromEvent(evt) as FileWithPath[];
  
  // Process files with path information
  files.forEach((file) => {
    console.log(`File: ${file.name}`);
    console.log(`Path: ${file.path}`);
    console.log(`Relative Path: ${file.relativePath}`);
    console.log(`Size: ${file.size} bytes`);
    console.log(`Type: ${file.type}`);
  });
});

File Input with Multiple Selection

import { fromEvent } from "file-selector";

const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.multiple = true;

fileInput.addEventListener("change", async (evt) => {
  const files = await fromEvent(evt);
  
  if (files.length === 0) {
    console.log("No files selected");
    return;
  }
  
  console.log(`Selected ${files.length} files:`);
  files.forEach((file, index) => {
    console.log(`${index + 1}. ${file.name} (${file.type})`);
  });
});

Modern File System API

import { fromEvent } from "file-selector";

// Check for File System API support
if (typeof window.showOpenFilePicker === "function") {
  try {
    // Open file picker
    const handles = await window.showOpenFilePicker({
      multiple: true,
      types: [{
        description: "Images",
        accept: {
          "image/*": [".png", ".jpg", ".jpeg", ".gif"]
        }
      }]
    });
    
    // Convert handles to files
    const files = await fromEvent(handles);
    console.log("Selected files:", files);
    
  } catch (err) {
    console.log("User cancelled file selection");
  }
}

Directory Upload

import { fromEvent } from "file-selector";

const directoryInput = document.createElement("input");
directoryInput.type = "file";
directoryInput.webkitdirectory = true;

directoryInput.addEventListener("change", async (evt) => {
  const files = await fromEvent(evt);
  
  // Group files by directory
  const filesByDir = files.reduce((acc, file) => {
    const dir = file.relativePath?.split("/")[0] || "root";
    if (!acc[dir]) acc[dir] = [];
    acc[dir].push(file);
    return acc;
  }, {} as Record<string, FileWithPath[]>);
  
  Object.entries(filesByDir).forEach(([dir, dirFiles]) => {
    console.log(`Directory: ${dir} (${dirFiles.length} files)`);
  });
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/file-selector@2.1.x
Publish Source
CLI
Badge
tessl/npm-file-selector badge