Lightweight JavaScript library that transforms HTML elements into drag-and-drop file upload zones with thumbnail previews, progress tracking, and extensive customization options.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Automatic image thumbnail creation with customizable dimensions, processing queue, and support for various image sources.
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
);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);
}
});
}
});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 processedUsage 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();
}
}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 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;
});
}
});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