@brandocms/jupiter 5.0.0-beta.3 → 5.0.0-beta.5
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.
package/package.json
CHANGED
|
@@ -306,20 +306,30 @@ export default class HeroSlider {
|
|
|
306
306
|
this._currentAnimation = animate(sequence)
|
|
307
307
|
|
|
308
308
|
this._currentAnimation.finished.then(() => {
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
// Reset ALL slides using instant animations to ensure Motion.js state is clean
|
|
310
|
+
Array.from(this.slides).forEach((slide) => {
|
|
311
|
+
if (slide === this._currentSlide) return
|
|
312
|
+
const img = slide.querySelector('.hero-slide-img')
|
|
313
|
+
if (img) {
|
|
314
|
+
// Use animate with duration 0 to reset Motion.js internal state
|
|
315
|
+
animate(img, { scale: 1 }, { duration: 0 })
|
|
316
|
+
}
|
|
317
|
+
const isNext = slide === this._nextSlide
|
|
318
|
+
// Reset slide using animate with duration 0
|
|
319
|
+
animate(slide, {
|
|
320
|
+
width: '100%',
|
|
321
|
+
opacity: isNext ? 1 : 0,
|
|
322
|
+
}, { duration: 0 })
|
|
323
|
+
slide.style.overflow = ''
|
|
324
|
+
slide.style.zIndex = isNext ? this.opts.zIndex.next : this.opts.zIndex.regular
|
|
315
325
|
})
|
|
316
|
-
|
|
317
|
-
|
|
326
|
+
|
|
327
|
+
animate(this._currentSlide, {
|
|
318
328
|
width: '100%',
|
|
319
|
-
opacity:
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
|
|
329
|
+
opacity: 1,
|
|
330
|
+
}, { duration: 0 })
|
|
331
|
+
this._currentSlide.style.zIndex = this.opts.zIndex.visible
|
|
332
|
+
|
|
323
333
|
this.next()
|
|
324
334
|
})
|
|
325
335
|
}
|
|
@@ -83,6 +83,65 @@ export default class Lazyload {
|
|
|
83
83
|
this.initObserver(this.revealObserver, false)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Observe new lazyload elements within a container
|
|
88
|
+
* Handles both [data-ll-image] and [data-ll-srcset] elements
|
|
89
|
+
* Useful for dynamically added content (e.g., Looper clones)
|
|
90
|
+
* @param {HTMLElement|HTMLElement[]|NodeList} elements - Container element(s) or lazyload element(s) to observe
|
|
91
|
+
*/
|
|
92
|
+
observe(elements) {
|
|
93
|
+
// Handle NodeList, array, or single element
|
|
94
|
+
const els = elements instanceof NodeList ? Array.from(elements) :
|
|
95
|
+
Array.isArray(elements) ? elements : [elements]
|
|
96
|
+
|
|
97
|
+
let imgIdx = this.lazyImages?.length || 0
|
|
98
|
+
let picIdx = this.lazyPictures?.length || 0
|
|
99
|
+
|
|
100
|
+
els.forEach(el => {
|
|
101
|
+
// Handle [data-ll-image] elements
|
|
102
|
+
if (this.imageObserver) {
|
|
103
|
+
const images = el.matches?.('[data-ll-image]')
|
|
104
|
+
? [el]
|
|
105
|
+
: el.querySelectorAll?.('[data-ll-image]') || []
|
|
106
|
+
|
|
107
|
+
images.forEach(img => {
|
|
108
|
+
// Skip if already observed or loaded
|
|
109
|
+
if (img.hasAttribute('data-ll-idx') || img.hasAttribute('data-ll-loaded')) return
|
|
110
|
+
|
|
111
|
+
img.setAttribute('data-ll-blurred', '')
|
|
112
|
+
img.setAttribute('data-ll-idx', imgIdx)
|
|
113
|
+
img.style.setProperty('--ll-idx', imgIdx)
|
|
114
|
+
this.imageObserver.observe(img)
|
|
115
|
+
imgIdx++
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Handle [data-ll-srcset] picture elements
|
|
120
|
+
if (this.loadObserver) {
|
|
121
|
+
const pictures = el.matches?.('[data-ll-srcset]')
|
|
122
|
+
? [el]
|
|
123
|
+
: el.querySelectorAll?.('[data-ll-srcset]') || []
|
|
124
|
+
|
|
125
|
+
pictures.forEach(picture => {
|
|
126
|
+
// Skip if already loaded
|
|
127
|
+
if (picture.hasAttribute('data-ll-srcset-ready')) return
|
|
128
|
+
|
|
129
|
+
picture.setAttribute('data-ll-srcset-initialized', '')
|
|
130
|
+
picture.querySelectorAll('img:not([data-ll-loaded])').forEach(img => {
|
|
131
|
+
img.removeAttribute('data-ll-idx') // Clear cloned idx
|
|
132
|
+
img.setAttribute('data-ll-blurred', '')
|
|
133
|
+
img.setAttribute('data-ll-idx', picIdx)
|
|
134
|
+
img.style.setProperty('--ll-idx', picIdx)
|
|
135
|
+
})
|
|
136
|
+
// Add to both observers like initObserver does
|
|
137
|
+
this.loadObserver.observe(picture)
|
|
138
|
+
this.revealObserver?.observe(picture)
|
|
139
|
+
picIdx++
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
86
145
|
initialize() {
|
|
87
146
|
// initialize ResizeObserver for images with data-sizes="auto"
|
|
88
147
|
this.initializeResizeObserver()
|
|
@@ -128,12 +187,7 @@ export default class Lazyload {
|
|
|
128
187
|
)
|
|
129
188
|
|
|
130
189
|
this.lazyImages = this.target.querySelectorAll('[data-ll-image]')
|
|
131
|
-
this.lazyImages
|
|
132
|
-
img.setAttribute('data-ll-blurred', '')
|
|
133
|
-
img.setAttribute('data-ll-idx', idx)
|
|
134
|
-
img.style.setProperty('--ll-idx', idx)
|
|
135
|
-
this.imageObserver.observe(img)
|
|
136
|
-
})
|
|
190
|
+
this.observe(this.lazyImages)
|
|
137
191
|
}
|
|
138
192
|
|
|
139
193
|
initObserver(observer, setAttrs = true) {
|
|
@@ -103,6 +103,10 @@ function horizontalLoop(app, items, config) {
|
|
|
103
103
|
let positionUnsubscribe = null // Track position listener for cleanup
|
|
104
104
|
let renderUnsubscribe = null // Track frame.render loop for cleanup
|
|
105
105
|
|
|
106
|
+
// Scroll direction tracking for wrap logic
|
|
107
|
+
let scrollDirection = 0 // -1 = backward, 0 = neutral, 1 = forward
|
|
108
|
+
let lastPositionForDirection = 0
|
|
109
|
+
|
|
106
110
|
// Display elements for index/count
|
|
107
111
|
let indexElements = []
|
|
108
112
|
let countElements = []
|
|
@@ -163,24 +167,45 @@ function horizontalLoop(app, items, config) {
|
|
|
163
167
|
// This prevents items from visibly moving to the back before they're off-screen
|
|
164
168
|
const minRequiredWidth = containerWidth * 2.5 + maxItemWidth
|
|
165
169
|
|
|
166
|
-
// Only replicate if needed
|
|
167
|
-
if (totalWidth >= minRequiredWidth) {
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
170
|
// Store original count to prevent exponential growth
|
|
172
171
|
const originalItemCount = items.length
|
|
173
172
|
const maxReplications = 10
|
|
174
173
|
let count = 0
|
|
175
174
|
let previousTotalWidth = totalWidth
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
// Always create at least one set of clones - the wrapping logic depends on clones existing
|
|
177
|
+
// Then continue until we have enough width for seamless looping
|
|
178
|
+
while ((count === 0 || totalWidth < minRequiredWidth) && count < maxReplications) {
|
|
178
179
|
// Clone ONLY original items
|
|
179
180
|
for (let i = 0; i < originalItemCount; i++) {
|
|
180
181
|
const clone = items[i].cloneNode(true)
|
|
181
182
|
clone.setAttribute('data-looper-clone', 'true')
|
|
182
183
|
container.appendChild(clone)
|
|
183
184
|
items.push(clone)
|
|
185
|
+
|
|
186
|
+
// Register cloned lazyload elements with Lazyload module if available
|
|
187
|
+
if (app?.lazyload?.observe) {
|
|
188
|
+
// Clear data-ll-idx from unloaded [data-ll-image] images
|
|
189
|
+
clone.querySelectorAll('[data-ll-image]:not([data-ll-loaded])').forEach(img => {
|
|
190
|
+
img.removeAttribute('data-ll-idx')
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// Clear attributes from unloaded [data-ll-srcset] pictures so they can be re-observed
|
|
194
|
+
clone.querySelectorAll('[data-ll-srcset]:not([data-ll-srcset-ready])').forEach(picture => {
|
|
195
|
+
picture.removeAttribute('data-ll-srcset-initialized')
|
|
196
|
+
// Clear ready state from sources and img so they can be re-processed
|
|
197
|
+
picture.querySelectorAll('source').forEach(source => {
|
|
198
|
+
source.removeAttribute('data-ll-ready')
|
|
199
|
+
})
|
|
200
|
+
picture.querySelectorAll('img').forEach(img => {
|
|
201
|
+
img.removeAttribute('data-ll-idx')
|
|
202
|
+
img.removeAttribute('data-ll-loaded')
|
|
203
|
+
img.removeAttribute('data-ll-ready')
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
app.lazyload.observe(clone)
|
|
208
|
+
}
|
|
184
209
|
}
|
|
185
210
|
|
|
186
211
|
// Force layout recalculation
|
|
@@ -390,10 +415,11 @@ function horizontalLoop(app, items, config) {
|
|
|
390
415
|
// In reset zone but item doesn't need reset → keep current offset
|
|
391
416
|
newOffset = itemWrapOffsets[i]
|
|
392
417
|
} else if (itemLeft < -(widths[i] + containerWidth * 0.5)) {
|
|
393
|
-
// Item exited LEFT edge
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
|
|
418
|
+
// Item exited LEFT edge - only wrap during forward scroll
|
|
419
|
+
// During reverse scroll (scrollDirection < 0), items off-screen left
|
|
420
|
+
// will naturally scroll back into view - don't wrap them
|
|
421
|
+
const isForwardScroll = scrollDirection >= 0
|
|
422
|
+
newOffset = (isForwardScroll && boundedPos < originalItemsWidth / 2) ? wrapOffset : 0
|
|
397
423
|
} else if (itemLeft > containerWidth + containerWidth * 0.5) {
|
|
398
424
|
// Item exited RIGHT edge
|
|
399
425
|
// This shouldn't happen much, but handle it
|
|
@@ -532,6 +558,13 @@ function horizontalLoop(app, items, config) {
|
|
|
532
558
|
// Set up boundedPos motionValue to automatically sync with position
|
|
533
559
|
// This calculates the bounded position (0 to originalItemsWidth)
|
|
534
560
|
const positionUnsubscribe = position.on('change', latest => {
|
|
561
|
+
// Track scroll direction for wrap logic
|
|
562
|
+
const delta = latest - lastPositionForDirection
|
|
563
|
+
if (Math.abs(delta) > 1) {
|
|
564
|
+
scrollDirection = delta > 0 ? 1 : -1
|
|
565
|
+
}
|
|
566
|
+
lastPositionForDirection = latest
|
|
567
|
+
|
|
535
568
|
const bounded = ((latest % originalItemsWidth) + originalItemsWidth) % originalItemsWidth
|
|
536
569
|
boundedPos.set(bounded)
|
|
537
570
|
})
|
|
@@ -1165,7 +1198,8 @@ function horizontalLoop(app, items, config) {
|
|
|
1165
1198
|
const currentPos = position.get()
|
|
1166
1199
|
|
|
1167
1200
|
// Calculate position within current cycle (using originalItemsWidth)
|
|
1168
|
-
|
|
1201
|
+
// Use proper modulo for negative positions (dragging right/backward)
|
|
1202
|
+
const cyclePos = ((currentPos % originalItemsWidth) + originalItemsWidth) % originalItemsWidth
|
|
1169
1203
|
const remainingDist = originalItemsWidth - cyclePos
|
|
1170
1204
|
const remainingDuration = remainingDist / pixelsPerSecond
|
|
1171
1205
|
|