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-filelistdependency 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-filelistto themodulessection ofnuxt.config.tsexport 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
.***-widthclasses 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
disabledproperty)
- any native dropzone options
uploadMultiplehas 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?: numberimageUrl?: stringcolor?: string | falsecrossOrigin: "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
@addedFileto 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
@addedFileto 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-filesneeds 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
elementto 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