@graphcommerce/next-ui 6.0.0-canary.32 → 6.0.0-canary.34
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 +26 -0
- package/Layout/components/LayoutHeaderContent.tsx +7 -7
- package/LayoutParts/MenuFabSecondaryItem.tsx +2 -0
- package/Navigation/components/NavigationOverlay.tsx +5 -2
- package/Overlay/components/OverlayBase.tsx +188 -84
- package/Overlay/utils/variantsToScrollSnapType.ts +2 -2
- package/OverlayOrPopperChip/types.ts +1 -2
- package/Styles/withEmotionCache.tsx +1 -1
- package/package.json +8 -8
- package/Overlay/hooks/useOverlayPosition.ts +0 -87
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 6.0.0-canary.34
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1830](https://github.com/graphcommerce-org/graphcommerce/pull/1830) [`dbc2ae0d3`](https://github.com/graphcommerce-org/graphcommerce/commit/dbc2ae0d360f645c3eab2a8f38b3d1431eab7d80) - Hide body scrollbar only for right overlays ([@paales](https://github.com/paales))
|
|
8
|
+
|
|
9
|
+
- [#1830](https://github.com/graphcommerce-org/graphcommerce/pull/1830) [`fafa76ba9`](https://github.com/graphcommerce-org/graphcommerce/commit/fafa76ba9e655739171abc553d309795c9d8e5c2) - Overlays now use an additional scroll container to handle vertical scroll, fixing:
|
|
10
|
+
|
|
11
|
+
- Scrolling on desktop will not close the overlay when there is content to be scrolled
|
|
12
|
+
- Scrolling will not snap to bottom / top when the content is barely scrollable
|
|
13
|
+
- Dragging will only open or close the drawer, not something inbetween
|
|
14
|
+
- Swiping up on mobile will not close the overlay, first you need to scroll to the top of the overlay.
|
|
15
|
+
- Floating overlays will now scroll inside the floating overlay. ([@paales](https://github.com/paales))
|
|
16
|
+
|
|
17
|
+
- [#1830](https://github.com/graphcommerce-org/graphcommerce/pull/1830) [`0bac3bdd8`](https://github.com/graphcommerce-org/graphcommerce/commit/0bac3bdd8505ccad8036d13e19559f2b3523fd92) - Navigation layout animation should only trigger when the active menu item changes ([@paales](https://github.com/paales))
|
|
18
|
+
|
|
19
|
+
- [#1830](https://github.com/graphcommerce-org/graphcommerce/pull/1830) [`6bffd680b`](https://github.com/graphcommerce-org/graphcommerce/commit/6bffd680b9d6a370048d06842cd3ce73130471dd) - Overlay shouldn't be closed when dragging beyond the pane ([@paales](https://github.com/paales))
|
|
20
|
+
|
|
21
|
+
- [#1830](https://github.com/graphcommerce-org/graphcommerce/pull/1830) [`ff0c70e31`](https://github.com/graphcommerce-org/graphcommerce/commit/ff0c70e3165c64ea4f236a15a5820428dbf36e6a) - Allow scrollbar to left and right full overlay variants. ([@paales](https://github.com/paales))
|
|
22
|
+
|
|
23
|
+
## 6.0.0-canary.33
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- [#1831](https://github.com/graphcommerce-org/graphcommerce/pull/1831) [`f4008bae3`](https://github.com/graphcommerce-org/graphcommerce/commit/f4008bae3e3ac8288c731b1dd87e6c6aef8e81fc) - Added a linting rule that disallows `import { Theme } from '@emotion/react'` because that causes huge performance issues. Added tsc:trace to the root project to debug typescript performance issues. ([@paales](https://github.com/paales))
|
|
28
|
+
|
|
3
29
|
## 6.0.0-canary.32
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
@@ -18,7 +18,7 @@ export type LayoutHeaderContentProps = FloatingProps & {
|
|
|
18
18
|
sxBg?: SxProps<Theme>
|
|
19
19
|
layout?: LayoutProps['layout']
|
|
20
20
|
size?: 'small' | 'responsive'
|
|
21
|
-
}
|
|
21
|
+
} & Pick<LayoutProps, 'layout' | 'layoutDependency'>
|
|
22
22
|
|
|
23
23
|
type OwnerState = {
|
|
24
24
|
floatingSm: boolean
|
|
@@ -45,6 +45,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
|
|
|
45
45
|
sx = [],
|
|
46
46
|
sxBg = [],
|
|
47
47
|
layout,
|
|
48
|
+
layoutDependency,
|
|
48
49
|
size = 'responsive',
|
|
49
50
|
} = props
|
|
50
51
|
|
|
@@ -156,7 +157,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
|
|
|
156
157
|
justifyContent: 'start',
|
|
157
158
|
})}
|
|
158
159
|
>
|
|
159
|
-
<MotionDiv layout={layout} sx={{ display: 'grid' }}>
|
|
160
|
+
<MotionDiv layout={layout} layoutDependency={layoutDependency} sx={{ display: 'grid' }}>
|
|
160
161
|
{left}
|
|
161
162
|
</MotionDiv>
|
|
162
163
|
</Box>
|
|
@@ -192,10 +193,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
|
|
|
192
193
|
},
|
|
193
194
|
})}
|
|
194
195
|
>
|
|
195
|
-
<MotionDiv
|
|
196
|
-
sx={{ minWidth: 0 }}
|
|
197
|
-
layout={layout}
|
|
198
|
-
>
|
|
196
|
+
<MotionDiv sx={{ minWidth: 0 }} layout={layout} layoutDependency={layoutDependency}>
|
|
199
197
|
{children}
|
|
200
198
|
</MotionDiv>
|
|
201
199
|
</Box>
|
|
@@ -213,7 +211,9 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
|
|
|
213
211
|
justifyContent: 'end',
|
|
214
212
|
})}
|
|
215
213
|
>
|
|
216
|
-
<MotionDiv layout={layout}
|
|
214
|
+
<MotionDiv layout={layout} layoutDependency={layoutDependency}>
|
|
215
|
+
{right}
|
|
216
|
+
</MotionDiv>
|
|
217
217
|
</Box>
|
|
218
218
|
{divider && (
|
|
219
219
|
<Box
|
|
@@ -2,6 +2,7 @@ import { ListItemButton, ListItemIcon, ListItemText, SxProps, Theme } from '@mui
|
|
|
2
2
|
import { useRouter } from 'next/router'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { extendableComponent } from '../Styles'
|
|
5
|
+
import { NextLink } from '../Theme'
|
|
5
6
|
|
|
6
7
|
export type FabMenuSecondaryItemProps = {
|
|
7
8
|
href: string
|
|
@@ -21,6 +22,7 @@ export function MenuFabSecondaryItem(props: FabMenuSecondaryItemProps) {
|
|
|
21
22
|
return (
|
|
22
23
|
<ListItemButton
|
|
23
24
|
href={href}
|
|
25
|
+
component={NextLink}
|
|
24
26
|
className={classes.root}
|
|
25
27
|
sx={[{}, ...(Array.isArray(sx) ? sx : [sx])]}
|
|
26
28
|
dense
|
|
@@ -72,6 +72,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
const selectedLevel = useMotionValueValue(selection, (s) => (s === false ? -1 : s.length))
|
|
75
|
+
const selectionValue = useMotionValueValue(selection, (s) => (s ? s.join('') : s))
|
|
75
76
|
const activeAndNotClosing = useMotionSelector([selection, closing], ([s, c]) =>
|
|
76
77
|
c ? false : s !== false,
|
|
77
78
|
)
|
|
@@ -108,6 +109,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
108
109
|
widthSm={false}
|
|
109
110
|
overlayPaneProps={{
|
|
110
111
|
layout: true,
|
|
112
|
+
layoutDependency: selectionValue,
|
|
111
113
|
initial: false,
|
|
112
114
|
onLayoutAnimationStart: () => {
|
|
113
115
|
animating.set(true)
|
|
@@ -127,7 +129,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
127
129
|
},
|
|
128
130
|
}}
|
|
129
131
|
>
|
|
130
|
-
<MotionDiv layout sx={{ display: 'grid' }}>
|
|
132
|
+
<MotionDiv layout layoutDependency={selectionValue} sx={{ display: 'grid' }}>
|
|
131
133
|
<Box
|
|
132
134
|
className={classes.header}
|
|
133
135
|
sx={(theme) => ({
|
|
@@ -142,6 +144,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
142
144
|
floatingSm={false}
|
|
143
145
|
switchPoint={0}
|
|
144
146
|
layout='position'
|
|
147
|
+
layoutDependency={selectionValue}
|
|
145
148
|
left={
|
|
146
149
|
selectedLevel > 0 && (
|
|
147
150
|
<Fab
|
|
@@ -173,7 +176,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
|
|
|
173
176
|
</LayoutHeaderContent>
|
|
174
177
|
</Box>
|
|
175
178
|
</MotionDiv>
|
|
176
|
-
<MotionDiv layout='position' sx={{ display: 'grid' }}>
|
|
179
|
+
<MotionDiv layout='position' layoutDependency={selectionValue} sx={{ display: 'grid' }}>
|
|
177
180
|
<Box
|
|
178
181
|
sx={[
|
|
179
182
|
(theme) => ({
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { Scroller, useScrollerContext, useScrollTo } from '@graphcommerce/framer-scroller'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
dvh,
|
|
4
|
+
dvw,
|
|
5
|
+
useConstant,
|
|
6
|
+
useElementScroll,
|
|
7
|
+
useIsomorphicLayoutEffect,
|
|
8
|
+
} from '@graphcommerce/framer-utils'
|
|
3
9
|
import { Box, styled, SxProps, Theme, useTheme, useThemeProps } from '@mui/material'
|
|
4
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
m,
|
|
12
|
+
MotionProps,
|
|
13
|
+
motionValue,
|
|
14
|
+
useDomEvent,
|
|
15
|
+
useMotionValue,
|
|
16
|
+
useTransform,
|
|
17
|
+
} from 'framer-motion'
|
|
18
|
+
import framesync from 'framesync'
|
|
5
19
|
import React, { useCallback, useEffect, useRef } from 'react'
|
|
6
20
|
import { LayoutProvider } from '../../Layout/components/LayoutProvider'
|
|
7
21
|
import { ExtendableComponent, extendableComponent } from '../../Styles'
|
|
8
|
-
import {
|
|
9
|
-
import framesync from 'framesync'
|
|
22
|
+
import { useMatchMedia } from '../../hooks/useMatchMedia'
|
|
10
23
|
|
|
11
24
|
export type LayoutOverlayVariant = 'left' | 'bottom' | 'right'
|
|
12
25
|
export type LayoutOverlaySize = 'floating' | 'minimal' | 'full'
|
|
@@ -66,10 +79,6 @@ declare module '@mui/material/styles/components' {
|
|
|
66
79
|
|
|
67
80
|
const MotionDiv = styled(m.div)({})
|
|
68
81
|
|
|
69
|
-
const clearScrollLock = () => {
|
|
70
|
-
document.body.style.overflow = ''
|
|
71
|
-
}
|
|
72
|
-
|
|
73
82
|
export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
74
83
|
const props = useThemeProps({ name, props: incomingProps })
|
|
75
84
|
|
|
@@ -103,26 +112,132 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
103
112
|
props.smSpacingTop ?? ((theme) => `calc(${theme.appShell.headerHeightSm} * 0.5)`)
|
|
104
113
|
)(th)
|
|
105
114
|
|
|
106
|
-
const { scrollerRef, snap, scroll } = useScrollerContext()
|
|
107
|
-
const positions = useOverlayPosition(variantSm, variantMd)
|
|
115
|
+
const { scrollerRef, snap, scroll, getScrollSnapPositions } = useScrollerContext()
|
|
108
116
|
const scrollTo = useScrollTo()
|
|
109
117
|
const beforeRef = useRef<HTMLDivElement>(null)
|
|
118
|
+
const overlayRef = useRef<HTMLDivElement>(null)
|
|
119
|
+
const overlayPaneRef = useRef<HTMLDivElement>(null)
|
|
110
120
|
|
|
111
121
|
const position = useMotionValue<OverlayPosition>(OverlayPosition.UNOPENED)
|
|
122
|
+
const overlayPaneScroll = useElementScroll(overlayPaneRef)
|
|
112
123
|
|
|
113
124
|
const classes = withState({ variantSm, variantMd, sizeSm, sizeMd, justifySm, justifyMd })
|
|
114
125
|
|
|
115
|
-
const
|
|
126
|
+
const match = useMatchMedia()
|
|
127
|
+
const positions = useConstant(() => ({
|
|
128
|
+
open: { x: motionValue(0), y: motionValue(0), visible: motionValue(0) },
|
|
129
|
+
closed: { x: motionValue(0), y: motionValue(0) },
|
|
130
|
+
}))
|
|
131
|
+
|
|
132
|
+
const variant = useCallback(
|
|
133
|
+
() => (match.up('md') ? variantMd : variantSm),
|
|
134
|
+
[match, variantMd, variantSm],
|
|
135
|
+
)
|
|
136
|
+
const prevVariant = useRef<LayoutOverlayVariant>()
|
|
137
|
+
|
|
138
|
+
useIsomorphicLayoutEffect(() => {
|
|
139
|
+
const scroller = scrollerRef.current
|
|
140
|
+
if (!scroller) return () => {}
|
|
141
|
+
|
|
142
|
+
const calcPositions = () => {
|
|
143
|
+
const snapPositions = getScrollSnapPositions()
|
|
144
|
+
const x = snapPositions.x[snapPositions.x.length - 1]
|
|
145
|
+
const y = snapPositions.y[snapPositions.y.length - 1]
|
|
146
|
+
|
|
147
|
+
if (variant() === 'left') {
|
|
148
|
+
positions.closed.x.set(x)
|
|
149
|
+
positions.open.x.set(0)
|
|
150
|
+
}
|
|
151
|
+
if (variant() === 'right') {
|
|
152
|
+
positions.open.x.set(x)
|
|
153
|
+
positions.closed.x.set(0)
|
|
154
|
+
}
|
|
155
|
+
if (variant() === 'bottom') {
|
|
156
|
+
positions.open.y.set(y)
|
|
157
|
+
positions.closed.y.set(0)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const forceScrollPosition = () => {
|
|
162
|
+
// Set the initial position of the overlay.
|
|
163
|
+
// Make sure the overlay stays open when the variant changes.
|
|
164
|
+
if (position.get() !== OverlayPosition.OPENED) {
|
|
165
|
+
scroller.scrollLeft = positions.closed.x.get()
|
|
166
|
+
scroller.scrollTop = positions.closed.y.get()
|
|
167
|
+
} else {
|
|
168
|
+
scroller.scrollLeft = positions.open.x.get()
|
|
169
|
+
scroller.scrollTop = positions.open.y.get()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const calcVisible = () => {
|
|
174
|
+
const snapPositions = getScrollSnapPositions()
|
|
175
|
+
const clampRound = (value: number) => Math.round(Math.max(0, Math.min(1, value)) * 100) / 100
|
|
176
|
+
|
|
177
|
+
const scrollX = scroller.scrollLeft || scroll.x.get()
|
|
178
|
+
const scrolly = scroller.scrollTop || scroll.y.get()
|
|
179
|
+
|
|
180
|
+
if (variant() === 'left') {
|
|
181
|
+
const closedX = snapPositions.x[1] ?? 0
|
|
182
|
+
positions.open.visible.set(closedX === 0 ? 0 : clampRound((scrollX - closedX) / -closedX))
|
|
183
|
+
}
|
|
184
|
+
if (variant() === 'right') {
|
|
185
|
+
const openedX = snapPositions.x[1] ?? 0
|
|
186
|
+
positions.open.visible.set(openedX === 0 ? 0 : clampRound(scrollX / openedX))
|
|
187
|
+
}
|
|
188
|
+
if (variant() === 'bottom') {
|
|
189
|
+
const openedY = snapPositions.y[1] ?? 0
|
|
190
|
+
positions.open.visible.set(openedY === 0 ? 0 : clampRound(scrolly / openedY))
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const handleScroll = () => {
|
|
195
|
+
calcPositions()
|
|
196
|
+
|
|
197
|
+
// if (!prevVariant.current) prevVariant.current = variant()
|
|
198
|
+
if (prevVariant.current !== variant()) {
|
|
199
|
+
forceScrollPosition()
|
|
200
|
+
prevVariant.current = variant()
|
|
201
|
+
} else {
|
|
202
|
+
// When we're not switching variants, we update the vibility of the overlay.
|
|
203
|
+
calcVisible()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const handleResize = () => {
|
|
208
|
+
calcPositions()
|
|
209
|
+
forceScrollPosition()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const measureScroll = () => framesync.read(handleScroll)
|
|
213
|
+
const measureResize = () => framesync.read(handleResize)
|
|
214
|
+
handleScroll()
|
|
215
|
+
|
|
216
|
+
const cancelX = scroll.x.on('change', measureScroll)
|
|
217
|
+
const cancelY = scroll.y.on('change', measureScroll)
|
|
218
|
+
|
|
219
|
+
const ro = new ResizeObserver(measureResize)
|
|
220
|
+
ro.observe(scrollerRef.current)
|
|
221
|
+
;[...scrollerRef.current.children].forEach((child) => ro.observe(child))
|
|
222
|
+
|
|
223
|
+
window.addEventListener('resize', measureResize)
|
|
224
|
+
return () => {
|
|
225
|
+
window.removeEventListener('resize', measureResize)
|
|
226
|
+
ro.disconnect()
|
|
227
|
+
cancelX()
|
|
228
|
+
cancelY()
|
|
229
|
+
}
|
|
230
|
+
}, [getScrollSnapPositions, position, positions, scroll, scrollerRef, variant])
|
|
116
231
|
|
|
117
232
|
// When the component is mounted, we need to set the initial position of the overlay.
|
|
118
233
|
useIsomorphicLayoutEffect(() => {
|
|
119
234
|
const scroller = scrollerRef.current
|
|
120
235
|
|
|
121
|
-
if (!scroller || !isPresent) return
|
|
236
|
+
if (!scroller || !isPresent) return
|
|
122
237
|
|
|
123
238
|
const open = { x: positions.open.x.get(), y: positions.open.y.get() }
|
|
124
239
|
|
|
125
|
-
document.body.style.overflow = 'hidden'
|
|
240
|
+
if (variant() === 'right') document.body.style.overflow = 'hidden'
|
|
126
241
|
|
|
127
242
|
if (position.get() !== OverlayPosition.OPENED) {
|
|
128
243
|
if (direction === 1) {
|
|
@@ -141,29 +256,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
141
256
|
snap.set(true)
|
|
142
257
|
}
|
|
143
258
|
}
|
|
144
|
-
|
|
145
|
-
return clearScrollLock
|
|
146
|
-
}, [direction, isPresent, position, positions, scrollTo, scrollerRef, snap])
|
|
147
|
-
|
|
148
|
-
// Make sure the overlay stays open when resizing the window.
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
const scroller = scrollerRef.current
|
|
151
|
-
if (!scroller) return () => {}
|
|
152
|
-
|
|
153
|
-
const resize = () => {
|
|
154
|
-
if (position.get() !== OverlayPosition.OPENED) {
|
|
155
|
-
scroller.scrollLeft = positions.closed.x.get()
|
|
156
|
-
scroller.scrollTop = positions.closed.y.get()
|
|
157
|
-
} else {
|
|
158
|
-
scroller.scrollLeft = positions.open.x.get()
|
|
159
|
-
scroller.scrollTop = positions.open.y.get()
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const resizeTimed = () => framesync.read(resize)
|
|
163
|
-
|
|
164
|
-
window.addEventListener('resize', resizeTimed)
|
|
165
|
-
return () => window.removeEventListener('resize', resizeTimed)
|
|
166
|
-
}, [position, positions, scrollerRef])
|
|
259
|
+
}, [direction, isPresent, position, positions, scrollTo, scrollerRef, snap, variant])
|
|
167
260
|
|
|
168
261
|
// When the overlay is closed by navigating away, we're closing the overlay.
|
|
169
262
|
useEffect(() => {
|
|
@@ -172,16 +265,19 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
172
265
|
|
|
173
266
|
if (position.get() === OverlayPosition.UNOPENED) {
|
|
174
267
|
position.set(OverlayPosition.CLOSED)
|
|
175
|
-
clearScrollLock()
|
|
176
268
|
scroller.scrollLeft = positions.closed.x.get()
|
|
177
269
|
scroller.scrollTop = positions.closed.y.get()
|
|
178
270
|
safeToRemove?.()
|
|
271
|
+
document.body.style.overflow = ''
|
|
179
272
|
} else {
|
|
180
273
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
181
274
|
scrollTo({
|
|
182
275
|
x: positions.closed.x.get(),
|
|
183
276
|
y: positions.closed.y.get(),
|
|
184
|
-
}).then(() =>
|
|
277
|
+
}).then(() => {
|
|
278
|
+
safeToRemove?.()
|
|
279
|
+
document.body.style.overflow = ''
|
|
280
|
+
})
|
|
185
281
|
}
|
|
186
282
|
}, [isPresent, position, positions, safeToRemove, scrollTo, scrollerRef])
|
|
187
283
|
|
|
@@ -189,7 +285,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
189
285
|
const closeOverlay = useCallback(() => {
|
|
190
286
|
if (position.get() !== OverlayPosition.OPENED) return
|
|
191
287
|
position.set(OverlayPosition.CLOSED)
|
|
192
|
-
|
|
288
|
+
// document.body.style.overflow = ''
|
|
193
289
|
onClosed()
|
|
194
290
|
}, [onClosed, position])
|
|
195
291
|
|
|
@@ -216,20 +312,14 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
216
312
|
}, [offsetY])
|
|
217
313
|
|
|
218
314
|
// Create the exact position for the LayoutProvider which offsets the top of the overlay
|
|
219
|
-
const scrollYOffset = useTransform(
|
|
220
|
-
[scroll.y, positions.open.y],
|
|
221
|
-
([y, openY]: number[]) => y - openY + offsetPageY,
|
|
222
|
-
)
|
|
315
|
+
const scrollYOffset = useTransform(overlayPaneScroll.y, (paneY) => paneY + offsetPageY)
|
|
223
316
|
|
|
224
317
|
const onClickAway = useCallback(
|
|
225
318
|
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
226
|
-
const isTarget =
|
|
227
|
-
event.target === scrollerRef.current ||
|
|
228
|
-
event.target === beforeRef.current ||
|
|
229
|
-
event.target === overlayRef.current
|
|
319
|
+
const isTarget = event.target === beforeRef.current
|
|
230
320
|
if (isTarget && snap.get()) closeOverlay()
|
|
231
321
|
},
|
|
232
|
-
[closeOverlay,
|
|
322
|
+
[closeOverlay, snap],
|
|
233
323
|
)
|
|
234
324
|
|
|
235
325
|
return (
|
|
@@ -261,6 +351,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
261
351
|
className={`${classes.scroller} ${className ?? ''}`}
|
|
262
352
|
grid={false}
|
|
263
353
|
onClick={onClickAway}
|
|
354
|
+
hideScrollbar
|
|
264
355
|
sx={[
|
|
265
356
|
(theme) => ({
|
|
266
357
|
overscrollBehavior: 'contain',
|
|
@@ -269,7 +360,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
269
360
|
cursor: 'default',
|
|
270
361
|
},
|
|
271
362
|
'&.mdSnapDirInline': {
|
|
272
|
-
overflow: 'auto',
|
|
363
|
+
overflow: active ? 'auto' : 'hidden',
|
|
273
364
|
},
|
|
274
365
|
|
|
275
366
|
height: dvh(100),
|
|
@@ -308,15 +399,11 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
308
399
|
gridTemplate: `"overlay beforeOverlay"`,
|
|
309
400
|
borderTopRightRadius: theme.shape.borderRadius * 4,
|
|
310
401
|
borderBottomRightRadius: theme.shape.borderRadius * 4,
|
|
311
|
-
|
|
312
|
-
'&::-webkit-scrollbar': { display: 'none' },
|
|
313
402
|
},
|
|
314
403
|
'&.variantMdRight': {
|
|
315
404
|
gridTemplate: `"beforeOverlay overlay"`,
|
|
316
405
|
borderTopLeftRadius: theme.shape.borderRadius * 4,
|
|
317
406
|
borderBottomLeftRadius: theme.shape.borderRadius * 4,
|
|
318
|
-
|
|
319
|
-
'&::-webkit-scrollbar': { display: 'none' },
|
|
320
407
|
},
|
|
321
408
|
'&.variantMdBottom': {
|
|
322
409
|
borderTopLeftRadius: theme.shape.borderRadius * 4,
|
|
@@ -332,7 +419,6 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
332
419
|
]}
|
|
333
420
|
>
|
|
334
421
|
<Box
|
|
335
|
-
onClick={onClickAway}
|
|
336
422
|
className={classes.beforeOverlay}
|
|
337
423
|
ref={beforeRef}
|
|
338
424
|
sx={(theme) => ({
|
|
@@ -362,12 +448,12 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
362
448
|
<Box
|
|
363
449
|
className={classes.overlay}
|
|
364
450
|
ref={overlayRef}
|
|
365
|
-
onClick={onClickAway}
|
|
366
451
|
sx={(theme) => ({
|
|
367
452
|
display: 'grid',
|
|
368
453
|
gridArea: 'overlay',
|
|
369
454
|
scrollSnapAlign: 'start',
|
|
370
455
|
scrollSnapStop: 'always',
|
|
456
|
+
pointerEvents: 'none',
|
|
371
457
|
[theme.breakpoints.down('md')]: {
|
|
372
458
|
justifyContent: justifySm,
|
|
373
459
|
alignItems: justifySm,
|
|
@@ -401,67 +487,85 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
|
|
|
401
487
|
<MotionDiv
|
|
402
488
|
{...overlayPaneProps}
|
|
403
489
|
className={classes.overlayPane}
|
|
490
|
+
ref={overlayPaneRef}
|
|
404
491
|
sx={(theme) => ({
|
|
405
|
-
pointerEvents: '
|
|
492
|
+
pointerEvents: 'auto',
|
|
406
493
|
backgroundColor: theme.palette.background.paper,
|
|
407
494
|
boxShadow: theme.shadows[24],
|
|
408
495
|
|
|
409
496
|
[theme.breakpoints.down('md')]: {
|
|
410
497
|
minWidth: '80vw',
|
|
498
|
+
overflowY: 'auto',
|
|
411
499
|
'&:not(.sizeSmFull)': {
|
|
412
500
|
width: 'auto',
|
|
413
501
|
},
|
|
414
502
|
|
|
415
|
-
'&.variantSmBottom.sizeSmFull': {
|
|
416
|
-
minHeight: `calc(${dvh(100)} - ${smSpacingTop})`,
|
|
417
|
-
},
|
|
418
|
-
|
|
419
503
|
'&.variantSmBottom': {
|
|
504
|
+
maxHeight: `calc(${dvh(100)} - ${smSpacingTop})`,
|
|
505
|
+
'&.sizeSmFloating': {
|
|
506
|
+
maxHeight: `calc(${dvh(100)} - (${theme.page.vertical} * 2))`,
|
|
507
|
+
},
|
|
508
|
+
'&.sizeSmFull': {
|
|
509
|
+
height: `calc(${dvh(100)} - ${smSpacingTop})`,
|
|
510
|
+
},
|
|
511
|
+
|
|
420
512
|
borderTopLeftRadius: `${theme.shape.borderRadius * 3}px`,
|
|
421
513
|
borderTopRightRadius: `${theme.shape.borderRadius * 3}px`,
|
|
422
514
|
},
|
|
423
|
-
'&.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
515
|
+
'&.variantSmLeft, &.variantSmRight': {
|
|
516
|
+
width: widthSm || 'max-content',
|
|
517
|
+
maxWidth: dvw(100),
|
|
518
|
+
maxHeight: dvh(100),
|
|
519
|
+
'&.sizeSmFull': {
|
|
520
|
+
height: dvh(100),
|
|
521
|
+
},
|
|
522
|
+
'&.sizeSmFloating': {
|
|
523
|
+
maxHeight: `calc(${dvh(100)} - (${theme.page.vertical} * 2))`,
|
|
524
|
+
},
|
|
429
525
|
},
|
|
430
|
-
|
|
526
|
+
|
|
527
|
+
'&.variantSmLeft.sizeSmFull, &.variantSmRight.sizeSmFull': {
|
|
431
528
|
paddingBottom: '1px',
|
|
432
|
-
minHeight: dvh(100),
|
|
433
529
|
},
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
530
|
+
|
|
531
|
+
'&.sizeSmFloating': {
|
|
532
|
+
borderRadius: `${theme.shape.borderRadius * 3}px`,
|
|
437
533
|
},
|
|
438
534
|
},
|
|
439
535
|
[theme.breakpoints.up('md')]: {
|
|
440
536
|
minWidth: '1px',
|
|
441
|
-
|
|
442
|
-
'
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
'&.sizeMdFull.variantMdLeft': {
|
|
446
|
-
paddingBottom: '1px',
|
|
447
|
-
minHeight: dvh(100),
|
|
448
|
-
},
|
|
449
|
-
'&.sizeMdFull.variantMdRight': {
|
|
450
|
-
paddingBottom: '1px',
|
|
451
|
-
minHeight: dvh(100),
|
|
537
|
+
overflowY: 'auto',
|
|
538
|
+
overscrollBehavior: 'contain',
|
|
539
|
+
'&.variantMdBottom.sizeMdFloating:not(.justifyMdStretch)': {
|
|
540
|
+
width: widthMd,
|
|
452
541
|
},
|
|
453
542
|
|
|
454
543
|
'&.variantMdBottom': {
|
|
544
|
+
maxHeight: `calc(${dvh(100)} - ${mdSpacingTop})`,
|
|
545
|
+
'&.sizeMdFloating': {
|
|
546
|
+
maxHeight: `calc(${dvh(100)} - (${theme.page.vertical} * 2))`,
|
|
547
|
+
},
|
|
548
|
+
'&.sizeMdFull': {
|
|
549
|
+
height: `calc(${dvh(100)} - ${mdSpacingTop})`,
|
|
550
|
+
},
|
|
551
|
+
|
|
455
552
|
borderTopLeftRadius: `${theme.shape.borderRadius * 4}px`,
|
|
456
553
|
borderTopRightRadius: `${theme.shape.borderRadius * 4}px`,
|
|
457
554
|
},
|
|
458
555
|
'&.variantMdLeft, &.variantMdRight': {
|
|
459
556
|
width: widthMd || 'max-content',
|
|
460
557
|
maxWidth: dvw(100),
|
|
558
|
+
maxHeight: dvh(100),
|
|
559
|
+
'&.sizeMdFull': {
|
|
560
|
+
height: dvh(100),
|
|
561
|
+
},
|
|
562
|
+
'&.sizeMdFloating': {
|
|
563
|
+
maxHeight: `calc(${dvh(100)} - (${theme.page.vertical} * 2))`,
|
|
564
|
+
},
|
|
461
565
|
},
|
|
462
566
|
|
|
463
|
-
'&.
|
|
464
|
-
|
|
567
|
+
'&.variantMdLeft.sizeMdFull, &.variantMdRight.sizeMdFull': {
|
|
568
|
+
paddingBottom: '1px',
|
|
465
569
|
},
|
|
466
570
|
|
|
467
571
|
'&.sizeMdFloating': {
|
|
@@ -10,7 +10,7 @@ export function variantsToScrollSnapType(variants: Variants): Return {
|
|
|
10
10
|
const inlineMd = variants.variantMd === 'left' || variants.variantMd === 'right'
|
|
11
11
|
|
|
12
12
|
return {
|
|
13
|
-
scrollSnapTypeSm: inlineSm ? 'inline mandatory' : 'block
|
|
14
|
-
scrollSnapTypeMd: inlineMd ? 'inline mandatory' : 'block
|
|
13
|
+
scrollSnapTypeSm: inlineSm ? 'inline mandatory' : 'block mandatory',
|
|
14
|
+
scrollSnapTypeMd: inlineMd ? 'inline mandatory' : 'block mandatory',
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmotionJSX } from '@emotion/react/types/jsx-namespace'
|
|
1
|
+
import type { EmotionJSX } from '@emotion/react/types/jsx-namespace'
|
|
2
2
|
import createEmotionServer from '@emotion/server/create-instance'
|
|
3
3
|
// eslint-disable-next-line @next/next/no-document-import-in-page
|
|
4
4
|
import type NextDocument from 'next/document'
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/next-ui",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "6.0.0-canary.
|
|
5
|
+
"version": "6.0.0-canary.34",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"@emotion/react": "^11.10.6",
|
|
19
19
|
"@emotion/server": "^11.4.0",
|
|
20
20
|
"@emotion/styled": "^11.10.6",
|
|
21
|
-
"@graphcommerce/framer-next-pages": "6.0.0-canary.
|
|
22
|
-
"@graphcommerce/framer-scroller": "6.0.0-canary.
|
|
23
|
-
"@graphcommerce/framer-utils": "6.0.0-canary.
|
|
24
|
-
"@graphcommerce/image": "6.0.0-canary.
|
|
21
|
+
"@graphcommerce/framer-next-pages": "6.0.0-canary.34",
|
|
22
|
+
"@graphcommerce/framer-scroller": "6.0.0-canary.34",
|
|
23
|
+
"@graphcommerce/framer-utils": "6.0.0-canary.34",
|
|
24
|
+
"@graphcommerce/image": "6.0.0-canary.34",
|
|
25
25
|
"cookie": "^0.5.0",
|
|
26
26
|
"react-is": "^18.2.0",
|
|
27
27
|
"schema-dts": "^1.1.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@graphcommerce/eslint-config-pwa": "6.0.0-canary.
|
|
31
|
-
"@graphcommerce/prettier-config-pwa": "6.0.0-canary.
|
|
32
|
-
"@graphcommerce/typescript-config-pwa": "6.0.0-canary.
|
|
30
|
+
"@graphcommerce/eslint-config-pwa": "6.0.0-canary.34",
|
|
31
|
+
"@graphcommerce/prettier-config-pwa": "6.0.0-canary.34",
|
|
32
|
+
"@graphcommerce/typescript-config-pwa": "6.0.0-canary.34",
|
|
33
33
|
"@types/cookie": "^0.5.1",
|
|
34
34
|
"@types/react-is": "^17.0.3",
|
|
35
35
|
"typescript": "4.9.5"
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { useScrollerContext } from '@graphcommerce/framer-scroller'
|
|
2
|
-
import { useConstant, useIsomorphicLayoutEffect } from '@graphcommerce/framer-utils'
|
|
3
|
-
import { motionValue } from 'framer-motion'
|
|
4
|
-
import framesync from 'framesync'
|
|
5
|
-
import { useCallback, useEffect } from 'react'
|
|
6
|
-
import { useMatchMedia } from '../../hooks'
|
|
7
|
-
|
|
8
|
-
const clampRound = (value: number) => Math.round(Math.max(0, Math.min(1, value)) * 100) / 100
|
|
9
|
-
|
|
10
|
-
export function useOverlayPosition(
|
|
11
|
-
variantSm: 'left' | 'bottom' | 'right',
|
|
12
|
-
variantMd: 'left' | 'bottom' | 'right',
|
|
13
|
-
) {
|
|
14
|
-
const match = useMatchMedia()
|
|
15
|
-
const { getScrollSnapPositions, scrollerRef, scroll } = useScrollerContext()
|
|
16
|
-
const state = useConstant(() => ({
|
|
17
|
-
open: {
|
|
18
|
-
x: motionValue(0),
|
|
19
|
-
y: motionValue(0),
|
|
20
|
-
visible: motionValue(0),
|
|
21
|
-
},
|
|
22
|
-
closed: {
|
|
23
|
-
x: motionValue(0),
|
|
24
|
-
y: motionValue(0),
|
|
25
|
-
},
|
|
26
|
-
}))
|
|
27
|
-
|
|
28
|
-
const variant = useCallback(
|
|
29
|
-
() => (match.up('md') ? variantMd : variantSm),
|
|
30
|
-
[match, variantMd, variantSm],
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
useIsomorphicLayoutEffect(() => {
|
|
34
|
-
if (!scrollerRef.current) return () => {}
|
|
35
|
-
|
|
36
|
-
const measure = () => {
|
|
37
|
-
const positions = getScrollSnapPositions()
|
|
38
|
-
const x = positions.x[positions.x.length - 1]
|
|
39
|
-
const y = positions.y[positions.y.length - 1]
|
|
40
|
-
|
|
41
|
-
const scrollX = scrollerRef.current?.scrollLeft ?? scroll.x.get()
|
|
42
|
-
const scrolly = scrollerRef.current?.scrollTop ?? scroll.y.get()
|
|
43
|
-
|
|
44
|
-
if (variant() === 'left') {
|
|
45
|
-
state.closed.x.set(x)
|
|
46
|
-
state.open.x.set(0)
|
|
47
|
-
|
|
48
|
-
const closedX = positions.x[1] ?? 0
|
|
49
|
-
state.open.visible.set(closedX === 0 ? 0 : clampRound((scrollX - closedX) / -closedX))
|
|
50
|
-
}
|
|
51
|
-
if (variant() === 'right') {
|
|
52
|
-
state.open.x.set(x)
|
|
53
|
-
state.closed.x.set(0)
|
|
54
|
-
|
|
55
|
-
const openedX = positions.x[1] ?? 0
|
|
56
|
-
state.open.visible.set(openedX === 0 ? 0 : clampRound(scrollX / openedX))
|
|
57
|
-
}
|
|
58
|
-
if (variant() === 'bottom') {
|
|
59
|
-
state.open.y.set(y)
|
|
60
|
-
state.closed.y.set(0)
|
|
61
|
-
|
|
62
|
-
const openedY = positions.y[1] ?? 0
|
|
63
|
-
state.open.visible.set(openedY === 0 ? 0 : clampRound(scrolly / openedY))
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const measureTimed = () => framesync.read(measure)
|
|
68
|
-
measure()
|
|
69
|
-
|
|
70
|
-
const cancelX = scroll.x.onChange(measure)
|
|
71
|
-
const cancelY = scroll.y.onChange(measure)
|
|
72
|
-
|
|
73
|
-
const ro = new ResizeObserver(measureTimed)
|
|
74
|
-
ro.observe(scrollerRef.current)
|
|
75
|
-
;[...scrollerRef.current.children].forEach((child) => ro.observe(child))
|
|
76
|
-
|
|
77
|
-
window.addEventListener('resize', measureTimed)
|
|
78
|
-
return () => {
|
|
79
|
-
window.removeEventListener('resize', measureTimed)
|
|
80
|
-
ro.disconnect()
|
|
81
|
-
cancelX()
|
|
82
|
-
cancelY()
|
|
83
|
-
}
|
|
84
|
-
}, [getScrollSnapPositions, scroll.x, scroll.y, scrollerRef, state, variant])
|
|
85
|
-
|
|
86
|
-
return state
|
|
87
|
-
}
|