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 to target elements: ```css [data-scope="carousel"][data-part="root"] {} [data-scope="carousel"][data-part="control"] {} [data-scope="carousel"][data-part="item-group"] {} [data-scope="carousel"][data-part="item"] {} [data-scope="carousel"][data-part="prev-trigger"] {} [data-scope="carousel"][data-part="next-trigger"] {} [data-scope="carousel"][data-part="indicator-group"] {} [data-scope="carousel"][data-part="indicator"] {} ``` If you wish to use the default Corex styling, you can use the class `carousel` on the component. This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; @import "../corex/tokens/themes/neo/light.css"; @import "../corex/components/carousel.css"; ``` You can then use modifiers ```heex <.carousel class="carousel carousel--accent carousel--lg" items={[]}> ``` Learn more about modifiers and [Corex Design](https://corex-ui.com/components/carousel#modifiers) ''' @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"""