@brandocms/jupiter 5.0.0-beta.3 → 5.0.0-beta.4

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandocms/jupiter",
3
- "version": "5.0.0-beta.3",
3
+ "version": "5.0.0-beta.4",
4
4
  "description": "Frontend helpers.",
5
5
  "author": "Univers/Twined",
6
6
  "license": "UNLICENSED",
@@ -306,20 +306,30 @@ export default class HeroSlider {
306
306
  this._currentAnimation = animate(sequence)
307
307
 
308
308
  this._currentAnimation.finished.then(() => {
309
- // Cleanup and shuffle z-indexes
310
- set(this._nextSlide, { zIndex: this.opts.zIndex.next, opacity: 1 })
311
- set(this._currentSlide, {
312
- zIndex: this.opts.zIndex.visible,
313
- width: '100%',
314
- opacity: 1,
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
- set(this._previousSlide, {
317
- zIndex: this.opts.zIndex.regular,
326
+
327
+ animate(this._currentSlide, {
318
328
  width: '100%',
319
- opacity: 0, // Hide previous slide
320
- })
321
- // Reset previous slide image scale for next time
322
- set(previousSlideImg, { scale: 1.0 })
329
+ opacity: 1,
330
+ }, { duration: 0 })
331
+ this._currentSlide.style.zIndex = this.opts.zIndex.visible
332
+
323
333
  this.next()
324
334
  })
325
335
  }
@@ -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,18 +167,15 @@ 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
- while (totalWidth < minRequiredWidth && count < maxReplications) {
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)
@@ -390,10 +391,11 @@ function horizontalLoop(app, items, config) {
390
391
  // In reset zone but item doesn't need reset → keep current offset
391
392
  newOffset = itemWrapOffsets[i]
392
393
  } else if (itemLeft < -(widths[i] + containerWidth * 0.5)) {
393
- // Item exited LEFT edge
394
- // Forward drag (low boundedPos): wrap to END
395
- // Backward drag (high boundedPos): don't wrap, clones fill in from right
396
- newOffset = boundedPos < originalItemsWidth / 2 ? wrapOffset : 0
394
+ // Item exited LEFT edge - only wrap during forward scroll
395
+ // During reverse scroll (scrollDirection < 0), items off-screen left
396
+ // will naturally scroll back into view - don't wrap them
397
+ const isForwardScroll = scrollDirection >= 0
398
+ newOffset = (isForwardScroll && boundedPos < originalItemsWidth / 2) ? wrapOffset : 0
397
399
  } else if (itemLeft > containerWidth + containerWidth * 0.5) {
398
400
  // Item exited RIGHT edge
399
401
  // This shouldn't happen much, but handle it
@@ -532,6 +534,13 @@ function horizontalLoop(app, items, config) {
532
534
  // Set up boundedPos motionValue to automatically sync with position
533
535
  // This calculates the bounded position (0 to originalItemsWidth)
534
536
  const positionUnsubscribe = position.on('change', latest => {
537
+ // Track scroll direction for wrap logic
538
+ const delta = latest - lastPositionForDirection
539
+ if (Math.abs(delta) > 1) {
540
+ scrollDirection = delta > 0 ? 1 : -1
541
+ }
542
+ lastPositionForDirection = latest
543
+
535
544
  const bounded = ((latest % originalItemsWidth) + originalItemsWidth) % originalItemsWidth
536
545
  boundedPos.set(bounded)
537
546
  })
@@ -1165,7 +1174,8 @@ function horizontalLoop(app, items, config) {
1165
1174
  const currentPos = position.get()
1166
1175
 
1167
1176
  // Calculate position within current cycle (using originalItemsWidth)
1168
- const cyclePos = currentPos % originalItemsWidth
1177
+ // Use proper modulo for negative positions (dragging right/backward)
1178
+ const cyclePos = ((currentPos % originalItemsWidth) + originalItemsWidth) % originalItemsWidth
1169
1179
  const remainingDist = originalItemsWidth - cyclePos
1170
1180
  const remainingDuration = remainingDist / pixelsPerSecond
1171
1181