defmodule Corex.Carousel do @moduledoc ~S''' Phoenix implementation of [Zag.js Carousel](https://zagjs.com/components/react/carousel). ## Examples ### Basic with image URLs ```heex <.carousel id="car" items={["/images/beach.jpg", "/images/fall.jpg", "/images/sand.jpg"]} class="carousel"> <:prev_trigger> <:next_trigger> ``` Items can be URLs (strings) or maps with `:url` and optional `:alt` keys. ## Styling Use data attributes: `[data-scope="carousel"][data-part="root"]`, `control`, `item-group`, `item`, `prev-trigger`, `next-trigger`, `indicator-group`, `indicator`. ''' @doc type: :component use Phoenix.Component alias Corex.Carousel.Anatomy.{ Props, Root, Control, ItemGroup, Item, PrevTrigger, NextTrigger, IndicatorGroup, Indicator } alias Corex.Carousel.Connect attr(:id, :string, required: false) attr(:items, :list, required: true, doc: "List of image URLs (strings) or maps with :url and optional :alt" ) attr(:page, :integer, default: 0) attr(:controlled, :boolean, default: false) attr(:dir, :string, default: nil, values: [nil, "ltr", "rtl"]) attr(:orientation, :string, default: "horizontal", values: ["horizontal", "vertical"]) attr(:slides_per_page, :integer, default: 1) attr(:loop, :boolean, default: false) attr(:allow_mouse_drag, :boolean, default: false) attr(:spacing, :string, default: "0px") attr(:on_page_change, :string, default: nil) attr(:on_page_change_client, :string, default: nil) attr(:rest, :global) slot(:prev_trigger, required: true) slot(:next_trigger, required: true) def carousel(assigns) do items = List.wrap(assigns.items) slide_count = length(items) slides_per_page = assigns.slides_per_page || 1 per_move = slides_per_page total_pages = 0..max(0, slide_count - 1) |> Enum.take_every(max(1, per_move)) |> Enum.count(fn i -> i + slides_per_page <= slide_count end) page = assigns.page || 0 loop = assigns.loop || false prev_disabled = !loop and page <= 0 next_disabled = !loop and (total_pages == 0 or page >= total_pages - 1) assigns = assigns |> assign_new(:id, fn -> "carousel-#{System.unique_integer([:positive])}" end) |> assign_new(:dir, fn -> "ltr" end) |> assign(:items, items) |> assign(:slide_count, slide_count) |> assign(:prev_disabled, prev_disabled) |> assign(:next_disabled, next_disabled) ~H"""