Universal UI plugin for Uppy file uploader providing comprehensive dashboard interface with drag-and-drop, file previews, metadata editing, and progress tracking.
—
Integrate with file editor plugins for image editing and metadata management.
Open the file editor interface for a specific file with compatible editor plugins.
/**
* Open file editor for specific file
* Checks for compatible editor plugins and activates editor overlay
* @param file - File to open in editor
*/
openFileEditor(file: UppyFile): void;Usage Examples:
import Dashboard from "@uppy/dashboard";
const dashboard = uppy.getPlugin("Dashboard") as Dashboard;
// Open editor for file
const file = uppy.getFile("file-id");
dashboard.openFileEditor(file);
// Open editor from file list click
function handleEditClick(fileId: string) {
const file = uppy.getFile(fileId);
if (file && dashboard.canEditFile(file)) {
dashboard.openFileEditor(file);
}
}
// Auto-open editor for images
uppy.on("file-added", (file) => {
if (file.type?.startsWith("image/") && dashboard.canEditFile(file)) {
dashboard.openFileEditor(file);
}
});Behavior:
showFileEditor to true in dashboard statefileCardFor to the file IDactiveOverlayType to 'FileEditor'selectFile(file) on all registered editor pluginsClose the file editor interface and return to appropriate dashboard state.
/**
* Close file editor panel
* Returns to file card if metadata editing is enabled, otherwise to main view
*/
closeFileEditor(): void;Usage Examples:
// Close editor programmatically
dashboard.closeFileEditor();
// Close editor on escape key
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && dashboard.getPluginState().showFileEditor) {
dashboard.closeFileEditor();
}
});
// Close editor after timeout
function autoCloseEditor(timeoutMs: number = 300000) { // 5 minutes
setTimeout(() => {
if (dashboard.getPluginState().showFileEditor) {
dashboard.closeFileEditor();
}
}, timeoutMs);
}Behavior:
showFileEditor to falseactiveOverlayType to 'FileCard'fileCardFor to null and activeOverlayType to 'AddFiles'Save changes from the file editor and close the editor interface.
/**
* Save file editor changes
* Calls save() on all registered editor plugins and closes editor
*/
saveFileEditor(): void;Usage Examples:
// Save editor changes
dashboard.saveFileEditor();
// Save with confirmation
function saveWithConfirmation() {
if (confirm("Save changes to file?")) {
dashboard.saveFileEditor();
} else {
dashboard.closeFileEditor();
}
}
// Auto-save editor changes
let autoSaveInterval: number;
uppy.on("dashboard:file-edit-start", () => {
autoSaveInterval = setInterval(() => {
if (dashboard.getPluginState().showFileEditor) {
dashboard.saveFileEditor();
}
}, 60000); // Auto-save every minute
});
uppy.on("dashboard:file-edit-complete", () => {
clearInterval(autoSaveInterval);
});Behavior:
save() method on all registered editor pluginscloseFileEditor() to hide the editor interfaceDetermine if a file can be edited with available editor plugins.
/**
* Check if file can be edited with available editor plugins
* @param file - File to check for editor compatibility
* @returns True if file can be edited, false otherwise
*/
canEditFile(file: UppyFile): boolean;Usage Examples:
// Check if file can be edited
const file = uppy.getFile("file-id");
if (dashboard.canEditFile(file)) {
// Show edit button
showEditButton(file.id);
} else {
// Hide edit button
hideEditButton(file.id);
}
// Filter editable files
const editableFiles = uppy.getFiles().filter(file =>
dashboard.canEditFile(file)
);
// Conditional UI rendering
function renderFileActions(file: UppyFile) {
const canEdit = dashboard.canEditFile(file);
return (
<div className="file-actions">
<button onClick={() => removeFile(file.id)}>Remove</button>
{canEdit && (
<button onClick={() => dashboard.openFileEditor(file)}>
Edit
</button>
)}
</div>
);
}How editor plugins integrate with the dashboard file editor system.
/**
* Editor plugin requirements for dashboard integration
*/
interface EditorPlugin {
/** Plugin must have unique ID */
id: string;
/** Plugin must be 'editor' type */
type: 'editor';
/** Check if plugin can edit specific file */
canEditFile(file: UppyFile): boolean;
/** Select file for editing */
selectFile(file: UppyFile): void;
/** Save editor changes */
save(): void;
/** Render editor interface */
render(): ComponentChild;
}Editor Plugin Examples:
// Image editor plugin
class ImageEditor extends BasePlugin {
type = 'editor';
id = 'ImageEditor';
canEditFile(file: UppyFile): boolean {
return file.type?.startsWith('image/') || false;
}
selectFile(file: UppyFile): void {
this.selectedFile = file;
this.loadImageIntoEditor(file.data);
}
save(): void {
if (this.selectedFile && this.hasChanges) {
const editedBlob = this.exportEditedImage();
this.uppy.setFileData(this.selectedFile.id, editedBlob);
}
}
render() {
return h('div', { className: 'image-editor' }, [
h('canvas', { ref: this.canvasRef }),
h('div', { className: 'editor-tools' }, this.renderTools())
]);
}
}
// Text metadata editor
class MetadataEditor extends BasePlugin {
type = 'editor';
id = 'MetadataEditor';
canEditFile(): boolean {
return true; // Can edit metadata for any file
}
selectFile(file: UppyFile): void {
this.selectedFile = file;
this.loadMetadata(file.meta);
}
save(): void {
if (this.selectedFile) {
const newMeta = this.getFormData();
this.uppy.setFileMeta(this.selectedFile.id, newMeta);
}
}
}Configure automatic editor opening based on file types or conditions.
/**
* Auto-open configuration options
*/
interface AutoOpenConfig {
/** Auto-open editor type on file selection */
autoOpen?: 'metaEditor' | 'imageEditor' | null;
}Auto-Open Examples:
// Auto-open metadata editor
uppy.use(Dashboard, {
autoOpen: 'metaEditor'
});
// Auto-open image editor for images
uppy.use(Dashboard, {
autoOpen: 'imageEditor'
});
// Custom auto-open logic
uppy.use(Dashboard, {
autoOpen: null // Disable automatic opening
});
uppy.on('files-added', (files) => {
files.forEach(file => {
if (file.type?.startsWith('image/') && needsImageEditing(file)) {
dashboard.openFileEditor(file);
} else if (needsMetadataEditing(file)) {
dashboard.toggleFileCard(true, file.id);
}
});
});State properties related to file editor functionality.
/**
* File editor state properties
*/
interface FileEditorState {
/** Whether file editor is currently shown */
showFileEditor: boolean;
/** File ID being edited, null if no file selected */
fileCardFor: string | null;
/** Active overlay type */
activeOverlayType: 'FileEditor' | 'FileCard' | null;
}Events emitted during file editor lifecycle.
/**
* File editor events
*/
interface FileEditorEvents<M extends Meta, B extends Body> {
/** Emitted when file editing starts */
"dashboard:file-edit-start": (file?: UppyFile<M, B>) => void;
/** Emitted when file editing completes */
"dashboard:file-edit-complete": (file?: UppyFile<M, B>) => void;
}Event Usage Examples:
// Track editor usage
uppy.on('dashboard:file-edit-start', (file) => {
console.log(`Started editing: ${file?.name}`);
trackEvent('file_edit_start', {
fileType: file?.type,
fileSize: file?.size
});
});
uppy.on('dashboard:file-edit-complete', (file) => {
console.log(`Finished editing: ${file?.name}`);
trackEvent('file_edit_complete', {
fileType: file?.type,
duration: Date.now() - editStartTime
});
});
// Editor session management
let editSession: EditSession | null = null;
uppy.on('dashboard:file-edit-start', (file) => {
editSession = new EditSession(file);
editSession.start();
});
uppy.on('dashboard:file-edit-complete', (file) => {
if (editSession) {
editSession.complete();
editSession = null;
}
});Handle multiple editor plugins and editor selection.
/**
* Multiple editor management
*/
interface MultipleEditorSupport {
/** Get all registered editor plugins */
getEditors(targets: Target[]): TargetWithRender[];
/** Check if any editor can handle file */
canEditFile(file: UppyFile): boolean;
/** Open file in all compatible editors */
openFileEditor(file: UppyFile): void;
}Multiple Editor Examples:
// Register multiple editors
uppy.use(ImageEditor, { /* options */ });
uppy.use(MetadataEditor, { /* options */ });
uppy.use(CropEditor, { /* options */ });
// Selective editor activation
function openSpecificEditor(file: UppyFile, editorType: string) {
const editors = dashboard.getPluginState().targets
.filter(target => target.type === 'editor' && target.id === editorType);
if (editors.length > 0) {
const plugin = uppy.getPlugin(editorType);
if (plugin && plugin.canEditFile(file)) {
dashboard.openFileEditor(file);
}
}
}
// Editor selection UI
function renderEditorOptions(file: UppyFile) {
const availableEditors = dashboard.getPluginState().targets
.filter(target => target.type === 'editor')
.filter(target => uppy.getPlugin(target.id).canEditFile(file));
return availableEditors.map(editor => (
<button
key={editor.id}
onClick={() => openSpecificEditor(file, editor.id)}
>
Edit with {editor.name}
</button>
));
}Recommended patterns for editor plugin development and integration.
/**
* Editor plugin best practices
*/
interface EditorBestPractices {
/** Implement proper file type checking */
canEditFile(file: UppyFile): boolean;
/** Handle file loading and error states */
selectFile(file: UppyFile): Promise<void>;
/** Provide clear save/cancel actions */
save(): Promise<void>;
/** Clean up resources on unmount */
unmount(): void;
/** Provide keyboard shortcuts */
handleKeyboard(event: KeyboardEvent): void;
/** Support undo/redo operations */
undo(): void;
redo(): void;
}Best Practice Examples:
class AdvancedImageEditor extends BasePlugin {
type = 'editor';
async selectFile(file: UppyFile) {
try {
this.setStatus('loading');
await this.loadImageData(file.data);
this.setStatus('ready');
} catch (error) {
this.setStatus('error');
this.uppy.info('Failed to load image in editor', 'error');
}
}
async save() {
try {
this.setStatus('saving');
const editedData = await this.exportImage();
this.uppy.setFileData(this.selectedFile.id, editedData);
this.setStatus('saved');
} catch (error) {
this.setStatus('error');
this.uppy.info('Failed to save edited image', 'error');
}
}
handleKeyboard(event: KeyboardEvent) {
if (event.ctrlKey || event.metaKey) {
switch (event.key) {
case 'z':
event.preventDefault();
if (event.shiftKey) {
this.redo();
} else {
this.undo();
}
break;
case 's':
event.preventDefault();
this.save();
break;
}
}
}
}Install with Tessl CLI
npx tessl i tessl/npm-uppy--dashboard