@thewhateverapp/tile-sdk 0.13.17 → 0.13.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"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"}
@@ -239,140 +239,177 @@ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, trans
239
239
  resume: useCallback(() => singleton.resume(), []),
240
240
  toggle: useCallback(() => singleton.toggle(), []),
241
241
  };
242
- // Touch/swipe handlers using native events (React touch events are passive)
242
+ // Touch/swipe handlers with visual drag feedback
243
+ // NOTE: We attach listeners once and check singleton state directly to avoid
244
+ // stale closure issues and listener teardown during swipe sequences.
243
245
  const touchCurrentRef = useRef(null);
246
+ const [dragOffset, setDragOffset] = useState(0); // Pixels dragged horizontally
247
+ const [isDragging, setIsDragging] = useState(false);
248
+ const containerWidthRef = useRef(0);
244
249
  useEffect(() => {
245
250
  const container = containerRef.current;
246
- console.log('[Slideshow Swipe] Effect running:', {
247
- hasContainer: !!container,
248
- swipeable,
249
- totalSlides: state.totalSlides,
250
- isTransitioning: state.isTransitioning,
251
- });
252
- if (!container) {
253
- console.log('[Slideshow Swipe] ❌ No container ref');
254
- return;
255
- }
256
- if (!swipeable) {
257
- console.log('[Slideshow Swipe] ❌ Swipeable is false');
258
- return;
259
- }
260
- if (state.totalSlides <= 1) {
261
- console.log('[Slideshow Swipe] ❌ Only', state.totalSlides, 'slide(s)');
251
+ if (!container || !swipeable) {
262
252
  return;
263
253
  }
264
- console.log('[Slideshow Swipe] Attaching touch listeners to container');
254
+ // Get singleton reference once - we'll check its state directly in handlers
255
+ const swipeSingleton = getSlideshowSingleton();
265
256
  const handleTouchStart = (e) => {
257
+ // Check totalSlides from singleton (not stale closure)
258
+ if (swipeSingleton.getState().totalSlides <= 1)
259
+ return;
266
260
  const touch = e.touches[0];
267
- console.log('[Slideshow Swipe] touchstart:', {
268
- hasTouch: !!touch,
269
- clientX: touch?.clientX,
270
- clientY: touch?.clientY,
271
- target: e.target?.tagName,
272
- currentTarget: e.currentTarget?.tagName,
273
- });
274
261
  if (!touch)
275
262
  return;
263
+ // Store container width for percentage calculations
264
+ containerWidthRef.current = container.offsetWidth;
276
265
  touchStartRef.current = {
277
266
  x: touch.clientX,
278
267
  y: touch.clientY,
279
268
  time: Date.now(),
280
269
  };
281
270
  touchCurrentRef.current = { x: touch.clientX, y: touch.clientY };
271
+ setIsDragging(true);
272
+ setDragOffset(0);
282
273
  };
283
274
  const handleTouchMove = (e) => {
284
- if (!touchStartRef.current) {
285
- console.log('[Slideshow Swipe] touchmove: no touchStart ref');
275
+ if (!touchStartRef.current)
286
276
  return;
287
- }
288
277
  const touch = e.touches[0];
289
- if (!touch) {
290
- console.log('[Slideshow Swipe] touchmove: no touch');
278
+ if (!touch)
291
279
  return;
292
- }
293
280
  touchCurrentRef.current = { x: touch.clientX, y: touch.clientY };
294
- // Prevent default and stop propagation for horizontal swipes
295
281
  const deltaX = touch.clientX - touchStartRef.current.x;
296
282
  const deltaY = touch.clientY - touchStartRef.current.y;
297
- if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 10) {
298
- console.log('[Slideshow Swipe] touchmove: horizontal swipe detected, preventing default', { deltaX, deltaY });
283
+ // Only start visual drag if horizontal movement dominates
284
+ if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 5) {
299
285
  e.preventDefault();
300
286
  e.stopPropagation();
287
+ // Apply resistance at edges (can't drag past first/last)
288
+ const currentIndex = swipeSingleton.getState().currentIndex;
289
+ const totalSlides = swipeSingleton.getState().totalSlides;
290
+ const isAtStart = currentIndex === 0 && deltaX > 0;
291
+ const isAtEnd = currentIndex === totalSlides - 1 && deltaX < 0;
292
+ if (isAtStart || isAtEnd) {
293
+ // Rubber band effect - reduced movement at edges
294
+ setDragOffset(deltaX * 0.3);
295
+ }
296
+ else {
297
+ setDragOffset(deltaX);
298
+ }
301
299
  }
302
300
  };
303
301
  const handleTouchEnd = (e) => {
304
- console.log('[Slideshow Swipe] touchend:', {
305
- hasTouchStart: !!touchStartRef.current,
306
- hasTouchCurrent: !!touchCurrentRef.current,
307
- isTransitioning: state.isTransitioning,
308
- });
302
+ const wasDragging = !!touchStartRef.current;
309
303
  if (!touchStartRef.current || !touchCurrentRef.current) {
310
- console.log('[Slideshow Swipe] touchend: missing refs, aborting');
311
304
  touchStartRef.current = null;
312
305
  touchCurrentRef.current = null;
306
+ setIsDragging(false);
307
+ setDragOffset(0);
313
308
  return;
314
309
  }
315
- // Don't process if transitioning
316
- if (state.isTransitioning) {
317
- console.log('[Slideshow Swipe] touchend: transitioning, aborting');
310
+ // Check transitioning from singleton directly (not stale closure)
311
+ if (swipeSingleton.getState().isTransitioning) {
318
312
  touchStartRef.current = null;
319
313
  touchCurrentRef.current = null;
314
+ setIsDragging(false);
315
+ setDragOffset(0);
320
316
  return;
321
317
  }
322
318
  const deltaX = touchCurrentRef.current.x - touchStartRef.current.x;
323
319
  const deltaY = touchCurrentRef.current.y - touchStartRef.current.y;
324
320
  const deltaTime = Date.now() - touchStartRef.current.time;
325
- // Lower threshold for smaller tiles - 30px minimum swipe distance
321
+ // 30px minimum swipe distance, must be horizontal
326
322
  const minSwipeDistance = 30;
327
323
  const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY);
328
324
  const isValidSwipe = Math.abs(deltaX) > minSwipeDistance && isHorizontalSwipe;
329
325
  const isQuickSwipe = deltaTime < 500 || Math.abs(deltaX) > 60;
330
- console.log('[Slideshow Swipe] touchend analysis:', {
331
- deltaX,
332
- deltaY,
333
- deltaTime,
334
- minSwipeDistance,
335
- isHorizontalSwipe,
336
- isValidSwipe,
337
- isQuickSwipe,
338
- willTrigger: isValidSwipe && isQuickSwipe,
339
- direction: deltaX > 0 ? 'prev' : 'next',
340
- });
326
+ // Reset drag state before triggering transition
327
+ setIsDragging(false);
328
+ setDragOffset(0);
341
329
  if (isValidSwipe && isQuickSwipe) {
342
330
  e.preventDefault();
343
331
  e.stopPropagation();
344
332
  if (deltaX > 0) {
345
- console.log('[Slideshow Swipe] 🎯 Triggering PREV');
346
- singleton.prev();
333
+ swipeSingleton.prev();
347
334
  }
348
335
  else {
349
- console.log('[Slideshow Swipe] 🎯 Triggering NEXT');
350
- singleton.next();
336
+ swipeSingleton.next();
351
337
  }
352
338
  }
353
- else {
354
- console.log('[Slideshow Swipe] ❌ Swipe not valid:', {
355
- reason: !isHorizontalSwipe ? 'not horizontal' : !isValidSwipe ? 'too short' : 'too slow',
356
- });
357
- }
358
339
  touchStartRef.current = null;
359
340
  touchCurrentRef.current = null;
360
341
  };
361
- // Use passive: false on all to allow preventDefault()
342
+ // Also handle touch cancel (e.g., incoming call)
343
+ const handleTouchCancel = () => {
344
+ touchStartRef.current = null;
345
+ touchCurrentRef.current = null;
346
+ setIsDragging(false);
347
+ setDragOffset(0);
348
+ };
349
+ // Use passive: false to allow preventDefault()
362
350
  container.addEventListener('touchstart', handleTouchStart, { passive: false });
363
351
  container.addEventListener('touchmove', handleTouchMove, { passive: false });
364
352
  container.addEventListener('touchend', handleTouchEnd, { passive: false });
365
- console.log('[Slideshow Swipe] Listeners attached successfully');
353
+ container.addEventListener('touchcancel', handleTouchCancel, { passive: false });
366
354
  return () => {
367
- console.log('[Slideshow Swipe] Cleaning up listeners');
368
355
  container.removeEventListener('touchstart', handleTouchStart);
369
356
  container.removeEventListener('touchmove', handleTouchMove);
370
357
  container.removeEventListener('touchend', handleTouchEnd);
358
+ container.removeEventListener('touchcancel', handleTouchCancel);
371
359
  };
372
- }, [swipeable, state.totalSlides, state.isTransitioning, singleton]);
373
- // Get transition styles
360
+ }, [swipeable]); // Only re-attach if swipeable changes - check singleton for dynamic state
361
+ // Get transition styles with drag offset support
374
362
  const getTransitionStyles = (index) => {
375
363
  const isActive = index === state.currentIndex;
364
+ const isPrev = index === state.currentIndex - 1 ||
365
+ (state.currentIndex === 0 && index === state.totalSlides - 1); // Wrap around
366
+ const isNext = index === state.currentIndex + 1 ||
367
+ (state.currentIndex === state.totalSlides - 1 && index === 0); // Wrap around
368
+ // During drag, use slide-style movement for visual feedback
369
+ if (isDragging && swipeable && state.totalSlides > 1) {
370
+ const containerWidth = containerWidthRef.current || 256;
371
+ const dragPercent = (dragOffset / containerWidth) * 100;
372
+ // Show current, prev, and next slides during drag
373
+ if (isActive) {
374
+ return {
375
+ transform: `translateX(${dragPercent}%)`,
376
+ transition: 'none', // Immediate response during drag
377
+ position: 'absolute',
378
+ inset: 0,
379
+ zIndex: 2,
380
+ };
381
+ }
382
+ else if (isPrev && dragOffset > 0) {
383
+ // Dragging right - show previous slide coming in from left
384
+ return {
385
+ transform: `translateX(${-100 + dragPercent}%)`,
386
+ transition: 'none',
387
+ position: 'absolute',
388
+ inset: 0,
389
+ zIndex: 1,
390
+ };
391
+ }
392
+ else if (isNext && dragOffset < 0) {
393
+ // Dragging left - show next slide coming in from right
394
+ return {
395
+ transform: `translateX(${100 + dragPercent}%)`,
396
+ transition: 'none',
397
+ position: 'absolute',
398
+ inset: 0,
399
+ zIndex: 1,
400
+ };
401
+ }
402
+ else {
403
+ // Hide other slides during drag
404
+ return {
405
+ opacity: 0,
406
+ position: 'absolute',
407
+ inset: 0,
408
+ zIndex: 0,
409
+ };
410
+ }
411
+ }
412
+ // Normal transition styles (when not dragging)
376
413
  switch (transition) {
377
414
  case 'fade':
378
415
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thewhateverapp/tile-sdk",
3
- "version": "0.13.17",
3
+ "version": "0.13.19",
4
4
  "description": "SDK for building interactive tiles on The Whatever App platform",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",