PhoenixKitProjects.Web.Components.PopupHost (PhoenixKitProjects v0.5.0)

Copy Markdown View Source

Layered daisyUI <dialog> modal stack driven by a modal_stack assign. The function component renders the always-visible content (default slot) plus one <dialog> per stack frame, delegating each frame's body rendering to the :frame slot the host provides.

The host LV owns state — receiving :opened / :closed / :saved / :deleted PubSub events, pushing/popping the stack, generating frame_refs. See PhoenixKitProjects.Web.PopupHostLive for the opinionated wrapper that does this automatically. Use the component directly when you need full control (e.g. modal-stack alongside other host state).

Reuses the daisyUI modal pattern from project_show_live.ex:1633-1662<dialog open class="modal modal-open"> + ESC handler + modal-backdrop button.

Slots

  • :inner_block (default) — the always-visible content. Host typically embeds the root LV here via live_render(@socket, ...).
  • :frame (with :let={frame}) — per-stack-frame content. Receives the frame map (%{frame_ref, lv, session, id}) so the host can call live_render(@socket, frame.lv, id: frame.id, session: frame.session).

Attrs

  • :modal_stack — list of frame maps (ordered bottom→top).
  • :on_close — event name fired on ESC, backdrop-click, and explicit close buttons. Host's handle_event/3 must pop the top frame in response. Defaults to "close_top_modal".
  • :class — outer wrapper class. Defaults to nil (no wrapping).

Z-index layering

Each frame's <dialog> gets z-[N] where N starts at 50 (matches the start-project modal precedent) and increments by 10 per stack depth. Stack cap at 5 frames matches PopupHostLive's @max_stack_depth.

Example

<.popup_host modal_stack={@modal_stack} on_close="close_top_modal">
  {live_render(@socket, PhoenixKitProjects.Web.OverviewLive,
     id: "embed-root",
     session: %{
       "mode" => "emit",
       "pubsub_topic" => @host_topic,
       "wrapper_class" => "flex flex-col w-full px-4 py-6 gap-6"
     })}

  <:frame :let={frame}>
    {live_render(@socket, frame.lv, id: frame.id, session: frame.session)}
  </:frame>
</.popup_host>

Summary

Functions