defmodule RiverConnectWeb.Components.UI.Collapsible do @moduledoc """ Implementation of Collapsible component for SaladUI framework. This component allows content to be shown or hidden with smooth animations, accessibility support, and keyboard navigation. ## Examples: <.collapsible id="collapsible-1" open> <.collapsible_trigger> <.button variant="outline">Show content <.collapsible_content>

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

""" use RiverConnectWeb.Components.UI, :component @doc """ The main collapsible component. ## Options * `:id` - Required unique identifier for the collapsible. * `:open` - Whether the collapsible is initially open. Defaults to `false`. * `:on-open` - Handler for collapsible open event. * `:on-close` - Handler for collapsible close event. * `:class` - Additional CSS classes. """ attr :id, :string, required: true, doc: "Unique identifier for the collapsible" attr :open, :boolean, default: false, doc: "Whether the collapsible is initially open" attr :"on-open", :any, default: nil, doc: "Handler for collapsible open event" attr :"on-close", :any, default: nil, doc: "Handler for collapsible close event" attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def collapsible(assigns) do # Collect event mappings event_map = %{} |> add_event_mapping(assigns, "opened", :"on-open") |> add_event_mapping(assigns, "closed", :"on-close") assigns = assigns |> assign(:event_map, Jason.encode!(event_map)) |> assign(:initial_state, if(assigns.open, do: "open", else: "closed")) |> assign( :options, Jason.encode!(%{ open: assigns.open, animations: get_animation_config() }) ) ~H"""
{render_slot(@inner_block)}
""" end @doc """ The trigger element that toggles the collapsible content. """ attr :class, :string, default: nil attr :as, :any, default: "div" attr :rest, :global slot :inner_block, required: true def collapsible_trigger(assigns) do ~H""" <.dynamic data-part="trigger" data-action="toggle" tag={@as} class={classes(["cursor-pointer", @class])} {@rest} > {render_slot(@inner_block)} """ end @doc """ The collapsible content that appears when triggered. """ attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def collapsible_content(assigns) do ~H""" """ end defp get_animation_config do %{ "closed_to_open" => %{ animation: ["ease-out duration-200", "opacity-0", "opacity-100"], duration: 200, target_part: "content" }, "open_to_closed" => %{ animation: ["ease-out duration-200", "opacity-100", "opacity-70"], duration: 200, target_part: "content" } } end end