@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 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}>{right}</MotionDiv>
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 { dvh, dvw, useIsomorphicLayoutEffect } from '@graphcommerce/framer-utils'
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 { m, MotionProps, useDomEvent, useMotionValue, useTransform } from 'framer-motion'
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 { useOverlayPosition } from '../hooks/useOverlayPosition'
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 overlayRef = useRef<HTMLDivElement>(null)
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 undefined
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(() => safeToRemove?.())
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
- clearScrollLock()
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, scrollerRef, snap],
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: 'all',
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
- '&.sizeSmFloating': {
424
- borderRadius: `${theme.shape.borderRadius * 3}px`,
425
- },
426
- '&.variantSmLeft.sizeSmFull': {
427
- paddingBottom: '1px',
428
- minHeight: dvh(100),
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
- '&.variantSmRight.sizeSmFull': {
526
+
527
+ '&.variantSmLeft.sizeSmFull, &.variantSmRight.sizeSmFull': {
431
528
  paddingBottom: '1px',
432
- minHeight: dvh(100),
433
529
  },
434
- '&.variantSmLeft, &.variantSmRight': {
435
- width: widthSm || undefined,
436
- maxWidth: dvw(100),
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
- '&.sizeMdFull.variantMdBottom': {
443
- minHeight: `calc(${dvh(100)} - ${mdSpacingTop})`,
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
- '&.variantMdBottom.sizeMdFloating': {
464
- width: widthMd,
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 proximity',
14
- scrollSnapTypeMd: inlineMd ? 'inline mandatory' : 'block proximity',
13
+ scrollSnapTypeSm: inlineSm ? 'inline mandatory' : 'block mandatory',
14
+ scrollSnapTypeMd: inlineMd ? 'inline mandatory' : 'block mandatory',
15
15
  }
16
16
  }
@@ -1,5 +1,4 @@
1
- import { Theme } from '@emotion/react'
2
- import { SxProps } from '@mui/material'
1
+ import { Theme, SxProps } from '@mui/material'
3
2
  import React from 'react'
4
3
 
5
4
  export type PanelActionsProps = {
@@ -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.32",
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.32",
22
- "@graphcommerce/framer-scroller": "6.0.0-canary.32",
23
- "@graphcommerce/framer-utils": "6.0.0-canary.32",
24
- "@graphcommerce/image": "6.0.0-canary.32",
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.32",
31
- "@graphcommerce/prettier-config-pwa": "6.0.0-canary.32",
32
- "@graphcommerce/typescript-config-pwa": "6.0.0-canary.32",
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
- }