Corex.Accordion
(Corex v0.1.0-beta.2)
View Source
Phoenix implementation of the Zag.js Accordion.
Anatomy
Minimal
<.accordion
class="accordion"
items={
Corex.Content.new([
%{trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit."},
%{trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula."},
%{trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a."}
])
}
/>With slots
With items and <:indicator> slot so every panel shares the same indicator markup.
<.accordion
class="accordion"
items={
Corex.Content.new([
%{trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit."},
%{trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula."},
%{trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a."}
])
}
>
<:indicator>
<.heroicon name="hero-chevron-right" />
</:indicator>
</.accordion>Custom slots
With items, customize each item using slots with :let={item} to access the item and its meta data
<.accordion
class="accordion"
value="lorem"
items={
Corex.Content.new([
%{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique.",
meta: %{indicator: "hero-arrow-long-right", icon: "hero-chat-bubble-left-right"}
},
%{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus.",
meta: %{indicator: "hero-chevron-right", icon: "hero-device-phone-mobile"}
},
%{
value: "donec",
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus.",
disabled: true,
meta: %{indicator: "hero-chevron-double-right", icon: "hero-phone"}
}
])
}
>
<:trigger :let={item}>
<.heroicon name={item.meta.icon} />{item.trigger}
</:trigger>
<:content :let={item}><p>{item.content}</p></:content>
<:indicator :let={item}>
<.heroicon name={item.meta.indicator} />
</:indicator>
</.accordion>Manual slots
With an empty items list, use multiple :trigger, :content, and optional :indicator slots.
Each slot takes a value string that ties the three together.
<.accordion class="accordion" value="lorem">
<:trigger value="lorem">
<.heroicon name="hero-chevron-right" /> Lorem ipsum dolor sit amet
</:trigger>
<:content value="lorem"><p>Consectetur adipiscing elit. Sed sodales ullamcorper tristique.</p></:content>
<:indicator value="lorem">
<.heroicon name="hero-chevron-down" />
</:indicator>
<:trigger value="duis">
<.heroicon name="hero-chevron-right" /> Duis dictum gravida odio ac pharetra?
</:trigger>
<:content value="duis"><p>Nullam eget vestibulum ligula, at interdum tellus.</p></:content>
<:indicator value="duis">
<.heroicon name="hero-chevron-down" />
</:indicator>
</.accordion>Compound
Take full structural control with the accordion_root, accordion_item, accordion_trigger, accordion_content, and accordion_indicator sub-components.
Manual items
<.accordion :let={ctx} compound class="accordion">
<.accordion_root ctx={ctx}>
<.accordion_item :let={item} ctx={ctx} value="lorem">
<.accordion_trigger item={item}>
Lorem ipsum dolor sit amet
<:indicator>
<.accordion_indicator item={item}>
<.heroicon name="hero-chevron-right" />
</.accordion_indicator>
</:indicator>
</.accordion_trigger>
<.accordion_content item={item}>
<p>Consectetur adipiscing elit. Sed sodales ullamcorper tristique.</p>
</.accordion_content>
</.accordion_item>
<.accordion_item :let={item} ctx={ctx} value="duis">
<.accordion_trigger item={item}>
Duis dictum gravida odio ac pharetra?
<:indicator>
<.accordion_indicator item={item}>
<.heroicon name="hero-chevron-right" />
</.accordion_indicator>
</:indicator>
</.accordion_trigger>
<.accordion_content item={item}>
<p>Nullam eget vestibulum ligula, at interdum tellus.</p>
</.accordion_content>
</.accordion_item>
<.accordion_item :let={item} ctx={ctx} value="donec">
<.accordion_trigger item={item}>
Donec condimentum ex mi
<:indicator>
<.accordion_indicator item={item}>
<.heroicon name="hero-chevron-right" />
</.accordion_indicator>
</:indicator>
</.accordion_trigger>
<.accordion_content item={item}>
<p>Congue molestie ipsum gravida a. Sed ac eros luctus.</p>
</.accordion_content>
</.accordion_item>
</.accordion_root>
</.accordion>From a list
<.accordion :let={ctx} compound id="faq" class="accordion">
<.accordion_root ctx={ctx}>
<.accordion_item :for={entry <- @items} :let={item} ctx={ctx} value={entry.value}>
<.accordion_trigger item={item}>
{entry.trigger}
<:indicator>
<.accordion_indicator item={item}>
<.heroicon name="hero-chevron-right" />
</.accordion_indicator>
</:indicator>
</.accordion_trigger>
<.accordion_content item={item}>
<p>{entry.content}</p>
</.accordion_content>
</.accordion_item>
</.accordion_root>
</.accordion>Patterns
Async
defmodule MyAppWeb.AccordionAsyncLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
socket =
socket
|> assign_async(:accordion, fn ->
items =
Corex.Content.new([
%{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique.",
disabled: true
},
%{
value: "duis",
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
value: "donec",
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus."
}
])
{:ok, %{accordion: %{items: items, value: ["duis", "donec"]}}}
end)
{:ok, socket}
end
def render(assigns) do
~H"""
<.async_result :let={accordion} assign={@accordion}>
<:loading>
<.accordion_skeleton count={3} class="accordion" />
</:loading>
<:failed>Could not load accordion.</:failed>
<.accordion
id="async-accordion"
class="accordion"
items={accordion.items}
value={accordion.value}
/>
</.async_result>
"""
end
endControlled
defmodule MyAppWeb.AccordionLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, :accordion_value, ["lorem"])}
end
def handle_event("accordion_value_changed", %{"id" => _id, "value" => value}, socket) do
{:noreply, assign(socket, :accordion_value, value)}
end
def render(assigns) do
~H"""
<.accordion
id="my-accordion"
controlled
value={@accordion_value}
on_value_change="accordion_value_changed"
class="accordion"
items={
Corex.Content.new([
%{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit."
},
%{
value: "duis",
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula."
}
])
}
/>
"""
end
endAnimation
js
Built-in height and opacity via the Web Animations API. Tune timing with animation_options using Corex.Animation.Height.
<.accordion
class="accordion"
animation="js"
animation_options={%Corex.Animation.Height{duration: 0.3, easing: "ease-out", opacity_start: 0, opacity_end: 1}}
items={
Corex.Content.new([
%{
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus."
}
])
}
>
<:indicator>
<.heroicon name="hero-chevron-right" />
</:indicator>
</.accordion>instant
Zag toggles the native hidden attribute; no height animation.
<.accordion
class="accordion"
animation="instant"
items={
Corex.Content.new([
%{
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus."
}
])
}
>
<:indicator>
<.heroicon name="hero-chevron-right" />
</:indicator>
</.accordion>custom
The hook removes hidden and dispatches a browser CustomEvent when the value changes. Use on_value_change_client for the event name. The event detail is enriched with deltas so user code rarely needs DOM lookups beyond the affected items:
// event.detail (AccordionChangedDetail)
{ id, value, previousValue, added, removed }Animate panels in your own JS. The example below also seeds initial closed-state styling on mount and after LiveView navigations.
<.accordion
id="accordion-custom-animate"
class="accordion"
animation="custom"
on_value_change_client="my-accordion-changed"
items={
Corex.Content.new([
%{
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus."
}
])
}
>
<:indicator>
<.heroicon name="hero-chevron-right" />
</:indicator>
</.accordion>import { animate } from "motion"
import {
findAccordionContent,
animateHeightOpen,
animateHeightClose,
} from "corex"
const reducedMotion = () =>
window.matchMedia("(prefers-reduced-motion: reduce)").matches
document.addEventListener("my-accordion-changed", (e) => {
const root = document.getElementById(e.detail.id)
if (!root) return
e.detail.added.forEach((v) => {
const el = findAccordionContent(root, v)
if (!el) return
animateHeightOpen(el, { animator: animate, duration: 0.55, easing: [0.16, 1, 0.3, 1] })
if (!reducedMotion()) {
animate(
el,
{ filter: ["blur(12px)", "blur(0px)"], scale: [0.96, 1] },
{ duration: 0.6, easing: [0.16, 1, 0.3, 1] },
)
}
})
e.detail.removed.forEach((v) => {
const el = findAccordionContent(root, v)
if (!el) return
animateHeightClose(el, { animator: animate, duration: 0.32, easing: [0.7, 0, 0.84, 0] })
if (!reducedMotion()) {
animate(
el,
{ filter: ["blur(0px)", "blur(10px)"], scale: [1, 0.97] },
{ duration: 0.3, easing: "ease-in" },
)
}
})
})API
These helpers target one accordion via its DOM id. Use them from LiveView (server), from HEEx bindings (client binding), or from plain JavaScript/TypeScript (client JS).
For value, focused, and item_state, you can pass respond_to: :server | :client | :both to control whether the response is pushed to LiveView, dispatched as a DOM event, or both.
Set value
Open/close panels programmatically.
Server
<.action phx-click="open_lorem" class="button button--sm">Open Lorem</.action>
<.action phx-click="open_lorem_and_donec" class="button button--sm">Lorem and Donec</.action>
<.action phx-click="close_all" class="button button--sm">Close all</.action>
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])
}
/>def handle_event("open_lorem", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", ["lorem"])}
end
def handle_event("open_lorem_and_donec", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", ["lorem", "donec"])}
end
def handle_event("close_all", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", [])}
endClient Binding
<.action phx-click={Corex.Accordion.set_value("my-accordion", ["lorem"])} class="button button--sm">
Open Lorem
</.action>
<.action
phx-click={Corex.Accordion.set_value("my-accordion", ["lorem", "donec"])}
class="button button--sm"
>
Lorem and Donec
</.action>
<.action phx-click={Corex.Accordion.set_value("my-accordion", [])} class="button button--sm">
Close all
</.action>
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])
}
/>Client JS
const el = document.getElementById("my-accordion");
const set = (value) =>
el?.dispatchEvent(
new CustomEvent("corex:accordion:set-value", {
bubbles: false,
detail: { value },
})
);
set(["lorem"]);
set(["lorem", "donec"]);
set([]);const el = document.getElementById("my-accordion");
const set = (value: string[]) =>
el?.dispatchEvent(
new CustomEvent("corex:accordion:set-value", {
bubbles: false,
detail: { value },
})
);
set(["lorem"]);
set(["lorem", "donec"]);
set([]);Read value
Ask the browser what is currently open.
Server
def handle_event("read_value", _params, socket) do
{:noreply, Corex.Accordion.value(socket, "my-accordion")}
end
def handle_event("accordion_value_response", %{"id" => "my-accordion", "value" => value}, socket) do
{:noreply, assign(socket, :accordion_value, value)}
endClient Binding
<.action phx-click={Corex.Accordion.value("my-accordion")} class="button button--sm">
Value
</.action>Client JS
const el = document.getElementById("my-accordion");
el?.dispatchEvent(new CustomEvent("corex:accordion:value", { bubbles: false, detail: {} }));
el?.addEventListener("accordion-value", (e) => {
console.log(e.detail.id, e.detail.value);
});const el = document.getElementById("my-accordion");
type Detail = { id: string; value: string[] | null };
el?.dispatchEvent(new CustomEvent("corex:accordion:value", { bubbles: false, detail: {} }));
el?.addEventListener("accordion-value", (event: Event) => {
const detail = (event as CustomEvent<Detail>).detail;
console.log(detail.id, detail.value);
});Read focused
Ask the browser which item is currently focused.
Server
def handle_event("read_focused", _params, socket) do
{:noreply, Corex.Accordion.focused(socket, "my-accordion")}
end
def handle_event("accordion_focused_response", %{"id" => "my-accordion", "value" => value}, socket) do
{:noreply, assign(socket, :accordion_focused, value)}
endClient Binding
<.action phx-click={Corex.Accordion.focused("my-accordion")} class="button button--sm">
Focused
</.action>Client JS
const el = document.getElementById("my-accordion");
el?.dispatchEvent(new CustomEvent("corex:accordion:focused", { bubbles: false, detail: {} }));
el?.addEventListener("accordion-focused", (e) => {
console.log(e.detail.id, e.detail.value);
});const el = document.getElementById("my-accordion");
type Detail = { id: string; value: string | null };
el?.dispatchEvent(new CustomEvent("corex:accordion:focused", { bubbles: false, detail: {} }));
el?.addEventListener("accordion-focused", (event: Event) => {
const detail = (event as CustomEvent<Detail>).detail;
console.log(detail.id, detail.value);
});Read item state
Ask the browser for one item's state.
Server
def handle_event("read_item_state", _params, socket) do
{:noreply, Corex.Accordion.item_state(socket, "my-accordion", "lorem", disabled: false)}
end
def handle_event(
"accordion_item_state_response",
%{"id" => "my-accordion", "value" => value, "state" => state},
socket
) do
{:noreply, assign(socket, :accordion_item_state, {value, state})}
endClient Binding
<.action
phx-click={Corex.Accordion.item_state("my-accordion", "lorem", disabled: false)}
class="button button--sm"
>
item_state("lorem")
</.action>
<.action
phx-click={Corex.Accordion.item_state("my-accordion", "donec", disabled: true)}
class="button button--sm"
>
item_state("donec", disabled)
</.action>Client JS
const el = document.getElementById("my-accordion");
el?.addEventListener("accordion-item-state", (e) => console.log(e.detail));
el?.dispatchEvent(
new CustomEvent("corex:accordion:item-state", {
bubbles: false,
detail: { value: "lorem", disabled: false, respond_to: "client" },
})
);const el = document.getElementById("my-accordion");
type State = { expanded: boolean; focused: boolean; disabled: boolean };
type Detail = { id: string; value: string; state: State };
el?.addEventListener("accordion-item-state", (event: Event) => {
const detail = (event as CustomEvent<Detail>).detail;
console.log(detail.id, detail.value, detail.state);
});Events
Use on_* to receive value/focus changes either in LiveView (pushEvent) or in the browser (CustomEvent).
Server (LiveView)
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])
}
on_value_change="accordion_value_changed"
on_focus_change="accordion_focus_changed"
/>def handle_event("accordion_value_changed", %{"id" => id, "value" => value}, socket) do
{:noreply, assign(socket, :accordion_open, {id, value})}
end
def handle_event("accordion_focus_changed", %{"id" => id, "value" => value}, socket) do
{:noreply, assign(socket, :accordion_focused, {id, value})}
endClient (CustomEvent / DOM)
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])
}
on_value_change_client="accordion-value-changed"
on_focus_change_client="accordion-focus-changed"
/>const el = document.getElementById("my-accordion");
el?.addEventListener("accordion-value-changed", (event) => {
const d = event.detail;
console.log(d.id, d.value, "added:", d.added, "removed:", d.removed);
});
el?.addEventListener("accordion-focus-changed", (event) => {
const d = event.detail;
console.log(d.id, d.value);
});import type { AccordionChangedDetail } from "corex";
const el = document.getElementById("my-accordion");
type FocusDetail = { id: string; value: string | null };
el?.addEventListener("accordion-value-changed", (event: Event) => {
const d = (event as CustomEvent<AccordionChangedDetail>).detail;
console.log(d.id, d.value, "added:", d.added, "removed:", d.removed);
});
el?.addEventListener("accordion-focus-changed", (event: Event) => {
const d = (event as CustomEvent<FocusDetail>).detail;
console.log(d.id, d.value);
});Style
Zag exposes data-scope and data-part on each element:
[data-scope="accordion"][data-part="root"] {}
[data-scope="accordion"][data-part="item"] {}
[data-scope="accordion"][data-part="item-trigger"] {}
[data-scope="accordion"][data-part="item-content"] {}
[data-scope="accordion"][data-part="item-indicator"] {}With Corex Design, import tokens and the accordion stylesheet, then add the accordion class and modifiers:
@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/accordion.css";<.accordion
class="accordion accordion--accent accordion--lg"
items={
Corex.Content.new([
%{trigger: "First", content: "First body."},
%{trigger: "Second", content: "Second body."}
])
}
/>
Summary
Components
Renders an accordion. See the module documentation for list-driven items, With slots, Custom slots, Manual and Compound modes, patterns, API, and events.
Renders a loading skeleton for the accordion component.
Compounds
Renders the content panel for an accordion item.
Renders the indicator for an accordion item.
Renders an accordion item. Use inside accordion compound mode with :let={ctx}.
Renders the root container for an accordion in compound mode.
Renders the trigger button for an accordion item.
API
Requests the accordion's focused item value from the browser. Returns a Phoenix.LiveView.JS command.
Requests the accordion's focused item value from the client. Pushes a LiveView event handled by the hook.
Zag getItemState for one item. Pass value and keyword opts (:disabled, :respond_to).
Sets the accordion value from client-side. Returns a Phoenix.LiveView.JS command.
Sets the accordion value from server-side. Pushes a LiveView event.
Requests the accordion's current open values from the browser. Returns a Phoenix.LiveView.JS command.
Requests the accordion's current open values from the client. Pushes a LiveView event handled by the hook.
Components
Renders an accordion. See the module documentation for list-driven items, With slots, Custom slots, Manual and Compound modes, patterns, API, and events.
Attributes
id(:string) - The id of the accordion, useful for API to identify the accordion.items(:list) - The items of the accordion, must be a list of %Corex.Content.Item{} structs. Defaults to[].value(:any) - The initial value or controlled value. Accepts a string or list of strings. Defaults to[].compound(:boolean) - Enable compound mode. Use with :let={ctx} and sub-components to fully control structure. Defaults tofalse.controlled(:boolean) - Whether the accordion is controlled. Only in LiveView, the on_value_change event is required. Defaults tofalse.collapsible(:boolean) - Whether the accordion is collapsible. Defaults totrue.multiple(:boolean) - Whether the accordion allows multiple items to be selected. Defaults totrue.animation(:string) - Animation mode for content open/close.instant— no animation, content opens/closes instantly via nativehiddenattributejs— built-in animation via Web Animations API (opacity + height); tune withanimation_options(Corex.Animation.Height)custom— removeshidden, full JS control viaon_value_change_client
Defaults to
"js". Must be one of"instant","js", or"custom".animation_options(Corex.Animation.Height) - Wired to the host whenanimationisjsonly. Custom transitions ignore this assign. SeeCorex.Animation.Height(opacity, height,block_interaction). Defaults to%Corex.Animation.Height{duration: 0.3, easing: "ease", opacity_start: 0.0, opacity_end: 1.0, block_interaction: false}.orientation(:string) - The orientation of the accordion. Defaults to"vertical". Must be one of"horizontal", or"vertical".dir(:string) - The direction of the accordion. When nil, derived from document (html lang + config :rtl_locales). Defaults to"ltr". Must be one of"ltr", or"rtl".on_value_change(:string) - LiveView event name forpushEventwhen the open value(s) change. Params:%{"id" => dom_id, "value" => list}. See Events in the module doc. Defaults tonil.on_value_change_client(:string) - BrowserCustomEventtype when the open value(s) change.event.detail:%{id: dom_id, value: list, previousValue: list, added: list, removed: list}(TS:AccordionChangedDetail). See Events in the module doc. Defaults tonil.on_focus_change(:string) - LiveView event name forpushEventwhen the focused item changes. Params:%{"id" => dom_id, "value" => focused_item_value}. See Events in the module doc. Defaults tonil.on_focus_change_client(:string) - BrowserCustomEventtype for focus changes.event.detail:%{id: dom_id, value: focused_value}. See Events in the module doc. Defaults tonil.Global attributes are accepted.
Slots
inner_block- Compound mode inner content. Use with thecompoundattribute and:let={ctx}.ctxis a map with keys:id,values,orientation,dir.indicator- Optional slot after each trigger. With:items, use:let={item}. Without:items(manual mode), use one slot per panel and a matchingvalueon:triggerand:content. Accepts attributes:value(:string)class(:string)
trigger- With:items, optional custom trigger; use:let={item}. Without:items(manual mode), one slot per panel withvalue(or defaultitem-0, …). Accepts attributes:value(:string)class(:string)disabled(:boolean)
content- With:items, optional custom content; use:let={item}. Without:items(manual mode), one slot per panel withvalue(or defaultitem-0, …). Accepts attributes:value(:string)class(:string)disabled(:boolean)
Renders a loading skeleton for the accordion component.
Attributes
count(:integer) - Defaults to3.- Global attributes are accepted.
Slots
trigger- Accepts attributes:class(:string)
indicator- Accepts attributes:class(:string)
content- Accepts attributes:class(:string)
Compounds
Renders the content panel for an accordion item.
Use inside accordion_item with :let={item}, passing the yielded item as the item attr.
Attributes
item(:map) (required) - The item struct yielded by accordion_item via :let={item}.animation(:string) - Override animation mode; defaults to the parent accordionanimationfrom compound ctx. Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Renders the indicator for an accordion item.
Use inside accordion_trigger inner block, passing the same item from accordion_item.
Attributes
item(:map) (required) - The item struct yielded by accordion_item via :let={item}.- Global attributes are accepted.
Slots
inner_block(required)
Renders an accordion item. Use inside accordion compound mode with :let={ctx}.
Yields the %Item{} struct via :let for use in child parts.
Attributes
ctx(:map) (required) - The context map yielded by the parent accordion via :let={ctx}.value(:string) (required) - The unique value identifying this item.disabled(:boolean) - Whether the item is disabled. Defaults tofalse.- Global attributes are accepted.
Slots
inner_block
Renders the root container for an accordion in compound mode.
Use inside accordion compound mode with :let={ctx}, wrapping all accordion_item components.
Attributes
ctx(:map) (required) - The context map yielded by the parent accordion via :let={ctx}.- Global attributes are accepted.
Slots
inner_block(required)
Renders the trigger button for an accordion item.
Use inside accordion_item with :let={item}, passing the yielded item as the item attr.
Place accordion_indicator inside this component's inner block if needed.
Attributes
item(:map) (required)- Global attributes are accepted.
Slots
inner_block(required)indicator
API
Requests the accordion's focused item value from the browser. Returns a Phoenix.LiveView.JS command.
Options: :respond_to — :server (default, accordion_focused_response only), :both (also dispatches
accordion-focused), or :client (accordion-focused DOM event only).
Examples
From Client Binding
<.action phx-click={Corex.Accordion.focused("my-accordion")} class="button button--sm">
Focused
</.action>
<.action phx-click={Corex.Accordion.focused("my-accordion", respond_to: :client)} class="button button--sm">
Focused (client only)
</.action>
<.accordion id="my-accordion" class="accordion" items={Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."}
])} />
```javascript
const el = document.getElementById("my-accordion");
el?.addEventListener("accordion-focused", (e) => console.log(e.detail));
```JS.dispatch
<.action
phx-click={JS.dispatch("corex:accordion:focused",
to: "#my-accordion",
detail: %{respond_to: "client"},
bubbles: false
)}
class="button button--sm"
>
Focused (JS.dispatch, client only)
</.action>
Requests the accordion's focused item value from the client. Pushes a LiveView event handled by the hook.
See focused/2 for :respond_to.
Examples
def handle_event("accordion_focused_response", %{"id" => id, "value" => value}, socket) do
{:noreply, assign(socket, :accordion_focused, {id, value})}
end
Zag getItemState for one item. Pass value and keyword opts (:disabled, :respond_to).
item_state/2—item_state(id, value); same asitem_state(id, value, []).item_state/3— client:item_state(id, value, opts). Server:item_state(socket, id, value)oritem_state(socket, id, value, opts).
The hook receives value and disabled, calls getItemState, pushes accordion_item_state_response
and/or dispatches accordion-item-state with detail: %{id, value, state} according to :respond_to.
Examples
From Client Binding
<.action phx-click={Corex.Accordion.item_state("my-accordion", "lorem", disabled: false)} class="button button--sm">
item_state("lorem")
</.action>
<.action phx-click={Corex.Accordion.item_state("my-accordion", "donec", disabled: true)} class="button button--sm">
item_state("donec", disabled)
</.action>
<.accordion id="my-accordion" class="accordion" items={Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])} />From Client JS
```javascript
const el = document.getElementById("my-accordion");
el?.addEventListener("accordion-item-state", (e) => console.log(e.detail));
```
<.action
phx-click={JS.dispatch("corex:accordion:item-state",
to: "#my-accordion",
detail: %{value: "lorem", disabled: false, respond_to: "client"},
bubbles: false
)}
class="button button--sm"
>
item_state("lorem") (JS.dispatch, client only)
</.action>From Server
def handle_event(
"accordion_item_state_response",
%{"id" => id, "value" => v, "state" => state},
socket
) do
{:noreply, assign(socket, :accordion_item_state, {id, v, state})}
end
Sets the accordion value from client-side. Returns a Phoenix.LiveView.JS command.
Pass a list of open item ids, or a comma-separated binary (trimmed); an empty binary closes all panels.
Examples
From Client Binding
<.action phx-click={Corex.Accordion.set_value("my-accordion", ["lorem"])} class="button button--sm">
Open Lorem
</.action>
<.action phx-click={Corex.Accordion.set_value("my-accordion", ["lorem", "donec"])} class="button button--sm">
Open Lorem and Donec
</.action>
<.action phx-click={Corex.Accordion.set_value("my-accordion", [])} class="button button--sm">
Close all
</.action>
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%{
value: "duis",
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
value: "donec",
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus.",
disabled: true
}
])
}
/>From Client JS
```javascript
const el = document.getElementById("my-accordion");
const set = (value) =>
el?.dispatchEvent(
new CustomEvent("corex:accordion:set-value", {
bubbles: false,
detail: { value },
})
);
set(["lorem"]);
set(["lorem", "donec"]);
set([]);
```
Sets the accordion value from server-side. Pushes a LiveView event.
Examples
Heex
<.action phx-click="open_lorem" class="button button--sm">
Open Lorem
</.action>
<.action phx-click="open_lorem_and_donec" class="button button--sm">
Open Lorem and Donec
</.action>
<.action phx-click="close_all" class="button button--sm">
Close all
</.action>
<.accordion
id="my-accordion"
class="accordion"
items={
Corex.Content.new([
%{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%{
value: "duis",
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%{
value: "donec",
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus.",
disabled: true
}
])
}
/>Elixir
def handle_event("open_lorem", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", ["lorem"])}
end
def handle_event("open_lorem_and_donec", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", ["lorem", "donec"])}
end
def handle_event("close_all", _params, socket) do
{:noreply, Corex.Accordion.set_value(socket, "my-accordion", [])}
end
Requests the accordion's current open values from the browser. Returns a Phoenix.LiveView.JS command.
Options:
:respond_to—:server(default, LiveViewaccordion_value_responseonly),:both(also dispatchesaccordion-value), or:client(DOMaccordion-valueonly). When:serverand the LiveView is not connected, nothing is pushed.
The hook pushes accordion_value_response when :respond_to is :both or :server, and dispatches
accordion-value on the element when :respond_to is :both or :client.
Examples
From Client Binding
<.action phx-click={Corex.Accordion.value("my-accordion")} class="button button--sm">
Value
</.action>
<.action phx-click={Corex.Accordion.value("my-accordion", respond_to: :client)} class="button button--sm">
Value (client only)
</.action>
<.accordion id="my-accordion" class="accordion" items={Corex.Content.new([
%{value: "lorem", trigger: "Lorem ipsum dolor sit amet", content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."},
%{value: "duis", trigger: "Duis dictum gravida odio ac pharetra?", content: "Nullam eget vestibulum ligula, at interdum tellus."},
%{value: "donec", trigger: "Donec condimentum ex mi", content: "Congue molestie ipsum gravida a. Sed ac eros luctus."}
])} />
```javascript
const el = document.getElementById("my-accordion");
el?.addEventListener("accordion-value", (e) => console.log(e.detail));
```JS.dispatch
<.action
phx-click={JS.dispatch("corex:accordion:value",
to: "#my-accordion",
detail: %{respond_to: "client"},
bubbles: false
)}
class="button button--sm"
>
Value (JS.dispatch, client only)
</.action>
Requests the accordion's current open values from the client. Pushes a LiveView event handled by the hook.
See value/2 for :respond_to. The hook pushes accordion_value_response and/or dispatches accordion-value
accordingly.
Examples
def handle_event("accordion_value_response", %{"id" => id, "value" => value}, socket) do
{:noreply, assign(socket, :accordion_value, {id, value})}
end