CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-dropzone

Lightweight JavaScript library that transforms HTML elements into drag-and-drop file upload zones with thumbnail previews, progress tracking, and extensive customization options.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

thumbnails.mddocs/

Thumbnail Generation

Automatic image thumbnail creation with customizable dimensions, processing queue, and support for various image sources.

Capabilities

Thumbnail Creation

Generate thumbnails for image files with configurable dimensions and processing.

/**
 * Create thumbnail for a file
 * @param file - File object to create thumbnail for
 * @param callback - Optional callback function called when thumbnail is ready
 */
createThumbnail(file: File, callback?: (dataUrl: string) => void): void;

/**
 * Create thumbnail from image URL
 * @param file - File object associated with the thumbnail
 * @param imageUrl - URL of the image to create thumbnail from
 * @param callback - Optional callback function called when thumbnail is ready
 * @param crossOrigin - Cross-origin setting for image loading
 */
createThumbnailFromUrl(
  file: File, 
  imageUrl: string, 
  callback?: (dataUrl: string) => void, 
  crossOrigin?: string
): void;

Usage Examples:

const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true,
  thumbnailWidth: 200,
  thumbnailHeight: 200,
  maxThumbnailFilesize: 10 // MB
});

// Create thumbnail manually
dropzone.on("addedfile", function(file) {
  if (file.type.startsWith("image/")) {
    dropzone.createThumbnail(file, function(dataUrl) {
      console.log("Thumbnail created for:", file.name);
      
      // Use custom thumbnail display
      const img = document.createElement("img");
      img.src = dataUrl;
      img.className = "custom-thumbnail";
      file.previewElement.appendChild(img);
    });
  }
});

// Create thumbnail from URL
const file = { name: "external-image.jpg", type: "image/jpeg" };
dropzone.createThumbnailFromUrl(
  file,
  "https://example.com/image.jpg",
  function(dataUrl) {
    console.log("Thumbnail created from URL");
    // Display thumbnail
  },
  "anonymous" // CORS setting
);

Thumbnail Configuration

Configure thumbnail generation behavior and appearance.

interface ThumbnailOptions {
  /** Whether to create image thumbnails automatically */
  createImageThumbnails?: boolean;
  /** Maximum file size for thumbnail generation in MB */
  maxThumbnailFilesize?: number;
  /** Thumbnail width in pixels */
  thumbnailWidth?: number;
  /** Thumbnail height in pixels */
  thumbnailHeight?: number;
  /** Custom resize function for thumbnail dimensions */
  resize?: (file: File) => {
    srcX: number;      // Source X coordinate
    srcY: number;      // Source Y coordinate
    srcWidth: number;  // Source width
    srcHeight: number; // Source height
    trgX: number;      // Target X coordinate
    trgY: number;      // Target Y coordinate
    trgWidth: number;  // Target width
    trgHeight: number; // Target height
  };
}

Usage Examples:

// Basic thumbnail configuration
const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true,
  thumbnailWidth: 150,
  thumbnailHeight: 150,
  maxThumbnailFilesize: 5 // Only create thumbnails for files < 5MB
});

// Custom resize logic
const customResizeDropzone = new Dropzone("#custom-resize", {
  url: "/upload",
  createImageThumbnails: true,
  thumbnailWidth: 200,
  thumbnailHeight: 200,
  
  resize: function(file) {
    // Custom resize calculation for square thumbnails
    const size = Math.min(file.width, file.height);
    const x = (file.width - size) / 2;
    const y = (file.height - size) / 2;
    
    return {
      srcX: x,
      srcY: y,
      srcWidth: size,
      srcHeight: size,
      trgX: 0,
      trgY: 0,
      trgWidth: this.options.thumbnailWidth,
      trgHeight: this.options.thumbnailHeight
    };
  }
});

// Disable thumbnails for large files
const selectiveDropzone = new Dropzone("#selective-thumbnails", {
  url: "/upload",
  createImageThumbnails: true,
  maxThumbnailFilesize: 2, // 2MB limit
  
  init: function() {
    this.on("addedfile", function(file) {
      if (file.size > 2 * 1024 * 1024 && file.type.startsWith("image/")) {
        console.log("Skipping thumbnail for large file:", file.name);
        // Show placeholder instead
        const placeholder = document.createElement("div");
        placeholder.className = "thumbnail-placeholder";
        placeholder.textContent = "Large image";
        file.previewElement.appendChild(placeholder);
      }
    });
  }
});

Thumbnail Queue Management

Internal thumbnail processing queue to manage thumbnail generation efficiently.

/**
 * Add file to thumbnail generation queue
 * @param file - File to add to thumbnail queue
 */
_enqueueThumbnail(file: File): void;

/**
 * Process the thumbnail generation queue
 */
_processThumbnailQueue(): void;

// Internal properties
_thumbnailQueue: File[];     // Queue of files waiting for thumbnail generation
_processingThumbnail: boolean; // Whether a thumbnail is currently being processed

Usage Examples:

const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true,
  
  init: function() {
    // Monitor thumbnail queue
    const originalEnqueue = this._enqueueThumbnail;
    this._enqueueThumbnail = function(file) {
      console.log("Adding to thumbnail queue:", file.name);
      console.log("Queue length:", this._thumbnailQueue.length);
      originalEnqueue.call(this, file);
    };
    
    // Monitor queue processing
    const originalProcess = this._processThumbnailQueue;
    this._processThumbnailQueue = function() {
      console.log("Processing thumbnail queue, length:", this._thumbnailQueue.length);
      originalProcess.call(this);
    };
  }
});

// Custom thumbnail queue management
function prioritizeThumbnails(dropzone, priorityFiles) {
  // Move priority files to front of thumbnail queue
  priorityFiles.forEach(priorityFile => {
    const index = dropzone._thumbnailQueue.indexOf(priorityFile);
    if (index > 0) {
      dropzone._thumbnailQueue.splice(index, 1);
      dropzone._thumbnailQueue.unshift(priorityFile);
    }
  });
  
  // Process queue if not already processing
  if (!dropzone._processingThumbnail) {
    dropzone._processThumbnailQueue();
  }
}

Thumbnail Events

Events related to thumbnail generation and processing.

/**
 * Thumbnail generated successfully
 * @param file - File for which thumbnail was generated
 * @param dataUrl - Data URL of the generated thumbnail
 */
on("thumbnail", (file: File, dataUrl: string) => void): void;

Usage Examples:

const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true
});

// Handle thumbnail generation
dropzone.on("thumbnail", function(file, dataUrl) {
  console.log("Thumbnail ready for:", file.name);
  
  // Custom thumbnail display
  const container = file.previewElement;
  const img = container.querySelector("img") || document.createElement("img");
  img.src = dataUrl;
  img.className = "dropzone-thumbnail";
  img.alt = file.name;
  
  // Add thumbnail to container if not already there
  if (!container.contains(img)) {
    container.appendChild(img);
  }
  
  // Store thumbnail data on file object
  file.thumbnailData = dataUrl;
  
  // Optional: Create multiple thumbnail sizes
  createAdditionalThumbnails(file, dataUrl);
});

function createAdditionalThumbnails(file, originalDataUrl) {
  const sizes = [
    { width: 64, height: 64, name: "small" },
    { width: 128, height: 128, name: "medium" },
    { width: 256, height: 256, name: "large" }
  ];
  
  sizes.forEach(size => {
    resizeImage(originalDataUrl, size.width, size.height)
      .then(resizedData => {
        file[`thumbnail_${size.name}`] = resizedData;
        console.log(`${size.name} thumbnail created for:`, file.name);
      });
  });
}

function resizeImage(dataUrl, width, height) {
  return new Promise((resolve) => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    const img = new Image();
    
    img.onload = function() {
      canvas.width = width;
      canvas.height = height;
      ctx.drawImage(img, 0, 0, width, height);
      resolve(canvas.toDataURL());
    };
    
    img.src = dataUrl;
  });
}

Advanced Thumbnail Features

Advanced thumbnail processing including iOS fixes and custom image handling.

// Internal utility functions (exposed for advanced usage)

/**
 * Detect vertical squash in iOS images (iOS 6/7 bug fix)
 * @param img - Image element to check
 */
detectVerticalSquash(img: HTMLImageElement): number;

/**
 * Draw image with iOS fixes applied
 * @param ctx - Canvas 2D context
 * @param img - Image to draw
 * @param sx - Source X coordinate
 * @param sy - Source Y coordinate
 * @param sw - Source width
 * @param sh - Source height
 * @param dx - Destination X coordinate
 * @param dy - Destination Y coordinate
 * @param dw - Destination width
 * @param dh - Destination height
 */
drawImageIOSFix(
  ctx: CanvasRenderingContext2D,
  img: HTMLImageElement,
  sx: number, sy: number, sw: number, sh: number,
  dx: number, dy: number, dw: number, dh: number
): void;

Usage Examples:

const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true,
  
  // Custom resize function with iOS handling
  resize: function(file) {
    const info = {
      srcX: 0,
      srcY: 0,
      srcWidth: file.width,
      srcHeight: file.height,
      trgX: 0,
      trgY: 0,
      trgWidth: this.options.thumbnailWidth,
      trgHeight: this.options.thumbnailHeight
    };
    
    // Apply aspect ratio preservation
    const sourceAspect = file.width / file.height;
    const targetAspect = info.trgWidth / info.trgHeight;
    
    if (sourceAspect > targetAspect) {
      // Source is wider, crop width
      info.srcWidth = file.height * targetAspect;
      info.srcX = (file.width - info.srcWidth) / 2;
    } else {
      // Source is taller, crop height
      info.srcHeight = file.width / targetAspect;
      info.srcY = (file.height - info.srcHeight) / 2;
    }
    
    return info;
  }
});

// Custom thumbnail processing with canvas
function createCustomThumbnail(file, callback) {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  const img = new Image();
  
  img.onload = function() {
    // Set canvas size
    canvas.width = 200;
    canvas.height = 200;
    
    // Handle iOS image squashing
    const verticalSquashRatio = detectVerticalSquash(img);
    
    // Draw with iOS fix
    drawImageIOSFix(
      ctx, img,
      0, 0, img.naturalWidth, img.naturalHeight,
      0, 0, 200, 200 / verticalSquashRatio
    );
    
    // Apply custom effects
    ctx.globalAlpha = 0.8;
    ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
    ctx.fillRect(0, 0, 200, 200);
    
    callback(canvas.toDataURL());
  };
  
  img.src = URL.createObjectURL(file);
}

// Use custom thumbnail processing
dropzone.on("addedfile", function(file) {
  if (file.type.startsWith("image/")) {
    createCustomThumbnail(file, function(dataUrl) {
      // Use the custom thumbnail
      file.customThumbnail = dataUrl;
    });
  }
});

Thumbnail Template Integration

Integration with Dropzone's preview template system.

// Default thumbnail template elements
interface ThumbnailTemplate {
  /** Main preview element containing thumbnail */
  previewElement: HTMLElement;
  /** Image element for thumbnail display */
  thumbnailElement: HTMLImageElement;
  /** Filename display element */
  filenameElement: HTMLElement;
  /** File size display element */
  filesizeElement: HTMLElement;
}

Usage Examples:

// Custom preview template with thumbnail integration
const dropzone = new Dropzone("#my-dropzone", {
  url: "/upload",
  createImageThumbnails: true,
  previewTemplate: `
    <div class="dz-preview dz-file-preview">
      <div class="dz-image">
        <img data-dz-thumbnail />
      </div>
      <div class="dz-details">
        <div class="dz-filename"><span data-dz-name></span></div>
        <div class="dz-size"><span data-dz-size></span></div>
      </div>
      <div class="dz-progress">
        <span class="dz-upload" data-dz-uploadprogress></span>
      </div>
      <div class="dz-error-message"><span data-dz-errormessage></span></div>
      <div class="dz-success-mark">✓</div>
      <div class="dz-error-mark">✗</div>
    </div>
  `,
  
  init: function() {
    this.on("thumbnail", function(file, dataUrl) {
      // Thumbnail automatically inserted into [data-dz-thumbnail] element
      console.log("Thumbnail integrated with template for:", file.name);
      
      // Add custom thumbnail effects
      const thumbnailEl = file.previewElement.querySelector("[data-dz-thumbnail]");
      if (thumbnailEl) {
        thumbnailEl.style.borderRadius = "8px";
        thumbnailEl.style.transition = "transform 0.2s";
        
        thumbnailEl.addEventListener("mouseover", function() {
          this.style.transform = "scale(1.05)";
        });
        
        thumbnailEl.addEventListener("mouseout", function() {
          this.style.transform = "scale(1)";
        });
      }
    });
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-dropzone

docs

constructor-config.md

event-system.md

file-management.md

index.md

static-utilities.md

thumbnails.md

upload-processing.md

tile.json