Skip to content

A Nuxt module with opiniated settings to show a file list, directly based on dropzone.js (NOT based on any other plugin or module).

License

Notifications You must be signed in to change notification settings

BananaAcid/nuxt-dropzone-filelist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

35 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Dropzone Filelist

npm version npm downloads License Nuxt

A Nuxt module with opinionated settings to show a file list, directly based on dropzone.js, NOT based on any other dropzone plugin or module.

image

Features

  • no secondary vue dependencies
  • opinionated to enable a specific style
  • inbuild file icon
  • places text input fields for filenames, while uploads files directly to custom url
  • filling files on load
  • even files added by code, have an input
  • it is always relyably multifile internally, dropzone options can limit the file amount

Quick Setup

  1. Add nuxt-dropzone-filelist dependency to your project

    # Using pnpm
    pnpm add -D nuxt-dropzone-filelist
    
    # Using yarn
    yarn add --dev nuxt-dropzone-filelist
    
    # Using npm
    npm install --save-dev nuxt-dropzone-filelist
  2. Add nuxt-dropzone-filelist to the modules section of nuxt.config.ts

    export default defineNuxtConfig({
      modules: [
        'nuxt-dropzone-filelist'
      ]
    })
  3. Use it

    <template>
      <DropzoneFilelist uploadUrl="/api/upload" />
    </template>

Purpose

Adds and removes corresponding input[type="hidden"] elements on dropzone usage:

  • this is the base idea of this module: use dropzone to upload files and provide a hidden input with the filename for a regular <form> element and default logic to handle (as well as to be used with oder form kits)
  • also allow getting an array of uploaded files for standalone usage

Wrap around the real dropzone js lib without intermediate js libs:

  • this module will only break, when something in dropzone is fundemantally changed
  • or nuxt changes something about handling modules

My own styles included:

  • I can push my own overall style
  • extended with features I thought should be preconfigured / included

Configuration

Properties:

Note: When using as attributes, the properies will need to be kebab-case.

id: String = null

  • used for the file[id][] file-input name, falls back to options.paramName -> element id -> random number

nameTemplate: String = 'file[${id}][]'

  • template for name to use, supports: ${id}, ${name}, ${num} (current file index, no logic)
  • Note: no template-string content support

uploadUrl: String (required)

  • upload target url to post the files to

uploadText: String = 'Drag files here or click to upload'

  • upload area text

removeText: String = 'remove?'

  • string to use on the remove file item button

imgMaxSize: String = '2.5em'

  • cube side size for the image preview and file icon

hasClick: Boolean = false

  • false will not show the click highlight and message, but still trigger the click-handler

clickText: String = 'Clicked!'

  • text on the click highlight

clickFn: ( fileName: string, fileElement: HTMLDivElement, dropzone: Dropzone ):void => ...

  • default: console.log("clicked", fileName, { fileElement, dropzone })
  • handler on clicking on an item

clickDurrationMs: Number = 1000

  • how long the click highlight should be shown, 0 will not show the click highlight and message

initialFiles: Array = []

  • list of files to fill in on initialization

filesIconColor: Object = ...

  • default: { txt: "#E9EAEB", rtf: "#7C54AB", pdf: "#f40f02", doc: "#1B5EBE", docx: "#1B5EBE", xls: "#1d6f42", xlsx: "#1d6f42" }
  • file color map for file icon template

fileIconTemplate: String | null = null

  • supply an alternative data string to use as file icon, placeholders: πŸ”† = color, ❓ = font-family, πŸ‘‘ = title
  • be aware: it has to be a image url (base64 or passed through encodeURIComponent()), resulting in something for SVGs like: "data:image/svg+xml,%3Csvg ..."

usePreviewIcon: Boolean = true

  • use to show file icon while loading preview or if preview is not available

useFontAwesomeIcons: Boolean = false

  • uses the fontawesome 5 icons, fontawesome css needs to be available allready

addFileInputs: Boolean = true

  • enable adding text input fields with filenames for each added file

columnMode: String = 'container'

  • adds .***-width classes to adjust to available space, select the method to use:
    • 'media' == media-query, 'container' == container-query, 'column' / nothing == 1fr

useFormelement: Boolean = false

  • usually uses a div as wrapper, this changes it to <form>

disabled: Boolean = false

  • disables using of the element like a form element (also sets a real disabled property)

options: Object = {}

Events:

@init: (options: {dropzone: Dropzone, element: HTMLElement}): void

  • event handler that gt4s called after initialization

@addedFile: (options: {file: DropzoneFile, dropzone: Dropzone, element: HTMLElement}): void

  • event handler that gets called when a file got added (as well as for initialFiles)
  • use to add event handlers to slotted elements

@removedFile: (options: { file: DropzoneFile, dropzone: Dropzone, element: HTMLElement}): void

  • event handler that gets called when a file got removed

Methods:

To use Methods, you need a vue ref property on the component, then access it as usual.

.addFile()

Params:
  1. nameOrFileObj: | string | { name: string; size?: number; imageUrl?: string }
  2. size?: number
  3. imageUrl?: string
  4. color?: string | false
  5. crossOrigin: "anonymous" | "use-credentials" | undefined = 'anonymous'
Examples:
<template>
  <DropzoneFilelist ref="dropzoneFilelist"  uploadUrl="/api/upload" />
</template>

Options API:

<script>
export default defineComponent({
  mounted() {
    // just making calling addFile() shorter
    const dropzoneFilelist = this.$refs.dropzoneFilelist;
    
    // add image
    dropzoneFilelist.addFile('name.jpg', 123123/*bytes*/, '/assets/preview.jpg');
    // add non-image
    dropzoneFilelist.addFile('name.ext', 123123/*bytes*/, undefined, '#FF0000');
    // as object
    dropzoneFilelist.addFile({
      name: 'name.ext',
      size: 123123,
      color: '#ff0000',
      // imageUrl: '/assets/preview.jpg'
    });
  }
});
</script>

Composition API:

<script setup>
import { ref, onMounted } from 'vue';

// declare a ref to hold the element reference
// the name must match template ref value
const dropzoneFilelist = ref(null);

onMounted(() => {
  // add image
  dropzoneFilelist.addFile('name.jpg', 123123/*bytes*/, '/assets/preview.jpg');
  // add non-image
  dropzoneFilelist.addFile('name.ext', 123123/*bytes*/, undefined, '#FF0000');
  // as object
  dropzoneFilelist.addFile({
    name: 'name.ext',
    size: 123123,
    color: '#ff0000',
    // imageUrl: '/assets/preview.jpg'
  });
});
</script>

Slots

default

  • use to add html to use as actions that will be visible on hover
  • use event @addedFile to add click handlers

Solutions & Examples

Tests are available here in the ./pages/ *.vue files

  • item click handler
    • to have a click action on a file item, add a handler with :click-fn
    • appearance can be changed by :has-click,:click-text,:click-durration-ms
  • action buttons
    • use default slot for additional buttons/links
    • handler: use @addedFile to add click handler by finding it with .querySelector()
      <template>
        <DropzoneFilelist upload-url="/api/upload" @addedFile="addedFile">
          <button data-action-open-external>open</button>
        </DropzoneFilelist>
      </template>
      
      <script>
        export default defineComponent({
          methods: {
            addedFile({file, dropzone, element}) {
              element
                .querySelector('[data-action-open-external]')
                .addEventListener('click', (ev) => {
                  ev.preventDefault();
                  ev.stopPropagation();
      
                  // custom action
                  alert(file.name);
                });
            },
          },
        });
      </script>
  • loading initial files
    • :initial-files needs an array of: {name, filesize , imgUrl?, color?} (imgUrl or color, color for file icon)
  • getting a count of added files
    • this.$refs.dzf.dropzone.files.length
  • getting list of added files
    • this.$refs.dzf.dropzone.files => File[]
  • item select toggle
    • use an item click handler
    • toggle a class on element to change the background color
    • use an array to add or remove the file names to keep track
  • limit files
    • use dropzone's options, namely maxFiles:number
      <DropzoneFilelist upload-url="/api/upload" :options="{maxFiles: 2}" />
  • allow specific files only
    • use dropzone's options, namely acceptedFiles:string
      <DropzoneFilelist upload-url="/api/upload" :options="{acceptedFiles: 'image/jpeg,image/png,application/pdf'}" />
  • upload to nuxt /api
    • use npm/h3-formidable
    • ... to let formidable save the file first, then copy it to where it should go (moving is usually a problem due to file permissions on temp files)
      // ./server/api/upload.post.ts
      import fs from "node:fs/promises";
      import path from "node:path";
      
      import { readFiles } from "h3-formidable";
      
      export default defineEventHandler(async (event) => {
      
        // ... do some validation here: session / bearer-token / jwt / ...
      
        const { fields, files } = await readFiles(event, {
          includeFields: true,
          maxFileSize: 20 * 1024 * 1024,
          filter: () => true,
          // filter: ({ name, originalFilename, mimetype }) => mimetype && mimetype.includes("image"),
      
          // uploadDir: './upload', // direct uploading
          // keepExtensions: true,
      
          // other formidable options here
          // https://github.com/node-formidable/formidable#options
        });
      
      
        // if not `uploadDir` ... we move the file:
        let f: any = files.file[0];
      
        const targetPath = path.join(
          await fs.realpath("./upload"),
          f.originalFilename
        );
      
        await fs.copyFile(f.filepath, targetPath);
        await fs.unlink(f.filepath);
      
        // if a string is returned, it will be shown as error by DropZone
        return "";
    • Nuxt (h3) has native Session support: useSession() (my documentation and example)
    • the server could also be a PHP/ASPx/... server (when using the dropzone component from this package without the nuxt module part)

Development

# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release

About

A Nuxt module with opiniated settings to show a file list, directly based on dropzone.js (NOT based on any other plugin or module).

Resources

License

Stars

Watchers

Forks

Packages

No packages published