defmodule Corex.FileUploadLive do @moduledoc ~S''' LiveView uploads wrapper that shares [`Corex.FileUpload`](Corex.FileUpload.html) layout tokens (`data-scope` / `data-part`) and styling. Use after [`allow_upload/3`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#allow_upload/3). Pass `upload={@uploads.name}` and `field` matching the atom given to `allow_upload`. Renders [`live_file_input`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#live_file_input/1) and `phx-drop-target`; **no** Zag `FileUpload` hook. Do not combine this component with `<.file_upload>` on the same file control. Forms must bind [`phx-change`](https://hexdocs.pm/phoenix_live_view/uploads.html) (and typically `phx-submit`) as in the [uploads guide](https://hexdocs.pm/phoenix_live_view/uploads.html). For shared form patterns, see the [Forms](forms.html) guide. ## Anatomy ### Minimal ```heex
``` ### With label ```heex ``` ### Custom slots ```heex ``` ### Form with submit ```heex ``` ```elixir def mount(_params, _session, socket) do {:ok, allow_upload(socket, :attachment, accept: ~W(.jpg .jpeg .png .pdf .txt), max_entries: 3, max_file_size: 8_000_000 )} end def handle_event("validate", _params, socket), do: {:noreply, socket} def handle_event("save", _params, socket) do _results = consume_uploaded_entries(socket, :attachment, fn %{path: path}, entry -> File.rm!(path) {:ok, entry.client_name} end) {:noreply, socket} end def handle_event("file_upload_live_cancel", params, socket) do %{"ref" => ref, "upload_field" => field} = params {:noreply, cancel_upload(socket, String.to_existing_atom(field), ref)} end ``` ### LiveView setup ```elixir def mount(_params, _session, socket) do {:ok, allow_upload(socket, :document, accept: ~w(.jpg .jpeg .png .pdf), max_entries: 3, max_file_size: 8_000_000 )} end def handle_event("file_upload_live_cancel", %{"ref" => ref, "upload_field" => field}, socket) do {:noreply, cancel_upload(socket, String.to_existing_atom(field), ref)} end ``` The `field` atom must match the name passed to `allow_upload/3`. Implement `file_upload_live_cancel` so remove-entry works; optional `cancel_event` on the component overrides the event name. ''' @doc type: :component use Phoenix.Component alias Corex.FileUpload.Translation alias Phoenix.LiveView.UploadConfig alias Phoenix.LiveView.UploadEntry attr(:upload, UploadConfig, required: true, doc: "Upload config from `allow_upload/3` on the LiveView socket" ) attr(:field, :atom, required: true, doc: "Upload name passed to `allow_upload` (for cancel events)" ) attr(:id, :string, default: nil, doc: "Stable prefix for internal ids; defaults to a generated id" ) attr(:dir, :string, default: nil, values: [nil, "ltr", "rtl"], doc: "Text direction (ltr or rtl)" ) attr(:invalid, :boolean, default: false, doc: "Whether the file upload is invalid") attr(:disabled, :boolean, default: false, doc: "Whether the file upload is disabled") attr(:cancel_event, :string, default: "file_upload_live_cancel", doc: "LiveView `handle_event` name; receives ref and upload_field" ) attr(:translation, Corex.FileUpload.Translation, default: nil, doc: "Same translatable strings as `<.file_upload>`" ) attr(:rest, :global) slot(:label, required: false, doc: "Label above the dropzone") slot(:dropzone, required: false, doc: "Custom dropzone content; defaults to translation dropzone text" ) slot(:open, required: false, doc: "Custom open-picker trigger; defaults to translation open text" ) slot(:close, required: true, doc: "Remove control for each upload entry") slot(:error, required: false, doc: "Error message content; receives the message as slot argument" ) do attr(:class, :string, required: false) end def file_upload_live(assigns) do translation = Translation.resolve(Map.get(assigns, :translation)) assigns = assigns |> assign_new(:id, fn -> "file-upload-live-#{System.unique_integer([:positive])}" end) |> assign(:translation, translation) ~H"""