@thewhateverapp/tile-sdk 0.15.8 → 0.16.0
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,9 +1,5 @@
|
|
|
1
1
|
import React, { type ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
interface Window {
|
|
4
|
-
__slideshowSingleton?: SlideshowSingletonClass;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
2
|
+
import useEmblaCarousel from 'embla-carousel-react';
|
|
7
3
|
export interface SlideImage {
|
|
8
4
|
url: string;
|
|
9
5
|
alt?: string;
|
|
@@ -28,11 +24,14 @@ export interface SlideshowContextValue {
|
|
|
28
24
|
state: SlideshowState;
|
|
29
25
|
controls: SlideshowControls;
|
|
30
26
|
}
|
|
27
|
+
declare global {
|
|
28
|
+
interface Window {
|
|
29
|
+
__slideshowSingleton?: SlideshowSingletonClass;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
31
32
|
type StateCallback = (state: SlideshowState) => void;
|
|
32
33
|
/**
|
|
33
34
|
* SlideshowSingleton - Manages slideshow state across route changes.
|
|
34
|
-
*
|
|
35
|
-
* The current slide index and pause state persist across Next.js soft navigations.
|
|
36
35
|
* When navigating from /tile to /page, the slideshow maintains its position.
|
|
37
36
|
*/
|
|
38
37
|
declare class SlideshowSingletonClass {
|
|
@@ -40,27 +39,18 @@ declare class SlideshowSingletonClass {
|
|
|
40
39
|
private currentState;
|
|
41
40
|
private intervalRef;
|
|
42
41
|
private intervalMs;
|
|
43
|
-
private
|
|
42
|
+
private emblaApi;
|
|
43
|
+
setEmblaApi(api: ReturnType<typeof useEmblaCarousel>[1] | null): void;
|
|
44
44
|
/**
|
|
45
45
|
* Initialize slideshow with images
|
|
46
|
-
*
|
|
47
|
-
* Auto-advance state is ALWAYS updated based on the autoAdvance option,
|
|
48
|
-
* allowing tile mode (no auto-advance) and page mode (auto-advance) to
|
|
49
|
-
* work correctly when navigating between routes with the same images.
|
|
50
46
|
*/
|
|
51
47
|
initialize(images: SlideImage[], options?: {
|
|
52
48
|
intervalMs?: number;
|
|
53
49
|
autoAdvance?: boolean;
|
|
54
|
-
transitionDuration?: number;
|
|
55
50
|
}): void;
|
|
56
|
-
/**
|
|
57
|
-
* Start auto-advance timer
|
|
58
|
-
*/
|
|
59
51
|
private startAutoAdvance;
|
|
60
|
-
/**
|
|
61
|
-
* Stop auto-advance timer
|
|
62
|
-
*/
|
|
63
52
|
private stopAutoAdvance;
|
|
53
|
+
updateFromEmbla(index: number): void;
|
|
64
54
|
next(): void;
|
|
65
55
|
prev(): void;
|
|
66
56
|
goTo(index: number): void;
|
|
@@ -70,9 +60,6 @@ declare class SlideshowSingletonClass {
|
|
|
70
60
|
getState(): SlideshowState;
|
|
71
61
|
onStateChange(callback: StateCallback): () => void;
|
|
72
62
|
private notifyListeners;
|
|
73
|
-
/**
|
|
74
|
-
* Clean up (call when slideshow is removed)
|
|
75
|
-
*/
|
|
76
63
|
destroy(): void;
|
|
77
64
|
}
|
|
78
65
|
export interface SlideshowProps {
|
|
@@ -82,9 +69,9 @@ export interface SlideshowProps {
|
|
|
82
69
|
intervalMs?: number;
|
|
83
70
|
/** Auto-advance slides (default: true) */
|
|
84
71
|
autoAdvance?: boolean;
|
|
85
|
-
/** Transition type
|
|
72
|
+
/** Transition type - note: Embla always uses slide, this is kept for API compat */
|
|
86
73
|
transition?: 'fade' | 'slide' | 'none';
|
|
87
|
-
/** Transition duration in ms (default:
|
|
74
|
+
/** Transition duration in ms (default: 300) */
|
|
88
75
|
transitionDuration?: number;
|
|
89
76
|
/** Show navigation dots (default: true) */
|
|
90
77
|
showDots?: boolean;
|
|
@@ -92,8 +79,10 @@ export interface SlideshowProps {
|
|
|
92
79
|
showArrows?: boolean;
|
|
93
80
|
/** Enable swipe gestures on touch devices (default: true) */
|
|
94
81
|
swipeable?: boolean;
|
|
95
|
-
/** Image fit mode: 'cover' fills container (may crop), 'contain' shows full image
|
|
82
|
+
/** Image fit mode: 'cover' fills container (may crop), 'contain' shows full image */
|
|
96
83
|
objectFit?: 'cover' | 'contain';
|
|
84
|
+
/** Enable looping (default: true) */
|
|
85
|
+
loop?: boolean;
|
|
97
86
|
/** Children rendered as overlay */
|
|
98
87
|
children?: ReactNode;
|
|
99
88
|
/** Additional class names */
|
|
@@ -102,22 +91,17 @@ export interface SlideshowProps {
|
|
|
102
91
|
imageClassName?: string;
|
|
103
92
|
}
|
|
104
93
|
/**
|
|
105
|
-
* Slideshow component
|
|
106
|
-
*
|
|
107
|
-
* The current slide index persists across Next.js route changes (tile ↔ page).
|
|
108
|
-
* When navigating between routes, the slideshow maintains its position.
|
|
94
|
+
* Slideshow component using Embla Carousel for smooth, native-like swiping.
|
|
109
95
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* ```
|
|
96
|
+
* Features:
|
|
97
|
+
* - Smooth touch/swipe with momentum physics
|
|
98
|
+
* - State persistence across route changes
|
|
99
|
+
* - Auto-advance with configurable interval
|
|
100
|
+
* - Overlay support for custom UI
|
|
116
101
|
*/
|
|
117
|
-
export declare function Slideshow({ images, intervalMs, autoAdvance, transition, transitionDuration, showDots, showArrows, swipeable, objectFit, children, className, imageClassName, }: SlideshowProps): React.JSX.Element;
|
|
102
|
+
export declare function Slideshow({ images, intervalMs, autoAdvance, transition, transitionDuration, showDots, showArrows, swipeable, objectFit, loop, children, className, imageClassName, }: SlideshowProps): React.JSX.Element;
|
|
118
103
|
/**
|
|
119
104
|
* Hook to access slideshow state and controls from within Slideshow children.
|
|
120
|
-
* Also aliased as useSlideshow for consistency with VideoPlayer.
|
|
121
105
|
*/
|
|
122
106
|
export declare function useSlideshowState(): SlideshowContextValue;
|
|
123
107
|
export declare const useSlideshow: typeof useSlideshowState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import React, { createContext, useContext, useEffect, useRef, useState, useCallback, } from 'react';
|
|
3
|
+
import useEmblaCarousel from 'embla-carousel-react';
|
|
3
4
|
/**
|
|
4
5
|
* SlideshowSingleton - Manages slideshow state across route changes.
|
|
5
|
-
*
|
|
6
|
-
* The current slide index and pause state persist across Next.js soft navigations.
|
|
7
6
|
* When navigating from /tile to /page, the slideshow maintains its position.
|
|
8
7
|
*/
|
|
9
8
|
class SlideshowSingletonClass {
|
|
@@ -18,21 +17,19 @@ class SlideshowSingletonClass {
|
|
|
18
17
|
};
|
|
19
18
|
this.intervalRef = null;
|
|
20
19
|
this.intervalMs = 5000;
|
|
21
|
-
|
|
20
|
+
// Embla API reference (set by component)
|
|
21
|
+
this.emblaApi = null;
|
|
22
|
+
}
|
|
23
|
+
setEmblaApi(api) {
|
|
24
|
+
this.emblaApi = api;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* Initialize slideshow with images
|
|
25
|
-
*
|
|
26
|
-
* Auto-advance state is ALWAYS updated based on the autoAdvance option,
|
|
27
|
-
* allowing tile mode (no auto-advance) and page mode (auto-advance) to
|
|
28
|
-
* work correctly when navigating between routes with the same images.
|
|
29
28
|
*/
|
|
30
29
|
initialize(images, options) {
|
|
31
30
|
const imagesChanged = JSON.stringify(images) !== JSON.stringify(this.currentState.images);
|
|
32
|
-
// Determine pause state from autoAdvance option (default: true = not paused)
|
|
33
31
|
const shouldBePaused = options?.autoAdvance === false;
|
|
34
32
|
if (imagesChanged) {
|
|
35
|
-
// Images changed - reset to first slide
|
|
36
33
|
this.currentState = {
|
|
37
34
|
...this.currentState,
|
|
38
35
|
currentIndex: 0,
|
|
@@ -42,8 +39,6 @@ class SlideshowSingletonClass {
|
|
|
42
39
|
};
|
|
43
40
|
}
|
|
44
41
|
else {
|
|
45
|
-
// Same images - preserve current slide but update auto-advance state
|
|
46
|
-
// This allows tile→page navigation to start auto-advancing
|
|
47
42
|
this.currentState = {
|
|
48
43
|
...this.currentState,
|
|
49
44
|
totalSlides: images.length,
|
|
@@ -54,15 +49,9 @@ class SlideshowSingletonClass {
|
|
|
54
49
|
if (options?.intervalMs) {
|
|
55
50
|
this.intervalMs = options.intervalMs;
|
|
56
51
|
}
|
|
57
|
-
if (options?.transitionDuration) {
|
|
58
|
-
this.transitionDuration = options.transitionDuration;
|
|
59
|
-
}
|
|
60
52
|
this.notifyListeners();
|
|
61
53
|
this.startAutoAdvance();
|
|
62
54
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Start auto-advance timer
|
|
65
|
-
*/
|
|
66
55
|
startAutoAdvance() {
|
|
67
56
|
this.stopAutoAdvance();
|
|
68
57
|
if (this.currentState.isPaused || this.currentState.totalSlides <= 1) {
|
|
@@ -72,78 +61,63 @@ class SlideshowSingletonClass {
|
|
|
72
61
|
this.next();
|
|
73
62
|
}, this.intervalMs);
|
|
74
63
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Stop auto-advance timer
|
|
77
|
-
*/
|
|
78
64
|
stopAutoAdvance() {
|
|
79
65
|
if (this.intervalRef) {
|
|
80
66
|
clearInterval(this.intervalRef);
|
|
81
67
|
this.intervalRef = null;
|
|
82
68
|
}
|
|
83
69
|
}
|
|
84
|
-
//
|
|
70
|
+
// Update state from Embla (called on slide change)
|
|
71
|
+
updateFromEmbla(index) {
|
|
72
|
+
if (index !== this.currentState.currentIndex) {
|
|
73
|
+
this.currentState = {
|
|
74
|
+
...this.currentState,
|
|
75
|
+
currentIndex: index,
|
|
76
|
+
isTransitioning: false,
|
|
77
|
+
};
|
|
78
|
+
this.notifyListeners();
|
|
79
|
+
// Restart auto-advance timer on manual navigation
|
|
80
|
+
if (!this.currentState.isPaused) {
|
|
81
|
+
this.startAutoAdvance();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
85
|
next() {
|
|
86
|
-
|
|
87
|
-
isTransitioning: this.currentState.isTransitioning,
|
|
88
|
-
totalSlides: this.currentState.totalSlides,
|
|
89
|
-
currentIndex: this.currentState.currentIndex,
|
|
90
|
-
});
|
|
91
|
-
if (this.currentState.isTransitioning || this.currentState.totalSlides <= 1) {
|
|
92
|
-
console.log('[SlideshowSingleton] next() blocked:', this.currentState.isTransitioning ? 'transitioning' : 'single slide');
|
|
86
|
+
if (this.currentState.totalSlides <= 1)
|
|
93
87
|
return;
|
|
88
|
+
if (this.emblaApi) {
|
|
89
|
+
this.emblaApi.scrollNext();
|
|
94
90
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
...this.currentState,
|
|
99
|
-
isTransitioning: true,
|
|
100
|
-
currentIndex: newIndex,
|
|
101
|
-
};
|
|
102
|
-
this.notifyListeners();
|
|
103
|
-
setTimeout(() => {
|
|
104
|
-
this.currentState = { ...this.currentState, isTransitioning: false };
|
|
91
|
+
else {
|
|
92
|
+
// Fallback without Embla
|
|
93
|
+
const newIndex = (this.currentState.currentIndex + 1) % this.currentState.totalSlides;
|
|
94
|
+
this.currentState = { ...this.currentState, currentIndex: newIndex };
|
|
105
95
|
this.notifyListeners();
|
|
106
|
-
}
|
|
96
|
+
}
|
|
107
97
|
}
|
|
108
98
|
prev() {
|
|
109
|
-
|
|
110
|
-
isTransitioning: this.currentState.isTransitioning,
|
|
111
|
-
totalSlides: this.currentState.totalSlides,
|
|
112
|
-
currentIndex: this.currentState.currentIndex,
|
|
113
|
-
});
|
|
114
|
-
if (this.currentState.isTransitioning || this.currentState.totalSlides <= 1) {
|
|
115
|
-
console.log('[SlideshowSingleton] prev() blocked:', this.currentState.isTransitioning ? 'transitioning' : 'single slide');
|
|
99
|
+
if (this.currentState.totalSlides <= 1)
|
|
116
100
|
return;
|
|
101
|
+
if (this.emblaApi) {
|
|
102
|
+
this.emblaApi.scrollPrev();
|
|
117
103
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
...this.currentState,
|
|
122
|
-
isTransitioning: true,
|
|
123
|
-
currentIndex: newIndex,
|
|
124
|
-
};
|
|
125
|
-
this.notifyListeners();
|
|
126
|
-
setTimeout(() => {
|
|
127
|
-
this.currentState = { ...this.currentState, isTransitioning: false };
|
|
104
|
+
else {
|
|
105
|
+
const newIndex = (this.currentState.currentIndex - 1 + this.currentState.totalSlides) % this.currentState.totalSlides;
|
|
106
|
+
this.currentState = { ...this.currentState, currentIndex: newIndex };
|
|
128
107
|
this.notifyListeners();
|
|
129
|
-
}
|
|
108
|
+
}
|
|
130
109
|
}
|
|
131
110
|
goTo(index) {
|
|
132
|
-
if (this.currentState.
|
|
133
|
-
index === this.currentState.currentIndex ||
|
|
134
|
-
index < 0 ||
|
|
135
|
-
index >= this.currentState.totalSlides)
|
|
111
|
+
if (index < 0 || index >= this.currentState.totalSlides || index === this.currentState.currentIndex) {
|
|
136
112
|
return;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
setTimeout(() => {
|
|
144
|
-
this.currentState = { ...this.currentState, isTransitioning: false };
|
|
113
|
+
}
|
|
114
|
+
if (this.emblaApi) {
|
|
115
|
+
this.emblaApi.scrollTo(index);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.currentState = { ...this.currentState, currentIndex: index };
|
|
145
119
|
this.notifyListeners();
|
|
146
|
-
}
|
|
120
|
+
}
|
|
147
121
|
}
|
|
148
122
|
pause() {
|
|
149
123
|
this.currentState = { ...this.currentState, isPaused: true };
|
|
@@ -163,7 +137,6 @@ class SlideshowSingletonClass {
|
|
|
163
137
|
this.pause();
|
|
164
138
|
}
|
|
165
139
|
}
|
|
166
|
-
// State management
|
|
167
140
|
getState() {
|
|
168
141
|
return { ...this.currentState };
|
|
169
142
|
}
|
|
@@ -175,17 +148,12 @@ class SlideshowSingletonClass {
|
|
|
175
148
|
notifyListeners() {
|
|
176
149
|
this.stateCallbacks.forEach(cb => cb(this.currentState));
|
|
177
150
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Clean up (call when slideshow is removed)
|
|
180
|
-
*/
|
|
181
151
|
destroy() {
|
|
182
152
|
this.stopAutoAdvance();
|
|
183
153
|
this.stateCallbacks.clear();
|
|
154
|
+
this.emblaApi = null;
|
|
184
155
|
}
|
|
185
156
|
}
|
|
186
|
-
/**
|
|
187
|
-
* Get the SlideshowSingleton instance (creates if needed)
|
|
188
|
-
*/
|
|
189
157
|
function getSlideshowSingleton() {
|
|
190
158
|
if (typeof window === 'undefined') {
|
|
191
159
|
return new SlideshowSingletonClass();
|
|
@@ -200,275 +168,68 @@ function getSlideshowSingleton() {
|
|
|
200
168
|
// =============================================================================
|
|
201
169
|
const SlideshowContext = createContext(null);
|
|
202
170
|
/**
|
|
203
|
-
* Slideshow component
|
|
204
|
-
*
|
|
205
|
-
* The current slide index persists across Next.js route changes (tile ↔ page).
|
|
206
|
-
* When navigating between routes, the slideshow maintains its position.
|
|
171
|
+
* Slideshow component using Embla Carousel for smooth, native-like swiping.
|
|
207
172
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* ```
|
|
173
|
+
* Features:
|
|
174
|
+
* - Smooth touch/swipe with momentum physics
|
|
175
|
+
* - State persistence across route changes
|
|
176
|
+
* - Auto-advance with configurable interval
|
|
177
|
+
* - Overlay support for custom UI
|
|
214
178
|
*/
|
|
215
|
-
export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, transition = '
|
|
179
|
+
export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, transition = 'slide', transitionDuration = 300, showDots = true, showArrows = true, swipeable = true, objectFit = 'cover', loop = true, children, className = '', imageClassName = '', }) {
|
|
216
180
|
const [state, setState] = useState(() => getSlideshowSingleton().getState());
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
181
|
+
const singleton = useRef(getSlideshowSingleton());
|
|
182
|
+
// Initialize Embla Carousel
|
|
183
|
+
const [emblaRef, emblaApi] = useEmblaCarousel({
|
|
184
|
+
loop: loop && images.length > 1,
|
|
185
|
+
dragFree: false,
|
|
186
|
+
containScroll: 'trimSnaps',
|
|
187
|
+
watchDrag: swipeable,
|
|
188
|
+
duration: transitionDuration,
|
|
189
|
+
startIndex: singleton.current.getState().currentIndex,
|
|
190
|
+
});
|
|
191
|
+
// Connect Embla API to singleton
|
|
220
192
|
useEffect(() => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
});
|
|
228
|
-
// Subscribe to state changes
|
|
229
|
-
const unsubscribe = singleton.onStateChange(setState);
|
|
230
|
-
return unsubscribe;
|
|
231
|
-
}, [images, intervalMs, autoAdvance, transitionDuration]);
|
|
232
|
-
// Control functions bound to singleton
|
|
233
|
-
const singleton = getSlideshowSingleton();
|
|
234
|
-
const controls = {
|
|
235
|
-
next: useCallback(() => singleton.next(), []),
|
|
236
|
-
prev: useCallback(() => singleton.prev(), []),
|
|
237
|
-
goTo: useCallback((index) => singleton.goTo(index), []),
|
|
238
|
-
pause: useCallback(() => singleton.pause(), []),
|
|
239
|
-
resume: useCallback(() => singleton.resume(), []),
|
|
240
|
-
toggle: useCallback(() => singleton.toggle(), []),
|
|
241
|
-
};
|
|
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.
|
|
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);
|
|
193
|
+
singleton.current.setEmblaApi(emblaApi);
|
|
194
|
+
return () => {
|
|
195
|
+
singleton.current.setEmblaApi(null);
|
|
196
|
+
};
|
|
197
|
+
}, [emblaApi]);
|
|
198
|
+
// Listen for Embla slide changes
|
|
249
199
|
useEffect(() => {
|
|
250
|
-
|
|
251
|
-
if (!container || !swipeable) {
|
|
200
|
+
if (!emblaApi)
|
|
252
201
|
return;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const handleTouchStart = (e) => {
|
|
257
|
-
// Check totalSlides from singleton (not stale closure)
|
|
258
|
-
if (swipeSingleton.getState().totalSlides <= 1)
|
|
259
|
-
return;
|
|
260
|
-
const touch = e.touches[0];
|
|
261
|
-
if (!touch)
|
|
262
|
-
return;
|
|
263
|
-
// Store container width for percentage calculations
|
|
264
|
-
containerWidthRef.current = container.offsetWidth;
|
|
265
|
-
touchStartRef.current = {
|
|
266
|
-
x: touch.clientX,
|
|
267
|
-
y: touch.clientY,
|
|
268
|
-
time: Date.now(),
|
|
269
|
-
};
|
|
270
|
-
touchCurrentRef.current = { x: touch.clientX, y: touch.clientY };
|
|
271
|
-
setIsDragging(true);
|
|
272
|
-
setDragOffset(0);
|
|
202
|
+
const onSelect = () => {
|
|
203
|
+
const index = emblaApi.selectedScrollSnap();
|
|
204
|
+
singleton.current.updateFromEmbla(index);
|
|
273
205
|
};
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
touchCurrentRef.current = { x: touch.clientX, y: touch.clientY };
|
|
281
|
-
const deltaX = touch.clientX - touchStartRef.current.x;
|
|
282
|
-
const deltaY = touch.clientY - touchStartRef.current.y;
|
|
283
|
-
// Only start visual drag if horizontal movement dominates (3px threshold for responsiveness)
|
|
284
|
-
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 3) {
|
|
285
|
-
e.preventDefault();
|
|
286
|
-
e.stopPropagation();
|
|
287
|
-
// Apply resistance at edges (can't drag past first/last)
|
|
288
|
-
const currentIndex = swipeSingleton.getState().currentIndex;
|
|
289
|
-
const slideshowTotalSlides = swipeSingleton.getState().totalSlides;
|
|
290
|
-
const isAtStart = currentIndex === 0 && deltaX > 0;
|
|
291
|
-
const isAtEnd = currentIndex === slideshowTotalSlides - 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
|
-
}
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
const handleTouchEnd = (e) => {
|
|
302
|
-
const wasDragging = !!touchStartRef.current;
|
|
303
|
-
if (!touchStartRef.current || !touchCurrentRef.current) {
|
|
304
|
-
touchStartRef.current = null;
|
|
305
|
-
touchCurrentRef.current = null;
|
|
306
|
-
setIsDragging(false);
|
|
307
|
-
setDragOffset(0);
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
// Check transitioning from singleton directly (not stale closure)
|
|
311
|
-
if (swipeSingleton.getState().isTransitioning) {
|
|
312
|
-
touchStartRef.current = null;
|
|
313
|
-
touchCurrentRef.current = null;
|
|
314
|
-
setIsDragging(false);
|
|
315
|
-
setDragOffset(0);
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
const deltaX = touchCurrentRef.current.x - touchStartRef.current.x;
|
|
319
|
-
const deltaY = touchCurrentRef.current.y - touchStartRef.current.y;
|
|
320
|
-
const deltaTime = Date.now() - touchStartRef.current.time;
|
|
321
|
-
// 15px minimum swipe distance, must be horizontal (lowered for better sensitivity)
|
|
322
|
-
const minSwipeDistance = 15;
|
|
323
|
-
const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY);
|
|
324
|
-
const isValidSwipe = Math.abs(deltaX) > minSwipeDistance && isHorizontalSwipe;
|
|
325
|
-
const isQuickSwipe = deltaTime < 600 || Math.abs(deltaX) > 35;
|
|
326
|
-
// Reset drag state before triggering transition
|
|
327
|
-
setIsDragging(false);
|
|
328
|
-
setDragOffset(0);
|
|
329
|
-
if (isValidSwipe && isQuickSwipe) {
|
|
330
|
-
e.preventDefault();
|
|
331
|
-
e.stopPropagation();
|
|
332
|
-
if (deltaX > 0) {
|
|
333
|
-
swipeSingleton.prev();
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
swipeSingleton.next();
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
touchStartRef.current = null;
|
|
340
|
-
touchCurrentRef.current = null;
|
|
341
|
-
};
|
|
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()
|
|
350
|
-
container.addEventListener('touchstart', handleTouchStart, { passive: false });
|
|
351
|
-
container.addEventListener('touchmove', handleTouchMove, { passive: false });
|
|
352
|
-
container.addEventListener('touchend', handleTouchEnd, { passive: false });
|
|
353
|
-
container.addEventListener('touchcancel', handleTouchCancel, { passive: false });
|
|
206
|
+
emblaApi.on('select', onSelect);
|
|
207
|
+
// Sync initial position if singleton has a different index
|
|
208
|
+
const currentState = singleton.current.getState();
|
|
209
|
+
if (currentState.currentIndex !== emblaApi.selectedScrollSnap() && currentState.currentIndex < images.length) {
|
|
210
|
+
emblaApi.scrollTo(currentState.currentIndex, true);
|
|
211
|
+
}
|
|
354
212
|
return () => {
|
|
355
|
-
|
|
356
|
-
container.removeEventListener('touchmove', handleTouchMove);
|
|
357
|
-
container.removeEventListener('touchend', handleTouchEnd);
|
|
358
|
-
container.removeEventListener('touchcancel', handleTouchCancel);
|
|
213
|
+
emblaApi.off('select', onSelect);
|
|
359
214
|
};
|
|
360
|
-
}, [
|
|
361
|
-
//
|
|
362
|
-
const [justFinishedDrag, setJustFinishedDrag] = useState(false);
|
|
363
|
-
// When isDragging changes to false, briefly keep slide mode for smooth finish
|
|
215
|
+
}, [emblaApi, images.length]);
|
|
216
|
+
// Initialize singleton and subscribe to state changes
|
|
364
217
|
useEffect(() => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
(state.currentIndex === totalSlides - 1 && index === 0); // Wrap around
|
|
381
|
-
// During drag, use slide-style movement for visual feedback
|
|
382
|
-
if (isDragging && swipeable && totalSlides > 1) {
|
|
383
|
-
const containerWidth = containerWidthRef.current || 256;
|
|
384
|
-
const dragPercent = (dragOffset / containerWidth) * 100;
|
|
385
|
-
// Check if at edges (no wrap-around during drag)
|
|
386
|
-
const isAtStart = state.currentIndex === 0;
|
|
387
|
-
const isAtEnd = state.currentIndex === totalSlides - 1;
|
|
388
|
-
// Show current, prev, and next slides during drag
|
|
389
|
-
if (isActive) {
|
|
390
|
-
return {
|
|
391
|
-
transform: `translateX(${dragPercent}%)`,
|
|
392
|
-
transition: 'none', // Immediate response during drag
|
|
393
|
-
position: 'absolute',
|
|
394
|
-
inset: 0,
|
|
395
|
-
zIndex: 2,
|
|
396
|
-
opacity: 1,
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
else if (isPrev && dragOffset > 0 && !isAtStart) {
|
|
400
|
-
// Dragging right - show previous slide (but not at start - no wrap)
|
|
401
|
-
return {
|
|
402
|
-
transform: `translateX(${-100 + dragPercent}%)`,
|
|
403
|
-
transition: 'none',
|
|
404
|
-
position: 'absolute',
|
|
405
|
-
inset: 0,
|
|
406
|
-
zIndex: 1,
|
|
407
|
-
opacity: 1,
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
else if (isNext && dragOffset < 0 && !isAtEnd) {
|
|
411
|
-
// Dragging left - show next slide (but not at end - no wrap)
|
|
412
|
-
return {
|
|
413
|
-
transform: `translateX(${100 + dragPercent}%)`,
|
|
414
|
-
transition: 'none',
|
|
415
|
-
position: 'absolute',
|
|
416
|
-
inset: 0,
|
|
417
|
-
zIndex: 1,
|
|
418
|
-
opacity: 1,
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
// Hide other slides during drag (positioned off-screen)
|
|
423
|
-
return {
|
|
424
|
-
transform: 'translateX(-200%)',
|
|
425
|
-
position: 'absolute',
|
|
426
|
-
inset: 0,
|
|
427
|
-
zIndex: 0,
|
|
428
|
-
opacity: 1,
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
// When swipeable is enabled, ALWAYS use slide/transform transitions
|
|
433
|
-
// Never use opacity-based fade to avoid any visual glitches
|
|
434
|
-
if (swipeable) {
|
|
435
|
-
const slideOffset = totalSlides > 1 ? (index - state.currentIndex) * 100 : 0;
|
|
436
|
-
return {
|
|
437
|
-
transform: `translateX(${slideOffset}%)`,
|
|
438
|
-
transition: (justFinishedDrag || state.isTransitioning) && totalSlides > 1
|
|
439
|
-
? `transform ${transitionDuration}ms ease-out`
|
|
440
|
-
: 'none',
|
|
441
|
-
position: 'absolute',
|
|
442
|
-
inset: 0,
|
|
443
|
-
opacity: 1, // Always fully visible - no fade
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
// Non-swipeable mode - use the configured transition type
|
|
447
|
-
switch (transition) {
|
|
448
|
-
case 'fade':
|
|
449
|
-
return {
|
|
450
|
-
opacity: isActive ? 1 : 0,
|
|
451
|
-
transition: `opacity ${transitionDuration}ms ease-in-out`,
|
|
452
|
-
position: 'absolute',
|
|
453
|
-
inset: 0,
|
|
454
|
-
};
|
|
455
|
-
case 'slide':
|
|
456
|
-
const offset = (index - state.currentIndex) * 100;
|
|
457
|
-
return {
|
|
458
|
-
transform: `translateX(${offset}%)`,
|
|
459
|
-
transition: `transform ${transitionDuration}ms ease-in-out`,
|
|
460
|
-
position: 'absolute',
|
|
461
|
-
inset: 0,
|
|
462
|
-
opacity: 1,
|
|
463
|
-
};
|
|
464
|
-
case 'none':
|
|
465
|
-
default:
|
|
466
|
-
return {
|
|
467
|
-
opacity: isActive ? 1 : 0,
|
|
468
|
-
position: 'absolute',
|
|
469
|
-
inset: 0,
|
|
470
|
-
};
|
|
471
|
-
}
|
|
218
|
+
singleton.current.initialize(images, {
|
|
219
|
+
intervalMs,
|
|
220
|
+
autoAdvance,
|
|
221
|
+
});
|
|
222
|
+
const unsubscribe = singleton.current.onStateChange(setState);
|
|
223
|
+
return unsubscribe;
|
|
224
|
+
}, [images, intervalMs, autoAdvance]);
|
|
225
|
+
// Control functions
|
|
226
|
+
const controls = {
|
|
227
|
+
next: useCallback(() => singleton.current.next(), []),
|
|
228
|
+
prev: useCallback(() => singleton.current.prev(), []),
|
|
229
|
+
goTo: useCallback((index) => singleton.current.goTo(index), []),
|
|
230
|
+
pause: useCallback(() => singleton.current.pause(), []),
|
|
231
|
+
resume: useCallback(() => singleton.current.resume(), []),
|
|
232
|
+
toggle: useCallback(() => singleton.current.toggle(), []),
|
|
472
233
|
};
|
|
473
234
|
const contextValue = {
|
|
474
235
|
state,
|
|
@@ -479,24 +240,24 @@ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, trans
|
|
|
479
240
|
React.createElement("p", { className: "text-white/60" }, "No images")));
|
|
480
241
|
}
|
|
481
242
|
return (React.createElement(SlideshowContext.Provider, { value: contextValue },
|
|
482
|
-
React.createElement("div", {
|
|
483
|
-
React.createElement("div", { className: "
|
|
484
|
-
React.createElement("
|
|
485
|
-
|
|
486
|
-
|
|
243
|
+
React.createElement("div", { className: `relative w-full h-full bg-black overflow-hidden ${className}` },
|
|
244
|
+
React.createElement("div", { ref: emblaRef, className: "w-full h-full overflow-hidden" },
|
|
245
|
+
React.createElement("div", { className: "flex h-full", style: { touchAction: swipeable ? 'pan-y' : 'auto' } }, images.map((image, index) => (React.createElement("div", { key: `${image.url}-${index}`, className: `flex-none w-full h-full flex items-center justify-center ${imageClassName}`, style: { minWidth: 0 } },
|
|
246
|
+
React.createElement("img", { src: image.url, alt: image.alt || `Slide ${index + 1}`, className: `w-full h-full ${objectFit === 'cover' ? 'object-cover' : 'object-contain'}`, style: { objectPosition: 'center' }, loading: index === 0 ? 'eager' : 'lazy', draggable: false })))))),
|
|
247
|
+
showArrows && images.length > 1 && (React.createElement(React.Fragment, null,
|
|
248
|
+
React.createElement("button", { onClick: controls.prev, className: "absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors z-10", "aria-label": "Previous slide" },
|
|
487
249
|
React.createElement("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
|
|
488
250
|
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }))),
|
|
489
|
-
React.createElement("button", { onClick: controls.next,
|
|
251
|
+
React.createElement("button", { onClick: controls.next, className: "absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors z-10", "aria-label": "Next slide" },
|
|
490
252
|
React.createElement("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
|
|
491
253
|
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }))))),
|
|
492
|
-
showDots &&
|
|
254
|
+
showDots && images.length > 1 && (React.createElement("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2 flex gap-1.5 z-10" }, images.map((_, index) => (React.createElement("button", { key: index, onClick: () => controls.goTo(index), className: `w-2 h-2 rounded-full transition-colors ${index === state.currentIndex
|
|
493
255
|
? 'bg-white'
|
|
494
256
|
: 'bg-white/40 hover:bg-white/60'}`, "aria-label": `Go to slide ${index + 1}` }))))),
|
|
495
|
-
React.createElement("div", { className: "absolute inset-0 z-
|
|
257
|
+
React.createElement("div", { className: "absolute inset-0 z-20 pointer-events-none" }, children))));
|
|
496
258
|
}
|
|
497
259
|
/**
|
|
498
260
|
* Hook to access slideshow state and controls from within Slideshow children.
|
|
499
|
-
* Also aliased as useSlideshow for consistency with VideoPlayer.
|
|
500
261
|
*/
|
|
501
262
|
export function useSlideshowState() {
|
|
502
263
|
const context = useContext(SlideshowContext);
|
|
@@ -505,5 +266,5 @@ export function useSlideshowState() {
|
|
|
505
266
|
}
|
|
506
267
|
return context;
|
|
507
268
|
}
|
|
508
|
-
// Alias for consistency
|
|
269
|
+
// Alias for consistency
|
|
509
270
|
export const useSlideshow = useSlideshowState;
|
package/dist/spec/schema.d.ts
CHANGED
|
@@ -103,12 +103,12 @@ declare const PlacementSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
103
103
|
align: z.ZodOptional<z.ZodEnum<["left", "center", "right"]>>;
|
|
104
104
|
}, "strip", z.ZodTypeAny, {
|
|
105
105
|
lane: "center" | "bottom" | "top";
|
|
106
|
-
inset?: number | undefined;
|
|
107
106
|
align?: "center" | "left" | "right" | undefined;
|
|
107
|
+
inset?: number | undefined;
|
|
108
108
|
}, {
|
|
109
109
|
lane: "center" | "bottom" | "top";
|
|
110
|
-
inset?: number | undefined;
|
|
111
110
|
align?: "center" | "left" | "right" | undefined;
|
|
111
|
+
inset?: number | undefined;
|
|
112
112
|
}>, z.ZodObject<{
|
|
113
113
|
type: z.ZodEnum<["fullscreen", "inset", "positioned"]>;
|
|
114
114
|
insets: z.ZodOptional<z.ZodObject<{
|
|
@@ -181,12 +181,12 @@ declare const OverlayCueSchema: z.ZodObject<{
|
|
|
181
181
|
align: z.ZodOptional<z.ZodEnum<["left", "center", "right"]>>;
|
|
182
182
|
}, "strip", z.ZodTypeAny, {
|
|
183
183
|
lane: "center" | "bottom" | "top";
|
|
184
|
-
inset?: number | undefined;
|
|
185
184
|
align?: "center" | "left" | "right" | undefined;
|
|
185
|
+
inset?: number | undefined;
|
|
186
186
|
}, {
|
|
187
187
|
lane: "center" | "bottom" | "top";
|
|
188
|
-
inset?: number | undefined;
|
|
189
188
|
align?: "center" | "left" | "right" | undefined;
|
|
189
|
+
inset?: number | undefined;
|
|
190
190
|
}>, z.ZodObject<{
|
|
191
191
|
type: z.ZodEnum<["fullscreen", "inset", "positioned"]>;
|
|
192
192
|
insets: z.ZodOptional<z.ZodObject<{
|
|
@@ -264,8 +264,8 @@ declare const OverlayCueSchema: z.ZodObject<{
|
|
|
264
264
|
};
|
|
265
265
|
placement: {
|
|
266
266
|
lane: "center" | "bottom" | "top";
|
|
267
|
-
inset?: number | undefined;
|
|
268
267
|
align?: "center" | "left" | "right" | undefined;
|
|
268
|
+
inset?: number | undefined;
|
|
269
269
|
} | {
|
|
270
270
|
type: "inset" | "fullscreen" | "positioned";
|
|
271
271
|
position?: {
|
|
@@ -295,8 +295,8 @@ declare const OverlayCueSchema: z.ZodObject<{
|
|
|
295
295
|
};
|
|
296
296
|
placement: {
|
|
297
297
|
lane: "center" | "bottom" | "top";
|
|
298
|
-
inset?: number | undefined;
|
|
299
298
|
align?: "center" | "left" | "right" | undefined;
|
|
299
|
+
inset?: number | undefined;
|
|
300
300
|
} | {
|
|
301
301
|
type: "inset" | "fullscreen" | "positioned";
|
|
302
302
|
position?: {
|
|
@@ -462,12 +462,12 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
462
462
|
align: z.ZodOptional<z.ZodEnum<["left", "center", "right"]>>;
|
|
463
463
|
}, "strip", z.ZodTypeAny, {
|
|
464
464
|
lane: "center" | "bottom" | "top";
|
|
465
|
-
inset?: number | undefined;
|
|
466
465
|
align?: "center" | "left" | "right" | undefined;
|
|
466
|
+
inset?: number | undefined;
|
|
467
467
|
}, {
|
|
468
468
|
lane: "center" | "bottom" | "top";
|
|
469
|
-
inset?: number | undefined;
|
|
470
469
|
align?: "center" | "left" | "right" | undefined;
|
|
470
|
+
inset?: number | undefined;
|
|
471
471
|
}>, z.ZodObject<{
|
|
472
472
|
type: z.ZodEnum<["fullscreen", "inset", "positioned"]>;
|
|
473
473
|
insets: z.ZodOptional<z.ZodObject<{
|
|
@@ -545,8 +545,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
545
545
|
};
|
|
546
546
|
placement: {
|
|
547
547
|
lane: "center" | "bottom" | "top";
|
|
548
|
-
inset?: number | undefined;
|
|
549
548
|
align?: "center" | "left" | "right" | undefined;
|
|
549
|
+
inset?: number | undefined;
|
|
550
550
|
} | {
|
|
551
551
|
type: "inset" | "fullscreen" | "positioned";
|
|
552
552
|
position?: {
|
|
@@ -576,8 +576,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
576
576
|
};
|
|
577
577
|
placement: {
|
|
578
578
|
lane: "center" | "bottom" | "top";
|
|
579
|
-
inset?: number | undefined;
|
|
580
579
|
align?: "center" | "left" | "right" | undefined;
|
|
580
|
+
inset?: number | undefined;
|
|
581
581
|
} | {
|
|
582
582
|
type: "inset" | "fullscreen" | "positioned";
|
|
583
583
|
position?: {
|
|
@@ -609,8 +609,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
609
609
|
};
|
|
610
610
|
placement: {
|
|
611
611
|
lane: "center" | "bottom" | "top";
|
|
612
|
-
inset?: number | undefined;
|
|
613
612
|
align?: "center" | "left" | "right" | undefined;
|
|
613
|
+
inset?: number | undefined;
|
|
614
614
|
} | {
|
|
615
615
|
type: "inset" | "fullscreen" | "positioned";
|
|
616
616
|
position?: {
|
|
@@ -656,8 +656,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
656
656
|
};
|
|
657
657
|
placement: {
|
|
658
658
|
lane: "center" | "bottom" | "top";
|
|
659
|
-
inset?: number | undefined;
|
|
660
659
|
align?: "center" | "left" | "right" | undefined;
|
|
660
|
+
inset?: number | undefined;
|
|
661
661
|
} | {
|
|
662
662
|
type: "inset" | "fullscreen" | "positioned";
|
|
663
663
|
position?: {
|
|
@@ -741,8 +741,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
741
741
|
};
|
|
742
742
|
placement: {
|
|
743
743
|
lane: "center" | "bottom" | "top";
|
|
744
|
-
inset?: number | undefined;
|
|
745
744
|
align?: "center" | "left" | "right" | undefined;
|
|
745
|
+
inset?: number | undefined;
|
|
746
746
|
} | {
|
|
747
747
|
type: "inset" | "fullscreen" | "positioned";
|
|
748
748
|
position?: {
|
|
@@ -820,8 +820,8 @@ export declare const OverlaySpecSchema: z.ZodObject<{
|
|
|
820
820
|
};
|
|
821
821
|
placement: {
|
|
822
822
|
lane: "center" | "bottom" | "top";
|
|
823
|
-
inset?: number | undefined;
|
|
824
823
|
align?: "center" | "left" | "right" | undefined;
|
|
824
|
+
inset?: number | undefined;
|
|
825
825
|
} | {
|
|
826
826
|
type: "inset" | "fullscreen" | "positioned";
|
|
827
827
|
position?: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thewhateverapp/tile-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"license": "MIT",
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@thewhateverapp/scene-sdk": "^0.1.1",
|
|
68
|
+
"embla-carousel-react": "^8.5.1",
|
|
68
69
|
"matter-js": "^0.19.0",
|
|
69
70
|
"zod": "^3.22.0"
|
|
70
71
|
},
|