PureAdmin ships 14 JavaScript hooks for interactive features. Import them all via PureAdminHooks or individually.
Setup
import { PureAdminHooks } from "keen_pure_admin"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { ...PureAdminHooks }
})Available Hooks
PureAdminSettings
Settings panel for theme mode, layout width, sidebar, fonts. Fetches theme manifests from /api/themes/manifests and dynamically populates the theme selector. All settings persist to localStorage.
Used by: <.settings_panel />
PureAdminProfilePanel
Profile panel with tab switching, favorites management, and click-outside-to-close.
Used by: <.profile_panel />
PureAdminTooltip
CSS-only tooltip positioning using Floating UI. Handles placement, auto-flip, and theme color variants.
Used by: <.tooltip /> (when using floating/JS positioning)
PureAdminPopover
Click-triggered popover with title, placement, size, and alignment. Uses Floating UI for positioning, moves content to document.body to avoid clipping.
Used by: <.popover />
PureAdminToast
Toast auto-dismiss with configurable duration. Handles show/hide transitions and progress bar animation. Renders toasts client-side from push_event data.
Used by: <.toast_container is_hook />
PureAdminFlash
Independent inline flash message containers. Multiple containers on the same page receive messages independently via push_flash/5. Renders pa-alert elements client-side. Supports markdown body (bold, italic, [links](url), lists, --- horizontal rules), action buttons with server callbacks, and auto-dismiss.
Used by: <.flash_container />
Usage:
<.flash_container id="my-form" /># Simple flash
socket |> push_flash("my-form", "success", "Saved!")
# With markdown body and action buttons
socket |> push_flash("my-form", "warning", """
Are you sure you want to delete **Invoice #1234**?
This action cannot be undone.
""",
title: "Confirm Deletion",
actions: [
%{label: "Delete", event: "delete-record", params: %{id: 1234}, variant: "danger"},
%{label: "Cancel", dismiss: true, variant: "secondary"}
])Options:
| Option | Default | Description |
|---|---|---|
:title | nil | Heading above the message |
:duration | 0 | Auto-dismiss in ms (0 = persistent) |
:dismissible | true | Show close button |
:actions | [] | Action button maps (see above) |
PureAdminCommandPalette
Spotlight-style command palette with three modes:
- Commands (
/prefix) — multi-step action wizards with step progression - Search contexts (
:prefix) — scoped entity search - Global search (no prefix) — search across everything
Keyboard: Ctrl+K toggle, ↑↓ navigate, Enter/Tab select, Escape/Backspace-at-0 step back. Debounced search input (150ms) for search modes, instant for command/context list filtering.
Used by: <.command_palette />
Event protocol (hook → LiveView):
| Event | Payload | When |
|---|---|---|
cp:toggle | {} | Ctrl+K |
cp:close | {} | Escape at top level, backdrop click |
cp:input | {query} | Input changed |
cp:navigate | {direction} | Arrow up/down |
cp:page | {direction} | Arrow left/right (search modes) |
cp:select | {index} | Enter, Tab, or click |
cp:step_back | {} | Backspace at pos 0, Escape in step/context mode |
LiveView → hook (push_event):
| Event | Payload | Purpose |
|---|---|---|
cp:focus | {} | Focus input |
cp:reset_input | {value} | Force-set input value on mode transition |
Registering commands:
commands = [
%{
id: "deploy",
shortcut: "/deploy",
aliases: ["/d"],
name: "Deploy to Environment",
description: "Deploy a branch to an environment",
icon: "🚀",
steps: [
%{id: "environment", prompt: " in ", placeholder: "Select environment..."},
%{id: "branch", prompt: " branch ", placeholder: "Type branch...", free_text: true}
]
}
]Registering search contexts:
contexts = [
%{id: "products", shortcut: ":products", aliases: [":p"], name: "Products", icon: "📦"}
]Handling command completion:
def handle_info({:command_complete, "deploy", selections}, socket) do
env = Enum.find(selections, & &1.step_id == "environment")
# Do something with selections...
{:noreply, socket}
endDisplay styles:
Two visual modes for command step progression:
display="inline"(default) — Svelte-style. Input shows the full accumulated sentence (e.g.,/assign iPad Air to). A command badge appears on the right. The locked prefix can't be deleted.display="tokens"— Token-style. Previous selections render as colored spans above a clean input. Each step starts with an empty input.
<.command_palette display="inline" ... />
<.command_palette display="tokens" ... />PureAdminDetailPanel
Detail panel toggle for inline split-view and overlay modes.
Used by: Detail panel patterns (see detail panel demo)
PureAdminSidebarResize
Drag-to-resize sidebar with mouse/touch events. Stores width in localStorage.
Used by: <.sidebar /> with is_resizable setting
PureAdminCharCounter
Character counter for textarea/input fields. Configurable max length with translatable message templates via data-msg/data-msg-over with {count}/{max} placeholders.
Used by: <.input type="textarea" /> with maxlength and char counter
PureAdminCheckbox
Syncs the indeterminate property from data-indeterminate attribute. Required for tri-state checkboxes since HTML doesn't have an indeterminate attribute.
Used by: <.checkbox is_indeterminate />
PureAdminSplitButton
Split button dropdown via Floating UI. Manages open/close state, closes other open split buttons, and handles pushEvent for menu item clicks and inline action buttons. Since the menu is moved to document.body for positioning, native phx-click doesn't work — the hook forwards clicks via pushEvent.
Used by: <.split_button />
PureAdminSidebarSubmenu
Persists sidebar submenu open/closed state to localStorage. Restores state on mount, URL-active submenus always win over stored state. Uses MutationObserver to detect JS command class changes.
Used by: <.sidebar_submenu />
PureAdminInfiniteScroll
IntersectionObserver-based infinite scroll. Fires a LiveView event when a sentinel element scrolls into view. Configurable throttle and preload buffer.
Data attributes:
| Attribute | Default | Description |
|---|---|---|
data-event | "load_more" | LiveView event to push |
data-has-more | "true" | Set to "false" to stop |
data-throttle | "500" | Min ms between triggers |
data-root-margin | "200px" | Preload buffer distance |
Usage:
<div
id="scroll-sentinel"
phx-hook="PureAdminInfiniteScroll"
data-event="load_more"
data-has-more={to_string(@has_more)}
>
<.loader :if={@loading} />
</div>Page Context
Server-rendered JSON available to JS synchronously via a hidden input. Avoids API fetches on page load.
import { getPageContext, getContextValue } from "keen_pure_admin"
const ctx = getPageContext() // full context
const manifests = getContextValue("themeManifests") // single keyThe settings panel reads themeManifests from the context automatically. Apps register providers:
config :keen_pure_admin,
page_context_providers: [&MyApp.PageContext.theme_manifests/1]Render in root layout: <.page_context />
Logging
Categorized, color-coded, silent by default. Enable at runtime:
// Browser console
PureAdmin.logging.enableLogging() // all → debug
PureAdmin.logging.setCategoryLevel('PA:SETTINGS', 'debug') // one category
PureAdmin.logging.getCategories() // list all
// KeenMate convention
window.components['keen-pure-admin'].logging.enableLogging()Categories: PA:SETTINGS, PA:CMD_PALETTE (more added as hooks are instrumented).
Use in custom hooks:
import { createLogger } from "keen_pure_admin"
const log = createLogger('MY_HOOK')
log.debug('mounted')Modal Dialogs (non-hook)
Programmatic dialogs are initialized separately:
import { initModalDialogs } from "keen_pure_admin"
initModalDialogs()This enables PureAdmin.confirm(), PureAdmin.alert(), and PureAdmin.prompt() globally.