@evermade/overflow-slider 1.0.0 → 2.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/README.md +74 -8
- package/dist/core/details.esm.js +35 -0
- package/dist/core/details.min.js +1 -0
- package/dist/core/overflow-slider.esm.js +26 -0
- package/dist/core/overflow-slider.min.js +1 -0
- package/dist/core/slider.esm.js +260 -0
- package/dist/core/slider.min.js +1 -0
- package/dist/core/utils.esm.js +22 -0
- package/dist/core/utils.min.js +1 -0
- package/dist/index.esm.js +1 -694
- package/dist/index.min.js +1 -2
- package/dist/overflow-slider.css +1 -1
- package/dist/plugins/arrows/arrows/index.esm.js +82 -0
- package/dist/plugins/arrows/arrows/index.min.js +1 -0
- package/dist/plugins/dots/dots/index.esm.js +99 -0
- package/dist/plugins/dots/dots/index.min.js +1 -0
- package/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +70 -0
- package/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -0
- package/dist/plugins/full-width/full-width/index.esm.js +31 -0
- package/dist/plugins/full-width/full-width/index.min.js +1 -0
- package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +133 -0
- package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -0
- package/dist/plugins/skip-links/skip-links/index.esm.js +42 -0
- package/dist/plugins/skip-links/skip-links/index.min.js +1 -0
- package/dist/plugins/thumbnails/thumbnails/index.esm.js +41 -0
- package/dist/plugins/thumbnails/thumbnails/index.min.js +1 -0
- package/docs/assets/demo.css +151 -5
- package/docs/assets/demo.js +107 -10
- package/docs/dist/core/details.esm.js +35 -0
- package/docs/dist/core/details.min.js +1 -0
- package/docs/dist/core/overflow-slider.esm.js +26 -0
- package/docs/dist/core/overflow-slider.min.js +1 -0
- package/docs/dist/core/slider.esm.js +260 -0
- package/docs/dist/core/slider.min.js +1 -0
- package/docs/dist/core/utils.esm.js +22 -0
- package/docs/dist/core/utils.min.js +1 -0
- package/docs/dist/index.esm.js +1 -0
- package/docs/dist/index.min.js +1 -0
- package/docs/dist/overflow-slider.css +1 -1
- package/docs/dist/plugins/arrows/arrows/index.esm.js +82 -0
- package/docs/dist/plugins/arrows/arrows/index.min.js +1 -0
- package/docs/dist/plugins/dots/dots/index.esm.js +99 -0
- package/docs/dist/plugins/dots/dots/index.min.js +1 -0
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +70 -0
- package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -0
- package/docs/dist/plugins/full-width/full-width/index.esm.js +31 -0
- package/docs/dist/plugins/full-width/full-width/index.min.js +1 -0
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +133 -0
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -0
- package/docs/dist/plugins/skip-links/skip-links/index.esm.js +42 -0
- package/docs/dist/plugins/skip-links/skip-links/index.min.js +1 -0
- package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +41 -0
- package/docs/dist/plugins/thumbnails/thumbnails/index.min.js +1 -0
- package/docs/index.html +136 -2
- package/package.json +35 -6
- package/rollup.config.js +58 -32
- package/src/core/details.ts +1 -1
- package/src/{overflow-slider.ts → core/overflow-slider.ts} +3 -2
- package/src/core/slider.ts +62 -16
- package/src/core/types.ts +9 -1
- package/src/index.ts +1 -12
- package/src/overflow-slider.scss +10 -183
- package/src/plugins/{arrows.ts → arrows/index.ts} +13 -4
- package/src/plugins/arrows/styles.scss +29 -0
- package/src/plugins/{dots.ts → dots/index.ts} +1 -1
- package/src/plugins/dots/styles.scss +56 -0
- package/src/plugins/{drag-scrolling.ts → drag-scrolling/index.ts} +39 -35
- package/src/plugins/drag-scrolling/styles.scss +12 -0
- package/src/plugins/full-width/index.ts +43 -0
- package/src/plugins/{scroll-indicator.ts → scroll-indicator/index.ts} +36 -20
- package/src/plugins/scroll-indicator/styles.scss +59 -0
- package/src/plugins/{skip-links.ts → skip-links/index.ts} +2 -2
- package/src/plugins/skip-links/styles.scss +35 -0
- package/src/plugins/thumbnails/index.ts +53 -0
- package/tsconfig.json +14 -2
- package/dist/index.esm.min.js +0 -2
- package/dist/index.js +0 -709
- package/docs/dist/overflow-slider.esm.js +0 -694
package/README.md
CHANGED
|
@@ -12,6 +12,18 @@ Overflow Slider aims to be lightweight, mobile-first and accessible. It is desig
|
|
|
12
12
|
|
|
13
13
|
### Usage
|
|
14
14
|
|
|
15
|
+
Markup:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<div class="overflow-slider">
|
|
19
|
+
<div>Slide 1</div>
|
|
20
|
+
<div>Slide 2</div>
|
|
21
|
+
<div>Slide 3</div>
|
|
22
|
+
</div>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You don't have to use even class `overflow-slider` and slides can be whatever elements like `<a>`, `<li>`, or `article`.
|
|
26
|
+
|
|
15
27
|
If you’re using a bundler (such as Webpack or Rollup), you can install through npm:
|
|
16
28
|
|
|
17
29
|
```bash
|
|
@@ -21,14 +33,12 @@ npm install @evermade/oveflow-slider
|
|
|
21
33
|
Import the `OverflowSlider` along with plugins you want to use.
|
|
22
34
|
|
|
23
35
|
```js
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
DotsPlugin
|
|
31
|
-
} from "@evermade/overflow-slider";
|
|
36
|
+
import { OverflowSlider } from '@evermade/overflow-slider';
|
|
37
|
+
import DragScrollingPlugin from '@evermade/overflow-slider/plugins/drag-scrolling';
|
|
38
|
+
import SkipLinksPlugin from '@evermade/overflow-slider/plugins/skip-links';
|
|
39
|
+
import ArrowsPlugin from '@evermade/overflow-slider/plugins/arrows';
|
|
40
|
+
import ScrollIndicatorPlugin from '@evermade/overflow-slider/plugins/scroll-indicator';
|
|
41
|
+
import DotsPlugin from '@evermade/overflow-slider/plugins/dots';
|
|
32
42
|
|
|
33
43
|
// minimal example
|
|
34
44
|
const minimalSlider = new OverflowSlider(
|
|
@@ -91,6 +101,62 @@ You can use the CSS variables to override some values easily.
|
|
|
91
101
|
|
|
92
102
|
Note that you can easily write styles from scratch if you want to. See source code from `src/overflow-slider.scss` for reference.
|
|
93
103
|
|
|
104
|
+
## Known issues
|
|
105
|
+
|
|
106
|
+
### Drag Scrolling and Smoothness
|
|
107
|
+
|
|
108
|
+
Scroll snapping doesn't apply smoothly to drag scrolling. It might be browser limitation or some issue with implementation here. Should look into fixing it.
|
|
109
|
+
|
|
110
|
+
## Limitations
|
|
111
|
+
|
|
112
|
+
### Vertical scrolling
|
|
113
|
+
|
|
114
|
+
The library is designed to work with horizontal scrolling. Vertical scrolling is not supported and likely never will because it is not a common use case for sliders.
|
|
115
|
+
|
|
116
|
+
### RTL support
|
|
117
|
+
|
|
118
|
+
RTL support is not implemented yet. In case need arises it can be implemented but requires changes to the core and plugins.
|
|
119
|
+
|
|
120
|
+
### Looping slides
|
|
121
|
+
|
|
122
|
+
Looping slides is not supported and likely never will be. It is a feature that is not very common and it is not very accessible.
|
|
123
|
+
|
|
124
|
+
### Auto-play
|
|
125
|
+
|
|
126
|
+
Auto-play is not supported at the moment but can probably be implemented as a plugin. It is not very accessible and should be avoided if possible.
|
|
127
|
+
|
|
128
|
+
## To-do
|
|
129
|
+
|
|
130
|
+
* Make drag scrolling snapping smooth
|
|
131
|
+
* Make moveToSlide method smooth
|
|
132
|
+
* Rethink dot amount calculation
|
|
133
|
+
* Maybe split styles to separate files for plugins (but keep offering bundle as well)
|
|
134
|
+
* Maybe add plugin that adds class for visible slides
|
|
135
|
+
* Document all plugins and their parameters here
|
|
136
|
+
|
|
137
|
+
## Changelog
|
|
138
|
+
|
|
139
|
+
### 2.0.0
|
|
140
|
+
|
|
141
|
+
* Breaking: Separate plugins to their own imports/files
|
|
142
|
+
* Add: FullWidthPlugin to allow full width sliders
|
|
143
|
+
* Add: ThumbnailsPlugin to show synchronized thumbnails
|
|
144
|
+
* Add: Slider container 'data-ready' attribute when initialized to help writing CSS
|
|
145
|
+
* Add: Support for optional separate containers for prev and next arrows
|
|
146
|
+
* Add: Slides as array to Slider instance
|
|
147
|
+
* Add: Active slide ID to Slider instance as activeSlideIdx and hook activeSlideChanged
|
|
148
|
+
* Fix: DragScrollingPlugin dragging clickable slides in Firefox
|
|
149
|
+
* Fix: DragScrollingPlugin dragging outside of container bugs in Firefox/Safari
|
|
150
|
+
* Fix: ScrollIndicatorPlugin width calculation when scrollbar and container are not same width
|
|
151
|
+
|
|
152
|
+
### 1.1.0
|
|
153
|
+
|
|
154
|
+
* Add: Grab cursor when hovering slider that has DragScrollingPlugin
|
|
155
|
+
* Add: Example of using entrance and exit animations for slides
|
|
156
|
+
* Fix: ScrollIndicatorPlugin dragging works now with touch
|
|
157
|
+
* Fix: Hide native scrollbar also in Firefox + Edge
|
|
158
|
+
* Docs: Add more info on required markup and limitations
|
|
159
|
+
|
|
94
160
|
## Development
|
|
95
161
|
|
|
96
162
|
Install tools `npm install` and build `npm run build` or develop with `npm run watch`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function details(slider) {
|
|
2
|
+
var _a;
|
|
3
|
+
let instance;
|
|
4
|
+
let hasOverflow = false;
|
|
5
|
+
let slideCount = 0;
|
|
6
|
+
let containerWidth = 0;
|
|
7
|
+
let scrollableAreaWidth = 0;
|
|
8
|
+
let amountOfPages = 0;
|
|
9
|
+
let currentPage = 1;
|
|
10
|
+
if (slider.container.scrollWidth > slider.container.clientWidth) {
|
|
11
|
+
hasOverflow = true;
|
|
12
|
+
}
|
|
13
|
+
slideCount = (_a = slider.slides.length) !== null && _a !== void 0 ? _a : 0;
|
|
14
|
+
containerWidth = slider.container.offsetWidth;
|
|
15
|
+
scrollableAreaWidth = slider.container.scrollWidth;
|
|
16
|
+
amountOfPages = Math.ceil(scrollableAreaWidth / containerWidth);
|
|
17
|
+
if (slider.container.scrollLeft >= 0) {
|
|
18
|
+
currentPage = Math.floor(slider.container.scrollLeft / containerWidth);
|
|
19
|
+
// consider as last page if the scrollLeft + containerWidth is equal to scrollWidth
|
|
20
|
+
if (slider.container.scrollLeft + containerWidth === scrollableAreaWidth) {
|
|
21
|
+
currentPage = amountOfPages - 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
instance = {
|
|
25
|
+
hasOverflow,
|
|
26
|
+
slideCount,
|
|
27
|
+
containerWidth,
|
|
28
|
+
scrollableAreaWidth,
|
|
29
|
+
amountOfPages,
|
|
30
|
+
currentPage,
|
|
31
|
+
};
|
|
32
|
+
return instance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { details as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t){var e;let n,l=!1,o=0,r=0,i=0,a=0,c=1;return t.container.scrollWidth>t.container.clientWidth&&(l=!0),o=null!==(e=t.slides.length)&&void 0!==e?e:0,r=t.container.offsetWidth,i=t.container.scrollWidth,a=Math.ceil(i/r),t.container.scrollLeft>=0&&(c=Math.floor(t.container.scrollLeft/r),t.container.scrollLeft+r===i&&(c=a-1)),n={hasOverflow:l,slideCount:o,containerWidth:r,scrollableAreaWidth:i,amountOfPages:a,currentPage:c},n}export{t as default};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Slider from './slider.esm.js';
|
|
2
|
+
|
|
3
|
+
function OverflowSlider(container, options, plugins) {
|
|
4
|
+
try {
|
|
5
|
+
// check that container HTML element
|
|
6
|
+
if (!(container instanceof Element)) {
|
|
7
|
+
throw new Error(`Container must be HTML element, found ${typeof container}`);
|
|
8
|
+
}
|
|
9
|
+
const defaults = {
|
|
10
|
+
scrollBehavior: "smooth",
|
|
11
|
+
scrollStrategy: "fullSlide",
|
|
12
|
+
slidesSelector: ":scope > *",
|
|
13
|
+
};
|
|
14
|
+
const sliderOptions = Object.assign(Object.assign({}, defaults), options);
|
|
15
|
+
// disable smooth scrolling if user prefers reduced motion
|
|
16
|
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
17
|
+
sliderOptions.scrollBehavior = "auto";
|
|
18
|
+
}
|
|
19
|
+
return Slider(container, sliderOptions, plugins);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
console.error(e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { OverflowSlider as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./slider.min.js";function o(o,r,t){try{if(!(o instanceof Element))throw new Error("Container must be HTML element, found "+typeof o);const s={scrollBehavior:"smooth",scrollStrategy:"fullSlide",slidesSelector:":scope > *"},n=Object.assign(Object.assign({},s),r);return window.matchMedia("(prefers-reduced-motion: reduce)").matches&&(n.scrollBehavior="auto"),e(o,n,t)}catch(e){console.error(e)}}export{o as default};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import details from './details.esm.js';
|
|
2
|
+
import { generateId, objectsAreEqual } from './utils.esm.js';
|
|
3
|
+
|
|
4
|
+
function Slider(container, options, plugins) {
|
|
5
|
+
let slider;
|
|
6
|
+
let subs = {};
|
|
7
|
+
function init() {
|
|
8
|
+
slider.container = container;
|
|
9
|
+
// ensure container has id
|
|
10
|
+
let containerId = container.getAttribute('id');
|
|
11
|
+
if (containerId === null) {
|
|
12
|
+
containerId = generateId('overflow-slider');
|
|
13
|
+
container.setAttribute('id', containerId);
|
|
14
|
+
}
|
|
15
|
+
setSlides();
|
|
16
|
+
setDetails(true);
|
|
17
|
+
setActiveSlideIdx();
|
|
18
|
+
slider.on('contentsChanged', () => {
|
|
19
|
+
setSlides();
|
|
20
|
+
setDetails();
|
|
21
|
+
setActiveSlideIdx();
|
|
22
|
+
});
|
|
23
|
+
slider.on('containerSizeChanged', () => setDetails());
|
|
24
|
+
let requestId = 0;
|
|
25
|
+
const setDetailsDebounce = () => {
|
|
26
|
+
if (requestId) {
|
|
27
|
+
window.cancelAnimationFrame(requestId);
|
|
28
|
+
}
|
|
29
|
+
requestId = window.requestAnimationFrame(() => {
|
|
30
|
+
setDetails();
|
|
31
|
+
setActiveSlideIdx();
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
slider.on('scroll', setDetailsDebounce);
|
|
35
|
+
addEventListeners();
|
|
36
|
+
setDataAttributes();
|
|
37
|
+
setCSSVariables();
|
|
38
|
+
if (plugins) {
|
|
39
|
+
for (const plugin of plugins) {
|
|
40
|
+
plugin(slider);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
slider.on('detailsChanged', () => {
|
|
44
|
+
setDataAttributes();
|
|
45
|
+
setCSSVariables();
|
|
46
|
+
});
|
|
47
|
+
slider.emit('created');
|
|
48
|
+
slider.container.setAttribute('data-ready', 'true');
|
|
49
|
+
}
|
|
50
|
+
function setDetails(isInit = false) {
|
|
51
|
+
const oldDetails = slider.details;
|
|
52
|
+
const newDetails = details(slider);
|
|
53
|
+
slider.details = newDetails;
|
|
54
|
+
if (!isInit && !objectsAreEqual(oldDetails, newDetails)) {
|
|
55
|
+
slider.emit('detailsChanged');
|
|
56
|
+
}
|
|
57
|
+
else if (isInit) {
|
|
58
|
+
slider.emit('detailsChanged');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function setSlides() {
|
|
62
|
+
slider.slides = Array.from(slider.container.querySelectorAll(slider.options.slidesSelector));
|
|
63
|
+
}
|
|
64
|
+
function addEventListeners() {
|
|
65
|
+
// changes to DOM
|
|
66
|
+
const observer = new MutationObserver(() => slider.emit('contentsChanged'));
|
|
67
|
+
observer.observe(slider.container, { childList: true });
|
|
68
|
+
// container size changes
|
|
69
|
+
const resizeObserver = new ResizeObserver(() => slider.emit('containerSizeChanged'));
|
|
70
|
+
resizeObserver.observe(slider.container);
|
|
71
|
+
// scroll event with debouncing
|
|
72
|
+
slider.container.addEventListener('scroll', () => slider.emit('scroll'));
|
|
73
|
+
// Listen for mouse down and touch start events on the document
|
|
74
|
+
// This handles both mouse clicks and touch interactions
|
|
75
|
+
let wasInteractedWith = false;
|
|
76
|
+
slider.container.addEventListener('mousedown', () => {
|
|
77
|
+
wasInteractedWith = true;
|
|
78
|
+
});
|
|
79
|
+
slider.container.addEventListener('touchstart', () => {
|
|
80
|
+
wasInteractedWith = true;
|
|
81
|
+
}, { passive: true });
|
|
82
|
+
slider.container.addEventListener('focusin', (e) => {
|
|
83
|
+
// move target parents as long as they are not the container
|
|
84
|
+
// but only if focus didn't start from mouse or touch
|
|
85
|
+
if (!wasInteractedWith) {
|
|
86
|
+
let target = e.target;
|
|
87
|
+
while (target.parentElement !== slider.container) {
|
|
88
|
+
if (target.parentElement) {
|
|
89
|
+
target = target.parentElement;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
ensureSlideIsInView(target);
|
|
96
|
+
}
|
|
97
|
+
wasInteractedWith = false;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function setCSSVariables() {
|
|
101
|
+
slider.container.style.setProperty('--slider-container-width', `${slider.details.containerWidth}px`);
|
|
102
|
+
slider.container.style.setProperty('--slider-scrollable-width', `${slider.details.scrollableAreaWidth}px`);
|
|
103
|
+
slider.container.style.setProperty('--slider-slides-count', `${slider.details.slideCount}`);
|
|
104
|
+
}
|
|
105
|
+
function setDataAttributes() {
|
|
106
|
+
slider.container.setAttribute('data-has-overflow', slider.details.hasOverflow ? 'true' : 'false');
|
|
107
|
+
}
|
|
108
|
+
function ensureSlideIsInView(slide) {
|
|
109
|
+
const slideRect = slide.getBoundingClientRect();
|
|
110
|
+
const sliderRect = slider.container.getBoundingClientRect();
|
|
111
|
+
const containerWidth = slider.container.offsetWidth;
|
|
112
|
+
const scrollLeft = slider.container.scrollLeft;
|
|
113
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
114
|
+
const slideEnd = slideStart + slideRect.width;
|
|
115
|
+
let scrollTarget = null;
|
|
116
|
+
if (slideStart < scrollLeft) {
|
|
117
|
+
scrollTarget = slideStart;
|
|
118
|
+
}
|
|
119
|
+
else if (slideEnd > scrollLeft + containerWidth) {
|
|
120
|
+
scrollTarget = slideEnd - containerWidth;
|
|
121
|
+
}
|
|
122
|
+
else if (slideStart === 0) {
|
|
123
|
+
scrollTarget = 0;
|
|
124
|
+
}
|
|
125
|
+
if (scrollTarget !== null) {
|
|
126
|
+
slider.container.style.scrollSnapType = 'none';
|
|
127
|
+
slider.container.scrollLeft = scrollTarget;
|
|
128
|
+
// @todo resume scroll snapping but at least proximity gives a lot of trouble
|
|
129
|
+
// and it's not really needed for this use case but it would be nice to have
|
|
130
|
+
// it back in case it's needed. We need to calculate scrollLeft some other way
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function setActiveSlideIdx() {
|
|
134
|
+
const sliderRect = slider.container.getBoundingClientRect();
|
|
135
|
+
const scrollLeft = slider.container.scrollLeft;
|
|
136
|
+
const slides = slider.slides;
|
|
137
|
+
let activeSlideIdx = 0;
|
|
138
|
+
for (let i = 0; i < slides.length; i++) {
|
|
139
|
+
const slideRect = slides[i].getBoundingClientRect();
|
|
140
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
|
|
141
|
+
if (slideStart > scrollLeft) {
|
|
142
|
+
activeSlideIdx = i;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const oldActiveSlideIdx = slider.activeSlideIdx;
|
|
147
|
+
slider.activeSlideIdx = activeSlideIdx;
|
|
148
|
+
if (oldActiveSlideIdx !== activeSlideIdx) {
|
|
149
|
+
slider.emit('activeSlideChanged');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function moveToSlide(idx) {
|
|
153
|
+
const slide = slider.slides[idx];
|
|
154
|
+
if (slide) {
|
|
155
|
+
ensureSlideIsInView(slide);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getGapSize() {
|
|
159
|
+
let gapSize = 0;
|
|
160
|
+
if (slider.slides.length > 1) {
|
|
161
|
+
const firstSlideRect = slider.slides[0].getBoundingClientRect();
|
|
162
|
+
const secondSlideRect = slider.slides[1].getBoundingClientRect();
|
|
163
|
+
gapSize = secondSlideRect.left - firstSlideRect.right;
|
|
164
|
+
}
|
|
165
|
+
return gapSize;
|
|
166
|
+
}
|
|
167
|
+
function moveToDirection(direction = "prev") {
|
|
168
|
+
const scrollStrategy = slider.options.scrollStrategy;
|
|
169
|
+
const scrollLeft = slider.container.scrollLeft;
|
|
170
|
+
const sliderRect = slider.container.getBoundingClientRect();
|
|
171
|
+
const containerWidth = slider.container.offsetWidth;
|
|
172
|
+
let targetScrollPosition = scrollLeft;
|
|
173
|
+
if (direction === 'prev') {
|
|
174
|
+
targetScrollPosition = Math.max(0, scrollLeft - slider.container.offsetWidth);
|
|
175
|
+
}
|
|
176
|
+
else if (direction === 'next') {
|
|
177
|
+
targetScrollPosition = Math.min(slider.container.scrollWidth, scrollLeft + slider.container.offsetWidth);
|
|
178
|
+
}
|
|
179
|
+
if (scrollStrategy === 'fullSlide') {
|
|
180
|
+
let fullSldeTargetScrollPosition = null;
|
|
181
|
+
// extend targetScrollPosition to include gap
|
|
182
|
+
if (direction === 'prev') {
|
|
183
|
+
fullSldeTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
fullSldeTargetScrollPosition = Math.min(slider.container.scrollWidth, targetScrollPosition + getGapSize());
|
|
187
|
+
}
|
|
188
|
+
if (direction === 'next') {
|
|
189
|
+
let partialSlideFound = false;
|
|
190
|
+
for (let slide of slider.slides) {
|
|
191
|
+
const slideRect = slide.getBoundingClientRect();
|
|
192
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
193
|
+
const slideEnd = slideStart + slideRect.width;
|
|
194
|
+
if (slideStart < targetScrollPosition && slideEnd > targetScrollPosition) {
|
|
195
|
+
fullSldeTargetScrollPosition = slideStart;
|
|
196
|
+
partialSlideFound = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!partialSlideFound) {
|
|
201
|
+
fullSldeTargetScrollPosition = Math.min(targetScrollPosition, slider.container.scrollWidth - slider.container.offsetWidth);
|
|
202
|
+
}
|
|
203
|
+
if (fullSldeTargetScrollPosition && fullSldeTargetScrollPosition > scrollLeft) {
|
|
204
|
+
targetScrollPosition = fullSldeTargetScrollPosition;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
let partialSlideFound = false;
|
|
209
|
+
for (let slide of slider.slides) {
|
|
210
|
+
const slideRect = slide.getBoundingClientRect();
|
|
211
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft;
|
|
212
|
+
const slideEnd = slideStart + slideRect.width;
|
|
213
|
+
if (slideStart < scrollLeft && slideEnd > scrollLeft) {
|
|
214
|
+
fullSldeTargetScrollPosition = slideEnd - containerWidth;
|
|
215
|
+
partialSlideFound = true;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (!partialSlideFound) {
|
|
220
|
+
fullSldeTargetScrollPosition = Math.max(0, scrollLeft - containerWidth);
|
|
221
|
+
}
|
|
222
|
+
if (fullSldeTargetScrollPosition && fullSldeTargetScrollPosition < scrollLeft) {
|
|
223
|
+
targetScrollPosition = fullSldeTargetScrollPosition;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
slider.container.style.scrollBehavior = slider.options.scrollBehavior;
|
|
228
|
+
slider.container.scrollLeft = targetScrollPosition;
|
|
229
|
+
setTimeout(() => slider.container.style.scrollBehavior = '', 50);
|
|
230
|
+
}
|
|
231
|
+
function on(name, cb) {
|
|
232
|
+
if (!subs[name]) {
|
|
233
|
+
subs[name] = [];
|
|
234
|
+
}
|
|
235
|
+
subs[name].push(cb);
|
|
236
|
+
}
|
|
237
|
+
function emit(name) {
|
|
238
|
+
var _a;
|
|
239
|
+
if (subs && subs[name]) {
|
|
240
|
+
subs[name].forEach(cb => {
|
|
241
|
+
cb(slider);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const optionCallBack = (_a = slider === null || slider === void 0 ? void 0 : slider.options) === null || _a === void 0 ? void 0 : _a[name];
|
|
245
|
+
if (typeof optionCallBack === 'function') {
|
|
246
|
+
optionCallBack(slider);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
slider = {
|
|
250
|
+
emit,
|
|
251
|
+
moveToDirection,
|
|
252
|
+
moveToSlide,
|
|
253
|
+
on,
|
|
254
|
+
options,
|
|
255
|
+
};
|
|
256
|
+
init();
|
|
257
|
+
return slider;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export { Slider as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./details.min.js";import{generateId as t,objectsAreEqual as n}from"./utils.min.js";function i(i,o,l){let r,s={};function a(t=!1){const i=r.details,o=e(r);r.details=o,t||n(i,o)?t&&r.emit("detailsChanged"):r.emit("detailsChanged")}function c(){r.slides=Array.from(r.container.querySelectorAll(r.options.slidesSelector))}function d(){r.container.style.setProperty("--slider-container-width",`${r.details.containerWidth}px`),r.container.style.setProperty("--slider-scrollable-width",`${r.details.scrollableAreaWidth}px`),r.container.style.setProperty("--slider-slides-count",`${r.details.slideCount}`)}function f(){r.container.setAttribute("data-has-overflow",r.details.hasOverflow?"true":"false")}function u(e){const t=e.getBoundingClientRect(),n=r.container.getBoundingClientRect(),i=r.container.offsetWidth,o=r.container.scrollLeft,l=t.left-n.left+o,s=l+t.width;let a=null;l<o?a=l:s>o+i?a=s-i:0===l&&(a=0),null!==a&&(r.container.style.scrollSnapType="none",r.container.scrollLeft=a)}function h(){const e=r.container.getBoundingClientRect(),t=r.container.scrollLeft,n=r.slides;let i=0;for(let o=0;o<n.length;o++){if(n[o].getBoundingClientRect().left-e.left+t+g()>t){i=o;break}}const o=r.activeSlideIdx;r.activeSlideIdx=i,o!==i&&r.emit("activeSlideChanged")}function g(){let e=0;if(r.slides.length>1){const t=r.slides[0].getBoundingClientRect();e=r.slides[1].getBoundingClientRect().left-t.right}return e}return r={emit:function(e){var t;s&&s[e]&&s[e].forEach((e=>{e(r)}));const n=null===(t=null==r?void 0:r.options)||void 0===t?void 0:t[e];"function"==typeof n&&n(r)},moveToDirection:function(e="prev"){const t=r.options.scrollStrategy,n=r.container.scrollLeft,i=r.container.getBoundingClientRect(),o=r.container.offsetWidth;let l=n;if("prev"===e?l=Math.max(0,n-r.container.offsetWidth):"next"===e&&(l=Math.min(r.container.scrollWidth,n+r.container.offsetWidth)),"fullSlide"===t){let t=null;if(t="prev"===e?Math.max(0,l-g()):Math.min(r.container.scrollWidth,l+g()),"next"===e){let e=!1;for(let o of r.slides){const r=o.getBoundingClientRect(),s=r.left-i.left+n,a=s+r.width;if(s<l&&a>l){t=s,e=!0;break}}e||(t=Math.min(l,r.container.scrollWidth-r.container.offsetWidth)),t&&t>n&&(l=t)}else{let e=!1;for(let l of r.slides){const r=l.getBoundingClientRect(),s=r.left-i.left+n,a=s+r.width;if(s<n&&a>n){t=a-o,e=!0;break}}e||(t=Math.max(0,n-o)),t&&t<n&&(l=t)}}r.container.style.scrollBehavior=r.options.scrollBehavior,r.container.scrollLeft=l,setTimeout((()=>r.container.style.scrollBehavior=""),50)},moveToSlide:function(e){const t=r.slides[e];t&&u(t)},on:function(e,t){s[e]||(s[e]=[]),s[e].push(t)},options:o},function(){r.container=i;let e=i.getAttribute("id");null===e&&(e=t("overflow-slider"),i.setAttribute("id",e)),c(),a(!0),h(),r.on("contentsChanged",(()=>{c(),a(),h()})),r.on("containerSizeChanged",(()=>a()));let n=0;if(r.on("scroll",(()=>{n&&window.cancelAnimationFrame(n),n=window.requestAnimationFrame((()=>{a(),h()}))})),function(){new MutationObserver((()=>r.emit("contentsChanged"))).observe(r.container,{childList:!0});new ResizeObserver((()=>r.emit("containerSizeChanged"))).observe(r.container),r.container.addEventListener("scroll",(()=>r.emit("scroll")));let e=!1;r.container.addEventListener("mousedown",(()=>{e=!0})),r.container.addEventListener("touchstart",(()=>{e=!0}),{passive:!0}),r.container.addEventListener("focusin",(t=>{if(!e){let e=t.target;for(;e.parentElement!==r.container&&e.parentElement;)e=e.parentElement;u(e)}e=!1}))}(),f(),d(),l)for(const e of l)e(r);r.on("detailsChanged",(()=>{f(),d()})),r.emit("created"),r.container.setAttribute("data-ready","true")}(),r}export{i as default};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function generateId(prefix, i = 1) {
|
|
2
|
+
const id = `${prefix}-${i}`;
|
|
3
|
+
if (document.getElementById(id)) {
|
|
4
|
+
return generateId(prefix, i + 1);
|
|
5
|
+
}
|
|
6
|
+
return id;
|
|
7
|
+
}
|
|
8
|
+
function objectsAreEqual(obj1, obj2) {
|
|
9
|
+
const keys1 = Object.keys(obj1);
|
|
10
|
+
const keys2 = Object.keys(obj2);
|
|
11
|
+
if (keys1.length !== keys2.length) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
for (let key of keys1) {
|
|
15
|
+
if (obj2.hasOwnProperty(key) === false || obj1[key] !== obj2[key]) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { generateId, objectsAreEqual };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(e,n=1){const r=`${e}-${n}`;return document.getElementById(r)?t(e,n+1):r}function e(t,e){const n=Object.keys(t),r=Object.keys(e);if(n.length!==r.length)return!1;for(let r of n)if(!1===e.hasOwnProperty(r)||t[r]!==e[r])return!1;return!0}export{t as generateId,e as objectsAreEqual};
|