@caprionlinesrl/puck-plugin-media 0.1.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Capri On Line S.r.l.
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,422 @@
1
+ # @caprionlinesrl/puck-plugin-media
2
+
3
+ A Puck plugin for media management with support for images, galleries, and documents.
4
+
5
+ ## Features
6
+
7
+ - **Image Field** - Select images with upload, multilingual alt text, and bulk delete
8
+ - **Gallery Field** - Create and manage image galleries with drag & drop upload
9
+ - **Document Field** - Upload and manage documents with multilingual titles
10
+ - **Media Panel** - Browse and manage all media directly in Puck's sidebar
11
+ - **Upload Support** - Drag & drop with progress tracking and file validation
12
+ - **Multilingual** - Alt text and titles in multiple languages
13
+ - **Search & Pagination** - Filter and load more items
14
+ - **Manage Mode** - Bulk select and delete items
15
+ - **Fully Typed** - Complete TypeScript support
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @caprionlinesrl/puck-plugin-media
21
+ # or
22
+ yarn add @caprionlinesrl/puck-plugin-media
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### 1. Create the plugin
28
+
29
+ ```tsx
30
+ import { createMediaPlugin } from '@caprionlinesrl/puck-plugin-media';
31
+
32
+ const mediaPlugin = createMediaPlugin({
33
+ languages: [
34
+ { code: 'en', label: 'English' },
35
+ { code: 'it', label: 'Italiano' },
36
+ ],
37
+
38
+ image: {
39
+ fetchList: async ({ query, page, pageSize }) => {
40
+ const res = await fetch(`/api/images?q=${query || ''}&page=${page}&limit=${pageSize}`);
41
+ const data = await res.json();
42
+ return { items: data.items, hasMore: data.hasMore };
43
+ },
44
+ upload: async (file, { onProgress }) => {
45
+ // Upload implementation with progress callback
46
+ const formData = new FormData();
47
+ formData.append('file', file);
48
+ const res = await fetch('/api/images/upload', { method: 'POST', body: formData });
49
+ return res.json();
50
+ },
51
+ update: async (id, data) => {
52
+ const res = await fetch(`/api/images/${id}`, {
53
+ method: 'PATCH',
54
+ body: JSON.stringify(data),
55
+ });
56
+ return res.json();
57
+ },
58
+ delete: async (id) => {
59
+ await fetch(`/api/images/${id}`, { method: 'DELETE' });
60
+ },
61
+ },
62
+
63
+ // Optional: Gallery support
64
+ gallery: {
65
+ fetchList: async (params) => { /* ... */ },
66
+ fetch: async (id) => { /* ... */ },
67
+ create: async (name) => { /* ... */ },
68
+ delete: async (id) => { /* ... */ },
69
+ upload: async (galleryId, file, callbacks) => { /* ... */ },
70
+ removeImage: async (galleryId, imageId) => { /* ... */ },
71
+ updateImage: async (galleryId, imageId, data) => { /* ... */ },
72
+ },
73
+
74
+ // Optional: Document support
75
+ document: {
76
+ fetchList: async (params) => { /* ... */ },
77
+ upload: async (file, callbacks) => { /* ... */ },
78
+ update: async (id, data) => { /* ... */ },
79
+ delete: async (id) => { /* ... */ },
80
+ },
81
+ });
82
+ ```
83
+
84
+ ### 2. Add to Puck
85
+
86
+ ```tsx
87
+ import { Puck } from '@puckeditor/core';
88
+ import '@caprionlinesrl/puck-plugin-media/styles.css';
89
+
90
+ <Puck
91
+ config={config}
92
+ data={pageData}
93
+ plugins={[mediaPlugin]}
94
+ />
95
+ ```
96
+
97
+ ### 3. Use fields in your blocks
98
+
99
+ ```tsx
100
+ const config = {
101
+ components: {
102
+ Hero: {
103
+ fields: {
104
+ backgroundImage: {
105
+ type: 'image',
106
+ label: 'Background Image',
107
+ },
108
+ title: {
109
+ type: 'text',
110
+ label: 'Title',
111
+ },
112
+ },
113
+ render: ({ backgroundImage, title }) => (
114
+ <div style={{ backgroundImage: `url(${backgroundImage?.url})` }}>
115
+ <h1>{title}</h1>
116
+ </div>
117
+ ),
118
+ },
119
+
120
+ PhotoGallery: {
121
+ fields: {
122
+ gallery: {
123
+ type: 'gallery',
124
+ label: 'Photo Gallery',
125
+ },
126
+ },
127
+ render: ({ gallery }) => (
128
+ <div className="gallery">
129
+ {gallery?.images.map((img) => (
130
+ <img key={img.id} src={img.url} alt={img.alt?.en || ''} />
131
+ ))}
132
+ </div>
133
+ ),
134
+ },
135
+
136
+ Download: {
137
+ fields: {
138
+ document: {
139
+ type: 'document',
140
+ label: 'Download File',
141
+ },
142
+ },
143
+ render: ({ document }) => (
144
+ <a href={document?.url} download>
145
+ {document?.title?.en || document?.filename}
146
+ </a>
147
+ ),
148
+ },
149
+ },
150
+ };
151
+ ```
152
+
153
+ ## Configuration
154
+
155
+ ### Languages
156
+
157
+ ```typescript
158
+ interface Language {
159
+ code: string; // e.g., 'en', 'it', 'de'
160
+ label: string; // e.g., 'English', 'Italiano', 'Deutsch'
161
+ }
162
+
163
+ // Default languages if not specified
164
+ const DEFAULT_LANGUAGES = [
165
+ { code: 'en', label: 'English' },
166
+ { code: 'it', label: 'Italiano' },
167
+ ];
168
+ ```
169
+
170
+ ### Image Options
171
+
172
+ ```typescript
173
+ interface ImageOptions {
174
+ // Required: Fetch paginated list of images
175
+ fetchList: (params: FetchListParams) => Promise<ImageItem[] | FetchListResult<ImageItem>>;
176
+
177
+ // Optional: Upload new images
178
+ upload?: (file: File, callbacks: UploadCallbacks) => Promise<ImageItem | ImageItem[]>;
179
+
180
+ // Optional: Update image metadata (alt text)
181
+ update?: (id: string, data: { alt?: LocalizedString }) => Promise<ImageItem>;
182
+
183
+ // Optional: Delete an image (enables Manage mode)
184
+ delete?: (id: string) => Promise<void>;
185
+
186
+ // Optional: Upload configuration
187
+ uploadConfig?: UploadConfig;
188
+ }
189
+ ```
190
+
191
+ ### Gallery Options
192
+
193
+ ```typescript
194
+ interface GalleryOptions {
195
+ // Required: Fetch paginated list of galleries
196
+ fetchList: (params: FetchListParams) => Promise<GalleryItem[] | FetchListResult<GalleryItem>>;
197
+
198
+ // Required: Fetch single gallery with all images
199
+ fetch: (id: string) => Promise<GalleryItem>;
200
+
201
+ // Required: Create a new gallery
202
+ create: (name: string) => Promise<GalleryItem>;
203
+
204
+ // Optional: Delete a gallery (enables Manage mode)
205
+ delete?: (id: string) => Promise<void>;
206
+
207
+ // Required: Upload images to a gallery
208
+ upload: (galleryId: string, file: File, callbacks: UploadCallbacks) => Promise<ImageItem | ImageItem[]>;
209
+
210
+ // Optional: Remove an image from a gallery (enables image Manage mode)
211
+ removeImage?: (galleryId: string, imageId: string) => Promise<void>;
212
+
213
+ // Optional: Update image metadata within a gallery
214
+ updateImage?: (galleryId: string, imageId: string, data: { alt?: LocalizedString }) => Promise<ImageItem>;
215
+ }
216
+ ```
217
+
218
+ ### Document Options
219
+
220
+ ```typescript
221
+ interface DocumentOptions {
222
+ // Required: Fetch paginated list of documents
223
+ fetchList: (params: FetchListParams) => Promise<DocumentItem[] | FetchListResult<DocumentItem>>;
224
+
225
+ // Optional: Upload new documents
226
+ upload?: (file: File, callbacks: UploadCallbacks) => Promise<DocumentItem | DocumentItem[]>;
227
+
228
+ // Optional: Update document metadata (title)
229
+ update?: (id: string, data: { title?: LocalizedString }) => Promise<DocumentItem>;
230
+
231
+ // Optional: Delete a document (enables Manage mode)
232
+ delete?: (id: string) => Promise<void>;
233
+
234
+ // Optional: Upload configuration
235
+ uploadConfig?: UploadConfig;
236
+ }
237
+ ```
238
+
239
+ ### Upload Configuration
240
+
241
+ ```typescript
242
+ interface UploadConfig {
243
+ // Accepted file types (default: 'image/*' for images, common doc types for documents)
244
+ accept?: string;
245
+
246
+ // Maximum file size in bytes (default: 10MB for images, 20MB for documents)
247
+ maxSize?: number;
248
+
249
+ // Allow multiple file selection (default: true)
250
+ multiple?: boolean;
251
+ }
252
+ ```
253
+
254
+ ## Types
255
+
256
+ ### ImageItem
257
+
258
+ ```typescript
259
+ interface ImageItem {
260
+ id: string;
261
+ url: string;
262
+ filename?: string;
263
+ alt?: LocalizedString; // { en: 'Alt text', it: 'Testo alt' }
264
+ width?: number;
265
+ height?: number;
266
+ size?: number; // File size in bytes
267
+ thumbnailUrl?: string; // For faster loading in grids
268
+ createdAt?: string;
269
+ }
270
+ ```
271
+
272
+ ### GalleryItem
273
+
274
+ ```typescript
275
+ interface GalleryItem {
276
+ id: string;
277
+ name: string;
278
+ coverImage?: ImageItem;
279
+ images: ImageItem[];
280
+ imageCount?: number;
281
+ createdAt?: string;
282
+ }
283
+ ```
284
+
285
+ ### DocumentItem
286
+
287
+ ```typescript
288
+ interface DocumentItem {
289
+ id: string;
290
+ url: string;
291
+ filename: string;
292
+ title?: LocalizedString; // { en: 'Title', it: 'Titolo' }
293
+ mimeType: string; // e.g., 'application/pdf'
294
+ size: number; // File size in bytes
295
+ extension: string; // e.g., 'pdf', 'docx'
296
+ createdAt?: string;
297
+ }
298
+ ```
299
+
300
+ ### LocalizedString
301
+
302
+ ```typescript
303
+ interface LocalizedString {
304
+ [languageCode: string]: string;
305
+ }
306
+
307
+ // Example
308
+ const alt: LocalizedString = {
309
+ en: 'Coastal sunset',
310
+ it: 'Tramonto sulla costa',
311
+ };
312
+ ```
313
+
314
+ ### FetchListParams
315
+
316
+ ```typescript
317
+ interface FetchListParams {
318
+ query?: string; // Search query
319
+ page?: number; // Page number (1-indexed)
320
+ pageSize?: number; // Items per page (default: 20)
321
+ }
322
+ ```
323
+
324
+ ### FetchListResult
325
+
326
+ ```typescript
327
+ interface FetchListResult<T> {
328
+ items: T[];
329
+ total?: number;
330
+ hasMore?: boolean;
331
+ }
332
+ ```
333
+
334
+ ### UploadCallbacks
335
+
336
+ ```typescript
337
+ interface UploadCallbacks {
338
+ onProgress?: (percent: number) => void;
339
+ }
340
+ ```
341
+
342
+ ## Components
343
+
344
+ For advanced customization, you can import individual components:
345
+
346
+ ```typescript
347
+ import {
348
+ // Fields (for custom block configurations)
349
+ ImageField,
350
+ GalleryField,
351
+ DocumentField,
352
+
353
+ // Modals (for custom implementations)
354
+ ImagePickerModal,
355
+ GalleryPickerModal,
356
+ DocumentPickerModal,
357
+
358
+ // Media Panel (shown in Puck sidebar)
359
+ MediaPanel,
360
+
361
+ // Upload components
362
+ UploadDropzone,
363
+ UploadQueue,
364
+
365
+ // Hook
366
+ useUpload,
367
+ formatFileSize,
368
+ } from '@caprionlinesrl/puck-plugin-media';
369
+ ```
370
+
371
+ ## Styling
372
+
373
+ The plugin uses CSS Modules with vanilla CSS that matches Puck's design language. Import the styles in your app:
374
+
375
+ ```tsx
376
+ import '@caprionlinesrl/puck-plugin-media/styles.css';
377
+ ```
378
+
379
+ ## Browser Support
380
+
381
+ - Chrome, Firefox, Safari, Edge (latest versions)
382
+ - React 18+
383
+ - Puck 0.21+
384
+
385
+ ## Development
386
+
387
+ ### Prerequisites
388
+
389
+ - Node.js 18+
390
+ - Yarn 4+
391
+
392
+ ### Setup
393
+
394
+ ```bash
395
+ git clone https://github.com/caprionlinesrl/puck-plugin-media.git
396
+ cd puck-plugin-media
397
+ yarn install
398
+ ```
399
+
400
+ ### Run the demo
401
+
402
+ ```bash
403
+ yarn demo
404
+ ```
405
+
406
+ Open [http://localhost:3000](http://localhost:3000)
407
+
408
+ ### Build the library
409
+
410
+ ```bash
411
+ yarn build
412
+ ```
413
+
414
+ ### Type check
415
+
416
+ ```bash
417
+ yarn typecheck
418
+ ```
419
+
420
+ ## License
421
+
422
+ MIT