Web component
<p-uploader>
Upload, order and describe a set of files
- Import
@parallelogram-js/core/components/PUploader- Tag
p-uploader
Playground
Uploads, saves and deletes go to a mock server in this site.
<p-uploader
max-files="4"
accept-types="image/*"
upload-action="/api/upload"
update-action="/api/update"
delete-action="/api/delete"
sequence-action="/api/sequence"
>
<p-uploader-fields slot="field-definitions">
<p-uploader-field key="title" label="Title" required></p-uploader-field>
<p-uploader-field key="caption" label="Caption" type="textarea"></p-uploader-field>
</p-uploader-fields>
<p-uploader-file file-id="terrace" filename="terrace.jpg" preview="images/terrace-640.jpg">
<p-uploader-data key="title">The terrace</p-uploader-data>
<p-uploader-data key="caption">Seats forty under heaters</p-uploader-data>
</p-uploader-file>
<p-uploader-file file-id="kitchen" filename="kitchen.jpg" preview="images/kitchen-640.jpg">
<p-uploader-data key="title">The kitchen</p-uploader-data>
</p-uploader-file>
</p-uploader>
Use the example to see the events it sends.
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.
<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>
Use the example to see the events it sends.
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
| Name | Type | Default | Description |
|---|---|---|---|
upload-action | url | none | Receives each new file as multipart form data and must answer with JSON carrying an id; files can be added only when set |
update-action | url | none | Receives { id, field, value } as JSON when a field is saved |
delete-action | url | none | Receives { id } as JSON when a file is deleted |
sequence-action | url | none | Receives { sequence: [ids] } as JSON after files are reordered |
max-files | number | 5 | Most files allowed |
accept-types | string | none | Comma-separated MIME types, wildcards such as image/* or extensions such as .pdf |
max-file-size | number | 10485760 | Largest accepted file in bytes |
input-name | string | file | Form data name each file is sent under |
allow-edit | boolean | true | false or 0 turns off editing and deleting |
allow-sort | boolean | true | false or 0 turns off reordering |
fullset by the component | flag | none | Set while the uploader holds max-files files; the drop zone is hidden |
stacked | flag | none | Join the files into one list, rounding only its outer corners |
add-label | string | Drag/Add files | The drop zone, when more than one file may be added For every instance, PUploader.defaults.addLabel. |
add-one-label | string | Drag/Add file | The drop zone when max-files is 1 For every instance, PUploader.defaults.addOneLabel. |
no-action-message | string | Files 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-message | string | {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-message | string | {file} isn’t an accepted file type. | Shown for a file outside accept-types For every instance, PUploader.defaults.wrongTypeMessage. |
limit-message | string | You can add up to {count} files. | Shown for files beyond max-files, with {count} the limit For every instance, PUploader.defaults.limitMessage. |
limit-one-message | string | You can add up to {count} file. | The same when max-files is 1 For every instance, PUploader.defaults.limitOneMessage. |
moved-message | string | Moved {file} to position {position} of {count}. | Announced after a file is moved For every instance, PUploader.defaults.movedMessage. |
unnamed-file | string | the file | Stands for {file} in that message when the file has no name For every instance, PUploader.defaults.unnamedFile. |
order-error | string | The 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-error | string | Upload failed | Shown on a card whose upload failed with a response carrying no message For every instance, PUploader.defaults.uploadError. |
invalid-response-message | string | Invalid server response | Shown on a card whose upload answered with something that is not JSON For every instance, PUploader.defaults.invalidResponseMessage. |
server-error | string | Server error | The same for a failed save or delete For every instance, PUploader.defaults.serverError. |
delete-error | string | Delete failed: {error} | Shown on a card whose delete failed, with {error} the reason For every instance, PUploader.defaults.deleteError. |
save-error | string | Changes couldn’t be saved: {error} | Shown in the edit panel when a save fails For every instance, PUploader.defaults.saveError. |
progress-label | string | Upload progress | The accessible name of the progress bar For every instance, PUploader.defaults.progressLabel. |
delete-heading | string | Delete this file? | The delete panel’s heading For every instance, PUploader.defaults.deleteHeading. |
cancel-delete-label | string | Cancel delete | The delete panel’s cancel button For every instance, PUploader.defaults.cancelDeleteLabel. |
confirm-delete-label | string | Confirm delete | The delete panel’s confirm button For every instance, PUploader.defaults.confirmDeleteLabel. |
edit-label | string | Edit details | The edit button, and the accessible name of the edit panel For every instance, PUploader.defaults.editLabel. |
cancel-edit-label | string | Cancel editing | The edit panel’s cancel button For every instance, PUploader.defaults.cancelEditLabel. |
save-label | string | Save details | The edit panel’s save button For every instance, PUploader.defaults.saveLabel. |
remove-label | string | Remove file | The button that removes a card whose upload failed For every instance, PUploader.defaults.removeLabel. |
cancel-upload-label | string | Cancel | The button that cancels a failed delete For every instance, PUploader.defaults.cancelUploadLabel. |
move-up-label | string | Move up | The move up button For every instance, PUploader.defaults.moveUpLabel. |
move-down-label | string | Move down | The move down button For every instance, PUploader.defaults.moveDownLabel. |
replace-label | string | Replace | The replace button For every instance, PUploader.defaults.replaceLabel. |
delete-label | string | Delete file | The button that opens the delete panel For every instance, PUploader.defaults.deleteLabel. |
file-info-label | string | File information for {file} | The accessible name of the card’s info panel, with {file} its name For every instance, PUploader.defaults.fileInfoLabel. |
Properties
| Name | Type | Description |
|---|---|---|
requestHeaders | Record<string, string> | (() => Record<string, string>) | null | Headers sent with every request |
Methods
| Name | Signature | Description |
|---|---|---|
getFieldSchema | () => Array<{ key: string; label: string; type: string; required: boolean; maxlength: number | null }> | The fields declared by <p-uploader-fields> |
Events
| Name | Detail | Description |
|---|---|---|
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
| Name | Description |
|---|---|
| default | The <p-uploader-file> elements |
field-definitions | A <p-uploader-fields> element |
Shadow parts
| Name | Description |
|---|---|
files | The list of files |
selector | The drop zone |
add-button | The button that opens the file picker |
drag-chip | The chip that follows the cursor while a file is dragged |
message | Messages about refused files and failed saves |
CSS custom properties
| Name | Default | Description |
|---|---|---|
--uploader-radius | none | Corner radius |
--uploader-border-width | none | Border width |
--uploader-border-color | var(--surface-panel-border-color) | Border colour: the panel border colour |
--uploader-bg | var(--surface-panel-color-bg) | Background: the panel surface |
--uploader-color | var(--surface-panel-color-text) | Text colour: the panel text colour, which is the page's text colour |
--uploader-shadow | var(--surface-panel-shadow) | Shadow: the panel shadow |
--uploader-padding | none | Padding |
--uploader-files-gap | none | Space between files |
--uploader-file-radius-start | none | Top corners of a file card |
--uploader-file-radius-end | none | Bottom corners of a file card |
<p-uploader-file>
A file, uploaded or existing, with its preview, fields and actions
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
file-id | string | none | The file's id on the server |
filename | string | none | The file name shown on the card |
preview | url | none | A preview image |
state | uploading | uploaded | error | uploaded | Upload progress |
progress | number | none | Upload progress from 0 to 100 |
error | string | none | Why the upload failed |
allow-edit | boolean | true | false or 0 turns off editing and deleting for this file |
data-current-panelset by the component | info | error | edit | delete | info | The panel the card shows |
Events
| Name | Detail | Description |
|---|---|---|
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
| Name | Description |
|---|---|
| default | The <p-uploader-data> elements holding the file's values |
Shadow parts
| Name | Description |
|---|---|
preview | The preview image |
progress | The upload progress bar |
panel | Each panel: details, error, edit and delete confirmation |
fields | The field list |
field | Each field |
filename | The file name |
toolbar | The Move up and Move down buttons; absent when neither is allowed |
pills | The Edit, Replace and Delete buttons, as one segmented pill |
actions | Button rows in panels |
edit-button | The Edit button |
replace-button | The Replace button |
delete-button | The Delete button that asks for confirmation |
move-up-button | The Move up button |
move-down-button | The Move down button |
cancel-button | The button that backs out of a panel |
confirm-button | The button that confirms a delete |
save-button | The button that saves the edited fields |
edit-panel | The 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
| Name | Type | Default | Description |
|---|---|---|---|
keyrequired | string | none | The field's key |
label | string | none | The field's label |
type | text | textarea | text | The input used to edit it |
required | flag | none | The field must have a value |
maxlength | number | none | Longest value allowed |
<p-uploader-data>
A file's value for one field, as text content
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
keyrequired | string | none | The field's key |