@blitheforge/media-library 1.0.5
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.
- package/LICENSE +21 -0
- package/README.md +558 -0
- package/dist/index.cjs +1440 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +1387 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +2 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Blitheforge
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
# @blitheforge/media-library
|
|
2
|
+
|
|
3
|
+
Production-ready React media library with nested folders, drag-and-drop upload, search, RBAC-driven UI, toast notifications, and configurable API URLs. Built with Tailwind CSS — works in Next.js, Vite, or any React 18+ app.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Nested folder management** — browse, create, and delete folders
|
|
8
|
+
- **File upload** — click to upload or drag-and-drop; files upload one-by-one with live preview cards in the grid
|
|
9
|
+
- **5 MB client-side limit** — oversized files show a warning toast and are skipped (configurable via `MAX_MEDIA_UPLOAD_BYTES`)
|
|
10
|
+
- **Search** — filter files in the current folder
|
|
11
|
+
- **RBAC / capabilities** — upload, create-folder, and delete UI is driven by your API `capabilities` response
|
|
12
|
+
- **Delete confirmation** — custom confirm dialog (not native `confirm()`)
|
|
13
|
+
- **Toast notifications** — success, error, and warning toasts (top-right inside the panel/modal)
|
|
14
|
+
- **Responsive** — mobile folder drawer, full-screen modal on small screens
|
|
15
|
+
- **Theme sync** — inherits host light/dark mode via CSS variables (`theme="sync"`)
|
|
16
|
+
- **Three display modes** — form pickers (`MediaPicker`), modal (`MediaLibraryModal`), embedded widget (`MediaLibraryWidget`)
|
|
17
|
+
- **Type-safe headless client** — `createMediaLibraryClient` for custom integrations
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @blitheforge/media-library
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Peer dependencies:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install react react-dom
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
### 1. Import styles once
|
|
38
|
+
|
|
39
|
+
In your app entry (e.g. `app/layout.tsx` or `main.tsx`):
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import "@blitheforge/media-library/styles.css";
|
|
43
|
+
import "./globals.css";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Import the library **before** your globals. Library utilities are scoped to a lower CSS layer so they will not override your app's responsive classes (e.g. `lg:block`).
|
|
47
|
+
|
|
48
|
+
Do **not** add `@source` for this package in your app Tailwind config.
|
|
49
|
+
|
|
50
|
+
### 2. Next.js (App Router)
|
|
51
|
+
|
|
52
|
+
Add the package to `transpilePackages` in `next.config.ts`:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const nextConfig = {
|
|
56
|
+
transpilePackages: ["@blitheforge/media-library"]
|
|
57
|
+
};
|
|
58
|
+
export default nextConfig;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Monorepo / workspace
|
|
62
|
+
|
|
63
|
+
Link the local package via pnpm workspace:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
# pnpm-workspace.yaml
|
|
67
|
+
packages:
|
|
68
|
+
- "Blitheforge-media-library"
|
|
69
|
+
- "."
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
// package.json
|
|
74
|
+
{
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@blitheforge/media-library": "workspace:*"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Rebuild after package changes:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cd Blitheforge-media-library && npm run build
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Publishing
|
|
90
|
+
|
|
91
|
+
Pushes to `main` run `.github/workflows/publish.yml`. The workflow
|
|
92
|
+
typechecks and builds the package, then publishes it only when the version in
|
|
93
|
+
`package.json` is not already present on npm.
|
|
94
|
+
|
|
95
|
+
Before the first automated publish:
|
|
96
|
+
|
|
97
|
+
1. Create an npm granular access token with **Bypass 2FA** enabled and
|
|
98
|
+
**Read and write** package permission for the `@blitheforge` scope.
|
|
99
|
+
2. In the GitHub repository, open **Settings > Secrets and variables >
|
|
100
|
+
Actions**.
|
|
101
|
+
3. Add the token as a repository secret named `NPM_TOKEN`.
|
|
102
|
+
|
|
103
|
+
For each release, bump the package version and push to `main`:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm version patch
|
|
107
|
+
git push --follow-tags
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Theming
|
|
113
|
+
|
|
114
|
+
Default is **`theme="sync"`** — the library reads your app's CSS variables:
|
|
115
|
+
|
|
116
|
+
| Host variable | Used for |
|
|
117
|
+
|---------------|----------|
|
|
118
|
+
| `--background` | Page background |
|
|
119
|
+
| `--foreground` | Text |
|
|
120
|
+
| `--surface` | Panel/card background |
|
|
121
|
+
| `--border` | Borders |
|
|
122
|
+
| `--primary` | Buttons, active states |
|
|
123
|
+
| `--destructive` | Delete actions |
|
|
124
|
+
| `--accent` | Hover/secondary surfaces |
|
|
125
|
+
| `--warning` | Warning toasts |
|
|
126
|
+
|
|
127
|
+
When your site toggles dark mode (e.g. `[data-theme="dark"]` on `<html>`), the media library follows automatically.
|
|
128
|
+
|
|
129
|
+
**Standalone** (no shared design tokens):
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<MediaPicker name="image" theme="light" />
|
|
133
|
+
<MediaPicker name="image" theme="dark" />
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or via config:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<MediaPicker name="image" config={{ theme: "sync" }} />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Theme modes: `"sync"` | `"light"` | `"dark"`
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Components
|
|
147
|
+
|
|
148
|
+
| Export | Use case |
|
|
149
|
+
|--------|----------|
|
|
150
|
+
| `MediaPicker` | Single file field for forms — opens modal, shows live preview |
|
|
151
|
+
| `MediaPickerMulti` | Multiple attachments with preview cards |
|
|
152
|
+
| `MediaLibraryModal` | Full-screen/modal picker with Done button |
|
|
153
|
+
| `MediaLibraryWidget` | Embedded admin panel — fixed width/height, no overlay |
|
|
154
|
+
| `MediaLibraryPanel` | Low-level panel (`variant="modal"` or `"embedded"`) |
|
|
155
|
+
| `MediaPreview` | Standalone image/PDF preview block |
|
|
156
|
+
| `createMediaLibraryClient` | Headless API client |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## MediaPicker — single file (forms)
|
|
161
|
+
|
|
162
|
+
Best for donation proofs, expense vouchers, profile photos, etc.
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { MediaPicker } from "@blitheforge/media-library";
|
|
166
|
+
|
|
167
|
+
export function DonationForm() {
|
|
168
|
+
return (
|
|
169
|
+
<form>
|
|
170
|
+
<MediaPicker
|
|
171
|
+
name="proofUrl"
|
|
172
|
+
label="Donation proof"
|
|
173
|
+
accept={["image", "pdf"]}
|
|
174
|
+
onChange={(path) => console.log(path)}
|
|
175
|
+
/>
|
|
176
|
+
</form>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Props: `name`, `label`, `value`, `defaultValue`, `onChange`, `accept`, `config`, `theme`, `title`, `description`, `className`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## MediaPickerMulti — multiple attachments
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import { MediaPickerMulti } from "@blitheforge/media-library";
|
|
189
|
+
|
|
190
|
+
<MediaPickerMulti
|
|
191
|
+
name="attachments"
|
|
192
|
+
label="Attachments"
|
|
193
|
+
max={5}
|
|
194
|
+
accept={["image", "pdf"]}
|
|
195
|
+
onChange={(paths) => console.log(paths)}
|
|
196
|
+
/>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## MediaLibraryModal — picker modal
|
|
202
|
+
|
|
203
|
+
Use when you need a custom trigger but still want the picker flow (select file → Done).
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { useState } from "react";
|
|
207
|
+
import { MediaLibraryModal } from "@blitheforge/media-library";
|
|
208
|
+
|
|
209
|
+
function CustomTrigger() {
|
|
210
|
+
const [open, setOpen] = useState(false);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
<button type="button" onClick={() => setOpen(true)}>Browse media</button>
|
|
215
|
+
<MediaLibraryModal
|
|
216
|
+
open={open}
|
|
217
|
+
onClose={() => setOpen(false)}
|
|
218
|
+
onSelect={(file) => {
|
|
219
|
+
console.log(file.url);
|
|
220
|
+
setOpen(false);
|
|
221
|
+
}}
|
|
222
|
+
accept={["image", "pdf"]}
|
|
223
|
+
/>
|
|
224
|
+
</>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Modal behavior:
|
|
230
|
+
- Portal overlay with backdrop blur
|
|
231
|
+
- Escape key closes (unless delete confirm is open)
|
|
232
|
+
- Mobile: full viewport height; desktop: centered dialog
|
|
233
|
+
- Footer with **Selected** label and **Done** button
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## MediaLibraryWidget — embedded admin panel
|
|
238
|
+
|
|
239
|
+
Use for a dedicated **Media Library** admin page — no modal, no overlay. Folders and files are always visible.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
import { MediaLibraryWidget } from "@blitheforge/media-library";
|
|
243
|
+
|
|
244
|
+
export function MediaAdminPage() {
|
|
245
|
+
return (
|
|
246
|
+
<MediaLibraryWidget
|
|
247
|
+
width="100%"
|
|
248
|
+
height="calc(100vh - 200px)"
|
|
249
|
+
title="Media Library"
|
|
250
|
+
description="Create folders, upload files, and manage media."
|
|
251
|
+
accept={["image", "pdf"]}
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Width & height props
|
|
258
|
+
|
|
259
|
+
| Prop | Type | Default | Examples |
|
|
260
|
+
|------|------|---------|----------|
|
|
261
|
+
| `width` | `string \| number` | `"100%"` | `800`, `"70vw"`, `"100%"` |
|
|
262
|
+
| `height` | `string \| number` | `640` | `720`, `"70vh"`, `"calc(100vh - 200px)"` |
|
|
263
|
+
|
|
264
|
+
- **Numbers** → pixels (`720` → `720px`)
|
|
265
|
+
- **Strings** → passed as CSS values (`"100%"`, `"70vh"`)
|
|
266
|
+
- **`calc()`** — use proper CSS syntax with spaces: `"calc(100vh - 200px)"`
|
|
267
|
+
- **Shorthand** — `"100vh-200px"` is auto-converted to `calc(100vh - 200px)`
|
|
268
|
+
|
|
269
|
+
### Widget vs modal
|
|
270
|
+
|
|
271
|
+
| | `MediaLibraryWidget` | `MediaLibraryModal` |
|
|
272
|
+
|--|----------------------|---------------------|
|
|
273
|
+
| Display | Inline on page | Overlay / portal |
|
|
274
|
+
| Close button | No | Yes |
|
|
275
|
+
| Done footer | Hidden by default | Always shown |
|
|
276
|
+
| Loads | On mount | When `open={true}` |
|
|
277
|
+
| Best for | Admin manage page | Form file picking |
|
|
278
|
+
|
|
279
|
+
### Selectable widget (picker in embedded mode)
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
<MediaLibraryWidget
|
|
283
|
+
width={900}
|
|
284
|
+
height={600}
|
|
285
|
+
selectable
|
|
286
|
+
onSelect={(file) => console.log(file.path)}
|
|
287
|
+
/>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## MediaLibraryPanel — low-level
|
|
293
|
+
|
|
294
|
+
Build custom layouts by composing the panel directly.
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { MediaLibraryPanel } from "@blitheforge/media-library";
|
|
298
|
+
|
|
299
|
+
<MediaLibraryPanel
|
|
300
|
+
active
|
|
301
|
+
variant="embedded"
|
|
302
|
+
selectable={false}
|
|
303
|
+
title="Files"
|
|
304
|
+
accept={["image"]}
|
|
305
|
+
className="h-full"
|
|
306
|
+
/>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Props: `active`, `variant` (`"modal"` | `"embedded"`), `selectable`, `onClose`, `onSelect`, `config`, `theme`, `title`, `description`, `accept`, `className`
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## MediaPreview
|
|
314
|
+
|
|
315
|
+
Standalone preview for a stored path/URL:
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { MediaPreview } from "@blitheforge/media-library";
|
|
319
|
+
|
|
320
|
+
<MediaPreview path="/uploads/media/photo.png" alt="Donation proof" />
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Configure API URLs
|
|
326
|
+
|
|
327
|
+
All components accept an optional `config` prop:
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
<MediaPicker
|
|
331
|
+
name="imageUrl"
|
|
332
|
+
config={{
|
|
333
|
+
listUrl: "/api/media",
|
|
334
|
+
uploadUrl: "/api/media/upload",
|
|
335
|
+
createFolderUrl: "/api/media/folders",
|
|
336
|
+
updateUrl: "/api/media",
|
|
337
|
+
deleteUrl: "/api/media",
|
|
338
|
+
rootLabel: "Root",
|
|
339
|
+
theme: "sync"
|
|
340
|
+
}}
|
|
341
|
+
/>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Default URLs (relative to your app):
|
|
345
|
+
|
|
346
|
+
| Key | Default |
|
|
347
|
+
|-----|---------|
|
|
348
|
+
| `listUrl` | `/api/media` |
|
|
349
|
+
| `uploadUrl` | `/api/media/upload` |
|
|
350
|
+
| `createFolderUrl` | `/api/media/folders` |
|
|
351
|
+
| `updateUrl` | `/api/media` |
|
|
352
|
+
| `deleteUrl` | `/api/media` |
|
|
353
|
+
| `rootLabel` | `"Root"` |
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Backend API contract
|
|
358
|
+
|
|
359
|
+
All responses must follow:
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{ "success": true, "data": ... }
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Errors:
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"success": false,
|
|
370
|
+
"error": { "code": "VALIDATION_ERROR", "message": "Human readable message" }
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### List — `GET {listUrl}?path=&q=`
|
|
375
|
+
|
|
376
|
+
Query params:
|
|
377
|
+
- `path` — current folder path (empty = root)
|
|
378
|
+
- `q` — search query (optional)
|
|
379
|
+
|
|
380
|
+
Response:
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"success": true,
|
|
385
|
+
"data": {
|
|
386
|
+
"path": "donations",
|
|
387
|
+
"folders": [
|
|
388
|
+
{ "name": "2026", "path": "donations/2026" }
|
|
389
|
+
],
|
|
390
|
+
"files": [
|
|
391
|
+
{
|
|
392
|
+
"name": "receipt.png",
|
|
393
|
+
"path": "donations/receipt.png",
|
|
394
|
+
"url": "/uploads/media/donations/receipt.png",
|
|
395
|
+
"size": 12345,
|
|
396
|
+
"mimeType": "image/png",
|
|
397
|
+
"updatedAt": "2026-06-06T00:00:00.000Z"
|
|
398
|
+
}
|
|
399
|
+
],
|
|
400
|
+
"capabilities": {
|
|
401
|
+
"view": true,
|
|
402
|
+
"upload": true,
|
|
403
|
+
"createFolder": true,
|
|
404
|
+
"delete": true,
|
|
405
|
+
"rename": true,
|
|
406
|
+
"select": true
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Capabilities (RBAC)
|
|
413
|
+
|
|
414
|
+
The UI reads `capabilities` from every list response to show or hide actions:
|
|
415
|
+
|
|
416
|
+
| Capability | Controls |
|
|
417
|
+
|------------|----------|
|
|
418
|
+
| `view` | Access to browse (required) |
|
|
419
|
+
| `upload` | Upload button + drag-and-drop |
|
|
420
|
+
| `createFolder` | Folder create form in sidebar |
|
|
421
|
+
| `delete` | Delete buttons on files/folders |
|
|
422
|
+
| `rename` | Reserved for future rename UI |
|
|
423
|
+
| `select` | File selection (picker flows) |
|
|
424
|
+
|
|
425
|
+
If `capabilities` is omitted, all actions default to enabled.
|
|
426
|
+
|
|
427
|
+
### Upload — `POST {uploadUrl}`
|
|
428
|
+
|
|
429
|
+
`multipart/form-data`:
|
|
430
|
+
- `path` — target folder path (empty string = root)
|
|
431
|
+
- `files` — one or more files
|
|
432
|
+
|
|
433
|
+
The UI uploads **one file at a time** sequentially and shows a preview card per file in the grid.
|
|
434
|
+
|
|
435
|
+
Recommended server limit: **5 MB per file** (matches client-side check).
|
|
436
|
+
|
|
437
|
+
### Create folder — `POST {createFolderUrl}`
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"path": "donations",
|
|
442
|
+
"name": "2026",
|
|
443
|
+
"nested": true
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- `nested: true` — create inside `path`
|
|
448
|
+
- `nested: false` — create at root level
|
|
449
|
+
|
|
450
|
+
### Rename — `PATCH {updateUrl}`
|
|
451
|
+
|
|
452
|
+
```json
|
|
453
|
+
{
|
|
454
|
+
"path": "donations/old.png",
|
|
455
|
+
"newName": "new.png",
|
|
456
|
+
"type": "file"
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
`type`: `"file"` | `"folder"`
|
|
461
|
+
|
|
462
|
+
### Delete — `DELETE {deleteUrl}`
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"path": "donations/old.png",
|
|
467
|
+
"type": "file"
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Deleting a folder should remove all nested content on the server.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Headless client
|
|
476
|
+
|
|
477
|
+
Use without UI for scripts, tests, or custom interfaces:
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
import { createMediaLibraryClient } from "@blitheforge/media-library";
|
|
481
|
+
|
|
482
|
+
const client = createMediaLibraryClient({
|
|
483
|
+
listUrl: "/api/media",
|
|
484
|
+
uploadUrl: "/api/media/upload",
|
|
485
|
+
createFolderUrl: "/api/media/folders",
|
|
486
|
+
updateUrl: "/api/media",
|
|
487
|
+
deleteUrl: "/api/media"
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const listing = await client.list("donations", "receipt");
|
|
491
|
+
await client.createFolder("", "archive", true);
|
|
492
|
+
await client.uploadOne("donations", file);
|
|
493
|
+
await client.rename("donations/old.png", "new.png", "file");
|
|
494
|
+
await client.remove("donations/old.png", "file");
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Utility exports
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
import {
|
|
501
|
+
MAX_MEDIA_UPLOAD_BYTES, // 5 * 1024 * 1024
|
|
502
|
+
isFileWithinUploadSizeLimit,
|
|
503
|
+
formatUploadSizeLimit, // "5 MB"
|
|
504
|
+
fileMatchesAccept,
|
|
505
|
+
fileMatchesAcceptForUpload,
|
|
506
|
+
fileNameFromPath,
|
|
507
|
+
isImagePath,
|
|
508
|
+
bfmlRootProps,
|
|
509
|
+
resolveThemeMode
|
|
510
|
+
} from "@blitheforge/media-library";
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## Upload behavior
|
|
516
|
+
|
|
517
|
+
1. User selects files (click or drag-and-drop)
|
|
518
|
+
2. Client validates type (`accept` prop) and size (5 MB default)
|
|
519
|
+
3. Oversized files → warning toast, skipped
|
|
520
|
+
4. Invalid type → error toast, skipped
|
|
521
|
+
5. Valid files → preview cards appear in the file grid
|
|
522
|
+
6. Each file uploads sequentially; card removed on success
|
|
523
|
+
7. Folder list refreshes silently after each successful upload
|
|
524
|
+
8. Success toast when batch completes
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## File type filtering
|
|
529
|
+
|
|
530
|
+
Pass `accept` to restrict allowed types:
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
accept={["image"]} // images only
|
|
534
|
+
accept={["pdf"]} // PDF only
|
|
535
|
+
accept={["image", "pdf"]} // both (default for admin widget)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Omit `accept` to allow all types returned by the API.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## TypeScript types
|
|
543
|
+
|
|
544
|
+
```tsx
|
|
545
|
+
import type {
|
|
546
|
+
MediaFile,
|
|
547
|
+
MediaFolder,
|
|
548
|
+
MediaListing,
|
|
549
|
+
MediaCapabilities,
|
|
550
|
+
MediaLibraryConfig,
|
|
551
|
+
MediaLibraryModalProps,
|
|
552
|
+
MediaLibraryWidgetProps,
|
|
553
|
+
MediaLibraryPanelProps,
|
|
554
|
+
MediaPickerProps,
|
|
555
|
+
MediaPickerMultiProps,
|
|
556
|
+
MediaLibraryThemeMode
|
|
557
|
+
} from "@blitheforge/media-library";
|
|
558
|
+
```
|