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.
- 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
-
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
-
Add
nuxt-dropzone-filelist
to themodules
section ofnuxt.config.ts
export default defineNuxtConfig({ modules: [ 'nuxt-dropzone-filelist' ] })
-
Use it
<template> <DropzoneFilelist uploadUrl="/api/upload" /> </template>
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
Note: When using as attributes, the properies will need to be kebab-case.
- used for the file[id][] file-input name, falls back to
options.paramName
-> element id -> random number
- template for name to use, supports:
${id}
,${name}
,${num}
(current file index, no logic) - Note: no template-string content support
- upload target url to post the files to
- upload area text
- string to use on the remove file item button
- cube side size for the image preview and file icon
- false will not show the click highlight and message, but still trigger the click-handler
- text on the click highlight
- default:
console.log("clicked", fileName, { fileElement, dropzone })
- handler on clicking on an item
- how long the click highlight should be shown, 0 will not show the click highlight and message
- list of files to fill in on initialization
- default:
{ txt: "#E9EAEB", rtf: "#7C54AB", pdf: "#f40f02", doc: "#1B5EBE", docx: "#1B5EBE", xls: "#1d6f42", xlsx: "#1d6f42" }
- file color map for file icon template
- 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 ..."
- use to show file icon while loading preview or if preview is not available
- uses the fontawesome 5 icons, fontawesome css needs to be available allready
- enable adding text input fields with filenames for each added file
- adds
.***-width
classes to adjust to available space, select the method to use:'media'
== media-query,'container'
== container-query,'column'
/ nothing == 1fr
- usually uses a div as wrapper, this changes it to
<form>
- disables using of the element like a form element (also sets a real
disabled
property)
- any native dropzone options
uploadMultiple
has no effect
- event handler that gt4s called after initialization
- event handler that gets called when a file got added (as well as for
initialFiles
) - use to add event handlers to slotted elements
- event handler that gets called when a file got removed
To use Methods, you need a vue ref property on the component, then access it as usual.
nameOrFileObj: | string | { name: string; size?: number; imageUrl?: string }
size?: number
imageUrl?: string
color?: string | false
crossOrigin: "anonymous" | "use-credentials" | undefined = 'anonymous'
<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>
- use to add html to use as actions that will be visible on hover
- use event
@addedFile
to add click handlers
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
- to have a click action on a file item, add a handler with
- 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}" />
- use dropzone's options, namely
- allow specific files only
- use dropzone's options, namely
acceptedFiles:string
<DropzoneFilelist upload-url="/api/upload" :options="{acceptedFiles: 'image/jpeg,image/png,application/pdf'}" />
- use dropzone's options, namely
- 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)
- use
# 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