# `PhoenixKitWeb.Components.MediaBrowser.Embed`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.164/lib/phoenix_kit_web/components/media_browser/embed.ex#L1)

One-line embedder for `PhoenixKitWeb.Components.MediaBrowser`.

LiveView uploads must live on the parent socket (not the LiveComponent),
so embedding the MediaBrowser requires three small pieces of plumbing on
the parent: an `allow_upload`, a `"validate"` event stub for the upload
channel, and a `handle_info` delegator for component→parent messages.

This module bundles all three into a single `use` call.

## Usage

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

      def mount(_params, _session, socket) do
        {:ok, socket}
      end
    end

Then in the template:

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

## URL sync (shareable folder links)

Pass `url_sync: true` (or `url_sync: [id: "your-component-id"]`) to make
the embedded browser reflect the current folder / search / page / view
in the page URL — so a deep link like `…?folder=<uuid>` reopens that
folder on reload and can be shared with someone else.

    use PhoenixKitWeb.Components.MediaBrowser.Embed, url_sync: true

    # multiple browsers, or a non-default component id:
    use PhoenixKitWeb.Components.MediaBrowser.Embed,
      url_sync: [id: "my-media-browser"]

With `url_sync` on, the host template must pass the controlled-mode
attrs through to the component:

    <.live_component
      module={PhoenixKitWeb.Components.MediaBrowser}
      id="media-browser"
      on_navigate={:navigate}
      initial_params={@initial_params}
      parent_uploads={@uploads}
    />

Everything else is automatic: `initial_params` is parsed from the URL
in `on_mount`, folder navigation `push_patch`es the new params into the
address bar, and a reload feeds them back to the component. The folder
is tracked by **uuid** (stable across renames); an unknown / out-of-scope
uuid falls back to root. The base path is taken from the live URL, so
any router prefix is respected.

URL sync is implemented with LiveView lifecycle hooks
(`attach_hook(:handle_params)` + `attach_hook(:handle_info)` in
`on_mount`), **not** injected `handle_params/3` clauses — so it composes
cleanly with a host LiveView that already defines its own
`handle_params` / `handle_info` (e.g. an `…/orders/:id/edit/files`
page that loads the order in its own `handle_params`). Nothing to
reconcile; both run. The `push_patch` only appends the query string to
the *current* path, so every existing segment (locale, parent resource
ids, sub-tab) is preserved.

Single-browser-per-page is assumed: the query keys (`folder`, `q`,
`page`, `orphaned`, `view`) are not namespaced per component, so two
url-synced browsers on one page would fight over them. Give only one
the `url_sync` option in that case.

## What gets injected

* `on_mount` — calls `MediaBrowser.setup_uploads/1` so `@uploads.media_files`
  is available on every mount of this LiveView. With `url_sync`, also
  assigns `:initial_params` parsed from the mount params.
* Fallback `handle_event("validate", _, socket)` — absorbs the upload
  channel's `phx-change` events. User-defined clauses with other event
  names still win because they are defined first.
* With `url_sync`: a `:handle_params` hook (feeds URL params to the
  component + captures the live path) and a `:handle_info` hook that
  intercepts `{MediaBrowser, id, {:navigate, _}}` and `push_patch`es,
  both attached in `on_mount` so they compose with the host's own
  handlers.
* Fallback `handle_info({MediaBrowser, _, _}, socket)` — forwards to
  `MediaBrowser.handle_parent_info/2` for component registration and
  upload piping.
* Fallback `handle_info({:leaf_changed, _}, socket)` — routes
  Leaf editor content updates from the sidebar comments (when
  PhoenixKitComments is installed) to
  `PhoenixKitComments.Web.CommentsComponent.forward_leaf_event/2`.
  Without this, comment Leaf editors render but the typed
  content never reaches the server. The clause only injects
  when `PhoenixKitComments.Web.CommentsComponent` is loaded —
  otherwise it's compiled away. User-defined `:leaf_changed`
  clauses (e.g. for a post-content editor on the same page)
  still win because they are defined first.

All fallbacks are injected via `@before_compile`, so user clauses
declared earlier in the module match before them.

# `build_nav_query`

Build the query-string map from a `{:navigate, params}` payload —
omitting defaults so a root, unsearched, first-page view yields a clean
bare URL.

# `on_mount`

# `parse_nav_params`

Parse a LiveView params map into the `nav_params` shape MediaBrowser's
controlled mode expects (`folder`, `q`, `page`, `filter_orphaned`,
`view`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
