use-scroller 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +108 -0
  3. package/dist/cjs/hooks/use-iso-morphic-effect.d.ts +2 -0
  4. package/dist/cjs/hooks/use-iso-morphic-effect.js +6 -0
  5. package/dist/cjs/hooks/use-scroll.d.ts +19 -0
  6. package/dist/cjs/hooks/use-scroll.js +84 -0
  7. package/dist/cjs/hooks/use-window-scroll.d.ts +12 -0
  8. package/dist/cjs/hooks/use-window-scroll.js +56 -0
  9. package/dist/cjs/index.d.ts +3 -0
  10. package/dist/cjs/index.js +19 -0
  11. package/dist/cjs/scroller.d.ts +9 -0
  12. package/dist/cjs/scroller.js +95 -0
  13. package/dist/cjs/types.d.ts +17 -0
  14. package/dist/cjs/types.js +2 -0
  15. package/dist/cjs/utils.d.ts +24 -0
  16. package/dist/cjs/utils.js +79 -0
  17. package/dist/esm/hooks/use-iso-morphic-effect.d.ts +2 -0
  18. package/dist/esm/hooks/use-iso-morphic-effect.js +3 -0
  19. package/dist/esm/hooks/use-scroll.d.ts +19 -0
  20. package/dist/esm/hooks/use-scroll.js +80 -0
  21. package/dist/esm/hooks/use-window-scroll.d.ts +12 -0
  22. package/dist/esm/hooks/use-window-scroll.js +52 -0
  23. package/dist/esm/index.d.ts +3 -0
  24. package/dist/esm/index.js +3 -0
  25. package/dist/esm/scroller.d.ts +9 -0
  26. package/dist/esm/scroller.js +91 -0
  27. package/dist/esm/types.d.ts +17 -0
  28. package/dist/esm/types.js +1 -0
  29. package/dist/esm/utils.d.ts +24 -0
  30. package/dist/esm/utils.js +72 -0
  31. package/package.json +50 -0
  32. package/src/hooks/use-iso-morphic-effect.ts +4 -0
  33. package/src/hooks/use-scroll.ts +88 -0
  34. package/src/hooks/use-window-scroll.ts +56 -0
  35. package/src/index.ts +3 -0
  36. package/src/scroller.ts +109 -0
  37. package/src/types.ts +21 -0
  38. package/src/utils.ts +90 -0
@@ -0,0 +1,109 @@
1
+ import { ScrollerProps } from './types'
2
+ import { defaultScrollOptions, getEasing, resolveScrollValues, validateElement } from './utils'
3
+
4
+ export function scroller({ container, options = defaultScrollOptions }: ScrollerProps) {
5
+ validateElement(container)
6
+
7
+ const { duration, easingOption, direction } = options
8
+ const { scrollLeft, scrollTop, clientWidth, clientHeight, scrollWidth, scrollHeight } =
9
+ resolveScrollValues(container)
10
+
11
+ /**
12
+ * Scroll the Element or Window to the given
13
+ * position.
14
+ *
15
+ * @param {number} from the current scroll position.
16
+ * @param {number} to the new position to scroll.
17
+ * @param {string} layout the scroll direction.
18
+ */
19
+ function scrollTo(from: number, to: number, layout: 'horizontal' | 'vertical') {
20
+ let startTime: number | null = null
21
+ let requestId = 0
22
+
23
+ function loop(currentTime: number) {
24
+ if (!startTime) startTime = currentTime
25
+
26
+ const time = Math.min((currentTime - startTime) / duration, 1)
27
+ const easingFunc = getEasing(easingOption)
28
+ const currentPositionInTime = easingFunc(time) * (to - from) + from
29
+
30
+ container.scrollTo({
31
+ left: layout === 'horizontal' ? currentPositionInTime : undefined,
32
+ top: layout === 'vertical' ? currentPositionInTime : undefined,
33
+ })
34
+
35
+ if (time < 1) {
36
+ requestId = requestAnimationFrame(loop)
37
+ } else {
38
+ cancelAnimationFrame(requestId)
39
+ }
40
+ }
41
+ requestId = requestAnimationFrame(loop)
42
+ }
43
+
44
+ function scrollToCenter() {
45
+ let from = 0
46
+ let to = 0
47
+
48
+ if (direction === 'vertical') {
49
+ const scrollYCenterPosition = (scrollHeight - clientHeight) / 2
50
+ from = scrollTop
51
+ to = scrollYCenterPosition
52
+ }
53
+
54
+ if (direction === 'horizontal') {
55
+ const scrollXCenterPosition = (scrollWidth - clientWidth) / 2
56
+ from = scrollLeft
57
+ to = scrollXCenterPosition
58
+ }
59
+ scrollTo(from, to, direction)
60
+ }
61
+
62
+ function scrollToTarget(target: HTMLElement) {
63
+ validateElement(target)
64
+ const { offsetTop, offsetLeft } = target
65
+ let from = 0
66
+ let to = 0
67
+
68
+ if (direction === 'vertical') {
69
+ from = scrollTop
70
+ to = offsetTop
71
+ }
72
+
73
+ if (direction === 'horizontal') {
74
+ from = scrollLeft
75
+ to = offsetLeft
76
+ }
77
+
78
+ scrollTo(from, to, direction)
79
+ }
80
+
81
+ const scrollToLeft = (offset?: number) => {
82
+ const scrollPosition = offset || scrollLeft - clientWidth
83
+ scrollTo(scrollLeft, scrollPosition, 'horizontal')
84
+ }
85
+
86
+ const scrollToRight = (offset?: number) => {
87
+ const scrollPosition = offset || scrollLeft + clientWidth
88
+ scrollTo(scrollLeft, scrollPosition, 'horizontal')
89
+ }
90
+
91
+ const scrollToTop = (offset?: number) => {
92
+ const scrollPosition = offset || scrollTop - clientHeight
93
+ scrollTo(scrollTop, scrollPosition, 'vertical')
94
+ }
95
+
96
+ const scrollToBottom = (offset?: number) => {
97
+ const scrollPosition = offset || scrollTop + clientHeight
98
+ scrollTo(scrollTop, scrollPosition, 'vertical')
99
+ }
100
+
101
+ return {
102
+ scrollToLeft,
103
+ scrollToRight,
104
+ scrollToTop,
105
+ scrollToBottom,
106
+ scrollToTarget,
107
+ scrollToCenter,
108
+ }
109
+ }
package/src/types.ts ADDED
@@ -0,0 +1,21 @@
1
+ export type ScrollOptions = {
2
+ direction: 'horizontal' | 'vertical'
3
+ duration: number
4
+ easingOption: EasingOptions
5
+ }
6
+
7
+ export type ScrollerProps = {
8
+ container: HTMLElement | Window
9
+ options: ScrollOptions
10
+ }
11
+
12
+ export type EasingOptions = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'
13
+
14
+ export type EasingFunction = (t: number) => number
15
+
16
+ export interface EasingFunctions {
17
+ linear: EasingFunction
18
+ 'ease-in': EasingFunction
19
+ 'ease-out': EasingFunction
20
+ 'ease-in-out': EasingFunction
21
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { RefObject } from 'react'
2
+ import { EasingFunction, EasingFunctions, EasingOptions, ScrollOptions } from './types'
3
+
4
+ export const isServer = typeof window === 'undefined' || typeof document === 'undefined'
5
+
6
+ export const defaultScrollOptions: ScrollOptions = {
7
+ direction: 'vertical',
8
+ duration: 300,
9
+ easingOption: 'ease-in-out',
10
+ }
11
+
12
+ /**
13
+ * Resolves scroll values based on the type of param.
14
+ * @param {HTMLElement | Window} element
15
+ */
16
+ export const resolveScrollValues = (element: HTMLElement | Window) => {
17
+ if (
18
+ element === document.body ||
19
+ element === document.documentElement ||
20
+ element instanceof Window
21
+ ) {
22
+ return {
23
+ scrollLeft: document.body.scrollLeft || document.documentElement.scrollLeft,
24
+ scrollTop: document.body.scrollTop || document.documentElement.scrollTop,
25
+ scrollWidth: document.body.scrollWidth || document.documentElement.scrollWidth,
26
+ clientWidth: document.body.clientWidth || document.documentElement.clientWidth,
27
+ clientHeight: document.body.clientHeight || document.documentElement.clientHeight,
28
+ scrollHeight: document.body.scrollHeight || document.documentElement.scrollHeight,
29
+ }
30
+ }
31
+ return {
32
+ scrollLeft: element.scrollLeft,
33
+ scrollTop: element.scrollTop,
34
+ scrollWidth: element.scrollWidth,
35
+ clientWidth: element.clientWidth,
36
+ clientHeight: element.clientHeight,
37
+ scrollHeight: element.scrollHeight,
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Validate if element passed to the function is valid.
43
+ * @param {HTMLElement | Window} element
44
+ */
45
+ export function validateElement(element?: HTMLElement | Window) {
46
+ if (element === undefined) {
47
+ throw new Error(`The element passed to scroller() was undefined`)
48
+ }
49
+ if (!(element instanceof HTMLElement || Window)) {
50
+ throw new TypeError(
51
+ `
52
+ The element passed to scroller() must be a valid element.
53
+ You passed ${element}.
54
+ `
55
+ )
56
+ }
57
+ }
58
+
59
+ export const getElement = (ref: RefObject<HTMLElement>) => {
60
+ if (!ref.current) {
61
+ throw new Error(`Could not resolve ref object: ${ref.current}`)
62
+ }
63
+ return ref.current
64
+ }
65
+
66
+ export const easingMap: EasingFunctions = {
67
+ linear(t: number) {
68
+ return t
69
+ },
70
+ 'ease-in'(t: number) {
71
+ return t * t
72
+ },
73
+ 'ease-out'(t: number) {
74
+ return t * (2 - t)
75
+ },
76
+ 'ease-in-out'(t: number) {
77
+ return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
78
+ },
79
+ }
80
+
81
+ export const getEasing = (easing?: EasingOptions): EasingFunction => {
82
+ const easeFunc = easingMap[easing || defaultScrollOptions.easingOption]
83
+ if (!easeFunc) {
84
+ const options = Object.keys(easingMap).join(',')
85
+ throw new Error(
86
+ `Scroll error: scroller does not support an easing option of "${easing}". Supported options are ${options}`
87
+ )
88
+ }
89
+ return easeFunc
90
+ }