@yoursurprise/slider 0.0.20 → 0.0.22

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 CHANGED
@@ -15,7 +15,7 @@ This slider has the following features:
15
15
  Before using this slider, please consider the following:
16
16
  - This project is still being set up, this is an alpha version
17
17
  - The slider only contains styling to make it functional
18
- - There is no configuration of aforementioned features (yet)
18
+ - There is no configuration of most aforementioned features (yet)
19
19
 
20
20
  Todos:
21
21
  - ~~Add a demo page~~
@@ -39,11 +39,13 @@ All browsers with [IntersectionObserver](https://developer.mozilla.org/en-US/doc
39
39
 
40
40
  ### Import the CSS files
41
41
 
42
- `import "@yoursurprise/slider/dist/index.css";`
42
+ ```javascript
43
+ import "@yoursurprise/slider/dist/index.css";
44
+ ```
43
45
 
44
46
  ### Implement the Slider
45
47
 
46
- ```
48
+ ```javascript
47
49
  import { Slider } from '@yoursurprise/slider';
48
50
  import '@yoursurprise/slider/dist/index.css';
49
51
 
@@ -59,6 +61,8 @@ export default function YourComponent() {
59
61
  }
60
62
  ```
61
63
 
64
+ ### Configuration
62
65
 
63
-
64
-
66
+ | Option | Type | Required | Default | Description |
67
+ |-----------------------|-----------|----------|---------|-------------------------------------|
68
+ | hideNavigationButtons | `boolean` | `false` | `false` | Always hides the navigation buttons |
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import './Button.scss';
3
+ interface Props {
4
+ onClick: () => void;
5
+ isHidden: boolean;
6
+ }
7
+ export declare const NextButton: React.FC<Props>;
8
+ export {};
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import './Button.scss';
3
+ interface Props {
4
+ onClick: () => void;
5
+ isHidden: boolean;
6
+ }
7
+ export declare const PreviousButton: React.FC<Props>;
8
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ export declare enum Visibility {
2
+ FULL = 0,
3
+ PARTIAL = 1,
4
+ NONE = 2
5
+ }
6
+ export declare enum NavigationDirection {
7
+ PREV = 0,
8
+ NEXT = 1
9
+ }
10
+ interface UseSlider {
11
+ getLeftPositionToScrollTo: (direction: NavigationDirection, slideOffsetLeft: number, wrapperOffsetLeft: number, wrapperWidth: number, slideWidth: number) => number;
12
+ getVisibilityByIntersectionRatio: (intersectionRatio: number) => Visibility;
13
+ getFirstVisibleSlideIndex: () => number;
14
+ getLastVisibleSlideIndex: () => number;
15
+ addVisibleSlide: (index: number) => void;
16
+ addPartiallyVisibleSlide: (index: number) => void;
17
+ sortSlides: () => void;
18
+ removeVisibleSlide: (index: number) => void;
19
+ removePartiallyVisibleSlide: (index: number) => void;
20
+ }
21
+ export declare const useSlider: () => UseSlider;
22
+ export {};
package/dist/Slider.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import type React from 'react';
2
2
  import type { PropsWithChildren } from 'react';
3
3
  import './Slider.scss';
4
- export declare const Slider: React.FC<PropsWithChildren>;
4
+ interface Settings {
5
+ hideNavigationButtons?: boolean;
6
+ }
7
+ export declare const Slider: React.FC<PropsWithChildren<Settings>>;
8
+ export {};
package/dist/index.css CHANGED
@@ -1,6 +1,3 @@
1
- .slider {
2
- position: relative;
3
- }
4
1
  .slider__button {
5
2
  position: absolute;
6
3
  top: 0;
@@ -9,12 +6,21 @@
9
6
  width: 48px;
10
7
  margin: auto 0;
11
8
  border-radius: 50%;
12
- border: 1px solid #C9C9C9;
13
9
  background-color: white;
14
10
  align-items: center;
15
11
  justify-content: center;
16
12
  cursor: pointer;
17
13
  display: none;
14
+ border: none;
15
+ outline: none;
16
+ box-shadow: inset 0 0 0 1px rgba(32, 32, 32, 0.1);
17
+ transition: box-shadow 0.2s ease-in-out;
18
+ }
19
+ .slider__button__icon {
20
+ fill: #554c44;
21
+ }
22
+ .slider__button:hover {
23
+ box-shadow: inset 0 0 0 1px rgba(32, 32, 32, 0.2);
18
24
  }
19
25
  @media (hover: hover) {
20
26
  .slider__button {
@@ -30,6 +36,9 @@
30
36
  .slider__button--prev {
31
37
  left: 5px;
32
38
  }
39
+ .slider {
40
+ position: relative;
41
+ }
33
42
  .slider__wrapper {
34
43
  overscroll-behavior-x: contain;
35
44
  -ms-overflow-style: none;
@@ -1 +1 @@
1
- {"version":3,"sources":["Slider.scss"],"names":[],"mappings":"AAAA;EACE,kBAAkB;AACpB;AACA;EACE,kBAAkB;EAClB,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,cAAc;EACd,kBAAkB;EAClB,yBAAyB;EACzB,uBAAuB;EACvB,mBAAmB;EACnB,uBAAuB;EACvB,eAAe;EACf,aAAa;AACf;AACA;EACE;IACE,aAAa;EACf;AACF;AACA;EACE,aAAa;AACf;AACA;EACE,UAAU;AACZ;AACA;EACE,SAAS;AACX;AACA;EACE,8BAA8B;EAC9B,wBAAwB;EACxB,qBAAqB;EACrB,aAAa;EACb,mBAAmB;EACnB,gBAAgB;EAChB,6BAA6B;AAC/B;AACA;EACE,aAAa;AACf;AACA;EACE;IACE,yBAAyB;EAC3B;AACF;AACA;EACE,uBAAuB;AACzB;AACA;EACE,YAAY;AACd;AACA;EACE,gBAAgB;EAChB,iBAAiB;AACnB;AACA;EACE,wBAAwB;AAC1B","file":"index.css","sourcesContent":[".slider {\n position: relative;\n}\n.slider__button {\n position: absolute;\n top: 0;\n bottom: 0;\n height: 48px;\n width: 48px;\n margin: auto 0;\n border-radius: 50%;\n border: 1px solid #C9C9C9;\n background-color: white;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n display: none;\n}\n@media (hover: hover) {\n .slider__button {\n display: flex;\n }\n}\n.slider__button--hidden {\n display: none;\n}\n.slider__button--next {\n right: 5px;\n}\n.slider__button--prev {\n left: 5px;\n}\n.slider__wrapper {\n overscroll-behavior-x: contain;\n -ms-overflow-style: none;\n scrollbar-width: none;\n display: flex;\n flex-direction: row;\n overflow-x: auto;\n scroll-snap-type: x proximity;\n}\n.slider__wrapper::-webkit-scrollbar {\n display: none;\n}\n@media (hover: hover) {\n .slider__wrapper {\n scroll-snap-type: initial;\n }\n}\n.slider__wrapper:not(.is-dragging) {\n scroll-behavior: smooth;\n}\n.slider__wrapper.is-scrollable {\n cursor: move;\n}\n.slider__wrapper.is-dragging {\n cursor: grabbing;\n user-select: none;\n}\n.slider__wrapper__slide {\n scroll-snap-align: start;\n}"]}
1
+ {"version":3,"sources":["Button.scss","Slider.scss"],"names":[],"mappings":"AAAA;EACE,kBAAkB;EAClB,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,cAAc;EACd,kBAAkB;EAClB,uBAAuB;EACvB,mBAAmB;EACnB,uBAAuB;EACvB,eAAe;EACf,aAAa;EACb,YAAY;EACZ,aAAa;EACb,iDAAiD;EACjD,uCAAuC;AACzC;AACA;EACE,aAAa;AACf;AACA;EACE,iDAAiD;AACnD;AACA;EACE;IACE,aAAa;EACf;AACF;AACA;EACE,aAAa;AACf;AACA;EACE,UAAU;AACZ;AACA;EACE,SAAS;AACX;ACrCA;EACE,kBAAkB;AACpB;AACA;EACE,8BAA8B;EAC9B,wBAAwB;EACxB,qBAAqB;EACrB,aAAa;EACb,mBAAmB;EACnB,gBAAgB;EAChB,6BAA6B;AAC/B;AACA;EACE,aAAa;AACf;AACA;EACE;IACE,yBAAyB;EAC3B;AACF;AACA;EACE,uBAAuB;AACzB;AACA;EACE,YAAY;AACd;AACA;EACE,gBAAgB;EAChB,iBAAiB;AACnB;AACA;EACE,wBAAwB;AAC1B","file":"index.css","sourcesContent":[".slider__button {\n position: absolute;\n top: 0;\n bottom: 0;\n height: 48px;\n width: 48px;\n margin: auto 0;\n border-radius: 50%;\n background-color: white;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n display: none;\n border: none;\n outline: none;\n box-shadow: inset 0 0 0 1px rgba(32, 32, 32, 0.1);\n transition: box-shadow 0.2s ease-in-out;\n}\n.slider__button__icon {\n fill: #554c44;\n}\n.slider__button:hover {\n box-shadow: inset 0 0 0 1px rgba(32, 32, 32, 0.2);\n}\n@media (hover: hover) {\n .slider__button {\n display: flex;\n }\n}\n.slider__button--hidden {\n display: none;\n}\n.slider__button--next {\n right: 5px;\n}\n.slider__button--prev {\n left: 5px;\n}",".slider {\n position: relative;\n}\n.slider__wrapper {\n overscroll-behavior-x: contain;\n -ms-overflow-style: none;\n scrollbar-width: none;\n display: flex;\n flex-direction: row;\n overflow-x: auto;\n scroll-snap-type: x proximity;\n}\n.slider__wrapper::-webkit-scrollbar {\n display: none;\n}\n@media (hover: hover) {\n .slider__wrapper {\n scroll-snap-type: initial;\n }\n}\n.slider__wrapper:not(.is-dragging) {\n scroll-behavior: smooth;\n}\n.slider__wrapper.is-scrollable {\n cursor: move;\n}\n.slider__wrapper.is-dragging {\n cursor: grabbing;\n user-select: none;\n}\n.slider__wrapper__slide {\n scroll-snap-align: start;\n}"]}
package/dist/index.js CHANGED
@@ -1,22 +1,149 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useRef, useState, useEffect, useCallback, Children } from 'react';
3
3
 
4
- var Visbility;
5
- (function (Visbility) {
6
- Visbility[Visbility["FULL"] = 0] = "FULL";
7
- Visbility[Visbility["PARTIAL"] = 1] = "PARTIAL";
8
- Visbility[Visbility["NONE"] = 2] = "NONE";
9
- })(Visbility || (Visbility = {}));
4
+ var classnamesExports = {};
5
+ var classnames = {
6
+ get exports(){ return classnamesExports; },
7
+ set exports(v){ classnamesExports = v; },
8
+ };
9
+
10
+ /*!
11
+ Copyright (c) 2018 Jed Watson.
12
+ Licensed under the MIT License (MIT), see
13
+ http://jedwatson.github.io/classnames
14
+ */
15
+
16
+ (function (module) {
17
+ /* global define */
18
+
19
+ (function () {
20
+
21
+ var hasOwn = {}.hasOwnProperty;
22
+
23
+ function classNames() {
24
+ var classes = [];
25
+
26
+ for (var i = 0; i < arguments.length; i++) {
27
+ var arg = arguments[i];
28
+ if (!arg) continue;
29
+
30
+ var argType = typeof arg;
31
+
32
+ if (argType === 'string' || argType === 'number') {
33
+ classes.push(arg);
34
+ } else if (Array.isArray(arg)) {
35
+ if (arg.length) {
36
+ var inner = classNames.apply(null, arg);
37
+ if (inner) {
38
+ classes.push(inner);
39
+ }
40
+ }
41
+ } else if (argType === 'object') {
42
+ if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
43
+ classes.push(arg.toString());
44
+ continue;
45
+ }
46
+
47
+ for (var key in arg) {
48
+ if (hasOwn.call(arg, key) && arg[key]) {
49
+ classes.push(key);
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ return classes.join(' ');
56
+ }
57
+
58
+ if (module.exports) {
59
+ classNames.default = classNames;
60
+ module.exports = classNames;
61
+ } else {
62
+ window.classNames = classNames;
63
+ }
64
+ }());
65
+ } (classnames));
66
+
67
+ var classNames = classnamesExports;
68
+
69
+ const NextButton = ({ onClick, isHidden }) => {
70
+ return (jsx("button", { "aria-label": "Next slide", type: "button", onClick: onClick, "aria-hidden": isHidden, className: classNames([
71
+ 'slider__button',
72
+ 'slider__button--next',
73
+ { 'slider__button--hidden': isHidden },
74
+ ]), children: jsx("svg", { className: "slider__button__icon", xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 256 256", children: jsx("path", { d: "M96,216a8.5,8.5,0,0,1-5.7-2.3,8.1,8.1,0,0,1,0-11.4L164.7,128,90.3,53.7a8.1,8.1,0,0,1,11.4-11.4l80,80a8.1,8.1,0,0,1,0,11.4l-80,80A8.5,8.5,0,0,1,96,216Z" }) }) }));
75
+ };
76
+
77
+ const PreviousButton = ({ onClick, isHidden }) => {
78
+ return (jsx("button", { "aria-label": "Previous slide", type: "button", onClick: onClick, "aria-hidden": isHidden, className: classNames([
79
+ 'slider__button',
80
+ 'slider__button--prev',
81
+ { 'slider__button--hidden': isHidden },
82
+ ]), children: jsx("svg", { className: "slider__button__icon", xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 256 256", children: jsx("path", { d: "M160,216a8.5,8.5,0,0,1-5.7-2.3l-80-80a8.1,8.1,0,0,1,0-11.4l80-80a8.1,8.1,0,0,1,11.4,11.4L91.3,128l74.4,74.3a8.1,8.1,0,0,1,0,11.4A8.5,8.5,0,0,1,160,216Z" }) }) }));
83
+ };
84
+
85
+ var Visibility;
86
+ (function (Visibility) {
87
+ Visibility[Visibility["FULL"] = 0] = "FULL";
88
+ Visibility[Visibility["PARTIAL"] = 1] = "PARTIAL";
89
+ Visibility[Visibility["NONE"] = 2] = "NONE";
90
+ })(Visibility || (Visibility = {}));
10
91
  var NavigationDirection;
11
92
  (function (NavigationDirection) {
12
93
  NavigationDirection[NavigationDirection["PREV"] = 0] = "PREV";
13
94
  NavigationDirection[NavigationDirection["NEXT"] = 1] = "NEXT";
14
95
  })(NavigationDirection || (NavigationDirection = {}));
15
- const Slider = ({ children }) => {
16
- const slides = useRef([]);
17
- const wrapper = useRef(null);
96
+ const useSlider = () => {
18
97
  const visibleSlideIndices = useRef([]);
19
98
  const partiallyVisibleSlideIndices = useRef([]);
99
+ const getFirstVisibleSlideIndex = () => visibleSlideIndices.current[0] ?? partiallyVisibleSlideIndices.current[0] ?? -1;
100
+ const getLastVisibleSlideIndex = () => visibleSlideIndices.current[visibleSlideIndices.current.length - 1]
101
+ ?? partiallyVisibleSlideIndices.current[partiallyVisibleSlideIndices.current.length - 1] ?? -1;
102
+ const addVisibleSlide = (index) => visibleSlideIndices.current.push(index);
103
+ const addPartiallyVisibleSlide = (index) => partiallyVisibleSlideIndices.current.push(index);
104
+ const removePartiallyVisibleSlide = (index) => partiallyVisibleSlideIndices.current = partiallyVisibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
105
+ const removeVisibleSlide = (index) => visibleSlideIndices.current = visibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
106
+ const sortSlides = () => {
107
+ visibleSlideIndices.current = [...new Set(visibleSlideIndices.current)].sort((a, b) => a - b);
108
+ partiallyVisibleSlideIndices.current = [...new Set(partiallyVisibleSlideIndices.current)].sort((a, b) => a - b);
109
+ };
110
+ const getLeftPositionToScrollTo = (direction, slideOffsetLeft, wrapperOffsetLeft, wrapperWidth, slideWidth) => {
111
+ let scrollLeft = 0;
112
+ if (direction === NavigationDirection.PREV) {
113
+ scrollLeft = slideOffsetLeft - wrapperOffsetLeft - wrapperWidth + slideWidth;
114
+ }
115
+ else {
116
+ scrollLeft = slideOffsetLeft - wrapperOffsetLeft;
117
+ }
118
+ return scrollLeft;
119
+ };
120
+ const getVisibilityByIntersectionRatio = (intersectionRatio) => {
121
+ if (intersectionRatio >= 0.9) {
122
+ return Visibility.FULL;
123
+ }
124
+ if (intersectionRatio >= 0.5) {
125
+ return Visibility.PARTIAL;
126
+ }
127
+ return Visibility.NONE;
128
+ };
129
+ return {
130
+ addVisibleSlide,
131
+ addPartiallyVisibleSlide,
132
+ removePartiallyVisibleSlide,
133
+ removeVisibleSlide,
134
+ sortSlides,
135
+ getFirstVisibleSlideIndex,
136
+ getLastVisibleSlideIndex,
137
+ getLeftPositionToScrollTo,
138
+ getVisibilityByIntersectionRatio,
139
+ };
140
+ };
141
+
142
+ const Slider = ({ children, hideNavigationButtons = false }) => {
143
+ const slides = useRef([]);
144
+ const wrapper = useRef(null);
145
+ const [nextArrowVisible, setNextArrowVisible] = useState(false);
146
+ const [prevArrowVisible, setPrevArrowVisible] = useState(false);
20
147
  const [isScrollable, setIsScrollable] = useState(false);
21
148
  const [isDragging, setIsDragging] = useState(false);
22
149
  const [isBlockingClicks, setIsBlockingClicks] = useState(false);
@@ -24,14 +151,13 @@ const Slider = ({ children }) => {
24
151
  clientX: 0,
25
152
  scrollX: 0,
26
153
  });
27
- const arrowPrevRef = useRef(null);
28
- const arrowNextRef = useRef(null);
154
+ const { getLeftPositionToScrollTo, getVisibilityByIntersectionRatio, addVisibleSlide, addPartiallyVisibleSlide, getLastVisibleSlideIndex, sortSlides, getFirstVisibleSlideIndex, removeVisibleSlide, removePartiallyVisibleSlide, } = useSlider();
29
155
  useEffect(() => {
30
156
  const currentWrapper = wrapper.current;
31
157
  if (!currentWrapper) {
32
158
  return () => { };
33
159
  }
34
- const checkScrollable = () => setIsScrollable(currentWrapper.classList.toggle('is-scrollable', currentWrapper.scrollWidth > currentWrapper.clientWidth));
160
+ const checkScrollable = () => setIsScrollable(currentWrapper.scrollWidth > currentWrapper.clientWidth);
35
161
  window?.addEventListener('resize', checkScrollable);
36
162
  checkScrollable();
37
163
  return () => {
@@ -39,10 +165,6 @@ const Slider = ({ children }) => {
39
165
  };
40
166
  }, [wrapper]);
41
167
  useEffect(() => {
42
- wrapper.current?.classList.toggle('is-scrollable', isScrollable);
43
- }, [isScrollable]);
44
- useEffect(() => {
45
- wrapper.current?.classList.toggle('is-dragging', isDragging);
46
168
  const onDocumentMouseUp = (event) => {
47
169
  event.stopPropagation();
48
170
  event.preventDefault();
@@ -63,9 +185,7 @@ const Slider = ({ children }) => {
63
185
  }
64
186
  setIsBlockingClicks(false);
65
187
  };
66
- const mouseUpHandler = () => {
67
- setIsDragging(false);
68
- };
188
+ const mouseUpHandler = () => setIsDragging(false);
69
189
  const mouseDownHandler = (event) => {
70
190
  setMousePosition({
71
191
  ...mousePosition,
@@ -86,57 +206,30 @@ const Slider = ({ children }) => {
86
206
  const addSlide = (node, index) => {
87
207
  slides.current[index] = {
88
208
  element: node,
89
- visibility: Visbility.NONE,
209
+ visibility: Visibility.NONE,
90
210
  };
91
211
  };
92
- const getFirstVisibleSlideIndex = () => visibleSlideIndices.current[0] ?? partiallyVisibleSlideIndices.current[0] ?? -1;
93
- const getLastVisibleSlideIndex = () => visibleSlideIndices.current[visibleSlideIndices.current.length - 1]
94
- ?? partiallyVisibleSlideIndices.current[partiallyVisibleSlideIndices.current.length - 1] ?? -1;
95
212
  const setControlsVisibility = useCallback(() => {
96
213
  const lastSlideFullyVisible = getLastVisibleSlideIndex() + 1 === slides.current.length;
97
- const moreContentAvailable = isScrollable && lastSlideFullyVisible === false;
98
- const previousContentAvailable = getFirstVisibleSlideIndex() > 0 && isScrollable;
99
- if (arrowNextRef.current && arrowPrevRef.current) {
100
- arrowNextRef.current.classList.toggle('slider__button--hidden', moreContentAvailable === false);
101
- arrowNextRef.current.ariaHidden = String(moreContentAvailable === false);
102
- arrowPrevRef.current.classList.toggle('slider__button--hidden', previousContentAvailable === false);
103
- arrowPrevRef.current.ariaHidden = String(previousContentAvailable === false);
104
- }
105
- }, [isScrollable]);
106
- const getVisibilityByIntersectionRatio = (intersectionRatio) => {
107
- if (intersectionRatio >= 0.9) {
108
- return Visbility.FULL;
109
- }
110
- if (intersectionRatio >= 0.5) {
111
- return Visbility.PARTIAL;
112
- }
113
- return Visbility.NONE;
114
- };
214
+ setPrevArrowVisible(getFirstVisibleSlideIndex() > 0 && isScrollable);
215
+ setNextArrowVisible(isScrollable && lastSlideFullyVisible === false);
216
+ }, [getFirstVisibleSlideIndex, getLastVisibleSlideIndex, isScrollable]);
115
217
  useEffect(() => {
116
218
  if (!wrapper.current) {
117
219
  return () => { };
118
220
  }
119
- setControlsVisibility();
120
221
  const intersectionCallback = (entries) => {
121
222
  entries.forEach((entry) => {
122
223
  const target = entry.target;
123
224
  const index = Number(target.dataset.slideIndex);
124
- if (getVisibilityByIntersectionRatio(entry.intersectionRatio) === Visbility.FULL) {
125
- visibleSlideIndices.current.push(index);
126
- }
127
- else {
128
- visibleSlideIndices.current = visibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
129
- }
130
- if (getVisibilityByIntersectionRatio(entry.intersectionRatio) === Visbility.PARTIAL) {
131
- partiallyVisibleSlideIndices.current.push(index);
132
- }
133
- else {
134
- partiallyVisibleSlideIndices.current = partiallyVisibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
135
- }
225
+ const visibility = getVisibilityByIntersectionRatio(entry.intersectionRatio);
226
+ visibility === Visibility.FULL ? addVisibleSlide(index) : removeVisibleSlide(index);
227
+ visibility === Visibility.PARTIAL ? addPartiallyVisibleSlide(index) : removePartiallyVisibleSlide(index);
136
228
  });
137
- visibleSlideIndices.current = [...new Set(visibleSlideIndices.current)].sort((a, b) => a - b);
138
- partiallyVisibleSlideIndices.current = [...new Set(partiallyVisibleSlideIndices.current)].sort((a, b) => a - b);
139
- setControlsVisibility();
229
+ sortSlides();
230
+ if (hideNavigationButtons === false) {
231
+ setControlsVisibility();
232
+ }
140
233
  };
141
234
  const intersectionObserver = new IntersectionObserver(intersectionCallback, {
142
235
  root: wrapper.current,
@@ -144,28 +237,35 @@ const Slider = ({ children }) => {
144
237
  });
145
238
  slides.current.forEach(({ element }) => intersectionObserver.observe(element));
146
239
  return () => intersectionObserver.disconnect();
147
- }, [wrapper, setControlsVisibility]);
240
+ }, [
241
+ wrapper,
242
+ setControlsVisibility,
243
+ hideNavigationButtons,
244
+ sortSlides,
245
+ addVisibleSlide,
246
+ removeVisibleSlide,
247
+ addPartiallyVisibleSlide,
248
+ removePartiallyVisibleSlide,
249
+ getVisibilityByIntersectionRatio,
250
+ ]);
148
251
  const navigate = (direction) => {
149
252
  if (!wrapper.current) {
150
253
  return;
151
254
  }
152
255
  const targetSlideIndex = direction === NavigationDirection.PREV ? getFirstVisibleSlideIndex() - 1 : getLastVisibleSlideIndex() + 1;
153
256
  const targetSlide = slides.current[targetSlideIndex];
154
- let scrollLeft = 0;
155
257
  if (!targetSlide) {
156
258
  return;
157
259
  }
158
- if (direction === NavigationDirection.PREV) {
159
- scrollLeft = targetSlide.element.offsetLeft - wrapper.current.offsetLeft - wrapper.current.clientWidth + targetSlide.element.clientWidth;
160
- }
161
- else {
162
- scrollLeft = targetSlide.element.offsetLeft - wrapper.current.offsetLeft;
163
- }
260
+ const scrollLeft = getLeftPositionToScrollTo(direction, targetSlide.element.offsetLeft, wrapper.current.offsetLeft, wrapper.current.clientWidth, targetSlide.element.clientWidth);
164
261
  wrapper.current.scrollTo({ behavior: 'smooth', left: scrollLeft, top: 0 });
165
262
  };
166
- return (jsxs("div", { className: "slider", children: [jsx("div", { className: "slider__wrapper", role: "list", ref: wrapper, onMouseDown: mouseDownHandler, onMouseMove: mouseMoveHandler, onMouseUp: mouseUpHandler, onClickCapture: blockChildClickHandler, children: Children.map(children, (child, index) => (jsx("div", { className: "slider__wrapper__slide", role: "listitem", "data-slide-index": index, ref: (node) => { if (node) {
263
+ return (jsxs("div", { className: "slider", children: [jsx("div", { role: "list", ref: wrapper, onMouseDown: mouseDownHandler, onMouseMove: mouseMoveHandler, onMouseUp: mouseUpHandler, onClickCapture: blockChildClickHandler, className: classNames('slider__wrapper', {
264
+ 'is-scrollable': isScrollable,
265
+ 'is-dragging': isDragging,
266
+ }), children: Children.map(children, (child, index) => (jsx("div", { className: "slider__wrapper__slide", role: "listitem", "data-slide-index": index, ref: (node) => { if (node) {
167
267
  addSlide(node, index);
168
- } }, children: child }, index))) }), jsx("button", { "aria-label": "Previous slide", type: "button", onClick: () => navigate(NavigationDirection.PREV), ref: arrowPrevRef, className: "slider__button slider__button--prev slider__button--hidden", children: jsx("svg", { fill: "#33333", viewBox: "0 0 256 256", width: "24", height: "24", xmlns: "http://www.w3.org/2000/svg", children: jsx("g", { children: jsx("path", { d: "M160,220a11.96287,11.96287,0,0,1-8.48535-3.51465l-80-80a12.00062,12.00062,0,0,1,0-16.9707l80-80a12.0001,12.0001,0,0,1,16.9707,16.9707L96.9707,128l71.51465,71.51465A12,12,0,0,1,160,220Z" }) }) }) }), jsx("button", { "aria-label": "Next slide", type: "button", onClick: () => navigate(NavigationDirection.NEXT), ref: arrowNextRef, className: "slider__button slider__button--next slider__button--hidden", children: jsx("svg", { fill: "#333333", viewBox: "0 0 256 256", width: "24", height: "24", xmlns: "http://www.w3.org/2000/svg", children: jsx("g", { children: jsx("path", { d: "M96,220a12,12,0,0,1-8.48535-20.48535L159.0293,128,87.51465,56.48535a12.0001,12.0001,0,0,1,16.9707-16.9707l80,80a12.00062,12.00062,0,0,1,0,16.9707l-80,80A11.96287,11.96287,0,0,1,96,220Z" }) }) }) })] }));
268
+ } }, children: child }, index))) }), hideNavigationButtons === false && (jsxs(Fragment, { children: [jsx(PreviousButton, { onClick: () => navigate(NavigationDirection.PREV), isHidden: prevArrowVisible === false }), jsx(NextButton, { onClick: () => navigate(NavigationDirection.NEXT), isHidden: nextArrowVisible === false })] }))] }));
169
269
  };
170
270
 
171
271
  export { Slider };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/Slider.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../node_modules/classnames/index.js","../src/Components/Controls/NextButton.tsx","../src/Components/Controls/PreviousButton.tsx","../src/Hooks/UseSlider.ts","../src/Slider.tsx"],"sourcesContent":["/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\tvar nativeCodeString = '[native code]';\n\n\tfunction classNames() {\n\t\tvar classes = [];\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (!arg) continue;\n\n\t\t\tvar argType = typeof arg;\n\n\t\t\tif (argType === 'string' || argType === 'number') {\n\t\t\t\tclasses.push(arg);\n\t\t\t} else if (Array.isArray(arg)) {\n\t\t\t\tif (arg.length) {\n\t\t\t\t\tvar inner = classNames.apply(null, arg);\n\t\t\t\t\tif (inner) {\n\t\t\t\t\t\tclasses.push(inner);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (argType === 'object') {\n\t\t\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\t\t\tclasses.push(arg.toString());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (var key in arg) {\n\t\t\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\t\t\tclasses.push(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn classes.join(' ');\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n",null,null,null,null],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoursurprise/slider",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Basic React slider using modern Javascript and CSS",
5
5
  "module": "dist/index.js",
6
6
  "main": "dist/index.js",
@@ -22,6 +22,9 @@
22
22
  "serve": "parcel index.html",
23
23
  "deploy-pages": "gh-pages -d dist"
24
24
  },
25
+ "dependencies": {
26
+ "classnames": "^2.3.2"
27
+ },
25
28
  "peerDependencies": {
26
29
  "react": "^18.2.0",
27
30
  "react-dom": "^18.2.0"
@@ -56,6 +59,7 @@
56
59
  "npm": "^8.19.3",
57
60
  "parcel": "^2.8.3",
58
61
  "postcss": "^8.4.18",
62
+ "process": "^0.11.10",
59
63
  "react": "^18.2.0",
60
64
  "react-dom": "^18.2.0",
61
65
  "react-test-renderer": "^18.2.0",
@@ -0,0 +1,42 @@
1
+ .slider__button {
2
+ position: absolute;
3
+ top: 0;
4
+ bottom: 0;
5
+ height: 48px;
6
+ width: 48px;
7
+ margin: auto 0;
8
+ border-radius: 50%;
9
+ background-color: white;
10
+ align-items: center;
11
+ justify-content: center;
12
+ cursor: pointer;
13
+ display: none;
14
+ border: none;
15
+ outline: none;
16
+ box-shadow: inset 0 0 0 1px rgba(32, 32, 32, .1);
17
+ transition: box-shadow .2s ease-in-out;
18
+
19
+ &__icon {
20
+ fill: #554c44;
21
+ }
22
+
23
+ &:hover {
24
+ box-shadow: inset 0 0 0 1px rgba(32, 32, 32, .2);
25
+ }
26
+
27
+ @media (hover: hover) {
28
+ display: flex;
29
+ }
30
+
31
+ &--hidden {
32
+ display: none;
33
+ }
34
+
35
+ &--next {
36
+ right: 5px;
37
+ }
38
+
39
+ &--prev {
40
+ left: 5px;
41
+ }
42
+ }
@@ -0,0 +1,28 @@
1
+ import classNames from 'classnames';
2
+ import React from 'react';
3
+ import './Button.scss';
4
+
5
+ interface Props {
6
+ onClick: () => void;
7
+ isHidden: boolean;
8
+ }
9
+
10
+ export const NextButton: React.FC<Props> = ({ onClick, isHidden }) => {
11
+ return (
12
+ <button
13
+ aria-label="Next slide"
14
+ type="button"
15
+ onClick={onClick}
16
+ aria-hidden={isHidden}
17
+ className={classNames([
18
+ 'slider__button',
19
+ 'slider__button--next',
20
+ { 'slider__button--hidden': isHidden },
21
+ ])}
22
+ >
23
+ <svg className="slider__button__icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 256 256">
24
+ <path d="M96,216a8.5,8.5,0,0,1-5.7-2.3,8.1,8.1,0,0,1,0-11.4L164.7,128,90.3,53.7a8.1,8.1,0,0,1,11.4-11.4l80,80a8.1,8.1,0,0,1,0,11.4l-80,80A8.5,8.5,0,0,1,96,216Z"/>
25
+ </svg>
26
+ </button>
27
+ );
28
+ };
@@ -0,0 +1,28 @@
1
+ import classNames from 'classnames';
2
+ import React from 'react';
3
+ import './Button.scss';
4
+
5
+ interface Props {
6
+ onClick: () => void;
7
+ isHidden: boolean;
8
+ }
9
+
10
+ export const PreviousButton: React.FC<Props> = ({ onClick, isHidden }) => {
11
+ return (
12
+ <button
13
+ aria-label="Previous slide"
14
+ type="button"
15
+ onClick={onClick}
16
+ aria-hidden={isHidden}
17
+ className={classNames([
18
+ 'slider__button',
19
+ 'slider__button--prev',
20
+ { 'slider__button--hidden': isHidden },
21
+ ])}
22
+ >
23
+ <svg className="slider__button__icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 256 256">
24
+ <path d="M160,216a8.5,8.5,0,0,1-5.7-2.3l-80-80a8.1,8.1,0,0,1,0-11.4l80-80a8.1,8.1,0,0,1,11.4,11.4L91.3,128l74.4,74.3a8.1,8.1,0,0,1,0,11.4A8.5,8.5,0,0,1,160,216Z"/>
25
+ </svg>
26
+ </button>
27
+ );
28
+ };
@@ -0,0 +1,123 @@
1
+ import { NavigationDirection, useSlider, Visibility } from './UseSlider';
2
+ import { renderHook } from '@testing-library/react';
3
+
4
+ describe('UseSlider', () => {
5
+ describe('keeps track of slides', () => {
6
+ it('gets the first fully visible slide', () => {
7
+ const { result } = renderHook(() => useSlider());
8
+
9
+ result.current.addPartiallyVisibleSlide(1);
10
+ result.current.addVisibleSlide(2);
11
+ result.current.addVisibleSlide(3);
12
+
13
+
14
+ expect(result.current.getFirstVisibleSlideIndex()).toBe(2);
15
+ });
16
+
17
+ it('gets the last fully visible slide', () => {
18
+ const { result } = renderHook(() => useSlider());
19
+
20
+ result.current.addVisibleSlide(1);
21
+ result.current.addVisibleSlide(2);
22
+ result.current.addPartiallyVisibleSlide(3);
23
+
24
+ expect(result.current.getLastVisibleSlideIndex()).toBe(2);
25
+ });
26
+
27
+
28
+ it('gets the first partially visible slide when no slide is fully visible', () => {
29
+ const { result } = renderHook(() => useSlider());
30
+
31
+ result.current.addPartiallyVisibleSlide(1);
32
+ result.current.addPartiallyVisibleSlide(2);
33
+
34
+ expect(result.current.getFirstVisibleSlideIndex()).toBe(1);
35
+ });
36
+
37
+ it('gets the last visible slide when no slide is fully visible', () => {
38
+ const { result } = renderHook(() => useSlider());
39
+
40
+ result.current.addPartiallyVisibleSlide(1);
41
+ result.current.addPartiallyVisibleSlide(2);
42
+
43
+ expect(result.current.getLastVisibleSlideIndex()).toBe(2);
44
+ });
45
+
46
+
47
+ it('falls back when first slide is non-existant', () => {
48
+ const { result } = renderHook(() => useSlider());
49
+ expect(result.current.getFirstVisibleSlideIndex()).toBe(-1);
50
+ });
51
+
52
+
53
+ it('falls back when last slide is non-existant', () => {
54
+ const { result } = renderHook(() => useSlider());
55
+ expect(result.current.getLastVisibleSlideIndex()).toBe(-1);
56
+ });
57
+
58
+ it('sorts the fully visible slides by index', () => {
59
+ const { result } = renderHook(() => useSlider());
60
+
61
+ result.current.addVisibleSlide(3);
62
+ result.current.addVisibleSlide(2);
63
+ result.current.addVisibleSlide(1);
64
+
65
+ expect(result.current.getFirstVisibleSlideIndex()).toBe(3);
66
+ expect(result.current.getLastVisibleSlideIndex()).toBe(1);
67
+
68
+ result.current.sortSlides();
69
+
70
+ expect(result.current.getLastVisibleSlideIndex()).toBe(3);
71
+ });
72
+
73
+ it('sorts the partially fully visible slides by index', () => {
74
+ const { result } = renderHook(() => useSlider());
75
+
76
+ result.current.addPartiallyVisibleSlide(3);
77
+ result.current.addPartiallyVisibleSlide(2);
78
+ result.current.addPartiallyVisibleSlide(1);
79
+
80
+ expect(result.current.getFirstVisibleSlideIndex()).toBe(3);
81
+ expect(result.current.getLastVisibleSlideIndex()).toBe(1);
82
+
83
+ result.current.sortSlides();
84
+
85
+ expect(result.current.getLastVisibleSlideIndex()).toBe(3);
86
+ });
87
+ });
88
+
89
+ describe('calculations', () => {
90
+
91
+ it('calculates the scrollLeft position (next)', () => {
92
+ const { result } = renderHook(() => useSlider());
93
+
94
+ expect(result.current.getLeftPositionToScrollTo(NavigationDirection.NEXT, 200, 50, 1250, 325)).toBe(150);
95
+ });
96
+
97
+ it('calculates the scrollLeft position (prev)', () => {
98
+ const { result } = renderHook(() => useSlider());
99
+
100
+ expect(result.current.getLeftPositionToScrollTo(NavigationDirection.PREV, 875, 50, 1250, 325)).toBe(-100);
101
+ });
102
+
103
+ it('calculates the visibility of a slide', () => {
104
+ const { result } = renderHook(() => useSlider());
105
+
106
+ [
107
+ [0.0, Visibility.NONE],
108
+ [0.1, Visibility.NONE],
109
+ [0.2, Visibility.NONE],
110
+ [0.3, Visibility.NONE],
111
+ [0.4, Visibility.NONE],
112
+ [0.5, Visibility.PARTIAL],
113
+ [0.6, Visibility.PARTIAL],
114
+ [0.7, Visibility.PARTIAL],
115
+ [0.8, Visibility.PARTIAL],
116
+ [0.9, Visibility.FULL],
117
+ [1.0, Visibility.FULL],
118
+ ].forEach(([ratio, expectation]) => {
119
+ expect(result.current.getVisibilityByIntersectionRatio(ratio)).toBe(expectation);
120
+ });
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,84 @@
1
+ import { useRef } from 'react';
2
+
3
+ export enum Visibility {
4
+ FULL,
5
+ PARTIAL,
6
+ NONE,
7
+ }
8
+
9
+ export enum NavigationDirection {
10
+ PREV,
11
+ NEXT,
12
+ }
13
+
14
+ interface UseSlider {
15
+ getLeftPositionToScrollTo: (direction: NavigationDirection, slideOffsetLeft: number, wrapperOffsetLeft: number, wrapperWidth: number, slideWidth: number) => number;
16
+ getVisibilityByIntersectionRatio: (intersectionRatio: number) => Visibility;
17
+ getFirstVisibleSlideIndex: () => number;
18
+ getLastVisibleSlideIndex: () => number;
19
+ addVisibleSlide: (index: number) => void;
20
+ addPartiallyVisibleSlide: (index: number) => void;
21
+ sortSlides: () => void;
22
+ removeVisibleSlide: (index: number) => void;
23
+ removePartiallyVisibleSlide: (index: number) => void;
24
+ }
25
+
26
+ export const useSlider = (): UseSlider => {
27
+ const visibleSlideIndices = useRef<number[]>([]);
28
+ const partiallyVisibleSlideIndices = useRef<number[]>([]);
29
+
30
+ const getFirstVisibleSlideIndex = (): number => visibleSlideIndices.current[0] ?? partiallyVisibleSlideIndices.current[0] ?? -1;
31
+
32
+ const getLastVisibleSlideIndex = (): number => visibleSlideIndices.current[visibleSlideIndices.current.length - 1]
33
+ ?? partiallyVisibleSlideIndices.current[partiallyVisibleSlideIndices.current.length - 1] ?? -1;
34
+
35
+ const addVisibleSlide = (index: number) => visibleSlideIndices.current.push(index);
36
+
37
+ const addPartiallyVisibleSlide = (index: number) => partiallyVisibleSlideIndices.current.push(index);
38
+
39
+ const removePartiallyVisibleSlide = (index: number) => partiallyVisibleSlideIndices.current = partiallyVisibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
40
+
41
+ const removeVisibleSlide = (index: number) => visibleSlideIndices.current = visibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
42
+
43
+ const sortSlides = () => {
44
+ // Make sure there are no duplicate visible slides, then sort to retain proper order
45
+ visibleSlideIndices.current = [...new Set(visibleSlideIndices.current)].sort((a, b) => a - b);
46
+ partiallyVisibleSlideIndices.current = [...new Set(partiallyVisibleSlideIndices.current)].sort((a, b) => a - b);
47
+ };
48
+
49
+ const getLeftPositionToScrollTo = (direction: NavigationDirection, slideOffsetLeft: number, wrapperOffsetLeft: number, wrapperWidth: number, slideWidth: number) => {
50
+ let scrollLeft = 0;
51
+
52
+ if (direction === NavigationDirection.PREV) {
53
+ scrollLeft = slideOffsetLeft - wrapperOffsetLeft - wrapperWidth + slideWidth;
54
+ } else {
55
+ scrollLeft = slideOffsetLeft - wrapperOffsetLeft;
56
+ }
57
+
58
+ return scrollLeft;
59
+ };
60
+
61
+ const getVisibilityByIntersectionRatio = (intersectionRatio: number) => {
62
+ if (intersectionRatio >= 0.9) {
63
+ return Visibility.FULL;
64
+ }
65
+
66
+ if (intersectionRatio >= 0.5) {
67
+ return Visibility.PARTIAL;
68
+ }
69
+
70
+ return Visibility.NONE;
71
+ };
72
+
73
+ return {
74
+ addVisibleSlide,
75
+ addPartiallyVisibleSlide,
76
+ removePartiallyVisibleSlide,
77
+ removeVisibleSlide,
78
+ sortSlides,
79
+ getFirstVisibleSlideIndex,
80
+ getLastVisibleSlideIndex,
81
+ getLeftPositionToScrollTo,
82
+ getVisibilityByIntersectionRatio,
83
+ };
84
+ };
package/src/Slider.scss CHANGED
@@ -1,43 +1,6 @@
1
1
  .slider {
2
2
  position: relative;
3
3
 
4
- &__button {
5
- position: absolute;
6
- top: 0;
7
- bottom: 0;
8
-
9
- height: 48px;
10
- width: 48px;
11
- margin: auto 0;
12
- border-radius: 50%;
13
-
14
- border: 1px solid #C9C9C9;
15
- background-color: white;
16
-
17
- align-items: center;
18
- justify-content: center;
19
-
20
- cursor: pointer;
21
-
22
- display: none;
23
-
24
- @media (hover: hover) {
25
- display: flex;
26
- }
27
-
28
- &--hidden {
29
- display: none;
30
- }
31
-
32
- &--next {
33
- right: 5px;
34
- }
35
-
36
- &--prev {
37
- left: 5px;
38
- }
39
- }
40
-
41
4
  &__wrapper {
42
5
 
43
6
  // Prevent the browser from going back in history when there is no scroll area left
@@ -1,4 +1,4 @@
1
- import { fireEvent, render, screen } from '@testing-library/react';
1
+ import { act, fireEvent, render, screen } from '@testing-library/react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import '@testing-library/jest-dom';
4
4
  import { Slider } from './Slider';
@@ -89,8 +89,23 @@ describe('UpsellSlider', () => {
89
89
  const nextButton = screen.getByLabelText('Next slide');
90
90
  const prevButton = screen.getByLabelText('Previous slide');
91
91
 
92
- expect(prevButton.ariaHidden).toBe('true');
93
- expect(nextButton.ariaHidden).toBe('false');
92
+ expect(prevButton).toHaveAttribute('aria-hidden', 'true');
93
+ expect(nextButton).toHaveAttribute('aria-hidden', 'true');
94
+ });
95
+
96
+ it('does not render controls if disabled', () => {
97
+ render(<Slider hideNavigationButtons={true}>
98
+ <span key={1}/>
99
+ <span key={2}/>
100
+ <span key={3}/>
101
+ <span key={4}/>
102
+ </Slider>);
103
+
104
+ const nextButton = screen.queryByLabelText('Next slide');
105
+ const prevButton = screen.queryByLabelText('Previous slide');
106
+
107
+ expect(prevButton).not.toBeInTheDocument();
108
+ expect(nextButton).not.toBeInTheDocument();
94
109
  });
95
110
 
96
111
  it('allows scrolling by dragging with the mouse', () => {
@@ -164,12 +179,14 @@ describe('UpsellSlider', () => {
164
179
  Object.defineProperty(child, 'offsetLeft', { value: 100 * (i + 1) });
165
180
  });
166
181
 
167
- intersectionCallback([
168
- { intersectionRatio: 1, target: slides[0] } as unknown as IntersectionObserverEntry,
169
- { intersectionRatio: 0.5, target: slides[1] } as unknown as IntersectionObserverEntry,
170
- { intersectionRatio: 0, target: slides[2] } as unknown as IntersectionObserverEntry,
171
- { intersectionRatio: 0, target: slides[3] } as unknown as IntersectionObserverEntry,
172
- ]);
182
+ await act(() => {
183
+ intersectionCallback([
184
+ { intersectionRatio: 1, target: slides[ 0 ] } as unknown as IntersectionObserverEntry,
185
+ { intersectionRatio: 0.5, target: slides[ 1 ] } as unknown as IntersectionObserverEntry,
186
+ { intersectionRatio: 0, target: slides[ 2 ] } as unknown as IntersectionObserverEntry,
187
+ { intersectionRatio: 0, target: slides[ 3 ] } as unknown as IntersectionObserverEntry,
188
+ ]);
189
+ });
173
190
 
174
191
  await userEvent.click(nextButton);
175
192
 
@@ -191,12 +208,14 @@ describe('UpsellSlider', () => {
191
208
  Object.defineProperty(child, 'offsetLeft', { value: 100 * (i + 1) });
192
209
  });
193
210
 
194
- intersectionCallback([
195
- { intersectionRatio: 0, target: slides[0] } as unknown as IntersectionObserverEntry,
196
- { intersectionRatio: 0, target: slides[1] } as unknown as IntersectionObserverEntry,
197
- { intersectionRatio: 1, target: slides[2] } as unknown as IntersectionObserverEntry,
198
- { intersectionRatio: 0.5, target: slides[3] } as unknown as IntersectionObserverEntry,
199
- ]);
211
+ await act(() => {
212
+ intersectionCallback([
213
+ { intersectionRatio: 0, target: slides[ 0 ] } as unknown as IntersectionObserverEntry,
214
+ { intersectionRatio: 0, target: slides[ 1 ] } as unknown as IntersectionObserverEntry,
215
+ { intersectionRatio: 1, target: slides[ 2 ] } as unknown as IntersectionObserverEntry,
216
+ { intersectionRatio: 0.5, target: slides[ 3 ] } as unknown as IntersectionObserverEntry,
217
+ ]);
218
+ });
200
219
 
201
220
  await userEvent.click(prevButton);
202
221
 
@@ -207,7 +226,7 @@ describe('UpsellSlider', () => {
207
226
  });
208
227
  });
209
228
 
210
- it('updates controls visibility', () => {
229
+ it('updates controls visibility', async () => {
211
230
  Object.defineProperty(HTMLElement.prototype, 'clientWidth', { configurable: true, value: 500 });
212
231
  Object.defineProperty(HTMLElement.prototype, 'scrollWidth', { configurable: true, value: 1000 });
213
232
  Object.defineProperty(HTMLElement.prototype, 'scrollLeft', { configurable: true, value: 0, writable: true });
@@ -223,26 +242,33 @@ describe('UpsellSlider', () => {
223
242
  const nextButton = screen.getByLabelText('Next slide');
224
243
  const prevButton = screen.getByLabelText('Previous slide');
225
244
 
226
- ([
245
+ const testCases = [
227
246
  // All slides are visible
228
247
  [[1, 1, 1, 1], true, true],
229
248
  // Only the first slide is not visible
230
249
  [[0, 1, 1, 1], false, true],
231
250
  // Only the last slide is not visible
232
251
  [[1, 1, 1, 0], true, false],
233
- ] as Array<[number[], boolean, boolean]>).forEach((expectations) => {
234
- const [intersections, prevButtonHidden, nextButtonHidden] = expectations;
235
-
236
- intersectionCallback([
237
- { intersectionRatio: intersections[0], target: slides[0] } as unknown as IntersectionObserverEntry,
238
- { intersectionRatio: intersections[1], target: slides[1] } as unknown as IntersectionObserverEntry,
239
- { intersectionRatio: intersections[2], target: slides[2] } as unknown as IntersectionObserverEntry,
240
- { intersectionRatio: intersections[3], target: slides[3] } as unknown as IntersectionObserverEntry,
241
- ]);
252
+ // Only a single slide is partially visible
253
+ [[0, 0, 0.5, 0], false, false],
254
+ ] as Array<[number[], boolean, boolean]>;
255
+
256
+ for (const testCase of testCases) {
257
+ const [intersections, prevButtonHidden, nextButtonHidden] = testCase;
258
+
259
+ // eslint-disable-next-line @typescript-eslint/no-loop-func
260
+ await act(() => {
261
+ intersectionCallback([
262
+ { intersectionRatio: intersections[0], target: slides[0] } as unknown as IntersectionObserverEntry,
263
+ { intersectionRatio: intersections[1], target: slides[1] } as unknown as IntersectionObserverEntry,
264
+ { intersectionRatio: intersections[2], target: slides[2] } as unknown as IntersectionObserverEntry,
265
+ { intersectionRatio: intersections[3], target: slides[3] } as unknown as IntersectionObserverEntry,
266
+ ]);
267
+ });
242
268
 
243
- expect(prevButton.ariaHidden).toBe(String(prevButtonHidden));
244
- expect(nextButton.ariaHidden).toBe(String(nextButtonHidden));
245
- });
269
+ expect(prevButton).toHaveAttribute('aria-hidden', String(prevButtonHidden));
270
+ expect(nextButton).toHaveAttribute('aria-hidden', String(nextButtonHidden));
271
+ }
246
272
  });
247
273
  });
248
274
  });
package/src/Slider.tsx CHANGED
@@ -1,29 +1,28 @@
1
+ import classNames from 'classnames';
1
2
  import type React from 'react';
2
3
  import type { MouseEvent as ReactMouseEvent, PropsWithChildren } from 'react';
3
4
  import { useRef, useEffect, Children, useCallback, useState } from 'react';
5
+ import { NextButton } from './Components/Controls/NextButton';
6
+ import { PreviousButton } from './Components/Controls/PreviousButton';
7
+ import { NavigationDirection, useSlider, Visibility } from './Hooks/UseSlider';
4
8
  import './Slider.scss';
5
9
 
6
- enum Visbility {
7
- FULL,
8
- PARTIAL,
9
- NONE,
10
- }
11
-
12
- enum NavigationDirection {
13
- PREV,
14
- NEXT,
15
- }
16
-
17
10
  interface SlideVisibilityEntry {
18
11
  element: HTMLDivElement;
19
- visibility: Visbility;
12
+ visibility: Visibility;
20
13
  }
21
14
 
22
- export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
15
+ interface Settings {
16
+ // Sets whether the navigation buttons (next/prev) are no longer rendered
17
+ hideNavigationButtons?: boolean;
18
+ }
19
+
20
+ export const Slider: React.FC<PropsWithChildren<Settings>> = ({ children, hideNavigationButtons = false }) => {
23
21
  const slides = useRef<SlideVisibilityEntry[]>([]);
24
22
  const wrapper = useRef<HTMLDivElement>(null);
25
- const visibleSlideIndices = useRef<number[]>([]);
26
- const partiallyVisibleSlideIndices = useRef<number[]>([]);
23
+
24
+ const [nextArrowVisible, setNextArrowVisible] = useState<boolean>(false);
25
+ const [prevArrowVisible, setPrevArrowVisible] = useState<boolean>(false);
27
26
 
28
27
  const [isScrollable, setIsScrollable] = useState<boolean>(false);
29
28
  const [isDragging, setIsDragging] = useState<boolean>(false);
@@ -33,8 +32,17 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
33
32
  scrollX: 0,
34
33
  });
35
34
 
36
- const arrowPrevRef = useRef<HTMLButtonElement>(null);
37
- const arrowNextRef = useRef<HTMLButtonElement>(null);
35
+ const {
36
+ getLeftPositionToScrollTo,
37
+ getVisibilityByIntersectionRatio,
38
+ addVisibleSlide,
39
+ addPartiallyVisibleSlide,
40
+ getLastVisibleSlideIndex,
41
+ sortSlides,
42
+ getFirstVisibleSlideIndex,
43
+ removeVisibleSlide,
44
+ removePartiallyVisibleSlide,
45
+ } = useSlider();
38
46
 
39
47
  useEffect(() => {
40
48
  const currentWrapper = wrapper.current;
@@ -43,7 +51,7 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
43
51
  return () => {};
44
52
  }
45
53
 
46
- const checkScrollable = () => setIsScrollable(currentWrapper.classList.toggle('is-scrollable', currentWrapper.scrollWidth > currentWrapper.clientWidth));
54
+ const checkScrollable = () => setIsScrollable(currentWrapper.scrollWidth > currentWrapper.clientWidth);
47
55
 
48
56
  window?.addEventListener('resize', checkScrollable);
49
57
 
@@ -55,12 +63,6 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
55
63
  }, [wrapper]);
56
64
 
57
65
  useEffect(() => {
58
- wrapper.current?.classList.toggle('is-scrollable', isScrollable);
59
- }, [isScrollable]);
60
-
61
- useEffect(() => {
62
- wrapper.current?.classList.toggle('is-dragging', isDragging);
63
-
64
66
  const onDocumentMouseUp = (event: MouseEvent) => {
65
67
  event.stopPropagation();
66
68
  event.preventDefault();
@@ -87,9 +89,7 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
87
89
  setIsBlockingClicks(false);
88
90
  };
89
91
 
90
- const mouseUpHandler = () => {
91
- setIsDragging(false);
92
- };
92
+ const mouseUpHandler = () => setIsDragging(false);
93
93
 
94
94
  const mouseDownHandler = (event: ReactMouseEvent<HTMLDivElement>) => {
95
95
  setMousePosition({
@@ -116,71 +116,37 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
116
116
  const addSlide = (node: HTMLDivElement, index: number) => {
117
117
  slides.current[index] = {
118
118
  element: node,
119
- visibility: Visbility.NONE,
119
+ visibility: Visibility.NONE,
120
120
  };
121
121
  };
122
122
 
123
- const getFirstVisibleSlideIndex = (): number => visibleSlideIndices.current[0] ?? partiallyVisibleSlideIndices.current[0] ?? -1;
124
-
125
- const getLastVisibleSlideIndex = (): number => visibleSlideIndices.current[visibleSlideIndices.current.length - 1]
126
- ?? partiallyVisibleSlideIndices.current[partiallyVisibleSlideIndices.current.length - 1] ?? -1;
127
-
128
123
  const setControlsVisibility = useCallback(() => {
129
124
  const lastSlideFullyVisible = getLastVisibleSlideIndex() + 1 === slides.current.length;
130
- const moreContentAvailable = isScrollable && lastSlideFullyVisible === false;
131
- const previousContentAvailable = getFirstVisibleSlideIndex() > 0 && isScrollable;
132
125
 
133
- if (arrowNextRef.current && arrowPrevRef.current) {
134
- arrowNextRef.current.classList.toggle('slider__button--hidden', moreContentAvailable === false);
135
- arrowNextRef.current.ariaHidden = String(moreContentAvailable === false);
136
-
137
- arrowPrevRef.current.classList.toggle('slider__button--hidden', previousContentAvailable === false);
138
- arrowPrevRef.current.ariaHidden = String(previousContentAvailable === false);
139
- }
140
- }, [isScrollable]);
141
-
142
- const getVisibilityByIntersectionRatio = (intersectionRatio: number) => {
143
- if (intersectionRatio >= 0.9) {
144
- return Visbility.FULL;
145
- }
146
-
147
- if (intersectionRatio >= 0.5) {
148
- return Visbility.PARTIAL;
149
- }
150
-
151
- return Visbility.NONE;
152
- };
126
+ setPrevArrowVisible(getFirstVisibleSlideIndex() > 0 && isScrollable);
127
+ setNextArrowVisible(isScrollable && lastSlideFullyVisible === false);
128
+ }, [getFirstVisibleSlideIndex, getLastVisibleSlideIndex, isScrollable]);
153
129
 
154
130
  useEffect(() => {
155
131
  if (!wrapper.current) {
156
132
  return () => {};
157
133
  }
158
134
 
159
- setControlsVisibility();
160
-
161
135
  const intersectionCallback = (entries: IntersectionObserverEntry[]) => {
162
136
  entries.forEach((entry: IntersectionObserverEntry) => {
163
137
  const target = entry.target as HTMLDivElement;
164
138
  const index = Number(target.dataset.slideIndex);
139
+ const visibility = getVisibilityByIntersectionRatio(entry.intersectionRatio);
165
140
 
166
- if (getVisibilityByIntersectionRatio(entry.intersectionRatio) === Visbility.FULL) {
167
- visibleSlideIndices.current.push(index);
168
- } else {
169
- visibleSlideIndices.current = visibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
170
- }
171
-
172
- if (getVisibilityByIntersectionRatio(entry.intersectionRatio) === Visbility.PARTIAL) {
173
- partiallyVisibleSlideIndices.current.push(index);
174
- } else {
175
- partiallyVisibleSlideIndices.current = partiallyVisibleSlideIndices.current.filter((slideIndex) => slideIndex !== index);
176
- }
141
+ visibility === Visibility.FULL ? addVisibleSlide(index) : removeVisibleSlide(index);
142
+ visibility === Visibility.PARTIAL ? addPartiallyVisibleSlide(index) : removePartiallyVisibleSlide(index);
177
143
  });
178
144
 
179
- // Make sure there are no duplicate visible slides, then sort to retain proper order
180
- visibleSlideIndices.current = [...new Set(visibleSlideIndices.current)].sort((a, b) => a - b);
181
- partiallyVisibleSlideIndices.current = [...new Set(partiallyVisibleSlideIndices.current)].sort((a, b) => a - b);
145
+ sortSlides();
182
146
 
183
- setControlsVisibility();
147
+ if (hideNavigationButtons === false) {
148
+ setControlsVisibility();
149
+ }
184
150
  };
185
151
 
186
152
  const intersectionObserver = new IntersectionObserver(intersectionCallback, {
@@ -191,7 +157,17 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
191
157
  slides.current.forEach(({ element }) => intersectionObserver.observe(element));
192
158
 
193
159
  return () => intersectionObserver.disconnect();
194
- }, [wrapper, setControlsVisibility]);
160
+ }, [
161
+ wrapper,
162
+ setControlsVisibility,
163
+ hideNavigationButtons,
164
+ sortSlides,
165
+ addVisibleSlide,
166
+ removeVisibleSlide,
167
+ addPartiallyVisibleSlide,
168
+ removePartiallyVisibleSlide,
169
+ getVisibilityByIntersectionRatio,
170
+ ]);
195
171
 
196
172
  const navigate = (direction: NavigationDirection) => {
197
173
  if (!wrapper.current) {
@@ -199,30 +175,34 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
199
175
  }
200
176
 
201
177
  const targetSlideIndex = direction === NavigationDirection.PREV ? getFirstVisibleSlideIndex() - 1 : getLastVisibleSlideIndex() + 1;
202
-
203
178
  const targetSlide = slides.current[targetSlideIndex];
204
- let scrollLeft = 0;
205
179
 
206
180
  if (!targetSlide) {
207
181
  return;
208
182
  }
209
183
 
210
- if (direction === NavigationDirection.PREV) {
211
- scrollLeft = targetSlide.element.offsetLeft - wrapper.current.offsetLeft - wrapper.current.clientWidth + targetSlide.element.clientWidth;
212
- } else {
213
- scrollLeft = targetSlide.element.offsetLeft - wrapper.current.offsetLeft;
214
- }
184
+ const scrollLeft = getLeftPositionToScrollTo(
185
+ direction,
186
+ targetSlide.element.offsetLeft,
187
+ wrapper.current.offsetLeft,
188
+ wrapper.current.clientWidth,
189
+ targetSlide.element.clientWidth,
190
+ );
215
191
 
216
192
  wrapper.current.scrollTo({ behavior: 'smooth', left: scrollLeft, top: 0 });
217
193
  };
218
194
 
219
195
  return (
220
196
  <div className="slider">
221
- <div className="slider__wrapper" role="list" ref={wrapper}
197
+ <div role="list" ref={wrapper}
222
198
  onMouseDown={mouseDownHandler}
223
199
  onMouseMove={mouseMoveHandler}
224
200
  onMouseUp={mouseUpHandler}
225
201
  onClickCapture={blockChildClickHandler}
202
+ className={classNames('slider__wrapper', {
203
+ 'is-scrollable': isScrollable,
204
+ 'is-dragging': isDragging,
205
+ })}
226
206
  >
227
207
  {Children.map(children, (child, index: number) => (
228
208
  <div className="slider__wrapper__slide" role="listitem" key={index} data-slide-index={index} ref={(node) => { if (node) { addSlide(node, index); } }}>
@@ -230,32 +210,12 @@ export const Slider: React.FC<PropsWithChildren> = ({ children }) => {
230
210
  </div>
231
211
  ))}
232
212
  </div>
233
- <button
234
- aria-label="Previous slide"
235
- type="button"
236
- onClick={() => navigate(NavigationDirection.PREV)}
237
- ref={arrowPrevRef}
238
- className="slider__button slider__button--prev slider__button--hidden"
239
- >
240
- <svg fill="#33333" viewBox="0 0 256 256" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
241
- <g>
242
- <path d="M160,220a11.96287,11.96287,0,0,1-8.48535-3.51465l-80-80a12.00062,12.00062,0,0,1,0-16.9707l80-80a12.0001,12.0001,0,0,1,16.9707,16.9707L96.9707,128l71.51465,71.51465A12,12,0,0,1,160,220Z"></path>
243
- </g>
244
- </svg>
245
- </button>
246
- <button
247
- aria-label="Next slide"
248
- type="button"
249
- onClick={() => navigate(NavigationDirection.NEXT)}
250
- ref={arrowNextRef}
251
- className="slider__button slider__button--next slider__button--hidden"
252
- >
253
- <svg fill="#333333" viewBox="0 0 256 256" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
254
- <g>
255
- <path d="M96,220a12,12,0,0,1-8.48535-20.48535L159.0293,128,87.51465,56.48535a12.0001,12.0001,0,0,1,16.9707-16.9707l80,80a12.00062,12.00062,0,0,1,0,16.9707l-80,80A11.96287,11.96287,0,0,1,96,220Z"></path>
256
- </g>
257
- </svg>
258
- </button>
213
+ { hideNavigationButtons === false && (
214
+ <>
215
+ <PreviousButton onClick={() => navigate(NavigationDirection.PREV)} isHidden={prevArrowVisible === false}/>
216
+ <NextButton onClick={() => navigate(NavigationDirection.NEXT)} isHidden={nextArrowVisible === false}/>
217
+ </>
218
+ )}
259
219
  </div>
260
220
  );
261
221
  };