@unciatech/file-manager 0.0.42 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +291 -410
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,517 +1,398 @@
1
1
  # 🗂️ File Manager
2
2
 
3
- A robust, production-ready file management component for any **React** application — works with Vite, Next.js, Remix, CRA, and more.
3
+ A robust, production-ready file management component for **React applications**.
4
4
 
5
- It supports deep folder nesting, drag-and-drop file uploads, metadata management for various file types (Images, Videos, Audio, Documents), unified grid layouts, and fully optimized loading states.
5
+ It works with **Vite, Next.js, Remix, CRA**, and other React frameworks.
6
6
 
7
- ## 🌟 Key Features
8
- - **Dual Operating Modes**: Use it as a standalone full-page media library or instantiate it as a picker modal for form inputs.
9
- - **Unified Grid View**: Beautiful, responsive layout that intelligently renders thumbnails, icons, and metadata based on the file's MIME type.
10
- - **Nested Folder Structure**: Infinite folder depth with smooth virtualized/paginated fetching.
11
- - **Provider Agnostic**: Built on an `IFileManagerProvider` interface. You can easily hot-swap the mock data provider for a real backend (Node.js, Supabase, Strapi, etc.).
12
- - **Bulk Actions**: Select multiple files/folders at once to bulk move or bulk delete.
13
- - **Optimistic UI Updates**: Instant visual feedback when renaming folders or updating file descriptions, with silent background synchronization.
14
- - **Graceful Error Handling**: Resilient `<FileManagerErrorBoundary>` that captures catastrophic failures and allows users to hard-reload safely without app crashes.
7
+ The library supports deep folder hierarchies, drag-and-drop uploads, metadata management for multiple file types (Images, Videos, Audio, Documents), and a responsive grid interface optimized for large media libraries.
15
8
 
16
- ## 🛠️ Tech Stack
17
- - **Framework**: React (any — Vite, Next.js, Remix, CRA)
18
- - **Styling**: Tailwind CSS
19
- - **Icons**: Custom SVG Icons
20
- - **Notifications**: Sonner
9
+ ---
21
10
 
22
- > [!WARNING]
23
- > This library is currently in **BETA**. Please report any bugs or feature requests on the GitHub issues page.
11
+ # 🌟 Key Features
24
12
 
25
- ## Setup & Requirements
13
+ * **Dual Operating Modes** - Use as a full-page media library or as a picker modal.
14
+ * **Unified Grid View** - Responsive layout that intelligently renders thumbnails, icons, and metadata.
15
+ * **Nested Folder Structure** - Infinite folder depth with smooth paginated fetching.
16
+ * **Provider Agnostic** - Integrates with any backend through the `IFileManagerProvider` interface.
17
+ * **Bulk Actions** - Select and move/delete multiple files or folders.
18
+ * **Optimistic UI Updates** - Instant UI feedback with background synchronization.
19
+ * **Error Boundary Protection** - Built-in `<FileManagerErrorBoundary>` prevents crashes.
26
20
 
27
- Because this library relies heavily on Tailwind CSS for minimal zero-config styling, ensure your app is configured to scan the library's components for Tailwind utility classes.
21
+ ---
28
22
 
29
- ### Tailwind v4
30
- If you are using **Tailwind CSS v4** (like in newer Next.js or Vite projects), add this to your main CSS file (`globals.css` or `index.css`). This pulls in the required theme configuration and ensures the library is scanned:
23
+ # 📦 Installation
31
24
 
32
- ```css
33
- @import "tailwindcss";
34
- @import "@unciatech/file-manager/styles";
35
- @source "../node_modules/@unciatech/file-manager";
25
+ Install the file manager and required utilities:
26
+
27
+ ```bash
28
+ npm install @unciatech/file-manager class-variance-authority clsx tailwind-merge
36
29
  ```
37
30
 
38
- ### Tailwind v3
39
- If you are still on **Tailwind v3**, add the library path to your `tailwind.config.ts`:
31
+ > ⚠️ This library relies on **Tailwind CSS** for styling.
32
+ > If your project does not use Tailwind yet, install it in Step 3.
40
33
 
41
- ```ts
42
- content: [
43
- // ... your other paths
44
- "./node_modules/@unciatech/file-manager/dist/**/*.{js,ts,jsx,tsx}",
45
- ],
34
+ ---
35
+
36
+ # Quick Start
37
+
38
+ You can instantly test the UI using the built-in `MockProvider`.
39
+
40
+ ```tsx
41
+ import { FileManager, MockProvider } from "@unciatech/file-manager"
42
+
43
+ const provider = new MockProvider()
44
+
45
+ export default function App() {
46
+ return (
47
+ <FileManager
48
+ provider={provider}
49
+ basePath="/"
50
+ viewMode="grid"
51
+ allowedFileTypes={["images", "videos", "files"]}
52
+ />
53
+ )
54
+ }
46
55
  ```
47
56
 
48
- ## Basic Usage in Your Project
57
+ ---
49
58
 
50
- Integrating the File Manager into your React application (Next.js, Vite, Remix, CRA, etc.) is easy.
59
+ # 🛠 Setup & Requirements
51
60
 
52
- ### Step 1: Install Dependencies
61
+ ## Step 1 - Install the Library
53
62
 
54
- Install the core library along with its Radix UI and Tailwind dependencies:
55
63
  ```bash
56
64
  npm install @unciatech/file-manager class-variance-authority clsx tailwind-merge
57
65
  ```
58
66
 
59
- > **Note:** The file manager uses **Tailwind CSS**. Ensure your project has Tailwind properly configured.
67
+ ---
68
+
69
+ ## Step 2 - Import Styles
70
+
71
+ Import the file manager styles in your root file.
72
+
73
+ ### Vite / React
60
74
 
61
- **(CRITICAL) Import the styles:**
62
- Import the styles in your root layout (`layout.tsx` for Next.js) or top-level file (`main.tsx` / `App.tsx` for Vite):
63
75
  ```ts
64
- import '@unciatech/file-manager/styles';
76
+ import "@unciatech/file-manager/styles"
65
77
  ```
66
78
 
67
- ### Step 2: Create your Custom API Provider
79
+ Place this inside:
68
80
 
69
- The file manager is completely agnostic to your backend database. You simply need to create a class that implements the `IFileManagerProvider` interface.
81
+ ```
82
+ main.tsx or App.tsx
83
+ ```
70
84
 
71
- Here is an example of what your custom provider might look like, making real API calls to your backend using `fetch`:
85
+ ### Next.js
72
86
 
73
- ```typescript
74
- // lib/my-api-provider.ts
75
- import {
76
- IFileManagerProvider,
77
- FolderId,
78
- FileUploadInput
79
- } from "@/types/file-manager"; // Or "@unciatech/file-manager" for external users
87
+ Import inside your root layout:
80
88
 
81
- export class MyCustomApiProvider implements IFileManagerProvider {
82
- private baseUrl = "https://api.mybackend.com/v1";
89
+ ```ts
90
+ import "@unciatech/file-manager/styles"
91
+ ```
83
92
 
84
- // Example 1: Fetching folders from your Real API
85
- async getFolders(folderId: FolderId, page = 1, limit = 24) {
86
- const parentQuery = folderId ? `&parentId=${folderId}` : "&isRoot=true";
87
-
88
- // Simulate real API Call
89
- const res = await fetch(`${this.baseUrl}/folders?page=${page}&limit=${limit}${parentQuery}`);
90
-
91
- if (!res.ok) throw new Error("Failed to fetch folders");
92
-
93
- const data = await res.json();
94
-
95
- return {
96
- folders: data.folders, // Array of Folder objects matching our interface
97
- pagination: data.pagination // { currentPage, totalPages, totalFiles, filesPerPage }
98
- };
99
- }
93
+ Place this in:
100
94
 
101
- // Example 2: Uploading files via multipart/form-data
102
- async uploadFiles(filesInput: FileUploadInput[], folderId?: FolderId) {
103
- const formData = new FormData();
104
- if (folderId) formData.append("folderId", String(folderId));
105
-
106
- filesInput.forEach(({ file }) => {
107
- formData.append("files", file);
108
- });
95
+ ```
96
+ app/layout.tsx
97
+ ```
109
98
 
110
- const res = await fetch(`${this.baseUrl}/upload`, {
111
- method: 'POST',
112
- body: formData
113
- });
99
+ ---
114
100
 
115
- return res.json(); // Returns array of FileMetaData
116
- }
101
+ # Step 3 - Configure Tailwind CSS
117
102
 
118
- // ... Implement the remaining interface methods (getFiles, renameFolder, bulkMove, etc.)
119
- }
120
- ```
103
+ This library relies heavily on **Tailwind CSS**.
121
104
 
122
- > **💡 Pro Tip - The Mock Provider:**
123
- > If you are just prototyping and don't have a backend ready yet, you can skip Step 2 entirely! We included a fully functional `MockProvider` that fakes network latency and stores data in memory. Just import it and use it right away to see the UI in action.
105
+ If your project **already uses Tailwind**, follow Option A.
106
+ If **not**, follow Option B.
124
107
 
125
- ### Step 3: Application Setup
108
+ ---
126
109
 
127
- Below are the complete setup guides for integrating the components into the two most popular React frameworks.
110
+ ## Option A - Project Already Uses Tailwind
128
111
 
129
- <details open>
130
- <summary><b>Example A: Vite (React Router)</b></summary>
131
- <br>
112
+ ### Tailwind v4
132
113
 
133
- **1. Set up the Router Shell (`src/main.tsx`)**
134
- Wrap your application in `BrowserRouter`:
135
- ```tsx
136
- import React from 'react';
137
- import ReactDOM from 'react-dom/client';
138
- import { BrowserRouter } from 'react-router-dom';
139
- import App from './App';
140
- import './index.css';
114
+ Add this to your main CSS file (`globals.css` or `index.css`):
141
115
 
142
- ReactDOM.createRoot(document.getElementById('root')!).render(
143
- <React.StrictMode>
144
- <BrowserRouter>
145
- <App />
146
- </BrowserRouter>
147
- </React.StrictMode>
148
- );
149
- ```
116
+ ```css
117
+ @import "tailwindcss";
118
+ @import "@unciatech/file-manager/styles";
150
119
 
151
- **2. Add Components & Routes (`src/App.tsx`)**
152
- ```tsx
153
- import { useState } from 'react';
154
- import { useNavigate, Routes, Route, Link } from 'react-router-dom';
155
- import { FileManager, FileManagerModal, MockProvider } from '@unciatech/file-manager';
120
+ @source "../node_modules/@unciatech/file-manager";
121
+ ```
156
122
 
157
- const provider = new MockProvider();
123
+ ---
158
124
 
159
- function FullPage() {
160
- const navigate = useNavigate();
161
- return (
162
- <div className="h-screen w-full flex flex-col">
163
- <div className="flex justify-between p-4 border-b">
164
- <h1 className="text-xl font-bold">Media Library</h1>
165
- <Link to="/" className="text-blue-600 hover:underline">Back to Demo</Link>
166
- </div>
167
- <div className="flex-1 overflow-hidden relative">
168
- <FileManager
169
- provider={provider}
170
- basePath="full"
171
- allowedFileTypes={["images", "videos", "audios", "files"]}
172
- onNavigate={(url, opts) => navigate(url, { replace: opts?.replace })}
173
- />
174
- </div>
175
- </div>
176
- );
177
- }
125
+ ### Tailwind v3
178
126
 
179
- function ModalDemo() {
180
- const [isOpen, setIsOpen] = useState(false);
181
- return (
182
- <div className="p-10 flex flex-col gap-4">
183
- <div className="flex gap-4">
184
- <button
185
- onClick={() => setIsOpen(true)}
186
- className="px-6 py-2 bg-blue-600 text-white rounded-md"
187
- >
188
- Open File Picker
189
- </button>
190
- <Link to="/full" className="px-6 py-2 bg-zinc-200 rounded-md">
191
- Go to Full Page View
192
- </Link>
193
- </div>
194
-
195
- <FileManagerModal
196
- open={isOpen}
197
- onClose={() => setIsOpen(false)}
198
- provider={provider}
199
- basePath="/"
200
- onFilesSelected={(files) => console.log(files)}
201
- />
202
- </div>
203
- );
204
- }
127
+ Update your `tailwind.config.ts`:
205
128
 
206
- export default function App() {
207
- return (
208
- <Routes>
209
- <Route path="/" element={<ModalDemo />} />
210
- <Route path="/full/*" element={<FullPage />} />
211
- </Routes>
212
- );
213
- }
129
+ ```ts
130
+ content: [
131
+ "./node_modules/@unciatech/file-manager/dist/**/*.{js,ts,jsx,tsx}",
132
+ ]
214
133
  ```
215
134
 
216
- </details>
135
+ ---
217
136
 
218
- <details open>
219
- <summary><b>Example B: Next.js (App Router)</b></summary>
220
- <br>
137
+ ## Option B - Install Tailwind CSS
221
138
 
222
- **1. Create the Full Page View (`app/media/[[...path]]/page.tsx`)**
223
- The `[[...path]]` catch-all route handles all folder navigations.
224
- ```tsx
225
- 'use client';
226
- import { useRouter } from 'next/navigation';
227
- import { FileManager, MockProvider } from '@unciatech/file-manager';
139
+ ```bash
140
+ npm install -D tailwindcss postcss autoprefixer
141
+ npx tailwindcss init -p
142
+ ```
228
143
 
229
- // Instantiate outside the component or in a context
230
- const provider = new MockProvider();
144
+ Update `tailwind.config.ts`:
145
+
146
+ ```ts
147
+ content: [
148
+ "./index.html",
149
+ "./src/**/*.{js,ts,jsx,tsx}",
150
+ "./node_modules/@unciatech/file-manager/dist/**/*.{js,ts,jsx,tsx}",
151
+ ]
152
+ ```
231
153
 
232
- export default function MediaLibraryPage() {
233
- const router = useRouter();
154
+ Add to `index.css` or `globals.css`:
234
155
 
235
- return (
236
- <div className="h-screen w-full flex flex-col">
237
- <div className="flex justify-between items-center p-4 border-b border-border">
238
- <h1 className="text-xl font-bold">Media Library</h1>
239
- </div>
240
- <div className="flex-1 relative overflow-hidden">
241
- <FileManager
242
- provider={provider}
243
- basePath="/media"
244
- allowedFileTypes={["images", "videos", "audios", "files"]}
245
- viewMode="grid"
246
- onNavigate={(url, opts) =>
247
- opts?.replace ? router.replace(url) : router.push(url)
248
- }
249
- />
250
- </div>
251
- </div>
252
- );
253
- }
156
+ ```css
157
+ @tailwind base;
158
+ @tailwind components;
159
+ @tailwind utilities;
160
+
161
+ @import "@unciatech/file-manager/styles";
254
162
  ```
255
163
 
256
- **2. Create the Modal Demo View (`app/modal-demo/page.tsx`)**
257
- ```tsx
258
- 'use client';
259
- import { useState } from 'react';
260
- import { FileManagerModal, MockProvider } from '@unciatech/file-manager';
164
+ ---
261
165
 
262
- const provider = new MockProvider();
166
+ # Step 4 - Create Your Custom API Provider
263
167
 
264
- export default function ModalDemoPage() {
265
- const [isOpen, setIsOpen] = useState(false);
168
+ > 💡 **Pro Tip - The Mock Provider**
169
+ > If you are just prototyping and don't have a backend ready yet, you can skip this step entirely.
170
+ >
171
+ > The library includes a fully functional `MockProvider` that simulates network latency and stores data in memory.
266
172
 
267
- return (
268
- <div className="p-10">
269
- <button
270
- onClick={() => setIsOpen(true)}
271
- className="px-6 py-2 bg-blue-600 text-white rounded-md"
272
- >
273
- Open File Picker
274
- </button>
275
-
276
- <FileManagerModal
277
- open={isOpen}
278
- onClose={() => setIsOpen(false)}
279
- provider={provider}
280
- basePath="/"
281
- onFilesSelected={(files) => {
282
- console.log("Selected:", files);
283
- setIsOpen(false);
284
- }}
285
- />
286
- </div>
287
- );
288
- }
173
+ Example:
174
+
175
+ ```tsx
176
+ import { FileManager, MockProvider } from "@unciatech/file-manager"
177
+
178
+ const provider = new MockProvider()
179
+
180
+ <FileManager provider={provider} basePath="/" viewMode="grid" />
289
181
  ```
290
- </details>
291
182
 
292
183
  ---
293
184
 
294
- ## 🔀 Framework Router Integration
185
+ ### Using Your Own Backend
295
186
 
296
- By default, the file manager uses the browser's native `history.pushState` API for navigation — no full page reloads, no dependencies. This works out of the box in any bare React app (Vite, CRA, etc.).
187
+ The file manager is **backend-agnostic**.
297
188
 
298
- However, if your app already has its own router (React Router, Next.js, TanStack Router), you should pass the `onNavigate` prop so the file manager delegates all navigation to your router instead of calling `history.pushState` directly. This keeps your router's internal state in sync.
189
+ Create a class implementing `IFileManagerProvider`.
299
190
 
300
- ### React Router v6
191
+ ```ts
192
+ import {
193
+ IFileManagerProvider,
194
+ FolderId,
195
+ FileUploadInput,
196
+ } from "@unciatech/file-manager"
301
197
 
302
- ```tsx
303
- import { useNavigate } from 'react-router-dom';
198
+ export class MyCustomApiProvider implements IFileManagerProvider {
199
+ private baseUrl = "https://api.mybackend.com/v1"
304
200
 
305
- function MyPage() {
306
- const navigate = useNavigate();
201
+ async getFolders(folderId: FolderId, page = 1, limit = 24) {
202
+ const parentQuery = folderId ? `&parentId=${folderId}` : "&isRoot=true"
307
203
 
308
- return (
309
- <FileManager
310
- provider={provider}
311
- allowedFileTypes={['images', 'videos']}
312
- onNavigate={(url) => navigate(url)}
313
- basePath="/media"
314
- />
315
- );
316
- }
317
- ```
204
+ const res = await fetch(
205
+ `${this.baseUrl}/folders?page=${page}&limit=${limit}${parentQuery}`
206
+ )
318
207
 
319
- ### Next.js (App Router)
208
+ if (!res.ok) throw new Error("Failed to fetch folders")
320
209
 
321
- ```tsx
322
- 'use client';
323
- import { useRouter } from 'next/navigation';
210
+ const data = await res.json()
324
211
 
325
- export default function MediaPage() {
326
- const router = useRouter();
212
+ return {
213
+ folders: data.folders,
214
+ pagination: data.pagination,
215
+ }
216
+ }
327
217
 
328
- return (
329
- <FileManager
330
- provider={provider}
331
- allowedFileTypes={['images', 'videos']}
332
- onNavigate={(url) => router.push(url)}
333
- basePath="/media"
334
- />
335
- );
336
- }
337
- ```
218
+ async uploadFiles(filesInput: FileUploadInput[], folderId?: FolderId) {
219
+ const formData = new FormData()
338
220
 
339
- ### TanStack Router
221
+ if (folderId) {
222
+ formData.append("folderId", String(folderId))
223
+ }
340
224
 
341
- ```tsx
342
- import { useRouter } from '@tanstack/react-router';
225
+ filesInput.forEach(({ file }) => {
226
+ formData.append("files", file)
227
+ })
343
228
 
344
- function MyPage() {
345
- const router = useRouter();
229
+ const res = await fetch(`${this.baseUrl}/upload`, {
230
+ method: "POST",
231
+ body: formData,
232
+ })
346
233
 
347
- return (
348
- <FileManager
349
- provider={provider}
350
- allowedFileTypes={['images', 'videos']}
351
- onNavigate={(url) => router.navigate({ href: url })}
352
- basePath="/media"
353
- />
354
- );
234
+ return res.json()
235
+ }
355
236
  }
356
237
  ```
357
238
 
358
- ```tsx
359
- <FileManager
360
- provider={provider}
361
- allowedFileTypes={['images', 'videos']}
362
- basePath="/media"
363
- // no onNavigate needed
364
- />
365
- ```
366
-
367
- ## 🎮 Playgrounds & Reference Implementations
239
+ ---
368
240
 
369
- For complete, working examples of how to integrate the File Manager into different application architectures, check out the [playgrounds](file:///Users/avi/Developer/Uncia/file-manager/playgrounds) directory. These are fully functional Vite-based projects that demonstrate real-world integration patterns.
241
+ # 🔀 Router Integration
370
242
 
371
- - **[React Router v7 Playground](file:///Users/avi/Developer/Uncia/file-manager/playgrounds/test-react-router)**: Demonstrates integration with `react-router-dom` using `useNavigate` and route-based navigation.
372
- - **[TanStack Router Playground](file:///Users/avi/Developer/Uncia/file-manager/playgrounds/test-tanstack)**: Demonstrates integration with `@tanstack/react-router` using the `router` object and `href` based navigation.
243
+ By default the file manager uses `history.pushState`.
373
244
 
374
- These playgrounds are a great starting point for seeing how to handle:
375
- - Styling with Tailwind CSS v4
376
- - Mapping `onNavigate` to your framework's router
377
- - Using the `MockProvider` for rapid prototyping
378
- - Configuring `FileManagerModal` v/s full-page `FileManager`
245
+ If your app uses a router, pass the `onNavigate` prop.
379
246
 
380
247
  ---
381
248
 
382
- > [!NOTE]
383
- > The `onNavigate` prop is also available on `<FileManagerModal>` for modal mode.
249
+ # Example - React Router
250
+
251
+ ### Install
252
+
253
+ ```bash
254
+ npm install react-router-dom
255
+ ```
256
+
257
+ ### main.tsx
258
+
259
+ ```tsx
260
+ import React from "react"
261
+ import ReactDOM from "react-dom/client"
262
+ import { BrowserRouter } from "react-router-dom"
263
+ import App from "./App"
264
+ import "./index.css"
265
+
266
+ ReactDOM.createRoot(document.getElementById("root")!).render(
267
+ <React.StrictMode>
268
+ <BrowserRouter>
269
+ <App />
270
+ </BrowserRouter>
271
+ </React.StrictMode>
272
+ )
273
+ ```
384
274
 
385
275
  ---
386
276
 
387
- ## 💾 Database Schema Design
277
+ # Example - TanStack Router
388
278
 
389
- Because this application relies heavily on tree structures (Folders inside Folders) and varied JSON metadata (Video durations vs Document page counts), using a relational database with JSONB support (like PostgreSQL) is highly recommended.
279
+ ### Install
390
280
 
391
- Below are production-ready schema examples using **Prisma** and **Drizzle ORM**.
281
+ ```bash
282
+ npm install @tanstack/react-router
283
+ ```
392
284
 
393
- ### Option A: Prisma Schema (PostgreSQL/MySQL)
285
+ ### main.tsx
394
286
 
395
- ```prisma
396
- generator client {
397
- provider = "prisma-client-js"
287
+ ```tsx
288
+ import React from "react"
289
+ import ReactDOM from "react-dom/client"
290
+ import {
291
+ RouterProvider,
292
+ createRouter,
293
+ createRoute,
294
+ createRootRoute,
295
+ } from "@tanstack/react-router"
296
+
297
+ import App, { ModalPage } from "./App"
298
+ import "./index.css"
299
+
300
+ const rootRoute = createRootRoute()
301
+
302
+ const indexRoute = createRoute({
303
+ getParentRoute: () => rootRoute,
304
+ path: "/",
305
+ component: ModalPage,
306
+ })
307
+
308
+ const fullRoute = createRoute({
309
+ getParentRoute: () => rootRoute,
310
+ path: "/full",
311
+ component: App,
312
+ })
313
+
314
+ const catchAllRoute = createRoute({
315
+ getParentRoute: () => rootRoute,
316
+ path: "$",
317
+ component: App,
318
+ })
319
+
320
+ const routeTree = rootRoute.addChildren([
321
+ indexRoute,
322
+ fullRoute,
323
+ catchAllRoute,
324
+ ])
325
+
326
+ const router = createRouter({ routeTree })
327
+
328
+ declare module "@tanstack/react-router" {
329
+ interface Register {
330
+ router: typeof router
331
+ }
398
332
  }
399
333
 
400
- datasource db {
401
- provider = "postgresql" // or "mysql"
402
- url = env("DATABASE_URL")
403
- }
334
+ ReactDOM.createRoot(document.getElementById("root")!).render(
335
+ <React.StrictMode>
336
+ <RouterProvider router={router} />
337
+ </React.StrictMode>
338
+ )
339
+ ```
404
340
 
341
+ ---
342
+
343
+ # 🗄️ Database Schema (Optional)
344
+
345
+ Using **PostgreSQL** with JSON support is recommended.
346
+
347
+ Example Prisma schema:
348
+
349
+ ```prisma
405
350
  model Folder {
406
- id Int @id @default(autoincrement())
351
+ id Int @id @default(autoincrement())
407
352
  name String
408
-
409
- // Self-referencing relationship for infinite folder depth
410
- parentId Int?
411
- parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id])
412
- children Folder[] @relation("FolderHierarchy")
413
-
414
- // Cached counts for fast UI rendering
415
- folderCount Int @default(0)
416
- fileCount Int @default(0)
417
-
418
- // Path optimization
419
- pathId Int @default(0)
420
- path String? // e.g. "/1/5/12"
421
-
422
- createdAt DateTime @default(now())
423
- updatedAt DateTime @updatedAt
424
-
353
+ parentId Int?
354
+
355
+ folderCount Int @default(0)
356
+ fileCount Int @default(0)
357
+
358
+ pathId Int @default(0)
359
+ path String?
360
+
361
+ createdAt DateTime @default(now())
362
+ updatedAt DateTime @updatedAt
363
+
425
364
  files File[]
426
365
 
427
366
  @@index([parentId])
428
367
  }
429
368
 
430
369
  model File {
431
- id Int @id @default(autoincrement())
432
- name String
433
-
434
- // Foreign Key to Folder
435
- folderId Int?
436
- folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
437
- folderPath String?
438
-
439
- // Core Asset Data
440
- size Int
441
- url String
442
- previewUrl String? // Lightweight thumbnail URL
443
- mime String // e.g. "image/jpeg", "video/mp4"
444
- ext String?
445
- hash String?
446
-
447
- // Common Media Details
448
- alternativeText String?
449
- caption String?
450
- width Int?
451
- height Int?
452
-
453
- // JSONB storage for flexible metadata
454
- // (e.g., formats: { thumbnail: {...}, small: {...} })
455
- formats Json?
456
- // (e.g., metaData: { duration: 120, bitrate: 320, pageCount: 15 })
457
- metaData Json?
458
-
459
- createdAt DateTime @default(now())
460
- updatedAt DateTime @updatedAt
461
-
462
- @@index([folderId])
370
+ id Int @id @default(autoincrement())
371
+ name String
372
+ folderId Int?
373
+
374
+ size Int
375
+ url String
376
+ mime String
377
+
378
+ formats Json?
379
+ metaData Json?
380
+
381
+ createdAt DateTime @default(now())
382
+ updatedAt DateTime @updatedAt
463
383
  }
464
384
  ```
465
385
 
386
+ ---
466
387
 
467
- ### Option B: Drizzle ORM Schema (PostgreSQL)
468
-
469
- If you prefer a lighter, TypeScript-first approach using Drizzle:
470
-
471
- ```typescript
472
- import { pgTable, serial, varchar, integer, timestamp, jsonb, text } from "drizzle-orm/pg-core";
473
-
474
- export const folders = pgTable("folders", {
475
- id: serial("id").primaryKey(),
476
- name: varchar("name", { length: 255 }).notNull(),
477
-
478
- // Nullable parentId allows root-level folders
479
- parentId: integer("parent_id"), // Needs recursive FK setup in relations
480
-
481
- folderCount: integer("folder_count").default(0),
482
- fileCount: integer("file_count").default(0),
483
-
484
- pathId: integer("path_id").default(0),
485
- path: varchar("path", { length: 255 }),
486
-
487
- createdAt: timestamp("created_at").defaultNow().notNull(),
488
- updatedAt: timestamp("updated_at").defaultNow().notNull(),
489
- });
490
-
491
- export const files = pgTable("files", {
492
- id: serial("id").primaryKey(),
493
- name: varchar("name", { length: 255 }).notNull(),
494
-
495
- folderId: integer("folder_id").references(() => folders.id, { onDelete: "cascade" }),
496
- folderPath: varchar("folder_path", { length: 255 }),
497
-
498
- size: integer("size").notNull(),
499
- url: text("url").notNull(),
500
- previewUrl: text("preview_url"),
501
- mime: varchar("mime", { length: 100 }).notNull(),
502
- ext: varchar("ext", { length: 20 }),
503
- hash: varchar("hash", { length: 255 }),
504
-
505
- alternativeText: text("alternative_text"),
506
- caption: text("caption"),
507
- width: integer("width"),
508
- height: integer("height"),
509
-
510
- // JSONB is perfect for storing our dynamic metadata & responsive formats
511
- formats: jsonb("formats"),
512
- metaData: jsonb("meta_data"),
513
-
514
- createdAt: timestamp("created_at").defaultNow().notNull(),
515
- updatedAt: timestamp("updated_at").defaultNow().notNull(),
516
- });
517
- ```
388
+ # ⚠️ Status
389
+
390
+ > This library is currently in **BETA**.
391
+
392
+ Please report bugs or feature requests through **GitHub Issues**.
393
+
394
+ ---
395
+
396
+ # License
397
+
398
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unciatech/file-manager",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "Modern file manager component for React applications",
5
5
  "author": "Avi",
6
6
  "license": "MIT",