Skip to content

Commit 017b2f4

Browse files
Carousel: new component (#501)
* Add the native daisy carousel component * Adding links to the images * WIP --------- Co-authored-by: Robson Tenório <[email protected]>
1 parent 7e971d5 commit 017b2f4

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

src/MaryServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Mary\View\Components\Button;
1515
use Mary\View\Components\Calendar;
1616
use Mary\View\Components\Card;
17+
use Mary\View\Components\Carousel;
1718
use Mary\View\Components\Chart;
1819
use Mary\View\Components\Checkbox;
1920
use Mary\View\Components\Choices;
@@ -173,6 +174,7 @@ public function registerComponents()
173174
Blade::component($prefix . 'theme-toggle', ThemeToggle::class);
174175
Blade::component($prefix . 'toast', Toast::class);
175176
Blade::component($prefix . 'toggle', Toggle::class);
177+
Blade::component($prefix . 'carousel', Carousel::class);
176178
}
177179

178180
public function registerBladeDirectives(): void

src/View/Components/Carousel.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace Mary\View\Components;
4+
5+
use Closure;
6+
use Illuminate\View\Component;
7+
use Illuminate\View\View;
8+
9+
/**
10+
* Inspired by Penguin UI.
11+
* Thank you.
12+
*/
13+
class Carousel extends Component
14+
{
15+
public string $uuid;
16+
17+
public function __construct(
18+
public array $slides,
19+
public ?bool $withoutIndicators = false,
20+
public ?bool $withoutArrows = false,
21+
22+
// Slots
23+
public mixed $content = null,
24+
) {
25+
$this->uuid = "mary" . md5(serialize($this));
26+
}
27+
28+
public function render(): View|Closure|string
29+
{
30+
return <<<'HTML'
31+
<div x-data="{
32+
slides: @js($slides),
33+
withoutIndicators: {{ json_encode($withoutIndicators) }},
34+
currentSlideIndex: 1,
35+
touchStartX: null,
36+
touchEndX: null,
37+
swipeThreshold: 50,
38+
previous() {
39+
this.currentSlideIndex = (this.currentSlideIndex > 1)
40+
? --this.currentSlideIndex
41+
: this.slides.length
42+
},
43+
next() {
44+
this.currentSlideIndex = (this.currentSlideIndex < this.slides.length)
45+
? ++this.currentSlideIndex
46+
: 1
47+
},
48+
handleTouchStart(event) {
49+
this.touchStartX = event.touches[0].clientX
50+
},
51+
handleTouchMove(event) {
52+
this.touchEndX = event.touches[0].clientX
53+
},
54+
handleTouchEnd() {
55+
if(this.touchEndX){
56+
if (this.touchStartX - this.touchEndX > this.swipeThreshold) {
57+
this.next()
58+
}
59+
if (this.touchStartX - this.touchEndX < -this.swipeThreshold) {
60+
this.previous()
61+
}
62+
this.touchStartX = null
63+
this.touchEndX = null
64+
}
65+
},
66+
}" class="relative w-full overflow-hidden">
67+
68+
@if(!$withoutArrows)
69+
<!-- previous button -->
70+
<x-mary-button icon="o-chevron-left" @click="previous()" class="absolute cursor-pointer left-5 top-1/2 z-[2] btn-circle btn-sm" />
71+
<!-- next button -->
72+
<x-mary-button icon="o-chevron-right" @click="next()" class="absolute cursor-pointer right-5 top-1/2 z-[2] btn-circle btn-sm" />
73+
@endif
74+
75+
<!-- slides -->
76+
<div
77+
@touchstart="handleTouchStart($event)" @touchmove="handleTouchMove($event)" @touchend="handleTouchEnd()"
78+
{{ $attributes->class(["relative h-64 w-full rounded-box overflow-hidden"]) }}
79+
>
80+
<!-- Slot content -->
81+
@foreach($slides as $index => $slide)
82+
<div
83+
x-cloak
84+
x-show="currentSlideIndex == {{ $index + 1 }}"
85+
x-transition.opacity.duration.500ms
86+
@class(["absolute inset-0", "cursor-pointer" => data_get($slide, 'url') ])
87+
@if(data_get($slide, 'url'))
88+
@click="window.location = '{{ data_get($slide, 'url') }}'"
89+
@endif
90+
>
91+
<!-- Custom content -->
92+
@if($content)
93+
<div class="absolute inset-0 z-[1]">
94+
{{ $content($slide) }}
95+
</div>
96+
<!-- Default content -->
97+
@else
98+
<div
99+
@class([
100+
"absolute inset-0 z-[1] flex flex-col items-center justify-end gap-2 px-20 py-12 text-center",
101+
"bg-gradient-to-t from-slate-900/85" => data_get($slide, 'urlText') || data_get($slide, 'title') || data_get($slide, 'description')
102+
])
103+
>
104+
<!-- Title -->
105+
<h3 class="w-full text-2xl lg:text-3xl font-bold text-white">{{ data_get($slide, 'title') }}</h3>
106+
107+
<!-- Description -->
108+
<div class="w-full text-sm text-white mb-5">{{ data_get($slide, 'description') }}</div>
109+
110+
<!-- Button-->
111+
@if(data_get($slide, 'urlText'))
112+
<a href="{{ data_get($slide, 'url') }}" class="btn btn-sm btn-ghost my-3 border-white text-white hover:border-white hover:scale-105">{{ data_get($slide, 'urlText') }}</a>
113+
@endif
114+
</div>
115+
@endif
116+
117+
<!-- Image -->
118+
<img class="w-full h-full inset-0 object-cover" src="{{ data_get($slide, 'image') }}" />
119+
</div>
120+
@endforeach
121+
</div>
122+
<!-- indicators -->
123+
@if(! $withoutIndicators)
124+
<div class="absolute rounded-xl bottom-3 md:bottom-5 left-1/2 z-[2] flex -translate-x-1/2 gap-4 md:gap-3 bg-white/75 px-1.5 py-1 md:px-2 dark:bg-slate-900/75" role="group" aria-label="slides" >
125+
<template x-for="(slide, index) in slides">
126+
<button class="size-2.5 cursor-pointer rounded-full transition hover:scale-125 bg-slate-700 dark:bg-slate-300" @click="currentSlideIndex = index + 1" :class="[currentSlideIndex === index + 1 ? 'bg-slate-700 dark:bg-slate-300' : 'bg-slate-700/50 dark:bg-slate-300/50']"></button>
127+
</template>
128+
</div>
129+
@endif
130+
</div>
131+
HTML;
132+
}
133+
}

0 commit comments

Comments
 (0)