CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-uppy--dashboard

Universal UI plugin for Uppy file uploader providing comprehensive dashboard interface with drag-and-drop, file previews, metadata editing, and progress tracking.

Pending
Overview
Eval results
Files

file-management.mddocs/

File Management

Handle file selection, metadata editing, and file operations within the dashboard.

Capabilities

Add Files

Add files to the uploader with automatic metadata detection and validation.

/**
 * Add files to the uploader
 * Handles file validation, metadata extraction, and error handling
 * @param files - Array of File objects to add
 */
addFiles(files: File[]): void;

Usage Examples:

import Dashboard from "@uppy/dashboard";

const dashboard = uppy.getPlugin("Dashboard") as Dashboard;

// Add files from input element
const fileInput = document.getElementById("file-input") as HTMLInputElement;
fileInput.addEventListener("change", (event) => {
  const files = Array.from(event.target.files || []);
  dashboard.addFiles(files);
});

// Add files from drag and drop
function handleDrop(event: DragEvent) {
  event.preventDefault();
  const files = Array.from(event.dataTransfer?.files || []);
  dashboard.addFiles(files);
}

// Add files programmatically
async function addFromCustomSource() {
  const customFiles = await fetchFilesFromAPI();
  const fileObjects = customFiles.map(data => new File([data.blob], data.name));
  dashboard.addFiles(fileObjects);
}

Behavior:

  • Creates file descriptors with source, name, type, and data
  • Extracts relative path metadata for directory uploads
  • Validates files against Uppy restrictions
  • Logs file addition events
  • Handles and logs any validation errors

Save File Card

Save metadata for a specific file and close the file card interface.

/**
 * Save file metadata
 * Updates file metadata and closes file card interface
 * @param meta - Metadata object to save
 * @param fileID - ID of the file to update
 */
saveFileCard(meta: Meta, fileID: string): void;

Usage Examples:

// Save file metadata from form
function saveFileMetadata(fileId: string, formData: FormData) {
  const metadata = {
    name: formData.get("title") as string,
    caption: formData.get("caption") as string,
    altText: formData.get("altText") as string
  };
  
  dashboard.saveFileCard(metadata, fileId);
}

// Save metadata with validation
function saveWithValidation(fileId: string, metadata: any) {
  // Validate required fields
  const requiredFields = uppy.opts.restrictions.requiredMetaFields || [];
  const missing = requiredFields.filter(field => !metadata[field]);
  
  if (missing.length > 0) {
    console.error("Missing required fields:", missing);
    return;
  }
  
  dashboard.saveFileCard(metadata, fileId);
}

// Programmatic metadata saving
uppy.on("file-added", (file) => {
  // Auto-populate metadata for certain file types
  if (file.type?.startsWith("image/")) {
    const autoMetadata = {
      altText: `Image: ${file.name}`,
      category: "image"
    };
    dashboard.saveFileCard(autoMetadata, file.id);
  }
});

Behavior:

  • Calls uppy.setFileMeta() to update file metadata
  • Calls toggleFileCard(false, fileID) to close the file card
  • Updates dashboard state to hide file card interface

File Card Management

Control the display and interaction with individual file metadata cards.

/**
 * Toggle file card visibility for specific file
 * @param show - Whether to show or hide the file card
 * @param fileID - ID of the file to show card for
 */
private toggleFileCard(show: boolean, fileID: string): void;

File Card State:

/**
 * File card related state properties
 */
interface FileCardState {
  /** File ID for which card is shown, null if no card is active */
  fileCardFor: string | null;
  /** Whether file editor is currently shown */
  showFileEditor: boolean;
  /** Active overlay type */
  activeOverlayType: 'FileCard' | 'FileEditor' | null;
}

Metadata Fields Configuration

Configure metadata fields for file information collection.

/**
 * Metadata field configuration
 */
interface MetaField {
  /** Unique field identifier */
  id: string;
  /** Display name for the field */
  name: string;
  /** Placeholder text for input field */
  placeholder?: string;
  /** Custom render function for field */
  render?: (field: FieldRenderOptions, h: PreactRender) => VNode<any>;
}

/**
 * Field render options passed to custom render functions
 */
interface FieldRenderOptions {
  /** Current field value */
  value: string;
  /** Function to update field value */
  onChange: (newVal: string) => void;
  /** CSS classes for field styling */
  fieldCSSClasses: { text: string };
  /** Whether field is required */
  required: boolean;
  /** Form identifier */
  form: string;
}

Metadata Configuration Examples:

// Static metadata fields
uppy.use(Dashboard, {
  metaFields: [
    { 
      id: "title", 
      name: "Title", 
      placeholder: "Enter file title" 
    },
    { 
      id: "description", 
      name: "Description", 
      placeholder: "Describe this file" 
    },
    { 
      id: "tags", 
      name: "Tags", 
      placeholder: "Comma-separated tags" 
    }
  ]
});

// Dynamic metadata fields based on file type
uppy.use(Dashboard, {
  metaFields: (file) => {
    const baseFields = [
      { id: "title", name: "Title", placeholder: "Enter title" }
    ];
    
    if (file.type?.startsWith("image/")) {
      return [
        ...baseFields,
        { id: "altText", name: "Alt Text", placeholder: "Describe the image" },
        { id: "caption", name: "Caption" }
      ];
    } else if (file.type?.startsWith("video/")) {
      return [
        ...baseFields,
        { id: "transcript", name: "Transcript" },
        { id: "duration", name: "Duration" }
      ];
    }
    
    return baseFields;
  }
});

// Custom field rendering
uppy.use(Dashboard, {
  metaFields: [
    {
      id: "priority",
      name: "Priority",
      render: (field, h) => {
        return h('select', {
          value: field.value,
          onChange: (e) => field.onChange(e.target.value),
          className: field.fieldCSSClasses.text
        }, [
          h('option', { value: '' }, 'Select priority'),
          h('option', { value: 'low' }, 'Low'),  
          h('option', { value: 'medium' }, 'Medium'),
          h('option', { value: 'high' }, 'High')
        ]);
      }
    }
  ]
});

File Events

Events related to file management operations.

/**
 * File management events
 */
interface DashboardFileEvents<M extends Meta, B extends Body> {
  /** Emitted when file card editing starts */
  "dashboard:file-edit-start": (file?: UppyFile<M, B>) => void;
  /** Emitted when file card editing completes */
  "dashboard:file-edit-complete": (file?: UppyFile<M, B>) => void;
}

Event Usage Examples:

// Track file editing
uppy.on("dashboard:file-edit-start", (file) => {
  console.log(`Started editing: ${file?.name}`);
  // Show editing indicators, disable other actions, etc.
});

uppy.on("dashboard:file-edit-complete", (file) => {
  console.log(`Finished editing: ${file?.name}`);
  // Hide editing indicators, enable other actions, etc.
});

// Auto-save drafts during editing
let editingFile: UppyFile | null = null;
uppy.on("dashboard:file-edit-start", (file) => {
  editingFile = file;
  startAutoSave();
});

uppy.on("dashboard:file-edit-complete", () => {
  editingFile = null;
  stopAutoSave();
});

function startAutoSave() {
  const interval = setInterval(() => {
    if (editingFile) {
      // Save current form state as draft
      saveDraft(editingFile.id);
    }
  }, 30000); // Save every 30 seconds
}

File Validation

Built-in file validation and restriction handling.

/**
 * File validation is handled by Uppy core
 * Dashboard respects these restriction options:
 */
interface UppyRestrictions {
  /** Allowed file types (MIME types or extensions) */
  allowedFileTypes?: string[];
  /** Maximum number of files */
  maxNumberOfFiles?: number;
  /** Maximum file size in bytes */
  maxFileSize?: number;
  /** Minimum file size in bytes */
  minFileSize?: number;
  /** Required metadata fields */
  requiredMetaFields?: string[];
}

Validation Examples:

// Configure file restrictions
const uppy = new Uppy({
  restrictions: {
    allowedFileTypes: ['.jpg', '.jpeg', '.png', '.gif'],
    maxNumberOfFiles: 5,
    maxFileSize: 10 * 1024 * 1024, // 10MB
    requiredMetaFields: ['title', 'description']
  }
});

// Handle validation errors
uppy.on('restriction-failed', (file, error) => {
  console.error(`File ${file?.name} rejected:`, error.message);
  // Show user-friendly error message
  showErrorMessage(error.message);
});

// Custom validation
function validateFileContent(file: File): Promise<boolean> {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      // Custom validation logic
      const content = e.target?.result;
      const isValid = performCustomValidation(content);
      resolve(isValid);
    };
    reader.readAsArrayBuffer(file);
  });
}

File Processing

Integration with file processing and transformation.

/**
 * File processing integration points
 */
interface FileProcessing {
  /** Generate thumbnails for files */
  handleRequestThumbnail(file: UppyFile): void;
  /** Cancel thumbnail generation */
  handleCancelThumbnail(file: UppyFile): void;
  /** Wait for thumbnails before upload */
  waitForThumbnailsBeforeUpload?: boolean;
}

Processing Examples:

// Configure thumbnail generation
uppy.use(Dashboard, {
  thumbnailWidth: 300,
  thumbnailHeight: 300,
  thumbnailType: 'image/jpeg',
  waitForThumbnailsBeforeUpload: true
});

// Custom file processing
uppy.on('file-added', async (file) => {
  if (file.type?.startsWith('image/')) {
    // Extract EXIF data
    const exifData = await extractExifData(file.data);
    uppy.setFileMeta(file.id, {
      ...file.meta,
      exif: exifData,
      location: exifData.gps
    });
  }
});

// Process files before upload
uppy.on('preprocess-progress', (file, progress) => {
  console.log(`Processing ${file.name}: ${progress.uploadProgress}%`);
});

uppy.on('preprocess-complete', (file) => {
  console.log(`Finished processing: ${file.name}`);
});

Drag and Drop Integration

Built-in drag and drop functionality for file selection.

/**
 * Drag and drop event handlers
 */
interface DragDropHandlers {
  /** Handle drag over events */
  onDragOver?: (event: DragEvent) => void;
  /** Handle drag leave events */  
  onDragLeave?: (event: DragEvent) => void;
  /** Handle drop events */
  onDrop?: (event: DragEvent) => void;
}

Drag and Drop Examples:

// Custom drag and drop behavior
uppy.use(Dashboard, {
  onDragOver: (event) => {
    console.log('Files being dragged over dashboard');
    // Add visual feedback
    event.currentTarget.classList.add('drag-active');
  },
  
  onDragLeave: (event) => {
    console.log('Drag left dashboard area');
    // Remove visual feedback
    event.currentTarget.classList.remove('drag-active');
  },
  
  onDrop: (event) => {
    console.log('Files dropped on dashboard');
    // Custom drop handling
    const files = Array.from(event.dataTransfer?.files || []);
    // Additional processing before adding files
    files.forEach(file => preprocessFile(file));
  }
});

// Drag and drop validation
function validateDropEvent(event: DragEvent): boolean {
  const items = event.dataTransfer?.items;
  if (!items) return false;
  
  // Check if all items are files
  for (let i = 0; i < items.length; i++) {
    if (items[i].kind !== 'file') {
      return false;
    }
  }
  
  return true;
}

Install with Tessl CLI

npx tessl i tessl/npm-uppy--dashboard

docs

configuration.md

file-editor.md

file-management.md

index.md

modal-management.md

panel-management.md

plugin-integration.md

tile.json