Skip to content

Commit 66d4a9c

Browse files
committed
carousel wip
1 parent 04c8352 commit 66d4a9c

File tree

4 files changed

+343
-11
lines changed

4 files changed

+343
-11
lines changed

assets/default.css

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@
291291
}
292292

293293
.pc-icon-button__tooltip__text {
294-
@apply relative z-10 p-2 text-xs leading-none text-white bg-gray-900 rounded-xs shadow-lg whitespace-nowrap dark:bg-gray-700;
294+
@apply relative z-10 p-2 text-xs leading-none text-white bg-gray-900 shadow-lg rounded-xs whitespace-nowrap dark:bg-gray-700;
295295
}
296296

297297
.pc-icon-button__tooltip__arrow {
@@ -857,7 +857,7 @@
857857
@apply dark:text-white;
858858
}
859859
.pc-switch {
860-
@apply relative inline-flex items-center justify-center shrink-0 cursor-pointer;
860+
@apply relative inline-flex items-center justify-center cursor-pointer shrink-0;
861861
}
862862
.pc-switch--xs {
863863
@apply w-6 h-3;
@@ -1285,7 +1285,7 @@
12851285
@apply object-cover rounded-full;
12861286
}
12871287
.pc-avatar--with-placeholder-icon {
1288-
@apply relative inline-block overflow-hidden bg-gray-100 text-gray-300 rounded-full dark:bg-gray-700;
1288+
@apply relative inline-block overflow-hidden text-gray-300 bg-gray-100 rounded-full dark:bg-gray-700;
12891289
}
12901290
.pc-avatar--with-placeholder-initials {
12911291
@apply flex items-center justify-center font-semibold text-gray-500 uppercase bg-gray-100 rounded-full dark:bg-gray-700 dark:text-gray-300;
@@ -1562,10 +1562,10 @@
15621562
/* Cards - with media */
15631563

15641564
.pc-card__image {
1565-
@apply shrink-0 object-cover w-full;
1565+
@apply object-cover w-full shrink-0;
15661566
}
15671567
.pc-card__image-placeholder {
1568-
@apply shrink-0 w-full bg-gray-300 dark:bg-gray-700;
1568+
@apply w-full bg-gray-300 shrink-0 dark:bg-gray-700;
15691569
}
15701570

15711571
/* Cards - footer */
@@ -1577,7 +1577,7 @@
15771577
/* Table */
15781578

15791579
.pc-table--basic {
1580-
@apply min-w-full overflow-hidden rounded-xs shadow-sm table-auto ring-1 ring-gray-200 dark:ring-gray-800 sm:rounded;
1580+
@apply min-w-full overflow-hidden shadow-sm table-auto rounded-xs ring-1 ring-gray-200 dark:ring-gray-800 sm:rounded;
15811581
}
15821582
.pc-table--ghost {
15831583
@apply min-w-full overflow-hidden table-auto;
@@ -1631,7 +1631,7 @@
16311631
@apply text-base font-semibold;
16321632
}
16331633
.pc-accordion-item__chevron {
1634-
@apply shrink-0 w-6 h-6 ml-3 text-gray-400 duration-300 fill-current dark:group-hover:text-gray-300 group-hover:text-gray-500;
1634+
@apply w-6 h-6 ml-3 text-gray-400 duration-300 fill-current shrink-0 dark:group-hover:text-gray-300 group-hover:text-gray-500;
16351635
}
16361636
.pc-accordion-item__content-container {
16371637
@apply p-5 bg-white border border-gray-200 dark:border-gray-700 dark:bg-gray-900;
@@ -2302,7 +2302,7 @@
23022302

23032303
/* Indicator */
23042304
.pc-stepper__indicator {
2305-
@apply grid shrink-0 text-white transition-all duration-200 bg-gray-500 rounded-full place-items-center;
2305+
@apply grid text-white transition-all duration-200 bg-gray-500 rounded-full shrink-0 place-items-center;
23062306
}
23072307

23082308
.pc-stepper__node--complete .pc-stepper__indicator {
@@ -2462,7 +2462,7 @@
24622462
}
24632463

24642464
.pc-vertical-menu-item__icon {
2465-
@apply shrink-0 w-5 h-5;
2465+
@apply w-5 h-5 shrink-0;
24662466
}
24672467

24682468
.pc-vertical-menu-item {
@@ -2511,11 +2511,11 @@
25112511
}
25122512

25132513
.pc-vertical-menu-item__icon--active {
2514-
@apply shrink-0 w-5 h-5;
2514+
@apply w-5 h-5 shrink-0;
25152515
}
25162516

25172517
.pc-vertical-menu-item__icon--inactive {
2518-
@apply shrink-0 w-5 h-5;
2518+
@apply w-5 h-5 shrink-0;
25192519
}
25202520

25212521
/* Other */

lib/petal_components.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule PetalComponents do
1010
Button,
1111
ButtonGroup,
1212
Card,
13+
Carousel,
1314
Container,
1415
Dropdown,
1516
Field,

lib/petal_components/carousel.ex

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
defmodule PetalComponents.Carousel do
2+
@moduledoc """
3+
Provides a versatile and customizable carousel component.
4+
5+
This component enables the creation of carousels with various features such as
6+
slide indicators, navigation controls, and dynamic slide content.
7+
8+
## Features
9+
10+
- **Slides**: Define multiple slides, each with custom content.
11+
- **Navigation Controls**: Include previous and next buttons to manually navigate through the slides.
12+
- **Indicators**: Optional indicators show the current slide and allow direct navigation to any slide.
13+
- **Transition Types**: Choose between fade and slide transitions.
14+
- **Responsive Design**: Supports various sizes and padding options to adapt to different screen sizes.
15+
"""
16+
17+
use Phoenix.Component
18+
import PetalComponents.Icon, only: [icon: 1]
19+
import Phoenix.LiveView.Utils, only: [random_id: 0]
20+
21+
@doc """
22+
The `carousel` component is used to create interactive carousels with customizable attributes
23+
such as `size`, `padding`, and `transition_type`. It supports adding multiple slides with different content,
24+
and includes options for navigation controls and indicators.
25+
26+
## Examples
27+
28+
```elixir
29+
<.carousel id="carousel-test-one" transition_type="fade" indicator={true}>
30+
<:slide title="Slide 1" />
31+
<:slide title="Slide 2" />
32+
<:slide title="Slide 3" />
33+
</.carousel>
34+
```
35+
"""
36+
@doc type: :component
37+
attr :id, :string, doc: "A unique identifier is used to manage state and interaction"
38+
attr :class, :string, default: nil, doc: "Custom CSS class for additional styling"
39+
attr :size, :string, default: "large", doc: "Determines the overall size of the elements"
40+
attr :padding, :string, default: "medium", doc: "Determines padding for items"
41+
attr :text_position, :string, default: "center", doc: "Determines the element's text position"
42+
attr :rest, :global, doc: "Global attributes can define defaults"
43+
attr :indicator, :boolean, default: false, doc: "Specifies whether to show element indicators"
44+
attr :control, :boolean, default: true, doc: "Determines whether to show navigation controls"
45+
attr :active_index, :integer, default: 0, doc: "Index of the active slide (starts at 0)"
46+
attr :autoplay, :boolean, default: false, doc: "Enable or disable autoplay functionality"
47+
attr :autoplay_interval, :integer, default: 5000, doc: "Time between slides in ms"
48+
49+
attr :transition_type, :string,
50+
default: "fade",
51+
doc: "Type of transition between slides (fade or slide)"
52+
53+
attr :transition_duration, :integer, default: 500, doc: "Duration of transition in milliseconds"
54+
55+
slot :slide, required: true do
56+
attr :title, :string, doc: "Title of the slide"
57+
attr :description, :string, doc: "Description of the slide"
58+
attr :class, :string, doc: "Custom CSS class for additional styling"
59+
end
60+
61+
def carousel(assigns) do
62+
assigns =
63+
assigns
64+
|> assign_new(:id, fn -> "carousel-#{random_id()}" end)
65+
|> assign_new(:transition_class, fn -> transition_class(assigns.transition_type) end)
66+
67+
~H"""
68+
<div
69+
id={@id}
70+
phx-hook="CarouselHook"
71+
phx-update="ignore"
72+
data-active-index={@active_index}
73+
data-autoplay={to_string(@autoplay)}
74+
data-autoplay-interval={@autoplay_interval}
75+
data-transition-type={@transition_type}
76+
data-transition-duration={@transition_duration}
77+
class={[
78+
"pc-carousel",
79+
@transition_class,
80+
size_class(@size),
81+
padding_class(@padding),
82+
text_position_class(@text_position),
83+
@class
84+
]}
85+
>
86+
<button
87+
:if={@control}
88+
id={"#{@id}-carousel-prev"}
89+
class="pc-carousel__button pc-carousel__button--prev"
90+
aria-label="Previous slide"
91+
>
92+
<.icon name="hero-chevron-left-solid" class="pc-carousel__icon" />
93+
</button>
94+
95+
<button
96+
:if={@control}
97+
id={"#{@id}-carousel-next"}
98+
class="pc-carousel__button pc-carousel__button--next"
99+
aria-label="Next slide"
100+
>
101+
<.icon name="hero-chevron-right-solid" class="pc-carousel__icon" />
102+
</button>
103+
104+
<div class="pc-carousel__slides">
105+
<div
106+
:for={{slide, index} <- Enum.with_index(@slide)}
107+
id={"#{@id}-carousel-slide-#{index}"}
108+
class={[
109+
"pc-carousel__slide",
110+
if(index == @active_index,
111+
do: "pc-carousel__slide--active",
112+
else: "pc-carousel__slide--inactive"
113+
),
114+
slide[:class]
115+
]}
116+
style={
117+
if @transition_type == "fade" do
118+
if index == @active_index,
119+
do: "opacity: 1; z-index: 10;",
120+
else: "opacity: 0; z-index: 0;"
121+
else
122+
if index == @active_index,
123+
do: "opacity: 1; z-index: 10; transform: translateX(0);",
124+
else: "opacity: 0; z-index: 0; transform: translateX(100%);"
125+
end
126+
}
127+
>
128+
<div class="pc-carousel__slide-content">
129+
<div class="pc-carousel__content">
130+
<div class="pc-carousel__content-wrapper">
131+
<div class="pc-carousel__title">
132+
{slide[:title] || "Slide #{index + 1}"}
133+
</div>
134+
<p :if={!is_nil(slide[:description])} class="pc-carousel__description">
135+
{slide[:description]}
136+
</p>
137+
</div>
138+
</div>
139+
</div>
140+
</div>
141+
</div>
142+
143+
<.slide_indicators :if={@indicator} id={@id} count={length(@slide)} />
144+
</div>
145+
"""
146+
end
147+
148+
defp slide_indicators(assigns) do
149+
~H"""
150+
<div id={"#{@id}-carousel-slide-indicator"} class="pc-carousel__indicators">
151+
<button
152+
:for={indicator_item <- 1..@count}
153+
id={"#{@id}-carousel-indicator-#{indicator_item}"}
154+
data-indicator-index={"#{indicator_item - 1}"}
155+
class="pc-carousel__indicator"
156+
aria-label={"Slide #{indicator_item}"}
157+
/>
158+
</div>
159+
"""
160+
end
161+
162+
defp transition_class("fade") do
163+
"[&_.pc-carousel__slide]:transition-opacity [&_.pc-carousel__slide]:duration-500 [&_.pc-carousel__slide]:ease-in-out"
164+
end
165+
166+
defp transition_class("slide") do
167+
"[&_.pc-carousel__slide]:transition-transform [&_.pc-carousel__slide]:duration-500 [&_.pc-carousel__slide]:ease-in-out"
168+
end
169+
170+
defp transition_class(_), do: ""
171+
172+
defp size_class("small"), do: "text-sm [&_.description-wrapper]:max-w-96"
173+
defp size_class("medium"), do: "text-base [&_.description-wrapper]:max-w-xl"
174+
defp size_class("large"), do: "text-lg [&_.description-wrapper]:max-w-2xl"
175+
defp size_class(_), do: ""
176+
177+
defp padding_class("small"), do: "[&_.description-wrapper]:p-3"
178+
defp padding_class("medium"), do: "[&_.description-wrapper]:p-4"
179+
defp padding_class("large"), do: "[&_.description-wrapper]:p-5"
180+
defp padding_class(_), do: ""
181+
182+
defp text_position_class("start"), do: "[&_.description-wrapper]:text-start"
183+
defp text_position_class("center"), do: "[&_.description-wrapper]:text-center"
184+
defp text_position_class("end"), do: "[&_.description-wrapper]:text-end"
185+
defp text_position_class(_), do: ""
186+
end

0 commit comments

Comments
 (0)