0
# Upload System
1
2
Advanced placeholder-based upload system with file validation, progress tracking, error handling, and drag & drop support. Provides a comprehensive solution for handling file uploads in rich text editing contexts.
3
4
## Capabilities
5
6
### Base Placeholder Plugin
7
8
Core plugin providing placeholder functionality for file uploads with validation and progress tracking.
9
10
```typescript { .api }
11
/**
12
* Base plugin with insert methods for all media types
13
* Provides placeholder elements that can be replaced with actual media
14
*/
15
const BasePlaceholderPlugin: TSlatePlugin<PlaceholderConfig>;
16
17
interface PlaceholderConfig extends MediaPluginOptions {
18
rules?: PlaceholderRule[];
19
}
20
21
interface PlaceholderRule {
22
/** Media type this rule applies to */
23
mediaType: string;
24
}
25
```
26
27
**Usage Example:**
28
29
```typescript
30
import { BasePlaceholderPlugin } from "@udecode/plate-media";
31
import { createSlateEditor } from "@udecode/plate";
32
33
const editor = createSlateEditor({
34
plugins: [
35
BasePlaceholderPlugin.configure({
36
options: {
37
rules: [
38
{ mediaType: 'image' },
39
{ mediaType: 'video' },
40
{ mediaType: 'audio' },
41
{ mediaType: 'file' }
42
]
43
}
44
})
45
]
46
});
47
```
48
49
### Placeholder Insertion
50
51
Functions for inserting placeholder elements of different media types.
52
53
#### Generic Placeholder Insertion
54
55
```typescript { .api }
56
/**
57
* Inserts a placeholder element for the specified media type
58
* @param editor - Slate editor instance
59
* @param mediaType - Type of media placeholder to insert
60
* @param options - Optional insertion configuration
61
*/
62
function insertPlaceholder(
63
editor: SlateEditor,
64
mediaType: string,
65
options?: InsertNodesOptions
66
): void;
67
```
68
69
#### Typed Placeholder Functions
70
71
```typescript { .api }
72
/**
73
* Insert image placeholder element
74
* @param editor - Slate editor instance
75
* @param options - Optional insertion configuration
76
*/
77
function insertImagePlaceholder(
78
editor: SlateEditor,
79
options?: InsertNodesOptions
80
): void;
81
82
/**
83
* Insert video placeholder element
84
* @param editor - Slate editor instance
85
* @param options - Optional insertion configuration
86
*/
87
function insertVideoPlaceholder(
88
editor: SlateEditor,
89
options?: InsertNodesOptions
90
): void;
91
92
/**
93
* Insert audio placeholder element
94
* @param editor - Slate editor instance
95
* @param options - Optional insertion configuration
96
*/
97
function insertAudioPlaceholder(
98
editor: SlateEditor,
99
options?: InsertNodesOptions
100
): void;
101
102
/**
103
* Insert file placeholder element
104
* @param editor - Slate editor instance
105
* @param options - Optional insertion configuration
106
*/
107
function insertFilePlaceholder(
108
editor: SlateEditor,
109
options?: InsertNodesOptions
110
): void;
111
```
112
113
**Usage Examples:**
114
115
```typescript
116
import {
117
insertImagePlaceholder,
118
insertVideoPlaceholder,
119
insertPlaceholder
120
} from "@udecode/plate-media";
121
122
// Insert specific placeholder types
123
insertImagePlaceholder(editor);
124
insertVideoPlaceholder(editor);
125
126
// Insert generic placeholder
127
insertPlaceholder(editor, 'audio');
128
insertPlaceholder(editor, 'file', { at: [0, 0] });
129
```
130
131
### Media Node Updates
132
133
Function for updating placeholder elements with actual media data after upload.
134
135
```typescript { .api }
136
/**
137
* Updates a placeholder or media element with new properties
138
* Used to replace placeholders with actual media after upload completion
139
* @param editor - Slate editor instance
140
* @param props - Media properties to set
141
* @param options - Optional node update configuration
142
*/
143
function setMediaNode(
144
editor: SlateEditor,
145
props: MediaNodeProps,
146
options?: SetNodesOptions
147
): void;
148
149
interface MediaNodeProps {
150
/** Media element type */
151
type: string;
152
/** Media URL */
153
url: string;
154
/** Optional media ID */
155
id?: string;
156
/** Initial height for sizing */
157
initialHeight?: number;
158
/** Initial width for sizing */
159
initialWidth?: number;
160
/** Whether this is an upload operation */
161
isUpload?: boolean;
162
/** File name for downloads */
163
name?: string;
164
/** Placeholder ID to replace */
165
placeholderId?: string;
166
/** Current width for responsive sizing */
167
width?: number;
168
}
169
```
170
171
**Usage Example:**
172
173
```typescript
174
import { setMediaNode, insertImagePlaceholder } from "@udecode/plate-media";
175
176
// Insert placeholder first
177
insertImagePlaceholder(editor);
178
179
// Later, after upload completes
180
const uploadResult = await uploadFile(file);
181
182
setMediaNode(editor, {
183
type: 'image',
184
url: uploadResult.url,
185
name: uploadResult.fileName,
186
initialWidth: uploadResult.width,
187
initialHeight: uploadResult.height,
188
isUpload: true
189
});
190
```
191
192
## React Upload System
193
194
Enhanced React integration with comprehensive file handling, validation, and UI components.
195
196
### Placeholder Plugin
197
198
React-enhanced plugin with file drop, paste handling, and upload management.
199
200
```typescript { .api }
201
/**
202
* Enhanced React plugin with file drop/paste handling and upload management
203
* Provides comprehensive file validation and progress tracking
204
*/
205
const PlaceholderPlugin: TPlatePlugin<PlaceholderConfig>;
206
207
interface PlaceholderConfig extends MediaPluginOptions {
208
/** Disable empty placeholder insertion */
209
disableEmptyPlaceholder?: boolean;
210
/** Disable drag & drop functionality */
211
disableFileDrop?: boolean;
212
/** File type configuration */
213
uploadConfig?: UploadConfig;
214
/** Maximum files allowed globally */
215
maxFileCount?: number;
216
/** Allow multiple file selection */
217
multiple?: boolean;
218
/** Current upload error */
219
error?: UploadError | null;
220
}
221
```
222
223
### File Type Configuration
224
225
Comprehensive file type support with validation and limits.
226
227
#### Upload Configuration
228
229
```typescript { .api }
230
/**
231
* Configuration mapping for different file types
232
*/
233
type UploadConfig = Partial<Record<AllowedFileType, MediaItemConfig>>;
234
235
interface MediaItemConfig {
236
/** Target media type for this file type */
237
mediaType: MediaKeys;
238
/** Maximum files allowed for this type */
239
maxFileCount?: number;
240
/** Maximum file size (e.g., "4MB", "100KB") */
241
maxFileSize?: FileSize;
242
/** Minimum files required */
243
minFileCount?: number;
244
}
245
246
type AllowedFileType = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob';
247
type FileSize = `${number}${'B' | 'KB' | 'MB' | 'GB'}`;
248
type MediaKeys = 'image' | 'video' | 'audio' | 'file';
249
```
250
251
#### File Type Constants
252
253
```typescript { .api }
254
/**
255
* Array of all supported file type categories
256
*/
257
const ALLOWED_FILE_TYPES: AllowedFileType[] = [
258
'image',
259
'video',
260
'audio',
261
'pdf',
262
'text',
263
'blob'
264
];
265
```
266
267
**Usage Example:**
268
269
```typescript
270
import { PlaceholderPlugin, type UploadConfig } from "@udecode/plate-media/react";
271
272
const uploadConfig: UploadConfig = {
273
image: {
274
mediaType: 'image',
275
maxFileCount: 10,
276
maxFileSize: '5MB'
277
},
278
video: {
279
mediaType: 'video',
280
maxFileCount: 3,
281
maxFileSize: '50MB'
282
},
283
audio: {
284
mediaType: 'audio',
285
maxFileCount: 5,
286
maxFileSize: '10MB'
287
},
288
pdf: {
289
mediaType: 'file',
290
maxFileCount: 2,
291
maxFileSize: '25MB'
292
}
293
};
294
295
const editor = createPlateEditor({
296
plugins: [
297
PlaceholderPlugin.configure({
298
options: {
299
uploadConfig,
300
maxFileCount: 20,
301
multiple: true,
302
disableFileDrop: false
303
}
304
})
305
]
306
});
307
```
308
309
### Error Handling
310
311
Comprehensive error system for upload validation and user feedback.
312
313
```typescript { .api }
314
/**
315
* Upload error codes enumeration
316
*/
317
enum UploadErrorCode {
318
INVALID_FILE_TYPE = 400,
319
TOO_MANY_FILES = 402,
320
INVALID_FILE_SIZE = 403,
321
TOO_LESS_FILES = 405,
322
TOO_LARGE = 413,
323
}
324
325
/**
326
* Discriminated union of upload error types with detailed data structures
327
*/
328
type UploadError =
329
| {
330
code: UploadErrorCode.INVALID_FILE_TYPE;
331
data: {
332
allowedTypes: string[];
333
files: File[];
334
};
335
}
336
| {
337
code: UploadErrorCode.TOO_MANY_FILES;
338
data: {
339
files: File[];
340
fileType: AllowedFileType | null;
341
maxFileCount: number;
342
};
343
}
344
| {
345
code: UploadErrorCode.INVALID_FILE_SIZE;
346
data: {
347
files: File[];
348
};
349
}
350
| {
351
code: UploadErrorCode.TOO_LESS_FILES;
352
data: {
353
files: File[];
354
fileType: AllowedFileType;
355
minFileCount: number;
356
};
357
}
358
| {
359
code: UploadErrorCode.TOO_LARGE;
360
data: {
361
files: File[];
362
fileType: AllowedFileType;
363
maxFileSize: string;
364
};
365
};
366
367
/**
368
* Creates typed upload errors with detailed error data
369
* @param code - Error code from UploadErrorCode enum
370
* @param data - Error-specific data structure
371
* @returns Formatted upload error
372
*/
373
function createUploadError<T extends UploadErrorCode>(
374
code: T,
375
data: Extract<UploadError, { code: T }>['data']
376
): UploadError;
377
378
/**
379
* Type guard to check if an unknown value is an UploadError
380
* @param error - Value to check
381
* @returns Boolean indicating if value is UploadError
382
*/
383
function isUploadError(error: unknown): error is UploadError;
384
```
385
386
**Usage Example:**
387
388
```typescript
389
import {
390
createUploadError,
391
isUploadError,
392
UploadErrorCode,
393
type UploadError
394
} from "@udecode/plate-media/react";
395
396
const handleUploadError = (error: UploadError) => {
397
switch (error.code) {
398
case UploadErrorCode.INVALID_FILE_TYPE:
399
console.error(`Invalid file types. Allowed: ${error.data.allowedTypes.join(', ')}`);
400
console.error(`Invalid files:`, error.data.files.map(f => f.name));
401
break;
402
case UploadErrorCode.TOO_MANY_FILES:
403
console.error(`Too many files. Maximum allowed: ${error.data.maxFileCount}`);
404
console.error(`File type: ${error.data.fileType}`);
405
break;
406
case UploadErrorCode.INVALID_FILE_SIZE:
407
console.error(`Invalid file size for files:`, error.data.files.map(f => f.name));
408
break;
409
case UploadErrorCode.TOO_LESS_FILES:
410
console.error(`Not enough ${error.data.fileType} files. Minimum required: ${error.data.minFileCount}`);
411
console.error(`Current files:`, error.data.files.map(f => f.name));
412
break;
413
case UploadErrorCode.TOO_LARGE:
414
console.error(`Files too large for ${error.data.fileType}. Max size: ${error.data.maxFileSize}`);
415
console.error(`Large files:`, error.data.files.map(f => f.name));
416
break;
417
}
418
};
419
```
420
421
### File Validation
422
423
Comprehensive file validation system with type detection and size checking.
424
425
#### Validation Functions
426
427
```typescript { .api }
428
/**
429
* Validates file selection against upload configuration
430
* @param files - FileList to validate
431
* @param config - Upload configuration
432
* @returns Validation result with errors if any
433
*/
434
function validateFiles(
435
files: FileList,
436
config: UploadConfig
437
): { isValid: boolean; error?: UploadError };
438
439
/**
440
* Validates individual file against configuration
441
* @param file - File to validate
442
* @param config - Media item configuration
443
* @returns Validation result
444
*/
445
function validateFileItem(
446
file: File,
447
config: MediaItemConfig
448
): { isValid: boolean; error?: UploadError };
449
450
/**
451
* Determines file type category from File object
452
* @param file - File to categorize
453
* @returns File type category or undefined if not supported
454
*/
455
function matchFileType(file: File): AllowedFileType | undefined;
456
457
/**
458
* Groups files by their detected media type
459
* @param files - Array of files to group
460
* @returns Object mapping media types to file arrays
461
*/
462
function groupFilesByType(files: File[]): Record<string, File[]>;
463
```
464
465
#### Utility Functions
466
467
```typescript { .api }
468
/**
469
* Converts file size string to bytes
470
* @param size - Size string like "4MB", "100KB"
471
* @returns Size in bytes
472
*/
473
function fileSizeToBytes(size: FileSize): number;
474
475
/**
476
* Determines appropriate media type for a file
477
* @param file - File to analyze
478
* @returns Corresponding media type
479
*/
480
function getMediaType(file: File): MediaKeys;
481
```
482
483
**Usage Examples:**
484
485
```typescript
486
import {
487
validateFiles,
488
matchFileType,
489
fileSizeToBytes,
490
groupFilesByType
491
} from "@udecode/plate-media/react";
492
493
// Validate file selection
494
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
495
if (event.target.files) {
496
const validation = validateFiles(event.target.files, uploadConfig);
497
498
if (!validation.isValid && validation.error) {
499
setError(validation.error);
500
return;
501
}
502
503
// Process valid files
504
processFiles(event.target.files);
505
}
506
};
507
508
// Check file types
509
const checkFileType = (file: File) => {
510
const fileType = matchFileType(file);
511
console.log(`File type: ${fileType}`); // "image", "video", etc.
512
};
513
514
// Convert file sizes
515
const maxSizeBytes = fileSizeToBytes("5MB"); // 5242880
516
517
// Group files by type
518
const files = Array.from(fileList);
519
const grouped = groupFilesByType(files);
520
// { image: [file1, file2], video: [file3], audio: [file4] }
521
```
522
523
### State Management
524
525
Upload progress and state tracking with React integration.
526
527
#### Placeholder Store
528
529
```typescript { .api }
530
/**
531
* Atom store for upload state management
532
*/
533
interface PlaceholderStore {
534
/** Upload operation in progress */
535
isUploading: boolean;
536
/** Progress by placeholder ID */
537
progresses: Record<string, number>;
538
/** Size constraints for uploads */
539
size: { width: number; height: number } | null;
540
/** Files currently being processed */
541
updatedFiles: File[];
542
}
543
544
/**
545
* Store access hooks
546
*/
547
function usePlaceholderStore(): PlaceholderStore;
548
function usePlaceholderValue<T>(selector: (store: PlaceholderStore) => T): T;
549
function usePlaceholderSet(): (updates: Partial<PlaceholderStore>) => void;
550
```
551
552
#### API Extensions
553
554
```typescript { .api }
555
/**
556
* Extended API for file tracking during uploads
557
*/
558
interface PlaceholderApi {
559
/** Add file to upload tracking */
560
addUploadingFile(id: string, file: File): void;
561
/** Retrieve uploading file by ID */
562
getUploadingFile(id: string): File | undefined;
563
/** Remove file from tracking */
564
removeUploadingFile(id: string): void;
565
}
566
567
/**
568
* Extended transforms for media insertion
569
*/
570
interface PlaceholderTransforms {
571
/** Insert media from file list */
572
insertMedia(files: FileList, options?: InsertNodesOptions): void;
573
}
574
```
575
576
### React Hooks
577
578
Specialized hooks for placeholder and upload functionality.
579
580
```typescript { .api }
581
/**
582
* Provides placeholder element state and handlers
583
* @returns Placeholder element props and interaction handlers
584
*/
585
function usePlaceholderElement(): {
586
element: TPlaceholderElement;
587
isUploading: boolean;
588
progress: number;
589
error: UploadError | null;
590
};
591
592
/**
593
* Manages placeholder popover interactions
594
* @returns Popover state and control functions
595
*/
596
function usePlaceholderPopover(): {
597
isOpen: boolean;
598
open: () => void;
599
close: () => void;
600
toggle: () => void;
601
};
602
```
603
604
**Usage Example:**
605
606
```typescript
607
import {
608
usePlaceholderElement,
609
usePlaceholderPopover
610
} from "@udecode/plate-media/react";
611
612
const PlaceholderComponent = () => {
613
const { element, isUploading, progress, error } = usePlaceholderElement();
614
const popover = usePlaceholderPopover();
615
616
return (
617
<div>
618
{isUploading && (
619
<div>
620
<div>Uploading... {Math.round(progress * 100)}%</div>
621
<progress value={progress} max={1} />
622
</div>
623
)}
624
625
{error && (
626
<div style={{ color: 'red' }}>
627
Error: {error.message}
628
</div>
629
)}
630
631
<button onClick={popover.toggle}>
632
{popover.isOpen ? 'Close' : 'Open'} Upload Options
633
</button>
634
635
{popover.isOpen && (
636
<div>
637
{/* Upload options UI */}
638
</div>
639
)}
640
</div>
641
);
642
};
643
```
644
645
## MIME Type Support
646
647
Comprehensive MIME type definitions organized by category for accurate file type detection.
648
649
### MIME Type Categories
650
651
The upload system supports extensive MIME type mappings:
652
653
- **Image**: JPG, PNG, GIF, SVG, WebP, HEIC, AVIF, and many RAW formats
654
- **Video**: MP4, WebM, AVI, MOV, MKV, and other common video formats
655
- **Audio**: MP3, WAV, FLAC, AAC, OGG, and professional audio formats
656
- **PDF**: Application/PDF documents
657
- **Text**: Plain text, markdown, CSV, and code files
658
- **Blob**: Generic file support for other formats
659
660
File type detection uses both MIME type and file extension for maximum compatibility across different browsers and systems.