PhoenixKitWeb.Components.MediaBrowser (phoenix_kit v2.34.0)

Copy Markdown View Source

MediaBrowser LiveComponent — embeddable media management UI.

Provides a full media browser with folder navigation, file upload, search, and selection tools. Operates in two modes:

  • Uncontrolled (default): all navigation state is owned internally. No URL sync.
  • Controlled (opt-in): when on_navigate is set to a truthy value, navigation events (navigate_folder, search, clear_search, set_page) emit {PhoenixKitWeb.Components.MediaBrowser, id, {:navigate, params}} to the parent LiveView process instead of mutating local state. The parent must call push_patch and feed URL params back via send_update with a :nav_params key.

Enabling uploads

The fastest path is the Embed helper — one use line gives you uploads, the validate-channel stub, and the message delegator:

defmodule MyAppWeb.MediaPage do
  use MyAppWeb, :live_view
  use PhoenixKitWeb.Components.MediaBrowser.Embed
end

Manual setup (if you prefer explicit wiring) is three calls:

def mount(_params, _session, socket) do
  {:ok, PhoenixKitWeb.Components.MediaBrowser.setup_uploads(socket)}
end

def handle_event("validate", _params, socket), do: {:noreply, socket}

def handle_info({__MODULE__, _, _} = msg, socket) do
  PhoenixKitWeb.Components.MediaBrowser.handle_parent_info(msg, socket)
end

Usage (uncontrolled)

<.live_component
  module={PhoenixKitWeb.Components.MediaBrowser}
  id="media-browser"
  phoenix_kit_current_user={@phoenix_kit_current_user}
/>

Picking into a typed slot

only_file_type restricts the browser to one kind for its whole lifetime — the listing is filtered to it, the type-filter control is hidden, and an off-type upload is refused, so the other kinds are simply not reachable:

<.live_component
  module={PhoenixKitWeb.Components.MediaBrowser}
  id="audio-picker"
  select_mode
  only_file_type="audio"
  phoenix_kit_current_user={@phoenix_kit_current_user}
/>

Use it wherever the selection fills a typed field. Offering everything and validating afterwards works, but it lets someone choose a PNG for an audio slot and only find out on the rejection — and if a consumer forgets that re-check, the PNG lands in the field and renders as a dead <audio>.

Values: image, video, document, audio, archive, other.

Usage (controlled — URL-sync driven by parent)

<.live_component
  module={PhoenixKitWeb.Components.MediaBrowser}
  id="media-browser"
  phoenix_kit_current_user={@phoenix_kit_current_user}
  on_navigate={true}
/>

The parent LiveView must implement:

def handle_info({PhoenixKitWeb.Components.MediaBrowser, "media-browser",
                 {:navigate, params}}, socket) do
  # push_patch to update URL, handle_params will send_update back
end

params carries :file — the uuid of the file open in the modal viewer, or nil when it is closed. Put it in the URL and hand it back in :nav_params and a refresh, or the browser's Back button, reopens or closes the viewer. A host that leaves the key out of :nav_params altogether keeps the viewer local to the component; only an explicit file: nil closes it.

Required attributes

  • id — unique DOM id (required by LiveComponent)

Optional attributes

  • phoenix_kit_current_user — logged-in user struct (for upload attribution)
  • scope_folder_id — constrain the browser to a virtual root folder
  • on_navigate — when truthy, enables controlled mode (URL-sync via parent)
  • admin — clicking a file always opens the in-place modal viewer (image / video / PDF / icon + metadata + Download + prev/next navigation). When true, the viewer sidebar additionally shows an "Open details page" button linking to /admin/media/:uuid, and rotations made in the viewer persist to the file row. Bulk-select is still reachable via the toolbar's Select button — once select_mode is on, clicks toggle selection instead of opening the modal.
  • featured — nil (default, feature off) or `%{uuid: uuidnil,
    label: String.t()nil}` naming the host's own featured-image
    pointer. label is the star badge's tooltip; falls back to gettext "Featured image" when nil/absent. When set, image tiles/rows gain a "Set as featured" / "Unset featured" kebab item (grid, list and stack views), the matching tile carries a data-role="featured-badge" star overlay, and the modal viewer's sidebar (for image files) shows the same toggle. The browser never persists anything itself: choosing or clearing a featured image sends {__MODULE__, id, {:set_featured, uuid | nil}} to the host process — the {MediaBrowser, id, payload} channel {:navigate, _} already uses — and optimistically moves the badge locally. ⚠️ A host that funnels every {MediaBrowser, _, _} message into handle_parent_info/2 must match {:set_featured, _} before it: that function handles only its own two internal messages and ends in a catch-all, so the choice is swallowed silently — no crash, no log, the star flips in the UI and nothing is ever persisted. The host persists the choice and, if the write is rejected or the pointer changes elsewhere, corrects it with a later featured assign (e.g. via send_update).

Summary

Functions

Catch-all handler for parent LiveViews to delegate MediaBrowser messages.

Callback implementation for Phoenix.LiveComponent.render/1.

Handles the upload setup message from the MediaBrowser component.

Functions

handle_event(binary, params, socket)

Callback implementation for Phoenix.LiveComponent.handle_event/3.

handle_parent_info(arg1, socket)

Catch-all handler for parent LiveViews to delegate MediaBrowser messages.

render(assigns)

Callback implementation for Phoenix.LiveComponent.render/1.

setup_uploads(socket)

Handles the upload setup message from the MediaBrowser component.

Parent LiveViews embedding MediaBrowser should add this to their handle_info:

def handle_info({PhoenixKitWeb.Components.MediaBrowser, _id, :setup_uploads}, socket) do
  {:noreply, PhoenixKitWeb.Components.MediaBrowser.setup_uploads(socket)}
end

Or use the catch-all delegator:

def handle_info({PhoenixKitWeb.Components.MediaBrowser, _, _} = msg, socket) do
  PhoenixKitWeb.Components.MediaBrowser.handle_parent_info(msg, socket)
end

update(assigns, socket)

Callback implementation for Phoenix.LiveComponent.update/2.