@evermade/overflow-slider 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -8
- package/dist/core/details.esm.js +5 -5
- package/dist/core/details.min.js +1 -1
- package/dist/core/overflow-slider.esm.js +1 -0
- package/dist/core/overflow-slider.min.js +1 -1
- package/dist/core/slider.esm.js +113 -44
- package/dist/core/slider.min.js +1 -1
- package/dist/core/utils.esm.js +15 -1
- package/dist/core/utils.min.js +1 -1
- package/dist/overflow-slider.css +1 -1
- package/dist/plugins/arrows/arrows/index.esm.js +20 -10
- package/dist/plugins/arrows/arrows/index.min.js +1 -1
- package/dist/plugins/dots/dots/index.esm.js +0 -4
- package/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +1 -1
- package/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
- package/dist/plugins/fade/fade/index.esm.js +7 -7
- package/dist/plugins/fade/fade/index.min.js +1 -1
- package/dist/plugins/full-width/full-width/index.esm.js +7 -2
- package/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +22 -22
- package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/dist/plugins/thumbnails/thumbnails/index.esm.js +7 -4
- package/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/assets/demo.css +11 -1
- package/docs/assets/demo.js +38 -12
- package/docs/dist/core/details.esm.js +5 -5
- package/docs/dist/core/details.min.js +1 -1
- package/docs/dist/core/overflow-slider.esm.js +1 -0
- package/docs/dist/core/overflow-slider.min.js +1 -1
- package/docs/dist/core/slider.esm.js +113 -44
- package/docs/dist/core/slider.min.js +1 -1
- package/docs/dist/core/utils.esm.js +15 -1
- package/docs/dist/core/utils.min.js +1 -1
- package/docs/dist/overflow-slider.css +1 -1
- package/docs/dist/plugins/arrows/arrows/index.esm.js +20 -10
- package/docs/dist/plugins/arrows/arrows/index.min.js +1 -1
- package/docs/dist/plugins/dots/dots/index.esm.js +0 -4
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +1 -1
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
- package/docs/dist/plugins/fade/fade/index.esm.js +7 -7
- package/docs/dist/plugins/fade/fade/index.min.js +1 -1
- package/docs/dist/plugins/full-width/full-width/index.esm.js +7 -2
- package/docs/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +22 -22
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +7 -4
- package/docs/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/index-rtl.html +396 -0
- package/docs/index.html +1 -1
- package/package.json +1 -1
- package/src/core/details.ts +5 -5
- package/src/core/overflow-slider.ts +1 -0
- package/src/core/slider.ts +125 -47
- package/src/core/types.ts +5 -0
- package/src/core/utils.ts +19 -1
- package/src/plugins/arrows/index.ts +20 -10
- package/src/plugins/dots/index.ts +0 -4
- package/src/plugins/drag-scrolling/index.ts +1 -1
- package/src/plugins/fade/index.ts +7 -8
- package/src/plugins/fade/styles.scss +10 -0
- package/src/plugins/full-width/index.ts +8 -2
- package/src/plugins/scroll-indicator/index.ts +62 -64
- package/src/plugins/thumbnails/index.ts +7 -5
package/src/core/slider.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Slider, SliderOptions, SliderPlugin } from './types';
|
|
2
2
|
import details from './details';
|
|
3
|
-
import { generateId, objectsAreEqual } from './utils';
|
|
3
|
+
import { generateId, objectsAreEqual, getOutermostChildrenEdgeMarginSum } from './utils';
|
|
4
4
|
|
|
5
5
|
export default function Slider( container: HTMLElement, options : SliderOptions, plugins? : SliderPlugin[] ) {
|
|
6
6
|
let slider: Slider;
|
|
@@ -94,7 +94,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
94
94
|
// any scroll
|
|
95
95
|
slider.container.addEventListener('scroll', () => {
|
|
96
96
|
const newScrollLeft = slider.container.scrollLeft;
|
|
97
|
-
if (scrollLeft !== newScrollLeft) {
|
|
97
|
+
if ( Math.floor( scrollLeft ) !== Math.floor( newScrollLeft ) ) {
|
|
98
98
|
if (!isScrolling) {
|
|
99
99
|
isScrolling = true;
|
|
100
100
|
slider.emit('scrollStart');
|
|
@@ -116,7 +116,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
116
116
|
// user initted scroll (touchmove, mouse wheel, etc.)
|
|
117
117
|
const nativeScrollHandler = () => {
|
|
118
118
|
const newScrollLeft = slider.container.scrollLeft;
|
|
119
|
-
if (nativeScrollLeft !== newScrollLeft && !isProgrammaticScrolling) {
|
|
119
|
+
if ( Math.floor( nativeScrollLeft ) !== Math.floor( newScrollLeft ) && ! isProgrammaticScrolling ) {
|
|
120
120
|
if (!isUserScrolling) {
|
|
121
121
|
slider.emit('nativeScrollStart');
|
|
122
122
|
isUserScrolling = true;
|
|
@@ -145,7 +145,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
145
145
|
|
|
146
146
|
slider.container.addEventListener('scroll', () => {
|
|
147
147
|
const newScrollLeft = slider.container.scrollLeft;
|
|
148
|
-
if (programmaticScrollLeft !== newScrollLeft && !isUserScrolling && isProgrammaticScrolling) {
|
|
148
|
+
if ( Math.floor( programmaticScrollLeft ) !== Math.floor( newScrollLeft ) && !isUserScrolling && isProgrammaticScrolling) {
|
|
149
149
|
programmaticScrollLeft = newScrollLeft;
|
|
150
150
|
clearTimeout(programmaticScrollTimeout);
|
|
151
151
|
programmaticScrollTimeout = setTimeout(() => {
|
|
@@ -207,6 +207,9 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
207
207
|
|
|
208
208
|
function setDataAttributes() {
|
|
209
209
|
slider.container.setAttribute('data-has-overflow', slider.details.hasOverflow ? 'true' : 'false');
|
|
210
|
+
if ( slider.options.rtl ) {
|
|
211
|
+
slider.container.setAttribute('dir', 'rtl');
|
|
212
|
+
}
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
function ensureSlideIsInView( slide: HTMLElement, scrollBehavior: null|ScrollBehavior = null) {
|
|
@@ -218,11 +221,11 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
218
221
|
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
219
222
|
const slideEnd = slideStart + slideRect.width;
|
|
220
223
|
let scrollTarget = null;
|
|
221
|
-
if (slideStart < scrollLeft) {
|
|
224
|
+
if ( Math.floor( slideStart ) < Math.floor( scrollLeft ) ) {
|
|
222
225
|
scrollTarget = slideStart;
|
|
223
|
-
} else if (slideEnd > scrollLeft + containerWidth) {
|
|
226
|
+
} else if ( Math.floor( slideEnd ) > Math.floor( scrollLeft ) + Math.floor( containerWidth ) ) {
|
|
224
227
|
scrollTarget = slideEnd - containerWidth;
|
|
225
|
-
} else if (slideStart === 0) {
|
|
228
|
+
} else if ( Math.floor( slideStart ) === 0) {
|
|
226
229
|
scrollTarget = 0;
|
|
227
230
|
} else {
|
|
228
231
|
scrollTarget = slideStart;
|
|
@@ -237,27 +240,66 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
237
240
|
|
|
238
241
|
function setActiveSlideIdx() {
|
|
239
242
|
const sliderRect = slider.container.getBoundingClientRect();
|
|
240
|
-
const scrollLeft = slider.
|
|
243
|
+
const scrollLeft = slider.getScrollLeft();
|
|
241
244
|
const slides = slider.slides;
|
|
242
245
|
let activeSlideIdx = 0;
|
|
246
|
+
let scrolledPastLastSlide = false;
|
|
247
|
+
|
|
248
|
+
if (slider.options.rtl) {
|
|
249
|
+
const scrolledDistance = slider.getInclusiveScrollWidth() - scrollLeft - slider.getInclusiveClientWidth();
|
|
250
|
+
const slidePositions = [];
|
|
251
|
+
for (let i = slides.length - 1; i >= 0; i--) {
|
|
252
|
+
const slideRect = slides[i].getBoundingClientRect();
|
|
253
|
+
const slideEnd = Math.abs(slideRect.left) - Math.abs(sliderRect.left) + scrolledDistance;
|
|
254
|
+
slidePositions.push({
|
|
255
|
+
slide: slides[i],
|
|
256
|
+
slideEnd: slideEnd,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
let closestSlide = null;
|
|
260
|
+
let closestDistance = null;
|
|
261
|
+
for (let i = 0; i < slidePositions.length; i++) {
|
|
262
|
+
const distance = Math.abs(slidePositions[i].slideEnd - scrolledDistance);
|
|
263
|
+
if (closestDistance === null || distance < closestDistance) {
|
|
264
|
+
closestDistance = distance;
|
|
265
|
+
closestSlide = slidePositions[i].slide;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (closestSlide) {
|
|
269
|
+
activeSlideIdx = slides.indexOf(closestSlide);
|
|
270
|
+
} else {
|
|
271
|
+
activeSlideIdx = slides.length - 1;
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
for (let i = 0; i < slides.length; i++) {
|
|
275
|
+
const slideRect = slides[i].getBoundingClientRect();
|
|
276
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
|
|
277
|
+
if (Math.floor(slideStart) >= Math.floor(scrollLeft)) {
|
|
278
|
+
activeSlideIdx = i;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
if ( i === slides.length - 1 ) {
|
|
282
|
+
scrolledPastLastSlide = true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
243
286
|
|
|
244
|
-
for (let i = 0; i < slides.length; i++) {
|
|
245
|
-
const slideRect = slides[i].getBoundingClientRect();
|
|
246
|
-
const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
|
|
247
287
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
288
|
+
if ( scrolledPastLastSlide ) {
|
|
289
|
+
activeSlideIdx = slides.length - 1;
|
|
252
290
|
}
|
|
253
291
|
|
|
254
292
|
const oldActiveSlideIdx = slider.activeSlideIdx;
|
|
255
293
|
slider.activeSlideIdx = activeSlideIdx;
|
|
294
|
+
|
|
295
|
+
// console.log('activeSlideIdx', activeSlideIdx);
|
|
296
|
+
|
|
256
297
|
if (oldActiveSlideIdx !== activeSlideIdx) {
|
|
257
298
|
slider.emit('activeSlideChanged');
|
|
258
299
|
}
|
|
259
300
|
}
|
|
260
301
|
|
|
302
|
+
|
|
261
303
|
function moveToSlide( idx: number ) {
|
|
262
304
|
const slide = slider.slides[idx];
|
|
263
305
|
if (slide) {
|
|
@@ -265,12 +307,28 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
265
307
|
}
|
|
266
308
|
};
|
|
267
309
|
|
|
310
|
+
function getInclusiveScrollWidth() : number {
|
|
311
|
+
return slider.container.scrollWidth + getOutermostChildrenEdgeMarginSum(slider.container);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
function getInclusiveClientWidth() : number {
|
|
315
|
+
return slider.container.clientWidth + getOutermostChildrenEdgeMarginSum(slider.container);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function getScrollLeft() : number {
|
|
319
|
+
return slider.options.rtl ? Math.abs(slider.container.scrollLeft) : slider.container.scrollLeft;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
function setScrollLeft(value: number) : void {
|
|
323
|
+
slider.container.scrollLeft = slider.options.rtl ? -value : value;
|
|
324
|
+
};
|
|
325
|
+
|
|
268
326
|
function getGapSize() : number {
|
|
269
327
|
let gapSize = 0;
|
|
270
328
|
if (slider.slides.length > 1) {
|
|
271
329
|
const firstSlideRect = slider.slides[0].getBoundingClientRect();
|
|
272
330
|
const secondSlideRect = slider.slides[1].getBoundingClientRect();
|
|
273
|
-
gapSize = secondSlideRect.left - firstSlideRect.right;
|
|
331
|
+
gapSize = slider.options.rtl ? Math.abs( Math.floor( secondSlideRect.right - firstSlideRect.left ) ) : Math.floor( secondSlideRect.left - firstSlideRect.right );
|
|
274
332
|
}
|
|
275
333
|
return gapSize;
|
|
276
334
|
};
|
|
@@ -281,7 +339,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
281
339
|
if (fullWidthOffset) {
|
|
282
340
|
offset = parseInt(fullWidthOffset);
|
|
283
341
|
}
|
|
284
|
-
return offset;
|
|
342
|
+
return Math.floor( offset );
|
|
285
343
|
};
|
|
286
344
|
|
|
287
345
|
function moveToDirection(direction = "prev") {
|
|
@@ -290,63 +348,74 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
290
348
|
const sliderRect = slider.container.getBoundingClientRect();
|
|
291
349
|
const containerWidth = slider.container.offsetWidth;
|
|
292
350
|
let targetScrollPosition = scrollLeft;
|
|
293
|
-
|
|
351
|
+
|
|
352
|
+
const realDirection = slider.options.rtl ? (direction === 'prev' ? 'next' : 'prev') : direction;
|
|
353
|
+
|
|
354
|
+
if (realDirection === 'prev') {
|
|
294
355
|
targetScrollPosition = Math.max(0, scrollLeft - slider.container.offsetWidth);
|
|
295
|
-
} else if (
|
|
296
|
-
targetScrollPosition = Math.min(slider.
|
|
356
|
+
} else if (realDirection === 'next') {
|
|
357
|
+
targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + slider.container.offsetWidth);
|
|
297
358
|
}
|
|
298
359
|
if (scrollStrategy === 'fullSlide') {
|
|
299
|
-
let
|
|
360
|
+
let fullSlideTargetScrollPosition = null;
|
|
300
361
|
|
|
301
362
|
// extend targetScrollPosition to include gap
|
|
302
|
-
if (
|
|
303
|
-
|
|
363
|
+
if (realDirection === 'prev') {
|
|
364
|
+
fullSlideTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
|
|
304
365
|
} else {
|
|
305
|
-
|
|
366
|
+
fullSlideTargetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), targetScrollPosition + getGapSize());
|
|
306
367
|
}
|
|
307
368
|
|
|
308
|
-
if (
|
|
369
|
+
if (realDirection === 'next') {
|
|
309
370
|
let partialSlideFound = false;
|
|
310
371
|
for (let slide of slider.slides) {
|
|
311
372
|
const slideRect = slide.getBoundingClientRect();
|
|
312
373
|
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
313
374
|
const slideEnd = slideStart + slideRect.width;
|
|
314
|
-
if (slideStart < targetScrollPosition && slideEnd > targetScrollPosition) {
|
|
315
|
-
|
|
375
|
+
if ( Math.floor( slideStart ) < Math.floor( targetScrollPosition ) && Math.floor( slideEnd ) > Math.floor( targetScrollPosition ) ) {
|
|
376
|
+
fullSlideTargetScrollPosition = slideStart;
|
|
316
377
|
partialSlideFound = true;
|
|
317
378
|
break;
|
|
318
379
|
}
|
|
319
380
|
}
|
|
320
|
-
if (!partialSlideFound) {
|
|
321
|
-
|
|
381
|
+
if ( ! partialSlideFound ) {
|
|
382
|
+
fullSlideTargetScrollPosition = Math.min(targetScrollPosition, slider.getInclusiveScrollWidth() - slider.container.offsetWidth);
|
|
322
383
|
}
|
|
323
|
-
if (
|
|
324
|
-
|
|
384
|
+
if ( fullSlideTargetScrollPosition ) {
|
|
385
|
+
if ( Math.floor( fullSlideTargetScrollPosition ) > Math.floor( scrollLeft ) ) {
|
|
386
|
+
// make sure fullSlideTargetScrollPosition is possible considering the container width
|
|
387
|
+
const maxScrollPosition = Math.floor( slider.getInclusiveScrollWidth() ) - Math.floor( containerWidth );
|
|
388
|
+
targetScrollPosition = Math.min( fullSlideTargetScrollPosition, maxScrollPosition );
|
|
389
|
+
} else {
|
|
390
|
+
// cannot snap to slide, move one page worth of distance
|
|
391
|
+
targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + containerWidth);
|
|
392
|
+
}
|
|
325
393
|
}
|
|
394
|
+
|
|
326
395
|
} else {
|
|
327
396
|
let partialSlideFound = false;
|
|
328
397
|
for (let slide of slider.slides) {
|
|
329
398
|
const slideRect = slide.getBoundingClientRect();
|
|
330
399
|
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
331
400
|
const slideEnd = slideStart + slideRect.width;
|
|
332
|
-
if (slideStart < scrollLeft && slideEnd > scrollLeft) {
|
|
333
|
-
|
|
401
|
+
if ( Math.floor( slideStart ) < Math.floor( scrollLeft ) && Math.floor( slideEnd ) > Math.floor( scrollLeft ) ) {
|
|
402
|
+
fullSlideTargetScrollPosition = slideEnd - containerWidth;
|
|
334
403
|
partialSlideFound = true;
|
|
335
404
|
break;
|
|
336
405
|
}
|
|
337
406
|
}
|
|
338
|
-
if (!partialSlideFound) {
|
|
339
|
-
|
|
407
|
+
if ( ! partialSlideFound ) {
|
|
408
|
+
fullSlideTargetScrollPosition = Math.max(0, scrollLeft - containerWidth);
|
|
340
409
|
}
|
|
341
|
-
if (
|
|
342
|
-
targetScrollPosition =
|
|
410
|
+
if ( fullSlideTargetScrollPosition && Math.floor( fullSlideTargetScrollPosition ) < Math.floor( scrollLeft ) ) {
|
|
411
|
+
targetScrollPosition = fullSlideTargetScrollPosition;
|
|
343
412
|
}
|
|
344
413
|
}
|
|
345
414
|
}
|
|
346
415
|
|
|
347
416
|
// add left offset
|
|
348
417
|
const offsettedTargetScrollPosition = targetScrollPosition - getLeftOffset();
|
|
349
|
-
if (offsettedTargetScrollPosition >= 0) {
|
|
418
|
+
if ( Math.floor( offsettedTargetScrollPosition ) >= 0) {
|
|
350
419
|
targetScrollPosition = offsettedTargetScrollPosition;
|
|
351
420
|
}
|
|
352
421
|
|
|
@@ -357,12 +426,12 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
357
426
|
};
|
|
358
427
|
|
|
359
428
|
function snapToClosestSlide(direction = "prev") {
|
|
360
|
-
const isMovingForward = direction === 'next';
|
|
429
|
+
const isMovingForward = slider.options.rtl ? direction === 'prev' : direction === 'next';
|
|
361
430
|
const slideReference = [];
|
|
362
431
|
for (let i = 0; i < slider.slides.length; i++) {
|
|
363
432
|
const slide = slider.slides[i];
|
|
364
433
|
const slideWidth = slide.offsetWidth;
|
|
365
|
-
const slideStart = slide.offsetLeft;
|
|
434
|
+
const slideStart = slider.options.rtl ? Math.abs( slide.offsetLeft + slideWidth - slider.details.containerWidth ) : slide.offsetLeft;
|
|
366
435
|
const slideEnd = slideStart + slideWidth;
|
|
367
436
|
const slideMiddle = slideStart + slideWidth / 2;
|
|
368
437
|
const trigger = Math.min(slideMiddle, slideStart + slider.options.emulateScrollSnapMaxThreshold);
|
|
@@ -373,18 +442,22 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
373
442
|
width: slideWidth,
|
|
374
443
|
trigger: trigger,
|
|
375
444
|
slide: slide,
|
|
445
|
+
// debug
|
|
446
|
+
offSetleft: slide.offsetLeft,
|
|
447
|
+
rect: slide.getBoundingClientRect(),
|
|
376
448
|
});
|
|
377
449
|
}
|
|
450
|
+
console.log('slideReference', slideReference);
|
|
378
451
|
let snapTarget = null;
|
|
379
|
-
const scrollPosition =
|
|
452
|
+
const scrollPosition = getScrollLeft();
|
|
380
453
|
if (isMovingForward) {
|
|
381
454
|
for (let i = 0; i < slideReference.length; i++) {
|
|
382
455
|
const item = slideReference[i];
|
|
383
|
-
if ( i === 0 && scrollPosition <= item.trigger ) {
|
|
456
|
+
if ( i === 0 && Math.floor( scrollPosition ) <= Math.floor( item.trigger ) ) {
|
|
384
457
|
snapTarget = 0;
|
|
385
458
|
break;
|
|
386
459
|
}
|
|
387
|
-
if (
|
|
460
|
+
if ( Math.floor( getScrollLeft() ) <= Math.floor( item.trigger ) ) {
|
|
388
461
|
snapTarget = item.start;
|
|
389
462
|
break;
|
|
390
463
|
}
|
|
@@ -392,11 +465,11 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
392
465
|
} else {
|
|
393
466
|
for (let i = slideReference.length - 1; i >= 0; i--) {
|
|
394
467
|
const item = slideReference[i];
|
|
395
|
-
if ( i === slideReference.length - 1 && scrollPosition >= item.trigger ) {
|
|
468
|
+
if ( i === slideReference.length - 1 && Math.floor( scrollPosition ) >= Math.floor( item.trigger ) ) {
|
|
396
469
|
snapTarget = item.start;
|
|
397
470
|
break;
|
|
398
471
|
}
|
|
399
|
-
if (
|
|
472
|
+
if ( Math.floor( getScrollLeft() ) >= Math.floor( item.trigger ) ) {
|
|
400
473
|
snapTarget = item.start;
|
|
401
474
|
break;
|
|
402
475
|
}
|
|
@@ -404,13 +477,14 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
404
477
|
}
|
|
405
478
|
if ( snapTarget !== null ) {
|
|
406
479
|
const offsettedSnapTarget = snapTarget - getLeftOffset();
|
|
407
|
-
if (offsettedSnapTarget >= 0) {
|
|
480
|
+
if ( Math.floor( offsettedSnapTarget ) >= 0 ) {
|
|
408
481
|
snapTarget = offsettedSnapTarget;
|
|
409
482
|
}
|
|
410
483
|
|
|
411
484
|
const scrollBehavior = slider.options.scrollBehavior || 'smooth';
|
|
485
|
+
|
|
412
486
|
slider.container.scrollTo({
|
|
413
|
-
left: snapTarget,
|
|
487
|
+
left: slider.options.rtl ? -snapTarget : snapTarget,
|
|
414
488
|
behavior: scrollBehavior as ScrollBehavior
|
|
415
489
|
});
|
|
416
490
|
}
|
|
@@ -440,6 +514,10 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
440
514
|
moveToDirection,
|
|
441
515
|
moveToSlide,
|
|
442
516
|
snapToClosestSlide,
|
|
517
|
+
getInclusiveScrollWidth,
|
|
518
|
+
getInclusiveClientWidth,
|
|
519
|
+
getScrollLeft,
|
|
520
|
+
setScrollLeft,
|
|
443
521
|
on,
|
|
444
522
|
options,
|
|
445
523
|
};
|
package/src/core/types.ts
CHANGED
|
@@ -11,6 +11,10 @@ export type Slider<O = {}, C = {}, H extends string = string> = {
|
|
|
11
11
|
moveToSlide: (
|
|
12
12
|
index: number
|
|
13
13
|
) => void
|
|
14
|
+
getInclusiveScrollWidth: () => number
|
|
15
|
+
getInclusiveClientWidth: () => number
|
|
16
|
+
getScrollLeft: () => number
|
|
17
|
+
setScrollLeft: (value: number) => void
|
|
14
18
|
on: (
|
|
15
19
|
name: H | SliderHooks,
|
|
16
20
|
cb: (props: Slider<O, C, H>) => void
|
|
@@ -26,6 +30,7 @@ export type SliderOptions = {
|
|
|
26
30
|
slidesSelector: string;
|
|
27
31
|
emulateScrollSnap: boolean;
|
|
28
32
|
emulateScrollSnapMaxThreshold: number;
|
|
33
|
+
rtl: boolean;
|
|
29
34
|
[key: string]: any;
|
|
30
35
|
}
|
|
31
36
|
|
package/src/core/utils.ts
CHANGED
|
@@ -21,4 +21,22 @@ function objectsAreEqual( obj1: any, obj2: any ) {
|
|
|
21
21
|
return true;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
function getOutermostChildrenEdgeMarginSum( el: HTMLElement ): number {
|
|
25
|
+
if (el.children.length === 0) {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// get the first child and its left margin
|
|
30
|
+
const firstChild = el.children[0];
|
|
31
|
+
const firstChildStyle = getComputedStyle(firstChild);
|
|
32
|
+
const firstChildMarginLeft = parseFloat(firstChildStyle.marginLeft);
|
|
33
|
+
|
|
34
|
+
// Get the last child and its right margin
|
|
35
|
+
const lastChild = el.children[el.children.length - 1];
|
|
36
|
+
const lastChildStyle = getComputedStyle(lastChild);
|
|
37
|
+
const lastChildMarginRight = parseFloat(lastChildStyle.marginRight);
|
|
38
|
+
|
|
39
|
+
return firstChildMarginLeft + lastChildMarginRight;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { generateId, objectsAreEqual, getOutermostChildrenEdgeMarginSum };
|
|
@@ -65,8 +65,12 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
65
65
|
prev.setAttribute( 'aria-label', options.texts.buttonPrevious );
|
|
66
66
|
prev.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '');
|
|
67
67
|
prev.setAttribute( 'data-type', 'prev' );
|
|
68
|
-
prev.innerHTML = options.icons.prev;
|
|
69
|
-
prev.addEventListener( 'click', () =>
|
|
68
|
+
prev.innerHTML = slider.options.rtl ? options.icons.next : options.icons.prev;
|
|
69
|
+
prev.addEventListener( 'click', () => {
|
|
70
|
+
if ( prev.getAttribute('data-has-content') === 'true' ) {
|
|
71
|
+
slider.moveToDirection( 'prev' );
|
|
72
|
+
}
|
|
73
|
+
} );
|
|
70
74
|
|
|
71
75
|
const next = document.createElement( 'button' );
|
|
72
76
|
next.setAttribute( 'class', options.classNames.nextButton );
|
|
@@ -74,28 +78,34 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
74
78
|
next.setAttribute( 'aria-label', options.texts.buttonNext );
|
|
75
79
|
next.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '');
|
|
76
80
|
next.setAttribute( 'data-type', 'next' );
|
|
77
|
-
next.innerHTML = options.icons.next;
|
|
78
|
-
next.addEventListener( 'click', () =>
|
|
81
|
+
next.innerHTML = slider.options.rtl ? options.icons.prev : options.icons.next;
|
|
82
|
+
next.addEventListener( 'click', () => {
|
|
83
|
+
if ( next.getAttribute('data-has-content') === 'true' ) {
|
|
84
|
+
slider.moveToDirection( 'next' );
|
|
85
|
+
}
|
|
86
|
+
} );
|
|
79
87
|
|
|
80
88
|
// insert buttons to the nav
|
|
81
89
|
nav.appendChild( prev );
|
|
82
90
|
nav.appendChild( next );
|
|
83
91
|
|
|
84
92
|
const update = () => {
|
|
85
|
-
const scrollLeft = slider.
|
|
86
|
-
const scrollWidth = slider.
|
|
87
|
-
const clientWidth = slider.
|
|
93
|
+
const scrollLeft = slider.getScrollLeft();
|
|
94
|
+
const scrollWidth = slider.getInclusiveScrollWidth();
|
|
95
|
+
const clientWidth = slider.getInclusiveClientWidth();
|
|
88
96
|
const buffer = 1;
|
|
89
|
-
if ( scrollLeft === 0 ) {
|
|
97
|
+
if ( Math.floor( scrollLeft ) === 0 ) {
|
|
90
98
|
prev.setAttribute( 'data-has-content', 'false' );
|
|
91
99
|
} else {
|
|
92
100
|
prev.setAttribute( 'data-has-content', 'true' );
|
|
93
101
|
}
|
|
94
|
-
|
|
102
|
+
const maxWidthDifference = Math.abs( Math.floor( scrollLeft + clientWidth ) - Math.floor( scrollWidth ) );
|
|
103
|
+
if ( maxWidthDifference <= buffer ) {
|
|
95
104
|
next.setAttribute( 'data-has-content', 'false' );
|
|
96
105
|
} else {
|
|
97
106
|
next.setAttribute( 'data-has-content', 'true' );
|
|
98
107
|
}
|
|
108
|
+
console.log( 'next', scrollLeft + clientWidth, scrollWidth );
|
|
99
109
|
};
|
|
100
110
|
|
|
101
111
|
if ( options.containerNext && options.containerPrev ) {
|
|
@@ -110,7 +120,7 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
update();
|
|
113
|
-
slider.on( '
|
|
123
|
+
slider.on( 'scrollEnd', update );
|
|
114
124
|
slider.on( 'contentsChanged', update );
|
|
115
125
|
slider.on( 'containerSizeChanged', update );
|
|
116
126
|
};
|
|
@@ -105,10 +105,6 @@ export default function DotsPlugin( args: { [key: string]: any } ) {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
const activateDot = ( item: number ) => {
|
|
108
|
-
// const scrollTargetPosition = slider.details.containerWidth * ( page - 1 );
|
|
109
|
-
// slider.container.style.scrollBehavior = slider.options.scrollBehavior;
|
|
110
|
-
// slider.container.scrollLeft = scrollTargetPosition;
|
|
111
|
-
// slider.container.style.scrollBehavior = '';
|
|
112
108
|
slider.moveToSlide( item - 1 );
|
|
113
109
|
};
|
|
114
110
|
|
|
@@ -59,7 +59,7 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
|
|
|
59
59
|
const walk = (x - startX);
|
|
60
60
|
const newScrollLeft = scrollLeft - walk;
|
|
61
61
|
mayNeedToSnap = true;
|
|
62
|
-
if ( slider.container.scrollLeft !== newScrollLeft ) {
|
|
62
|
+
if ( Math.floor( slider.container.scrollLeft ) !== Math.floor( newScrollLeft ) ) {
|
|
63
63
|
isMovingForward = slider.container.scrollLeft < newScrollLeft;
|
|
64
64
|
}
|
|
65
65
|
slider.container.scrollLeft = newScrollLeft;
|
|
@@ -48,26 +48,26 @@ export default function FadePlugin( args: { [key: string]: any } ) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const hasFadeAtStart = () => {
|
|
51
|
-
return slider.
|
|
51
|
+
return slider.getScrollLeft() > fadeItemStart.offsetWidth;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const fadeAtStartOpacity = () => {
|
|
55
|
-
const position = slider.
|
|
56
|
-
if ( position <= fadeItemStart.offsetWidth ) {
|
|
55
|
+
const position = slider.getScrollLeft();
|
|
56
|
+
if ( Math.floor( position ) <= Math.floor( fadeItemStart.offsetWidth ) ) {
|
|
57
57
|
return position / Math.max(fadeItemStart.offsetWidth, 1);
|
|
58
58
|
}
|
|
59
59
|
return 1;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const hasFadeAtEnd = () => {
|
|
63
|
-
return slider.
|
|
63
|
+
return Math.floor( slider.getScrollLeft() ) < Math.floor( slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth() - fadeItemEnd.offsetWidth );
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const fadeAtEndOpacity = () => {
|
|
67
|
-
const position = slider.
|
|
68
|
-
const maxPosition = slider.
|
|
67
|
+
const position = slider.getScrollLeft();
|
|
68
|
+
const maxPosition = slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth();
|
|
69
69
|
const maxFadePosition = maxPosition - fadeItemEnd.offsetWidth;
|
|
70
|
-
if ( position >= maxFadePosition ) {
|
|
70
|
+
if ( Math.floor( position ) >= Math.floor( maxFadePosition ) ) {
|
|
71
71
|
return ( ( maxFadePosition - position ) / Math.max(fadeItemEnd.offsetWidth, 1) ) + 1;
|
|
72
72
|
}
|
|
73
73
|
return 1;
|
|
@@ -97,6 +97,5 @@ export default function FadePlugin( args: { [key: string]: any } ) {
|
|
|
97
97
|
};
|
|
98
98
|
slider.on('scroll', debouncedUpdate);
|
|
99
99
|
|
|
100
|
-
|
|
101
100
|
};
|
|
102
101
|
}
|
|
@@ -19,9 +19,19 @@
|
|
|
19
19
|
.overflow-slider-fade--start {
|
|
20
20
|
left: 0;
|
|
21
21
|
background: linear-gradient(to right, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
22
|
+
[dir="rtl"] & {
|
|
23
|
+
left: auto;
|
|
24
|
+
right: 0;
|
|
25
|
+
background: linear-gradient(to left, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
.overflow-slider-fade--end {
|
|
25
30
|
right: 0;
|
|
26
31
|
background: linear-gradient(to left, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
32
|
+
[dir="rtl"] & {
|
|
33
|
+
right: auto;
|
|
34
|
+
left: 0;
|
|
35
|
+
background: linear-gradient(to right, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
36
|
+
}
|
|
27
37
|
}
|
|
@@ -29,16 +29,22 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
|
|
|
29
29
|
|
|
30
30
|
const marginAmount = Math.floor((window.innerWidth - options.targetWidth(slider)) / 2);
|
|
31
31
|
if ( options.addMarginBefore ) {
|
|
32
|
-
firstSlide.style.
|
|
32
|
+
firstSlide.style.marginInlineStart = `${marginAmount}px`;
|
|
33
33
|
}
|
|
34
34
|
if ( options.addMarginAfter ) {
|
|
35
|
-
lastSlide.style.
|
|
35
|
+
lastSlide.style.marginInlineEnd = `${marginAmount}px`;
|
|
36
36
|
}
|
|
37
37
|
slider.container.setAttribute( 'data-full-width-offset', marginAmount.toString() );
|
|
38
|
+
setCSS();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const setCSS = () => {
|
|
42
|
+
slider.container.style.setProperty('--slider-container-target-width', `${options.targetWidth(slider)}px`);
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
update();
|
|
41
46
|
slider.on( 'contentsChanged', update );
|
|
42
47
|
slider.on( 'containerSizeChanged', update );
|
|
48
|
+
window.addEventListener( 'resize', setCSS );
|
|
43
49
|
};
|
|
44
50
|
}
|