1.0.0 • Published 6 months ago

@fivexlabs/use-file-system v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

🗂️ useFileSystem Hook

A comprehensive React hook for the File System Access API with full TypeScript support. Simplify modern file system interactions in your React applications with a clean, intuitive API.

npm version License: MIT TypeScript

✨ Features

  • 🔍 Browser Compatibility Check - Automatically detects File System Access API support
  • 📁 File Operations - Open single or multiple files with type filtering
  • 💾 Save Operations - Save files with custom names and formats
  • 📂 Directory Access - Open and interact with directories
  • 🏗️ State Management - Built-in loading, error, and data states
  • 🛡️ Error Handling - Graceful error handling with detailed error states
  • 📘 TypeScript Support - Full type definitions included
  • Modern API - Built on the latest File System Access API
  • 🎣 React Hooks - Follows React hooks patterns and best practices

🚀 Installation

npm install @fivexlabs/use-file-system
yarn add @fivexlabs/use-file-system
pnpm add @fivexlabs/use-file-system

🌐 Browser Support

The File System Access API is currently supported in:

  • Chrome 86+
  • Edge 86+
  • Opera 72+

Note: This API is not yet supported in Firefox or Safari. The hook includes built-in browser compatibility detection.

📖 Basic Usage

import React from 'react';
import { useFileSystem } from '@fivexlabs/use-file-system';

function MyComponent() {
  const {
    isSupported,
    file,
    loading,
    error,
    openFile,
    saveAs,
    clearError
  } = useFileSystem();

  const handleOpenFile = async () => {
    try {
      await openFile({
        types: [{
          description: 'Text files',
          accept: {
            'text/plain': ['.txt'],
            'text/markdown': ['.md']
          }
        }]
      });
    } catch (err) {
      console.error('Failed to open file:', err);
    }
  };

  const handleSaveFile = async () => {
    const content = 'Hello, World!';
    try {
      await saveAs(content, 'hello.txt');
    } catch (err) {
      console.error('Failed to save file:', err);
    }
  };

  if (!isSupported) {
    return <div>File System Access API is not supported in this browser.</div>;
  }

  return (
    <div>
      {error && (
        <div style={{ color: 'red' }}>
          Error: {error.message}
          <button onClick={clearError}>Clear</button>
        </div>
      )}

      {loading && <div>Loading...</div>}

      <button onClick={handleOpenFile} disabled={loading}>
        Open File
      </button>

      <button onClick={handleSaveFile} disabled={loading}>
        Save File
      </button>

      {file && (
        <div>
          <h3>Selected File:</h3>
          <p><strong>Name:</strong> {file.name}</p>
          <p><strong>Size:</strong> {file.size} bytes</p>
          <p><strong>Type:</strong> {file.type}</p>
          {file.content && (
            <pre>{String(file.content).slice(0, 200)}...</pre>
          )}
        </div>
      )}
    </div>
  );
}

📚 API Reference

Hook Return Value

The useFileSystem hook returns an object with the following properties:

State Properties

PropertyTypeDescription
isSupportedbooleanWhether the File System Access API is supported
fileFileDetails \| nullCurrently selected single file
filesFileDetails[]Array of selected multiple files
directoryDirectoryDetails \| nullCurrently selected directory
loadingbooleanLoading state for async operations
errorError \| nullCurrent error state

Action Methods

MethodParametersDescription
openFileoptions?: OpenFilePickerOptionsOpen a single file
openMultipleFilesoptions?: OpenFilePickerOptionsOpen multiple files
saveFilesuggestedName: string, data: BlobPart[], options?: SaveFilePickerOptionsSave data to a file
saveAscontent: string \| ArrayBuffer, suggestedName?: string, options?: SaveFilePickerOptionsSave content with a suggested name
openDirectoryoptions?: DirectoryPickerOptionsOpen a directory
readFileContentfile: File, readAs?: 'text' \| 'buffer'Read file content as text or buffer

Clear Methods

MethodDescription
clearFile()Clear the current file
clearFiles()Clear all selected files
clearDirectory()Clear the selected directory
clearError()Clear the current error

Type Definitions

interface FileDetails {
  name: string;
  content: string | ArrayBuffer | null;
  handle: FileSystemFileHandle | null;
  size: number;
  type: string;
  lastModified: number;
}

interface DirectoryDetails {
  name: string;
  handle: FileSystemDirectoryHandle | null;
}

🎯 Advanced Examples

Opening Multiple Files with Type Filtering

const handleOpenImages = async () => {
  try {
    await openMultipleFiles({
      types: [{
        description: 'Images',
        accept: {
          'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp']
        }
      }]
    });
  } catch (err) {
    console.error('Failed to open images:', err);
  }
};

Saving JSON Data

const handleSaveJSON = async () => {
  const data = { message: 'Hello', timestamp: Date.now() };
  try {
    await saveAs(
      JSON.stringify(data, null, 2),
      'data.json',
      {
        types: [{
          description: 'JSON files',
          accept: {
            'application/json': ['.json']
          }
        }]
      }
    );
  } catch (err) {
    console.error('Failed to save JSON:', err);
  }
};

Working with Directories

const handleOpenDirectory = async () => {
  try {
    await openDirectory({ mode: 'readwrite' });
    // Now you can access directory.handle for further operations
  } catch (err) {
    console.error('Failed to open directory:', err);
  }
};

Reading File Content as ArrayBuffer

const handleOpenBinaryFile = async () => {
  try {
    const input = document.createElement('input');
    input.type = 'file';
    input.onchange = async (e) => {
      const file = e.target.files[0];
      if (file) {
        const buffer = await readFileContent(file, 'buffer');
        console.log('File buffer:', buffer);
      }
    };
    input.click();
  } catch (err) {
    console.error('Failed to read file:', err);
  }
};

🔧 Configuration Options

OpenFilePickerOptions

interface OpenFilePickerOptions {
  multiple?: boolean;
  excludeAcceptAllOption?: boolean;
  types?: FilePickerAcceptType[];
}

SaveFilePickerOptions

interface SaveFilePickerOptions {
  suggestedName?: string;
  excludeAcceptAllOption?: boolean;
  types?: FilePickerAcceptType[];
}

DirectoryPickerOptions

interface DirectoryPickerOptions {
  mode?: 'read' | 'readwrite';
}

FilePickerAcceptType

interface FilePickerAcceptType {
  description?: string;
  accept: Record<string, string | string[]>;
}

🛡️ Error Handling

The hook provides comprehensive error handling:

const { error, clearError } = useFileSystem();

// Handle different types of errors
if (error) {
  if (error.name === 'AbortError') {
    // User cancelled the operation
    console.log('Operation was cancelled');
  } else if (error.name === 'NotAllowedError') {
    // Permission denied
    console.log('Permission denied');
  } else {
    // Other errors
    console.log('An error occurred:', error.message);
  }
}

🎨 Demo Application

This repository includes a comprehensive demo application showcasing all features. To run the demo:

git clone https://github.com/fivex-labs/use-file-system.git
cd use-file-system
npm install
npm run dev

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

git clone https://github.com/fivex-labs/use-file-system.git
cd use-file-system
npm install

Available Scripts

  • npm run dev - Start the demo application
  • npm run build - Build the demo application
  • npm run build:lib - Build the library for distribution
  • npm run lint - Run ESLint

📜 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Built with ❤️ by Fivex Labs
  • Inspired by the modern web's File System Access API
  • Thanks to the React community for hooks patterns and best practices

🔗 Links


Made with ❤️ by Fivex Labs for the developer community