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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@papit/file-input

v0.0.1

Published

An elegant solution to file input — fully featured with drag-and-drop, file indicator, upload progress, and form association

Readme

@papit/file-input

An elegant solution to file input — fully featured with drag-and-drop, file indicator, upload progress, and form association.

Logo


Type Tests NPM version


Installation

npm install @papit/file-input

HTML

<script type="module" defer>
  import "@papit/file-input";
</script>

<pap-file-input></pap-file-input>

JavaScript / TypeScript

import "@papit/file-input";

Usage

Basic

<pap-file-input></pap-file-input>

With indicator (shows selected files)

<pap-file-input indicator></pap-file-input>

Dropzone

<pap-file-input dropzone indicator></pap-file-input>

Multiple files

<pap-file-input multiple indicator></pap-file-input>

Restrict accepted file types

<pap-file-input accept="image/*,.pdf"></pap-file-input>

Custom accept text via slot:

<pap-file-input accept="image/*" dropzone>
  <span slot="accept">PNG, JPG, GIF up to 10MB</span>
</pap-file-input>

Upload on demand

<pap-file-input id="fi" uploadurl="/api/upload" indicator></pap-file-input>
<button onclick="document.getElementById('fi').upload()">Submit</button>

Properties

| Property | Attribute | Type | Default | Description | | -------------- | -------------- | ----------------- | ----------- | --------------------------------------------------------------------------------------------- | | multiple | multiple | boolean | false | Allow selecting multiple files. When true, upload sends all files in a single batch request | | accept | accept | string | "" | Accepted file types, passed directly to the native input (e.g. image/*, .pdf) | | dropzone | dropzone | boolean | false | Renders a drag-and-drop zone | | indicator | indicator | boolean | false | Renders the list of selected files with name, size, delete, and upload progress | | name | name | string | "file" | Field name used in FormData for both form submission and upload requests | | uploadurl | uploadurl | string | undefined | Endpoint URL for upload(). Required for upload to work | | uploadmethod | uploadmethod | "post" \| "put" | "POST" | HTTP method used by upload() |


Methods

upload(): Promise<void>

Triggers file upload to uploadurl.

  • When multiple is false: sends one XHR per file in parallel, each with individual progress tracking
  • When multiple is true: sends all files in a single batched request; progress is shared across all files
const fi = document.querySelector("pap-file-input");
await fi.upload();

Throws if any individual upload fails. For batch, rejects on non-2xx status.

files (getter)

Returns the currently selected File[] array.

const files = fi.files; // File[]

Events

| Event | Detail | Description | | -------- | ------------------- | -------------------------------------------- | | change | — | Fired when files are added or removed | | upload | { total: number } | Fired when all uploads complete successfully |

fi.addEventListener("change", () => {
  console.log(fi.files);
});

fi.addEventListener("upload", (e) => {
  console.log(`Uploaded ${e.detail.total} files`);
});

CSS Parts

| Part | Element | Description | | ----------- | -------------- | --------------------------------------------------------------- | | label | <label> | Outer label wrapping the button and dropzone content | | dropzone | <div> | The drag-and-drop content area (visible when dropzone is set) | | accept | <p> | The accepted file types hint text | | button | <pap-button> | The "Upload" trigger button | | input | <input> | The hidden native file input | | indicator | <section> | The file list container | | file-link | <a> | Each file's anchor link in the indicator |

CSS States

| State | Description | | ------------------ | ------------------------------------------------------------------ | | :state(dragging) | Applied to :host while files are being dragged over the dropzone |

pap-file-input::part(dropzone) {
  border: 2px dashed #ccc;
  border-radius: 8px;
  padding: 2rem;
}

pap-file-input:state(dragging)::part(dropzone) {
  border-color: blue;
  background: #e8f0ff;
}

Slots

| Slot | Description | | -------- | ------------------------------------------------------------------ | | accept | Override the default accepted file types hint text in the dropzone |


Form Association

pap-file-input is a form-associated custom element. It works inside <form> like a native <input type="file">:

<form>
  <pap-file-input name="attachments" multiple></pap-file-input>
  <button type="submit">Submit</button>
</form>

The name attribute controls the FormData field name. Multiple files are appended under the same key (standard multipart behaviour).


Accessibility (WCAG)

  • Label is associated with the input via for/id
  • File indicator uses aria-live="polite" — additions and deletions are announced to screen readers
  • Delete buttons carry aria-label="delete {filename}" for unambiguous identification
  • File links include aria-label noting the file opens in a new tab
  • Drag-and-drop is supplementary — file selection via button is always available as a keyboard-accessible alternative
  • :state(dragging) is CSS-only and does not affect keyboard users

Contributing

Contributions are welcome. Please follow the development guidelines and ensure all tests pass before submitting a pull request.

License

Licensed under the @Papit License 1.0 — Copyright (c) 2024 Henry Pap (@onkelhoy)

  • ✅ Free to use in commercial projects
  • ✅ Free to modify and distribute
  • ✅ Attribution required
  • ❌ Cannot resell the component itself as a standalone product

See the LICENSE file for full details.

Related

Support

For issues, questions, or contributions visit the GitHub repository.