npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

dropzone-frontend

v1.0.32

Published

Turns an html5 - component in an amazing dropzone for fileupload. - There is a fallback to an html5 with input type file provided. - It can directly be consumed as a typescript library. - Styling can be changed through changing of scss variables or thr

Downloads

34

Readme

dropzone-frontend

Turns an html5 - component in an amazing dropzone for fileupload.

  • There is a fallback to an html5 with input type file provided.
  • It can directly be consumed as a typescript library.
  • Styling can be changed through changing of scss variables or through css - overrides.
  • You have to provide the handlers for uploading, downloading, deleting yourself offcourse. So, there are no strings attached to any backend technology.
  • The dropzone can be populated with current state. It can be used for a readonly file output object.
  • i18n: you can add custom labels, or translate labels with the i18n dropzone resource.
  • configure the accepted droppable/uploadable types with fileTypeSpecifiers. ( https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers )
  • The current setup needs a scss compilation step.
  • can be used for uploading on drop, or deleting on delete pressed
  • can be used for uploading after an external trigger (for example a button press)
  • IE11 support (include polyfills)

dropzone-example-giffy

Installation

npm i dropzone-frontend

How to use

TypeScript setup with scss

  • Include scss in scss file

      @import '../../node_modules/dropzone-frontend/lib/styles/export/dropzone-lib';
  • Html file: add a container on your Html file

      <div id="myDropzone">
      </div>
        
  • TypeScript file: turn the html-container in a dropzone

Meta flow

  • turn the html element in a dropzone

      const dropzone: Dropzone = new Dropzone(window.document.querySelector('#myDropzone')/*,
          optionally a dropzoneProps - object can be added,  
          optionally an i18n - override can be added */);
        
  • add a listener for a file drop (give feedback with the progress, succesCallback and errorCallback - functions)

      dropzone.addOnFileDroppedEventListener((file: DropzoneFile,
          successCallback: (createdDropzoneFile: DropzoneFile) => any | void,
          errorCallback: (error?: any) => any,
          progress: (uploadPercentage: number) => void) => {
          // <- upload the file anyway you want
  • add a listener when the user wants to download the file

      dropzone.addDownloadFileEventListener((dropzoneFile: DropzoneFile) => { 
          // <- download the file any way you want
          
  • add a delete listener

      dropzone.addOnDeleteFileEventListener((file: DropzoneFile,
         successCallback: (deletedFile?: any) => any,
         errorCallback: (error?: any) => void) => {
         // <- delete the file, any way you want.
                                   
  • popupulate the dropzone with the current state. (Expected type: DropzoneFile)

      dropzone.setDropzoneFiles(jsonData); // put the loaded files on the dropzone

Full example with a node backend running on port 3000.

import {DropzoneFile} from "dropzone-frontend/lib/scripts/model/dropzone-file";
import {Dropzone} from "dropzone-frontend/lib/scripts/dropzone";

const dropzone: Dropzone = new Dropzone(window.document.querySelector('#myDropzone')/*, 
    optionally a dropzoneProps - object can be added, 
    optionally an i18n - override can be added */); 

// upload the file when it is dropped.
dropzone.addOnFileDroppedEventListener((file: DropzoneFile,
    successCallback: (createdDropzoneFile: DropzoneFile) => any | void,
    errorCallback: (error?: any) => any,
    progress: (uploadPercentage: number) => void) => {
  const formData = new FormData();
  formData.append('file', file.file); 
    // <- the file is in the file.file attribute.
  
  if (xhr.upload) {
    xhr.upload.onprogress = (e: any) => {
      const done = e.position || e.loaded,
        total = e.totalSize || e.total;
      const percentage = Math.floor((done / total) * 1000) / 10;
      progress(percentage); 
        // <- give progress feedback by calling progress with percentage
    };
  }
  
  xhr.onerror = () => {
    errorCallback(); // <- if something goes wrong with the upload, let it know at the dropzone with the error callback function
  };
  
  xhr.onreadystatechange = (e: any) => {
    if (xhr.readyState === 4 && xhr.status === 200) {
      successCallback(file); 
        // <- if everything goes well notify the dropzone by the successCallback
        // the successCallback excepts an object of type DropzoneFile
    }
  };
  xhr.open('post', 'http://localhost:3000/api/upload-file', true);
    xhr.send(formData); // <- upload the file anyway you want
  });

// handle the download file action
dropzone.addDownloadFileEventListener((dropzoneFile: DropzoneFile) => {
  window.open(`http://localhost:3000/api/upload-file/${dropzoneFile.fileName}`); 
    // <- download the file any way you want
});

// When the user want's to delete the file, do the delete
dropzone.addOnDeleteFileEventListener((file: DropzoneFile,
   successCallback: (deletedFile?: any) => any,
   errorCallback: (error?: any) => void) => {
  const formData = new FormData();
  formData.append('file', file.file);
  fetch(`http://localhost:3000/api/upload-file/${file.id}`, {
    method: 'DELETE', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // no-referrer, *client
    body: formData, // body data type must match "Content-Type" header
  })
    .then(data => {
      successCallback(data as any);
    }) // JSON-string from `response.json()` call
    .catch(error => {
      errorCallback(error);
    });
});

// load all the files
fetch('http://localhost:3000/api/upload-file', {
  method: 'GET', // *GET, POST, PUT, DELETE, etc.
  mode: 'cors', // no-cors, cors, *same-origin
  cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
  credentials: 'same-origin', // include, *same-origin, omit
  headers: {
    'Content-Type': 'application/json',
  },
  redirect: 'follow', // manual, *follow, error
  referrer: 'no-referrer', // no-referrer, *client
  body: undefined, // body data type must match "Content-Type" header
})
  .then(data => {
    data.json().then(jsonData => {
      jsonData.forEach((aJson: any) => {
        if (aJson.thumbnail) {
          aJson.thumbnailUrl = 'http://localhost:3000/api/upload-file/' + aJson.thumbnail;
        }
      });
      dropzone.setDropzoneFiles(jsonData); // put the loaded files on the dropzone
    });
  }) // JSON-string from `response.json()` call
  .catch(error => {
    console.error('error during read', error);
  });
  

Upload after an external trigger (buttonpress): Full example with a node backend running on port 3000.

  • Html file: add a container on your Html file

      <h1>Attachments  but no autoupload</h1>
      <div class="m-dropzone--no-autoupload">
      </div>
      <button id="saveNotUploadDropzoneButton">SAVE THEM</button>
        
  • TypeScript file: turn the html-container in a dropzone Keep track of the items to save/delete. Use the isSaved - flag of the DropzoneFile - model.

const notAutoUploadOnDropDropzone: Dropzone = new Dropzone(
    window.document.querySelector('.m-dropzone--no-autoupload'),
    {
        readonly: false,
    },
    {
        uploadProgressLabel: 'upload progress',
        uploadErrorLabel: 'upload error',
        uploadCompleteLabel: 'De upload is voltooid',
        browseLabel: 'browse.',
        dropFilesLabel: 'Sleep bestanden om bij te voegen of ',
    },
);

let filesToSave: DropzoneFile[] = []; // <- keep state of filesToSave
let filesToDelete: DropzoneFile[] = []; // <- keep state of filesToDelete

notAutoUploadOnDropDropzone.addOnFileDroppedEventListener((file, successCallback, errorCallback, progress) => {
    // <- do not save to backend on drop! but update state.
    filesToSave.push(file);
    file.canBeDeleted = true;
    if (file.file.type.indexOf('image/') === 0) {
        file.thumbnailObjectURL = URL.createObjectURL(file.file);
        // <- optionally provide a thumbnailObjectURL for an image type
    }
    notAutoUploadOnDropDropzone.updateDropzoneFile(file);
});

notAutoUploadOnDropDropzone.addDownloadFileEventListener((dropzoneFile: DropzoneFile) => {
    if (dropzoneFile.isSaved) {
        window.open(`http://localhost:3000/api/upload-file/${dropzoneFile.fileName}`);
    } else {
        window.alert('This file cannot be donwloaded yet, because it is not saved yet!');
    }
});


notAutoUploadOnDropDropzone.addOnDeleteFileEventListener((file, successCallback, errorCallback) => {
    // <- do not persist to backend, but keep track of the filesToSave and delete state.
    if (file.isSaved) { // <- use the isSaved flag to keep your state nice and clean.
        filesToDelete.push(file);
    } else if (filesToSave.indexOf(file) !== -1) {
        // <- item is not yet saved, so it doesn't need to be persisted to backend later.
        filesToSave = filesToSave.filter(aFileToSave => {
            return aFileToSave.id !== file.id;
        });
        
    }
    successCallback(file); // <- successCallback removes the item visually from the dropzoe
});

// <- init the dropzone with your file data.
fetch('http://localhost:3000/api/upload-file', {
    method: 'GET', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
        'Content-Type': 'application/json',
    },
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // no-referrer, *client
    body: undefined, // body data type must match "Content-Type" header
})
    .then(data => {
        data.json().then(jsonData => {
            jsonData.forEach((aJson: any | DropzoneFile) => {
                if (aJson.thumbnail) {
                    aJson.thumbnailUrl = 'http://localhost:3000/api/upload-file/' + aJson.thumbnail;
                }
                aJson.isSaved = true;
            });
            notAutoUploadOnDropDropzone.setDropzoneFiles(jsonData);
        });
    }) 
    .catch(error => {
        console.error('error during read', error);
    });

// <- add a button and handle the file upload/delete there.
// off course you can prefer to send them all at once to the backend. Suit yourself!

document.querySelector('#saveNotUploadDropzoneButton').addEventListener('click', () => {
    filesToDelete.forEach(aFileToDelete => {
        const formData = new FormData();
        formData.append('file', aFileToDelete.file);

        fetch(`http://localhost:3000/api/upload-file/${aFileToDelete.id}`, {
            method: 'DELETE', // *GET, POST, PUT, DELETE, etc.
            mode: 'cors', // no-cors, cors, *same-origin
            cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
            credentials: 'same-origin', // include, *same-origin, omit
            //headers: {
            //    'Content-Type': 'multipart/form-data', https://muffinman.io/uploading-files-using-fetch-multipart-form-data/
            //},
            redirect: 'follow', // manual, *follow, error
            referrer: 'no-referrer', // no-referrer, *client
            body: formData, // body data type must match "Content-Type" header
        })
            .then(data => {
                console.log('file is deleted!');
            })
            .catch(error => {
                window.alert('An error did occur while deleting file ' + aFileToDelete.fileName + '. The file is added again to the dropzone.');
                notAutoUploadOnDropDropzone.appendDropzoneFile(aFileToDelete);
            });
        filesToDelete = [];
    });

    filesToSave.forEach(aFileToSave => {
        const formData = new FormData();
        formData.append('file', aFileToSave.file);

        const xhr = new XMLHttpRequest();

        xhr.onerror = () => {
            console.log('** An error occurred during the transaction');
            window.alert('An error did occur while saving file ' + aFileToSave.fileName + '.');
            notAutoUploadOnDropDropzone.appendDropzoneFile(aFileToSave);
            filesToSave.push(aFileToSave);
        };

        xhr.onreadystatechange = (e: any) => {
            if (xhr.readyState === 4 && xhr.status === 200) {
                aFileToSave.canBeDeleted = true;
                aFileToSave.canBeDownloaded = true;
                aFileToSave.isSaved = true;
                notAutoUploadOnDropDropzone.updateDropzoneFile(aFileToSave);
            }
        };
        xhr.open('post', 'http://localhost:3000/api/upload-file', true);
        // xhr.setRequestHeader("Content-Type","multipart/form-data");
        xhr.send(formData);
    });
    filesToSave = [];
});

configure accepted filetypes

import {DropzoneFile} from "dropzone-frontend/lib/scripts/model/dropzone-file";
import {Dropzone} from "dropzone-frontend/lib/scripts/dropzone";
import {DefaultDropzoneProps} from './dropzone/model/dropzone-props';

const dropzoneProps = new DefaultDropzoneProps();
// dropzoneProps.acceptedFileTypeSpecifiers = ['.pdf', 'video/*', 'image/png'];
dropzoneProps.acceptedFileTypeSpecifiers = ['application/pdf'];

let dropzone: Dropzone = new Dropzone(window.document.querySelector('.m-dropzone--default'), dropzoneProps, {
    uploadProgressLabel: 'upload progress',
    uploadErrorLabel: 'upload error',
    uploadCompleteLabel: 'De upload is voltooid',
    browseLabel: 'browse.',
    dropFilesLabel: 'Sleep bestanden om bij te voegen of ',
    invalidTypeText: 'Gelieve enkel pdf - bestanden bij te voegen.'
});

IE11 support with polyfills

You can make the dropzone work on IE11 with an array polyfill.

For example install the polyfill package: npm i core-js

And then include next polyfill.

import 'core-js/es/array';