@graphcommerce/framer-scroller 2.1.39 → 2.1.41
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 +24 -0
- package/components/ScrollerProvider.tsx +29 -11
- package/hooks/useScrollTo.ts +4 -13
- package/hooks/useScroller.ts +3 -0
- package/hooks/useVelocitySnapTo.ts +11 -6
- package/package.json +4 -4
- package/utils/distanceAnimationDuration.ts +8 -0
- package/utils/isHTMLMousePointerEvent.ts +1 -1
- package/utils/scrollSnapTypeDirection.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2.1.41
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`81f31d1e5`](https://github.com/graphcommerce-org/graphcommerce/commit/81f31d1e54397368088a4289aaddd29facfceeef) Thanks [@paales](https://github.com/paales)! - Make sure we can only drag in the current Scroller to prevent overscroll issues
|
|
8
|
+
|
|
9
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`a8905d263`](https://github.com/graphcommerce-org/graphcommerce/commit/a8905d263273cb9322583d5759a5fdc66eceb8e4) Thanks [@paales](https://github.com/paales)! - The duration of the animation should be based on the distance the animation has to go
|
|
10
|
+
|
|
11
|
+
- [#1675](https://github.com/graphcommerce-org/graphcommerce/pull/1675) [`1b1504c9b`](https://github.com/graphcommerce-org/graphcommerce/commit/1b1504c9b0e51f2787bce91e1ff1940f540411d6) Thanks [@paales](https://github.com/paales)! - Added crosssel functionality
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`9e630670f`](https://github.com/graphcommerce-org/graphcommerce/commit/9e630670ff6c952ab7b938d890b5509804985cf3), [`2e9fa5984`](https://github.com/graphcommerce-org/graphcommerce/commit/2e9fa5984a07ff14fc1b3a4f62189a26e8e3ecdd), [`adf13069a`](https://github.com/graphcommerce-org/graphcommerce/commit/adf13069af6460c960276b402237371c12fc6dec), [`1b1504c9b`](https://github.com/graphcommerce-org/graphcommerce/commit/1b1504c9b0e51f2787bce91e1ff1940f540411d6), [`6c2e27b1b`](https://github.com/graphcommerce-org/graphcommerce/commit/6c2e27b1be4aaa888e65a2bd69eaeb467a54a023)]:
|
|
14
|
+
- @graphcommerce/next-ui@4.28.1
|
|
15
|
+
- @graphcommerce/framer-utils@3.2.1
|
|
16
|
+
- @graphcommerce/image@3.1.10
|
|
17
|
+
|
|
18
|
+
## 2.1.40
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a) Thanks [@paales](https://github.com/paales)! - Move to useMatchMedia to have a simple boolean utility that allows to match to a certain breakpoint
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [[`0c21c5c23`](https://github.com/graphcommerce-org/graphcommerce/commit/0c21c5c233ebab15f6629c234e3de1cc8c0452e1), [`de8925aa9`](https://github.com/graphcommerce-org/graphcommerce/commit/de8925aa910b191c62041530c68c697a58a1e52d), [`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a)]:
|
|
25
|
+
- @graphcommerce/next-ui@4.28.0
|
|
26
|
+
|
|
3
27
|
## 2.1.39
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
|
@@ -21,6 +21,9 @@ export type ScrollerProviderProps = {
|
|
|
21
21
|
scrollSnapTypeMd?: ScrollSnapType
|
|
22
22
|
scrollSnapAlign?: ScrollSnapAlign
|
|
23
23
|
scrollSnapStop?: ScrollSnapStop
|
|
24
|
+
|
|
25
|
+
/** @private */
|
|
26
|
+
_inititalSnap?: boolean
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
function useObserveItems(scrollerRef: ReactHtmlRefObject, items: MotionValue<ItemState[]>) {
|
|
@@ -71,6 +74,7 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
71
74
|
scrollSnapStop = 'normal',
|
|
72
75
|
scrollSnapTypeSm = 'inline mandatory',
|
|
73
76
|
scrollSnapTypeMd = 'inline mandatory',
|
|
77
|
+
_inititalSnap = true,
|
|
74
78
|
...providerProps
|
|
75
79
|
} = props
|
|
76
80
|
|
|
@@ -79,7 +83,7 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
79
83
|
[scrollSnapAlign, scrollSnapStop, scrollSnapTypeMd, scrollSnapTypeSm],
|
|
80
84
|
)
|
|
81
85
|
|
|
82
|
-
const snap = useMotionValue(
|
|
86
|
+
const snap = useMotionValue(_inititalSnap)
|
|
83
87
|
|
|
84
88
|
// Monitor the visbility of all elements and store them for later use.
|
|
85
89
|
const items = useMotionValue<ItemState[]>([])
|
|
@@ -190,7 +194,9 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
190
194
|
continue
|
|
191
195
|
}
|
|
192
196
|
|
|
193
|
-
const align = getComputedStyle(child)
|
|
197
|
+
const { scrollSnapAlign: align } = getComputedStyle(child)
|
|
198
|
+
|
|
199
|
+
// console.trace(scrollPaddingTop, scrollPaddingRight, scrollPaddingBottom, scrollPaddingLeft)
|
|
194
200
|
let [childAlignY, childAlignX] = align.split(' ') as [
|
|
195
201
|
ScrollSnapAlignAxis,
|
|
196
202
|
ScrollSnapAlignAxis | undefined,
|
|
@@ -266,20 +272,32 @@ export function ScrollerProvider(props: ScrollerProviderProps) {
|
|
|
266
272
|
}
|
|
267
273
|
|
|
268
274
|
const clamp = (min: number, max: number) => (value: number) =>
|
|
269
|
-
Math.max(min, Math.min(max, value))
|
|
275
|
+
Math.round(Math.max(min, Math.min(max, value)))
|
|
270
276
|
|
|
271
277
|
return {
|
|
272
278
|
x: [
|
|
273
|
-
...
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
279
|
+
...new Set(
|
|
280
|
+
[
|
|
281
|
+
...snapPositions.x.start.map((v) => v - scrollPadding.x.before),
|
|
282
|
+
...snapPositions.x.center.map((v) => v - rect.width / 2),
|
|
283
|
+
...snapPositions.x.end.map((v) => v - rect.width + scrollPadding.x.after),
|
|
284
|
+
]
|
|
285
|
+
.map(clamp(0, maxScroll.x))
|
|
286
|
+
.sort((a, b) => a - b),
|
|
287
|
+
),
|
|
288
|
+
],
|
|
277
289
|
|
|
278
290
|
y: [
|
|
279
|
-
...
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
291
|
+
...new Set(
|
|
292
|
+
[
|
|
293
|
+
...snapPositions.y.start.map((v) => v - scrollPadding.y.before),
|
|
294
|
+
...snapPositions.y.center.map((v) => v - rect.height / 2),
|
|
295
|
+
...snapPositions.y.end.map((v) => v - rect.height + scrollPadding.y.after),
|
|
296
|
+
]
|
|
297
|
+
.map(clamp(0, maxScroll.y))
|
|
298
|
+
.sort((a, b) => a - b),
|
|
299
|
+
),
|
|
300
|
+
],
|
|
283
301
|
}
|
|
284
302
|
}
|
|
285
303
|
|
package/hooks/useScrollTo.ts
CHANGED
|
@@ -2,29 +2,20 @@ import { useElementScroll } from '@graphcommerce/framer-utils'
|
|
|
2
2
|
import { MotionConfigContext, Point, Tween } from 'framer-motion'
|
|
3
3
|
import { animate } from 'popmotion'
|
|
4
4
|
import { useCallback, useContext } from 'react'
|
|
5
|
+
import { distanceAnimationDuration } from '../utils/distanceAnimationDuration'
|
|
5
6
|
import { useScrollerContext } from './useScrollerContext'
|
|
6
7
|
|
|
7
8
|
export function useScrollTo() {
|
|
8
9
|
const { scrollerRef, register, disableSnap, enableSnap } = useScrollerContext()
|
|
9
10
|
const scroll = useElementScroll(scrollerRef)
|
|
10
11
|
|
|
11
|
-
const duration =
|
|
12
|
-
((useContext(MotionConfigContext).transition as Tween | undefined)?.duration ?? 0.375) * 1000
|
|
12
|
+
const duration = (useContext(MotionConfigContext).transition as Tween | undefined)?.duration ?? 0
|
|
13
13
|
|
|
14
14
|
const scrollTo = useCallback(
|
|
15
15
|
async (to: Point) => {
|
|
16
16
|
const ref = scrollerRef.current
|
|
17
17
|
if (!ref) return
|
|
18
18
|
|
|
19
|
-
// In the future we want to move to browser native scrolling behavior, but since the animation timing isn't configurable we can't use it.
|
|
20
|
-
// if ('scrollBehavior' in document.documentElement.style) {
|
|
21
|
-
// scrollerRef.current.scrollTo({ left: to.x, top: to.y, behavior: 'smooth' })
|
|
22
|
-
// await new Promise((onComplete) => {
|
|
23
|
-
// setTimeout(onComplete, 2000)
|
|
24
|
-
// })
|
|
25
|
-
// return
|
|
26
|
-
// }
|
|
27
|
-
|
|
28
19
|
// @ts-expect-error private api, but we're updating the animation value here manually instead of relying on the event listener.
|
|
29
20
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
30
21
|
scroll.scroll.start(() => () => {})
|
|
@@ -43,7 +34,7 @@ export function useScrollTo() {
|
|
|
43
34
|
},
|
|
44
35
|
onComplete,
|
|
45
36
|
onStop: onComplete,
|
|
46
|
-
duration,
|
|
37
|
+
duration: duration * 1000 || distanceAnimationDuration(ref.scrollLeft, to.x),
|
|
47
38
|
}),
|
|
48
39
|
)
|
|
49
40
|
} else onComplete()
|
|
@@ -63,7 +54,7 @@ export function useScrollTo() {
|
|
|
63
54
|
},
|
|
64
55
|
onComplete,
|
|
65
56
|
onStop: onComplete,
|
|
66
|
-
duration,
|
|
57
|
+
duration: duration * 1000 || distanceAnimationDuration(ref.scrollTop, to.y),
|
|
67
58
|
}),
|
|
68
59
|
)
|
|
69
60
|
} else {
|
package/hooks/useScroller.ts
CHANGED
|
@@ -83,6 +83,7 @@ export function useScroller<
|
|
|
83
83
|
const onPanStart: PanHandlers['onPanStart'] = (event) => {
|
|
84
84
|
// If we're not dealing with the mouse we don't need to do anything
|
|
85
85
|
if (!isHTMLMousePointerEvent(event)) return
|
|
86
|
+
if (event.target.closest('.Scroller-root') !== scrollerRef.current) return
|
|
86
87
|
|
|
87
88
|
scrollStart.x.set(scroll.x.get())
|
|
88
89
|
scrollStart.y.set(scroll.y.get())
|
|
@@ -95,6 +96,7 @@ export function useScroller<
|
|
|
95
96
|
|
|
96
97
|
// If we're not dealing with the mouse we don't need to do anything
|
|
97
98
|
if (!isHTMLMousePointerEvent(event)) return
|
|
99
|
+
if (event.target.closest('.Scroller-root') !== scrollerRef.current) return
|
|
98
100
|
|
|
99
101
|
scrollerRef.current.scrollLeft = scrollStart.x.get() - info.offset.x
|
|
100
102
|
scrollerRef.current.scrollTop = scrollStart.y.get() - info.offset.y
|
|
@@ -103,6 +105,7 @@ export function useScroller<
|
|
|
103
105
|
const onPanEnd: PanHandlers['onPanEnd'] = (event, info) => {
|
|
104
106
|
// If we're not dealing with the mouse we don't need to do anything
|
|
105
107
|
if (!isHTMLMousePointerEvent(event)) return
|
|
108
|
+
if (event.target.closest('.Scroller-root') !== scrollerRef.current) return
|
|
106
109
|
|
|
107
110
|
setPanning(false)
|
|
108
111
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMatchMedia } from '@graphcommerce/next-ui'
|
|
2
|
+
import { useTheme } from '@mui/material'
|
|
2
3
|
import { PanInfo } from 'framer-motion'
|
|
3
4
|
import { inertia, InertiaOptions } from 'popmotion'
|
|
4
5
|
import { scrollSnapTypeDirection } from '../utils/scrollSnapTypeDirection'
|
|
@@ -21,9 +22,13 @@ export const useVelocitySnapTo = (
|
|
|
21
22
|
) => {
|
|
22
23
|
const { disableSnap, enableSnap, register, getScrollSnapPositions, scrollSnap } =
|
|
23
24
|
useScrollerContext()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
|
|
26
|
+
const matchMedia = useMatchMedia()
|
|
27
|
+
|
|
28
|
+
const direction = () =>
|
|
29
|
+
matchMedia.down('md')
|
|
30
|
+
? scrollSnapTypeDirection(scrollSnap.scrollSnapTypeSm)
|
|
31
|
+
: scrollSnapTypeDirection(scrollSnap.scrollSnapTypeMd)
|
|
27
32
|
|
|
28
33
|
const inertiaOptions: InertiaOptions = {
|
|
29
34
|
power: 1,
|
|
@@ -43,7 +48,7 @@ export const useVelocitySnapTo = (
|
|
|
43
48
|
const xDone = new Promise<void>((onComplete) => {
|
|
44
49
|
const targetX = clamp(info, 'x') * -1 + scrollLeft
|
|
45
50
|
const closestX =
|
|
46
|
-
direction !== 'block' ? closest(getScrollSnapPositions().x, targetX) : undefined
|
|
51
|
+
direction() !== 'block' ? closest(getScrollSnapPositions().x, targetX) : undefined
|
|
47
52
|
|
|
48
53
|
if ((closestX ?? 0) !== scrollLeft) {
|
|
49
54
|
disableSnap()
|
|
@@ -67,7 +72,7 @@ export const useVelocitySnapTo = (
|
|
|
67
72
|
const yDone = new Promise<void>((onComplete) => {
|
|
68
73
|
const targetY = clamp(info, 'y') * -1 + scrollTop
|
|
69
74
|
const closestY =
|
|
70
|
-
direction !== 'inline' ? closest(getScrollSnapPositions().y, targetY) : undefined
|
|
75
|
+
direction() !== 'inline' ? closest(getScrollSnapPositions().y, targetY) : undefined
|
|
71
76
|
|
|
72
77
|
if ((closestY ?? 0) !== scrollTop) {
|
|
73
78
|
disableSnap()
|
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": "2.1.
|
|
5
|
+
"version": "2.1.41",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "tsc -W"
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@graphcommerce/framer-utils": "3.2.
|
|
19
|
-
"@graphcommerce/image": "3.1.
|
|
20
|
-
"@graphcommerce/next-ui": "4.
|
|
18
|
+
"@graphcommerce/framer-utils": "3.2.1",
|
|
19
|
+
"@graphcommerce/image": "3.1.10",
|
|
20
|
+
"@graphcommerce/next-ui": "4.28.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@graphcommerce/eslint-config-pwa": "^4.1.10",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Get the duration in ms of the animation depending upon the distance provided in pixels */
|
|
2
|
+
export function distanceAnimationDuration(fromPx: number, toPx: number): number {
|
|
3
|
+
const height = Math.abs(fromPx - toPx)
|
|
4
|
+
if (!height) return 0
|
|
5
|
+
|
|
6
|
+
const constant = Math.abs(fromPx - toPx) / 36
|
|
7
|
+
return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10)
|
|
8
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ScrollSnapType } from '../types'
|
|
2
2
|
|
|
3
|
-
export type SnapTypeDirection = 'block' | 'inline' | 'both'
|
|
3
|
+
export type SnapTypeDirection = 'block' | 'inline' | 'both'
|
|
4
4
|
export function scrollSnapTypeDirection(scrollSnapType: ScrollSnapType): SnapTypeDirection {
|
|
5
5
|
let snapDir = scrollSnapType.split(' ')[0]
|
|
6
6
|
snapDir = snapDir.replace('y', 'block')
|