@graphcommerce/next-ui 4.27.0 → 4.28.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.
- package/CHANGELOG.md +16 -0
- package/FramerScroller/SidebarGallery.tsx +3 -4
- package/Navigation/components/NavigationItem.tsx +22 -23
- package/Navigation/components/NavigationOverlay.tsx +7 -3
- package/Navigation/components/NavigationProvider.tsx +15 -2
- package/Navigation/hooks/useNavigation.ts +19 -6
- package/hooks/index.ts +1 -0
- package/hooks/useMatchMedia.ts +17 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`0c21c5c23`](https://github.com/graphcommerce-org/graphcommerce/commit/0c21c5c233ebab15f6629c234e3de1cc8c0452e1) Thanks [@paales](https://github.com/paales)! - Implement serverRenderDepth prop to the Navigation to limit initial render time and TBT
|
|
8
|
+
|
|
9
|
+
* [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a) Thanks [@paales](https://github.com/paales)! - Move to useMatchMedia to have a simple boolean utility that allows to match to a certain breakpoint
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`de8925aa9`](https://github.com/graphcommerce-org/graphcommerce/commit/de8925aa910b191c62041530c68c697a58a1e52d) Thanks [@paales](https://github.com/paales)! - Allow for a custom Component for magentoMenuToNavigation and allow React.ReactNode for items
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a), [`9e0ca73eb`](https://github.com/graphcommerce-org/graphcommerce/commit/9e0ca73eb50ded578f4a98e40a7eb920bf8ab421)]:
|
|
16
|
+
- @graphcommerce/framer-scroller@2.1.40
|
|
17
|
+
- @graphcommerce/framer-next-pages@3.3.1
|
|
18
|
+
|
|
3
19
|
## 4.27.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { Fab, useTheme, Box, styled, SxProps, Theme } from '@mui/material'
|
|
11
11
|
import { m, useDomEvent, useMotionValue } from 'framer-motion'
|
|
12
12
|
import { useRouter } from 'next/router'
|
|
13
|
-
import React, { useEffect,
|
|
13
|
+
import React, { useEffect, useRef } from 'react'
|
|
14
14
|
import { IconSvg } from '../IconSvg'
|
|
15
15
|
import { Row } from '../Row/Row'
|
|
16
16
|
import { extendableComponent } from '../Styles'
|
|
@@ -107,9 +107,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
107
107
|
|
|
108
108
|
const headerHeight = `${theme.appShell.headerHeightSm} - ${theme.spacings.sm} * 2`
|
|
109
109
|
const galleryMargin = theme.spacings.lg
|
|
110
|
-
const extraSpacing = theme.spacings.md
|
|
111
110
|
|
|
112
|
-
const maxHeight = `calc(100vh - ${headerHeight} - ${galleryMargin}
|
|
111
|
+
const maxHeight = `calc(100vh - ${headerHeight} - ${galleryMargin})`
|
|
113
112
|
const ratio = `calc(${height} / ${width} * 100%)`
|
|
114
113
|
|
|
115
114
|
const hasImages = images.length > 0
|
|
@@ -200,7 +199,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
200
199
|
width={image.width}
|
|
201
200
|
height={image.height}
|
|
202
201
|
loading={idx === 0 ? 'eager' : 'lazy'}
|
|
203
|
-
sx={{ display: 'block' }}
|
|
202
|
+
sx={{ display: 'block', objectFit: 'contain' }}
|
|
204
203
|
sizes={{
|
|
205
204
|
0: '100vw',
|
|
206
205
|
[theme.breakpoints.values.md]: zoomed ? '100vw' : '60vw',
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
2
2
|
import { useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
3
|
-
import {
|
|
4
|
-
Box,
|
|
5
|
-
ListItemButton,
|
|
6
|
-
styled,
|
|
7
|
-
Theme,
|
|
8
|
-
useEventCallback,
|
|
9
|
-
useMediaQuery,
|
|
10
|
-
alpha,
|
|
11
|
-
} from '@mui/material'
|
|
3
|
+
import { alpha, Box, ListItemButton, styled, useEventCallback, useTheme } from '@mui/material'
|
|
12
4
|
import PageLink from 'next/link'
|
|
13
5
|
import React from 'react'
|
|
14
6
|
import { IconSvg } from '../../IconSvg'
|
|
15
7
|
import { extendableComponent } from '../../Styles/extendableComponent'
|
|
8
|
+
import { useMatchMedia } from '../../hooks'
|
|
16
9
|
import { iconChevronRight } from '../../icons'
|
|
17
10
|
import {
|
|
18
11
|
isNavigationButton,
|
|
@@ -54,9 +47,9 @@ const NavigationLI = styled('li')({ display: 'contents' })
|
|
|
54
47
|
|
|
55
48
|
export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
56
49
|
const { id, parentPath, idx, first, last, NavigationList, mouseEvent } = props
|
|
50
|
+
const { selection, hideRootOnNavigate, closing, animating, serverRenderDepth } = useNavigation()
|
|
57
51
|
|
|
58
52
|
const row = idx + 1
|
|
59
|
-
const { selection, hideRootOnNavigate, closing, animating } = useNavigation()
|
|
60
53
|
|
|
61
54
|
const itemPath = [...(parentPath ? parentPath.split(',') : []), id]
|
|
62
55
|
|
|
@@ -83,13 +76,18 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
83
76
|
closing.set(true)
|
|
84
77
|
})
|
|
85
78
|
|
|
86
|
-
const
|
|
79
|
+
const matchMedia = useMatchMedia()
|
|
87
80
|
|
|
88
81
|
if (isNavigationButton(props)) {
|
|
89
|
-
const { childItems, name } = props
|
|
82
|
+
const { childItems, name, href } = props
|
|
83
|
+
|
|
84
|
+
const skipChildren = itemPath.length + 1 > serverRenderDepth && !isSelected && !!href
|
|
85
|
+
|
|
90
86
|
return (
|
|
91
87
|
<NavigationLI className={classes.li}>
|
|
92
88
|
<ListItemButton
|
|
89
|
+
component={href ? 'a' : 'div'}
|
|
90
|
+
href={href || undefined}
|
|
93
91
|
className={classes.item}
|
|
94
92
|
role='button'
|
|
95
93
|
sx={[
|
|
@@ -116,14 +114,14 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
116
114
|
tabIndex={tabIndex}
|
|
117
115
|
onClick={(e) => {
|
|
118
116
|
e.preventDefault()
|
|
119
|
-
if (!isSelected && animating.get()
|
|
117
|
+
if (!isSelected && !animating.get()) {
|
|
120
118
|
selection.set(itemPath)
|
|
121
119
|
}
|
|
122
120
|
}}
|
|
123
121
|
onMouseMove={
|
|
124
|
-
itemPath.length > 1 && mouseEvent === 'hover'
|
|
122
|
+
(itemPath.length > 1 || !hideRootOnNavigate) && mouseEvent === 'hover'
|
|
125
123
|
? (e) => {
|
|
126
|
-
if (
|
|
124
|
+
if (!isSelected && !animating.get() && matchMedia.up('md')) {
|
|
127
125
|
e.preventDefault()
|
|
128
126
|
setTimeout(() => selection.set(itemPath), 0)
|
|
129
127
|
}
|
|
@@ -145,18 +143,21 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
145
143
|
<IconSvg src={iconChevronRight} sx={{ flexShrink: 0 }} />
|
|
146
144
|
</ListItemButton>
|
|
147
145
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
{!skipChildren && (
|
|
147
|
+
<NavigationList
|
|
148
|
+
items={childItems}
|
|
149
|
+
selected={isSelected}
|
|
150
|
+
parentPath={itemPath.join(',')}
|
|
151
|
+
mouseEvent={mouseEvent}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
154
|
</NavigationLI>
|
|
155
155
|
)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
if (isNavigationHref(props)) {
|
|
159
159
|
const { name, href } = props
|
|
160
|
+
|
|
160
161
|
return (
|
|
161
162
|
<NavigationLI sx={[hideItem && { display: 'none' }]} className={classes.li}>
|
|
162
163
|
<PageLink href={href} passHref prefetch={false}>
|
|
@@ -198,8 +199,6 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
198
199
|
)
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
if (process.env.NODE_ENV !== 'production') throw Error('NavigationItem: unknown type')
|
|
202
|
-
|
|
203
202
|
return null
|
|
204
203
|
})
|
|
205
204
|
|
|
@@ -11,6 +11,7 @@ import { LayoutTitle } from '../../Layout/components/LayoutTitle'
|
|
|
11
11
|
import { Overlay } from '../../Overlay/components/Overlay'
|
|
12
12
|
import { extendableComponent } from '../../Styles/extendableComponent'
|
|
13
13
|
import { useFabSize } from '../../Theme'
|
|
14
|
+
import { useMatchMedia } from '../../hooks'
|
|
14
15
|
import { iconClose, iconChevronLeft } from '../../icons'
|
|
15
16
|
import { useNavigation } from '../hooks/useNavigation'
|
|
16
17
|
import { mouseEventPref } from './NavigationItem'
|
|
@@ -57,14 +58,15 @@ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
|
57
58
|
mouseEvent,
|
|
58
59
|
itemPadding = 'md',
|
|
59
60
|
} = props
|
|
60
|
-
const { selection, items, animating, closing } = useNavigation()
|
|
61
|
+
const { selection, items, animating, closing, serverRenderDepth } = useNavigation()
|
|
61
62
|
|
|
62
63
|
const fabSize = useFabSize('responsive')
|
|
63
64
|
const svgSize = useIconSvgSize('large')
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
+
const matchMedia = useMatchMedia()
|
|
67
|
+
|
|
66
68
|
const handleOnBack = useEventCallback(() => {
|
|
67
|
-
if (
|
|
69
|
+
if (matchMedia.down('md')) {
|
|
68
70
|
const current = selection.get()
|
|
69
71
|
selection.set(current !== false ? current.slice(0, -1) : false)
|
|
70
72
|
} else selection.set([])
|
|
@@ -87,6 +89,8 @@ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
|
87
89
|
|
|
88
90
|
const handleClose = useEventCallback(() => closing.set(true))
|
|
89
91
|
|
|
92
|
+
if (selectedLevel === -1 && serverRenderDepth <= 0) return null
|
|
93
|
+
|
|
90
94
|
return (
|
|
91
95
|
<Overlay
|
|
92
96
|
className={classes.root}
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
NavigationContextType,
|
|
7
7
|
NavigationContext,
|
|
8
8
|
UseNavigationSelection,
|
|
9
|
+
NavigationNodeType,
|
|
10
|
+
NavigationNodeComponent,
|
|
9
11
|
} from '../hooks/useNavigation'
|
|
10
12
|
|
|
11
13
|
export type NavigationProviderProps = {
|
|
@@ -15,6 +17,7 @@ export type NavigationProviderProps = {
|
|
|
15
17
|
children?: React.ReactNode
|
|
16
18
|
animationDuration?: number
|
|
17
19
|
selection: UseNavigationSelection
|
|
20
|
+
serverRenderDepth?: number
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
const nonNullable = <T,>(value: T): value is NonNullable<T> => value !== null && value !== undefined
|
|
@@ -27,6 +30,7 @@ export const NavigationProvider = React.memo<NavigationProviderProps>((props) =>
|
|
|
27
30
|
animationDuration = 0.225,
|
|
28
31
|
children,
|
|
29
32
|
selection,
|
|
33
|
+
serverRenderDepth = 2,
|
|
30
34
|
} = props
|
|
31
35
|
|
|
32
36
|
const animating = useMotionValue(false)
|
|
@@ -39,10 +43,19 @@ export const NavigationProvider = React.memo<NavigationProviderProps>((props) =>
|
|
|
39
43
|
animating,
|
|
40
44
|
closing,
|
|
41
45
|
items: items
|
|
42
|
-
.map((item, index) =>
|
|
46
|
+
.map((item, index) =>
|
|
47
|
+
isElement(item)
|
|
48
|
+
? ({
|
|
49
|
+
type: NavigationNodeType.COMPONENT,
|
|
50
|
+
id: item.key ?? index,
|
|
51
|
+
component: item,
|
|
52
|
+
} as NavigationNodeComponent)
|
|
53
|
+
: item,
|
|
54
|
+
)
|
|
43
55
|
.filter(nonNullable),
|
|
56
|
+
serverRenderDepth,
|
|
44
57
|
}),
|
|
45
|
-
[hideRootOnNavigate, selection, animating, closing, items],
|
|
58
|
+
[hideRootOnNavigate, selection, animating, closing, items, serverRenderDepth],
|
|
46
59
|
)
|
|
47
60
|
|
|
48
61
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MotionValue, useMotionValue } from 'framer-motion'
|
|
2
|
-
import { createContext, MutableRefObject, useContext } from 'react'
|
|
2
|
+
import React, { createContext, MutableRefObject, useContext } from 'react'
|
|
3
3
|
|
|
4
4
|
export type NavigationId = string | number
|
|
5
5
|
export type NavigationPath = NavigationId[]
|
|
@@ -19,38 +19,51 @@ export type NavigationContextType = {
|
|
|
19
19
|
hideRootOnNavigate: boolean
|
|
20
20
|
animating: MotionValue<boolean>
|
|
21
21
|
closing: MotionValue<boolean>
|
|
22
|
+
serverRenderDepth: number
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
type NavigationNodeBase = {
|
|
26
|
+
type?: NavigationNodeType
|
|
25
27
|
id: NavigationId
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
export enum NavigationNodeType {
|
|
31
|
+
LINK,
|
|
32
|
+
BUTTON,
|
|
33
|
+
COMPONENT,
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
export type NavigationNodeHref = NavigationNodeBase & {
|
|
29
|
-
name:
|
|
37
|
+
name: React.ReactNode
|
|
30
38
|
href: string
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export type NavigationNodeButton = NavigationNodeBase & {
|
|
34
|
-
name:
|
|
42
|
+
name: React.ReactNode
|
|
43
|
+
type: NavigationNodeType.BUTTON
|
|
44
|
+
href?: string
|
|
35
45
|
childItems: NavigationNode[]
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
export type NavigationNodeComponent = NavigationNodeBase & {
|
|
49
|
+
type: NavigationNodeType.COMPONENT
|
|
39
50
|
component: React.ReactNode
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export type NavigationNode = NavigationNodeHref | NavigationNodeButton | NavigationNodeComponent
|
|
43
54
|
|
|
44
55
|
export function isNavigationHref(node: NavigationNodeBase): node is NavigationNodeHref {
|
|
45
|
-
return 'href' in node
|
|
56
|
+
return 'href' in node && node.type !== NavigationNodeType.BUTTON
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
export function isNavigationButton(node: NavigationNodeBase): node is NavigationNodeButton {
|
|
49
|
-
return (
|
|
60
|
+
return (
|
|
61
|
+
node.type === NavigationNodeType.BUTTON && (node as NavigationNodeButton).childItems?.length > 0
|
|
62
|
+
)
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
export function isNavigationComponent(node: NavigationNodeBase): node is NavigationNodeComponent {
|
|
53
|
-
return 'component' in node
|
|
66
|
+
return node.type === NavigationNodeType.COMPONENT && 'component' in node
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
export const NavigationContext = createContext(undefined as unknown as NavigationContextType)
|
package/hooks/index.ts
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Breakpoint, useTheme } from '@mui/material'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
|
|
4
|
+
export function useMatchMedia() {
|
|
5
|
+
const theme = useTheme()
|
|
6
|
+
|
|
7
|
+
return useMemo(() => {
|
|
8
|
+
const callback = (direction: 'up' | 'down', breakpointKey: number | Breakpoint) =>
|
|
9
|
+
window.matchMedia(theme.breakpoints[direction](breakpointKey).replace(/^@media( ?)/m, ''))
|
|
10
|
+
.matches
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
down: (key: number | Breakpoint) => callback('down', key),
|
|
14
|
+
up: (key: number | Breakpoint) => callback('up', key),
|
|
15
|
+
}
|
|
16
|
+
}, [theme.breakpoints])
|
|
17
|
+
}
|
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": "4.
|
|
5
|
+
"version": "4.28.0",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"@emotion/react": "^11.9.3",
|
|
20
20
|
"@emotion/server": "^11.4.0",
|
|
21
21
|
"@emotion/styled": "^11.9.3",
|
|
22
|
-
"@graphcommerce/framer-next-pages": "3.3.
|
|
23
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
22
|
+
"@graphcommerce/framer-next-pages": "3.3.1",
|
|
23
|
+
"@graphcommerce/framer-scroller": "2.1.40",
|
|
24
24
|
"@graphcommerce/framer-utils": "3.2.0",
|
|
25
25
|
"@graphcommerce/image": "3.1.9",
|
|
26
26
|
"cookie": "^0.5.0",
|