@evermade/overflow-slider 2.0.2 → 3.0.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/.nvmrc +1 -1
- package/README.md +12 -2
- package/dist/core/overflow-slider.esm.js +2 -0
- package/dist/core/overflow-slider.min.js +1 -1
- package/dist/core/slider.esm.js +159 -9
- package/dist/core/slider.min.js +1 -1
- package/dist/overflow-slider.css +1 -1
- package/dist/plugins/dots/dots/index.esm.js +21 -22
- package/dist/plugins/dots/dots/index.min.js +1 -1
- package/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +30 -3
- package/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
- package/dist/plugins/fade/fade/index.esm.js +81 -0
- package/dist/plugins/fade/fade/index.min.js +1 -0
- package/dist/plugins/full-width/full-width/index.esm.js +1 -0
- package/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +0 -2
- package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/dist/plugins/thumbnails/thumbnails/index.esm.js +2 -1
- package/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/assets/demo.css +16 -0
- package/docs/assets/demo.js +44 -7
- package/docs/dist/core/overflow-slider.esm.js +2 -0
- package/docs/dist/core/overflow-slider.min.js +1 -1
- package/docs/dist/core/slider.esm.js +159 -9
- package/docs/dist/core/slider.min.js +1 -1
- package/docs/dist/overflow-slider.css +1 -1
- package/docs/dist/plugins/dots/dots/index.esm.js +21 -22
- package/docs/dist/plugins/dots/dots/index.min.js +1 -1
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +30 -3
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
- package/docs/dist/plugins/fade/fade/index.esm.js +81 -0
- package/docs/dist/plugins/fade/fade/index.min.js +1 -0
- package/docs/dist/plugins/full-width/full-width/index.esm.js +1 -0
- package/docs/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +0 -2
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +2 -1
- package/docs/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/index.html +39 -7
- package/package.json +5 -1
- package/src/core/overflow-slider.ts +2 -0
- package/src/core/slider.ts +176 -14
- package/src/core/types.ts +31 -1
- package/src/overflow-slider.scss +2 -1
- package/src/plugins/dots/index.ts +21 -23
- package/src/plugins/drag-scrolling/index.ts +34 -5
- package/src/plugins/fade/index.ts +102 -0
- package/src/plugins/fade/styles.scss +27 -0
- package/src/plugins/full-width/index.ts +1 -0
- package/src/plugins/scroll-indicator/index.ts +0 -2
- package/src/plugins/thumbnails/index.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){var t;const
|
|
1
|
+
function e(e){var t;const o=null!==(t=null==e?void 0:e.draggedDistanceThatPreventsClick)&&void 0!==t?t:20;return e=>{let t=!1,n=0,r=0,a=!1,s=!1,i=!1;e.container.setAttribute("data-has-drag-scrolling","true");if(window.addEventListener("mousedown",(o=>{s=!1,e.details.hasOverflow&&e.container.contains(o.target)&&(t=!0,n=o.pageX-e.container.offsetLeft,r=e.container.scrollLeft,e.container.style.cursor="grabbing",e.container.style.scrollBehavior="auto",o.preventDefault(),o.stopPropagation())})),window.addEventListener("mousemove",(l=>{if(!e.details.hasOverflow)return void(s=!1);if(!t)return void(s=!1);l.preventDefault(),s||(s=!0,e.emit("programmaticScrollStart"));const c=l.pageX-e.container.offsetLeft-n,d=r-c;i=!0,e.container.scrollLeft!==d&&(a=e.container.scrollLeft<d),e.container.scrollLeft=d;const u=Math.abs(c),f=e.container.querySelectorAll(e.options.slidesSelector),v=u>o?"none":"";f.forEach((e=>{e.style.pointerEvents=v}))})),window.addEventListener("mouseup",(()=>{e.details.hasOverflow?(t=!1,e.container.style.cursor="",setTimeout((()=>{s=!1,e.container.style.scrollBehavior="";e.container.querySelectorAll(e.options.slidesSelector).forEach((e=>{e.style.pointerEvents=""}))}),50)):s=!1})),e.options.emulateScrollSnap){const o=()=>{i&&!t&&(i=!1,e.snapToClosestSlide(a?"next":"prev"))};e.on("programmaticScrollEnd",o),window.addEventListener("mouseup",o)}}}export{e as default};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
function FadePlugin(args) {
|
|
2
|
+
return (slider) => {
|
|
3
|
+
var _a, _b, _c;
|
|
4
|
+
const options = {
|
|
5
|
+
classNames: {
|
|
6
|
+
fadeItem: 'overflow-slider-fade',
|
|
7
|
+
fadeItemStart: 'overflow-slider-fade--start',
|
|
8
|
+
fadeItemEnd: 'overflow-slider-fade--end',
|
|
9
|
+
},
|
|
10
|
+
container: (_a = args === null || args === void 0 ? void 0 : args.container) !== null && _a !== void 0 ? _a : null,
|
|
11
|
+
containerStart: (_b = args === null || args === void 0 ? void 0 : args.containerStart) !== null && _b !== void 0 ? _b : null,
|
|
12
|
+
containerEnd: (_c = args === null || args === void 0 ? void 0 : args.containerEnd) !== null && _c !== void 0 ? _c : null,
|
|
13
|
+
};
|
|
14
|
+
const fadeItemStart = document.createElement('div');
|
|
15
|
+
fadeItemStart.classList.add(options.classNames.fadeItem, options.classNames.fadeItemStart);
|
|
16
|
+
fadeItemStart.setAttribute('aria-hidden', 'true');
|
|
17
|
+
fadeItemStart.setAttribute('tabindex', '-1');
|
|
18
|
+
const fadeItemEnd = document.createElement('div');
|
|
19
|
+
fadeItemEnd.classList.add(options.classNames.fadeItem, options.classNames.fadeItemEnd);
|
|
20
|
+
fadeItemEnd.setAttribute('aria-hidden', 'true');
|
|
21
|
+
fadeItemEnd.setAttribute('tabindex', '-1');
|
|
22
|
+
if (options.containerStart) {
|
|
23
|
+
options.containerStart.appendChild(fadeItemStart);
|
|
24
|
+
}
|
|
25
|
+
else if (options.container) {
|
|
26
|
+
options.container.appendChild(fadeItemStart);
|
|
27
|
+
}
|
|
28
|
+
if (options.containerEnd) {
|
|
29
|
+
options.containerEnd.appendChild(fadeItemEnd);
|
|
30
|
+
}
|
|
31
|
+
else if (options.container) {
|
|
32
|
+
options.container.appendChild(fadeItemEnd);
|
|
33
|
+
}
|
|
34
|
+
const hasFadeAtStart = () => {
|
|
35
|
+
return slider.container.scrollLeft > fadeItemStart.offsetWidth;
|
|
36
|
+
};
|
|
37
|
+
const fadeAtStartOpacity = () => {
|
|
38
|
+
const position = slider.container.scrollLeft;
|
|
39
|
+
if (position <= fadeItemStart.offsetWidth) {
|
|
40
|
+
return position / Math.max(fadeItemStart.offsetWidth, 1);
|
|
41
|
+
}
|
|
42
|
+
return 1;
|
|
43
|
+
};
|
|
44
|
+
const hasFadeAtEnd = () => {
|
|
45
|
+
return slider.container.scrollLeft < (slider.container.scrollWidth - slider.container.clientWidth - fadeItemEnd.offsetWidth);
|
|
46
|
+
};
|
|
47
|
+
const fadeAtEndOpacity = () => {
|
|
48
|
+
const position = slider.container.scrollLeft;
|
|
49
|
+
const maxPosition = slider.container.scrollWidth - slider.container.clientWidth;
|
|
50
|
+
const maxFadePosition = maxPosition - fadeItemEnd.offsetWidth;
|
|
51
|
+
if (position >= maxFadePosition) {
|
|
52
|
+
return ((maxFadePosition - position) / Math.max(fadeItemEnd.offsetWidth, 1)) + 1;
|
|
53
|
+
}
|
|
54
|
+
return 1;
|
|
55
|
+
};
|
|
56
|
+
const update = () => {
|
|
57
|
+
fadeItemStart.setAttribute('data-has-fade', hasFadeAtStart().toString());
|
|
58
|
+
fadeItemStart.style.opacity = fadeAtStartOpacity().toString();
|
|
59
|
+
fadeItemEnd.setAttribute('data-has-fade', hasFadeAtEnd().toString());
|
|
60
|
+
fadeItemEnd.style.opacity = fadeAtEndOpacity().toString();
|
|
61
|
+
};
|
|
62
|
+
update();
|
|
63
|
+
slider.on('created', update);
|
|
64
|
+
slider.on('contentsChanged', update);
|
|
65
|
+
slider.on('containerSizeChanged', update);
|
|
66
|
+
slider.on('scrollEnd', update);
|
|
67
|
+
slider.on('scrollStart', update);
|
|
68
|
+
let requestId = 0;
|
|
69
|
+
const debouncedUpdate = () => {
|
|
70
|
+
if (requestId) {
|
|
71
|
+
window.cancelAnimationFrame(requestId);
|
|
72
|
+
}
|
|
73
|
+
requestId = window.requestAnimationFrame(() => {
|
|
74
|
+
update();
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
slider.on('scroll', debouncedUpdate);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { FadePlugin as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t){return e=>{var n,a,i;const o={classNames:{fadeItem:"overflow-slider-fade",fadeItemStart:"overflow-slider-fade--start",fadeItemEnd:"overflow-slider-fade--end"},container:null!==(n=null==t?void 0:t.container)&&void 0!==n?n:null,containerStart:null!==(a=null==t?void 0:t.containerStart)&&void 0!==a?a:null,containerEnd:null!==(i=null==t?void 0:t.containerEnd)&&void 0!==i?i:null},r=document.createElement("div");r.classList.add(o.classNames.fadeItem,o.classNames.fadeItemStart),r.setAttribute("aria-hidden","true"),r.setAttribute("tabindex","-1");const d=document.createElement("div");d.classList.add(o.classNames.fadeItem,o.classNames.fadeItemEnd),d.setAttribute("aria-hidden","true"),d.setAttribute("tabindex","-1"),o.containerStart?o.containerStart.appendChild(r):o.container&&o.container.appendChild(r),o.containerEnd?o.containerEnd.appendChild(d):o.container&&o.container.appendChild(d);const l=()=>{r.setAttribute("data-has-fade",(e.container.scrollLeft>r.offsetWidth).toString()),r.style.opacity=(()=>{const t=e.container.scrollLeft;return t<=r.offsetWidth?t/Math.max(r.offsetWidth,1):1})().toString(),d.setAttribute("data-has-fade",(e.container.scrollLeft<e.container.scrollWidth-e.container.clientWidth-d.offsetWidth).toString()),d.style.opacity=(()=>{const t=e.container.scrollLeft,n=e.container.scrollWidth-e.container.clientWidth-d.offsetWidth;return t>=n?(n-t)/Math.max(d.offsetWidth,1)+1:1})().toString()};l(),e.on("created",l),e.on("contentsChanged",l),e.on("containerSizeChanged",l),e.on("scrollEnd",l),e.on("scrollStart",l);let s=0;e.on("scroll",(()=>{s&&window.cancelAnimationFrame(s),s=window.requestAnimationFrame((()=>{l()}))}))}}export{t as default};
|
|
@@ -21,6 +21,7 @@ function FullWidthPlugin(args) {
|
|
|
21
21
|
if (options.addMarginAfter) {
|
|
22
22
|
lastSlide.style.marginRight = `${marginAmount}px`;
|
|
23
23
|
}
|
|
24
|
+
slider.container.setAttribute('data-full-width-offset', marginAmount.toString());
|
|
24
25
|
};
|
|
25
26
|
update();
|
|
26
27
|
slider.on('contentsChanged', update);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const t=t=>{var n,e;return null!==(e=null===(n=t.container.parentElement)||void 0===n?void 0:n.offsetWidth)&&void 0!==e?e:window.innerWidth};function n(n){return e=>{var i,r,o;const d={targetWidth:null!==(i=null==n?void 0:n.targetWidth)&&void 0!==i?i:t,addMarginBefore:null===(r=null==n?void 0:n.addMarginBefore)||void 0===r||r,addMarginAfter:null===(o=null==n?void 0:n.addMarginAfter)||void 0===o||o},a=()=>{const t=e.container.querySelectorAll(e.options.slidesSelector);if(!t.length)return;const n=t[0],i=t[t.length-1],r=Math.floor((window.innerWidth-d.targetWidth(e))/2);d.addMarginBefore&&(n.style.marginLeft=`${r}px`),d.addMarginAfter&&(i.style.marginRight=`${r}px`),e.container.setAttribute("data-full-width-offset",r.toString())};a(),e.on("contentsChanged",a),e.on("containerSizeChanged",a)}}export{n as default};
|
|
@@ -99,7 +99,6 @@ function ScrollIndicatorPlugin(args) {
|
|
|
99
99
|
scrollLeft = slider.container.scrollLeft;
|
|
100
100
|
// change cursor to grabbing
|
|
101
101
|
scrollbarButton.style.cursor = 'grabbing';
|
|
102
|
-
slider.container.style.scrollSnapType = 'none';
|
|
103
102
|
scrollbarButton.setAttribute('data-is-grabbed', 'true');
|
|
104
103
|
e.preventDefault();
|
|
105
104
|
e.stopPropagation();
|
|
@@ -118,7 +117,6 @@ function ScrollIndicatorPlugin(args) {
|
|
|
118
117
|
const onInteractionUp = () => {
|
|
119
118
|
isInteractionDown = false;
|
|
120
119
|
scrollbarButton.style.cursor = '';
|
|
121
|
-
slider.container.style.scrollSnapType = '';
|
|
122
120
|
scrollbarButton.setAttribute('data-is-grabbed', 'false');
|
|
123
121
|
};
|
|
124
122
|
scrollbarButton.addEventListener('mousedown', onInteractionDown);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const t={scrollIndicator:"overflow-slider__scroll-indicator",scrollIndicatorBar:"overflow-slider__scroll-indicator-bar",scrollIndicatorButton:"overflow-slider__scroll-indicator-button"};function e(e){return n=>{var o,r,a;const i={classNames:Object.assign(Object.assign({},t),(null==e?void 0:e.classNames)||[]),container:null!==(o=null==e?void 0:e.container)&&void 0!==o?o:null},s=document.createElement("div");s.setAttribute("class",i.classNames.scrollIndicator),s.setAttribute("tabindex","0"),s.setAttribute("role","scrollbar"),s.setAttribute("aria-controls",null!==(r=n.container.getAttribute("id"))&&void 0!==r?r:""),s.setAttribute("aria-orientation","horizontal"),s.setAttribute("aria-valuemax","100"),s.setAttribute("aria-valuemin","0"),s.setAttribute("aria-valuenow","0");const l=document.createElement("div");l.setAttribute("class",i.classNames.scrollIndicatorBar);const c=document.createElement("div");c.setAttribute("class",i.classNames.scrollIndicatorButton),c.setAttribute("data-is-grabbed","false"),l.appendChild(c),s.appendChild(l);const d=()=>{s.setAttribute("data-has-overflow",n.details.hasOverflow.toString())};d();const u=()=>{const t=c.offsetWidth/n.details.containerWidth;return n.container.scrollLeft*t};let v=0;const f=()=>{v&&window.cancelAnimationFrame(v),v=window.requestAnimationFrame((()=>{const t=n.details.containerWidth/n.details.scrollableAreaWidth*100,e=u();c.style.width=`${t}%`,c.style.transform=`translateX(${e}px)`;const o=n.container.scrollLeft/(n.container.scrollWidth-n.container.offsetWidth)*100;s.setAttribute("aria-valuenow",Math.round(Number.isNaN(o)?0:o).toString())}))};i.container?i.container.appendChild(s):null===(a=n.container.parentNode)||void 0===a||a.insertBefore(s,n.container.nextSibling),f(),n.on("scroll",f),n.on("contentsChanged",f),n.on("containerSizeChanged",f),n.on("detailsChanged",d),s.addEventListener("keydown",(t=>{"ArrowLeft"===t.key?n.moveToDirection("prev"):"ArrowRight"===t.key&&n.moveToDirection("next")})),s.addEventListener("click",(t=>{const e=c.offsetWidth,o=u(),r=o+e,a=t.pageX-s.offsetLeft;a<o?n.moveToDirection("prev"):a>r&&n.moveToDirection("next")}));let b=!1,m=0,h=0;const
|
|
1
|
+
const t={scrollIndicator:"overflow-slider__scroll-indicator",scrollIndicatorBar:"overflow-slider__scroll-indicator-bar",scrollIndicatorButton:"overflow-slider__scroll-indicator-button"};function e(e){return n=>{var o,r,a;const i={classNames:Object.assign(Object.assign({},t),(null==e?void 0:e.classNames)||[]),container:null!==(o=null==e?void 0:e.container)&&void 0!==o?o:null},s=document.createElement("div");s.setAttribute("class",i.classNames.scrollIndicator),s.setAttribute("tabindex","0"),s.setAttribute("role","scrollbar"),s.setAttribute("aria-controls",null!==(r=n.container.getAttribute("id"))&&void 0!==r?r:""),s.setAttribute("aria-orientation","horizontal"),s.setAttribute("aria-valuemax","100"),s.setAttribute("aria-valuemin","0"),s.setAttribute("aria-valuenow","0");const l=document.createElement("div");l.setAttribute("class",i.classNames.scrollIndicatorBar);const c=document.createElement("div");c.setAttribute("class",i.classNames.scrollIndicatorButton),c.setAttribute("data-is-grabbed","false"),l.appendChild(c),s.appendChild(l);const d=()=>{s.setAttribute("data-has-overflow",n.details.hasOverflow.toString())};d();const u=()=>{const t=c.offsetWidth/n.details.containerWidth;return n.container.scrollLeft*t};let v=0;const f=()=>{v&&window.cancelAnimationFrame(v),v=window.requestAnimationFrame((()=>{const t=n.details.containerWidth/n.details.scrollableAreaWidth*100,e=u();c.style.width=`${t}%`,c.style.transform=`translateX(${e}px)`;const o=n.container.scrollLeft/(n.container.scrollWidth-n.container.offsetWidth)*100;s.setAttribute("aria-valuenow",Math.round(Number.isNaN(o)?0:o).toString())}))};i.container?i.container.appendChild(s):null===(a=n.container.parentNode)||void 0===a||a.insertBefore(s,n.container.nextSibling),f(),n.on("scroll",f),n.on("contentsChanged",f),n.on("containerSizeChanged",f),n.on("detailsChanged",d),s.addEventListener("keydown",(t=>{"ArrowLeft"===t.key?n.moveToDirection("prev"):"ArrowRight"===t.key&&n.moveToDirection("next")})),s.addEventListener("click",(t=>{const e=c.offsetWidth,o=u(),r=o+e,a=t.pageX-s.offsetLeft;a<o?n.moveToDirection("prev"):a>r&&n.moveToDirection("next")}));let b=!1,m=0,h=0;const w=t=>{b=!0;const e=t.pageX||t.touches[0].pageX;m=e-s.offsetLeft,h=n.container.scrollLeft,c.style.cursor="grabbing",c.setAttribute("data-is-grabbed","true"),t.preventDefault(),t.stopPropagation()},p=t=>{if(!b)return;t.preventDefault();const e=(t.pageX||t.touches[0].pageX)-s.offsetLeft,o=n.details.scrollableAreaWidth/s.offsetWidth,r=(e-m)*o;n.container.scrollLeft=h+r},A=()=>{b=!1,c.style.cursor="",c.setAttribute("data-is-grabbed","false")};c.addEventListener("mousedown",w),c.addEventListener("touchstart",w),window.addEventListener("mousemove",p),window.addEventListener("touchmove",p,{passive:!1}),window.addEventListener("mouseup",A),window.addEventListener("touchend",A)}}export{e as default};
|
|
@@ -27,7 +27,8 @@ function FullWidthPlugin(args) {
|
|
|
27
27
|
};
|
|
28
28
|
setActiveThumbnail();
|
|
29
29
|
addClickListeners();
|
|
30
|
-
|
|
30
|
+
// @todo debounce on scroll
|
|
31
|
+
mainSlider.on('scrollEnd', () => {
|
|
31
32
|
setTimeout(() => {
|
|
32
33
|
const activeSlideIdx = mainSlider.activeSlideIdx;
|
|
33
34
|
const activeThumbnail = slider.slides[activeSlideIdx];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return i=>{const
|
|
1
|
+
function e(e){return i=>{const l={mainSlider:e.mainSlider}.mainSlider,t=(e=null)=>{null===e&&i.slides.length>0&&(e=i.slides[0]),null!==e&&(i.slides.forEach((e=>{e.setAttribute("aria-current","false")})),e.setAttribute("aria-current","true"))};t(),i.slides.forEach(((e,i)=>{e.addEventListener("click",(()=>{l.moveToSlide(i),t(e)}))})),l.on("scrollEnd",(()=>{setTimeout((()=>{const e=l.activeSlideIdx,r=i.slides[e];t(r),i.moveToSlide(e)}),50)}))}}export{e as default};
|
package/docs/index.html
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<a href="#12" class="example-item example-item--12">12</a>
|
|
70
70
|
<a href="#13" class="example-item example-item--13">13</a>
|
|
71
71
|
</div>
|
|
72
|
-
<p style="margin-top:1.5rem;">These slides are not clickable
|
|
72
|
+
<p style="margin-top:1.5rem;">These slides are not clickable.</p>
|
|
73
73
|
<div class="overflow-slider example-container example-container-1-drag-scrolling-not-clickable">
|
|
74
74
|
<div class="example-item example-item--1">1</div>
|
|
75
75
|
<div class="example-item example-item--2">2</div>
|
|
@@ -85,6 +85,22 @@
|
|
|
85
85
|
<div class="example-item example-item--12">12</div>
|
|
86
86
|
<div class="example-item example-item--13">13</div>
|
|
87
87
|
</div>
|
|
88
|
+
<p style="margin-top:1.5rem;">Scroll-snap emulation enabled.</p>
|
|
89
|
+
<div class="overflow-slider example-container example-container-1-drag-scrolling-scroll-snap">
|
|
90
|
+
<div class="example-item example-item--1">1</div>
|
|
91
|
+
<div class="example-item example-item--2">2</div>
|
|
92
|
+
<div class="example-item example-item--3">3</div>
|
|
93
|
+
<div class="example-item example-item--4">4</div>
|
|
94
|
+
<div class="example-item example-item--5">5</div>
|
|
95
|
+
<div class="example-item example-item--6">6</div>
|
|
96
|
+
<div class="example-item example-item--7">7</div>
|
|
97
|
+
<div class="example-item example-item--8">8</div>
|
|
98
|
+
<div class="example-item example-item--9">9</div>
|
|
99
|
+
<div class="example-item example-item--10">10</div>
|
|
100
|
+
<div class="example-item example-item--11">11</div>
|
|
101
|
+
<div class="example-item example-item--12">12</div>
|
|
102
|
+
<div class="example-item example-item--13">13</div>
|
|
103
|
+
</div>
|
|
88
104
|
</div>
|
|
89
105
|
|
|
90
106
|
<div class="entry__item">
|
|
@@ -134,13 +150,29 @@
|
|
|
134
150
|
<a href="#4" class="example-item example-item--4">4</a>
|
|
135
151
|
<a href="#5" class="example-item example-item--5">5</a>
|
|
136
152
|
<a href="#6" class="example-item example-item--6">6</a>
|
|
137
|
-
<a href="#7" class="example-item example-item--7">7</a>
|
|
138
|
-
<a href="#8" class="example-item example-item--8">8</a>
|
|
139
|
-
<a href="#9" class="example-item example-item--9">9</a>
|
|
140
|
-
<a href="#10" class="example-item example-item--10">10</a>
|
|
141
153
|
</div>
|
|
142
154
|
</div>
|
|
143
155
|
|
|
156
|
+
<div class="entry__item">
|
|
157
|
+
<h3>Fade</h3>
|
|
158
|
+
<p>Display fade. Uses FadePlugin. Fade can be on both sides but having it only at end is more usable.</p>
|
|
159
|
+
<div class="example-container-1-fade-wrap">
|
|
160
|
+
<div class="example-container-1-fade__start"></div>
|
|
161
|
+
<div class="overflow-slider example-container example-container-1-fade">
|
|
162
|
+
<a href="#1" class="example-item example-item--1">1</a>
|
|
163
|
+
<a href="#2" class="example-item example-item--2">2</a>
|
|
164
|
+
<a href="#3" class="example-item example-item--3">3</a>
|
|
165
|
+
<a href="#4" class="example-item example-item--4">4</a>
|
|
166
|
+
<a href="#5" class="example-item example-item--5">5</a>
|
|
167
|
+
<a href="#6" class="example-item example-item--6">6</a>
|
|
168
|
+
<a href="#7" class="example-item example-item--7">7</a>
|
|
169
|
+
<a href="#8" class="example-item example-item--8">8</a>
|
|
170
|
+
<a href="#9" class="example-item example-item--9">9</a>
|
|
171
|
+
<a href="#10" class="example-item example-item--10">10</a>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="example-container-1-fade__end"></div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
144
176
|
|
|
145
177
|
</section>
|
|
146
178
|
|
|
@@ -231,8 +263,8 @@
|
|
|
231
263
|
</div>
|
|
232
264
|
|
|
233
265
|
<div class="entry__item">
|
|
234
|
-
<h3>Entrance and Exit Animations</h3>
|
|
235
|
-
<p>Animate slides when they appear and disappear.</p>
|
|
266
|
+
<h3>Entrance and Exit Animations (experimental)</h3>
|
|
267
|
+
<p>Animate slides when they appear and disappear. Seem to only work on Chrome.</p>
|
|
236
268
|
<div class="overflow-slider example-container example-container-3-entrance-animation">
|
|
237
269
|
<a href="#1" class="example-item example-item--1">1</a>
|
|
238
270
|
<a href="#2" class="example-item example-item--2">2</a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evermade/overflow-slider",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Accessible slider tha works with overflow: auto.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"import": "./dist/plugins/thumbnails/thumbnails/index.esm.js",
|
|
40
40
|
"require": "./dist/plugins/thumbnails/thumbnails/index.min.js"
|
|
41
41
|
},
|
|
42
|
+
"./plugins/fade": {
|
|
43
|
+
"import": "./dist/plugins/fade/fade/index.esm.js",
|
|
44
|
+
"require": "./dist/plugins/fade/fade/index.min.js"
|
|
45
|
+
},
|
|
42
46
|
"./style.css": "./dist/overflow-slider.css"
|
|
43
47
|
},
|
|
44
48
|
"repository": {
|
|
@@ -21,6 +21,8 @@ export default function OverflowSlider (
|
|
|
21
21
|
scrollBehavior: "smooth",
|
|
22
22
|
scrollStrategy: "fullSlide",
|
|
23
23
|
slidesSelector: ":scope > *",
|
|
24
|
+
emulateScrollSnap: false,
|
|
25
|
+
emulateScrollSnapMaxThreshold: 64,
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
const sliderOptions = { ...defaults, ...options };
|
package/src/core/slider.ts
CHANGED
|
@@ -39,6 +39,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
39
39
|
addEventListeners();
|
|
40
40
|
setDataAttributes();
|
|
41
41
|
setCSSVariables();
|
|
42
|
+
|
|
42
43
|
if (plugins) {
|
|
43
44
|
for (const plugin of plugins) {
|
|
44
45
|
plugin(slider);
|
|
@@ -78,7 +79,96 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
78
79
|
resizeObserver.observe( slider.container );
|
|
79
80
|
|
|
80
81
|
// scroll event with debouncing
|
|
81
|
-
|
|
82
|
+
let scrollTimeout: ReturnType<typeof setTimeout>;
|
|
83
|
+
let nativeScrollTimeout: ReturnType<typeof setTimeout>;
|
|
84
|
+
let programmaticScrollTimeout: ReturnType<typeof setTimeout>;
|
|
85
|
+
|
|
86
|
+
let scrollLeft = slider.container.scrollLeft;
|
|
87
|
+
let nativeScrollLeft = slider.container.scrollLeft;
|
|
88
|
+
let programmaticScrollLeft = slider.container.scrollLeft;
|
|
89
|
+
|
|
90
|
+
let isScrolling = false;
|
|
91
|
+
let isUserScrolling = false;
|
|
92
|
+
let isProgrammaticScrolling = false;
|
|
93
|
+
|
|
94
|
+
// any scroll
|
|
95
|
+
slider.container.addEventListener('scroll', () => {
|
|
96
|
+
const newScrollLeft = slider.container.scrollLeft;
|
|
97
|
+
if (scrollLeft !== newScrollLeft) {
|
|
98
|
+
if (!isScrolling) {
|
|
99
|
+
isScrolling = true;
|
|
100
|
+
slider.emit('scrollStart');
|
|
101
|
+
}
|
|
102
|
+
scrollLeft = newScrollLeft;
|
|
103
|
+
clearTimeout(scrollTimeout);
|
|
104
|
+
scrollTimeout = setTimeout(() => {
|
|
105
|
+
isScrolling = false;
|
|
106
|
+
slider.emit('scrollEnd');
|
|
107
|
+
}, 50);
|
|
108
|
+
slider.emit('scroll');
|
|
109
|
+
}
|
|
110
|
+
// keep up nativeScrolling to take into account scroll-snap
|
|
111
|
+
if ( isUserScrolling ) {
|
|
112
|
+
nativeScrollHandler();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// user initted scroll (touchmove, mouse wheel, etc.)
|
|
117
|
+
const nativeScrollHandler = () => {
|
|
118
|
+
const newScrollLeft = slider.container.scrollLeft;
|
|
119
|
+
if (nativeScrollLeft !== newScrollLeft && !isProgrammaticScrolling) {
|
|
120
|
+
if (!isUserScrolling) {
|
|
121
|
+
slider.emit('nativeScrollStart');
|
|
122
|
+
isUserScrolling = true;
|
|
123
|
+
}
|
|
124
|
+
slider.emit('nativeScroll');
|
|
125
|
+
nativeScrollLeft = newScrollLeft;
|
|
126
|
+
clearTimeout(nativeScrollTimeout);
|
|
127
|
+
nativeScrollTimeout = setTimeout(() => {
|
|
128
|
+
isUserScrolling = false;
|
|
129
|
+
slider.emit('nativeScrollEnd');
|
|
130
|
+
// update programmaticScrollLeft to match nativeScrollLeft
|
|
131
|
+
// this prevents programmaticScroll triggering with no real change to scrollLeft
|
|
132
|
+
programmaticScrollLeft = nativeScrollLeft;
|
|
133
|
+
}, 50);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
slider.container.addEventListener('touchmove', nativeScrollHandler);
|
|
138
|
+
slider.container.addEventListener('mousewheel', nativeScrollHandler);
|
|
139
|
+
slider.container.addEventListener('wheel', nativeScrollHandler);
|
|
140
|
+
|
|
141
|
+
// programmatic scroll (scrollTo, etc.)
|
|
142
|
+
slider.on('programmaticScrollStart', () => {
|
|
143
|
+
isProgrammaticScrolling = true;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
slider.container.addEventListener('scroll', () => {
|
|
147
|
+
const newScrollLeft = slider.container.scrollLeft;
|
|
148
|
+
if (programmaticScrollLeft !== newScrollLeft && !isUserScrolling && isProgrammaticScrolling) {
|
|
149
|
+
programmaticScrollLeft = newScrollLeft;
|
|
150
|
+
clearTimeout(programmaticScrollTimeout);
|
|
151
|
+
programmaticScrollTimeout = setTimeout(() => {
|
|
152
|
+
isProgrammaticScrolling = false;
|
|
153
|
+
slider.emit('programmaticScrollEnd');
|
|
154
|
+
// update nativeScrollLeft to match programmaticScrollLeft
|
|
155
|
+
// this prevents nativeScroll triggering with no real change to scrollLeft
|
|
156
|
+
nativeScrollLeft = programmaticScrollLeft;
|
|
157
|
+
}, 50);
|
|
158
|
+
slider.emit('programmaticScroll');
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Fix issues on scroll snapping not working on programmatic scroll (it's not smooth)
|
|
163
|
+
// by disabling scroll snap if scrolling is programmatic
|
|
164
|
+
slider.on( 'programmaticScrollStart', () => {
|
|
165
|
+
slider.container.style.scrollSnapType = 'none';
|
|
166
|
+
} );
|
|
167
|
+
|
|
168
|
+
// restore scroll snap if user scroll starts
|
|
169
|
+
slider.on( 'nativeScrollStart', () => {
|
|
170
|
+
slider.container.style.scrollSnapType = '';
|
|
171
|
+
} );
|
|
82
172
|
|
|
83
173
|
// Listen for mouse down and touch start events on the document
|
|
84
174
|
// This handles both mouse clicks and touch interactions
|
|
@@ -134,20 +224,14 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
134
224
|
scrollTarget = slideEnd - containerWidth;
|
|
135
225
|
} else if (slideStart === 0) {
|
|
136
226
|
scrollTarget = 0;
|
|
227
|
+
} else {
|
|
228
|
+
scrollTarget = slideStart;
|
|
137
229
|
}
|
|
138
230
|
if (scrollTarget !== null) {
|
|
139
|
-
slider.container.style.scrollSnapType = 'none';
|
|
140
|
-
// seems like in order for scroll behavior: smooth to work, we need to wait a bit to disable scrollSnapType
|
|
141
231
|
setTimeout((scrollTarget) => {
|
|
232
|
+
slider.emit('programmaticScrollStart');
|
|
142
233
|
slider.container.scrollTo({ left: scrollTarget, behavior: behavior });
|
|
143
234
|
}, 50, scrollTarget);
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
// leave snapping off to fix issues with slide moving back on focus
|
|
146
|
-
if ( behavior == 'smooth' ) {
|
|
147
|
-
slider.container.style.scrollSnapType = '';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
}, 500);
|
|
151
235
|
}
|
|
152
236
|
};
|
|
153
237
|
|
|
@@ -189,7 +273,16 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
189
273
|
gapSize = secondSlideRect.left - firstSlideRect.right;
|
|
190
274
|
}
|
|
191
275
|
return gapSize;
|
|
192
|
-
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
function getLeftOffset() : number {
|
|
279
|
+
let offset = 0;
|
|
280
|
+
const fullWidthOffset = slider.container.getAttribute('data-full-width-offset');
|
|
281
|
+
if (fullWidthOffset) {
|
|
282
|
+
offset = parseInt(fullWidthOffset);
|
|
283
|
+
}
|
|
284
|
+
return offset;
|
|
285
|
+
};
|
|
193
286
|
|
|
194
287
|
function moveToDirection(direction = "prev") {
|
|
195
288
|
const scrollStrategy = slider.options.scrollStrategy;
|
|
@@ -250,17 +343,85 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
250
343
|
}
|
|
251
344
|
}
|
|
252
345
|
}
|
|
346
|
+
|
|
347
|
+
// add left offset
|
|
348
|
+
const offsettedTargetScrollPosition = targetScrollPosition - getLeftOffset();
|
|
349
|
+
if (offsettedTargetScrollPosition >= 0) {
|
|
350
|
+
targetScrollPosition = offsettedTargetScrollPosition;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
slider.emit('programmaticScrollStart');
|
|
253
354
|
slider.container.style.scrollBehavior = slider.options.scrollBehavior;
|
|
254
355
|
slider.container.scrollLeft = targetScrollPosition;
|
|
255
356
|
setTimeout(() => slider.container.style.scrollBehavior = '', 50);
|
|
256
|
-
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
function snapToClosestSlide(direction = "prev") {
|
|
360
|
+
const isMovingForward = direction === 'next';
|
|
361
|
+
const slideReference = [];
|
|
362
|
+
for (let i = 0; i < slider.slides.length; i++) {
|
|
363
|
+
const slide = slider.slides[i];
|
|
364
|
+
const slideWidth = slide.offsetWidth;
|
|
365
|
+
const slideStart = slide.offsetLeft;
|
|
366
|
+
const slideEnd = slideStart + slideWidth;
|
|
367
|
+
const slideMiddle = slideStart + slideWidth / 2;
|
|
368
|
+
const trigger = Math.min(slideMiddle, slideStart + slider.options.emulateScrollSnapMaxThreshold);
|
|
369
|
+
slideReference.push({
|
|
370
|
+
start: slideStart,
|
|
371
|
+
middle: slideMiddle,
|
|
372
|
+
end: slideEnd,
|
|
373
|
+
width: slideWidth,
|
|
374
|
+
trigger: trigger,
|
|
375
|
+
slide: slide,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
let snapTarget = null;
|
|
379
|
+
const scrollPosition = slider.container.scrollLeft;
|
|
380
|
+
if (isMovingForward) {
|
|
381
|
+
for (let i = 0; i < slideReference.length; i++) {
|
|
382
|
+
const item = slideReference[i];
|
|
383
|
+
if ( i === 0 && scrollPosition <= item.trigger ) {
|
|
384
|
+
snapTarget = 0;
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
if (slider.container.scrollLeft <= item.trigger) {
|
|
388
|
+
snapTarget = item.start;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
for (let i = slideReference.length - 1; i >= 0; i--) {
|
|
394
|
+
const item = slideReference[i];
|
|
395
|
+
if ( i === slideReference.length - 1 && scrollPosition >= item.trigger ) {
|
|
396
|
+
snapTarget = item.start;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
if (slider.container.scrollLeft >= item.trigger) {
|
|
400
|
+
snapTarget = item.start;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if ( snapTarget !== null ) {
|
|
406
|
+
const offsettedSnapTarget = snapTarget - getLeftOffset();
|
|
407
|
+
if (offsettedSnapTarget >= 0) {
|
|
408
|
+
snapTarget = offsettedSnapTarget;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const scrollBehavior = slider.options.scrollBehavior || 'smooth';
|
|
412
|
+
slider.container.scrollTo({
|
|
413
|
+
left: snapTarget,
|
|
414
|
+
behavior: scrollBehavior as ScrollBehavior
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
};
|
|
257
418
|
|
|
258
419
|
function on(name: string, cb: any) {
|
|
259
420
|
if (!subs[name]) {
|
|
260
421
|
subs[name] = [];
|
|
261
422
|
}
|
|
262
423
|
subs[name].push(cb);
|
|
263
|
-
}
|
|
424
|
+
};
|
|
264
425
|
|
|
265
426
|
function emit(name: string) {
|
|
266
427
|
if (subs && subs[name]) {
|
|
@@ -272,12 +433,13 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
272
433
|
if (typeof optionCallBack === 'function') {
|
|
273
434
|
optionCallBack(slider);
|
|
274
435
|
}
|
|
275
|
-
}
|
|
436
|
+
};
|
|
276
437
|
|
|
277
438
|
slider = <Slider>{
|
|
278
439
|
emit,
|
|
279
440
|
moveToDirection,
|
|
280
441
|
moveToSlide,
|
|
442
|
+
snapToClosestSlide,
|
|
281
443
|
on,
|
|
282
444
|
options,
|
|
283
445
|
};
|
package/src/core/types.ts
CHANGED
|
@@ -5,6 +5,9 @@ export type Slider<O = {}, C = {}, H extends string = string> = {
|
|
|
5
5
|
moveToDirection: (
|
|
6
6
|
direction: 'prev' | 'next'
|
|
7
7
|
) => void
|
|
8
|
+
snapToClosestSlide: (
|
|
9
|
+
direction: 'prev' | 'next'
|
|
10
|
+
) => void
|
|
8
11
|
moveToSlide: (
|
|
9
12
|
index: number
|
|
10
13
|
) => void
|
|
@@ -21,6 +24,8 @@ export type SliderOptions = {
|
|
|
21
24
|
scrollBehavior: string;
|
|
22
25
|
scrollStrategy: string;
|
|
23
26
|
slidesSelector: string;
|
|
27
|
+
emulateScrollSnap: boolean;
|
|
28
|
+
emulateScrollSnapMaxThreshold: number;
|
|
24
29
|
[key: string]: any;
|
|
25
30
|
}
|
|
26
31
|
|
|
@@ -38,7 +43,16 @@ export type SliderHooks =
|
|
|
38
43
|
| HOOK_CONTENTS_CHANGED
|
|
39
44
|
| HOOK_DETAILS_CHANGED
|
|
40
45
|
| HOOK_CONTAINER_SIZE_CHANGED
|
|
41
|
-
| HOOK_ACTIVE_SLIDE_CHANGED
|
|
46
|
+
| HOOK_ACTIVE_SLIDE_CHANGED
|
|
47
|
+
| HOOK_SCROLL_START
|
|
48
|
+
| HOOK_SCROLL
|
|
49
|
+
| HOOK_SCROLL_END
|
|
50
|
+
| HOOK_NATIVE_SCROLL_START
|
|
51
|
+
| HOOK_NATIVE_SCROLL
|
|
52
|
+
| HOOK_NATIVE_SCROLL_END
|
|
53
|
+
| HOOK_PROGRAMMATIC_SCROLL_START
|
|
54
|
+
| HOOK_PROGRAMMATIC_SCROLL
|
|
55
|
+
| HOOK_PROGRAMMATIC_SCROLL_END;
|
|
42
56
|
|
|
43
57
|
export type HOOK_CREATED = 'created';
|
|
44
58
|
export type HOOK_DETAILS_CHANGED = 'detailsChanged';
|
|
@@ -46,4 +60,20 @@ export type HOOK_CONTENTS_CHANGED = 'contentsChanged';
|
|
|
46
60
|
export type HOOK_CONTAINER_SIZE_CHANGED = 'containerSizeChanged';
|
|
47
61
|
export type HOOK_ACTIVE_SLIDE_CHANGED = 'activeSlideChanged';
|
|
48
62
|
|
|
63
|
+
// any type of scroll
|
|
64
|
+
export type HOOK_SCROLL_START = 'scrollStart';
|
|
65
|
+
export type HOOK_SCROLL = 'scroll';
|
|
66
|
+
export type HOOK_SCROLL_END = 'scrollEnd';
|
|
67
|
+
|
|
68
|
+
// user initted scroll (touch, mouse wheel, etc.)
|
|
69
|
+
export type HOOK_NATIVE_SCROLL_START = 'nativeScrollStart';
|
|
70
|
+
export type HOOK_NATIVE_SCROLL = 'nativeScroll';
|
|
71
|
+
export type HOOK_NATIVE_SCROLL_END = 'nativeScrollEnd';
|
|
72
|
+
|
|
73
|
+
// programmatic scroll (e.g. el.scrollTo)
|
|
74
|
+
export type HOOK_PROGRAMMATIC_SCROLL_START = 'programmaticScrollStart';
|
|
75
|
+
export type HOOK_PROGRAMMATIC_SCROLL = 'programmaticScroll';
|
|
76
|
+
export type HOOK_PROGRAMMATIC_SCROLL_END = 'programmaticScrollEnd';
|
|
77
|
+
|
|
78
|
+
|
|
49
79
|
export type SliderPlugin = (slider: Slider) => void;
|
package/src/overflow-slider.scss
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
.overflow-slider {
|
|
10
10
|
overflow: auto;
|
|
11
11
|
width: 100%;
|
|
12
|
-
overflow: auto;
|
|
13
12
|
display: grid;
|
|
14
13
|
grid-auto-flow: column;
|
|
15
14
|
grid-template-columns: max-content;
|
|
16
15
|
max-width: max-content;
|
|
16
|
+
position: relative;
|
|
17
17
|
// hide native scrollbars
|
|
18
18
|
&::-webkit-scrollbar {
|
|
19
19
|
display: none;
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
@import 'plugins/arrows/styles.scss';
|
|
34
34
|
@import 'plugins/dots/styles.scss';
|
|
35
|
+
@import 'plugins/fade/styles.scss';
|
|
35
36
|
@import 'plugins/drag-scrolling/styles.scss';
|
|
36
37
|
@import 'plugins/scroll-indicator/styles.scss';
|
|
37
38
|
@import 'plugins/skip-links/styles.scss';
|