lazer-slider 1.0.3 → 1.0.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/README.md +457 -0
- package/dist/index.cjs +10 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# Lazer Slider
|
|
2
|
+
|
|
3
|
+
A lightweight, accessible slider with smooth scroll-to-snap animations, drag-to-scroll support, and full keyboard navigation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Smooth Animations** - Buttery smooth scroll animations using `requestAnimationFrame` with customizable easing
|
|
8
|
+
- **Drag to Scroll** - Mouse and touch drag support with momentum physics
|
|
9
|
+
- **Responsive** - Separate settings for mobile and desktop (slidesPerView, slidesPerScroll)
|
|
10
|
+
- **Loop Mode** - Infinite loop navigation
|
|
11
|
+
- **Autoplay** - Automatic slide advancement with pause on hover
|
|
12
|
+
- **Accessible** - Full ARIA support, keyboard navigation (arrow keys)
|
|
13
|
+
- **Lightweight** - Zero dependencies, ~20KB unminified
|
|
14
|
+
- **TypeScript** - Full type definitions included
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install lazer-slider
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### HTML Structure
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<div class="slider">
|
|
28
|
+
<button class="slider-prev">Previous</button>
|
|
29
|
+
<button class="slider-next">Next</button>
|
|
30
|
+
|
|
31
|
+
<div class="slider-feed">
|
|
32
|
+
<div class="slide">Slide 1</div>
|
|
33
|
+
<div class="slide">Slide 2</div>
|
|
34
|
+
<div class="slide">Slide 3</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div class="slider-dots">
|
|
38
|
+
<button class="dot"></button>
|
|
39
|
+
<button class="dot"></button>
|
|
40
|
+
<button class="dot"></button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### CSS (Required)
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
.slider-feed {
|
|
49
|
+
display: flex;
|
|
50
|
+
overflow-x: auto;
|
|
51
|
+
scroll-snap-type: x mandatory;
|
|
52
|
+
-webkit-overflow-scrolling: touch;
|
|
53
|
+
scrollbar-width: none; /* Firefox */
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.slider-feed::-webkit-scrollbar {
|
|
57
|
+
display: none; /* Chrome, Safari */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.slide {
|
|
61
|
+
scroll-snap-align: start;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### JavaScript
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { createSlider } from 'lazer-slider'
|
|
70
|
+
|
|
71
|
+
const slider = createSlider({
|
|
72
|
+
feed: document.querySelector('.slider-feed'),
|
|
73
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
74
|
+
prevSlideButton: document.querySelector('.slider-prev'),
|
|
75
|
+
nextSlideButton: document.querySelector('.slider-next'),
|
|
76
|
+
thumbs: [...document.querySelectorAll('.dot')],
|
|
77
|
+
enableDragToScroll: true
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Cleanup when done
|
|
81
|
+
slider.unload()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration Options
|
|
85
|
+
|
|
86
|
+
### Required
|
|
87
|
+
|
|
88
|
+
| Option | Type | Description |
|
|
89
|
+
|--------|------|-------------|
|
|
90
|
+
| `feed` | `HTMLElement` | Container element that holds the slides (scrollable) |
|
|
91
|
+
| `slides` | `HTMLElement[]` | Array of slide elements |
|
|
92
|
+
|
|
93
|
+
### Navigation
|
|
94
|
+
|
|
95
|
+
| Option | Type | Default | Description |
|
|
96
|
+
|--------|------|---------|-------------|
|
|
97
|
+
| `prevSlideButton` | `HTMLElement \| null` | `null` | Previous slide button |
|
|
98
|
+
| `nextSlideButton` | `HTMLElement \| null` | `null` | Next slide button |
|
|
99
|
+
| `thumbs` | `HTMLElement[]` | `undefined` | Thumbnail/dot elements for direct navigation |
|
|
100
|
+
|
|
101
|
+
### Responsive
|
|
102
|
+
|
|
103
|
+
| Option | Type | Default | Description |
|
|
104
|
+
|--------|------|---------|-------------|
|
|
105
|
+
| `mobileSlidesPerView` | `number \| 'auto'` | `undefined` | Slides visible at once on mobile |
|
|
106
|
+
| `desktopSlidesPerView` | `number \| 'auto'` | `undefined` | Slides visible at once on desktop |
|
|
107
|
+
| `mobileSlidesPerScroll` | `number` | `1` | Slides to scroll per nav click on mobile |
|
|
108
|
+
| `desktopSlidesPerScroll` | `number` | `1` | Slides to scroll per nav click on desktop |
|
|
109
|
+
| `slideGap` | `number` | `0` | Gap between slides in pixels |
|
|
110
|
+
|
|
111
|
+
> **Note:** Desktop breakpoint is `min-width: 64rem` (1024px)
|
|
112
|
+
|
|
113
|
+
> **Tip:** Use `'auto'` for `slidesPerView` when slides have different/natural widths defined in CSS.
|
|
114
|
+
|
|
115
|
+
### Behavior
|
|
116
|
+
|
|
117
|
+
| Option | Type | Default | Description |
|
|
118
|
+
|--------|------|---------|-------------|
|
|
119
|
+
| `enableDragToScroll` | `boolean` | `false` | Enable mouse/touch drag to scroll |
|
|
120
|
+
| `loop` | `boolean` | `false` | Enable infinite loop navigation |
|
|
121
|
+
| `autoplay` | `boolean` | `false` | Enable automatic slide advancement |
|
|
122
|
+
| `autoplayInterval` | `number` | `3000` | Autoplay interval in milliseconds |
|
|
123
|
+
| `pauseOnHover` | `boolean` | `true` | Pause autoplay on hover/touch |
|
|
124
|
+
| `easing` | `EasingFunction` | `easeOutExpo` | Custom easing function |
|
|
125
|
+
|
|
126
|
+
### Scrollbar (Optional)
|
|
127
|
+
|
|
128
|
+
| Option | Type | Default | Description |
|
|
129
|
+
|--------|------|---------|-------------|
|
|
130
|
+
| `scrollbarThumb` | `HTMLElement \| null` | `null` | Custom scrollbar thumb element |
|
|
131
|
+
| `scrollbarTrack` | `HTMLElement \| null` | `null` | Custom scrollbar track element |
|
|
132
|
+
|
|
133
|
+
### Callbacks
|
|
134
|
+
|
|
135
|
+
| Option | Type | Description |
|
|
136
|
+
|--------|------|-------------|
|
|
137
|
+
| `onScrollStart` | `(params) => void` | Fired when scroll animation starts |
|
|
138
|
+
| `onScroll` | `(params) => void` | Fired during scroll |
|
|
139
|
+
| `onScrollEnd` | `(params) => void` | Fired when scroll animation ends |
|
|
140
|
+
|
|
141
|
+
#### Callback Parameters
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// onScrollStart
|
|
145
|
+
{
|
|
146
|
+
currentScroll: number // Current scroll position in pixels
|
|
147
|
+
target: HTMLElement // Target slide element
|
|
148
|
+
direction: 'prev' | 'next'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// onScroll, onScrollEnd
|
|
152
|
+
{
|
|
153
|
+
currentScroll: number // Current scroll position in pixels
|
|
154
|
+
currentSlideIndex: number // Index of the current slide
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## API Methods
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const slider = createSlider({ /* options */ })
|
|
162
|
+
|
|
163
|
+
// Navigate to specific slide (0-indexed)
|
|
164
|
+
slider.goToIndex(2)
|
|
165
|
+
|
|
166
|
+
// Navigate prev/next (respects loop setting)
|
|
167
|
+
slider.next()
|
|
168
|
+
slider.prev()
|
|
169
|
+
|
|
170
|
+
// Autoplay control
|
|
171
|
+
slider.play() // Start autoplay
|
|
172
|
+
slider.pause() // Stop autoplay
|
|
173
|
+
|
|
174
|
+
// Recalculate dimensions (call after DOM changes)
|
|
175
|
+
slider.refresh()
|
|
176
|
+
|
|
177
|
+
// Cleanup all event listeners
|
|
178
|
+
slider.unload()
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Easing Functions
|
|
182
|
+
|
|
183
|
+
Built-in easing functions for smooth animations:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import {
|
|
187
|
+
createSlider,
|
|
188
|
+
easeOutExpo, // Default - smooth deceleration
|
|
189
|
+
easeOutCubic, // Gentler deceleration
|
|
190
|
+
easeInOutCubic, // Smooth acceleration & deceleration
|
|
191
|
+
easeOutQuad, // Gentle deceleration
|
|
192
|
+
linear // Constant speed
|
|
193
|
+
} from 'lazer-slider'
|
|
194
|
+
|
|
195
|
+
const slider = createSlider({
|
|
196
|
+
// ... options
|
|
197
|
+
easing: easeOutCubic
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Custom Easing
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// Custom easing function (t goes from 0 to 1)
|
|
205
|
+
const customEase = (t: number) => t * t * t
|
|
206
|
+
|
|
207
|
+
const slider = createSlider({
|
|
208
|
+
// ... options
|
|
209
|
+
easing: customEase
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Examples
|
|
214
|
+
|
|
215
|
+
### Basic Slider
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const slider = createSlider({
|
|
219
|
+
feed: document.querySelector('.slider-feed'),
|
|
220
|
+
slides: [...document.querySelectorAll('.slide')]
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### With Navigation Buttons
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const slider = createSlider({
|
|
228
|
+
feed: document.querySelector('.slider-feed'),
|
|
229
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
230
|
+
prevSlideButton: document.querySelector('.prev'),
|
|
231
|
+
nextSlideButton: document.querySelector('.next')
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Responsive Multi-Slide
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const slider = createSlider({
|
|
239
|
+
feed: document.querySelector('.slider-feed'),
|
|
240
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
241
|
+
mobileSlidesPerView: 1,
|
|
242
|
+
desktopSlidesPerView: 3,
|
|
243
|
+
slideGap: 16,
|
|
244
|
+
mobileSlidesPerScroll: 1,
|
|
245
|
+
desktopSlidesPerScroll: 3
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Autoplay Carousel
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
const slider = createSlider({
|
|
253
|
+
feed: document.querySelector('.slider-feed'),
|
|
254
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
255
|
+
loop: true,
|
|
256
|
+
autoplay: true,
|
|
257
|
+
autoplayInterval: 5000,
|
|
258
|
+
pauseOnHover: true
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Bullets/Dots Navigation
|
|
263
|
+
|
|
264
|
+
Use the `thumbs` option to add clickable dots that navigate to specific slides. The slider automatically manages the `active` class.
|
|
265
|
+
|
|
266
|
+
```html
|
|
267
|
+
<div class="slider">
|
|
268
|
+
<div class="slider-feed">
|
|
269
|
+
<div class="slide">Slide 1</div>
|
|
270
|
+
<div class="slide">Slide 2</div>
|
|
271
|
+
<div class="slide">Slide 3</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div class="slider-dots">
|
|
275
|
+
<button class="dot" aria-label="Go to slide 1"></button>
|
|
276
|
+
<button class="dot" aria-label="Go to slide 2"></button>
|
|
277
|
+
<button class="dot" aria-label="Go to slide 3"></button>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```css
|
|
283
|
+
.slider-dots {
|
|
284
|
+
display: flex;
|
|
285
|
+
gap: 8px;
|
|
286
|
+
justify-content: center;
|
|
287
|
+
margin-top: 16px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.dot {
|
|
291
|
+
width: 12px;
|
|
292
|
+
height: 12px;
|
|
293
|
+
border-radius: 50%;
|
|
294
|
+
border: none;
|
|
295
|
+
background: #ccc;
|
|
296
|
+
cursor: pointer;
|
|
297
|
+
transition: background 0.3s;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.dot.active {
|
|
301
|
+
background: #333;
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const slider = createSlider({
|
|
307
|
+
feed: document.querySelector('.slider-feed'),
|
|
308
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
309
|
+
thumbs: [...document.querySelectorAll('.dot')]
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Custom Scrollbar
|
|
314
|
+
|
|
315
|
+
Create a draggable scrollbar that syncs with the slider position.
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<div class="slider">
|
|
319
|
+
<div class="slider-feed">
|
|
320
|
+
<div class="slide">Slide 1</div>
|
|
321
|
+
<div class="slide">Slide 2</div>
|
|
322
|
+
<div class="slide">Slide 3</div>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<div class="scrollbar-track">
|
|
326
|
+
<div class="scrollbar-thumb"></div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```css
|
|
332
|
+
.scrollbar-track {
|
|
333
|
+
width: 100%;
|
|
334
|
+
height: 4px;
|
|
335
|
+
background: #eee;
|
|
336
|
+
border-radius: 2px;
|
|
337
|
+
margin-top: 16px;
|
|
338
|
+
position: relative;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.scrollbar-thumb {
|
|
342
|
+
height: 100%;
|
|
343
|
+
background: #333;
|
|
344
|
+
border-radius: 2px;
|
|
345
|
+
/* Width is set automatically by the slider */
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
const slider = createSlider({
|
|
351
|
+
feed: document.querySelector('.slider-feed'),
|
|
352
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
353
|
+
scrollbarThumb: document.querySelector('.scrollbar-thumb'),
|
|
354
|
+
scrollbarTrack: document.querySelector('.scrollbar-track')
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The slider automatically:
|
|
359
|
+
- Sets the thumb width based on visible content ratio
|
|
360
|
+
- Updates thumb position on scroll
|
|
361
|
+
- Hides the scrollbar when all content is visible
|
|
362
|
+
|
|
363
|
+
### Auto Width Slides
|
|
364
|
+
|
|
365
|
+
Use `slidesPerView: 'auto'` when slides have different natural widths defined in CSS.
|
|
366
|
+
|
|
367
|
+
```html
|
|
368
|
+
<div class="slider-feed">
|
|
369
|
+
<div class="slide slide--small">Small</div>
|
|
370
|
+
<div class="slide slide--medium">Medium</div>
|
|
371
|
+
<div class="slide slide--large">Large content here</div>
|
|
372
|
+
</div>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
```css
|
|
376
|
+
.slide--small { width: 150px; }
|
|
377
|
+
.slide--medium { width: 250px; }
|
|
378
|
+
.slide--large { width: 400px; }
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
const slider = createSlider({
|
|
383
|
+
feed: document.querySelector('.slider-feed'),
|
|
384
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
385
|
+
mobileSlidesPerView: 'auto',
|
|
386
|
+
desktopSlidesPerView: 'auto',
|
|
387
|
+
slideGap: 16,
|
|
388
|
+
enableDragToScroll: true
|
|
389
|
+
})
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Full Featured
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
const slider = createSlider({
|
|
396
|
+
feed: document.querySelector('.slider-feed'),
|
|
397
|
+
slides: [...document.querySelectorAll('.slide')],
|
|
398
|
+
prevSlideButton: document.querySelector('.prev'),
|
|
399
|
+
nextSlideButton: document.querySelector('.next'),
|
|
400
|
+
thumbs: [...document.querySelectorAll('.dot')],
|
|
401
|
+
|
|
402
|
+
// Responsive
|
|
403
|
+
mobileSlidesPerView: 1,
|
|
404
|
+
desktopSlidesPerView: 3,
|
|
405
|
+
slideGap: 20,
|
|
406
|
+
|
|
407
|
+
// Behavior
|
|
408
|
+
enableDragToScroll: true,
|
|
409
|
+
loop: true,
|
|
410
|
+
autoplay: true,
|
|
411
|
+
autoplayInterval: 4000,
|
|
412
|
+
|
|
413
|
+
// Callbacks
|
|
414
|
+
onScrollEnd: ({ currentSlideIndex }) => {
|
|
415
|
+
console.log('Now viewing slide:', currentSlideIndex)
|
|
416
|
+
}
|
|
417
|
+
})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Accessibility
|
|
421
|
+
|
|
422
|
+
The slider automatically adds ARIA attributes for accessibility:
|
|
423
|
+
|
|
424
|
+
- `role="region"` and `aria-roledescription="carousel"` on the feed
|
|
425
|
+
- `role="group"` and `aria-label="Slide X of Y"` on each slide
|
|
426
|
+
- `aria-controls` linking buttons to the feed
|
|
427
|
+
- `aria-hidden` and `tabindex` management for visibility
|
|
428
|
+
- Keyboard navigation with arrow keys when feed is focused
|
|
429
|
+
|
|
430
|
+
## TypeScript
|
|
431
|
+
|
|
432
|
+
Full TypeScript support with exported types:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import {
|
|
436
|
+
createSlider,
|
|
437
|
+
type SliderSettings,
|
|
438
|
+
type Slider,
|
|
439
|
+
type EasingFunction,
|
|
440
|
+
type ScrollParams,
|
|
441
|
+
type ScrollStartParams,
|
|
442
|
+
type SliderDirection
|
|
443
|
+
} from 'lazer-slider'
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Browser Support
|
|
447
|
+
|
|
448
|
+
- Chrome (latest)
|
|
449
|
+
- Firefox (latest)
|
|
450
|
+
- Safari (latest)
|
|
451
|
+
- Edge (latest)
|
|
452
|
+
|
|
453
|
+
Requires `scroll-snap-type` CSS support for snap behavior.
|
|
454
|
+
|
|
455
|
+
## License
|
|
456
|
+
|
|
457
|
+
UNLICENSED
|
package/dist/index.cjs
CHANGED
|
@@ -311,17 +311,23 @@ var createSlider = (settings) => {
|
|
|
311
311
|
};
|
|
312
312
|
const applySlideWidths = () => {
|
|
313
313
|
const perView = isDesktop() ? settings.desktopSlidesPerView : settings.mobileSlidesPerView;
|
|
314
|
-
if (!perView) return;
|
|
315
314
|
const gap = settings.slideGap ?? 0;
|
|
315
|
+
if (gap > 0) {
|
|
316
|
+
settings.feed.style.gap = `${gap}px`;
|
|
317
|
+
}
|
|
318
|
+
if (!perView || perView === "auto") {
|
|
319
|
+
settings.slides.forEach((slide) => {
|
|
320
|
+
slide.style.flex = "";
|
|
321
|
+
slide.style.minWidth = "";
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
316
325
|
const totalGapWidth = gap * (perView - 1);
|
|
317
326
|
const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`;
|
|
318
327
|
settings.slides.forEach((slide) => {
|
|
319
328
|
slide.style.flex = `0 0 ${slideWidth}`;
|
|
320
329
|
slide.style.minWidth = slideWidth;
|
|
321
330
|
});
|
|
322
|
-
if (gap > 0) {
|
|
323
|
-
settings.feed.style.gap = `${gap}px`;
|
|
324
|
-
}
|
|
325
331
|
};
|
|
326
332
|
const updateScrollbar = () => {
|
|
327
333
|
if (!settings.scrollbarThumb) return;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/slider.ts"],"sourcesContent":["// Main slider factory\nexport { createSlider } from './core/slider.js'\n\n// Types\nexport type {\n SliderSettings,\n SliderState,\n SliderDirection,\n ScrollStartParams,\n ScrollParams,\n EasingFunction,\n Slider,\n DragState\n} from './core/types.js'\n\n// Easing functions\nexport {\n easeOutExpo,\n easeOutCubic,\n easeInOutCubic,\n easeOutQuad,\n linear\n} from './core/easing.js'\n","import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n if (!perView) return\n\n const gap = settings.slideGap ?? 0\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n const allVisibleSlides = getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = settings.slides.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = allVisibleSlides.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const visibleSlides = getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalSlides = visibleSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n // Calculate new index with loop support\n if (direction === 'prev') {\n if (settings.loop && state.currentSlideIndex === 0) {\n // Loop to end\n state.currentSlideIndex = totalSlides - 1\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n }\n } else {\n if (settings.loop && state.currentSlideIndex >= totalSlides - 1) {\n // Loop to start\n state.currentSlideIndex = 0\n } else {\n state.currentSlideIndex = Math.min(\n totalSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n }\n }\n\n const targetSlide = visibleSlides[state.currentSlideIndex]\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n const visibleSlides = getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, visibleSlides.length - 1))\n const targetSlide = visibleSlides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay\n stopAutoplay()\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Start autoplay if enabled\n if (settings.autoplay) {\n startAutoplay()\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play: startAutoplay,\n pause: stopAutoplay,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/slider.ts"],"sourcesContent":["// Main slider factory\nexport { createSlider } from './core/slider.js'\n\n// Types\nexport type {\n SliderSettings,\n SliderState,\n SliderDirection,\n ScrollStartParams,\n ScrollParams,\n EasingFunction,\n Slider,\n DragState\n} from './core/types.js'\n\n// Easing functions\nexport {\n easeOutExpo,\n easeOutCubic,\n easeInOutCubic,\n easeOutQuad,\n linear\n} from './core/easing.js'\n","import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n const allVisibleSlides = getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = settings.slides.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = allVisibleSlides.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const visibleSlides = getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalSlides = visibleSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n // Calculate new index with loop support\n if (direction === 'prev') {\n if (settings.loop && state.currentSlideIndex === 0) {\n // Loop to end\n state.currentSlideIndex = totalSlides - 1\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n }\n } else {\n if (settings.loop && state.currentSlideIndex >= totalSlides - 1) {\n // Loop to start\n state.currentSlideIndex = 0\n } else {\n state.currentSlideIndex = Math.min(\n totalSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n }\n }\n\n const targetSlide = visibleSlides[state.currentSlideIndex]\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n const visibleSlides = getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, visibleSlides.length - 1))\n const targetSlide = visibleSlides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay\n stopAutoplay()\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Start autoplay if enabled\n if (settings.autoplay) {\n startAutoplay()\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play: startAutoplay,\n pause: stopAutoplay,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -48,10 +48,10 @@ interface SliderSettings {
|
|
|
48
48
|
mobileSlidesPerScroll?: number;
|
|
49
49
|
/** Number of slides to scroll on desktop (default: 1) */
|
|
50
50
|
desktopSlidesPerScroll?: number;
|
|
51
|
-
/** Number of slides visible at once on mobile */
|
|
52
|
-
mobileSlidesPerView?: number;
|
|
53
|
-
/** Number of slides visible at once on desktop */
|
|
54
|
-
desktopSlidesPerView?: number;
|
|
51
|
+
/** Number of slides visible at once on mobile (number or 'auto' for natural width) */
|
|
52
|
+
mobileSlidesPerView?: number | 'auto';
|
|
53
|
+
/** Number of slides visible at once on desktop (number or 'auto' for natural width) */
|
|
54
|
+
desktopSlidesPerView?: number | 'auto';
|
|
55
55
|
/** Gap between slides in pixels (used with slidesPerView) */
|
|
56
56
|
slideGap?: number;
|
|
57
57
|
/** Enable mouse/touch drag to scroll (default: false) */
|
package/dist/index.d.ts
CHANGED
|
@@ -48,10 +48,10 @@ interface SliderSettings {
|
|
|
48
48
|
mobileSlidesPerScroll?: number;
|
|
49
49
|
/** Number of slides to scroll on desktop (default: 1) */
|
|
50
50
|
desktopSlidesPerScroll?: number;
|
|
51
|
-
/** Number of slides visible at once on mobile */
|
|
52
|
-
mobileSlidesPerView?: number;
|
|
53
|
-
/** Number of slides visible at once on desktop */
|
|
54
|
-
desktopSlidesPerView?: number;
|
|
51
|
+
/** Number of slides visible at once on mobile (number or 'auto' for natural width) */
|
|
52
|
+
mobileSlidesPerView?: number | 'auto';
|
|
53
|
+
/** Number of slides visible at once on desktop (number or 'auto' for natural width) */
|
|
54
|
+
desktopSlidesPerView?: number | 'auto';
|
|
55
55
|
/** Gap between slides in pixels (used with slidesPerView) */
|
|
56
56
|
slideGap?: number;
|
|
57
57
|
/** Enable mouse/touch drag to scroll (default: false) */
|
package/dist/index.js
CHANGED
|
@@ -280,17 +280,23 @@ var createSlider = (settings) => {
|
|
|
280
280
|
};
|
|
281
281
|
const applySlideWidths = () => {
|
|
282
282
|
const perView = isDesktop() ? settings.desktopSlidesPerView : settings.mobileSlidesPerView;
|
|
283
|
-
if (!perView) return;
|
|
284
283
|
const gap = settings.slideGap ?? 0;
|
|
284
|
+
if (gap > 0) {
|
|
285
|
+
settings.feed.style.gap = `${gap}px`;
|
|
286
|
+
}
|
|
287
|
+
if (!perView || perView === "auto") {
|
|
288
|
+
settings.slides.forEach((slide) => {
|
|
289
|
+
slide.style.flex = "";
|
|
290
|
+
slide.style.minWidth = "";
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
285
294
|
const totalGapWidth = gap * (perView - 1);
|
|
286
295
|
const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`;
|
|
287
296
|
settings.slides.forEach((slide) => {
|
|
288
297
|
slide.style.flex = `0 0 ${slideWidth}`;
|
|
289
298
|
slide.style.minWidth = slideWidth;
|
|
290
299
|
});
|
|
291
|
-
if (gap > 0) {
|
|
292
|
-
settings.feed.style.gap = `${gap}px`;
|
|
293
|
-
}
|
|
294
300
|
};
|
|
295
301
|
const updateScrollbar = () => {
|
|
296
302
|
if (!settings.scrollbarThumb) return;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/slider.ts"],"sourcesContent":["import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n if (!perView) return\n\n const gap = settings.slideGap ?? 0\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n const allVisibleSlides = getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = settings.slides.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = allVisibleSlides.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const visibleSlides = getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalSlides = visibleSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n // Calculate new index with loop support\n if (direction === 'prev') {\n if (settings.loop && state.currentSlideIndex === 0) {\n // Loop to end\n state.currentSlideIndex = totalSlides - 1\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n }\n } else {\n if (settings.loop && state.currentSlideIndex >= totalSlides - 1) {\n // Loop to start\n state.currentSlideIndex = 0\n } else {\n state.currentSlideIndex = Math.min(\n totalSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n }\n }\n\n const targetSlide = visibleSlides[state.currentSlideIndex]\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n const visibleSlides = getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, visibleSlides.length - 1))\n const targetSlide = visibleSlides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay\n stopAutoplay()\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Start autoplay if enabled\n if (settings.autoplay) {\n startAutoplay()\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play: startAutoplay,\n pause: stopAutoplay,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/slider.ts"],"sourcesContent":["import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n const allVisibleSlides = getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = settings.slides.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = allVisibleSlides.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const visibleSlides = getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalSlides = visibleSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n // Calculate new index with loop support\n if (direction === 'prev') {\n if (settings.loop && state.currentSlideIndex === 0) {\n // Loop to end\n state.currentSlideIndex = totalSlides - 1\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n }\n } else {\n if (settings.loop && state.currentSlideIndex >= totalSlides - 1) {\n // Loop to start\n state.currentSlideIndex = 0\n } else {\n state.currentSlideIndex = Math.min(\n totalSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n }\n }\n\n const targetSlide = visibleSlides[state.currentSlideIndex]\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n const visibleSlides = getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, visibleSlides.length - 1))\n const targetSlide = visibleSlides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay\n stopAutoplay()\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Start autoplay if enabled\n if (settings.autoplay) {\n startAutoplay()\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play: startAutoplay,\n pause: stopAutoplay,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED