@graphcommerce/next-ui 4.11.2 → 4.13.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.
@@ -82,7 +82,7 @@ export function ActionCard(props: ActionCardProps) {
82
82
  gridTemplateAreas: `
83
83
  "image title action"
84
84
  "image details ${price ? 'price' : 'details'}"
85
- "image secondaryActio additionalDetails"
85
+ "image secondaryAction additionalDetails"
86
86
  "after after after"
87
87
  `,
88
88
  justifyContent: 'unset',
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1552](https://github.com/graphcommerce-org/graphcommerce/pull/1552) [`18054c441`](https://github.com/graphcommerce-org/graphcommerce/commit/18054c441962ba750bed3acc39ab46c8d3a341ce) Thanks [@paales](https://github.com/paales)! - Updated to Next.js v12.2.2 and other packages and made compatible
8
+
9
+ * [#1552](https://github.com/graphcommerce-org/graphcommerce/pull/1552) [`c5c539c44`](https://github.com/graphcommerce-org/graphcommerce/commit/c5c539c44eeac524cd62ce649e132d2e00333794) Thanks [@paales](https://github.com/paales)! - Make sure the gallery doesn't scroll when overlays are opened
10
+
11
+ - [#1552](https://github.com/graphcommerce-org/graphcommerce/pull/1552) [`6f69bc54c`](https://github.com/graphcommerce-org/graphcommerce/commit/6f69bc54c6e0224452817c532ae58d9c332b61ea) Thanks [@paales](https://github.com/paales)! - Prevent back button scrolling when navigating between overlays
12
+
13
+ * [#1552](https://github.com/graphcommerce-org/graphcommerce/pull/1552) [`21886d6fa`](https://github.com/graphcommerce-org/graphcommerce/commit/21886d6fa64a48d9e932bfaf8d138c9b13c36e43) Thanks [@paales](https://github.com/paales)! - Fix page stacking and scroll restoration when navigating
14
+
15
+ * Updated dependencies [[`18054c441`](https://github.com/graphcommerce-org/graphcommerce/commit/18054c441962ba750bed3acc39ab46c8d3a341ce), [`21886d6fa`](https://github.com/graphcommerce-org/graphcommerce/commit/21886d6fa64a48d9e932bfaf8d138c9b13c36e43), [`b4936e961`](https://github.com/graphcommerce-org/graphcommerce/commit/b4936e96175fe80717895822e245274db05638bd)]:
16
+ - @graphcommerce/framer-next-pages@3.2.4
17
+ - @graphcommerce/framer-scroller@2.1.23
18
+
19
+ ## 4.13.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [#1522](https://github.com/graphcommerce-org/graphcommerce/pull/1522) [`8d8fda262`](https://github.com/graphcommerce-org/graphcommerce/commit/8d8fda2623e561cb43441110c67ffa34b692668a) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Introducing a new Navigation component that builds on the existing navigation component and tries to address the 'mega menu' question where there are tons of categories that need to be navigated quickly.
24
+
25
+ * [#1522](https://github.com/graphcommerce-org/graphcommerce/pull/1522) [`cefa7b365`](https://github.com/graphcommerce-org/graphcommerce/commit/cefa7b3652b55108d2178927e3c5d98a111cf373) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Introducting a new Overlay component with is the generic part of LayoutOverlay into OverlayBase. The new Overlay is used to render the new Navigation component.
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies [[`584b683a2`](https://github.com/graphcommerce-org/graphcommerce/commit/584b683a2aedcdf5067644c8dcc0e63a5b9e894c)]:
30
+ - @graphcommerce/framer-scroller@2.1.22
31
+
32
+ ## 4.12.0
33
+
34
+ ### Minor Changes
35
+
36
+ - [#1534](https://github.com/graphcommerce-org/graphcommerce/pull/1534) [`c756f42e5`](https://github.com/graphcommerce-org/graphcommerce/commit/c756f42e503761a497e4a5a7a02d02141df231c3) Thanks [@mikekeehnen](https://github.com/mikekeehnen)! - Added the method title to the action card title for shipping methods.
37
+
38
+ ### Patch Changes
39
+
40
+ - Updated dependencies []:
41
+ - @graphcommerce/framer-scroller@2.1.21
42
+
3
43
  ## 4.11.2
4
44
 
5
45
  ### Patch Changes
@@ -119,6 +119,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
119
119
  <Row maxWidth={false} disableGutters className={classes.row} sx={sx}>
120
120
  <MotionBox
121
121
  layout
122
+ layoutDependency={zoomed}
122
123
  className={classes.root}
123
124
  sx={[
124
125
  {
@@ -146,6 +147,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
146
147
  >
147
148
  <MotionBox
148
149
  layout
150
+ layoutDependency={zoomed}
149
151
  className={classes.scrollerContainer}
150
152
  sx={[
151
153
  {
@@ -193,6 +195,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
193
195
  <MotionImageAspect
194
196
  key={typeof image.src === 'string' ? image.src : idx}
195
197
  layout
198
+ layoutDependency={zoomed}
196
199
  src={image.src}
197
200
  width={image.width}
198
201
  height={image.height}
@@ -209,6 +212,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
209
212
  </Scroller>
210
213
  <MotionBox
211
214
  layout
215
+ layoutDependency={zoomed}
212
216
  className={classes.topRight}
213
217
  sx={{
214
218
  display: 'grid',
@@ -243,6 +247,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
243
247
  >
244
248
  <ScrollerButton
245
249
  layout
250
+ layoutDependency={zoomed}
246
251
  direction='left'
247
252
  size='small'
248
253
  className={classes.sliderButtons}
@@ -262,6 +267,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
262
267
  >
263
268
  <ScrollerButton
264
269
  layout
270
+ layoutDependency={zoomed}
265
271
  direction='right'
266
272
  size='small'
267
273
  className={classes.sliderButtons}
@@ -286,7 +292,11 @@ export function SidebarGallery(props: SidebarGalleryProps) {
286
292
  },
287
293
  }}
288
294
  >
289
- <ScrollerDots layout sx={{ backgroundColor: 'background.paper', boxShadow: 6 }} />
295
+ <ScrollerDots
296
+ layout
297
+ layoutDependency={zoomed}
298
+ sx={{ backgroundColor: 'background.paper', boxShadow: 6 }}
299
+ />
290
300
  </Box>
291
301
  </MotionBox>
292
302
 
@@ -319,6 +329,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
319
329
  >
320
330
  <MotionBox
321
331
  layout
332
+ layoutDependency={zoomed}
322
333
  className={classes.sidebar}
323
334
  sx={{
324
335
  boxSizing: 'border-box',
@@ -60,7 +60,7 @@ export function LayoutHeader(props: LayoutHeaderProps) {
60
60
  let left = secondary
61
61
  let right = primary
62
62
 
63
- if (back) left = back
63
+ if (back && !secondary) left = back
64
64
 
65
65
  if (!right) right = close
66
66
  else if (!left) left = close
@@ -1,10 +1,13 @@
1
1
  import { useMotionValueValue } from '@graphcommerce/framer-utils'
2
- import { Box, SxProps, Theme } from '@mui/material'
2
+ import { Box, styled, SxProps, Theme } from '@mui/material'
3
+ import { LayoutProps, m } from 'framer-motion'
3
4
  import React, { useRef } from 'react'
4
5
  import { extendableComponent } from '../../Styles'
5
6
  import { useScrollY } from '../hooks/useScrollY'
6
7
  import { FloatingProps } from './LayoutHeadertypes'
7
8
 
9
+ const MotionDiv = styled(m.div)({})
10
+
8
11
  export type LayoutHeaderContentProps = FloatingProps & {
9
12
  children?: React.ReactNode
10
13
  left?: React.ReactNode
@@ -13,6 +16,7 @@ export type LayoutHeaderContentProps = FloatingProps & {
13
16
  switchPoint?: number
14
17
  sx?: SxProps<Theme>
15
18
  sxBg?: SxProps<Theme>
19
+ layout?: LayoutProps['layout']
16
20
  }
17
21
 
18
22
  type OwnerState = { floatingSm: boolean; floatingMd: boolean; scrolled: boolean; divider: boolean }
@@ -33,6 +37,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
33
37
  switchPoint = 50,
34
38
  sx = [],
35
39
  sxBg = [],
40
+ layout,
36
41
  } = props
37
42
 
38
43
  const scroll = useScrollY()
@@ -139,7 +144,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
139
144
  justifyContent: 'start',
140
145
  })}
141
146
  >
142
- {left}
147
+ <MotionDiv layout={layout}>{left}</MotionDiv>
143
148
  </Box>
144
149
  )}
145
150
  <Box
@@ -172,7 +177,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
172
177
  },
173
178
  })}
174
179
  >
175
- {children}
180
+ <MotionDiv layout={layout}>{children}</MotionDiv>
176
181
  </Box>
177
182
  <Box
178
183
  className={classes.right}
@@ -188,7 +193,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
188
193
  justifyContent: 'end',
189
194
  })}
190
195
  >
191
- {right}
196
+ <MotionDiv layout={layout}>{right}</MotionDiv>
192
197
  </Box>
193
198
  {divider && (
194
199
  <Box
@@ -39,8 +39,10 @@ export function LayoutDefault(props: LayoutDefaultProps) {
39
39
  sx = [],
40
40
  } = props
41
41
 
42
- const offset = useScrollOffset().y
43
- const scrollWithOffset = useTransform(useViewportScroll().scrollY, (y) => y + offset)
42
+ const scrollWithOffset = useTransform(
43
+ [useViewportScroll().scrollY, useScrollOffset()],
44
+ ([y, offset]: number[]) => y + offset,
45
+ )
44
46
 
45
47
  const classes = withState({ noSticky })
46
48
 
@@ -104,7 +106,7 @@ export function LayoutDefault(props: LayoutDefaultProps) {
104
106
  justifyContent: 'space-between',
105
107
  width: '100%',
106
108
  height: 0,
107
- zIndex: 'drawer',
109
+ zIndex: 'speedDial',
108
110
  [theme.breakpoints.up('sm')]: {
109
111
  padding: `0 ${theme.page.horizontal}`,
110
112
  position: 'sticky',
@@ -1,10 +1,14 @@
1
+ import { usePageContext, useGo, useScrollOffset } from '@graphcommerce/framer-next-pages'
1
2
  import { ScrollerProvider, ScrollSnapType } from '@graphcommerce/framer-scroller'
3
+ import { useMotionValueValue } from '@graphcommerce/framer-utils'
4
+ import { usePresence } from 'framer-motion'
2
5
  import type { SetOptional } from 'type-fest'
3
- import { LayoutOverlayBase, LayoutOverlayBaseProps } from './LayoutOverlayBase'
6
+ import { OverlayBase, LayoutOverlayBaseProps } from '../../Overlay/components/OverlayBase'
4
7
 
5
- export type { LayoutOverlayVariant } from './LayoutOverlayBase'
6
-
7
- export type LayoutOverlayProps = SetOptional<LayoutOverlayBaseProps, 'variantSm' | 'variantMd'>
8
+ export type LayoutOverlayProps = Omit<
9
+ SetOptional<LayoutOverlayBaseProps, 'variantSm' | 'variantMd'>,
10
+ 'active' | 'direction' | 'onClosed' | 'offsetPageY' | 'isPresent' | 'safeToRemove'
11
+ >
8
12
 
9
13
  export function LayoutOverlay(props: LayoutOverlayProps) {
10
14
  const { children, variantSm = 'bottom', variantMd = 'right', ...otherProps } = props
@@ -14,11 +18,26 @@ export function LayoutOverlay(props: LayoutOverlayProps) {
14
18
  const scrollSnapTypeMd: ScrollSnapType =
15
19
  variantMd === 'left' || variantMd === 'right' ? 'inline mandatory' : 'block proximity'
16
20
 
21
+ const { closeSteps, active, direction } = usePageContext()
22
+ const onCloseHandler = useGo(closeSteps * -1)
23
+ const offsetPageY = useMotionValueValue(useScrollOffset(), (v) => v)
24
+ const [isPresent, safeToRemove] = usePresence()
25
+
17
26
  return (
18
27
  <ScrollerProvider scrollSnapTypeSm={scrollSnapTypeSm} scrollSnapTypeMd={scrollSnapTypeMd}>
19
- <LayoutOverlayBase variantMd={variantMd} variantSm={variantSm} {...otherProps}>
28
+ <OverlayBase
29
+ active={active}
30
+ direction={direction}
31
+ onClosed={onCloseHandler}
32
+ offsetPageY={offsetPageY}
33
+ variantMd={variantMd}
34
+ variantSm={variantSm}
35
+ isPresent={isPresent}
36
+ safeToRemove={safeToRemove}
37
+ {...otherProps}
38
+ >
20
39
  {children}
21
- </LayoutOverlayBase>
40
+ </OverlayBase>
22
41
  </ScrollerProvider>
23
42
  )
24
43
  }
@@ -43,7 +43,7 @@ export function DesktopNavBar(props: MenuTabsProps) {
43
43
  sx={(theme) => ({
44
44
  gridArea: `1 / 1 / 1 / 4`,
45
45
  columnGap: theme.spacings.md,
46
- padding: `0 ${theme.spacings.sm}`,
46
+ padding: `0 ${theme.spacings.md}`,
47
47
  gridAutoColumns: 'min-content',
48
48
  })}
49
49
  className={classes.scroller}
@@ -5,11 +5,45 @@ import { extendableComponent } from '../Styles/extendableComponent'
5
5
 
6
6
  const { classes, selectors } = extendableComponent('DesktopNavItem', ['root', 'line'] as const)
7
7
 
8
- export type DesktopNavItemProps = LinkProps & Pick<PageLinkProps, 'href'>
8
+ export type DesktopNavItemLinkProps = LinkProps<'a'> & Pick<PageLinkProps, 'href'>
9
+ export type DesktopNavItemButtonProps = LinkProps<'div'> & {
10
+ onClick: LinkProps<'button'>['onClick']
11
+ }
12
+
13
+ function isLinkProps(
14
+ props: DesktopNavItemLinkProps | DesktopNavItemButtonProps,
15
+ ): props is DesktopNavItemLinkProps {
16
+ return 'href' in props
17
+ }
18
+
19
+ export function DesktopNavItem(props: DesktopNavItemLinkProps | DesktopNavItemButtonProps) {
20
+ const router = useRouter()
21
+
22
+ if (!isLinkProps(props)) {
23
+ const { onClick, children, sx = [], ...linkProps } = props
24
+
25
+ return (
26
+ <Link
27
+ className={classes.root}
28
+ component='div'
29
+ variant='h6'
30
+ color='text.primary'
31
+ underline='none'
32
+ {...linkProps}
33
+ onClick={onClick}
34
+ sx={[
35
+ { whiteSpace: 'nowrap', paddingTop: '6px', cursor: 'pointer' },
36
+ ...(Array.isArray(sx) ? sx : [sx]),
37
+ ]}
38
+ >
39
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>{children}</Box>
40
+ </Link>
41
+ )
42
+ }
9
43
 
10
- export function DesktopNavItem(props: DesktopNavItemProps) {
11
44
  const { href, children, sx = [], ...linkProps } = props
12
- const active = useRouter().asPath.startsWith(href.toString())
45
+
46
+ const active = router.asPath.startsWith(href.toString())
13
47
 
14
48
  return (
15
49
  <PageLink href={href} passHref>
@@ -21,7 +55,7 @@ export function DesktopNavItem(props: DesktopNavItemProps) {
21
55
  {...linkProps}
22
56
  sx={[{ whiteSpace: 'nowrap', paddingTop: '6px' }, ...(Array.isArray(sx) ? sx : [sx])]}
23
57
  >
24
- {children}
58
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>{children}</Box>
25
59
  <Box
26
60
  component='span'
27
61
  className={classes.line}
@@ -1,6 +1,6 @@
1
1
  import { ListItemButton, ListItemIcon, ListItemText, SxProps, Theme } from '@mui/material'
2
2
  import PageLink from 'next/link'
3
- import router from 'next/router'
3
+ import { useRouter } from 'next/router'
4
4
  import React from 'react'
5
5
  import { extendableComponent } from '../Styles'
6
6
 
@@ -17,6 +17,7 @@ const { classes } = extendableComponent(compName, parts)
17
17
 
18
18
  export function MenuFabSecondaryItem(props: FabMenuSecondaryItemProps) {
19
19
  const { href, children, icon, sx = [] } = props
20
+ const router = useRouter()
20
21
 
21
22
  return (
22
23
  <PageLink href={href} passHref>
@@ -0,0 +1,106 @@
1
+ import { useMotionValueValue } from '@graphcommerce/framer-utils'
2
+ import { Fab, styled, Box, SxProps, Theme, FabProps } from '@mui/material'
3
+ import { m } from 'framer-motion'
4
+ import { useRouter } from 'next/router'
5
+ import React, { useEffect } from 'react'
6
+ import { IconSvg } from '../../IconSvg'
7
+ import { useScrollY } from '../../Layout/hooks/useScrollY'
8
+ import { useFabAnimation } from '../../LayoutParts/useFabAnimation'
9
+ import { extendableComponent } from '../../Styles/extendableComponent'
10
+ import { useFabSize } from '../../Theme'
11
+ import { iconMenu, iconClose } from '../../icons'
12
+
13
+ const MotionDiv = styled(m.div)({})
14
+
15
+ export type NavigationFabProps = {
16
+ menuIcon?: React.ReactNode
17
+ closeIcon?: React.ReactNode
18
+ sx?: SxProps<Theme>
19
+ } & Pick<FabProps, 'color' | 'size' | 'variant' | 'onClick'>
20
+
21
+ const name = 'MenuFab'
22
+ const parts = ['wrapper', 'fab', 'shadow', 'menu'] as const
23
+ type OwnerState = {
24
+ scrolled: boolean
25
+ }
26
+
27
+ const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
28
+
29
+ export function NavigationFab(props: NavigationFabProps) {
30
+ const { menuIcon, closeIcon, sx = [], ...fabProps } = props
31
+ const router = useRouter()
32
+ const [openEl, setOpenEl] = React.useState<null | HTMLElement>(null)
33
+
34
+ const { opacity, shadowOpacity } = useFabAnimation()
35
+ const scrollY = useScrollY()
36
+ const scrolled = useMotionValueValue(scrollY, (y) => y > 10)
37
+
38
+ useEffect(() => {
39
+ const clear = () => setOpenEl(null)
40
+ router.events.on('routeChangeStart', clear)
41
+ return () => router.events.off('routeChangeStart', clear)
42
+ }, [router.events])
43
+
44
+ const fabIconSize = useFabSize('responsive')
45
+
46
+ const classes = withState({ scrolled })
47
+
48
+ return (
49
+ <Box
50
+ sx={[
51
+ { position: 'relative', width: fabIconSize, height: fabIconSize },
52
+ ...(Array.isArray(sx) ? sx : [sx]),
53
+ ]}
54
+ >
55
+ <MotionDiv
56
+ className={classes.wrapper}
57
+ sx={(theme) => ({
58
+ [theme.breakpoints.down('md')]: {
59
+ opacity: '1 !important',
60
+ transform: 'none !important',
61
+ },
62
+ })}
63
+ style={{ opacity }}
64
+ >
65
+ <Fab
66
+ color='inherit'
67
+ aria-label='Open Menu'
68
+ size='responsive'
69
+ sx={(theme) => ({
70
+ boxShadow: 'none',
71
+ '&:hover, &:focus': {
72
+ boxShadow: 'none',
73
+ background: theme.palette.text.primary,
74
+ },
75
+ background: theme.palette.text.primary,
76
+ pointerEvents: 'all',
77
+ color: theme.palette.background.paper,
78
+ })}
79
+ className={classes.fab}
80
+ {...fabProps}
81
+ >
82
+ {closeIcon ?? (
83
+ <IconSvg src={iconClose} size='large' sx={{ display: openEl ? 'block' : 'none' }} />
84
+ )}
85
+ {menuIcon ?? (
86
+ <IconSvg src={iconMenu} size='large' sx={{ display: openEl ? 'none' : 'block' }} />
87
+ )}
88
+ </Fab>
89
+ <MotionDiv
90
+ sx={(theme) => ({
91
+ pointerEvents: 'none',
92
+ borderRadius: '99em',
93
+ position: 'absolute',
94
+ height: '100%',
95
+ width: '100%',
96
+ boxShadow: theme.shadows[6],
97
+ top: 0,
98
+ [theme.breakpoints.down('md')]: { opacity: '1 !important' },
99
+ })}
100
+ className={classes.shadow}
101
+ style={{ opacity: shadowOpacity }}
102
+ />
103
+ </MotionDiv>
104
+ </Box>
105
+ )
106
+ }
@@ -0,0 +1,147 @@
1
+ /* eslint-disable @typescript-eslint/no-use-before-define */
2
+ import { Box, ListItemButton, styled, useEventCallback } from '@mui/material'
3
+ import PageLink from 'next/link'
4
+ import { IconSvg } from '../../IconSvg'
5
+ import { extendableComponent } from '../../Styles/extendableComponent'
6
+ import { iconChevronRight } from '../../icons'
7
+ import {
8
+ isNavigationButton,
9
+ isNavigationComponent,
10
+ isNavigationHref,
11
+ NavigationNode,
12
+ NavigationPath,
13
+ useNavigation,
14
+ } from '../hooks/useNavigation'
15
+ import type { NavigationList } from './NavigationList'
16
+
17
+ type OwnerState = {
18
+ first?: boolean
19
+ last?: boolean
20
+ // It is actually used.
21
+ // eslint-disable-next-line react/no-unused-prop-types
22
+ column: number
23
+ }
24
+
25
+ type NavigationItemProps = NavigationNode & {
26
+ parentPath: NavigationPath
27
+ idx: number
28
+ NavigationList: typeof NavigationList
29
+ } & OwnerState
30
+
31
+ const componentName = 'NavigationItem'
32
+ const parts = ['li', 'ul', 'item'] as const
33
+
34
+ const { withState } = extendableComponent<OwnerState, typeof componentName, typeof parts>(
35
+ componentName,
36
+ parts,
37
+ )
38
+
39
+ const NavigationLI = styled('li')({ display: 'contents' })
40
+
41
+ export function NavigationItem(props: NavigationItemProps) {
42
+ const { id, parentPath, idx, first, last, NavigationList } = props
43
+
44
+ const row = idx + 1
45
+ const { selected, select, hideRootOnNavigate, onClose } = useNavigation()
46
+
47
+ const itemPath = [...parentPath, id]
48
+ const isSelected = selected.slice(0, itemPath.length).join('/') === itemPath.join('/')
49
+
50
+ const hidingRoot = hideRootOnNavigate && selected.length > 0
51
+ const hideItem = hidingRoot && itemPath.length === 1
52
+
53
+ const column = hidingRoot ? itemPath.length - 1 : itemPath.length
54
+ const classes = withState({ first, last, column: itemPath.length })
55
+
56
+ const onCloseHandler: React.MouseEventHandler<HTMLAnchorElement> = useEventCallback((e) => {
57
+ if (!isNavigationHref(props)) return
58
+ const { href } = props
59
+ onClose?.(e, href)
60
+ })
61
+
62
+ if (isNavigationButton(props)) {
63
+ const { childItems, name } = props
64
+ return (
65
+ <NavigationLI className={classes.li}>
66
+ <ListItemButton
67
+ className={classes.item}
68
+ role='button'
69
+ sx={{
70
+ gridRowStart: row,
71
+ gridColumnStart: column,
72
+ gap: (theme) => theme.spacings.xxs,
73
+ display: hideItem ? 'none' : 'flex',
74
+ }}
75
+ disabled={isSelected}
76
+ tabIndex={selected.join(',').includes(parentPath.join(',')) ? undefined : -1}
77
+ onClick={(e) => {
78
+ e.preventDefault()
79
+ if (!isSelected) select(itemPath)
80
+ }}
81
+ >
82
+ <Box
83
+ component='span'
84
+ sx={{
85
+ whiteSpace: 'nowrap',
86
+ overflowX: 'hidden',
87
+ textOverflow: 'ellipsis',
88
+ flexGrow: 1,
89
+ }}
90
+ >
91
+ {name}
92
+ </Box>
93
+ <IconSvg src={iconChevronRight} sx={{ flexShrink: 0 }} />
94
+ </ListItemButton>
95
+
96
+ <NavigationList items={childItems} selected={isSelected} parentPath={itemPath} />
97
+ </NavigationLI>
98
+ )
99
+ }
100
+
101
+ if (isNavigationHref(props)) {
102
+ const { name, href } = props
103
+ return (
104
+ <NavigationLI sx={[hideItem && { display: 'none' }]} className={classes.li}>
105
+ <PageLink href={href} passHref>
106
+ <ListItemButton
107
+ className={classes.item}
108
+ component='a'
109
+ sx={(theme) => ({
110
+ gridRowStart: row,
111
+ gridColumnStart: column,
112
+ gap: theme.spacings.xxs,
113
+ })}
114
+ tabIndex={selected.join(',').includes(parentPath.join(',')) ? undefined : -1}
115
+ onClick={onCloseHandler}
116
+ >
117
+ <Box
118
+ component='span'
119
+ sx={{
120
+ whiteSpace: 'nowrap',
121
+ overflowX: 'hidden',
122
+ textOverflow: 'ellipsis',
123
+ }}
124
+ >
125
+ {name}
126
+ </Box>
127
+ </ListItemButton>
128
+ </PageLink>
129
+ </NavigationLI>
130
+ )
131
+ }
132
+
133
+ if (isNavigationComponent(props)) {
134
+ const { component } = props
135
+ return (
136
+ <NavigationLI sx={[hideItem && { display: 'none' }]} className={classes.li}>
137
+ <Box sx={{ gridRowStart: row, gridColumnStart: column }} className={classes.item}>
138
+ {component}
139
+ </Box>
140
+ </NavigationLI>
141
+ )
142
+ }
143
+
144
+ if (process.env.NODE_ENV !== 'production') throw Error('NavigationItem: unknown type')
145
+
146
+ return null
147
+ }
@@ -0,0 +1,50 @@
1
+ import { styled } from '@mui/material'
2
+ import { extendableComponent } from '../../Styles/extendableComponent'
3
+ import { NavigationNode, NavigationPath } from '../hooks/useNavigation'
4
+ import { NavigationItem } from './NavigationItem'
5
+
6
+ const NavigationUList = styled('ul')({})
7
+
8
+ type NavigationItemsProps = {
9
+ parentPath?: NavigationPath
10
+ items: NavigationNode[]
11
+ selected?: boolean
12
+ }
13
+
14
+ type OwnerState = {
15
+ column: number
16
+ }
17
+
18
+ const name = 'NavigationList'
19
+ const parts = ['root'] as const
20
+ const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
21
+
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 } = props
27
+
28
+ 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
+ >
36
+ {items.map((item, idx) => (
37
+ <NavigationItem
38
+ NavigationList={NavigationList}
39
+ key={item.id}
40
+ {...item}
41
+ parentPath={parentPath}
42
+ idx={idx}
43
+ first={idx === 0}
44
+ last={idx === items.length - 1}
45
+ column={0}
46
+ />
47
+ ))}
48
+ </NavigationUList>
49
+ )
50
+ }