@graphcommerce/next-ui 4.21.0 → 4.23.1

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 (43) hide show
  1. package/ActionCard/ActionCard.tsx +47 -13
  2. package/ActionCard/ActionCardList.tsx +33 -32
  3. package/ActionCard/ActionCardListForm.tsx +1 -2
  4. package/AnimatedRow/AnimatedRow.tsx +1 -0
  5. package/Blog/BlogHeader/BlogHeader.tsx +7 -2
  6. package/Blog/BlogListItem/BlogListItem.tsx +5 -1
  7. package/Blog/BlogTags/BlogTag.tsx +2 -3
  8. package/CHANGELOG.md +52 -0
  9. package/Footer/Footer.tsx +3 -2
  10. package/Form/Form.tsx +7 -2
  11. package/Form/FormActions.tsx +1 -1
  12. package/FramerScroller/SidebarGallery.tsx +1 -5
  13. package/IconSvg/IconSvg.tsx +4 -4
  14. package/Layout/components/LayoutHeaderClose.tsx +2 -0
  15. package/Layout/components/LayoutHeaderContent.tsx +3 -1
  16. package/LayoutDefault/components/LayoutDefault.tsx +3 -3
  17. package/LayoutOverlay/components/LayoutOverlay.tsx +2 -1
  18. package/LayoutParts/DesktopHeaderBadge.tsx +3 -3
  19. package/LayoutParts/MenuFab.tsx +2 -2
  20. package/Navigation/components/NavigationItem.tsx +33 -19
  21. package/Navigation/components/NavigationList.tsx +21 -15
  22. package/Navigation/components/NavigationOverlay.tsx +63 -35
  23. package/Navigation/components/NavigationProvider.tsx +12 -30
  24. package/Navigation/hooks/useNavigation.ts +33 -6
  25. package/Overlay/components/Overlay.tsx +1 -2
  26. package/Overlay/components/OverlayBase.tsx +19 -19
  27. package/Overlay/hooks/useOverlayPosition.ts +6 -1
  28. package/Page/types.ts +10 -1
  29. package/Row/HeroBanner/HeroBanner.tsx +7 -2
  30. package/Row/ImageText/ImageText.tsx +10 -3
  31. package/Row/ImageTextBoxed/ImageTextBoxed.tsx +7 -1
  32. package/Row/ParagraphWithSidebarSlide/ParagraphWithSidebarSlide.tsx +4 -2
  33. package/Row/SpecialBanner/SpecialBanner.tsx +10 -2
  34. package/Snackbar/MessageSnackbarImpl.tsx +9 -5
  35. package/Stepper/Stepper.tsx +1 -2
  36. package/Styles/breakpointVal.tsx +1 -1
  37. package/TextInputNumber/TextInputNumber.tsx +5 -4
  38. package/Theme/MuiButton.ts +12 -3
  39. package/Theme/MuiFab.ts +2 -2
  40. package/ToggleButton/ToggleButton.tsx +15 -5
  41. package/hooks/index.ts +1 -0
  42. package/hooks/useMemoDeep.ts +15 -0
  43. package/package.json +5 -5
@@ -1,38 +1,40 @@
1
1
  import { styled } from '@mui/material'
2
+ import React from 'react'
2
3
  import { extendableComponent } from '../../Styles/extendableComponent'
3
- import { NavigationNode, NavigationPath } from '../hooks/useNavigation'
4
+ import { NavigationNode } from '../hooks/useNavigation'
4
5
  import { NavigationItem, mouseEventPref } from './NavigationItem'
5
6
 
6
- const NavigationUList = styled('ul')({})
7
+ const NavigationUList = styled('ul')({
8
+ display: 'block',
9
+ position: 'absolute',
10
+ left: '-10000px',
11
+ top: '-10000px',
12
+ '&.selected': {
13
+ display: 'contents',
14
+ },
15
+ })
7
16
 
8
17
  type NavigationItemsProps = {
9
- parentPath?: NavigationPath
18
+ parentPath?: string
10
19
  items: NavigationNode[]
11
20
  selected?: boolean
12
21
  } & mouseEventPref
13
22
 
14
23
  type OwnerState = {
15
24
  column: number
25
+ selected: boolean
16
26
  }
17
27
 
18
28
  const name = 'NavigationList'
19
29
  const parts = ['root'] as const
20
30
  const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
21
31
 
22
- // const componentName = 'NavigationItem'
23
- // const parts = ['li', 'ul', 'item'] as const
24
-
25
- export function NavigationList(props: NavigationItemsProps) {
26
- const { items, parentPath = [], selected = false, mouseEvent } = props
32
+ export const NavigationList = React.memo<NavigationItemsProps>((props) => {
33
+ const { items, parentPath = '', selected = false, mouseEvent } = props
27
34
 
35
+ const classes = withState({ column: 0, selected })
28
36
  return (
29
- <NavigationUList
30
- sx={[
31
- { display: 'block', position: 'absolute', left: '-10000px', top: '-10000px' },
32
- selected && { display: 'contents' },
33
- ]}
34
- className={withState({ column: 0 }).root}
35
- >
37
+ <NavigationUList className={classes.root}>
36
38
  {items.map((item, idx) => (
37
39
  <NavigationItem
38
40
  NavigationList={NavigationList}
@@ -48,4 +50,8 @@ export function NavigationList(props: NavigationItemsProps) {
48
50
  ))}
49
51
  </NavigationUList>
50
52
  )
53
+ })
54
+
55
+ if (process.env.NODE_ENV !== 'production') {
56
+ NavigationList.displayName = 'NavigationList'
51
57
  }
@@ -1,9 +1,11 @@
1
1
  import styled from '@emotion/styled'
2
+ import { useMotionValueValue, useMotionSelector } from '@graphcommerce/framer-utils'
2
3
  import { i18n } from '@lingui/core'
3
4
  import { Trans } from '@lingui/react'
4
5
  import { Box, Fab, SxProps, Theme, useEventCallback, useMediaQuery } from '@mui/material'
5
6
  import { m } from 'framer-motion'
6
- import { useEffect, useState } from 'react'
7
+ import React, { useEffect } from 'react'
8
+ import type { LiteralUnion } from 'type-fest'
7
9
  import { IconSvg, useIconSvgSize } from '../../IconSvg'
8
10
  import { LayoutHeaderContent } from '../../Layout/components/LayoutHeaderContent'
9
11
  import { LayoutTitle } from '../../Layout/components/LayoutTitle'
@@ -17,6 +19,7 @@ import {
17
19
  NavigationContextType,
18
20
  NavigationNodeButton,
19
21
  NavigationNodeHref,
22
+ NavigationPath,
20
23
  useNavigation,
21
24
  } from '../hooks/useNavigation'
22
25
  import { mouseEventPref } from './NavigationItem'
@@ -25,9 +28,9 @@ import { NavigationList } from './NavigationList'
25
28
  type LayoutOverlayVariant = 'left' | 'bottom' | 'right'
26
29
  type LayoutOverlaySize = 'floating' | 'minimal' | 'full'
27
30
  type LayoutOverlayAlign = 'start' | 'end' | 'center' | 'stretch'
31
+ type ItemPadding = LiteralUnion<keyof Theme['spacings'], string | number>
28
32
 
29
33
  type NavigationOverlayProps = {
30
- active: boolean
31
34
  sx?: SxProps<Theme>
32
35
  stretchColumns?: boolean
33
36
  variantSm: LayoutOverlayVariant
@@ -38,12 +41,14 @@ type NavigationOverlayProps = {
38
41
  justifyMd?: LayoutOverlayAlign
39
42
  itemWidthSm?: string
40
43
  itemWidthMd?: string
44
+ itemPadding?: ItemPadding
41
45
  } & mouseEventPref
42
46
 
43
47
  function findCurrent(
44
48
  items: NavigationContextType['items'],
45
- selected: NavigationContextType['selected'],
49
+ selected: NavigationPath | false,
46
50
  ): NavigationNodeHref | NavigationNodeButton | undefined {
51
+ if (selected === false) return undefined
47
52
  const lastItem = selected.slice(-1)[0]
48
53
 
49
54
  if (!lastItem) return undefined
@@ -67,9 +72,8 @@ const componentName = 'Navigation'
67
72
  const parts = ['root', 'navigation', 'header', 'column'] as const
68
73
  const { classes } = extendableComponent(componentName, parts)
69
74
 
70
- export function NavigationOverlay(props: NavigationOverlayProps) {
75
+ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
71
76
  const {
72
- active,
73
77
  sx,
74
78
  stretchColumns,
75
79
  variantMd,
@@ -81,29 +85,44 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
81
85
  itemWidthSm,
82
86
  itemWidthMd,
83
87
  mouseEvent,
88
+ itemPadding = 'md',
84
89
  } = props
85
- const { selected, select, items, onClose, animating } = useNavigation()
90
+ const { selection, items, animating, closing } = useNavigation()
86
91
 
87
92
  const fabSize = useFabSize('responsive')
88
93
  const svgSize = useIconSvgSize('large')
89
94
 
90
95
  const isMobile = useMediaQuery<Theme>((theme) => theme.breakpoints.down('md'))
91
96
  const handleOnBack = useEventCallback(() => {
92
- if (isMobile) select(selected.slice(0, -1))
93
- else select([])
97
+ if (isMobile) {
98
+ const current = selection.get()
99
+ selection.set(current !== false ? current.slice(0, -1) : false)
100
+ } else selection.set([])
94
101
  })
95
102
 
103
+ const selectedLevel = useMotionValueValue(selection, (s) => (s === false ? -1 : s.length))
104
+ const activeAndNotClosing = useMotionSelector([selection, closing], ([s, c]) =>
105
+ c ? false : s !== false,
106
+ )
107
+
96
108
  useEffect(() => {
97
- animating.current = false
98
- }, [active, animating])
109
+ animating.set(false)
110
+ }, [activeAndNotClosing, animating])
111
+
112
+ const afterClose = useEventCallback(() => {
113
+ if (!closing.get()) return
114
+ closing.set(false)
115
+ selection.set(false)
116
+ })
99
117
 
100
- const showBack = selected.length > 0
118
+ const handleClose = useEventCallback(() => closing.set(true))
101
119
 
102
120
  return (
103
121
  <Overlay
104
122
  className={classes.root}
105
- active={active}
106
- onClosed={onClose}
123
+ active={activeAndNotClosing}
124
+ safeToRemove={afterClose}
125
+ onClosed={handleClose}
107
126
  variantSm={variantSm}
108
127
  sizeSm={sizeSm}
109
128
  justifySm={justifySm}
@@ -111,13 +130,14 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
111
130
  sizeMd={sizeMd}
112
131
  justifyMd={justifyMd}
113
132
  overlayPaneProps={{
133
+ layout: true,
134
+ initial: false,
114
135
  onLayoutAnimationStart: () => {
115
- animating.current = true
136
+ animating.set(true)
116
137
  },
117
138
  onLayoutAnimationComplete: () => {
118
- animating.current = false
139
+ animating.set(false)
119
140
  },
120
- layout: true,
121
141
  }}
122
142
  sx={{
123
143
  zIndex: 'drawer',
@@ -146,7 +166,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
146
166
  switchPoint={0}
147
167
  layout='position'
148
168
  left={
149
- showBack && (
169
+ selectedLevel > 0 && (
150
170
  <Fab
151
171
  color='inherit'
152
172
  onClick={handleOnBack}
@@ -165,7 +185,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
165
185
  right={
166
186
  <Fab
167
187
  color='inherit'
168
- onClick={() => onClose()}
188
+ onClick={handleClose}
169
189
  sx={{
170
190
  boxShadow: 'none',
171
191
  marginLeft: `calc((${fabSize} - ${svgSize}) * -0.5)`,
@@ -179,7 +199,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
179
199
  }
180
200
  >
181
201
  <LayoutTitle size='small' component='span'>
182
- {findCurrent(items, selected)?.name ?? <Trans id='Menu' />}
202
+ {findCurrent(items, selection.get())?.name ?? <Trans id='Menu' />}
183
203
  </LayoutTitle>
184
204
  </LayoutHeaderContent>
185
205
  </Box>
@@ -193,7 +213,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
193
213
  '& .NavigationItem-item': {
194
214
  // eslint-disable-next-line no-nested-ternary
195
215
  width: itemWidthMd
196
- ? selected.length >= 1
216
+ ? selectedLevel >= 1
197
217
  ? `calc(${itemWidthMd} + 1px)`
198
218
  : itemWidthMd
199
219
  : 'stretch',
@@ -201,7 +221,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
201
221
  [theme.breakpoints.down('md')]: {
202
222
  width:
203
223
  sizeSm !== 'floating'
204
- ? `calc(${itemWidthSm || '100vw'} + ${selected.length}px)`
224
+ ? `calc(${itemWidthSm || '100vw'} + ${selectedLevel}px)`
205
225
  : `calc(${itemWidthSm || '100vw'} - ${theme.page.horizontal} - ${
206
226
  theme.page.horizontal
207
227
  })`,
@@ -211,13 +231,17 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
211
231
  '& .NavigationItem-item': {
212
232
  width:
213
233
  sizeSm !== 'floating'
214
- ? `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
215
- theme.spacings.md
216
- } + ${selected.length}px)`
217
- : `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
218
- theme.spacings.md
219
- } - ${theme.page.horizontal} - ${theme.page.horizontal})`,
220
- minWidth: `calc(${200}px - ${theme.spacings.md} - ${theme.spacings.md})`,
234
+ ? `calc(${itemWidthSm || '100vw'} - ${
235
+ theme.spacings[itemPadding] ?? itemPadding
236
+ } - ${theme.spacings[itemPadding] ?? itemPadding} + ${selectedLevel}px)`
237
+ : `calc(${itemWidthSm || '100vw'} - ${
238
+ theme.spacings[itemPadding] ?? itemPadding
239
+ } - ${theme.spacings[itemPadding] ?? itemPadding} - ${
240
+ theme.page.horizontal
241
+ } - ${theme.page.horizontal})`,
242
+ minWidth: `calc(${200}px - ${theme.spacings[itemPadding] ?? itemPadding} - ${
243
+ theme.spacings[itemPadding] ?? itemPadding
244
+ })`,
221
245
  },
222
246
  },
223
247
  })}
@@ -226,7 +250,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
226
250
  className={classes.navigation}
227
251
  sx={[
228
252
  (theme) => ({
229
- py: theme.spacings.md,
253
+ py: theme.spacings[itemPadding] ?? itemPadding,
230
254
  display: 'grid',
231
255
  gridAutoFlow: 'column',
232
256
  scrollSnapAlign: 'end',
@@ -238,11 +262,11 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
238
262
  },
239
263
  '& .Navigation-column': {},
240
264
  '& .NavigationItem-item': {
241
- mx: theme.spacings.md,
265
+ mx: theme.spacings[itemPadding] ?? itemPadding,
242
266
  whiteSpace: 'nowrap',
243
267
  },
244
268
  '& .NavigationItem-item.first': {
245
- // mt: theme.spacings.md,
269
+ // mt: paddingMd,
246
270
  },
247
271
  '& .Navigation-column:first-of-type': {
248
272
  boxShadow: 'none',
@@ -251,7 +275,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
251
275
  ...(Array.isArray(sx) ? sx : [sx]),
252
276
  ]}
253
277
  >
254
- {selected.length >= 0 && (
278
+ {selectedLevel >= 0 && (
255
279
  <Box
256
280
  sx={(theme) => ({
257
281
  gridArea: '1 / 1 / 999 / 2',
@@ -260,7 +284,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
260
284
  className={classes.column}
261
285
  />
262
286
  )}
263
- {selected.length >= 1 && (
287
+ {selectedLevel >= 1 && (
264
288
  <Box
265
289
  sx={(theme) => ({
266
290
  gridArea: '1 / 2 / 999 / 3',
@@ -269,7 +293,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
269
293
  className={classes.column}
270
294
  />
271
295
  )}
272
- {selected.length >= 2 && (
296
+ {selectedLevel >= 2 && (
273
297
  <Box
274
298
  sx={(theme) => ({
275
299
  gridArea: '1 / 3 / 999 / 4',
@@ -278,7 +302,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
278
302
  className={classes.column}
279
303
  />
280
304
  )}
281
- {selected.length >= 3 && (
305
+ {selectedLevel >= 3 && (
282
306
  <Box
283
307
  sx={(theme) => ({
284
308
  gridArea: '1 / 4 / 999 / 5',
@@ -294,4 +318,8 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
294
318
  </MotionDiv>
295
319
  </Overlay>
296
320
  )
321
+ })
322
+
323
+ if (process.env.NODE_ENV !== 'production') {
324
+ NavigationOverlay.displayName = 'NavigationOverlay'
297
325
  }
@@ -1,13 +1,11 @@
1
- import { useEventCallback } from '@mui/material'
2
- import { MotionConfig } from 'framer-motion'
3
- import { useState, useMemo, SetStateAction, useRef } from 'react'
1
+ import { MotionConfig, useMotionValue } from 'framer-motion'
2
+ import React, { useMemo } from 'react'
4
3
  import { isElement } from 'react-is'
5
4
  import {
6
5
  NavigationNode,
7
- NavigationPath,
8
6
  NavigationContextType,
9
7
  NavigationContext,
10
- NavigationSelect,
8
+ UseNavigationSelection,
11
9
  } from '../hooks/useNavigation'
12
10
 
13
11
  export type NavigationProviderProps = {
@@ -16,51 +14,35 @@ export type NavigationProviderProps = {
16
14
  closeAfterNavigate?: boolean
17
15
  children?: React.ReactNode
18
16
  animationDuration?: number
19
- selected: NavigationPath
20
- setSelected: (value: SetStateAction<NavigationPath>) => void
21
- onChange?: NavigationSelect
22
- onClose?: NavigationContextType['onClose']
17
+ selection: UseNavigationSelection
23
18
  }
24
19
 
25
20
  const nonNullable = <T,>(value: T): value is NonNullable<T> => value !== null && value !== undefined
26
21
 
27
- export function NavigationProvider(props: NavigationProviderProps) {
22
+ export const NavigationProvider = React.memo<NavigationProviderProps>((props) => {
28
23
  const {
29
24
  items,
30
- onChange,
31
25
  hideRootOnNavigate = true,
32
26
  closeAfterNavigate = false,
33
27
  animationDuration = 0.275,
34
28
  children,
35
- onClose: onCloseUnstable,
36
- selected,
37
- setSelected,
29
+ selection,
38
30
  } = props
39
31
 
40
- const animating = useRef(false)
41
-
42
- const select = useEventCallback((incomming: NavigationPath) => {
43
- setSelected(incomming)
44
- onChange?.(incomming)
45
- })
46
-
47
- const onClose: NavigationContextType['onClose'] = useEventCallback((e, href) => {
48
- onCloseUnstable?.(e, href)
49
- setTimeout(() => select([]), animationDuration * 1000)
50
- })
32
+ const animating = useMotionValue(false)
33
+ const closing = useMotionValue(false)
51
34
 
52
35
  const value = useMemo<NavigationContextType>(
53
36
  () => ({
54
37
  hideRootOnNavigate,
55
- selected,
56
- select,
38
+ selection,
57
39
  animating,
40
+ closing,
58
41
  items: items
59
42
  .map((item, index) => (isElement(item) ? { id: item.key ?? index, component: item } : item))
60
43
  .filter(nonNullable),
61
- onClose,
62
44
  }),
63
- [hideRootOnNavigate, selected, select, items, onClose],
45
+ [hideRootOnNavigate, selection, animating, closing, items],
64
46
  )
65
47
 
66
48
  return (
@@ -68,4 +50,4 @@ export function NavigationProvider(props: NavigationProviderProps) {
68
50
  <NavigationContext.Provider value={value}>{children}</NavigationContext.Provider>
69
51
  </MotionConfig>
70
52
  )
71
- }
53
+ })
@@ -1,23 +1,24 @@
1
- import { createContext, MutableRefObject, SetStateAction, useContext } from 'react'
1
+ import { MotionValue, useMotionValue } from 'framer-motion'
2
+ import { createContext, MutableRefObject, useContext } from 'react'
2
3
 
3
4
  export type NavigationId = string | number
4
5
  export type NavigationPath = NavigationId[]
5
- export type NavigationSelect = (selected: NavigationPath) => void
6
6
  export type NavigationRender = React.FC<
7
7
  (NavigationNodeComponent | NavigationNodeHref) & { children?: React.ReactNode }
8
8
  >
9
9
 
10
+ export type UseNavigationSelection = MotionValue<NavigationPath | false>
11
+
10
12
  export type NavigationOnClose = (
11
13
  event?: React.MouseEvent<HTMLAnchorElement>,
12
14
  href?: string | undefined,
13
15
  ) => void
14
16
  export type NavigationContextType = {
15
- selected: NavigationPath
16
- select: NavigationSelect
17
+ selection: UseNavigationSelection
17
18
  items: NavigationNode[]
18
19
  hideRootOnNavigate: boolean
19
- onClose: NavigationOnClose
20
- animating: MutableRefObject<boolean>
20
+ animating: MotionValue<boolean>
21
+ closing: MotionValue<boolean>
21
22
  }
22
23
 
23
24
  type NavigationNodeBase = {
@@ -57,3 +58,29 @@ export const NavigationContext = createContext(undefined as unknown as Navigatio
57
58
  export function useNavigation() {
58
59
  return useContext(NavigationContext)
59
60
  }
61
+
62
+ /**
63
+ * To prevent excessive rerenders we're not using plain React useState, but we're using a reactive
64
+ * motion value (could easily be any other reactive variable like Zustand, MobX, etc).
65
+ *
66
+ * Usage:
67
+ *
68
+ * ```tsx
69
+ * const selection = useNavigationSelection()
70
+ *
71
+ * function onClose() {
72
+ * selection.set(false)
73
+ * }
74
+ *
75
+ * function openRoot() {
76
+ * selection.set([])
77
+ * }
78
+ *
79
+ * function openPath() {
80
+ * selection.set(['my-path'])
81
+ * }
82
+ * ```
83
+ */
84
+ export function useNavigationSelection(): UseNavigationSelection {
85
+ return useMotionValue<NavigationPath | false>(false)
86
+ }
@@ -6,7 +6,7 @@ import { OverlayBase, LayoutOverlayBaseProps } from './OverlayBase'
6
6
 
7
7
  export type OverlayProps = Omit<
8
8
  SetOptional<LayoutOverlayBaseProps, 'variantSm' | 'variantMd'>,
9
- 'direction' | 'offsetPageY' | 'isPresent' | 'safeToRemove'
9
+ 'direction' | 'offsetPageY' | 'isPresent'
10
10
  >
11
11
 
12
12
  export function Overlay(props: OverlayProps) {
@@ -50,7 +50,6 @@ export function Overlay(props: OverlayProps) {
50
50
  direction={1}
51
51
  active={active}
52
52
  isPresent={active}
53
- safeToRemove={undefined}
54
53
  {...otherProps}
55
54
  >
56
55
  {children}
@@ -39,7 +39,7 @@ export type LayoutOverlayBaseProps = {
39
39
  onClosed: () => void
40
40
  offsetPageY: number
41
41
  isPresent: boolean
42
- safeToRemove: (() => void) | null | undefined
42
+ safeToRemove?: (() => void) | null | undefined
43
43
  overlayPaneProps?: MotionProps
44
44
  } & StyleProps &
45
45
  OverridableProps
@@ -115,14 +115,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
115
115
  useIsomorphicLayoutEffect(() => {
116
116
  const scroller = scrollerRef.current
117
117
 
118
- if (!scroller) return undefined
119
-
120
- if (!isPresent && position.get() === OverlayPosition.UNOPENED) {
121
- scroller.scrollLeft = positions.closed.x.get()
122
- scroller.scrollTop = positions.closed.y.get()
123
- }
124
-
125
- if (!isPresent) return undefined
118
+ if (!scroller || !isPresent) return undefined
126
119
 
127
120
  const open = { x: positions.open.x.get(), y: positions.open.y.get() }
128
121
 
@@ -170,16 +163,23 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
170
163
 
171
164
  // When the overlay is closed by navigating away, we're closing the overlay.
172
165
  useEffect(() => {
173
- if (isPresent || position.get() === OverlayPosition.UNOPENED) return
174
- position.set(OverlayPosition.CLOSED)
175
- clearScrollLock()
166
+ const scroller = scrollerRef.current
167
+ if (isPresent || !scroller) return
176
168
 
177
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
178
- scrollTo({
179
- x: positions.closed.x.get(),
180
- y: positions.closed.y.get(),
181
- }).then(() => safeToRemove?.())
182
- }, [isPresent, position, positions, safeToRemove, scrollTo])
169
+ if (position.get() === OverlayPosition.UNOPENED) {
170
+ position.set(OverlayPosition.CLOSED)
171
+ clearScrollLock()
172
+ scroller.scrollLeft = positions.closed.x.get()
173
+ scroller.scrollTop = positions.closed.y.get()
174
+ safeToRemove?.()
175
+ } else {
176
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
177
+ scrollTo({
178
+ x: positions.closed.x.get(),
179
+ y: positions.closed.y.get(),
180
+ }).then(() => safeToRemove?.())
181
+ }
182
+ }, [isPresent, position, positions, safeToRemove, scrollTo, scrollerRef])
183
183
 
184
184
  // Only go back to a previous page if the overlay isn't closed.
185
185
  const closeOverlay = useCallback(() => {
@@ -384,7 +384,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
384
384
  [theme.breakpoints.down('md')]: {
385
385
  minWidth: '80vw',
386
386
  '&:not(.sizeMdFull)': {
387
- width: 'max-content',
387
+ width: 'auto',
388
388
  },
389
389
 
390
390
  '&.variantSmBottom.sizeSmFull': {
@@ -26,6 +26,7 @@ export function useOverlayPosition() {
26
26
 
27
27
  const measure = () => {
28
28
  const positions = getScrollSnapPositions()
29
+
29
30
  state.open.x.set(positions.x[1] ?? 0)
30
31
  state.closed.x.set(positions.x[0])
31
32
  state.open.y.set(positions.y[1] ?? 0)
@@ -50,10 +51,14 @@ export function useOverlayPosition() {
50
51
 
51
52
  const xC = state.closed.x.get()
52
53
  const xO = state.open.x.get()
54
+
53
55
  const visX = xO === xC ? 1 : Math.max(0, Math.min(1, (x - xC) / (xO - xC)))
54
56
 
57
+ let vis = visY * visX
58
+ if (xC === 0 && xO === 0 && yC === 0 && yO === 0) vis = 0
59
+
55
60
  // todo: visibility sometimes flickers
56
- state.open.visible.set(visY * visX)
61
+ state.open.visible.set(vis)
57
62
  }
58
63
 
59
64
  const cancelY = scroll.y.onChange(calc)
package/Page/types.ts CHANGED
@@ -3,7 +3,10 @@ import { UpPage } from '@graphcommerce/framer-next-pages/types'
3
3
  // todo: remove references to GraphQL
4
4
  // eslint-disable-next-line import/no-extraneous-dependencies
5
5
  import { NormalizedCacheObject } from '@graphcommerce/graphql'
6
- import { GetStaticProps as GetStaticPropsNext } from 'next'
6
+ import {
7
+ GetStaticProps as GetStaticPropsNext,
8
+ GetServerSideProps as GetServerSidePropsNext,
9
+ } from 'next'
7
10
 
8
11
  type AnyObj = Record<string, unknown>
9
12
 
@@ -14,3 +17,9 @@ export type GetStaticProps<
14
17
  P extends AnyObj = AnyObj,
15
18
  Q extends ParsedUrlQuery = ParsedUrlQuery,
16
19
  > = GetStaticPropsNext<P & Omit<PL, 'children'> & ApolloStateProps, Q>
20
+
21
+ export type GetServerSideProps<
22
+ PL extends AnyObj,
23
+ P extends AnyObj = AnyObj,
24
+ Q extends ParsedUrlQuery = ParsedUrlQuery,
25
+ > = GetServerSidePropsNext<P & Omit<PL, 'children'> & ApolloStateProps, Q>
@@ -3,6 +3,7 @@ import { m, useTransform } from 'framer-motion'
3
3
  import React from 'react'
4
4
  import { useScrollY } from '../../Layout/hooks/useScrollY'
5
5
  import { extendableComponent } from '../../Styles'
6
+ import { breakpointVal } from '../../Styles/breakpointVal'
6
7
  import { responsiveVal } from '../../Styles/responsiveVal'
7
8
  import { Row } from '../Row'
8
9
 
@@ -84,9 +85,11 @@ export function HeroBanner(props: HeroBannerProps) {
84
85
  width: '100%',
85
86
  height: '100%',
86
87
  [theme.breakpoints.down('md')]: {
87
- borderRadius: responsiveVal(
88
+ ...breakpointVal(
89
+ 'borderRadius',
88
90
  theme.shape.borderRadius * 2,
89
91
  theme.shape.borderRadius * 3,
92
+ theme.breakpoints.values,
90
93
  ),
91
94
  },
92
95
  },
@@ -99,9 +102,11 @@ export function HeroBanner(props: HeroBannerProps) {
99
102
  style={{ width: !matches ? width : 0, borderRadius }}
100
103
  className={classes.animated}
101
104
  sx={(theme) => ({
102
- borderRadius: responsiveVal(
105
+ ...breakpointVal(
106
+ 'borderRadius',
103
107
  theme.shape.borderRadius * 2,
104
108
  theme.shape.borderRadius * 3,
109
+ theme.breakpoints.values,
105
110
  ),
106
111
  overflow: 'hidden',
107
112
  transform: 'translateZ(0)',
@@ -1,7 +1,7 @@
1
1
  import { Box, SxProps, Theme } from '@mui/material'
2
2
  import React from 'react'
3
3
  import { extendableComponent } from '../../Styles'
4
- import { responsiveVal } from '../../Styles/responsiveVal'
4
+ import { breakpointVal } from '../../Styles/breakpointVal'
5
5
  import { Row } from '../Row'
6
6
 
7
7
  export type ImageTextProps = {
@@ -37,7 +37,12 @@ export function ImageText(props: ImageTextProps) {
37
37
  background: 'none',
38
38
  gridTemplateColumns: '1fr 1fr',
39
39
  },
40
- borderRadius: responsiveVal(theme.shape.borderRadius * 2, theme.shape.borderRadius * 3),
40
+ ...breakpointVal(
41
+ 'borderRadius',
42
+ theme.shape.borderRadius * 2,
43
+ theme.shape.borderRadius * 3,
44
+ theme.breakpoints.values,
45
+ ),
41
46
  })}
42
47
  >
43
48
  <Box
@@ -49,9 +54,11 @@ export function ImageText(props: ImageTextProps) {
49
54
  height: '100%',
50
55
  width: '100%',
51
56
  objectFit: 'cover',
52
- borderRadius: responsiveVal(
57
+ ...breakpointVal(
58
+ 'borderRadius',
53
59
  theme.shape.borderRadius * 2,
54
60
  theme.shape.borderRadius * 3,
61
+ theme.breakpoints.values,
55
62
  ),
56
63
  },
57
64
  })}