Skua.Components.Overlay (Skua v0.1.0)

Copy Markdown View Source

Top-layer overlay components — popover, dialog and drawer.

Both use browser-native top-layer primitives (the Popover API and native <dialog>), so an ancestor's overflow can never clip them. They are morphdom-safe: the dialog ships JS.ignore_attributes("open") so a re-render never strips the browser-set open attribute (LiveView #4152), and every panel carries a stable id so morphdom never replaces the node (which would force-close top-layer state).

Summary

Functions

JS command that closes the dialog with the given id.

A modal dialog built on the native <dialog> element + showModal().

A drawer / slide-over sheet — a native <dialog> anchored to a viewport edge. It shares the dialog engine (native showModal() backdrop, focus trap, and Esc-to-close), so open and close it exactly like a dialog

JS command that opens the dialog with the given id (calls showModal() via the hook).

A popover: a trigger that toggles a top-layer panel positioned next to it.

Functions

close_dialog(js \\ %JS{}, id)

JS command that closes the dialog with the given id.

dialog(assigns)

A modal dialog built on the native <dialog> element + showModal().

<.dialog id="confirm">
  <:title>Delete project?</:title>
  <:subtitle>This cannot be undone.</:subtitle>
  <p>All data will be permanently removed.</p>
  <:footer>
    <.button data-sk-close>Cancel</.button>
    <.button variant="danger" phx-click="delete">Delete</.button>
  </:footer>
</.dialog>

Open it from any element with phx-click={Skua.Components.Overlay.open_dialog("confirm")}. Native showModal() provides the inert backdrop, focus trap, and Esc-to-close; JS.ignore_attributes("open") keeps it open across LiveView re-renders.

Attributes

  • id (:string) (required)
  • class (:any) - Defaults to nil.
  • on_close (Phoenix.LiveView.JS) - Defaults to %Phoenix.LiveView.JS{ops: []}.

Slots

  • title
  • subtitle
  • footer
  • inner_block (required)

drawer(assigns)

A drawer / slide-over sheet — a native <dialog> anchored to a viewport edge. It shares the dialog engine (native showModal() backdrop, focus trap, and Esc-to-close), so open and close it exactly like a dialog:

<.button phx-click={Skua.Components.Overlay.open_dialog("filters")}>Filters</.button>

<.drawer id="filters" side="right">
  <:title>Filters</:title>
  <:subtitle>Narrow the results.</:subtitle>
  <p>controls</p>
  <:footer>
    <.button data-sk-close>Cancel</.button>
    <.button variant="primary" data-sk-close>Apply</.button>
  </:footer>
</.drawer>

side is right (default) | left | top | bottom.

Attributes

  • id (:string) (required)
  • side (:string) - Defaults to "right". Must be one of "right", "left", "top", or "bottom".
  • class (:any) - Defaults to nil.
  • on_close (Phoenix.LiveView.JS) - Defaults to %Phoenix.LiveView.JS{ops: []}.

Slots

  • title
  • subtitle
  • footer
  • inner_block (required)

open_dialog(js \\ %JS{}, id)

JS command that opens the dialog with the given id (calls showModal() via the hook).

popover(assigns)

A popover: a trigger that toggles a top-layer panel positioned next to it.

<.popover id="invite" pad width="280px">
  <:trigger><.button>Invite</.button></:trigger>
  <p>Panel content</p>
</.popover>

The id must be stable. The trigger is the button (give it a label via the trigger slot and a look via trigger_variant) — do not nest a <.button> inside the slot, which would produce invalid nested buttons. It owns a DOM id and a measurable box and is wired with aria-haspopup/ aria-expanded/aria-controls.

Attributes

  • id (:string) (required)
  • pad (:boolean) - Defaults to false.
  • width (:string) - Defaults to nil.
  • placement (:string) - Defaults to "bottom". Must be one of "bottom", or "right".
  • trigger_variant (:string) - Defaults to "secondary". Must be one of "primary", "secondary", "ghost", or "danger".
  • class (:any) - Defaults to nil.

Slots

  • trigger (required)
  • inner_block (required)