Promise-based programmatic dialogs. No HTML boilerplate — created on-demand via JavaScript.

Uso básico

All dialog functions return Promises, so you can use async/await for clean, synchronous-looking code:

// Confirm dialog
const confirmed = await PureAdmin.confirm({
  title: 'Delete Item?',
  message: 'This action cannot be undone.',
  variant: 'danger'
});

if (confirmed) {
  // User clicked OK
}

// Alert dialog
await PureAdmin.alert({
  title: 'Success!',
  message: 'Your changes have been saved.',
  variant: 'success'
});

// Prompt dialog
const name = await PureAdmin.prompt({
  title: 'Enter Name',
  message: 'Please enter your name:',
  defaultValue: 'John Doe'
});

if (name !== null) {
  console.log('User entered:', name);
}

Diálogos de confirmación

Two-button dialogs that return true (confirmed) or false (cancelled).

Opciones de posición

Dialogs can be positioned in the center (default) or at the top of the viewport.

// Center position (default)
await PureAdmin.alert({
  title: 'Centered',
  message: 'This dialog is centered.',
  position: 'center'  // default
});

// Top position
await PureAdmin.prompt({
  title: 'Top Position',
  message: 'This dialog appears near the top.',
  position: 'top'
});

Diálogos de alerta

Single-button dialogs for notifications. Just wait for the user to acknowledge.

Diálogos de solicitud

Text input dialogs that return the entered value (or null if cancelled).

Diálogos secuenciales

Chain multiple dialogs together using async/await:

async function sequentialDialogs() {
  const name = await PureAdmin.prompt({
    title: 'Step 1 of 3',
    message: 'Enter your name:'
  });

  if (name === null) return;

  const email = await PureAdmin.prompt({
    title: 'Step 2 of 3',
    message: 'Enter your email:'
  });

  if (email === null) return;

  const confirmed = await PureAdmin.confirm({
    title: 'Step 3 of 3',
    message: `Confirm: ${name} <${email}>`,
    variant: 'success'
  });

  if (confirmed) {
    await PureAdmin.alert({
      title: 'Complete!',
      message: 'Registration successful.',
      variant: 'success'
    });
  }
}

Integración de LiveView

Use onclick with liveSocket to push results back to the server. The server records the outcome and pushes a toast straight back — try the buttons:

Client — push the result to the server:

// Push a dialog result to LiveView from plain JS (outside a hook).
async function confirmAndPush(action) {
  const confirmed = await PureAdmin.confirm({
    title: 'Delete Item?',
    message: 'This action cannot be undone.',
    variant: 'danger'
  });

  if (confirmed) {
    // Use the public execJS API with the same encoded command phx-click uses.
    // `value` becomes the event params on the server.
    const el = document.querySelector('[data-phx-main]');
    window.liveSocket.execJS(el, JSON.stringify(
      [["push", { event: "dialog_result", value: { action, result: "confirmed" } }]]
    ));
  }
}

Servidor — reply with a toast, no reload:

# The LiveView receives the pushed event and sends a toast back — no reload,
# the PureAdminToast hook just renders it wherever the user is.
def handle_event("dialog_result", %{"action" => action, "result" => result}, socket) do
  {:noreply,
   socket
   |> assign(last_result: "#{action}: #{result}")
   |> push_toast("success", "Server received your action",
        "You just performed #{action} (#{result}) in the dialog.")}
end

Server-Initiated Dialog

The opposite direction, and a first-class keen feature: the button below is a real phx-click that hits the server first. The server opens a server-rendered dialog with PureAdmin.Dialog.confirm/2; each button is an ordinary event, so the user's choice arrives as a normal handle_event and the dialog auto-closes — no client bridge, no promise plumbing. Every outcome pushes a toast back from the server, and the ✕/backdrop/Escape dismiss is a first-class event too (on_dismiss), so nothing closes silently. Classic use: "this document was changed by someone else."

Pick a button, or dismiss with ✕ / click-outside / Escape — each pushes a different toast.

# 1. Setup once. on_mount seeds :pa_dialog + auto-close; the host renders it.
live_session :default, on_mount: [{PureAdmin.Dialog, :default}] do
  # ...your live routes...
end
# app.html.heex, mounted once:
#   <PureAdmin.Dialog.host dialog={@pa_dialog} />

# 2. A real phx-click handler opens the dialog FROM THE SERVER. `on_dismiss`
#    makes the ✕/backdrop/Esc dismiss a first-class event too.
def handle_event("simulate_external_edit", _params, socket) do
  {:noreply,
   PureAdmin.Dialog.confirm(socket,
     title: "Document changed",
     message: "Someone else updated this document. Reload their version?",
     variant: "warning", banded: true, position: :top,
     confirm: [label: "Reload theirs", variant: "warning", event: "doc_reload"],
     cancel: [label: "Keep mine", event: "doc_keep"],
     on_dismiss: "doc_dismiss"
   )}
end

# 3. Every outcome is an ORDINARY event; the dialog auto-closes. Push a toast
#    straight back from the server (PureAdmin.Components.Toast.push_toast/4).
def handle_event("doc_reload", _p, socket), do:
  {:noreply, push_toast(socket, "warning", "Document reloaded", "Server version loaded.")}

def handle_event("doc_keep", _p, socket), do:
  {:noreply, push_toast(socket, "success", "Kept your version", "Local changes preserved.")}

def handle_event("doc_dismiss", _p, socket), do:   # ✕ / backdrop / Escape
  {:noreply, push_toast(socket, "info", "Dialog dismissed", "Nothing was changed.")}

Custom / Form Dialog

Standard dialogs can't express an arbitrary form, so PureAdmin.Dialog.open/3 just stashes a keyed spec and you render your own <.modal> with a real LiveView form — full phx-change validation and phx-submit. close/1 dismisses it. The server opens it the same way (a phx-click handler), but owns the content.

# Custom/form dialog (requirement B): open/3 stashes a keyed spec — YOU render
# your own <.modal> with a real LiveView form. close/1 dismisses it.
def handle_event("edit_profile", _p, socket) do
  form = to_form(%{"name" => "Ada Lovelace", "email" => "ada@example.com"}, as: :profile)
  {:noreply, socket |> assign(profile_form: form) |> PureAdmin.Dialog.open(:edit_profile)}
end

def handle_event("validate_profile", %{"profile" => params}, socket) do
  {:noreply, assign(socket, profile_form: to_form(params, as: :profile, errors: errors(params)))}
end

def handle_event("save_profile", %{"profile" => params}, socket) do
  case errors(params) do
    []   -> {:noreply, socket |> PureAdmin.Dialog.close() |> assign(profile_result: "Saved.")}
    errs -> {:noreply, assign(socket, profile_form: to_form(params, as: :profile, errors: errs))}
  end
end

# render — your OWN modal + form, keyed on @pa_dialog:
<.modal :if={@pa_dialog && @pa_dialog.key == :edit_profile} id="edit-profile" show
        title_text="Edit profile" on_cancel={JS.push("pa-dialog:close")}>
  <.form for={@profile_form} class="pa-form"
         phx-change="validate_profile" phx-submit="save_profile">
    <div class="pa-form-group"><label>Name</label><.input field={@profile_form[:name]} /></div>
    <div class="pa-form-group"><label>Email</label><.input field={@profile_form[:email]} type="email" /></div>
    <div class="pa-modal__footer">
      <button type="button" class="pa-btn pa-btn--secondary" phx-click="pa-dialog:close">Cancel</button>
      <button type="submit" class="pa-btn pa-btn--primary">Save</button>
    </div>
  </.form>
</.modal>

Referencia de la API

PureAdmin.confirm(options)

Returns Promise<boolean>

OpciónTipoPredeterminadoDescripción
title string 'Confirm' Dialog title
message string 'Are you sure?' Dialog message
confirmText string 'OK' Confirm button text
cancelText string 'Cancel' Cancel button text
variant string 'primary' Header theme: primary, success, warning, danger
confirmVariant string (same as variant) Confirm button style
size string 'sm' Modal size: sm, md, lg, xl
position string 'center' Vertical position: center, top
closeOnBackdrop boolean true Close when clicking outside

PureAdmin.alert(options)

Returns Promise<void>

OpciónTipoPredeterminadoDescripción
title string 'Alert' Dialog title
message string '' Dialog message
okText string 'OK' Button text
variant string 'primary' Header and button theme
size string 'sm' Modal size
position string 'center' Vertical position: center, top

PureAdmin.prompt(options)

Returns Promise<string | null>

OpciónTipoPredeterminadoDescripción
title string 'Input' Dialog title
message string 'Enter value:' Dialog message
defaultValue string '' Initial input value
placeholder string '' Input placeholder
confirmText string 'OK' Confirm button text
cancelText string 'Cancel' Cancel button text
validator function null Validation: (value) => true | string
variant string 'primary' Header theme
size string 'sm' Modal size
position string 'center' Vertical position: center, top

John Doe

Administrator

Configuración

Press Ctrl+K to preview the palette.
Sidebar
Display
Profile Panel
Body text size. All elements scale proportionally.
Comandos
🚀
Desplegar a entorno
Desplegar una rama a un entorno
g d
👤
Asignar a usuario
Asignar un elemento a un miembro del equipo
g a
🧭
Ir a la página
Navegar a una página
g g
🎨
Cambiar tema
Cambiar el tema visual
g t
Buscar
📦
Productos
Buscar productos
:products
📋
Pedidos
Buscar pedidos
:orders
👥
Usuarios
Buscar usuarios
:users
🧾
Facturas
Buscar facturas
:invoices