@graphcommerce/next-ui 4.19.0 → 4.22.0

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 (41) hide show
  1. package/ActionCard/ActionCard.tsx +46 -12
  2. package/ActionCard/ActionCardList.tsx +17 -15
  3. package/ActionCard/ActionCardListForm.tsx +1 -2
  4. package/AnimatedRow/AnimatedRow.tsx +3 -3
  5. package/Blog/BlogHeader/BlogHeader.tsx +7 -1
  6. package/Blog/BlogListItem/BlogListItem.tsx +5 -1
  7. package/Blog/BlogTags/BlogTag.tsx +2 -3
  8. package/CHANGELOG.md +57 -0
  9. package/Footer/Footer.tsx +3 -2
  10. package/Form/Form.tsx +7 -1
  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/Navigation/components/NavigationItem.tsx +31 -17
  20. package/Navigation/components/NavigationList.tsx +21 -15
  21. package/Navigation/components/NavigationOverlay.tsx +48 -27
  22. package/Navigation/components/NavigationProvider.tsx +12 -30
  23. package/Navigation/hooks/useNavigation.ts +33 -6
  24. package/Overlay/components/Overlay.tsx +1 -2
  25. package/Overlay/components/OverlayBase.tsx +27 -23
  26. package/Overlay/hooks/useOverlayPosition.ts +6 -1
  27. package/Row/HeroBanner/HeroBanner.tsx +7 -2
  28. package/Row/ImageText/ImageText.tsx +10 -3
  29. package/Row/ImageTextBoxed/ImageTextBoxed.tsx +7 -1
  30. package/Row/ParagraphWithSidebarSlide/ParagraphWithSidebarSlide.tsx +4 -2
  31. package/Row/SpecialBanner/SpecialBanner.tsx +10 -2
  32. package/Snackbar/MessageSnackbarImpl.tsx +9 -5
  33. package/Stepper/Stepper.tsx +1 -1
  34. package/Styles/breakpointVal.tsx +1 -1
  35. package/TextInputNumber/TextInputNumber.tsx +5 -4
  36. package/Theme/MuiButton.ts +12 -3
  37. package/Theme/MuiFab.ts +2 -2
  38. package/ToggleButton/ToggleButton.tsx +15 -5
  39. package/hooks/index.ts +1 -0
  40. package/hooks/useMemoDeep.ts +15 -0
  41. package/package.json +6 -6
@@ -1,6 +1,7 @@
1
- import { SxProps, ButtonBase, Box, Theme } from '@mui/material'
1
+ import { SxProps, ButtonBase, Box, Theme, alpha } from '@mui/material'
2
2
  import React, { FormEvent } from 'react'
3
3
  import { extendableComponent } from '../Styles'
4
+ import { breakpointVal } from '../Styles/breakpointVal'
4
5
 
5
6
  export type ActionCardProps = {
6
7
  sx?: SxProps<Theme>
@@ -94,12 +95,32 @@ export function ActionCard(props: ActionCardProps) {
94
95
  border: `1px solid ${theme.palette.divider}`,
95
96
  borderBottomColor: `transparent`,
96
97
  '&:first-of-type': {
97
- borderTopLeftRadius: theme.shape.borderRadius,
98
- borderTopRightRadius: theme.shape.borderRadius,
98
+ ...breakpointVal(
99
+ 'borderTopLeftRadius',
100
+ theme.shape.borderRadius * 3,
101
+ theme.shape.borderRadius * 4,
102
+ theme.breakpoints.values,
103
+ ),
104
+ ...breakpointVal(
105
+ 'borderTopRightRadius',
106
+ theme.shape.borderRadius * 3,
107
+ theme.shape.borderRadius * 4,
108
+ theme.breakpoints.values,
109
+ ),
99
110
  },
100
111
  '&:last-of-type': {
101
- borderBottomLeftRadius: theme.shape.borderRadius,
102
- borderBottomRightRadius: theme.shape.borderRadius,
112
+ ...breakpointVal(
113
+ 'borderBottomLeftRadius',
114
+ theme.shape.borderRadius * 3,
115
+ theme.shape.borderRadius * 4,
116
+ theme.breakpoints.values,
117
+ ),
118
+ ...breakpointVal(
119
+ 'borderBottomRightRadius',
120
+ theme.shape.borderRadius * 3,
121
+ theme.shape.borderRadius * 4,
122
+ theme.breakpoints.values,
123
+ ),
103
124
  borderBottom: `1px solid ${theme.palette.divider}`,
104
125
  },
105
126
  }),
@@ -118,15 +139,27 @@ export function ActionCard(props: ActionCardProps) {
118
139
  selected &&
119
140
  ((theme) => ({
120
141
  border: `2px solid ${theme.palette.secondary.main} !important`,
121
- borderTopLeftRadius: theme.shape.borderRadius,
122
- borderTopRightRadius: theme.shape.borderRadius,
123
- borderBottomLeftRadius: theme.shape.borderRadius,
124
- borderBottomRightRadius: theme.shape.borderRadius,
142
+ boxShadow: `0 0 0 4px ${alpha(
143
+ theme.palette.secondary.main,
144
+ theme.palette.action.hoverOpacity,
145
+ )} !important`,
146
+ ...breakpointVal(
147
+ 'borderRadius',
148
+ theme.shape.borderRadius * 3,
149
+ theme.shape.borderRadius * 4,
150
+ theme.breakpoints.values,
151
+ ),
125
152
  padding: `${theme.spacings.xxs} ${theme.spacings.xs}`,
126
153
  })),
127
154
  disabled &&
128
155
  ((theme) => ({
129
- background: theme.palette.background.default,
156
+ '& *': {
157
+ opacity: theme.palette.action.disabledOpacity,
158
+ },
159
+ background: alpha(
160
+ theme.palette.action.disabledBackground,
161
+ theme.palette.action.disabledOpacity / 10,
162
+ ),
130
163
  })),
131
164
 
132
165
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -156,10 +189,11 @@ export function ActionCard(props: ActionCardProps) {
156
189
  {details && (
157
190
  <Box
158
191
  className={classes.details}
159
- sx={{
192
+ sx={(theme) => ({
193
+ typography: 'body2',
160
194
  gridArea: 'details',
161
195
  color: 'text.secondary',
162
- }}
196
+ })}
163
197
  >
164
198
  {details}
165
199
  </Box>
@@ -1,8 +1,6 @@
1
- import { Alert, Box, FormHelperText } from '@mui/material'
2
- import { AnimatePresence } from 'framer-motion'
1
+ import { Alert, Box } from '@mui/material'
3
2
  import React from 'react'
4
3
  import { isFragment } from 'react-is'
5
- import { AnimatedRow } from '../AnimatedRow/AnimatedRow'
6
4
  import { ActionCardProps } from './ActionCard'
7
5
 
8
6
  type MultiSelect = {
@@ -67,7 +65,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
67
65
  }
68
66
 
69
67
  type ActionCardLike = React.ReactElement<
70
- Pick<ActionCardProps, 'value' | 'selected' | 'disabled' | 'onClick'>
68
+ Pick<ActionCardProps, 'value' | 'selected' | 'disabled' | 'onClick' | 'hidden'>
71
69
  >
72
70
  function isActionCardLike(el: React.ReactElement): el is ActionCardLike {
73
71
  const hasValue = (el as ActionCardLike).props.value
@@ -113,18 +111,14 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
113
111
  borderRight: 2,
114
112
  borderLeftColor: 'error.main',
115
113
  borderRightColor: 'error.main',
116
- paddingLeft: theme.spacings.xs,
117
- paddingRight: theme.spacings.xs,
118
114
  },
119
115
  '& > div:first-of-type.ActionCard-root': {
120
116
  borderTop: 2,
121
117
  borderTopColor: 'error.main',
122
- paddingTop: theme.spacings.xxs,
123
118
  },
124
119
  '& > div:last-of-type.ActionCard-root': {
125
120
  borderBottom: 2,
126
121
  borderBottomColor: 'error.main',
127
- paddingBottom: theme.spacings.xxs,
128
122
  },
129
123
  })),
130
124
  ]}
@@ -132,6 +126,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
132
126
  {childReactNodes.map((child) =>
133
127
  React.cloneElement(child, {
134
128
  onClick: handleChange,
129
+ hidden: !!value && value !== child.props.value,
135
130
  selected:
136
131
  child.props.selected === undefined
137
132
  ? isValueSelected(child.props.value, value)
@@ -139,13 +134,20 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
139
134
  }),
140
135
  )}
141
136
  {error && (
142
- <Alert
143
- severity='error'
144
- variant='filled'
145
- sx={{ borderStartStartRadius: 0, borderStartEndRadius: 0 }}
146
- >
147
- {errorMessage}
148
- </Alert>
137
+ <Box component='span'>
138
+ <Alert
139
+ severity='error'
140
+ variant='standard'
141
+ sx={(theme) => ({
142
+ marginTop: 0.5,
143
+ borderStartStartRadius: 0,
144
+ borderStartEndRadius: 0,
145
+ borderRadius: theme.shape.borderRadius * 1,
146
+ })}
147
+ >
148
+ {errorMessage}
149
+ </Alert>
150
+ </Box>
149
151
  )}
150
152
  </Box>
151
153
  )
@@ -34,7 +34,7 @@ export function ActionCardListForm<T extends ActionCardItemBase>(
34
34
  control={control}
35
35
  name={name}
36
36
  rules={{ required, ...rules, validate: (v) => (v ? true : errorMessage) }}
37
- render={({ field: { onChange, value, onBlur, ref }, fieldState, formState }) => (
37
+ render={({ field: { onChange, value, ref }, fieldState, formState }) => (
38
38
  <ActionCardList
39
39
  required
40
40
  value={value}
@@ -49,7 +49,6 @@ export function ActionCardListForm<T extends ActionCardItemBase>(
49
49
  key={item.value}
50
50
  value={item.value}
51
51
  selected={value === item.value}
52
- hidden={!!value && value !== item.value}
53
52
  onReset={(e) => {
54
53
  e.preventDefault()
55
54
  onChange(null)
@@ -4,17 +4,17 @@ import { ReactHTML } from 'react'
4
4
 
5
5
  export type AnimatedRowProps = Omit<
6
6
  ReactHTML['div'] & HTMLMotionProps<'div'>,
7
- 'layout' | 'initial' | 'animate' | 'exit' | 'transition'
7
+ 'initial' | 'animate' | 'exit' | 'transition'
8
8
  > & { sx?: SxProps<Theme> }
9
9
 
10
10
  const StyledDiv = styled(m.div)({})
11
11
 
12
+ /** @deprecated Should be replaced with Box component */
12
13
  export function AnimatedRow(props: AnimatedRowProps) {
13
14
  return (
14
15
  <StyledDiv
15
16
  {...props}
16
- layout
17
- initial={{ opacity: 0, height: 0 }}
17
+ initial={{ opacity: 0, height: 'auto' }}
18
18
  animate={{ opacity: 1, height: 'auto' }}
19
19
  exit={{ opacity: 0, height: 0 }}
20
20
  transition={{ type: 'tween' }}
@@ -1,6 +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 { breakpointVal } from '../../Styles/breakpointVal'
4
5
  import { responsiveVal } from '../../Styles/responsiveVal'
5
6
 
6
7
  export type BlogHeaderProps = {
@@ -24,7 +25,12 @@ export function BlogHeader(props: BlogHeaderProps) {
24
25
  margin: `0 auto`,
25
26
  marginBottom: theme.spacings.md,
26
27
  '& img': {
27
- borderRadius: responsiveVal(theme.shape.borderRadius * 2, theme.shape.borderRadius * 3),
28
+ ...breakpointVal(
29
+ 'borderRadius',
30
+ theme.shape.borderRadius * 2,
31
+ theme.shape.borderRadius * 3,
32
+ theme.breakpoints.values,
33
+ ),
28
34
  },
29
35
  }),
30
36
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -2,6 +2,7 @@ import { Box, Link, SxProps, Theme, Typography } from '@mui/material'
2
2
  import PageLink from 'next/link'
3
3
  import React from 'react'
4
4
  import { extendableComponent } from '../../Styles'
5
+ import { breakpointVal } from '../../Styles/breakpointVal'
5
6
  import { responsiveVal } from '../../Styles/responsiveVal'
6
7
  import { useDateTimeFormat } from '../../hooks'
7
8
 
@@ -43,12 +44,15 @@ export function BlogListItem(props: BlogListItemProps) {
43
44
  className={classes.asset}
44
45
  sx={(theme) => ({
45
46
  display: 'grid',
47
+ alignContent: 'center',
46
48
  overflow: 'hidden',
47
49
  height: '100%',
48
50
  width: '100%',
49
- borderRadius: responsiveVal(
51
+ ...breakpointVal(
52
+ 'borderRadius',
50
53
  theme.shape.borderRadius * 2,
51
54
  theme.shape.borderRadius * 3,
55
+ theme.breakpoints.values,
52
56
  ),
53
57
  '& img': {
54
58
  height: '100% !important',
@@ -15,9 +15,8 @@ export function BlogTag(props: BlogTagsProps) {
15
15
  label={title}
16
16
  sx={[
17
17
  {
18
- marginRight: '8px',
19
- borderRadius: '4px',
20
- fontSize: '14px',
18
+ marginRight: 3,
19
+ borderRadius: 2,
21
20
  },
22
21
  ...(Array.isArray(sx) ? sx : [sx]),
23
22
  ]}
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1610](https://github.com/graphcommerce-org/graphcommerce/pull/1610) [`bb94e7045`](https://github.com/graphcommerce-org/graphcommerce/commit/bb94e7045460cb671c45d612a0833731d7c20c30) Thanks [@mikekeehnen](https://github.com/mikekeehnen)! - Previously when the persisted selected value didn't exist in the list of ActionCard items, all items would be hidden. In this fix we set the hidden prop in the ActionCardList component, where we check if the value exists, if not, we display all items
8
+
9
+ * [#1602](https://github.com/graphcommerce-org/graphcommerce/pull/1602) [`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Default styles and layout fixes
10
+
11
+ - Scaled icons and fonts down. Size in typography is now more gradual: https://graphcommerce.vercel.app/test/typography
12
+ - Multiple accessibility fixes. Missing button/input labels, and fixed spacing issues resulting in high % appropriately sized tap targets
13
+ - Replaced responsiveVal usage with better performaning breakpointVal where possible
14
+ - All buttons are now Pill by default.
15
+ - Cleaned up checkout styles
16
+
17
+ ### Patch Changes
18
+
19
+ - [#1601](https://github.com/graphcommerce-org/graphcommerce/pull/1601) [`04708dacc`](https://github.com/graphcommerce-org/graphcommerce/commit/04708daccc213c6ea927bc67fa3bd0d5b1fad619) Thanks [@paales](https://github.com/paales)! - Navigation now uses a single `const selection = useNavigationSelection()` motionValue to control the state of the menu, to prevent excessive rerenders.
20
+
21
+ * [#1611](https://github.com/graphcommerce-org/graphcommerce/pull/1611) [`b0dc4e2e1`](https://github.com/graphcommerce-org/graphcommerce/commit/b0dc4e2e1982d502d38dd50a0f493396360a7a15) Thanks [@FrankHarland](https://github.com/FrankHarland)! - Fix overlay doule click
22
+
23
+ - [#1609](https://github.com/graphcommerce-org/graphcommerce/pull/1609) [`4a5286dfe`](https://github.com/graphcommerce-org/graphcommerce/commit/4a5286dfeaa1719e594a0078f274fbab53969c4e) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Solve issue where navigation back would happen twice when closing an overlay
24
+
25
+ * [#1601](https://github.com/graphcommerce-org/graphcommerce/pull/1601) [`d46d5ed0c`](https://github.com/graphcommerce-org/graphcommerce/commit/d46d5ed0cc5794391b7527fc17bbb68ec2212e33) Thanks [@paales](https://github.com/paales)! - Move to newer useScroll hook to watch body scroll, prevents deprecation warnings.
26
+
27
+ * Updated dependencies [[`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315), [`01372b918`](https://github.com/graphcommerce-org/graphcommerce/commit/01372b918a291e01cbf5db40edcb40fb1c2af313)]:
28
+ - @graphcommerce/framer-next-pages@3.3.0
29
+ - @graphcommerce/framer-utils@3.2.0
30
+ - @graphcommerce/framer-scroller@2.1.33
31
+ - @graphcommerce/image@3.1.9
32
+
33
+ ## 4.21.0
34
+
35
+ ### Minor Changes
36
+
37
+ - [#1597](https://github.com/graphcommerce-org/graphcommerce/pull/1597) [`1f7ee6f6c`](https://github.com/graphcommerce-org/graphcommerce/commit/1f7ee6f6cfb28544439ed36e10929ac530d1b2b7) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Fix safari nav bug
38
+
39
+ ### Patch Changes
40
+
41
+ - Updated dependencies [[`707dbc73d`](https://github.com/graphcommerce-org/graphcommerce/commit/707dbc73d181204d88fdbbd2e09340e25b2b5f7b), [`5c5645e6e`](https://github.com/graphcommerce-org/graphcommerce/commit/5c5645e6eaf5314c063f05547707fcd4b34a8717)]:
42
+ - @graphcommerce/framer-utils@3.1.5
43
+ - @graphcommerce/framer-scroller@2.1.32
44
+ - @graphcommerce/framer-next-pages@3.2.5
45
+ - @graphcommerce/image@3.1.8
46
+
47
+ ## 4.20.0
48
+
49
+ ### Minor Changes
50
+
51
+ - [#1592](https://github.com/graphcommerce-org/graphcommerce/pull/1592) [`43822fd61`](https://github.com/graphcommerce-org/graphcommerce/commit/43822fd61c949215b8ddce9fb37d09f29b638426) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Animation fixes
52
+
53
+ * [#1596](https://github.com/graphcommerce-org/graphcommerce/pull/1596) [`3a619b70d`](https://github.com/graphcommerce-org/graphcommerce/commit/3a619b70d082804b8de46a8e8232f9431479a8b7) Thanks [@paales](https://github.com/paales)! - Create products sitemap
54
+
55
+ ### Patch Changes
56
+
57
+ - Updated dependencies []:
58
+ - @graphcommerce/framer-scroller@2.1.31
59
+
3
60
  ## 4.19.0
4
61
 
5
62
  ### Minor Changes
package/Footer/Footer.tsx CHANGED
@@ -43,10 +43,11 @@ export function Footer(props: FooterProps) {
43
43
  'social social'
44
44
  'links links'
45
45
  `,
46
- gap: theme.spacings.sm,
46
+ gap: theme.spacings.md,
47
47
  '& > *': { maxWidth: 'max-content' },
48
48
 
49
49
  [theme.breakpoints.up('sm')]: {
50
+ gap: theme.spacings.sm,
50
51
  gridTemplateAreas: `
51
52
  'social switcher'
52
53
  'links support'
@@ -70,7 +71,7 @@ export function Footer(props: FooterProps) {
70
71
  justifyContent: 'start',
71
72
  gridAutoFlow: 'column',
72
73
  gridArea: 'social',
73
- gap: { xs: `0 ${theme.spacings.xs}`, md: `0 ${theme.spacings.sm}` },
74
+ gap: { xs: `0 ${theme.spacings.xs}`, md: `0 ${theme.spacings.xs}` },
74
75
  '& > *': {
75
76
  minWidth: 'min-content',
76
77
  },
package/Form/Form.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import { darken, lighten, styled, Theme } from '@mui/material'
2
+ import { breakpointVal } from '../Styles/breakpointVal'
2
3
  import { responsiveVal } from '../Styles/responsiveVal'
3
4
  import { sx } from '../Theme/themeDefaults'
4
5
 
@@ -27,7 +28,12 @@ const styles = ({ theme, contained = false, background }: { theme: Theme } & For
27
28
  padding: `${theme.spacings.xxs} ${theme.spacings.sm}`,
28
29
  // paddingTop: theme.spacings.md,
29
30
  overflow: 'hidden',
30
- borderRadius: responsiveVal(theme.shape.borderRadius * 3, theme.shape.borderRadius * 4),
31
+ ...breakpointVal(
32
+ 'borderRadius',
33
+ theme.shape.borderRadius * 3,
34
+ theme.shape.borderRadius * 4,
35
+ theme.breakpoints.values,
36
+ ),
31
37
  },
32
38
  ])
33
39
 
@@ -6,5 +6,5 @@ export const FormActions = styled('div', { name: 'FormActions' })(({ theme }) =>
6
6
  justifyContent: 'center',
7
7
  display: 'grid',
8
8
  gridAutoFlow: 'column',
9
- gap: `calc(${theme.spacings.xxs} * 2)`,
9
+ gap: theme.spacings.sm,
10
10
  }))
@@ -292,11 +292,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
292
292
  },
293
293
  }}
294
294
  >
295
- <ScrollerDots
296
- layout='position'
297
- layoutDependency={zoomed}
298
- sx={{ backgroundColor: 'background.paper', boxShadow: 6 }}
299
- />
295
+ <ScrollerDots layout='position' layoutDependency={zoomed} />
300
296
  </Box>
301
297
  </MotionBox>
302
298
 
@@ -41,10 +41,10 @@ export type IconSvgProps = StyleProps &
41
41
  Pick<ComponentProps<'svg'>, 'className' | 'style'> & { sx?: SxProps<Theme> }
42
42
 
43
43
  export const sizes = {
44
- xs: rv(11, 13),
45
- small: rv(12, 16),
46
- medium: rv(22, 24),
47
- large: rv(24, 28),
44
+ xs: rv(11, 12),
45
+ small: rv(12, 15),
46
+ medium: rv(22, 23),
47
+ large: rv(24, 26),
48
48
  xl: rv(38, 62),
49
49
  xxl: rv(96, 148),
50
50
  } as const
@@ -1,4 +1,5 @@
1
1
  import { useGo, usePageContext } from '@graphcommerce/framer-next-pages'
2
+ import { i18n } from '@lingui/core'
2
3
  import { Fab } from '@mui/material'
3
4
  import { useState } from 'react'
4
5
  import { IconSvg, useIconSvgSize } from '../../IconSvg'
@@ -32,6 +33,7 @@ export function LayoutHeaderClose() {
32
33
  }}
33
34
  size='responsive'
34
35
  disabled={disabled}
36
+ aria-label={i18n._(/* i18n */ 'Close')}
35
37
  >
36
38
  <IconSvg src={iconClose} size='large' />
37
39
  </Fab>
@@ -144,7 +144,9 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
144
144
  justifyContent: 'start',
145
145
  })}
146
146
  >
147
- <MotionDiv layout={layout}>{left}</MotionDiv>
147
+ <MotionDiv layout={layout} sx={{ display: 'grid' }}>
148
+ {left}
149
+ </MotionDiv>
148
150
  </Box>
149
151
  )}
150
152
  <Box
@@ -1,6 +1,6 @@
1
1
  import { useScrollOffset } from '@graphcommerce/framer-next-pages'
2
2
  import { Box, SxProps, Theme } from '@mui/material'
3
- import { useTransform, useViewportScroll } from 'framer-motion'
3
+ import { useTransform, useScroll } from 'framer-motion'
4
4
  import { LayoutProvider } from '../../Layout/components/LayoutProvider'
5
5
  import { extendableComponent } from '../../Styles'
6
6
  import { useFabSize } from '../../Theme'
@@ -40,7 +40,7 @@ export function LayoutDefault(props: LayoutDefaultProps) {
40
40
  } = props
41
41
 
42
42
  const scrollWithOffset = useTransform(
43
- [useViewportScroll().scrollY, useScrollOffset()],
43
+ [useScroll().scrollY, useScrollOffset()],
44
44
  ([y, offset]: number[]) => y + offset,
45
45
  )
46
46
 
@@ -110,7 +110,7 @@ export function LayoutDefault(props: LayoutDefaultProps) {
110
110
  [theme.breakpoints.up('sm')]: {
111
111
  padding: `0 ${theme.page.horizontal}`,
112
112
  position: 'sticky',
113
- marginTop: `calc(${theme.appShell.headerHeightMd} * -1 + calc(${fabIconSize} / 2))`,
113
+ marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - calc(${fabIconSize} / 2))`,
114
114
  top: `calc(${theme.appShell.headerHeightMd} / 2 - (${fabIconSize} / 2))`,
115
115
  },
116
116
  [theme.breakpoints.down('md')]: {
@@ -1,6 +1,7 @@
1
1
  import { usePageContext, useGo, useScrollOffset } from '@graphcommerce/framer-next-pages'
2
2
  import { ScrollerProvider, ScrollSnapType } from '@graphcommerce/framer-scroller'
3
3
  import { useMotionValueValue } from '@graphcommerce/framer-utils'
4
+ import { useEventCallback } from '@mui/material'
4
5
  import { usePresence } from 'framer-motion'
5
6
  import type { SetOptional } from 'type-fest'
6
7
  import { OverlayBase, LayoutOverlayBaseProps } from '../../Overlay/components/OverlayBase'
@@ -28,7 +29,7 @@ export function LayoutOverlay(props: LayoutOverlayProps) {
28
29
  <OverlayBase
29
30
  active={active}
30
31
  direction={direction}
31
- onClosed={onCloseHandler}
32
+ onClosed={useEventCallback(() => isPresent && onCloseHandler())}
32
33
  offsetPageY={offsetPageY}
33
34
  variantMd={variantMd}
34
35
  variantSm={variantSm}
@@ -13,13 +13,13 @@ export function DesktopHeaderBadge(props: BadgeProps) {
13
13
  bgcolor: 'text.disabled',
14
14
  },
15
15
  '& .MuiBadge-anchorOriginTopRightCircular': {
16
- right: { xs: '3px', md: '8px' },
17
- top: { xs: '3px', md: '8px' },
16
+ right: { xs: '3px', md: '5px' },
17
+ top: { xs: '3px', md: '5px' },
18
18
  },
19
19
  '& .MuiBadge-badge': {
20
20
  typography: 'caption',
21
21
  borderRadius: '100%',
22
- padding: { xs: '3px', md: '6px' },
22
+ padding: { xs: '3px', md: '5px' },
23
23
  },
24
24
  },
25
25
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
+ import { useMotionValueValue } from '@graphcommerce/framer-utils'
2
3
  import { Box, ListItemButton, styled, Theme, useEventCallback, useMediaQuery } from '@mui/material'
3
4
  import PageLink from 'next/link'
4
- import { useEffect } from 'react'
5
+ import React from 'react'
5
6
  import { IconSvg } from '../../IconSvg'
6
7
  import { extendableComponent } from '../../Styles/extendableComponent'
7
8
  import { iconChevronRight } from '../../icons'
@@ -10,7 +11,6 @@ import {
10
11
  isNavigationComponent,
11
12
  isNavigationHref,
12
13
  NavigationNode,
13
- NavigationPath,
14
14
  useNavigation,
15
15
  } from '../hooks/useNavigation'
16
16
  import type { NavigationList } from './NavigationList'
@@ -24,7 +24,7 @@ type OwnerState = {
24
24
  }
25
25
 
26
26
  type NavigationItemProps = NavigationNode & {
27
- parentPath: NavigationPath
27
+ parentPath: string
28
28
  idx: number
29
29
  NavigationList: typeof NavigationList
30
30
  } & OwnerState &
@@ -44,16 +44,27 @@ const { withState } = extendableComponent<OwnerState, typeof componentName, type
44
44
 
45
45
  const NavigationLI = styled('li')({ display: 'contents' })
46
46
 
47
- export function NavigationItem(props: NavigationItemProps) {
47
+ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
48
48
  const { id, parentPath, idx, first, last, NavigationList, mouseEvent } = props
49
49
 
50
50
  const row = idx + 1
51
- const { selected, select, hideRootOnNavigate, onClose, animating } = useNavigation()
51
+ const { selection, hideRootOnNavigate, closing, animating } = useNavigation()
52
52
 
53
- const itemPath = [...parentPath, id]
54
- const isSelected = selected.slice(0, itemPath.length).join('/') === itemPath.join('/')
53
+ const itemPath = [...(parentPath ? parentPath.split(',') : []), id]
54
+
55
+ const isSelected = useMotionValueValue(
56
+ selection,
57
+ (s) => s !== false && s.slice(0, itemPath.length).join('/') === itemPath.join('/'),
58
+ )
59
+ const hidingRoot = useMotionValueValue(
60
+ selection,
61
+ (s) => s === false || (hideRootOnNavigate && s.length > 0),
62
+ )
63
+
64
+ const tabIndex = useMotionValueValue(selection, (s) =>
65
+ s !== false && s.join(',').includes(parentPath) ? undefined : -1,
66
+ )
55
67
 
56
- const hidingRoot = hideRootOnNavigate && selected.length > 0
57
68
  const hideItem = hidingRoot && itemPath.length === 1
58
69
 
59
70
  const column = hidingRoot ? itemPath.length - 1 : itemPath.length
@@ -61,8 +72,7 @@ export function NavigationItem(props: NavigationItemProps) {
61
72
 
62
73
  const onCloseHandler: React.MouseEventHandler<HTMLAnchorElement> = useEventCallback((e) => {
63
74
  if (!isNavigationHref(props)) return
64
- const { href } = props
65
- onClose?.(e, href)
75
+ closing.set(true)
66
76
  })
67
77
 
68
78
  const isDesktop = useMediaQuery<Theme>((theme) => theme.breakpoints.up('md'))
@@ -95,19 +105,19 @@ export function NavigationItem(props: NavigationItemProps) {
95
105
  : {},
96
106
  ]}
97
107
  disabled={isSelected}
98
- tabIndex={selected.join(',').includes(parentPath.join(',')) ? undefined : -1}
108
+ tabIndex={tabIndex}
99
109
  onClick={(e) => {
100
110
  e.preventDefault()
101
- if (!isSelected && animating.current === false) {
102
- select(itemPath)
111
+ if (!isSelected && animating.get() === false) {
112
+ selection.set(itemPath)
103
113
  }
104
114
  }}
105
115
  onMouseMove={
106
116
  itemPath.length > 1 && mouseEvent === 'hover'
107
117
  ? (e) => {
108
- if (isDesktop && animating.current === false && !isSelected) {
118
+ if (isDesktop && animating.get() === false && !isSelected) {
109
119
  e.preventDefault()
110
- setTimeout(() => select(itemPath), 0)
120
+ setTimeout(() => selection.set(itemPath), 0)
111
121
  }
112
122
  }
113
123
  : undefined
@@ -130,7 +140,7 @@ export function NavigationItem(props: NavigationItemProps) {
130
140
  <NavigationList
131
141
  items={childItems}
132
142
  selected={isSelected}
133
- parentPath={itemPath}
143
+ parentPath={itemPath.join(',')}
134
144
  mouseEvent={mouseEvent}
135
145
  />
136
146
  </NavigationLI>
@@ -150,7 +160,7 @@ export function NavigationItem(props: NavigationItemProps) {
150
160
  gridColumnStart: column,
151
161
  gap: theme.spacings.xxs,
152
162
  })}
153
- tabIndex={selected.join(',').includes(parentPath.join(',')) ? undefined : -1}
163
+ tabIndex={tabIndex}
154
164
  onClick={onCloseHandler}
155
165
  >
156
166
  <Box
@@ -183,4 +193,8 @@ export function NavigationItem(props: NavigationItemProps) {
183
193
  if (process.env.NODE_ENV !== 'production') throw Error('NavigationItem: unknown type')
184
194
 
185
195
  return null
196
+ })
197
+
198
+ if (process.env.NODE_ENV !== 'production') {
199
+ NavigationItem.displayName = 'NavigationItem'
186
200
  }