PhoenixImageTools.Components (phoenix_image_tools v0.3.0)

View Source

Provides Phoenix LiveView components for displaying responsive images.

Usage

<.picture
  versions={[:xs, :sm, :md, :lg, :xl]}
  base={:original}
  url_fun={&MyApp.Uploaders.ProfileImageUploader.url({@user.profile_image, @user}, &1)}
  alt="User profile image"
  lazy_loading={true}
  rounded={true}
  class="object-cover"
/>

Summary

Functions

Processes a single uploaded file and prepares it for insertion into database records.

Processes uploaded files and prepares them for insertion into database records.

Renders a file upload field component for handling image uploads with preview.

Determines if a file is an image based on its extension.

Attributes

  • title (:string) - Defaults to nil.

Slots

  • inner_block (required)

Renders a responsive picture element with proper srcset attributes.

Functions

consume_upload(socket, params, field_key, options \\ [])

@spec consume_upload(Phoenix.LiveView.Socket.t(), map(), atom(), keyword()) :: map()

Processes a single uploaded file and prepares it for insertion into database records.

Similar to consume_uploads/4 but designed for single file uploads. It expects only one file to be uploaded at a time.

Parameters

  • socket - The LiveView socket with uploads
  • params - The params map (usually from the form submission)
  • field_key - The atom key of the upload field
  • options - A keyword list of options
    • :generate_name - When true, generates a unique UUID for the filename (default: false)
    • :merge_fun - A function that merges the uploaded file path into params (default: simple Map.put)

Returns

The updated params map with the uploaded file merged in.

Examples

# In a LiveView handle_event function
def handle_event("save", %{"user" => user_params}, socket) do
  user_params =
    PhoenixImageTools.Components.consume_upload(
      socket, 
      user_params, 
      :avatar, 
      generate_name: true
    )
  
  case Accounts.update_user(socket.assigns.user, user_params) do
    {:ok, user} ->
      {:noreply, socket |> put_flash(:info, "Profile updated") |> assign(:user, user)}
    
    {:error, changeset} ->
      {:noreply, assign_form(socket, changeset)}
  end
end

With custom merge function:

# Custom merge that handles nested params
merge_fun = fn params, key, uploaded_file ->
  put_in(params, ["profile", "{key}"], uploaded_file)
end

PhoenixImageTools.Components.consume_upload(
  socket, 
  user_params, 
  :avatar, 
  generate_name: true,
  merge_fun: merge_fun
)

consume_uploads(socket, params, field_key, options \\ [])

@spec consume_uploads(Phoenix.LiveView.Socket.t(), map(), atom(), keyword()) :: map()

Processes uploaded files and prepares them for insertion into database records.

This function helps consume LiveView uploads and merge the results into the params map, making it ready for changesets. It's designed to work with the form_upload_field component and handles both single and multiple file uploads.

Parameters

  • socket - The LiveView socket with uploads
  • params - The params map (usually from the form submission)
  • field_key - The atom key of the upload field
  • options - A keyword list of options
    • :generate_names - When true, generates unique UUIDs for filenames (default: false)

Returns

The updated params map with the uploaded files merged in.

Examples

# In a LiveView handle_event function
def handle_event("save", %{"product" => product_params}, socket) do
  product_params =
    PhoenixImageTools.Components.consume_uploads(
      socket, 
      product_params, 
      :images, 
      generate_names: true
    )
  
  case Products.create_product(product_params) do
    {:ok, product} ->
      {:noreply, socket |> put_flash(:info, "Product created") |> push_navigate(to: ~p"/products")}
    
    {:error, changeset} ->
      {:noreply, assign_form(socket, changeset)}
  end
end

The files will be available in your changeset as a list of maps in the format:

%{
  "0" => %{"delete" => "false", "file" => "/path/to/file.jpg"},
  "1" => %{"delete" => "false", "file" => "/path/to/another_file.png"}
}

This format is compatible with cast_attachments/4 from PhoenixImageTools.Schema.

form_upload_field(assigns)

Renders a file upload field component for handling image uploads with preview.

This component simplifies the creation of LiveView file upload experiences, supporting both new uploads and displaying previously uploaded images.

Examples

<.form_upload_field
  upload={@uploads}
  name={:images}
  label="Product Images"
/>

With previous uploads and custom cancel action:

<.form_upload_field
  upload={@uploads}
  name={:profile_image}
  label="Profile Picture"
  target={@myself}
  accept={~w(.jpg .jpeg .png)}
  max_entries={1}
>
  <:previous_uploads>
    <.inputs_for :let={image_form} field={@form[:images]}>
      <.hidden_input form={image_form} field={:delete} />
      <.hidden_input form={image_form} field={:id} />
      
      <div id={"image-{image_form.index}"} class={image_form[:delete].value == "true" && "hidden"}>
        <figure class="mb-2">
          <img
            src={MyApp.MediaUploader.url(Ecto.Changeset.get_field(image_form.source, :file), :thumbnail)}
            class="rounded-lg max-w-[200px]"
            alt="Uploaded image"
          />
        </figure>
        <div>
          <button
            type="button"
            phx-click="mark-image-for-deletion"
            phx-value-index={image_form.index}
            class="text-red-600 text-sm"
          >
            Remove
          </button>
        </div>
      </div>
    </.inputs_for>
  </:previous_uploads>
</.form_upload_field>

In the LiveView module, you'll need to set up uploads:

def mount(_params, _session, socket) do
  socket =
    socket
    |> assign_form(...)
    |> allow_upload(:images, 
        accept: ~w(.jpg .jpeg .png .webp), 
        max_entries: 5, 
        max_file_size: 10_000_000
      )
    
  {:ok, socket}
end

def handle_event("save", %{"entity" => entity_params}, socket) do
  entity_params =
    PhoenixImageTools.Components.consume_uploads(
      socket, 
      entity_params, 
      :images, 
      generate_names: true
    )
    
  # Continue with saving logic
end

def handle_event("cancel-upload", %{"ref" => ref}, socket) do
  {:noreply, cancel_upload(socket, :images, ref)}
end

Attributes

  • upload - The LiveView uploads map.
  • name - The upload field name (must match the field name in allow_upload).
  • label - Optional label text for the upload field (defaults to capitalized name).
  • target - Optional target for phx events (useful in LiveComponents).
  • class - Optional CSS classes to apply to the container.
  • drop_prompt - Optional custom prompt for drag and drop area.
  • accept - Optional list of allowed file extensions (for information only).
  • max_entries - Optional maximum number of files (for information only).
  • max_file_size_mb - Optional max file size in MB (for information only).

Slots

  • previous_uploads - Slot for rendering previously uploaded files.
  • upload_entry - Slot for customizing the display of upload entries.

Usage with consume_uploads

The companion function consume_uploads/4 helps process uploaded files and prepare them for insertion into database records. See its documentation for details.

Attributes

  • upload (:map) (required) - The LiveView uploads map.
  • name (:atom) (required) - The upload field name (must match the field name in allow_upload).
  • label (:string) - Optional label text for the upload field. Defaults to nil.
  • target (:any) - Optional target for phx events (useful in LiveComponents). Defaults to nil.
  • class (:string) - Optional CSS classes to apply to the container. Defaults to "".
  • drop_prompt (:string) - Custom prompt for drag and drop area. Defaults to "Drag and drop files or click to browse".
  • accept (:list) - Optional list of allowed file extensions (for information only). Defaults to nil.
  • max_entries (:integer) - Optional maximum number of files (for information only). Defaults to nil.
  • max_file_size_mb (:integer) - Optional max file size in MB (for information only). Defaults to nil.

Slots

  • previous_uploads - Slot for rendering previously uploaded files.
  • upload_entry - Slot for customizing the display of upload entries. Accepts attributes:
    • entry (:map)

is_image?(file_name)

@spec is_image?(String.t()) :: boolean()

Determines if a file is an image based on its extension.

Checks if a given filename has a supported image extension.

Parameters

  • file_name - The filename or path to check

Returns

Boolean indicating if the file has a supported image extension

Examples

iex> PhoenixImageTools.Components.is_image?("photo.jpg")
true

iex> PhoenixImageTools.Components.is_image?("document.pdf")
false

nested_form_box(assigns)

Attributes

  • title (:string) - Defaults to nil.

Slots

  • inner_block (required)

picture(assigns)

Renders a responsive picture element with proper srcset attributes.

Attributes

  • class - CSS classes to apply to the picture element.
  • url_fun - Function to generate URLs for different image versions.
  • versions - List of image versions to include in the srcset.
  • base - Base version to use as the default image src.
  • zoom_version - Version to use for the zoomable view (defaults to base if not provided).
  • lazy_loading - Whether to enable lazy loading for the image.
  • rounded - Whether to apply rounded corners to the image.
  • zoomable - Whether the image should be zoomable.
  • alt - Alternative text for the image.
  • height - Height attribute for the image (defaults to 500).
  • width - Width attribute for the image (defaults to 500).

Attributes

  • class (:any) - CSS classes to apply to the picture element. Defaults to nil.
  • url_fun (:any) - Function to generate URLs for different image versions.
  • versions (:list) - List of image versions to include in the srcset.
  • base (:atom) - Base version to use as the default image src.
  • zoom_version (:atom) - Version to use for the zoomable view (defaults to base if not provided). Defaults to nil.
  • lazy_loading (:boolean) - Whether to enable lazy loading for the image. Defaults to false.
  • rounded (:boolean) - Whether to apply rounded corners to the image. Defaults to false.
  • zoomable (:boolean) - Whether the image should be zoomable. Defaults to false.
  • alt (:string) - Alternative text for the image. Defaults to "".
  • height (:integer) - Height attribute for the image. Defaults to 500.
  • width (:integer) - Width attribute for the image. Defaults to 500.
  • Global attributes are accepted.