@@ -18,6 +18,13 @@ class ImageCarousel {
1818 this . slides = Array . from (
1919 this . container . querySelectorAll ( '.carousel-slide' )
2020 )
21+
22+ // Don't initialize if no slides
23+ if ( this . slides . length === 0 ) {
24+ console . warn ( 'No carousel slides found' )
25+ return
26+ }
27+
2128 this . indicators = Array . from (
2229 this . container . querySelectorAll ( '.carousel-indicator' )
2330 )
@@ -26,6 +33,7 @@ class ImageCarousel {
2633
2734 this . initializeSlides ( )
2835 this . setupEventListeners ( )
36+ this . positionControls ( )
2937 }
3038
3139 private initializeSlides ( ) : void {
@@ -60,6 +68,23 @@ class ImageCarousel {
6068 indicator . addEventListener ( 'click' , ( ) => this . goToSlide ( index ) )
6169 } )
6270
71+ // Handle image clicks for modal
72+ this . slides . forEach ( ( slide ) => {
73+ const imageLink = slide . querySelector ( '.carousel-image-reference' )
74+ if ( imageLink ) {
75+ imageLink . addEventListener ( 'click' , ( e ) => {
76+ e . preventDefault ( )
77+ const modalId = imageLink . getAttribute ( 'data-modal-id' )
78+ if ( modalId ) {
79+ const modal = document . getElementById ( modalId )
80+ if ( modal ) {
81+ modal . style . display = 'flex'
82+ }
83+ }
84+ } )
85+ }
86+ } )
87+
6388 // Keyboard navigation
6489 document . addEventListener ( 'keydown' , ( e ) => {
6590 if ( ! this . isInViewport ( ) ) return
@@ -125,6 +150,69 @@ class ImageCarousel {
125150 ( window . innerWidth || document . documentElement . clientWidth )
126151 )
127152 }
153+
154+ private positionControls ( ) : void {
155+ if ( ! this . prevButton || ! this . nextButton ) return
156+
157+ // Wait for images to load before positioning
158+ const images = Array . from ( this . container . querySelectorAll ( 'img' ) )
159+ if ( images . length === 0 ) return
160+
161+ let loadedCount = 0
162+ const totalImages = images . length
163+
164+ const positionAfterLoad = ( ) => {
165+ loadedCount ++
166+ if ( loadedCount === totalImages ) {
167+ this . calculateControlPosition ( )
168+ }
169+ }
170+
171+ images . forEach ( ( img ) => {
172+ if ( img . complete ) {
173+ positionAfterLoad ( )
174+ } else {
175+ img . addEventListener ( 'load' , positionAfterLoad )
176+ img . addEventListener ( 'error' , positionAfterLoad ) // Handle failed loads
177+ }
178+ } )
179+ }
180+
181+ private calculateControlPosition ( ) : void {
182+ if ( ! this . prevButton || ! this . nextButton ) return
183+
184+ const images = Array . from ( this . container . querySelectorAll ( 'img' ) )
185+ let minHeight = Infinity
186+
187+ // Find the smallest image height among all images
188+ images . forEach ( ( img ) => {
189+ const height = img . offsetHeight
190+ if ( height > 0 && height < minHeight ) {
191+ minHeight = height
192+ }
193+ } )
194+
195+ // Position controls at 40% the height of the smallest image
196+ // But ensure a minimum distance from the top (50px) and don't go below 80% of the smallest image
197+ if ( minHeight !== Infinity && minHeight > 0 ) {
198+ const fortyPercentHeight = Math . floor ( minHeight * 0.4 )
199+ const minTop = 50 // Minimum 50px from top
200+ const maxTop = Math . floor ( minHeight * 0.8 ) // Maximum 80% down the smallest image
201+
202+ const controlTop = Math . max (
203+ minTop ,
204+ Math . min ( fortyPercentHeight , maxTop )
205+ )
206+
207+ this . prevButton . style . top = `${ controlTop } px`
208+ this . nextButton . style . top = `${ controlTop } px`
209+
210+ // Debug logging (remove in production)
211+ console . log (
212+ `Carousel controls positioned: minHeight=${ minHeight } px, controlTop=${ controlTop } px`
213+ )
214+ }
215+ }
128216}
129217
130218// Export function to initialize carousels
@@ -136,19 +224,22 @@ export function initImageCarousel(): void {
136224 carousels . forEach ( ( carouselElement ) => {
137225 const carousel = carouselElement as HTMLElement
138226
139- // Get the existing track
227+ // Skip if carousel already has slides (server-rendered)
228+ const existingSlides = carousel . querySelectorAll ( '.carousel-slide' )
229+ if ( existingSlides . length > 0 ) {
230+ // Just initialize the existing carousel
231+ new ImageCarousel ( carousel )
232+ return
233+ }
234+
235+ // Get the existing track for dynamic carousels
140236 let track = carousel . querySelector ( '.carousel-track' )
141237 if ( ! track ) {
142238 track = document . createElement ( 'div' )
143239 track . className = 'carousel-track'
144240 carousel . appendChild ( track )
145241 }
146242
147- // Clean up any existing slides - this prevents duplicates
148- const existingSlides = Array . from (
149- track . querySelectorAll ( '.carousel-slide' )
150- )
151-
152243 // Find all image links that might be related to this carousel
153244 const section = findSectionForCarousel ( carousel )
154245 if ( ! section ) return
0 commit comments