Corex. Collapsible
(Corex v0.1.2)
View Source
Phoenix implementation of Zag.js Collapsible.
Anatomy
Minimal
<.collapsible class="collapsible">
<:trigger>Toggle</:trigger>
<:content>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</:content>
</.collapsible>With indicator
Optional :closed and :opened slots render after the trigger label. Use one chevron in :closed and default CSS rotates it when open.
<.collapsible class="collapsible">
<:trigger>Toggle</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</:content>
</.collapsible>Custom slots
Use :let={collapsible} on slots to read collapsible.open and collapsible.disabled.
<.collapsible class="collapsible">
<:trigger :let={c}>
{if c.open, do: "Collapse", else: "Expand"}
</:trigger>
<:closed>
<.heroicon name="hero-chevron-down" />
</:closed>
<:opened>
<.heroicon name="hero-chevron-up" />
</:opened>
<:content :let={_c}>
Panel body with custom opened/closed adornments.
</:content>
</.collapsible>API
Requires a stable id on <.collapsible>.
| Function | Action | Returns |
|---|---|---|
set_open/2 | Set open state (client) | %Phoenix.LiveView.JS{} |
set_open/3 | Set open state (server) | socket |
set_open
<.action phx-click={Corex.Collapsible.set_open("collapsible-api", true)} class="button button--sm">
Open
</.action>
<.collapsible id="collapsible-api" class="collapsible">
<:trigger>Toggle</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>Lorem ipsum dolor sit amet.</:content>
</.collapsible>def handle_event("open_collapsible", _, socket) do
{:noreply, Corex.Collapsible.set_open(socket, "collapsible-api", true)}
endEvents
Server events
| Event | When | Payload |
|---|---|---|
on_open_change="collapsible_open_changed" | Open state changes | %{"id" => id, "open" => open} |
on_open_change
<.collapsible
class="collapsible"
on_open_change="collapsible_open_changed"
>
<:trigger>Toggle</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>Lorem ipsum dolor sit amet.</:content>
</.collapsible>def handle_event("collapsible_open_changed", %{"id" => id, "open" => open}, socket) do
{:noreply, assign(socket, :open, open)}
endClient events
| Event | When | event.detail |
|---|---|---|
on_open_change_client="collapsible-open-changed" | Open state changes | id, open, previousOpen |
on_open_change_client
<.collapsible
id="collapsible-events-client"
class="collapsible"
on_open_change_client="collapsible-open-changed"
>
<:trigger>Toggle</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>Lorem ipsum dolor sit amet.</:content>
</.collapsible>document.getElementById("collapsible-events-client")?.addEventListener("collapsible-open-changed", (e) => {
console.log(e.detail);
});Patterns
Async
<.async_result :let={panel} assign={@collapsible}>
<:loading>
<.collapsible_skeleton class="collapsible" />
</:loading>
<.collapsible class="collapsible" open={panel.open}>
<:trigger>Details</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>{panel.body}</:content>
</.collapsible>
</.async_result>socket =
assign_async(socket, :collapsible, fn ->
{:ok, %{collapsible: %{open: false, body: "Loaded after async."}}}
end)Controlled
<.collapsible
class="collapsible"
controlled
open={@open}
on_open_change="patterns_collapsible_changed"
>
<:trigger>Controlled</:trigger>
<:closed>
<.heroicon name="hero-chevron-right" />
</:closed>
<:content>LiveView owns open.</:content>
</.collapsible>def handle_event("patterns_collapsible_changed", %{"open" => open}, socket) do
{:noreply, assign(socket, :open, open)}
endStyle
Target parts with data-scope and data-part. Content exposes --height, --collapsed-height, and related CSS variables for height animations.
[data-scope="collapsible"][data-part="root"] {}
[data-scope="collapsible"][data-part="trigger"] {}
[data-scope="collapsible"][data-part="content"] {}
[data-scope="collapsible"][data-part="closed"] {}
[data-scope="collapsible"][data-part="opened"] {}@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/collapsible.css";Color
| Modifier | Classes |
|---|---|
| Default | collapsible collapsible--md |
| Accent | collapsible collapsible--md collapsible--accent |
| Brand | collapsible collapsible--md collapsible--brand |
Size
| Modifier | Classes |
|---|---|
| SM | collapsible collapsible--sm |
| MD | collapsible collapsible--md |
| LG | collapsible collapsible--lg |
Summary
Components
Renders a collapsible component.
Renders a loading skeleton for the collapsible component.
Components
Renders a collapsible component.
Requires :trigger and :content slots. Optional :closed and :opened slots add visual surfaces after the trigger label (Connect data-part only, no Zag props). Use :let={collapsible} on slots to access collapsible.open and collapsible.disabled.
Attributes
id(:string) - The id of the collapsible, useful for API to identify the collapsible.open(:boolean) - The initial open state or the controlled open state. Defaults tofalse.controlled(:boolean) - Whether the collapsible is controlled. Only in LiveView, the on_open_change event is required. Defaults tofalse.disabled(:boolean) - Whether the collapsible is disabled. Defaults tofalse.dir(:string) - The direction of the collapsible. When nil, derived from document (html lang + config :rtl_locales). Defaults tonil. Must be one ofnil,"ltr", or"rtl".orientation(:string) - Layout orientation for CSS. Defaults to"vertical". Must be one of"horizontal", or"vertical".on_open_change(:string) - The server event name when the open state changes. Defaults tonil.on_open_change_client(:string) - The client event name when the open state changes. Defaults tonil.- Global attributes are accepted.
Slots
trigger(required) - Trigger button content. Use :let={collapsible} to access open and disabled state. Accepts attributes:class(:string)
content(required) - Expandable content. Use :let={collapsible} to access open and disabled state. Accepts attributes:class(:string)
closed- Optional surface after the trigger, visible when closed (or use with:openedto swap content by state). Accepts attributes:class(:string)
opened- Optional surface after the trigger, visible when open (use with:closedto swap content by state). Accepts attributes:class(:string)
Renders a loading skeleton for the collapsible component.
Attributes
dir(:string) - Same as collapsible: logical direction for the skeleton root. Defaults tonil. Must be one ofnil,"ltr", or"rtl".orientation(:string) - Same as collapsible: layout orientation for CSS. Defaults to"vertical". Must be one of"horizontal", or"vertical".- Global attributes are accepted.
API
Set expanded or collapsed state from a control (phx-click).
<.action phx-click={Corex.Collapsible.set_open("my-collapsible", true)}>Expand</.action>
<.collapsible id="my-collapsible" class="collapsible collapsible--md">
<:trigger :let={c}>{if c.open, do: "Hide", else: "Show"}</:trigger>
<:content>Details.</:content>
</.collapsible>document.getElementById("my-collapsible")?.dispatchEvent(
new CustomEvent("corex:collapsible:set-open", {
bubbles: false,
detail: { open: true },
})
);
Set open state from handle_event.
<.action phx-click="expand_collapsible">Expand</.action>
<.collapsible id="my-collapsible" class="collapsible collapsible--md">
<:trigger :let={c}>{if c.open, do: "Hide", else: "Show"}</:trigger>
<:content>Details.</:content>
</.collapsible>def handle_event("expand_collapsible", _, socket) do
{:noreply, Corex.Collapsible.set_open(socket, "my-collapsible", true)}
end