UNPKG

@fivexlabs/use-file-system

Version:

A comprehensive React hook for the File System Access API with TypeScript support

370 lines (295 loc) 9.48 kB
# 🗂️ 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](https://badge.fury.io/js/@fivexlabs%2Fuse-file-system.svg)](https://badge.fury.io/js/@fivexlabs%2Fuse-file-system) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) ## ✨ 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 ```bash npm install @fivexlabs/use-file-system ``` ```bash yarn add @fivexlabs/use-file-system ``` ```bash 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 ```jsx 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 | Property | Type | Description | |----------|------|-------------| | `isSupported` | `boolean` | Whether the File System Access API is supported | | `file` | `FileDetails \| null` | Currently selected single file | | `files` | `FileDetails[]` | Array of selected multiple files | | `directory` | `DirectoryDetails \| null` | Currently selected directory | | `loading` | `boolean` | Loading state for async operations | | `error` | `Error \| null` | Current error state | #### Action Methods | Method | Parameters | Description | |--------|------------|-------------| | `openFile` | `options?: OpenFilePickerOptions` | Open a single file | | `openMultipleFiles` | `options?: OpenFilePickerOptions` | Open multiple files | | `saveFile` | `suggestedName: string, data: BlobPart[], options?: SaveFilePickerOptions` | Save data to a file | | `saveAs` | `content: string \| ArrayBuffer, suggestedName?: string, options?: SaveFilePickerOptions` | Save content with a suggested name | | `openDirectory` | `options?: DirectoryPickerOptions` | Open a directory | | `readFileContent` | `file: File, readAs?: 'text' \| 'buffer'` | Read file content as text or buffer | #### Clear Methods | Method | Description | |--------|-------------| | `clearFile()` | Clear the current file | | `clearFiles()` | Clear all selected files | | `clearDirectory()` | Clear the selected directory | | `clearError()` | Clear the current error | ### Type Definitions ```typescript 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 ```jsx 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 ```jsx 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 ```jsx 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 ```jsx 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 ```typescript interface OpenFilePickerOptions { multiple?: boolean; excludeAcceptAllOption?: boolean; types?: FilePickerAcceptType[]; } ``` ### SaveFilePickerOptions ```typescript interface SaveFilePickerOptions { suggestedName?: string; excludeAcceptAllOption?: boolean; types?: FilePickerAcceptType[]; } ``` ### DirectoryPickerOptions ```typescript interface DirectoryPickerOptions { mode?: 'read' | 'readwrite'; } ``` ### FilePickerAcceptType ```typescript interface FilePickerAcceptType { description?: string; accept: Record<string, string | string[]>; } ``` ## 🛡️ Error Handling The hook provides comprehensive error handling: ```jsx 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: ```bash 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 ```bash 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](LICENSE) file for details. ## 🙏 Acknowledgments - Built with ❤️ by [Fivex Labs](https://github.com/fivex-labs) - Inspired by the modern web's File System Access API - Thanks to the React community for hooks patterns and best practices ## 🔗 Links - [File System Access API Documentation](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) - [Browser Compatibility](https://caniuse.com/native-filesystem-api) - [Demo Application](https://use-file-system-demo.netlify.app) --- **Made with ❤️ [by Fivex Labs](https://fivexlabs.com) for the developer community**