@codesinger0/shared-components 1.1.24 → 1.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,276 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import useEmblaCarousel from 'embla-carousel-react';
3
+ import Autoplay from 'embla-carousel-autoplay';
4
+
5
+ const FullscreenCarousel = ({
6
+ slides = [],
7
+ desktopHeight = '100vh',
8
+ mobileHeight = '100vh',
9
+ autoplay = true,
10
+ scrollInterval = 5000,
11
+ loop = true,
12
+ showDots = true,
13
+ showArrows = true,
14
+ className = '',
15
+ ...props
16
+ }) => {
17
+ const [prevBtnEnabled, setPrevBtnEnabled] = useState(false);
18
+ const [nextBtnEnabled, setNextBtnEnabled] = useState(false);
19
+ const [selectedIndex, setSelectedIndex] = useState(0);
20
+ const [scrollSnaps, setScrollSnaps] = useState([]);
21
+
22
+ // Embla carousel setup
23
+ const autoplayOptions = {
24
+ delay: scrollInterval,
25
+ stopOnInteraction: false,
26
+ stopOnMouseEnter: false,
27
+ rootNode: (emblaRoot) => emblaRoot.parentElement,
28
+ };
29
+
30
+ const plugins = autoplay ? [Autoplay(autoplayOptions)] : [];
31
+
32
+ const [emblaRef, emblaApi] = useEmblaCarousel(
33
+ {
34
+ loop: loop,
35
+ direction: 'rtl',
36
+ skipSnaps: false,
37
+ },
38
+ plugins
39
+ );
40
+
41
+ const scrollPrev = useCallback(() => emblaApi && emblaApi.scrollPrev(), [emblaApi]);
42
+ const scrollNext = useCallback(() => emblaApi && emblaApi.scrollNext(), [emblaApi]);
43
+ const scrollTo = useCallback((index) => emblaApi && emblaApi.scrollTo(index), [emblaApi]);
44
+
45
+ const onSelect = useCallback(() => {
46
+ if (!emblaApi) return;
47
+ setSelectedIndex(emblaApi.selectedScrollSnap());
48
+ setPrevBtnEnabled(emblaApi.canScrollPrev());
49
+ setNextBtnEnabled(emblaApi.canScrollNext());
50
+ }, [emblaApi]);
51
+
52
+ useEffect(() => {
53
+ if (!emblaApi) return;
54
+ onSelect();
55
+ setScrollSnaps(emblaApi.scrollSnapList());
56
+ emblaApi.on('select', onSelect);
57
+ emblaApi.on('reInit', onSelect);
58
+ }, [emblaApi, onSelect]);
59
+
60
+ if (!slides || slides.length === 0) {
61
+ return (
62
+ <div className="w-full flex items-center justify-center bg-gray-100" style={{ height: desktopHeight }}>
63
+ <p className="text-gray-500">No slides to display</p>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ return (
69
+ <div className={`fullscreen-carousel-wrapper ${className}`} {...props}>
70
+ <div className="fullscreen-carousel" dir="rtl">
71
+ <div className="fullscreen-carousel__viewport" ref={emblaRef}>
72
+ <div className="fullscreen-carousel__container">
73
+ {slides.map((slide, index) => (
74
+ <div key={index} className="fullscreen-carousel__slide">
75
+ {/* Desktop Layout - Horizontal Split */}
76
+ <div
77
+ className="hidden md:grid md:grid-cols-2 w-full h-full"
78
+ style={{ height: desktopHeight }}
79
+ >
80
+ <div className="w-full h-full">
81
+ {slide.leftContent}
82
+ </div>
83
+ <div className="w-full h-full">
84
+ {slide.rightContent}
85
+ </div>
86
+ </div>
87
+
88
+ {/* Mobile Layout - Vertical Split */}
89
+ <div
90
+ className="md:hidden grid grid-rows-2 w-full h-full"
91
+ style={{ height: mobileHeight }}
92
+ >
93
+ <div className="w-full h-full">
94
+ {slide.leftContent}
95
+ </div>
96
+ <div className="w-full h-full">
97
+ {slide.rightContent}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ ))}
102
+ </div>
103
+ </div>
104
+
105
+ {/* Dots indicator */}
106
+ {showDots && slides.length > 1 && (
107
+ <div className="fullscreen-carousel__dots">
108
+ {scrollSnaps.map((_, index) => (
109
+ <button
110
+ key={index}
111
+ className={`fullscreen-carousel__dot ${index === selectedIndex ? 'fullscreen-carousel__dot--selected' : ''}`}
112
+ onClick={() => scrollTo(index)}
113
+ aria-label={`Go to slide ${index + 1}`}
114
+ />
115
+ ))}
116
+ </div>
117
+ )}
118
+
119
+ {/* Navigation buttons */}
120
+ {showArrows && slides.length > 1 && (
121
+ <>
122
+ <button
123
+ className="fullscreen-carousel__button fullscreen-carousel__button--prev"
124
+ onClick={scrollPrev}
125
+ disabled={!prevBtnEnabled}
126
+ aria-label="Previous slide"
127
+ >
128
+ <svg className="fullscreen-carousel__button__svg" viewBox="0 0 24 24">
129
+ <path d="M9 18l6-6-6-6" />
130
+ </svg>
131
+ </button>
132
+ <button
133
+ className="fullscreen-carousel__button fullscreen-carousel__button--next"
134
+ onClick={scrollNext}
135
+ disabled={!nextBtnEnabled}
136
+ aria-label="Next slide"
137
+ >
138
+ <svg className="fullscreen-carousel__button__svg" viewBox="0 0 24 24">
139
+ <path d="M15 18l-6-6 6-6" />
140
+ </svg>
141
+ </button>
142
+ </>
143
+ )}
144
+ </div>
145
+
146
+ {/* Styles */}
147
+ <style jsx>{`
148
+ .fullscreen-carousel-wrapper {
149
+ position: relative;
150
+ width: 100%;
151
+ overflow: hidden;
152
+ }
153
+
154
+ .fullscreen-carousel {
155
+ position: relative;
156
+ width: 100%;
157
+ }
158
+
159
+ .fullscreen-carousel__viewport {
160
+ overflow: hidden;
161
+ width: 100%;
162
+ }
163
+
164
+ .fullscreen-carousel__container {
165
+ display: flex;
166
+ width: 100%;
167
+ }
168
+
169
+ .fullscreen-carousel__slide {
170
+ flex: 0 0 100%;
171
+ min-width: 0;
172
+ position: relative;
173
+ }
174
+
175
+ .fullscreen-carousel__button {
176
+ display: none;
177
+ background-color: rgba(255, 255, 255, 0.9);
178
+ color: #333;
179
+ border: none;
180
+ width: 3rem;
181
+ height: 3rem;
182
+ border-radius: 50%;
183
+ cursor: pointer;
184
+ align-items: center;
185
+ justify-content: center;
186
+ transition: all 0.2s ease;
187
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
188
+ position: absolute;
189
+ top: 50%;
190
+ transform: translateY(-50%);
191
+ z-index: 10;
192
+ }
193
+
194
+ @media (min-width: 768px) {
195
+ .fullscreen-carousel__button {
196
+ display: flex;
197
+ }
198
+ }
199
+
200
+ .fullscreen-carousel__button--prev {
201
+ right: 2rem;
202
+ }
203
+
204
+ .fullscreen-carousel__button--next {
205
+ left: 2rem;
206
+ }
207
+
208
+ .fullscreen-carousel__controls {
209
+ display: flex;
210
+ justify-content: center;
211
+ gap: 1rem;
212
+ position: absolute;
213
+ bottom: 4rem;
214
+ left: 50%;
215
+ transform: translateX(-50%);
216
+ z-index: 10;
217
+ }
218
+
219
+ .fullscreen-carousel__button:hover:not(:disabled) {
220
+ background-color: rgba(255, 255, 255, 1);
221
+ transform: scale(1.1);
222
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
223
+ }
224
+
225
+ .fullscreen-carousel__button:disabled {
226
+ opacity: 0.3;
227
+ cursor: not-allowed;
228
+ }
229
+
230
+ .fullscreen-carousel__button__svg {
231
+ width: 1rem;
232
+ height: 1rem;
233
+ fill: none;
234
+ stroke: currentColor;
235
+ stroke-width: 2;
236
+ stroke-linecap: round;
237
+ stroke-linejoin: round;
238
+ }
239
+
240
+ .fullscreen-carousel__dots {
241
+ display: flex;
242
+ justify-content: center;
243
+ gap: 0.5rem;
244
+ position: absolute;
245
+ bottom: 2rem;
246
+ left: 50%;
247
+ transform: translateX(-50%);
248
+ z-index: 10;
249
+ }
250
+
251
+ .fullscreen-carousel__dot {
252
+ background-color: rgba(255, 255, 255, 0.5);
253
+ border: none;
254
+ width: 0.75rem;
255
+ height: 0.75rem;
256
+ border-radius: 50%;
257
+ cursor: pointer;
258
+ opacity: 0.5;
259
+ transition: all 0.2s ease;
260
+ }
261
+
262
+ .fullscreen-carousel__dot--selected {
263
+ opacity: 1;
264
+ background-color: rgba(255, 255, 255, 1);
265
+ transform: scale(1.2);
266
+ }
267
+
268
+ .fullscreen-carousel__dot:hover {
269
+ opacity: 0.8;
270
+ }
271
+ `}</style>
272
+ </div>
273
+ );
274
+ };
275
+
276
+ export default FullscreenCarousel;
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ export { default as ProductsSidebar } from './components/products/ProductsSideba
20
20
  export { default as MyOrdersDisplay } from './components/MyOrdersDisplay'
21
21
  export { default as AccessibilityMenu } from './components/AccessibilityMenu'
22
22
  export { default as FloatingWhatsAppButton } from './components/FloatingWhatsAppButton'
23
+ export { default as FullscreenCarousel } from './components/FullscreenCarousel'
23
24
 
24
25
  // Modals
25
26
  export { default as ItemDetailsModal } from './components/modals/ItemDetailsModal'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesinger0/shared-components",
3
- "version": "1.1.24",
3
+ "version": "1.1.25",
4
4
  "description": "Shared React components for customer projects",
5
5
  "main": "dist/index.js",
6
6
  "files": [