@codesinger0/shared-components 1.1.106 → 1.1.108

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.
@@ -4,19 +4,26 @@ import Autoplay from 'embla-carousel-autoplay';
4
4
 
5
5
  const FullscreenCarousel = ({
6
6
  slides = [],
7
- height = '500px',
7
+ desktopHeight = '100vh',
8
+ mobileHeight = '100vh',
8
9
  autoplay = true,
9
10
  scrollInterval = 5000,
10
11
  loop = true,
11
12
  showDots = true,
12
13
  showArrows = true,
13
14
  dotsColor = 'rgba(255, 255, 255, 1)',
15
+ dotsPosition = { mobile: 'inside', desktop: 'inside' }, // 'inside' | 'below'
14
16
  className = '',
15
17
  ...props
16
18
  }) => {
19
+ // Normalize dotsPosition to always be an object
20
+ const normalizedDotsPosition = typeof dotsPosition === 'string'
21
+ ? { mobile: dotsPosition, desktop: dotsPosition }
22
+ : { mobile: dotsPosition?.mobile || 'inside', desktop: dotsPosition?.desktop || 'inside' };
17
23
  const [prevBtnEnabled, setPrevBtnEnabled] = useState(false);
18
24
  const [nextBtnEnabled, setNextBtnEnabled] = useState(false);
19
25
  const [selectedIndex, setSelectedIndex] = useState(0);
26
+ const [scrollSnaps, setScrollSnaps] = useState([]);
20
27
 
21
28
  // Embla carousel setup
22
29
  const autoplayOptions = {
@@ -51,13 +58,14 @@ const FullscreenCarousel = ({
51
58
  useEffect(() => {
52
59
  if (!emblaApi) return;
53
60
  onSelect();
61
+ setScrollSnaps(emblaApi.scrollSnapList());
54
62
  emblaApi.on('select', onSelect);
55
63
  emblaApi.on('reInit', onSelect);
56
64
  }, [emblaApi, onSelect]);
57
65
 
58
66
  if (!slides || slides.length === 0) {
59
67
  return (
60
- <div className="w-full flex items-center justify-center bg-gray-100" style={{ height }}>
68
+ <div className="w-full flex items-center justify-center bg-gray-100" style={{ height: desktopHeight }}>
61
69
  <p className="text-gray-500">No slides to display</p>
62
70
  </div>
63
71
  );
@@ -65,17 +73,79 @@ const FullscreenCarousel = ({
65
73
 
66
74
  return (
67
75
  <div className={`fullscreen-carousel-wrapper ${className}`} {...props}>
68
- <div className="fullscreen-carousel" style={{ height }} dir="rtl">
76
+ <div className="fullscreen-carousel" dir="rtl">
69
77
  <div className="fullscreen-carousel__viewport" ref={emblaRef}>
70
78
  <div className="fullscreen-carousel__container">
71
- {slides.map((slide, index) => (
72
- <div key={index} className="fullscreen-carousel__slide">
73
- {slide.content}
74
- </div>
75
- ))}
79
+ {slides.map((slide, index) => {
80
+ const isSingleSlide = !slide.leftContent && !slide.rightContent;
81
+
82
+ return (
83
+ <div key={index} className="fullscreen-carousel__slide">
84
+ {isSingleSlide ? (
85
+ /* Single Slide Mode */
86
+ <>
87
+ <div
88
+ className="hidden md:block w-full h-full"
89
+ style={{ height: desktopHeight }}
90
+ >
91
+ {slide.content}
92
+ </div>
93
+ <div
94
+ className="block md:hidden w-full h-full"
95
+ style={{ height: mobileHeight }}
96
+ >
97
+ {slide.content}
98
+ </div>
99
+ </>
100
+ ) : (
101
+ <>
102
+ {/* Desktop Layout - Horizontal Split */}
103
+ <div
104
+ className="hidden md:grid md:grid-cols-2 w-full h-full"
105
+ style={{ height: desktopHeight }}
106
+ >
107
+ <div className="w-full h-full">
108
+ {slide.leftContent}
109
+ </div>
110
+ <div className="w-full h-full">
111
+ {slide.rightContent}
112
+ </div>
113
+ </div>
114
+
115
+ {/* Mobile Layout - Vertical Split */}
116
+ <div
117
+ className="md:hidden grid grid-rows-2 w-full h-full"
118
+ style={{ height: mobileHeight }}
119
+ >
120
+ <div className="w-full h-full">
121
+ {slide.leftContent}
122
+ </div>
123
+ <div className="w-full h-full">
124
+ {slide.rightContent}
125
+ </div>
126
+ </div>
127
+ </>
128
+ )}
129
+ </div>
130
+ );
131
+ })}
76
132
  </div>
77
133
  </div>
78
134
 
135
+ {/* Dots indicator - inside content (absolute positioned) */}
136
+ {showDots && slides.length > 1 && (
137
+ <div className={`fullscreen-carousel__dots fullscreen-carousel__dots--mobile-${normalizedDotsPosition.mobile} fullscreen-carousel__dots--desktop-${normalizedDotsPosition.desktop}`}>
138
+ {scrollSnaps.map((_, index) => (
139
+ <button
140
+ key={index}
141
+ className={`fullscreen-carousel__dot ${index === selectedIndex ? 'fullscreen-carousel__dot--selected' : ''}`}
142
+ onClick={() => scrollTo(index)}
143
+ aria-label={`Go to slide ${index + 1}`}
144
+ />
145
+ ))}
146
+ </div>
147
+ )}
148
+
79
149
  {/* Navigation buttons */}
80
150
  {showArrows && slides.length > 1 && (
81
151
  <>
@@ -101,20 +171,6 @@ const FullscreenCarousel = ({
101
171
  </button>
102
172
  </>
103
173
  )}
104
-
105
- {/* Dots indicator */}
106
- {showDots && slides.length > 1 && (
107
- <div className="fullscreen-carousel__dots">
108
- {slides.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
174
  </div>
119
175
 
120
176
  {/* Styles */}
@@ -122,6 +178,7 @@ const FullscreenCarousel = ({
122
178
  .fullscreen-carousel-wrapper {
123
179
  position: relative;
124
180
  width: 100%;
181
+ overflow: hidden;
125
182
  }
126
183
 
127
184
  .fullscreen-carousel {
@@ -132,20 +189,17 @@ const FullscreenCarousel = ({
132
189
  .fullscreen-carousel__viewport {
133
190
  overflow: hidden;
134
191
  width: 100%;
135
- height: 100%;
136
192
  }
137
-
193
+
138
194
  .fullscreen-carousel__container {
139
195
  display: flex;
140
196
  width: 100%;
141
- height: 100%;
142
197
  }
143
198
 
144
199
  .fullscreen-carousel__slide {
145
200
  flex: 0 0 100%;
146
201
  min-width: 0;
147
202
  position: relative;
148
- height: 100%;
149
203
  }
150
204
 
151
205
  .fullscreen-carousel__button {
@@ -217,17 +271,33 @@ const FullscreenCarousel = ({
217
271
  display: flex;
218
272
  justify-content: center;
219
273
  gap: 0.5rem;
220
- padding: 1rem 0;
274
+ left: 50%;
275
+ transform: translateX(-50%);
221
276
  z-index: 10;
222
277
  }
223
278
 
279
+ /* Mobile: inside (default) */
280
+ .fullscreen-carousel__dots--mobile-inside {
281
+ position: absolute;
282
+ bottom: 2rem;
283
+ }
284
+
285
+ /* Mobile: below */
286
+ .fullscreen-carousel__dots--mobile-below {
287
+ position: relative;
288
+ padding: 1rem 0;
289
+ }
290
+
291
+ /* Desktop overrides */
224
292
  @media (min-width: 768px) {
225
- .fullscreen-carousel__dots {
293
+ .fullscreen-carousel__dots--desktop-inside {
226
294
  position: absolute;
227
295
  bottom: 2rem;
228
- left: 50%;
229
- transform: translateX(-50%);
230
- padding: 0;
296
+ }
297
+
298
+ .fullscreen-carousel__dots--desktop-below {
299
+ position: relative;
300
+ padding: 1rem 0;
231
301
  }
232
302
  }
233
303
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesinger0/shared-components",
3
- "version": "1.1.106",
3
+ "version": "1.1.108",
4
4
  "description": "Shared React components for customer projects",
5
5
  "main": "dist/index.js",
6
6
  "files": [