0
# File Editor Integration
1
2
Integrate with file editor plugins for image editing and metadata management.
3
4
## Capabilities
5
6
### Open File Editor
7
8
Open the file editor interface for a specific file with compatible editor plugins.
9
10
```typescript { .api }
11
/**
12
* Open file editor for specific file
13
* Checks for compatible editor plugins and activates editor overlay
14
* @param file - File to open in editor
15
*/
16
openFileEditor(file: UppyFile): void;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import Dashboard from "@uppy/dashboard";
23
24
const dashboard = uppy.getPlugin("Dashboard") as Dashboard;
25
26
// Open editor for file
27
const file = uppy.getFile("file-id");
28
dashboard.openFileEditor(file);
29
30
// Open editor from file list click
31
function handleEditClick(fileId: string) {
32
const file = uppy.getFile(fileId);
33
if (file && dashboard.canEditFile(file)) {
34
dashboard.openFileEditor(file);
35
}
36
}
37
38
// Auto-open editor for images
39
uppy.on("file-added", (file) => {
40
if (file.type?.startsWith("image/") && dashboard.canEditFile(file)) {
41
dashboard.openFileEditor(file);
42
}
43
});
44
```
45
46
**Behavior:**
47
- Sets `showFileEditor` to true in dashboard state
48
- Sets `fileCardFor` to the file ID
49
- Sets `activeOverlayType` to 'FileEditor'
50
- Calls `selectFile(file)` on all registered editor plugins
51
- Only works if compatible editor plugins are available
52
53
### Close File Editor
54
55
Close the file editor interface and return to appropriate dashboard state.
56
57
```typescript { .api }
58
/**
59
* Close file editor panel
60
* Returns to file card if metadata editing is enabled, otherwise to main view
61
*/
62
closeFileEditor(): void;
63
```
64
65
**Usage Examples:**
66
67
```typescript
68
// Close editor programmatically
69
dashboard.closeFileEditor();
70
71
// Close editor on escape key
72
document.addEventListener("keydown", (event) => {
73
if (event.key === "Escape" && dashboard.getPluginState().showFileEditor) {
74
dashboard.closeFileEditor();
75
}
76
});
77
78
// Close editor after timeout
79
function autoCloseEditor(timeoutMs: number = 300000) { // 5 minutes
80
setTimeout(() => {
81
if (dashboard.getPluginState().showFileEditor) {
82
dashboard.closeFileEditor();
83
}
84
}, timeoutMs);
85
}
86
```
87
88
**Behavior:**
89
- Sets `showFileEditor` to false
90
- If metadata editing is enabled: sets `activeOverlayType` to 'FileCard'
91
- If metadata editing is disabled: sets `fileCardFor` to null and `activeOverlayType` to 'AddFiles'
92
93
### Save File Editor
94
95
Save changes from the file editor and close the editor interface.
96
97
```typescript { .api }
98
/**
99
* Save file editor changes
100
* Calls save() on all registered editor plugins and closes editor
101
*/
102
saveFileEditor(): void;
103
```
104
105
**Usage Examples:**
106
107
```typescript
108
// Save editor changes
109
dashboard.saveFileEditor();
110
111
// Save with confirmation
112
function saveWithConfirmation() {
113
if (confirm("Save changes to file?")) {
114
dashboard.saveFileEditor();
115
} else {
116
dashboard.closeFileEditor();
117
}
118
}
119
120
// Auto-save editor changes
121
let autoSaveInterval: number;
122
123
uppy.on("dashboard:file-edit-start", () => {
124
autoSaveInterval = setInterval(() => {
125
if (dashboard.getPluginState().showFileEditor) {
126
dashboard.saveFileEditor();
127
}
128
}, 60000); // Auto-save every minute
129
});
130
131
uppy.on("dashboard:file-edit-complete", () => {
132
clearInterval(autoSaveInterval);
133
});
134
```
135
136
**Behavior:**
137
- Calls `save()` method on all registered editor plugins
138
- Calls `closeFileEditor()` to hide the editor interface
139
140
### Check File Editor Compatibility
141
142
Determine if a file can be edited with available editor plugins.
143
144
```typescript { .api }
145
/**
146
* Check if file can be edited with available editor plugins
147
* @param file - File to check for editor compatibility
148
* @returns True if file can be edited, false otherwise
149
*/
150
canEditFile(file: UppyFile): boolean;
151
```
152
153
**Usage Examples:**
154
155
```typescript
156
// Check if file can be edited
157
const file = uppy.getFile("file-id");
158
if (dashboard.canEditFile(file)) {
159
// Show edit button
160
showEditButton(file.id);
161
} else {
162
// Hide edit button
163
hideEditButton(file.id);
164
}
165
166
// Filter editable files
167
const editableFiles = uppy.getFiles().filter(file =>
168
dashboard.canEditFile(file)
169
);
170
171
// Conditional UI rendering
172
function renderFileActions(file: UppyFile) {
173
const canEdit = dashboard.canEditFile(file);
174
return (
175
<div className="file-actions">
176
<button onClick={() => removeFile(file.id)}>Remove</button>
177
{canEdit && (
178
<button onClick={() => dashboard.openFileEditor(file)}>
179
Edit
180
</button>
181
)}
182
</div>
183
);
184
}
185
```
186
187
### Editor Plugin Integration
188
189
How editor plugins integrate with the dashboard file editor system.
190
191
```typescript { .api }
192
/**
193
* Editor plugin requirements for dashboard integration
194
*/
195
interface EditorPlugin {
196
/** Plugin must have unique ID */
197
id: string;
198
/** Plugin must be 'editor' type */
199
type: 'editor';
200
/** Check if plugin can edit specific file */
201
canEditFile(file: UppyFile): boolean;
202
/** Select file for editing */
203
selectFile(file: UppyFile): void;
204
/** Save editor changes */
205
save(): void;
206
/** Render editor interface */
207
render(): ComponentChild;
208
}
209
```
210
211
**Editor Plugin Examples:**
212
213
```typescript
214
// Image editor plugin
215
class ImageEditor extends BasePlugin {
216
type = 'editor';
217
id = 'ImageEditor';
218
219
canEditFile(file: UppyFile): boolean {
220
return file.type?.startsWith('image/') || false;
221
}
222
223
selectFile(file: UppyFile): void {
224
this.selectedFile = file;
225
this.loadImageIntoEditor(file.data);
226
}
227
228
save(): void {
229
if (this.selectedFile && this.hasChanges) {
230
const editedBlob = this.exportEditedImage();
231
this.uppy.setFileData(this.selectedFile.id, editedBlob);
232
}
233
}
234
235
render() {
236
return h('div', { className: 'image-editor' }, [
237
h('canvas', { ref: this.canvasRef }),
238
h('div', { className: 'editor-tools' }, this.renderTools())
239
]);
240
}
241
}
242
243
// Text metadata editor
244
class MetadataEditor extends BasePlugin {
245
type = 'editor';
246
id = 'MetadataEditor';
247
248
canEditFile(): boolean {
249
return true; // Can edit metadata for any file
250
}
251
252
selectFile(file: UppyFile): void {
253
this.selectedFile = file;
254
this.loadMetadata(file.meta);
255
}
256
257
save(): void {
258
if (this.selectedFile) {
259
const newMeta = this.getFormData();
260
this.uppy.setFileMeta(this.selectedFile.id, newMeta);
261
}
262
}
263
}
264
```
265
266
### Auto-Open Configuration
267
268
Configure automatic editor opening based on file types or conditions.
269
270
```typescript { .api }
271
/**
272
* Auto-open configuration options
273
*/
274
interface AutoOpenConfig {
275
/** Auto-open editor type on file selection */
276
autoOpen?: 'metaEditor' | 'imageEditor' | null;
277
}
278
```
279
280
**Auto-Open Examples:**
281
282
```typescript
283
// Auto-open metadata editor
284
uppy.use(Dashboard, {
285
autoOpen: 'metaEditor'
286
});
287
288
// Auto-open image editor for images
289
uppy.use(Dashboard, {
290
autoOpen: 'imageEditor'
291
});
292
293
// Custom auto-open logic
294
uppy.use(Dashboard, {
295
autoOpen: null // Disable automatic opening
296
});
297
298
uppy.on('files-added', (files) => {
299
files.forEach(file => {
300
if (file.type?.startsWith('image/') && needsImageEditing(file)) {
301
dashboard.openFileEditor(file);
302
} else if (needsMetadataEditing(file)) {
303
dashboard.toggleFileCard(true, file.id);
304
}
305
});
306
});
307
```
308
309
### Editor State Management
310
311
State properties related to file editor functionality.
312
313
```typescript { .api }
314
/**
315
* File editor state properties
316
*/
317
interface FileEditorState {
318
/** Whether file editor is currently shown */
319
showFileEditor: boolean;
320
/** File ID being edited, null if no file selected */
321
fileCardFor: string | null;
322
/** Active overlay type */
323
activeOverlayType: 'FileEditor' | 'FileCard' | null;
324
}
325
```
326
327
### Editor Events
328
329
Events emitted during file editor lifecycle.
330
331
```typescript { .api }
332
/**
333
* File editor events
334
*/
335
interface FileEditorEvents<M extends Meta, B extends Body> {
336
/** Emitted when file editing starts */
337
"dashboard:file-edit-start": (file?: UppyFile<M, B>) => void;
338
/** Emitted when file editing completes */
339
"dashboard:file-edit-complete": (file?: UppyFile<M, B>) => void;
340
}
341
```
342
343
**Event Usage Examples:**
344
345
```typescript
346
// Track editor usage
347
uppy.on('dashboard:file-edit-start', (file) => {
348
console.log(`Started editing: ${file?.name}`);
349
trackEvent('file_edit_start', {
350
fileType: file?.type,
351
fileSize: file?.size
352
});
353
});
354
355
uppy.on('dashboard:file-edit-complete', (file) => {
356
console.log(`Finished editing: ${file?.name}`);
357
trackEvent('file_edit_complete', {
358
fileType: file?.type,
359
duration: Date.now() - editStartTime
360
});
361
});
362
363
// Editor session management
364
let editSession: EditSession | null = null;
365
366
uppy.on('dashboard:file-edit-start', (file) => {
367
editSession = new EditSession(file);
368
editSession.start();
369
});
370
371
uppy.on('dashboard:file-edit-complete', (file) => {
372
if (editSession) {
373
editSession.complete();
374
editSession = null;
375
}
376
});
377
```
378
379
### Multiple Editor Support
380
381
Handle multiple editor plugins and editor selection.
382
383
```typescript { .api }
384
/**
385
* Multiple editor management
386
*/
387
interface MultipleEditorSupport {
388
/** Get all registered editor plugins */
389
getEditors(targets: Target[]): TargetWithRender[];
390
/** Check if any editor can handle file */
391
canEditFile(file: UppyFile): boolean;
392
/** Open file in all compatible editors */
393
openFileEditor(file: UppyFile): void;
394
}
395
```
396
397
**Multiple Editor Examples:**
398
399
```typescript
400
// Register multiple editors
401
uppy.use(ImageEditor, { /* options */ });
402
uppy.use(MetadataEditor, { /* options */ });
403
uppy.use(CropEditor, { /* options */ });
404
405
// Selective editor activation
406
function openSpecificEditor(file: UppyFile, editorType: string) {
407
const editors = dashboard.getPluginState().targets
408
.filter(target => target.type === 'editor' && target.id === editorType);
409
410
if (editors.length > 0) {
411
const plugin = uppy.getPlugin(editorType);
412
if (plugin && plugin.canEditFile(file)) {
413
dashboard.openFileEditor(file);
414
}
415
}
416
}
417
418
// Editor selection UI
419
function renderEditorOptions(file: UppyFile) {
420
const availableEditors = dashboard.getPluginState().targets
421
.filter(target => target.type === 'editor')
422
.filter(target => uppy.getPlugin(target.id).canEditFile(file));
423
424
return availableEditors.map(editor => (
425
<button
426
key={editor.id}
427
onClick={() => openSpecificEditor(file, editor.id)}
428
>
429
Edit with {editor.name}
430
</button>
431
));
432
}
433
```
434
435
### Editor Integration Best Practices
436
437
Recommended patterns for editor plugin development and integration.
438
439
```typescript { .api }
440
/**
441
* Editor plugin best practices
442
*/
443
interface EditorBestPractices {
444
/** Implement proper file type checking */
445
canEditFile(file: UppyFile): boolean;
446
/** Handle file loading and error states */
447
selectFile(file: UppyFile): Promise<void>;
448
/** Provide clear save/cancel actions */
449
save(): Promise<void>;
450
/** Clean up resources on unmount */
451
unmount(): void;
452
/** Provide keyboard shortcuts */
453
handleKeyboard(event: KeyboardEvent): void;
454
/** Support undo/redo operations */
455
undo(): void;
456
redo(): void;
457
}
458
```
459
460
**Best Practice Examples:**
461
462
```typescript
463
class AdvancedImageEditor extends BasePlugin {
464
type = 'editor';
465
466
async selectFile(file: UppyFile) {
467
try {
468
this.setStatus('loading');
469
await this.loadImageData(file.data);
470
this.setStatus('ready');
471
} catch (error) {
472
this.setStatus('error');
473
this.uppy.info('Failed to load image in editor', 'error');
474
}
475
}
476
477
async save() {
478
try {
479
this.setStatus('saving');
480
const editedData = await this.exportImage();
481
this.uppy.setFileData(this.selectedFile.id, editedData);
482
this.setStatus('saved');
483
} catch (error) {
484
this.setStatus('error');
485
this.uppy.info('Failed to save edited image', 'error');
486
}
487
}
488
489
handleKeyboard(event: KeyboardEvent) {
490
if (event.ctrlKey || event.metaKey) {
491
switch (event.key) {
492
case 'z':
493
event.preventDefault();
494
if (event.shiftKey) {
495
this.redo();
496
} else {
497
this.undo();
498
}
499
break;
500
case 's':
501
event.preventDefault();
502
this.save();
503
break;
504
}
505
}
506
}
507
}
508
```