@evermade/overflow-slider 4.2.2 → 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.
Files changed (39) hide show
  1. package/.github/workflows/npm-publish.yml +40 -22
  2. package/.github/workflows/publish.yml +35 -0
  3. package/CHANGELOG.md +116 -0
  4. package/README.md +55 -111
  5. package/RELEASE.md +44 -0
  6. package/dist/index.esm.js +106 -13
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.min.js +1 -1
  9. package/dist/index.min.js.map +1 -1
  10. package/dist/plugins/core/index.d2.ts +3 -2
  11. package/dist/plugins/thumbnails/index.d.ts +2 -2
  12. package/dist/plugins/thumbnails/index.esm.js +4 -4
  13. package/dist/plugins/thumbnails/index.min.js +1 -1
  14. package/docs/assets/demo.css +5 -0
  15. package/docs/assets/demo.js +11 -0
  16. package/docs/dist/index.d.ts +1 -1
  17. package/docs/dist/index.esm.js +106 -13
  18. package/docs/dist/index.esm.js.map +1 -1
  19. package/docs/dist/index.min.js +1 -1
  20. package/docs/dist/index.min.js.map +1 -1
  21. package/docs/dist/plugins/arrows/index.d.ts +1 -1
  22. package/docs/dist/plugins/autoplay/index.d.ts +1 -1
  23. package/docs/dist/plugins/classnames/index.d.ts +1 -1
  24. package/docs/dist/plugins/core/index.d.ts +10 -63
  25. package/docs/dist/plugins/core/index.d2.ts +64 -10
  26. package/docs/dist/plugins/dots/index.d.ts +1 -1
  27. package/docs/dist/plugins/drag-scrolling/index.d.ts +1 -1
  28. package/docs/dist/plugins/fade/index.d.ts +1 -1
  29. package/docs/dist/plugins/full-width/index.d.ts +1 -1
  30. package/docs/dist/plugins/scroll-indicator/index.d.ts +1 -1
  31. package/docs/dist/plugins/skip-links/index.d.ts +1 -1
  32. package/docs/dist/plugins/thumbnails/index.d.ts +3 -3
  33. package/docs/dist/plugins/thumbnails/index.esm.js +4 -4
  34. package/docs/dist/plugins/thumbnails/index.min.js +1 -1
  35. package/docs/index.html +29 -0
  36. package/package.json +1 -1
  37. package/src/core/slider.ts +111 -12
  38. package/src/core/types.ts +4 -1
  39. package/src/plugins/thumbnails/index.ts +4 -4
@@ -1,33 +1,51 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3
-
1
+ # Publish to npm using Trusted Publishers (OIDC)
4
2
  name: Publish to NPM
5
3
 
6
4
  on:
7
5
  release:
8
6
  types: [created]
7
+ workflow_dispatch:
8
+ inputs:
9
+ dry_run:
10
+ description: 'Run in dry-run mode (no actual publish)'
11
+ required: false
12
+ default: false
13
+ type: boolean
9
14
 
10
15
  jobs:
11
- build:
16
+ publish:
12
17
  runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: read
20
+ id-token: write
13
21
  steps:
14
- - uses: actions/checkout@v3
15
- - uses: actions/setup-node@v3
16
- with:
17
- node-version: 16
18
- - run: npm ci
19
- - run: npm test
22
+ - name: Checkout
23
+ uses: actions/checkout@v4
20
24
 
21
- publish-npm:
22
- needs: build
23
- runs-on: ubuntu-latest
24
- steps:
25
- - uses: actions/checkout@v3
26
- - uses: actions/setup-node@v3
25
+ - name: Setup Node.js
26
+ uses: actions/setup-node@v4
27
27
  with:
28
- node-version: 16
29
- registry-url: https://registry.npmjs.org/
30
- - run: npm ci
31
- - run: npm publish
32
- env:
33
- NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
28
+ node-version: '20'
29
+ registry-url: 'https://registry.npmjs.org'
30
+
31
+ - name: Install dependencies
32
+ run: npm ci
33
+
34
+ - name: Run tests
35
+ run: npm test
36
+
37
+ - name: Debug authentication
38
+ run: |
39
+ echo "=== Environment Check ==="
40
+ echo "ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${ACTIONS_ID_TOKEN_REQUEST_TOKEN:+SET}"
41
+ echo "ACTIONS_ID_TOKEN_REQUEST_URL: ${ACTIONS_ID_TOKEN_REQUEST_URL:+SET}"
42
+ echo "NPM_CONFIG_REGISTRY: $NPM_CONFIG_REGISTRY"
43
+ echo "=== npm whoami ==="
44
+ npm whoami || echo "Failed to authenticate"
45
+ echo "=== npm ping ==="
46
+ npm ping
47
+ echo "=== Check registry ==="
48
+ npm config get registry
49
+
50
+ - name: Publish to npm
51
+ run: npm publish --access public ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}
@@ -0,0 +1,35 @@
1
+ # Publish to npm using Trusted Publishers (OIDC)
2
+ name: Publish to NPM
3
+
4
+ permissions:
5
+ id-token: write # Required for OIDC
6
+ contents: read
7
+
8
+ on:
9
+ release:
10
+ types: [created]
11
+ workflow_dispatch:
12
+ inputs:
13
+ dry_run:
14
+ description: 'Run in dry-run mode (no actual publish)'
15
+ required: false
16
+ default: false
17
+ type: boolean
18
+
19
+ jobs:
20
+ publish:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: actions/setup-node@v4
26
+ with:
27
+ node-version: '20'
28
+ registry-url: 'https://registry.npmjs.org'
29
+
30
+ - run: npm ci
31
+ - run: npm test
32
+ - run: npm whoami || echo "OIDC auth failed"
33
+ - run: npm publish ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}
34
+ env:
35
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/CHANGELOG.md ADDED
@@ -0,0 +1,116 @@
1
+ # Changelog
2
+
3
+ ### 4.2.3
4
+
5
+ * Add: React example in README.md
6
+ * Change: Move changelog to separate file to make README.md more compact
7
+ * Fix: ThumbnailPlugin not setting first item active when navigatin back to it when thumbnails are not overflowing
8
+ * Fix: moving slider to keyboard focused item work better with slide's inner focusable elements
9
+
10
+ ### 4.2.2
11
+
12
+ * Add: `justify-content: flex-start` to overflow-slider base styles to prevent centering of slides or other unexpected behavior when slider container is wider than its content.
13
+
14
+ ### 4.2.1
15
+
16
+ * Fix: currentPage and amountOfPages not being calculated correctly with FullWidthPlugin causin last page no to activate properly.
17
+
18
+ ### 4.2.0
19
+
20
+ * Add: View mode in DotsPlugin in addition to previous "slide mode"
21
+ * Fix: Remove forgotten console.log from ClassNamesPlugin
22
+
23
+ ### 4.1.0
24
+
25
+ * Add: ClassNamesPlugin to add classes to visible/partly visible/hidden slides.
26
+ * Add: targetWidth property to core level (backwards compatible with FullWidthPlugin implementation)
27
+ * Fix: Scroll snapping for FullWidthPlugin
28
+ * Fix: Possible issues where plugin changed some details and that was not applied for first render
29
+ * Fix: Rendering issue where transition on slides could prevent calculations initially from working
30
+
31
+ ### 4.0.0
32
+
33
+ * Add: AutoPlayPlugin to allow auto-playing slides
34
+ * Add: Mixins that can be imported to SCSS projects
35
+ * Add: CSS variable: `--slider-container-height`
36
+ * Add: CSS variable: `--slider-x-offset`
37
+ * Add: Option `cssVariableContainer` to expose CSS variables for example higher container
38
+ * Add: `canMoveToSlide` method to check if slider can move to a specific slide (does it exist, is it already in view)
39
+ * Add: `targetWidth` to slider options as relying on `--slider-container-target-width` can be more solid to calculate fractional slide widths on (at least when there is only few slides)
40
+ * Fix: Export TypeScript types properly from core and plugins to be available automatically
41
+ * Fix: ScrollIndicatorPlugin click to scroll bar didn't always detect click position correctly
42
+ * Fix: snapToClosestSlide method edge cases on DragScrollingPlugin sometimes not snapping on right slide
43
+
44
+ ### 3.3.1
45
+
46
+ * Fix: FullWidthPlugin margin calculation not being run if there's too few slides for overflow and you resize screen without container width changing
47
+
48
+ ### 3.3.0
49
+
50
+ * Add: Ability to move each direction by one slide at a time via `moveToSlideInDirection` prev/next
51
+ * Add: Support for ArrowsPlugin to move by one slide at a time (default is still one view at a time)
52
+ * Fix: Remove console logs
53
+ * Refactor: Plugin build paths to match import paths. Might fix some eslint warnings. If you are not using import but directly referencing the plugin files under `dist/` you might need to update your paths.
54
+
55
+ ### 3.2.1
56
+
57
+ * Add: Documentation on plugins
58
+ * Fix: Make types more strict and remove all "any" types
59
+
60
+ ### 3.2.0
61
+
62
+ * Add: RTL support
63
+ * Add: `--slider-container-target-width` for FullWidthPlugin to allow CSS based on target size
64
+ * Add: Documentation how to set slides per view in CSS
65
+ * Fix: Attach ThumbnailsPlugin to scrollEnd which skips in-between slides when multiple slides are scrolled at once
66
+
67
+ ### 3.1.0
68
+
69
+ * Add: slider.getInclusiveScrollWidth and slider.getInclusiveScrollHeight methods to get widths including outermost childs outermost margins
70
+ * Fix: Lot of bugs related to subpixel widths
71
+ * Fix: Don't run arrow click action if there are no more slides to scroll to
72
+ * Fix: FullWidthPlugin bugs where arrows were not detecting start or end properly (because of child margins not taken into account)
73
+ * Fix: Attach ThumbnailsPlugin to activeSlideChanged which is more appropriate hook
74
+
75
+ ### 3.0.0
76
+
77
+ * Breaking: Change dot plugin to calculate dots based on slides instead of container width "pages"
78
+ * Add: FadePlugin to hint that there are more slides to scroll to
79
+ * Add: Scroll snap emulation method
80
+ * Add: Scroll snap emulation for DragScrollingPlugin
81
+ * Add: Hooks for different types of scrolling (any, native, programmatic)
82
+ * Add: Hooks for different states of scrolling (start, scroll, end) for above types
83
+ * Refactor: Scroll snapping exceptions to be handled by the core slider
84
+ * Fix: Enhance performance by hooking some plugins only when scrolling has ended
85
+ * Fix: Full width alignment to take into account the container offset
86
+
87
+ ### 2.0.2
88
+
89
+ * Fix: Import style.css from correct path
90
+
91
+ ### 2.0.1
92
+
93
+ * Fix: Smooth scrolling for moveToSlide method
94
+ * Fix: Prev arrow sometimes leaving visible although there are no more slides to scroll to
95
+
96
+ ### 2.0.0
97
+
98
+ * Breaking: Separate plugins to their own imports/files
99
+ * Add: FullWidthPlugin to allow full width sliders
100
+ * Add: ThumbnailsPlugin to show synchronized thumbnails
101
+ * Add: Slider container 'data-ready' attribute when initialized to help writing CSS
102
+ * Add: Support for optional separate containers for prev and next arrows
103
+ * Add: Slides as array to Slider instance
104
+ * Add: Active slide ID to Slider instance as activeSlideIdx and hook activeSlideChanged
105
+ * Fix: DragScrollingPlugin dragging clickable slides in Firefox
106
+ * Fix: DragScrollingPlugin dragging outside of container bugs in Firefox/Safari
107
+ * Fix: ScrollIndicatorPlugin width calculation when scrollbar and container are not same width
108
+
109
+ ### 1.1.0
110
+
111
+ * Add: Grab cursor when hovering slider that has DragScrollingPlugin
112
+ * Add: Example of using entrance and exit animations for slides
113
+ * Fix: ScrollIndicatorPlugin dragging works now with touch
114
+ * Fix: Hide native scrollbar also in Firefox + Edge
115
+ * Docs: Add more info on required markup and limitations
116
+
package/README.md CHANGED
@@ -102,6 +102,59 @@ You can use the CSS variables to override some values easily.
102
102
 
103
103
  Note that you can easily write styles from scratch if you want to. See source code from `src/overflow-slider.scss` for reference.
104
104
 
105
+ ## Using in React
106
+
107
+ Overflow Slider can be used with React. There is no separate core or plugins for React so usage is very similar to vanilla JS. Overflow Slider depends on expanding existing DOM and adding DOM elements so it needs reliable access to these elements.
108
+
109
+ In React the way to give this access is `useRef`.
110
+
111
+ ```js
112
+ import { useRef, useEffect } from 'react';
113
+ import { OverflowSlider } from '@evermade/overflow-slider';
114
+ import DragScrollingPlugin from '@evermade/overflow-slider/plugins/drag-scrolling';
115
+ import ArrowsPlugin from '@evermade/overflow-slider/plugins/arrows';
116
+
117
+ const ImageSlider = () => {
118
+ const sliderElement = useRef(null);
119
+ const sliderControls = useRef(null);
120
+
121
+ useEffect(() => {
122
+ if (!sliderElement.current || !sliderControls.current) {
123
+ return;
124
+ }
125
+
126
+ const slider = new OverflowSlider(
127
+ sliderElement.current,
128
+ {
129
+ emulateScrollSnap: true,
130
+ },
131
+ [
132
+ DragScrollingPlugin(),
133
+ ArrowsPlugin({
134
+ container: sliderControls.current
135
+ }),
136
+ ]
137
+ );
138
+ }, []);
139
+
140
+ return (
141
+ <div className="slider-container">
142
+ <div className="overflow-slider" ref={sliderElement}>
143
+ <div className="overflow-slider__slider-item"><h2>Slide 1</h2></div>
144
+ <div className="overflow-slider__slider-item"><h2>Slide 2</h2></div>
145
+ <div className="overflow-slider__slider-item"><h2>Slide 3</h2></div>
146
+ </div>
147
+ <div className="slider-controls" ref={sliderControls}>
148
+ </div>
149
+ </div>
150
+ );
151
+ };
152
+
153
+ export default ImageSlider;
154
+ ```
155
+
156
+ Note that Overflow Slider does not have destroy() function. If not having it turns out to be a big issue for React we could add it but that is something affecting all core/plugins and can increase bundle size ~25% as it can be a lot of code.
157
+
105
158
  ## Mixins
106
159
 
107
160
  If you are using SCSS, you can use these helpers.
@@ -562,112 +615,7 @@ Infinite scroll is not supported and likely never will be. It is not accessible
562
615
 
563
616
  ## Changelog
564
617
 
565
- ### 4.2.2
566
-
567
- * Add: `justify-content: flex-start` to overflow-slider base styles to prevent centering of slides or other unexpected behavior when slider container is wider than its content.
568
-
569
- ### 4.2.1
570
-
571
- * Fix: currentPage and amountOfPages not being calculated correctly with FullWidthPlugin causin last page no to activate properly.
572
-
573
- ### 4.2.0
574
-
575
- * Add: View mode in DotsPlugin in addition to previous "slide mode"
576
- * Fix: Remove forgotten console.log from ClassNamesPlugin
577
-
578
- ### 4.1.0
579
-
580
- * Add: ClassNamesPlugin to add classes to visible/partly visible/hidden slides.
581
- * Add: targetWidth property to core level (backwards compatible with FullWidthPlugin implementation)
582
- * Fix: Scroll snapping for FullWidthPlugin
583
- * Fix: Possible issues where plugin changed some details and that was not applied for first render
584
- * Fix: Rendering issue where transition on slides could prevent calculations initially from working
585
-
586
- ### 4.0.0
587
-
588
- * Add: AutoPlayPlugin to allow auto-playing slides
589
- * Add: Mixins that can be imported to SCSS projects
590
- * Add: CSS variable: `--slider-container-height`
591
- * Add: CSS variable: `--slider-x-offset`
592
- * Add: Option `cssVariableContainer` to expose CSS variables for example higher container
593
- * Add: `canMoveToSlide` method to check if slider can move to a specific slide (does it exist, is it already in view)
594
- * Add: `targetWidth` to slider options as relying on `--slider-container-target-width` can be more solid to calculate fractional slide widths on (at least when there is only few slides)
595
- * Fix: Export TypeScript types properly from core and plugins to be available automatically
596
- * Fix: ScrollIndicatorPlugin click to scroll bar didn't always detect click position correctly
597
- * Fix: snapToClosestSlide method edge cases on DragScrollingPlugin sometimes not snapping on right slide
598
-
599
- ### 3.3.1
600
-
601
- * Fix: FullWidthPlugin margin calculation not being run if there's too few slides for overflow and you resize screen without container width changing
602
-
603
- ### 3.3.0
604
-
605
- * Add: Ability to move each direction by one slide at a time via `moveToSlideInDirection` prev/next
606
- * Add: Support for ArrowsPlugin to move by one slide at a time (default is still one view at a time)
607
- * Fix: Remove console logs
608
- * Refactor: Plugin build paths to match import paths. Might fix some eslint warnings. If you are not using import but directly referencing the plugin files under `dist/` you might need to update your paths.
609
-
610
- ### 3.2.1
611
-
612
- * Add: Documentation on plugins
613
- * Fix: Make types more strict and remove all "any" types
614
-
615
- ### 3.2.0
616
-
617
- * Add: RTL support
618
- * Add: `--slider-container-target-width` for FullWidthPlugin to allow CSS based on target size
619
- * Add: Documentation how to set slides per view in CSS
620
- * Fix: Attach ThumbnailsPlugin to scrollEnd which skips in-between slides when multiple slides are scrolled at once
621
-
622
- ### 3.1.0
623
-
624
- * Add: slider.getInclusiveScrollWidth and slider.getInclusiveScrollHeight methods to get widths including outermost childs outermost margins
625
- * Fix: Lot of bugs related to subpixel widths
626
- * Fix: Don't run arrow click action if there are no more slides to scroll to
627
- * Fix: FullWidthPlugin bugs where arrows were not detecting start or end properly (because of child margins not taken into account)
628
- * Fix: Attach ThumbnailsPlugin to activeSlideChanged which is more appropriate hook
629
-
630
- ### 3.0.0
631
-
632
- * Breaking: Change dot plugin to calculate dots based on slides instead of container width "pages"
633
- * Add: FadePlugin to hint that there are more slides to scroll to
634
- * Add: Scroll snap emulation method
635
- * Add: Scroll snap emulation for DragScrollingPlugin
636
- * Add: Hooks for different types of scrolling (any, native, programmatic)
637
- * Add: Hooks for different states of scrolling (start, scroll, end) for above types
638
- * Refactor: Scroll snapping exceptions to be handled by the core slider
639
- * Fix: Enhance performance by hooking some plugins only when scrolling has ended
640
- * Fix: Full width alignment to take into account the container offset
641
-
642
- ### 2.0.2
643
-
644
- * Fix: Import style.css from correct path
645
-
646
- ### 2.0.1
647
-
648
- * Fix: Smooth scrolling for moveToSlide method
649
- * Fix: Prev arrow sometimes leaving visible although there are no more slides to scroll to
650
-
651
- ### 2.0.0
652
-
653
- * Breaking: Separate plugins to their own imports/files
654
- * Add: FullWidthPlugin to allow full width sliders
655
- * Add: ThumbnailsPlugin to show synchronized thumbnails
656
- * Add: Slider container 'data-ready' attribute when initialized to help writing CSS
657
- * Add: Support for optional separate containers for prev and next arrows
658
- * Add: Slides as array to Slider instance
659
- * Add: Active slide ID to Slider instance as activeSlideIdx and hook activeSlideChanged
660
- * Fix: DragScrollingPlugin dragging clickable slides in Firefox
661
- * Fix: DragScrollingPlugin dragging outside of container bugs in Firefox/Safari
662
- * Fix: ScrollIndicatorPlugin width calculation when scrollbar and container are not same width
663
-
664
- ### 1.1.0
665
-
666
- * Add: Grab cursor when hovering slider that has DragScrollingPlugin
667
- * Add: Example of using entrance and exit animations for slides
668
- * Fix: ScrollIndicatorPlugin dragging works now with touch
669
- * Fix: Hide native scrollbar also in Firefox + Edge
670
- * Docs: Add more info on required markup and limitations
618
+ See [CHANGELOG.md](./CHANGELOG.md)
671
619
 
672
620
  ## Development
673
621
 
@@ -675,8 +623,4 @@ Install tools `npm install` and build `npm run build` or develop with `npm run w
675
623
 
676
624
  Releasing new version:
677
625
 
678
- * Update version in `package.json`
679
- * Commit to master
680
- * Set tag with version number to git
681
- * Create new release in GitHub
682
- * NPM package is automatically published from GitHub
626
+ See [RELEASE.md](./RELEASE.md)
package/RELEASE.md ADDED
@@ -0,0 +1,44 @@
1
+ # Release Process
2
+
3
+ This project uses a manual release process.
4
+
5
+ ## Steps to Release
6
+
7
+ 1. **Update version in package.json**
8
+
9
+ 2. **Build the package**
10
+ ```bash
11
+ npm run build
12
+ ```
13
+
14
+ 3. **Push changes and create GitHub release**
15
+ ```bash
16
+ git push
17
+ git push --tags
18
+ ```
19
+
20
+ 4. **Create GitHub Release**
21
+ - Go to GitHub repository
22
+ - Click "Releases" → "Create a new release"
23
+ - Select the version tag
24
+ - Write release notes
25
+ - Publish release
26
+
27
+ 5. **Publish to npm**
28
+ ```bash
29
+ npm publish --access public
30
+ ```
31
+
32
+ ## Prerequisites
33
+
34
+ - Make sure you're logged into npm: `npm whoami`
35
+ - Make sure you have write access to the `@evermade/overflow-slider` package
36
+ - Make sure you're on the main branch with latest changes
37
+
38
+ ## Notes
39
+
40
+ - The `npm version` command automatically updates package.json and creates a git tag
41
+ - Use `npm publish --dry-run` first if you want to test without actually publishing
42
+ - The package is public, so `--access public` ensures it's published correctly
43
+
44
+ That's it! Simple and reliable manual process.
package/dist/index.esm.js CHANGED
@@ -252,21 +252,38 @@ function Slider(container, options, plugins) {
252
252
  wasInteractedWith = true;
253
253
  }, { passive: true });
254
254
  slider.container.addEventListener('focusin', (e) => {
255
- // move target parents as long as they are not the container
256
- // but only if focus didn't start from mouse or touch
257
- if (!wasInteractedWith) {
258
- let target = e.target;
259
- while (target.parentElement !== slider.container) {
260
- if (target.parentElement) {
261
- target = target.parentElement;
262
- }
263
- else {
264
- break;
265
- }
266
- }
267
- ensureSlideIsInView(target, 'auto');
255
+ // Only handle keyboard-initiated focus (not mouse or touch)
256
+ if (wasInteractedWith) {
257
+ wasInteractedWith = false;
258
+ return;
268
259
  }
269
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);
270
287
  });
271
288
  }
272
289
  function setCSSVariables() {
@@ -313,6 +330,82 @@ function Slider(container, options, plugins) {
313
330
  }, 50, scrollTarget);
314
331
  }
315
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
+ }
316
409
  function setActiveSlideIdx() {
317
410
  const sliderRect = slider.container.getBoundingClientRect();
318
411
  const scrollLeft = slider.getScrollLeft();