@evermade/overflow-slider 4.2.1 → 4.2.3
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/.github/workflows/npm-publish.yml +40 -22
- package/.github/workflows/publish.yml +35 -0
- package/CHANGELOG.md +116 -0
- package/README.md +55 -107
- package/RELEASE.md +44 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +107 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/overflow-slider.css +1 -1
- package/dist/plugins/arrows/index.d.ts +1 -1
- package/dist/plugins/autoplay/index.d.ts +1 -1
- package/dist/plugins/classnames/index.d.ts +1 -1
- package/dist/plugins/core/index.d.ts +10 -63
- package/dist/plugins/core/index.d2.ts +64 -10
- package/dist/plugins/dots/index.d.ts +1 -1
- package/dist/plugins/drag-scrolling/index.d.ts +1 -1
- package/dist/plugins/fade/index.d.ts +1 -1
- package/dist/plugins/full-width/index.d.ts +1 -1
- package/dist/plugins/scroll-indicator/index.d.ts +1 -1
- package/dist/plugins/skip-links/index.d.ts +1 -1
- package/dist/plugins/thumbnails/index.d.ts +3 -3
- package/dist/plugins/thumbnails/index.esm.js +4 -4
- package/dist/plugins/thumbnails/index.min.js +1 -1
- package/docs/assets/demo.css +5 -0
- package/docs/assets/demo.js +11 -0
- package/docs/dist/index.d.ts +1 -1
- package/docs/dist/index.esm.js +107 -15
- package/docs/dist/index.esm.js.map +1 -1
- package/docs/dist/index.min.js +1 -1
- package/docs/dist/index.min.js.map +1 -1
- package/docs/dist/overflow-slider.css +1 -1
- package/docs/dist/plugins/arrows/index.d.ts +1 -1
- package/docs/dist/plugins/autoplay/index.d.ts +1 -1
- package/docs/dist/plugins/classnames/index.d.ts +1 -1
- package/docs/dist/plugins/core/index.d.ts +10 -63
- package/docs/dist/plugins/core/index.d2.ts +64 -10
- package/docs/dist/plugins/dots/index.d.ts +1 -1
- package/docs/dist/plugins/drag-scrolling/index.d.ts +1 -1
- package/docs/dist/plugins/fade/index.d.ts +1 -1
- package/docs/dist/plugins/full-width/index.d.ts +1 -1
- package/docs/dist/plugins/scroll-indicator/index.d.ts +1 -1
- package/docs/dist/plugins/skip-links/index.d.ts +1 -1
- package/docs/dist/plugins/thumbnails/index.d.ts +3 -3
- package/docs/dist/plugins/thumbnails/index.esm.js +4 -4
- package/docs/dist/plugins/thumbnails/index.min.js +1 -1
- package/docs/index.html +29 -0
- package/package.json +1 -1
- package/src/core/slider.ts +111 -12
- package/src/core/types.ts +4 -1
- package/src/overflow-slider.scss +1 -0
- package/src/plugins/thumbnails/index.ts +4 -4
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { SliderOptionArgs, SliderPlugin, SliderCallback, SliderOptions, SliderDetails } from './index.d2.ts';
|
|
2
|
+
|
|
3
|
+
declare function OverflowSlider(container: HTMLElement, options?: SliderOptionArgs, plugins?: SliderPlugin[]): {
|
|
2
4
|
container: HTMLElement;
|
|
3
5
|
slides: HTMLElement[];
|
|
4
|
-
emit: (name:
|
|
5
|
-
moveToDirection: (direction:
|
|
6
|
-
moveToSlideInDirection: (direction:
|
|
7
|
-
snapToClosestSlide: (direction:
|
|
6
|
+
emit: (name: string) => void;
|
|
7
|
+
moveToDirection: (direction: "prev" | "next") => void;
|
|
8
|
+
moveToSlideInDirection: (direction: "prev" | "next") => void;
|
|
9
|
+
snapToClosestSlide: (direction: "prev" | "next") => void;
|
|
8
10
|
moveToSlide: (index: number) => void;
|
|
9
11
|
canMoveToSlide: (index: number) => boolean;
|
|
10
12
|
getInclusiveScrollWidth: () => number;
|
|
@@ -14,65 +16,10 @@ type Slider<O = {}, C = {}, H extends string = string> = {
|
|
|
14
16
|
getScrollLeft: () => number;
|
|
15
17
|
setScrollLeft: (value: number) => void;
|
|
16
18
|
setActiveSlideIdx: () => void;
|
|
17
|
-
on: (name:
|
|
19
|
+
on: (name: string, cb: SliderCallback) => void;
|
|
18
20
|
options: SliderOptions;
|
|
19
21
|
details: SliderDetails;
|
|
20
22
|
activeSlideIdx: number;
|
|
21
|
-
}
|
|
22
|
-
type SliderCallback<O = {}, C = {}, H extends string = string> = (props: Slider<O, C, H>) => void;
|
|
23
|
-
/**
|
|
24
|
-
* Recursively makes all properties of T optional.
|
|
25
|
-
* @see https://www.typescriptlang.org/docs/handbook/utility-types.html#mapped-types
|
|
26
|
-
*/
|
|
27
|
-
type DeepPartial<T> = {
|
|
28
|
-
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
29
|
-
};
|
|
30
|
-
type SliderOptions = {
|
|
31
|
-
scrollBehavior: string;
|
|
32
|
-
scrollStrategy: string;
|
|
33
|
-
slidesSelector: string;
|
|
34
|
-
emulateScrollSnap: boolean;
|
|
35
|
-
emulateScrollSnapMaxThreshold?: number;
|
|
36
|
-
cssVariableContainer: HTMLElement;
|
|
37
|
-
rtl: boolean;
|
|
38
|
-
targetWidth?: (slider: Slider) => number;
|
|
39
|
-
[key: string]: unknown;
|
|
40
|
-
};
|
|
41
|
-
type SliderOptionArgs = {
|
|
42
|
-
scrollBehavior?: 'smooth' | 'auto';
|
|
43
|
-
scrollStrategy?: 'fullSlide' | 'partialSlide';
|
|
44
|
-
slidesSelector?: string;
|
|
45
|
-
emulateScrollSnap?: boolean;
|
|
46
|
-
emulateScrollSnapMaxThreshold?: number;
|
|
47
|
-
cssVariableContainer?: HTMLElement;
|
|
48
|
-
rtl?: boolean;
|
|
49
|
-
targetWidth?: (slider: Slider) => number;
|
|
50
|
-
[key: string]: unknown;
|
|
51
|
-
};
|
|
52
|
-
type SliderDetails = {
|
|
53
|
-
hasOverflow: boolean;
|
|
54
|
-
slideCount: number;
|
|
55
|
-
containerWidth: number;
|
|
56
|
-
containerHeight: number;
|
|
57
|
-
scrollableAreaWidth: number;
|
|
58
|
-
amountOfPages: number;
|
|
59
|
-
currentPage: number;
|
|
60
|
-
};
|
|
61
|
-
type SliderHooks = HOOK_CREATED | HOOK_CONTENTS_CHANGED | HOOK_DETAILS_CHANGED | HOOK_CONTAINER_SIZE_CHANGED | HOOK_ACTIVE_SLIDE_CHANGED | HOOK_SCROLL_START | HOOK_SCROLL | HOOK_SCROLL_END | HOOK_NATIVE_SCROLL_START | HOOK_NATIVE_SCROLL | HOOK_NATIVE_SCROLL_END | HOOK_PROGRAMMATIC_SCROLL_START | HOOK_PROGRAMMATIC_SCROLL | HOOK_PROGRAMMATIC_SCROLL_END;
|
|
62
|
-
type HOOK_CREATED = 'created';
|
|
63
|
-
type HOOK_DETAILS_CHANGED = 'detailsChanged';
|
|
64
|
-
type HOOK_CONTENTS_CHANGED = 'contentsChanged';
|
|
65
|
-
type HOOK_CONTAINER_SIZE_CHANGED = 'containerSizeChanged';
|
|
66
|
-
type HOOK_ACTIVE_SLIDE_CHANGED = 'activeSlideChanged';
|
|
67
|
-
type HOOK_SCROLL_START = 'scrollStart';
|
|
68
|
-
type HOOK_SCROLL = 'scroll';
|
|
69
|
-
type HOOK_SCROLL_END = 'scrollEnd';
|
|
70
|
-
type HOOK_NATIVE_SCROLL_START = 'nativeScrollStart';
|
|
71
|
-
type HOOK_NATIVE_SCROLL = 'nativeScroll';
|
|
72
|
-
type HOOK_NATIVE_SCROLL_END = 'nativeScrollEnd';
|
|
73
|
-
type HOOK_PROGRAMMATIC_SCROLL_START = 'programmaticScrollStart';
|
|
74
|
-
type HOOK_PROGRAMMATIC_SCROLL = 'programmaticScroll';
|
|
75
|
-
type HOOK_PROGRAMMATIC_SCROLL_END = 'programmaticScrollEnd';
|
|
76
|
-
type SliderPlugin = (slider: Slider) => void;
|
|
23
|
+
} | undefined;
|
|
77
24
|
|
|
78
|
-
export
|
|
25
|
+
export { OverflowSlider as default };
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
declare function OverflowSlider(container: HTMLElement, options?: SliderOptionArgs, plugins?: SliderPlugin[]): {
|
|
1
|
+
type Slider<O = {}, C = {}, H extends string = string> = {
|
|
4
2
|
container: HTMLElement;
|
|
5
3
|
slides: HTMLElement[];
|
|
6
|
-
emit: (name:
|
|
7
|
-
moveToDirection: (direction:
|
|
8
|
-
moveToSlideInDirection: (direction:
|
|
9
|
-
snapToClosestSlide: (direction:
|
|
4
|
+
emit: (name: H | SliderHooks) => void;
|
|
5
|
+
moveToDirection: (direction: 'prev' | 'next') => void;
|
|
6
|
+
moveToSlideInDirection: (direction: 'prev' | 'next') => void;
|
|
7
|
+
snapToClosestSlide: (direction: 'prev' | 'next') => void;
|
|
10
8
|
moveToSlide: (index: number) => void;
|
|
11
9
|
canMoveToSlide: (index: number) => boolean;
|
|
12
10
|
getInclusiveScrollWidth: () => number;
|
|
@@ -16,10 +14,66 @@ declare function OverflowSlider(container: HTMLElement, options?: SliderOptionAr
|
|
|
16
14
|
getScrollLeft: () => number;
|
|
17
15
|
setScrollLeft: (value: number) => void;
|
|
18
16
|
setActiveSlideIdx: () => void;
|
|
19
|
-
on: (name:
|
|
17
|
+
on: (name: H | SliderHooks, cb: SliderCallback) => void;
|
|
20
18
|
options: SliderOptions;
|
|
21
19
|
details: SliderDetails;
|
|
22
20
|
activeSlideIdx: number;
|
|
23
|
-
}
|
|
21
|
+
} & C;
|
|
22
|
+
type SliderCallback<O = {}, C = {}, H extends string = string> = (props: Slider<O, C, H>) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Recursively makes all properties of T optional.
|
|
25
|
+
* @see https://www.typescriptlang.org/docs/handbook/utility-types.html#mapped-types
|
|
26
|
+
*/
|
|
27
|
+
type DeepPartial<T> = {
|
|
28
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
29
|
+
};
|
|
30
|
+
type SliderOptions = {
|
|
31
|
+
scrollBehavior: string;
|
|
32
|
+
scrollStrategy: string;
|
|
33
|
+
slidesSelector: string;
|
|
34
|
+
emulateScrollSnap: boolean;
|
|
35
|
+
emulateScrollSnapMaxThreshold?: number;
|
|
36
|
+
cssVariableContainer: HTMLElement;
|
|
37
|
+
rtl: boolean;
|
|
38
|
+
targetWidth?: (slider: Slider) => number;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
};
|
|
41
|
+
type SliderOptionArgs = {
|
|
42
|
+
scrollBehavior?: 'smooth' | 'auto';
|
|
43
|
+
scrollStrategy?: 'fullSlide' | 'partialSlide';
|
|
44
|
+
slidesSelector?: string;
|
|
45
|
+
emulateScrollSnap?: boolean;
|
|
46
|
+
emulateScrollSnapMaxThreshold?: number;
|
|
47
|
+
cssVariableContainer?: HTMLElement;
|
|
48
|
+
rtl?: boolean;
|
|
49
|
+
targetWidth?: (slider: Slider) => number;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
};
|
|
52
|
+
type SliderDetails = {
|
|
53
|
+
hasOverflow: boolean;
|
|
54
|
+
slideCount: number;
|
|
55
|
+
containerWidth: number;
|
|
56
|
+
containerHeight: number;
|
|
57
|
+
scrollableAreaWidth: number;
|
|
58
|
+
amountOfPages: number;
|
|
59
|
+
currentPage: number;
|
|
60
|
+
};
|
|
61
|
+
type SliderHooks = HOOK_CREATED | HOOK_CONTENTS_CHANGED | HOOK_DETAILS_CHANGED | HOOK_CONTAINER_SIZE_CHANGED | HOOK_ACTIVE_SLIDE_CHANGED | HOOK_SCROLL_START | HOOK_SCROLL | HOOK_SCROLL_END | HOOK_NATIVE_SCROLL_START | HOOK_NATIVE_SCROLL | HOOK_NATIVE_SCROLL_END | HOOK_PROGRAMMATIC_SCROLL_START | HOOK_PROGRAMMATIC_SCROLL | HOOK_PROGRAMMATIC_SCROLL_END | HOOK_FOCUS_SCROLL;
|
|
62
|
+
type HOOK_CREATED = 'created';
|
|
63
|
+
type HOOK_DETAILS_CHANGED = 'detailsChanged';
|
|
64
|
+
type HOOK_CONTENTS_CHANGED = 'contentsChanged';
|
|
65
|
+
type HOOK_CONTAINER_SIZE_CHANGED = 'containerSizeChanged';
|
|
66
|
+
type HOOK_ACTIVE_SLIDE_CHANGED = 'activeSlideChanged';
|
|
67
|
+
type HOOK_SCROLL_START = 'scrollStart';
|
|
68
|
+
type HOOK_SCROLL = 'scroll';
|
|
69
|
+
type HOOK_SCROLL_END = 'scrollEnd';
|
|
70
|
+
type HOOK_NATIVE_SCROLL_START = 'nativeScrollStart';
|
|
71
|
+
type HOOK_NATIVE_SCROLL = 'nativeScroll';
|
|
72
|
+
type HOOK_NATIVE_SCROLL_END = 'nativeScrollEnd';
|
|
73
|
+
type HOOK_PROGRAMMATIC_SCROLL_START = 'programmaticScrollStart';
|
|
74
|
+
type HOOK_PROGRAMMATIC_SCROLL = 'programmaticScroll';
|
|
75
|
+
type HOOK_PROGRAMMATIC_SCROLL_END = 'programmaticScrollEnd';
|
|
76
|
+
type HOOK_FOCUS_SCROLL = 'focusScroll';
|
|
77
|
+
type SliderPlugin = (slider: Slider) => void;
|
|
24
78
|
|
|
25
|
-
export {
|
|
79
|
+
export type { DeepPartial, HOOK_ACTIVE_SLIDE_CHANGED, HOOK_CONTAINER_SIZE_CHANGED, HOOK_CONTENTS_CHANGED, HOOK_CREATED, HOOK_DETAILS_CHANGED, HOOK_FOCUS_SCROLL, HOOK_NATIVE_SCROLL, HOOK_NATIVE_SCROLL_END, HOOK_NATIVE_SCROLL_START, HOOK_PROGRAMMATIC_SCROLL, HOOK_PROGRAMMATIC_SCROLL_END, HOOK_PROGRAMMATIC_SCROLL_START, HOOK_SCROLL, HOOK_SCROLL_END, HOOK_SCROLL_START, Slider, SliderCallback, SliderDetails, SliderHooks, SliderOptionArgs, SliderOptions, SliderPlugin };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DeepPartial, Slider } from '../core/index.
|
|
1
|
+
import { DeepPartial, Slider } from '../core/index.d2.ts';
|
|
2
2
|
|
|
3
3
|
type ThumbnailsOptions = {
|
|
4
4
|
mainSlider: Slider;
|
|
5
5
|
};
|
|
6
|
-
declare function
|
|
6
|
+
declare function ThumbnailPlugin(args: DeepPartial<ThumbnailsOptions>): (slider: Slider) => void;
|
|
7
7
|
|
|
8
|
-
export {
|
|
8
|
+
export { ThumbnailPlugin as default };
|
|
9
9
|
export type { ThumbnailsOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function ThumbnailPlugin(args) {
|
|
2
2
|
return (slider) => {
|
|
3
3
|
const options = {
|
|
4
4
|
mainSlider: args.mainSlider,
|
|
@@ -31,15 +31,15 @@ function FullWidthPlugin(args) {
|
|
|
31
31
|
setTimeout(() => {
|
|
32
32
|
const mainActiveSlideIdx = mainSlider.activeSlideIdx;
|
|
33
33
|
const thumbActiveSlideIdx = slider.activeSlideIdx;
|
|
34
|
+
const activeThumbnail = slider.slides[mainActiveSlideIdx];
|
|
35
|
+
setActiveThumbnail(activeThumbnail);
|
|
34
36
|
if (thumbActiveSlideIdx === mainActiveSlideIdx) {
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
|
-
const activeThumbnail = slider.slides[mainActiveSlideIdx];
|
|
38
|
-
setActiveThumbnail(activeThumbnail);
|
|
39
39
|
slider.moveToSlide(mainActiveSlideIdx);
|
|
40
40
|
}, 50);
|
|
41
41
|
});
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export {
|
|
45
|
+
export { ThumbnailPlugin as default };
|
|
@@ -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.activeSlideIdx,s=i.slides[e];t(s),r!==e&&i.moveToSlide(e)},50)})}}export{e as default};
|
package/docs/assets/demo.css
CHANGED
package/docs/assets/demo.js
CHANGED
|
@@ -241,6 +241,17 @@ import ClassNamesPlugin from '../dist/plugins/classnames/index.esm.js';
|
|
|
241
241
|
);
|
|
242
242
|
console.log( '1-autoplay-view', example1AutoplayView );
|
|
243
243
|
|
|
244
|
+
const example1Focus = new OverflowSlider(
|
|
245
|
+
document.querySelector( '.example-container-1-focus' ),
|
|
246
|
+
{
|
|
247
|
+
rtl: document.documentElement.dir === 'rtl',
|
|
248
|
+
},
|
|
249
|
+
[
|
|
250
|
+
DragScrollingPlugin(),
|
|
251
|
+
]
|
|
252
|
+
);
|
|
253
|
+
console.log( '1-focus', example1Focus );
|
|
254
|
+
|
|
244
255
|
const example2PerfectFit = new OverflowSlider(
|
|
245
256
|
document.querySelector( '.example-container-2-perfect-fit' ),
|
|
246
257
|
{
|
package/docs/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as OverflowSlider } from './plugins/core/index.
|
|
1
|
+
export { default as OverflowSlider } from './plugins/core/index.js';
|
package/docs/dist/index.esm.js
CHANGED
|
@@ -21,8 +21,7 @@ function details(slider) {
|
|
|
21
21
|
// Consider as last page if we're within tolerance of the maximum scroll position
|
|
22
22
|
// When FullWidthPlugin is active, account for the margin offset
|
|
23
23
|
const maxScroll = scrollableAreaWidth - containerWidth - (2 * slider.getLeftOffset());
|
|
24
|
-
|
|
25
|
-
if (currentScroll >= maxScroll - 1) {
|
|
24
|
+
if (slider.getScrollLeft() >= maxScroll - 1) {
|
|
26
25
|
currentPage = amountOfPages - 1;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
@@ -253,21 +252,38 @@ function Slider(container, options, plugins) {
|
|
|
253
252
|
wasInteractedWith = true;
|
|
254
253
|
}, { passive: true });
|
|
255
254
|
slider.container.addEventListener('focusin', (e) => {
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
while (target.parentElement !== slider.container) {
|
|
261
|
-
if (target.parentElement) {
|
|
262
|
-
target = target.parentElement;
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
ensureSlideIsInView(target, 'auto');
|
|
255
|
+
// Only handle keyboard-initiated focus (not mouse or touch)
|
|
256
|
+
if (wasInteractedWith) {
|
|
257
|
+
wasInteractedWith = false;
|
|
258
|
+
return;
|
|
269
259
|
}
|
|
270
260
|
wasInteractedWith = false;
|
|
261
|
+
// No scrolling needed if there is no overflow
|
|
262
|
+
if (!slider.details.hasOverflow) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const focusedElement = e.target;
|
|
266
|
+
// Walk up from the focused element to find the direct child (slide) of the container
|
|
267
|
+
let slide = focusedElement;
|
|
268
|
+
while (slide.parentElement !== slider.container) {
|
|
269
|
+
if (slide.parentElement) {
|
|
270
|
+
slide = slide.parentElement;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// Focused element is not inside the slider container
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Emit programmaticScrollStart immediately so the browser's native focus
|
|
278
|
+
// scroll events are classified as programmatic (not native). This prevents
|
|
279
|
+
// nativeScrollStart from restoring scrollSnapType and fighting our correction.
|
|
280
|
+
slider.emit('programmaticScrollStart');
|
|
281
|
+
// Use setTimeout to let the browser's native focus scroll complete,
|
|
282
|
+
// then override with our WCAG-compliant scroll positioning
|
|
283
|
+
setTimeout(() => {
|
|
284
|
+
scrollFocusedSlideIntoView(slide, focusedElement);
|
|
285
|
+
slider.emit('focusScroll');
|
|
286
|
+
}, 50);
|
|
271
287
|
});
|
|
272
288
|
}
|
|
273
289
|
function setCSSVariables() {
|
|
@@ -314,6 +330,82 @@ function Slider(container, options, plugins) {
|
|
|
314
330
|
}, 50, scrollTarget);
|
|
315
331
|
}
|
|
316
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Scrolls a focused slide (or child element) into view for WCAG AA compliance.
|
|
335
|
+
* Priority:
|
|
336
|
+
* 1. Show the full slide if it fits in the container
|
|
337
|
+
* 2. If the slide is wider than the container, show the focused element
|
|
338
|
+
* 3. If neither fits, align the leading edge (left for LTR, right for RTL)
|
|
339
|
+
*/
|
|
340
|
+
function scrollFocusedSlideIntoView(slide, focusedElement) {
|
|
341
|
+
const isRtl = slider.options.rtl;
|
|
342
|
+
const containerRect = slider.container.getBoundingClientRect();
|
|
343
|
+
const containerWidth = slider.container.offsetWidth;
|
|
344
|
+
const slideRect = slide.getBoundingClientRect();
|
|
345
|
+
const scrollLeft = slider.container.scrollLeft;
|
|
346
|
+
// Calculate visual offsets relative to the container viewport
|
|
347
|
+
const slideLeftOffset = slideRect.left - containerRect.left;
|
|
348
|
+
const slideRightOffset = slideRect.right - containerRect.right;
|
|
349
|
+
// Check if slide is already fully visible (1px tolerance for sub-pixel rounding)
|
|
350
|
+
if (slideLeftOffset >= -1 && slideRightOffset <= 1) {
|
|
351
|
+
slider.container.style.scrollSnapType = '';
|
|
352
|
+
slider.emit('programmaticScrollEnd');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
let scrollTarget;
|
|
356
|
+
if (slideRect.width <= containerWidth) {
|
|
357
|
+
// Slide fits in container — align its leading edge to show it fully
|
|
358
|
+
if (isRtl) {
|
|
359
|
+
// RTL: align slide's right edge with container's right edge
|
|
360
|
+
scrollTarget = scrollLeft + slideRightOffset;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
// LTR: align slide's left edge with container's left edge
|
|
364
|
+
scrollTarget = scrollLeft + slideLeftOffset;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
else if (focusedElement !== slide) {
|
|
368
|
+
// Slide is wider than container — try to show the focused child element
|
|
369
|
+
const focusRect = focusedElement.getBoundingClientRect();
|
|
370
|
+
const focusLeftOffset = focusRect.left - containerRect.left;
|
|
371
|
+
const focusRightOffset = focusRect.right - containerRect.right;
|
|
372
|
+
// Check if focused element is already fully visible
|
|
373
|
+
if (focusLeftOffset >= -1 && focusRightOffset <= 1) {
|
|
374
|
+
slider.container.style.scrollSnapType = '';
|
|
375
|
+
slider.emit('programmaticScrollEnd');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (focusRect.width <= containerWidth) {
|
|
379
|
+
// Focused element fits in container — align its leading edge
|
|
380
|
+
if (isRtl) {
|
|
381
|
+
scrollTarget = scrollLeft + focusRightOffset;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
scrollTarget = scrollLeft + focusLeftOffset;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// Focused element is also wider than container — align leading edge
|
|
389
|
+
if (isRtl) {
|
|
390
|
+
scrollTarget = scrollLeft + focusRightOffset;
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
scrollTarget = scrollLeft + focusLeftOffset;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
// Slide is the focused element and wider than container — align leading edge
|
|
399
|
+
if (isRtl) {
|
|
400
|
+
scrollTarget = scrollLeft + slideRightOffset;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
scrollTarget = scrollLeft + slideLeftOffset;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
slider.emit('programmaticScrollStart');
|
|
407
|
+
slider.container.scrollTo({ left: scrollTarget, behavior: 'auto' });
|
|
408
|
+
}
|
|
317
409
|
function setActiveSlideIdx() {
|
|
318
410
|
const sliderRect = slider.container.getBoundingClientRect();
|
|
319
411
|
const scrollLeft = slider.getScrollLeft();
|