Corex.FileUpload (Corex v0.1.2)

View Source

Phoenix implementation of Zag.js File Upload.

Use multipart on the parent form and read %Plug.Upload{} from params in a controller action, as in Phoenix file uploads.

LiveView phx-submit cannot transport raw multipart file bytes over the WebSocket; use a controller route for classic Plug.Upload, or allow_upload/3 for LiveView-native uploads with Corex.FileUploadLive (<.file_upload_live>). Do not combine this Zag component with live_file_input on the same file control.

Anatomy

Minimal

<.file_upload name="document" class="file-upload">
  <:close>
    <.heroicon name="hero-x-mark" />
  </:close>
</.file_upload>

With label

<.file_upload name="document" class="file-upload">
  <:label>Files</:label>
  <:close>
    <.heroicon name="hero-x-mark" />
  </:close>
</.file_upload>

Custom slots

<.file_upload name="document" class="file-upload">
  <:dropzone>
    <span>Custom dropzone</span>
  </:dropzone>
  <:open>
    <span>Custom trigger</span>
  </:open>
  <:close>
    <.heroicon name="hero-x-mark" />
  </:close>
</.file_upload>

Multipart form (controller)

<.form for={@form} action={~p"/file-upload/form"} method="post" multipart>
  <input type="hidden" name="file_upload_changeset[_sent]" value="1" />
  <.file_upload field={@form[:attachment]} class="file-upload">
    <:label>Attachment</:label>
    <:close>
      <.heroicon name="hero-x-mark" />
    </:close>
    <:error :let={msg}>
      <.heroicon name="hero-exclamation-circle" class="icon" />
      {msg}
    </:error>
  </.file_upload>
  <.action type="submit" class="button button--accent w-full">Submit</.action>
</.form>

Use multipart on the parent form so %Plug.Upload{} is available on the server for classic uploads. Optional hidden _sent supports used_input? when validating empty submits.

API

Requires a stable id on <.file_upload>.

FunctionActionReturns
clear_files/1Clear accepted files (client)%Phoenix.LiveView.JS{}
clear_files/2Clear accepted files (server)socket
clear_rejected_files/1Clear rejected list (client)%Phoenix.LiveView.JS{}
clear_rejected_files/2Clear rejected list (server)socket
open_file_picker/1Open native picker (client)%Phoenix.LiveView.JS{}
open_file_picker/2Open native picker (server)socket

Events

Pick an event name and pass it to on_* on <.file_upload>. Rejected files are not listed in the DOM; use on_file_reject to react to validation failures.

Server events

EventWhenPayload
on_file_change="files_changed"Accepted file list changes%{"id" => id, ...}
on_file_accept="file_accepted"File passes validation%{"id" => id, ...}
on_file_reject="file_rejected"File fails validation%{"id" => id, ...}

Client events

EventWhenevent.detail
on_file_change_client="files-changed"Accepted list changesid, file metadata
on_file_accept_client="file-accepted"File acceptedid, file metadata
on_file_reject_client="file-rejected"File rejectedid, reason

Form

See Multipart form under Anatomy. Use field={f[:attachment]} inside <.form multipart>.

For cross-cutting invalid styling and error presentation, see the Forms guide.

Summary

API

Drop every accepted file entry from phx-click. Dispatches corex:file-upload:clear-files.

Clear accepted files from handle_event (file_upload_clear_files).

Remove rejected entries from phx-click. Dispatches corex:file-upload:clear-rejected.

Clear rejected files from handle_event (file_upload_clear_rejected).

Open the OS file picker from phx-click. Dispatches corex:file-upload:open.

Open the OS file picker from handle_event (file_upload_open).

Components

file_upload(assigns)

Attributes

  • id (:string) - Stable id for the file upload root; set automatically when using field.
  • disabled (:boolean) - Whether the file upload is disabled. Defaults to false.
  • invalid (:boolean) - Whether the file upload is invalid. Defaults to false.
  • read_only (:boolean) - Whether the file upload is read-only. Defaults to false.
  • required (:boolean) - Whether at least one file is required. Defaults to false.
  • name (:string) - The name attribute of the hidden file input.
  • form (:string) - The id of the form this control belongs to.
  • dir (:string) - Text direction (ltr or rtl). Defaults to nil. Must be one of nil, "ltr", or "rtl".
  • max_files (:integer) - Maximum number of files the user may select. Defaults to 1.
  • max_file_size (:integer) - Maximum file size in bytes; omit for no limit. Defaults to nil.
  • min_file_size (:integer) - Minimum file size in bytes; omit for no limit. Defaults to nil.
  • allow_drop (:boolean) - Whether drag-and-drop onto the dropzone is enabled. Defaults to true.
  • prevent_document_drop (:boolean) - When true, prevents dropping files on the document outside the dropzone. Defaults to true.
  • accept (:string) - Comma-separated MIME types or extensions (e.g. image/*,.pdf). Defaults to nil.
  • directory (:boolean) - When true, allow selecting a directory instead of individual files. Defaults to false.
  • on_file_change (:string) - Server event when the accepted file list changes. Defaults to nil.
  • on_file_change_client (:string) - Client event name when the accepted file list changes. Defaults to nil.
  • on_file_accept (:string) - Server event when a file passes validation. Defaults to nil.
  • on_file_accept_client (:string) - Client event name when a file passes validation. Defaults to nil.
  • on_file_reject (:string) - Server event when a file fails validation. Defaults to nil.
  • on_file_reject_client (:string) - Client event name when a file fails validation. Defaults to nil.
  • translation (Corex.FileUpload.Translation) - Override translatable strings. Defaults to nil.
  • errors (:list) - List of error messages when not using field=. Defaults to [].
  • field (Phoenix.HTML.FormField) - Form field for id, name, form, invalid, and required wiring.
  • Global attributes are accepted.

Slots

  • label - Label above the dropzone. Accepts attributes:
    • class (:string)
  • dropzone - Custom dropzone content; defaults to translation dropzone text.
  • open - Custom open-picker trigger; defaults to translation open text.
  • close (required) - Remove control for each accepted file entry. Accepts attributes:
    • class (:string)
  • error - Error message content; receives the message as slot argument. Accepts attributes:
    • class (:string)

API

clear_files(file_upload_id)

Drop every accepted file entry from phx-click. Dispatches corex:file-upload:clear-files.

<.action phx-click={Corex.FileUpload.clear_files("my-fu")}>Clear accepted</.action>
<.file_upload id="my-fu" class="file-upload" accept="image/*">
  <:close><.heroicon name="hero-x-mark" /></:close>
</.file_upload>
document.getElementById("my-fu")?.dispatchEvent(new CustomEvent("corex:file-upload:clear-files", { bubbles: false }));

clear_files(socket, file_upload_id)

Clear accepted files from handle_event (file_upload_clear_files).

def handle_event("clear_files", _, socket) do
  {:noreply, Corex.FileUpload.clear_files(socket, "my-fu")}
end

clear_rejected_files(file_upload_id)

Remove rejected entries from phx-click. Dispatches corex:file-upload:clear-rejected.

<.action phx-click={Corex.FileUpload.clear_rejected_files("my-fu")}>Clear rejected</.action>
<.file_upload id="my-fu" class="file-upload">
  <:close><.heroicon name="hero-x-mark" /></:close>
</.file_upload>

clear_rejected_files(socket, file_upload_id)

Clear rejected files from handle_event (file_upload_clear_rejected).

def handle_event("clear_rejected", _, socket) do
  {:noreply, Corex.FileUpload.clear_rejected_files(socket, "my-fu")}
end

open_file_picker(file_upload_id)

Open the OS file picker from phx-click. Dispatches corex:file-upload:open.

<.action phx-click={Corex.FileUpload.open_file_picker("my-fu")}>Browse</.action>
<.file_upload id="my-fu" class="file-upload">
  <:close><.heroicon name="hero-x-mark" /></:close>
</.file_upload>

open_file_picker(socket, file_upload_id)

Open the OS file picker from handle_event (file_upload_open).

def handle_event("browse", _, socket) do
  {:noreply, Corex.FileUpload.open_file_picker(socket, "my-fu")}
end