defmodule Corex.Accordion do
@moduledoc ~S'''
Phoenix implementation of [Zag.js Accordion](https://zagjs.com/components/react/accordion).
## Examples
### List
You must use `Corex.Accordion.Item` struct for items.
The value for each item is optional, useful for controlled mode and API to identify the item.
You can specify disabled for each item.
```heex
<.accordion
class="accordion"
items={[
%Corex.Accordion.Item{
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique."
},
%Corex.Accordion.Item{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%Corex.Accordion.Item{
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus."
}
]}
/>
```
### List Custom
Similar to List but render a custom item slot that will be used for all items.
Use `{item.meta.trigger}` and `{item.meta.content}` to render the trigger and content for each item.
This example assumes the import of `.icon` from `Core Components`
```heex
<.accordion
class="accordion"
items={[
%Corex.Accordion.Item{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique.",
meta: %{
indicator: "hero-chevron-right",
}
},
%Corex.Accordion.Item{
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus.",
meta: %{
indicator: "hero-chevron-right",
}
},
%Corex.Accordion.Item{
value: "donec",
trigger: "Donec condimentum ex mi",
content: "Congue molestie ipsum gravida a. Sed ac eros luctus.",
disabled: true,
meta: %{
indicator: "hero-chevron-right",
}
}
]}
>
<:item :let={item}>
<.accordion_trigger item={item}>
{item.data.trigger}
<:indicator>
<.icon name={item.data.meta.indicator} />
<.accordion_content item={item}>
{item.data.content}
```
### Custom
Render a custom item slot per accordion item manually.
Use let={item} to get the item data and pass it to the `accordion_trigger/1` and `accordion_content/1` components.
The trigger component takes an optional `:indicator` slot to render the indicator ico
This example assumes the import of `.icon` from `Core Components`
```heex
<.accordion id="my-accordion" value={["duis"]} class="accordion">
<:item :let={item} value="lorem" disabled>
<.accordion_trigger item={item}>
Lorem ipsum dolor sit amet
<:indicator>
<.icon name="hero-chevron-right" />
<.accordion_content item={item}>
Consectetur adipiscing elit. Sed sodales ullamcorper tristique. Proin quis risus feugiat tellus iaculis fringilla.
<:item :let={item} value="duis">
<.accordion_trigger item={item}>
Duis dictum gravida odio ac pharetra?
<:indicator>
<.icon name="hero-chevron-right" />
<.accordion_content item={item}>
Nullam eget vestibulum ligula, at interdum tellus. Quisque feugiat, dui ut fermentum sodales, lectus metus dignissim ex.
```
### Controlled
Render an accordion controlled by the server.
You must use the `on_value_change` event to update the value on the server and pass the value as a list of strings.
The event will receive the value as a map with the key `value` and the id of the accordion.
```elixir
defmodule MyAppWeb.AccordionLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, :value, ["lorem"])}
end
def handle_event("on_value_change", %{"value" => value}, socket) do
{:noreply, assign(socket, :value, value)}
end
def render(assigns) do
~H"""
<.accordion value={@value} on_value_change="on_value_change" class="accordion">
<:item :let={item} value="lorem">
<.accordion_trigger item={item}>
Lorem ipsum dolor sit amet
<.accordion_content item={item}>
Consectetur adipiscing elit. Sed sodales ullamcorper tristique. Proin quis risus feugiat tellus iaculis fringilla.
<:item :let={item} value="duis">
<.accordion_trigger item={item}>
Duis dictum gravida odio ac pharetra?
<.accordion_content item={item}>
Nullam eget vestibulum ligula, at interdum tellus. Quisque feugiat, dui ut fermentum sodales, lectus metus dignissim ex.
"""
end
end
```
### Async
When the initial props are not available on mount, you can use the `Phoenix.LiveView.assign_async` function to assign the props asynchronously
You can use the optional `Corex.Accordion.accordion_skeleton/1` to render a loading or error state
```elixir
defmodule MyAppWeb.AccordionAsyncLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
socket =
socket
|> assign_async(:accordion, fn ->
Process.sleep(1000)
items = [
%Corex.Accordion.Item{
value: "lorem",
trigger: "Lorem ipsum dolor sit amet",
content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique.",
disabled: true
},
%Corex.Accordion.Item{
value: "duis",
trigger: "Duis dictum gravida odio ac pharetra?",
content: "Nullam eget vestibulum ligula, at interdum tellus."
},
%Corex.Accordion.Item{
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"""
Accordion
Async