@graphcommerce/framer-scroller 6.2.0-canary.62 → 6.2.0-canary.64
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/CHANGELOG.md +8 -0
- package/components/ScrollerProvider.tsx +29 -8
- package/hooks/useScrollTo.ts +21 -7
- package/hooks/useScroller.ts +1 -15
- package/package.json +6 -6
- package/types.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 6.2.0-canary.64
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1998](https://github.com/graphcommerce-org/graphcommerce/pull/1998) [`a788baaeb`](https://github.com/graphcommerce-org/graphcommerce/commit/a788baaeb9646c5a30b55998697d38a6093b6c44) - Added option to useScrollTo to allow/disallow stopping the scroll animation on user scroll interaction ([@bramvanderholst](https://github.com/bramvanderholst))
|
|
8
|
+
|
|
9
|
+
## 6.2.0-canary.63
|
|
10
|
+
|
|
3
11
|
## 6.2.0-canary.62
|
|
4
12
|
|
|
5
13
|
## 6.2.0-canary.61
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useConstant, useElementScroll } from '@graphcommerce/framer-utils'
|
|
2
|
-
import { MotionValue, motionValue, Point, useMotionValue } from 'framer-motion'
|
|
2
|
+
import { MotionValue, motionValue, Point, useDomEvent, useMotionValue } from 'framer-motion'
|
|
3
3
|
import { PlaybackControls } from 'popmotion'
|
|
4
4
|
import React, { useEffect, useRef, useMemo, useCallback } from 'react'
|
|
5
5
|
import { scrollerContext } from '../context/scrollerContext'
|
|
@@ -85,6 +85,7 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
85
85
|
)
|
|
86
86
|
|
|
87
87
|
const snap = useMotionValue(_inititalSnap)
|
|
88
|
+
const stopOnInteraction = useMotionValue(_inititalSnap)
|
|
88
89
|
|
|
89
90
|
// Monitor the visbility of all elements and store them for later use.
|
|
90
91
|
const items = useMotionValue<ItemState[]>([])
|
|
@@ -104,16 +105,23 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
104
105
|
[stop],
|
|
105
106
|
)
|
|
106
107
|
|
|
107
|
-
const disableSnap = useCallback(
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
const disableSnap = useCallback(
|
|
109
|
+
(stopAnimationOnScroll = true) => {
|
|
110
|
+
stopOnInteraction.set(stopAnimationOnScroll)
|
|
111
|
+
if (snap.get() === false) stop()
|
|
112
|
+
if (scrollerRef.current) scrollerRef.current.style.scrollSnapType = 'none'
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
scroll.animating.set(true)
|
|
115
|
+
snap.set(false)
|
|
116
|
+
},
|
|
117
|
+
[stopOnInteraction, snap, stop, scroll.animating],
|
|
118
|
+
)
|
|
114
119
|
|
|
115
120
|
const enableSnap = useCallback(() => {
|
|
116
121
|
if (snap.get() === true) return
|
|
122
|
+
|
|
123
|
+
stop()
|
|
124
|
+
|
|
117
125
|
const scroller = scrollerRef.current
|
|
118
126
|
if (scroller) scroller.style.scrollSnapType = ''
|
|
119
127
|
|
|
@@ -132,7 +140,20 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
132
140
|
})
|
|
133
141
|
})
|
|
134
142
|
}
|
|
135
|
-
}, [snap, scroll])
|
|
143
|
+
}, [snap, scroll.animating, stop])
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* If the scroller is animating and the user now uses their scrollwheel, enable the snapping
|
|
147
|
+
* behavior.
|
|
148
|
+
*
|
|
149
|
+
* If the scroller doesn't have snap enabled and isPanning===false, enable snap
|
|
150
|
+
*/
|
|
151
|
+
useDomEvent(scrollerRef as React.RefObject<EventTarget>, 'wheel', () => {
|
|
152
|
+
if (stopOnInteraction.get()) enableSnap()
|
|
153
|
+
})
|
|
154
|
+
useDomEvent(scrollerRef as React.RefObject<EventTarget>, 'touchmove', () => {
|
|
155
|
+
if (stopOnInteraction.get()) stop()
|
|
156
|
+
})
|
|
136
157
|
|
|
137
158
|
useObserveItems(scrollerRef, items)
|
|
138
159
|
|
package/hooks/useScrollTo.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { useCallback, useContext } from 'react'
|
|
|
4
4
|
import { distanceAnimationDuration } from '../utils/distanceAnimationDuration'
|
|
5
5
|
import { useScrollerContext } from './useScrollerContext'
|
|
6
6
|
|
|
7
|
+
type Options = {
|
|
8
|
+
stopAnimationOnScroll?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
export function useScrollTo() {
|
|
8
12
|
const { scrollerRef, register, disableSnap, enableSnap, scroll, getScrollSnapPositions } =
|
|
9
13
|
useScrollerContext()
|
|
@@ -11,8 +15,13 @@ export function useScrollTo() {
|
|
|
11
15
|
const duration = (useContext(MotionConfigContext).transition as Tween | undefined)?.duration ?? 0
|
|
12
16
|
|
|
13
17
|
const scrollTo = useCallback(
|
|
14
|
-
async (
|
|
18
|
+
async (
|
|
19
|
+
incoming: Point | [number, number],
|
|
20
|
+
options: Options = { stopAnimationOnScroll: true },
|
|
21
|
+
__retrigger = 0,
|
|
22
|
+
) => {
|
|
15
23
|
const ref = scrollerRef.current
|
|
24
|
+
const { stopAnimationOnScroll } = options
|
|
16
25
|
if (!ref) return
|
|
17
26
|
|
|
18
27
|
let to: Point
|
|
@@ -24,20 +33,20 @@ export function useScrollTo() {
|
|
|
24
33
|
to = incoming
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
if (process.env.NODE_ENV === 'development' && scroll.animating.get() &&
|
|
36
|
+
if (process.env.NODE_ENV === 'development' && scroll.animating.get() && __retrigger === 0) {
|
|
28
37
|
console.warn(
|
|
29
38
|
`scrollTo triggered while another animation is in progress. This cancels the current animation and creates a new one.`,
|
|
30
39
|
)
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
if (process.env.NODE_ENV === 'development' &&
|
|
42
|
+
if (process.env.NODE_ENV === 'development' && __retrigger > 5) {
|
|
34
43
|
console.error(
|
|
35
44
|
`scrollTo triggered more than 5 times, is the element resizing constantly? Bailing out.`,
|
|
36
45
|
)
|
|
37
46
|
return
|
|
38
47
|
}
|
|
39
48
|
|
|
40
|
-
if (process.env.NODE_ENV === 'development' &&
|
|
49
|
+
if (process.env.NODE_ENV === 'development' && __retrigger > 0) {
|
|
41
50
|
console.warn(
|
|
42
51
|
`scrollTo re-animating to because the final location changed during animation.`,
|
|
43
52
|
)
|
|
@@ -47,7 +56,8 @@ export function useScrollTo() {
|
|
|
47
56
|
|
|
48
57
|
const xDone = new Promise<void>((onComplete) => {
|
|
49
58
|
if (ref.scrollLeft !== to.x) {
|
|
50
|
-
disableSnap()
|
|
59
|
+
disableSnap(stopAnimationOnScroll)
|
|
60
|
+
if (!stopAnimationOnScroll) ref.style.overflow = 'hidden'
|
|
51
61
|
|
|
52
62
|
stop.push(
|
|
53
63
|
animate({
|
|
@@ -68,7 +78,9 @@ export function useScrollTo() {
|
|
|
68
78
|
|
|
69
79
|
const yDone = new Promise<void>((onComplete) => {
|
|
70
80
|
if (ref.scrollTop !== to.y) {
|
|
71
|
-
disableSnap()
|
|
81
|
+
disableSnap(stopAnimationOnScroll)
|
|
82
|
+
if (!stopAnimationOnScroll) ref.style.overflow = 'hidden'
|
|
83
|
+
|
|
72
84
|
stop.push(
|
|
73
85
|
animate({
|
|
74
86
|
from: ref.scrollTop,
|
|
@@ -92,10 +104,12 @@ export function useScrollTo() {
|
|
|
92
104
|
register({ stop: () => stop.forEach((s) => s.stop()) })
|
|
93
105
|
await Promise.all([xDone, yDone])
|
|
94
106
|
|
|
107
|
+
if (!stopAnimationOnScroll) ref.style.removeProperty('overflow')
|
|
108
|
+
|
|
95
109
|
if (Array.isArray(incoming)) {
|
|
96
110
|
const checkPositions = getScrollSnapPositions()
|
|
97
111
|
if (checkPositions.x[incoming[0]] !== to.x || checkPositions.y[incoming[1]] !== to.y)
|
|
98
|
-
await scrollTo(incoming,
|
|
112
|
+
await scrollTo(incoming, options, __retrigger + 1)
|
|
99
113
|
}
|
|
100
114
|
enableSnap()
|
|
101
115
|
},
|
package/hooks/useScroller.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
MotionValue,
|
|
8
8
|
PanHandlers,
|
|
9
9
|
PanInfo,
|
|
10
|
-
useDomEvent,
|
|
11
10
|
useTransform,
|
|
12
11
|
} from 'framer-motion'
|
|
13
12
|
import React, { ReactHTML, useEffect, useState } from 'react'
|
|
@@ -47,7 +46,7 @@ export function useScroller<
|
|
|
47
46
|
>(props: ScrollableProps<TagName>, forwardedRef: React.ForwardedRef<E>) {
|
|
48
47
|
const { hideScrollbar = false, children, grid = false, ...divProps } = props
|
|
49
48
|
|
|
50
|
-
const { scrollSnap, scrollerRef,
|
|
49
|
+
const { scrollSnap, scrollerRef, disableSnap, snap, registerChildren, scroll } =
|
|
51
50
|
useScrollerContext()
|
|
52
51
|
|
|
53
52
|
useEffect(() => {
|
|
@@ -68,19 +67,6 @@ export function useScroller<
|
|
|
68
67
|
|
|
69
68
|
const [isPanning, setPanning] = useState(false)
|
|
70
69
|
|
|
71
|
-
/** If the scroller doesn't have snap enabled and the user is not panning, enable snap */
|
|
72
|
-
useDomEvent(scrollerRef as React.RefObject<EventTarget>, 'wheel', (e) => {
|
|
73
|
-
/**
|
|
74
|
-
* Todo: this is actually incorrect because when enabling the snap points, the area jumps to the
|
|
75
|
-
* nearest point a snap.
|
|
76
|
-
*
|
|
77
|
-
* What we SHOULD do is wait for the scroll position to be set exactly on a snappoint and then
|
|
78
|
-
* enable it. However, to do that then we need to know the position of all elements at all time,
|
|
79
|
-
* we now are lazy :)
|
|
80
|
-
*/
|
|
81
|
-
if (!snap.get() && !isPanning && e instanceof WheelEvent) enableSnap()
|
|
82
|
-
})
|
|
83
|
-
|
|
84
70
|
const scrollStart = useConstant(() => ({ x: motionValue(0), y: motionValue(0) }))
|
|
85
71
|
const onPanStart: PanHandlers['onPanStart'] = (event) => {
|
|
86
72
|
// If we're not dealing with the mouse we don't need to do anything
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/framer-scroller",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "6.2.0-canary.
|
|
5
|
+
"version": "6.2.0-canary.64",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "tsc -W"
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"popmotion": "11.0.5",
|
|
19
|
-
"@graphcommerce/framer-utils": "6.2.0-canary.
|
|
20
|
-
"@graphcommerce/image": "6.2.0-canary.
|
|
19
|
+
"@graphcommerce/framer-utils": "6.2.0-canary.64",
|
|
20
|
+
"@graphcommerce/image": "6.2.0-canary.64"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@graphcommerce/eslint-config-pwa": "6.2.0-canary.
|
|
24
|
-
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.
|
|
25
|
-
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.
|
|
23
|
+
"@graphcommerce/eslint-config-pwa": "6.2.0-canary.64",
|
|
24
|
+
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.64",
|
|
25
|
+
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.64"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@mui/material": "^5.10.16",
|
package/types.ts
CHANGED
|
@@ -32,7 +32,7 @@ export type ScrollerContext = {
|
|
|
32
32
|
/** @private */
|
|
33
33
|
enableSnap: () => void
|
|
34
34
|
/** @private */
|
|
35
|
-
disableSnap: () => void
|
|
35
|
+
disableSnap: (stopAnimationOnScroll?: boolean) => void
|
|
36
36
|
/** @private */
|
|
37
37
|
register: (controls: PlaybackControls) => void
|
|
38
38
|
/** @private */
|