Corex. FloatingPanel
(Corex v0.1.1)
View Source
Phoenix implementation of Zag.js Floating Panel.
Anatomy
Basic
<.floating_panel class="floating-panel">
<:trigger class="button button--ghost button--sm">
<span data-closed>Open panel</span>
<span data-open>Close panel</span>
</:trigger>
<:title>Panel</:title>
<:minimize_trigger>
<.heroicon name="hero-arrow-down-left" class="icon" />
</:minimize_trigger>
<:maximize_trigger>
<.heroicon name="hero-arrows-pointing-out" class="icon" />
</:maximize_trigger>
<:default_trigger>
<.heroicon name="hero-rectangle-stack" class="icon" />
</:default_trigger>
<:close_trigger>
<.heroicon name="hero-x-mark" class="icon" />
</:close_trigger>
<:content>
<p>
Congue molestie ipsum gravida a. Sed ac eros luctus, cursus turpis
non, pellentesque elit. Pellentesque sagittis fermentum.
</p>
</:content>
</.floating_panel>Required slots: :trigger, :title, :close_trigger, :content.
Set class on :trigger to style the outer trigger button (e.g. button button--ghost button--sm).
Use data-open and data-closed on elements inside :trigger to swap label when the panel is open vs closed (see default rules in floating-panel.css). You can also target [data-part="trigger"][data-state="open"] / closed with your own selectors.
Optional slots: :minimize_trigger, :maximize_trigger, :default_trigger. Omit them to hide the minimize, maximize, and restore controls.
Set position={%Corex.Point{}} or position={%{x: n, y: n}} for a fixed initial Zag Point (data-default-position / defaultPosition). If position is set, it overrides anchor placement.
Set size={%{width:, height:}} for initial dimensions (data-default-size / Zag defaultSize). Use value_size for a controlled current size (data-size / Zag size).
Optional positioning={%Corex.Positioning{}} sets data-position-* for the client hook. When position is omitted, the hook passes Zag getAnchorPosition from placement and boundary (e.g. placement: "bottom-start" and gutter: 16 for a bottom corner). Do not rely on both position and positioning for the same panel; prefer position for explicit pixels, positioning for placement rules.
API
Requires a stable id on <.floating_panel>.
| Function | Action | Returns |
|---|---|---|
set_open/2 | Set open state (client) | %Phoenix.LiveView.JS{} |
set_open/3 | Set open state (server) | socket |
Events
Pick an event name and pass it to on_* on <.floating_panel>.
Server events
| Event | When | Payload |
|---|---|---|
on_open_change="panel_open_changed" | Open state changes | %{"id" => id, "open" => boolean} |
on_position_change="panel_position_changed" | Position changes | %{"id" => id, ...} |
on_size_change="panel_size_changed" | Size changes | %{"id" => id, ...} |
on_stage_change="panel_stage_changed" | Stage changes | %{"id" => id, ...} |
Client events
| Event | When | event.detail |
|---|---|---|
on_open_change_client="panel-open-changed" | Open state changes | id, open |
on_position_change_client="panel-position-changed" | Position changes | id, ... |
on_size_change_client="panel-size-changed" | Size changes | id, ... |
on_stage_change_client="panel-stage-changed" | Stage changes | id, ... |
Style
Use data attributes to target elements:
[data-scope="floating-panel"][data-part="root"] {}
[data-scope="floating-panel"][data-part="trigger"] {}
[data-scope="floating-panel"][data-part="positioner"] {}
[data-scope="floating-panel"][data-part="content"] {}
[data-scope="floating-panel"][data-part="title"] {}
[data-scope="floating-panel"][data-part="header"] {}
[data-scope="floating-panel"][data-part="body"] {}
[data-scope="floating-panel"][data-part="drag-trigger"] {}
[data-scope="floating-panel"][data-part="resize-trigger"] {}
[data-scope="floating-panel"][data-part="close-trigger"] {}
[data-scope="floating-panel"][data-part="control"] {}
[data-scope="floating-panel"][data-part="stage-trigger"] {}If you wish to use the default Corex styling, you can use the class floating-panel on the component.
This requires to install Mix.Tasks.Corex.Design first and import the component css file.
@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/floating-panel.css";You can then use modifiers
<.floating_panel class="floating-panel floating-panel--accent floating-panel--lg">
<:trigger>
<span data-closed>Closed</span>
<span data-open>Open</span>
</:trigger>
<:title>Title</:title>
<:close_trigger>Close</:close_trigger>
<:content>Body</:content>
</.floating_panel>
Summary
Components
Attributes
id(:string) - The id of the floating panel.draggable(:boolean) - Whether the panel can be dragged. Defaults totrue.resizable(:boolean) - Whether the panel can be resized. Defaults totrue.allow_overflow(:boolean) - Whether content can overflow. Defaults totrue.close_on_escape(:boolean) - Whether Escape closes the panel. Defaults totrue.disabled(:boolean) - Whether the panel is disabled. Defaults tofalse.dir(:string) - Text direction. Defaults tonil. Must be one ofnil,"ltr", or"rtl".orientation(:string) - Layout orientation for CSS and ignored attribute lists. Defaults to"vertical". Must be one of"horizontal", or"vertical".size(:map) - Zag defaultSize; sets data-default-size on the hook root. Defaults tonil.value_size(:map) - Zag controlled size; sets data-size when present. Defaults tonil.position(:any) - Initial ZagPoint(defaultPosition/data-default-position).%Corex.Point{}or%{x:, y:}. Defaults tonil.positioning(Corex.Positioning) - Optional placement forgetAnchorPositionon the client (data-position-*). Ignored for anchor math whenpositionis set. Defaults tonil.min_size(:map) - Minimum size constraints. Defaults tonil.max_size(:map) - Maximum size constraints. Defaults tonil.persist_rect(:boolean) - Whether to persist position and size. Defaults tofalse.grid_size(:integer) - Grid snapping size for drag and resize. Defaults to1.on_open_change(:string) - Server event when open state changes. Defaults tonil.on_open_change_client(:string) - Client event when open state changes. Defaults tonil.on_position_change(:string) - Server event when position changes. Defaults tonil.on_size_change(:string) - Server event when size changes. Defaults tonil.on_stage_change(:string) - Server event when stage (minimized/maximized) changes. Defaults tonil.on_position_change_client(:string) - Client event when position changes. Defaults tonil.on_size_change_client(:string) - Client event when size changes. Defaults tonil.on_stage_change_client(:string) - Client event when stage (minimized/maximized) changes. Defaults tonil.translation(Corex.FloatingPanel.Translation) - Override translatable strings. Defaults tonil.- Global attributes are accepted.
Slots
trigger(required) - Accepts attributes:class(:string)
title(required) - Accepts attributes:class(:string)
minimize_trigger- Accepts attributes:class(:string)
maximize_trigger- Accepts attributes:class(:string)
default_trigger- Accepts attributes:class(:string)
close_trigger(required) - Accepts attributes:class(:string)
content(required) - Accepts attributes:class(:string)
API
Set open state from a control (phx-click).
<.action phx-click={Corex.FloatingPanel.set_open("my-floating-panel", true)}>Open</.action>
<.floating_panel id="my-floating-panel" class="floating-panel">
<:trigger class="button button--ghost button--sm"><span>Open</span></:trigger>
<:title>Panel</:title>
<:close_trigger><.heroicon name="hero-x-mark" class="icon" /></:close_trigger>
<:content><p>Content.</p></:content>
</.floating_panel>document.getElementById("my-floating-panel")?.dispatchEvent(
new CustomEvent("corex:floating-panel:set-open", {
bubbles: false,
detail: { open: true },
})
);
Set open state from handle_event.
<.action phx-click="open_panel">Open</.action>
<.floating_panel id="my-floating-panel" class="floating-panel">
<:trigger class="button button--ghost button--sm"><span>Open</span></:trigger>
<:title>Panel</:title>
<:close_trigger><.heroicon name="hero-x-mark" class="icon" /></:close_trigger>
<:content><p>Content.</p></:content>
</.floating_panel>def handle_event("open_panel", _, socket) do
{:noreply, Corex.FloatingPanel.set_open(socket, "my-floating-panel", true)}
end