Web component

<p-uploader>

Upload, order and describe a set of files

Import
@parallelogram-js/core/components/PUploader
Tag
p-uploader

Playground

With max-files="1" the card offers Replace, between Edit and Delete in the same pill. Choosing a file uploads it in the card’s own place and deletes the old one once it has arrived, so the list never grows; if the upload fails the original comes back.

The terrace
<p-uploader
  max-files="1"
  accept-types="image/*"
  upload-action="/api/upload"
  update-action="/api/update"
  delete-action="/api/delete"
>
  <p-uploader-fields slot="field-definitions">
    <p-uploader-field key="title" label="Title"></p-uploader-field>
  </p-uploader-fields>

  <p-uploader-file file-id="hero" filename="terrace.jpg" preview="images/terrace-640.jpg">
    <p-uploader-data key="title">The terrace</p-uploader-data>
  </p-uploader-file>
</p-uploader>

About

Files are added by dropping them or through a button that opens the file picker, and each one uploads as multipart form data with its progress shown. Existing files are <p-uploader-file> children. Each feature appears only when its server address is set: uploading, editing fields, deleting and reordering.

With a sequence-action, each file has Move up and Move down buttons as a keyboard alternative to dragging. With max-files="1" and an upload-action, each file has a Replace button. Fields are shown only when <p-uploader-fields> declares them, and every field is edited in a panel that slides over the card's details. Requests send an X-CSRF-Token header from <meta name="csrf-token"> unless requestHeaders is set.

The upload endpoint must answer with JSON carrying an id, which is written to the file's file-id and is the id every later update, delete and sequence request sends. Without it an upload looks like it worked and nothing that follows can name the file. A preview in the same response becomes the card's thumbnail, which is worth returning for a file the browser cannot draw itself. Everything else in the response is kept and passed to p-uploader:upload-success as response.

A failed request is read for something worth showing: JSON contributes its message or error, plain text is used as it is when it is under 200 characters and contains no <, and anything else -- an HTML error page, or a 2xx whose body will not parse -- falls back to the generic message, so a stack trace never reaches the page.

Without JavaScript

The file input lives in this element's shadow root, so before the module loads there is no way to choose a file, and <p-uploader-file> children show nothing. The package stylesheet hides the element while scripts are running. An upload posts to its own endpoint rather than with the surrounding form, so there is nothing here to fall back to: where uploading has to work without scripts, render an ordinary <input type="file"> form and use this element in place of it once the module is in.

Usage

Register the component and the framework loads it the first time a page contains p-uploader.

import { Parallelogram } from '@parallelogram-js/core';

const app = Parallelogram.create();
app.components.add('p-uploader', () => import('@parallelogram-js/core/components/PUploader'));
app.run();

Attributes

NameTypeDefaultDescription
upload-actionurlnoneReceives each new file as multipart form data and must answer with JSON carrying an id; files can be added only when set
update-actionurlnoneReceives { id, field, value } as JSON when a field is saved
delete-actionurlnoneReceives { id } as JSON when a file is deleted
sequence-actionurlnoneReceives { sequence: [ids] } as JSON after files are reordered
max-filesnumber5Most files allowed
accept-typesstringnoneComma-separated MIME types, wildcards such as image/* or extensions such as .pdf
max-file-sizenumber10485760Largest accepted file in bytes
input-namestringfileForm data name each file is sent under
allow-editbooleantruefalse or 0 turns off editing and deleting
allow-sortbooleantruefalse or 0 turns off reordering
fullset by the componentflagnoneSet while the uploader holds max-files files; the drop zone is hidden
stackedflagnoneJoin the files into one list, rounding only its outer corners
add-labelstringDrag/Add filesThe drop zone, when more than one file may be added For every instance, PUploader.defaults.addLabel.
add-one-labelstringDrag/Add fileThe drop zone when max-files is 1 For every instance, PUploader.defaults.addOneLabel.
no-action-messagestringFiles can’t be added because no upload-action is set.Shown when a file is added and there is no upload-action For every instance, PUploader.defaults.noActionMessage.
too-large-messagestring{file} is larger than {size}.Shown for a file over max-file-size, with {file} its name and {size} the limit For every instance, PUploader.defaults.tooLargeMessage.
wrong-type-messagestring{file} isn’t an accepted file type.Shown for a file outside accept-types For every instance, PUploader.defaults.wrongTypeMessage.
limit-messagestringYou can add up to {count} files.Shown for files beyond max-files, with {count} the limit For every instance, PUploader.defaults.limitMessage.
limit-one-messagestringYou can add up to {count} file.The same when max-files is 1 For every instance, PUploader.defaults.limitOneMessage.
moved-messagestringMoved {file} to position {position} of {count}.Announced after a file is moved For every instance, PUploader.defaults.movedMessage.
unnamed-filestringthe fileStands for {file} in that message when the file has no name For every instance, PUploader.defaults.unnamedFile.
order-errorstringThe new order couldn’t be saved, so the files are back in their previous order.Shown when sequence-action fails For every instance, PUploader.defaults.orderError.
upload-errorstringUpload failedShown on a card whose upload failed with a response carrying no message For every instance, PUploader.defaults.uploadError.
invalid-response-messagestringInvalid server responseShown on a card whose upload answered with something that is not JSON For every instance, PUploader.defaults.invalidResponseMessage.
server-errorstringServer errorThe same for a failed save or delete For every instance, PUploader.defaults.serverError.
delete-errorstringDelete failed: {error}Shown on a card whose delete failed, with {error} the reason For every instance, PUploader.defaults.deleteError.
save-errorstringChanges couldn’t be saved: {error}Shown in the edit panel when a save fails For every instance, PUploader.defaults.saveError.
progress-labelstringUpload progressThe accessible name of the progress bar For every instance, PUploader.defaults.progressLabel.
delete-headingstringDelete this file?The delete panel’s heading For every instance, PUploader.defaults.deleteHeading.
cancel-delete-labelstringCancel deleteThe delete panel’s cancel button For every instance, PUploader.defaults.cancelDeleteLabel.
confirm-delete-labelstringConfirm deleteThe delete panel’s confirm button For every instance, PUploader.defaults.confirmDeleteLabel.
edit-labelstringEdit detailsThe edit button, and the accessible name of the edit panel For every instance, PUploader.defaults.editLabel.
cancel-edit-labelstringCancel editingThe edit panel’s cancel button For every instance, PUploader.defaults.cancelEditLabel.
save-labelstringSave detailsThe edit panel’s save button For every instance, PUploader.defaults.saveLabel.
remove-labelstringRemove fileThe button that removes a card whose upload failed For every instance, PUploader.defaults.removeLabel.
cancel-upload-labelstringCancelThe button that cancels a failed delete For every instance, PUploader.defaults.cancelUploadLabel.
move-up-labelstringMove upThe move up button For every instance, PUploader.defaults.moveUpLabel.
move-down-labelstringMove downThe move down button For every instance, PUploader.defaults.moveDownLabel.
replace-labelstringReplaceThe replace button For every instance, PUploader.defaults.replaceLabel.
delete-labelstringDelete fileThe button that opens the delete panel For every instance, PUploader.defaults.deleteLabel.
file-info-labelstringFile information for {file}The accessible name of the card’s info panel, with {file} its name For every instance, PUploader.defaults.fileInfoLabel.

Properties

NameTypeDescription
requestHeadersRecord<string, string> | (() => Record<string, string>) | nullHeaders sent with every request

Methods

NameSignatureDescription
getFieldSchema() => Array<{ key: string; label: string; type: string; required: boolean; maxlength: number | null }>The fields declared by <p-uploader-fields>

Events

NameDetailDescription
p-uploader:upload-success{ fileId: string; response: unknown }A file finished uploading DOM event
p-uploader:upload-error{ fileId: string; error: string }A file failed to upload DOM event
p-uploader:sequence-update{ sequence: string[] }A new order was saved DOM event
p-uploader:limit{ maxFiles: number; accepted: File[]; rejected: File[] }Files went beyond max-files; cancel it to show your own message DOM event Cancelable.
p-uploader:reject{ file: File; reason: 'type' | 'size' }A file was refused; cancel it to show your own message DOM event Cancelable.

Slots

NameDescription
defaultThe <p-uploader-file> elements
field-definitionsA <p-uploader-fields> element

Shadow parts

NameDescription
filesThe list of files
selectorThe drop zone
add-buttonThe button that opens the file picker
drag-chipThe chip that follows the cursor while a file is dragged
messageMessages about refused files and failed saves

CSS custom properties

NameDefaultDescription
--uploader-radiusnoneCorner radius
--uploader-border-widthnoneBorder width
--uploader-border-colorvar(--surface-panel-border-color)Border colour: the panel border colour
--uploader-bgvar(--surface-panel-color-bg)Background: the panel surface
--uploader-colorvar(--surface-panel-color-text)Text colour: the panel text colour, which is the page's text colour
--uploader-shadowvar(--surface-panel-shadow)Shadow: the panel shadow
--uploader-paddingnonePadding
--uploader-files-gapnoneSpace between files
--uploader-file-radius-startnoneTop corners of a file card
--uploader-file-radius-endnoneBottom corners of a file card

<p-uploader-file>

A file, uploaded or existing, with its preview, fields and actions

Attributes

NameTypeDefaultDescription
file-idstringnoneThe file's id on the server
filenamestringnoneThe file name shown on the card
previewurlnoneA preview image
stateuploading | uploaded | erroruploadedUpload progress
progressnumbernoneUpload progress from 0 to 100
errorstringnoneWhy the upload failed
allow-editbooleantruefalse or 0 turns off editing and deleting for this file
data-current-panelset by the componentinfo | error | edit | deleteinfoThe panel the card shows

Events

NameDetailDescription
p-uploader-file:update{ fileId: string; field: string; value: string }A field was saved DOM event
p-uploader-file:delete{ fileId: string }The file was deleted DOM event

Slots

NameDescription
defaultThe <p-uploader-data> elements holding the file's values

Shadow parts

NameDescription
previewThe preview image
progressThe upload progress bar
panelEach panel: details, error, edit and delete confirmation
fieldsThe field list
fieldEach field
filenameThe file name
toolbarThe Move up and Move down buttons; absent when neither is allowed
pillsThe Edit, Replace and Delete buttons, as one segmented pill
actionsButton rows in panels
edit-buttonThe Edit button
replace-buttonThe Replace button
delete-buttonThe Delete button that asks for confirmation
move-up-buttonThe Move up button
move-down-buttonThe Move down button
cancel-buttonThe button that backs out of a panel
confirm-buttonThe button that confirms a delete
save-buttonThe button that saves the edited fields
edit-panelThe panel holding the edit form

<p-uploader-fields>

Holds the field definitions; put it in the field-definitions slot

<p-uploader-field>

Declares a field every file has

Attributes

NameTypeDefaultDescription
keyrequiredstringnoneThe field's key
labelstringnoneThe field's label
typetext | textareatextThe input used to edit it
requiredflagnoneThe field must have a value
maxlengthnumbernoneLongest value allowed

<p-uploader-data>

A file's value for one field, as text content

Attributes

NameTypeDefaultDescription
keyrequiredstringnoneThe field's key