Media File Upload

File Upload

Progressive enhancement of a native <input type="file"> via wf-file.js . The native input stays in the DOM hidden, so standard form submissions require zero changes. Three layout variants are available — inline (default), dropzone , and button — all with drag-and-drop support.

Import the module once. All [data-wf-file] wrappers are enhanced automatically on DOMContentLoaded .

Basic

Use inside a .wf-field for a labeled form field with a hint:

PDF or DOCX, max 5 MB
HTML
<div class="wf-field">
  <label>Attachment</label>
  <div class="wf-file" data-wf-file>
    <input type="file" name="attachment" accept=".pdf,.docx">
  </div>
  <span class="wf-field-hint">PDF or DOCX, max 5 MB</span>
</div>

Inline

The default layout. Renders a trigger button alongside a name box that displays the selected filename(s). Drag-and-drop highlights the name box border on hover. When multiple is set on the native input, a drop accumulates files; single-file mode is replaced on a drop.

HTML
<!-- Single file -->
<div class="wf-file" data-wf-file>
  <input type="file" name="attachment">
</div>

<!-- Multiple files -->
<div class="wf-file" data-wf-file>
  <input type="file" name="attachments" multiple>
</div>

Dropzone

A large clickable drag-and-drop zone. Image preview is automatically enabled. The zone responds visually to hover, focus, and active drag states. Add data-wf-hint to show a custom hint line inside the zone.

HTML
<div class="wf-file" data-wf-file
     data-wf-layout="dropzone"
     data-wf-hint="PNG, JPG or GIF — max 10 MB each">
  <input type="file" name="photos" multiple accept="image/*">
</div>

Button

Trigger button only. A count badge appears on the button once files are selected. Drag-and-drop highlights the button with a focus ring. Combine with data-wf-show-details="false" to suppress the file list entirely when badge feedback is enough.

HTML
<!-- Button with file list below -->
<div class="wf-file" data-wf-file data-wf-layout="button">
  <input type="file" name="docs" multiple>
</div>

<!-- Button-only (suppress file list) -->
<div class="wf-file" data-wf-file data-wf-layout="button"
     data-wf-show-details="false">
  <input type="file" name="docs" multiple>
</div>

Sizes

Set data-wf-size to sm or lg for size presets; the default is medium. Works across all three layout variants.

HTML
<div class="wf-file" data-wf-file data-wf-size="sm">
  <input type="file" name="attachment">
</div>

<div class="wf-file" data-wf-file>
  <input type="file" name="attachment">
</div>

<div class="wf-file" data-wf-file data-wf-size="lg">
  <input type="file" name="attachment">
</div>

Preview & File List

Add data-wf-preview to show image thumbnails in the file list (automatically on for the dropzone layout). Each list item includes a thumbnail or file icon, the filename, formatted size, and a remove button. Cap the visible list length with data-wf-max-list="N" ; a “+N more” line appears for overflow files.

HTML
<!-- Inline with image previews, list capped at 3 -->
<div class="wf-file" data-wf-file data-wf-preview data-wf-max-list="3">
  <input type="file" name="photos" multiple accept="image/*">
</div>

Use data-wf-hint to add helper text below the control (or inside the dropzone). On inline and button layouts it renders as a .wf-file-hint paragraph.

HTML
<div class="wf-file" data-wf-file
     data-wf-hint="PDF or DOCX, max 5 MB per file">
  <input type="file" name="docs" multiple accept=".pdf,.docx">
</div>

Auto Submit

Add data-wf-auto-submit to trigger an upload the moment files are selected or dropped — no submit button required. Two integration modes are available depending on whether the .wf-file wrapper is inside a data-wf-ajax-form form or used standalone.

Mode 1 — inside a wf-ajax-form

When .wf-file is nested inside a form[data-wf-ajax-form] , adding data-wf-auto-submit calls form.requestSubmit() as soon as files arrive. The form handles the fetch, CSRF, and toast response exactly as a normal AJAX form submission — no extra configuration needed on the file wrapper.

HTML
<!-- wf-ajax-form handles the fetch; wf-file triggers it on drop/select -->
<form data-wf-ajax-form
      data-wf-url="/api/upload"
      data-wf-csrf-header="X-CSRF-Token">
  <div class="wf-file" data-wf-file
       data-wf-layout="dropzone"
       data-wf-hint="Drop an image to upload instantly"
       data-wf-auto-submit>
    <input type="file" name="avatar" accept="image/*">
  </div>
</form>

Mode 2 — standalone

When the wrapper is not inside a data-wf-ajax-form , add data-wf-url so the component uploads directly. Extra fields can be merged via data-wf-params (JSON object), a CSRF header injected with data-wf-csrf-header (reads the <meta name="csrf-token"> value), and data-wf-clear-on-success to reset the file list after a confirmed upload. Server responses follow the standard { type, title, message } format and are shown via WojoToast.

HTML
<div class="wf-file" data-wf-file
     data-wf-layout="dropzone"
     data-wf-hint="Drop a document to upload"
     data-wf-auto-submit
     data-wf-url="/api/upload"
     data-wf-params='{"folder":"documents","overwrite":"true"}'
     data-wf-csrf-header="X-CSRF-Token"
     data-wf-clear-on-success>
  <input type="file" name="document" accept=".pdf,.docx">
</div>

Listening to upload events

In standalone mode the component dispatches wf:file-upload-success and wf:file-upload-error after the server responds, in addition to showing a toast. Use these to update surrounding UI without polling.

JS
const el = document.querySelector('[data-wf-file]')

el.addEventListener('wf:file-upload-success', e => {
  console.log('Uploaded files:', e.detail.files)
  console.log('Server data:', e.detail.data)
  // e.detail.data is the parsed JSON response from the server
})

el.addEventListener('wf:file-upload-error', e => {
  console.warn('Upload failed:', e.detail.data?.message)
})

Data Attributes

Place all attributes on the .wf-file wrapper element.

Attribute Values Description
data-wf-file Required. Marks the wrapper for auto-init on DOMContentLoaded.
data-wf-layout inline · dropzone · button Layout variant. Default: inline.
data-wf-size sm · lg Size preset. Omit for medium (default).
data-wf-preview Show image thumbnails in the file list. Automatically enabled for the dropzone layout.
data-wf-max-list integer Cap file list at N visible items; a “+N more” line appears for overflow. 0 = show all. Default: 0.
data-wf-show-details false Set to false to suppress the file list entirely. The trigger button and badge still work normally.
data-wf-hint string Hint text shown below the control (inline/button) or inside the dropzone body.
data-wf-disabled Disables the component and mirrors disabled on the native input.
data-wf-complete DOM mutation applied to data-wf-parent on success. Same strategies as wf-ajax-action: append | prepend | replace | innerHTML | insert | remove | reload | highlight. Server can also set response.complete to override.
data-wf-auto-submit Upload immediately when files are selected or dropped. Delegates to the nearest data-wf-ajax-form parent if present; otherwise uses data-wf-url for a standalone fetch.
data-wf-parent CSS selector of the element to mutate on success.
data-wf-url URL string Upload endpoint for standalone auto-submit (no data-wf-ajax-form parent). Required when using data-wf-auto-submit outside a form.
data-wf-params JSON object Extra key/value pairs merged into the upload FormData for standalone auto-submit. Example: data-wf-params='{"folder":"docs"}'.
data-wf-csrf-header string Header name for CSRF protection (e.g. X-CSRF-Token). Value is read from <meta name="csrf-token">.
data-wf-clear-on-success Clear the file list after a successful standalone upload response.
Show More

Native input attributes

These are placed on the <input type="file"> inside the wrapper.

Attribute Description
multiple Allow multi-file selection. Drop accumulates files; single-file mode is replaced on a drop.
accept Restrict the file picker and validate dropped files. Accepts MIME types (image/*), extensions (.pdf), or comma-separated combinations.
disabled Also disables the component UI.
name Used for form submission as normal.

CSS Custom Properties

Override on the .wf-file element or any ancestor.

Property Default Description
--wf-file-radius var(--wf-radius-4) Border radius is applied to the name box, dropzone, and button.
--wf-file-border var(--wf-border) Border color for the name box and dropzone outline.

Programmatic API

After initialization the instance is stored on the wrapper element as el._wfFile .

JS
const el = document.querySelector('[data-wf-file]')
const fs = el._wfFile

fs.addFiles(someFileList)   // add files (filtered by accept)
fs.removeFile(0)            // remove first file
fs.clear()                  // remove all files
fs.getFiles()               // returns File[]
fs.setDisabled(true)        // disable
fs.setShowDetails(false)    // hide file list
fs.destroy()                // remove listeners & revoke preview URLs
Method Returns Description
addFiles(fileList) Add files from a FileList or File[], filtered by accept. Accumulates in multiple modes; replaces it in a single mode.
removeFile(index) Remove the file at the given 0-based index.
clear() Remove all files and reset the UI.
getFiles() File[] Returns a shallow copy of the internal file list.
setDisabled(bool) Enable or disable the component and the underlying native input.
setShowDetails(bool) Show or hide the file list. Re-renders immediately when toggled on.
destroy() Remove all event listeners and revoke all image preview object URLs.

Events

All events are dispatched on the .wf-file wrapper element and bubble up the DOM.

JS
const el = document.querySelector('[data-wf-file]')

el.addEventListener('wf:file-change', e => {
  console.log('Current files:', e.detail.files)
})

el.addEventListener('wf:file-add', e => {
  console.log('Added:', e.detail.added)
  console.log('All files:', e.detail.files)
})

el.addEventListener('wf:file-remove', e => {
  console.log('Removed:', e.detail.removed)
  console.log('Remaining:', e.detail.files)
})
Event detail Fired when
wf:file-change { files } The file list changes for any reason. files is the current File[].
wf:file-add { files, added } Files were added. added is the array of newly-added File objects.
wf:file-remove { files, removed } Files were removed. removed is the array of removed File objects.
wf:file-upload-success { files, data } Standalone auto-submit completed successfully. data is the parsed JSON response from the server. Not fired in wf-ajax-form mode.
wf:file-upload-error { files, data } Standalone auto-submit received an error response from the server. data is the parsed JSON response. Not fired in wf-ajax-form mode.

i18n

Register a locale globally with WojoI18n before DOMContentLoaded . Any key you omit falls back to the built-in English default. Use the token {name} in removeLabel and {n} in selectedCount .

For one-off text overrides set the data-wf-i18n-* attributes directly on the .wf-file wrapper. These take the highest priority.

JS
import { WojoI18n } from './wf-base.js'

WojoI18n.setLocale('fr')
WojoI18n.register('fr', {
  fileSelect: {
    triggerLabel:         'Choisir un fichier',
    triggerLabelMultiple: 'Choisir des fichiers',
    noFileChosen:         'Aucun fichier sélectionné',
    dropzoneLabel:        'Déposez vos fichiers ici',
    dropzoneHint:         'ou cliquez pour parcourir',
    draggingLabel:        'Déposez pour envoyer',
    removeLabel:          'Supprimer {name}',
    clearLabel:           'Tout supprimer',
    selectedCount:        '{n} fichiers sélectionnés',
  }
})
HTML
<!-- Per-instance overrides (highest priority) -->
<div class="wf-file" data-wf-file
     data-wf-i18n-trigger-label="Wählen Sie eine Datei"
     data-wf-i18n-no-file-chosen="Keine Datei ausgewählt"
     data-wf-i18n-dropzone-label="Dateien hier ablegen"
     data-wf-i18n-selected-count="{n} Dateien ausgewählt">
  <input type="file" name="upload" multiple>
</div>

i18n keys

Key data-wf-i18n-* attribute Default (English)
triggerLabel data-wf-i18n-trigger-label Choose a file
triggerLabelMultiple data-wf-i18n-trigger-label-multiple Choose files
noFileChosen data-wf-i18n-no-file-chosen No file is chosen
dropzoneLabel data-wf-i18n-dropzone-label Drop files here
dropzoneHint data-wf-i18n-dropzone-hint or click to browse
draggingLabel data-wf-i18n-dragging-label Drop to upload
removeLabel data-wf-i18n-remove-label Remove {name}
clearLabel data-wf-i18n-clear-label Clear all
selectedCount data-wf-i18n-selected-count {n} files selected

Class Reference

All modifier classes available on .wf-file

Class Description
wf-file Root wrapper element. Add data-wf-file for auto-init. Receives data-wf-layout, data-wf-size, data-wf-disabled, and data-wf-dragging data attributes set by JS.
wf-file-row Flex row inside the inline layout containing the trigger label and name box. Created by JS.
wf-file-trigger The visible trigger <label> that opens the file picker. Styled as a primary button. Receives wf-btn-sm or wf-btn-lg for size variants.
wf-file-namebox Filename display area in the inline layout. Receives data-wf-has-files once files are selected (changes text color from muted to normal).
wf-file-dropzone Drag-and-drop zone element in the dropzone layout. Acts as a button role element with keyboard support.
wf-file-dropzone-icon Cloud upload icon inside the dropzone. Animates upward on hover and drag.
wf-file-dropzone-label Primary heading text inside the dropzone. Hidden during active drag.
wf-file-dropzone-hint Subtext line inside the dropzone. Hidden during active drag.
wf-file-dropzone-dragging-label Text shown only while a drag is active over the zone (“Drop to upload”).
wf-file-badge File count badge on the trigger button in the button layout. Hidden when the count is 0.
wf-file-hint Hint paragraph rendered below the control (inline/button) or inside the dropzone when data-wf-hint is set.
wf-file-list Selected-files <ul>. Hidden when the list is empty. Absent entirely when data-wf-show-details="false".
wf-file-item Individual file row <li> inside the list. Animates in addition.
wf-file-item-thumb 32×32 thumbnail area. Shows an <img> for images when preview is on, or a generic file icon otherwise.
wf-file-item-details Container for the filename and size spans.
wf-file-item-name Filename span. Truncates with ellipsis on overflow.
wf-file-item-size Formatted file size span (B / KB / MB / GB).
wf-file-item-remove Remove the button for an individual file item. Carries data-idx for the 0-based file index.
wf-file-more Overflow indicator <li> shown when the file list exceeds data-wf-max-list (“+N more”).
Show More