@graphcommerce/next-ui 7.1.0-canary.9 → 8.0.0-canary.100
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/ActionCard/ActionCard.tsx +2 -0
- package/CHANGELOG.md +255 -8
- package/Config.graphqls +24 -0
- package/Footer/Footer.tsx +97 -94
- package/FramerScroller/ItemScroller.tsx +3 -3
- package/FramerScroller/SidebarGallery.tsx +181 -162
- package/JsonLd/JsonLd.tsx +0 -2
- package/LayoutDefault/components/LayoutDefault.tsx +6 -1
- package/LayoutParts/DesktopNavBar.tsx +2 -0
- package/LayoutParts/Logo.tsx +20 -20
- package/LazyHydrate/LazyHydrate.tsx +72 -0
- package/LazyHydrate/index.ts +1 -0
- package/Navigation/components/NavigationFab.tsx +9 -7
- package/Navigation/components/NavigationOverlay.tsx +9 -1
- package/Navigation/components/NavigationProvider.tsx +18 -4
- package/Overlay/components/OverlayBase.tsx +1 -1
- package/Overlay/components/index.ts +1 -0
- package/Page/CssAndFramerMotionProvider.tsx +20 -18
- package/PageMeta/PageMeta.tsx +20 -13
- package/RenderType/filterNonNullableKeys.ts +11 -14
- package/Row/ColumnOne/variant/VariantMessage.tsx +12 -0
- package/Row/ColumnOne/variant/index.ts +1 -0
- package/Row/index.ts +1 -0
- package/SkipLink/SkipLink.tsx +38 -0
- package/Snackbar/DismissibleSnackbar.tsx +28 -0
- package/Snackbar/MessageSnackbarImpl.tsx +19 -2
- package/Styles/EmotionProvider.tsx +8 -8
- package/Styles/createEmotionCache.ts +14 -0
- package/Styles/withEmotionCache.tsx +26 -16
- package/TextInputNumber/TextInputNumber.tsx +7 -1
- package/Theme/MuiButton.ts +2 -2
- package/Theme/MuiChip.ts +2 -2
- package/Theme/MuiFab.ts +1 -1
- package/Theme/MuiSlider.ts +2 -2
- package/Theme/MuiSnackbar.ts +6 -4
- package/hooks/useDateTimeFormat.ts +3 -5
- package/hooks/useNumberFormat.ts +3 -5
- package/index.ts +4 -2
- package/package.json +16 -18
|
@@ -3,13 +3,23 @@ import {
|
|
|
3
3
|
MotionImageAspect,
|
|
4
4
|
MotionImageAspectProps,
|
|
5
5
|
Scroller,
|
|
6
|
-
ScrollerButton,
|
|
7
|
-
ScrollerButtonProps,
|
|
8
6
|
ScrollerDots,
|
|
7
|
+
ScrollerButton,
|
|
9
8
|
ScrollerProvider,
|
|
9
|
+
unstable_usePreventScroll as usePreventScroll,
|
|
10
|
+
ScrollerButtonProps,
|
|
11
|
+
ScrollerThumbnails,
|
|
10
12
|
} from '@graphcommerce/framer-scroller'
|
|
11
13
|
import { dvh } from '@graphcommerce/framer-utils'
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
Fab,
|
|
16
|
+
useTheme,
|
|
17
|
+
Box,
|
|
18
|
+
styled,
|
|
19
|
+
SxProps,
|
|
20
|
+
Theme,
|
|
21
|
+
Unstable_TrapFocus as TrapFocus,
|
|
22
|
+
} from '@mui/material'
|
|
13
23
|
import { m, useDomEvent, useMotionValue } from 'framer-motion'
|
|
14
24
|
import { useRouter } from 'next/router'
|
|
15
25
|
import React, { useEffect, useRef } from 'react'
|
|
@@ -21,7 +31,7 @@ import { iconChevronLeft, iconChevronRight, iconFullscreen, iconFullscreenExit }
|
|
|
21
31
|
|
|
22
32
|
const MotionBox = styled(m.div)({})
|
|
23
33
|
|
|
24
|
-
type OwnerState = { zoomed: boolean }
|
|
34
|
+
type OwnerState = { zoomed: boolean; disableZoom: boolean }
|
|
25
35
|
const name = 'SidebarGallery' as const
|
|
26
36
|
const parts = [
|
|
27
37
|
'row',
|
|
@@ -59,8 +69,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
59
69
|
aspectRatio: [width, height] = [1, 1],
|
|
60
70
|
sx,
|
|
61
71
|
routeHash = 'gallery',
|
|
62
|
-
disableZoom,
|
|
63
72
|
showButtons,
|
|
73
|
+
disableZoom = false,
|
|
64
74
|
} = props
|
|
65
75
|
|
|
66
76
|
const router = useRouter()
|
|
@@ -70,6 +80,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
70
80
|
const route = `#${routeHash}`
|
|
71
81
|
// We're using the URL to manage the state of the gallery.
|
|
72
82
|
const zoomed = router.asPath.endsWith(route)
|
|
83
|
+
usePreventScroll(zoomed)
|
|
73
84
|
|
|
74
85
|
// cleanup if someone enters the page with #gallery
|
|
75
86
|
useEffect(() => {
|
|
@@ -86,14 +97,13 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
86
97
|
if (!zoomed) {
|
|
87
98
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
88
99
|
router.push(route, undefined, { shallow: true })
|
|
89
|
-
document.body.style.overflow = 'hidden'
|
|
90
100
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
91
101
|
} else {
|
|
92
102
|
router.back()
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
|
|
96
|
-
const classes = withState({ zoomed })
|
|
106
|
+
const classes = withState({ zoomed, disableZoom })
|
|
97
107
|
const theme = useTheme()
|
|
98
108
|
const windowRef = useRef(typeof window !== 'undefined' ? window : null)
|
|
99
109
|
|
|
@@ -132,8 +142,10 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
132
142
|
{
|
|
133
143
|
willChange: 'transform',
|
|
134
144
|
display: 'grid',
|
|
145
|
+
gridTemplate: '"left" "right"',
|
|
135
146
|
[theme.breakpoints.up('md')]: {
|
|
136
|
-
gridTemplateColumns: '1fr auto',
|
|
147
|
+
// gridTemplateColumns: '1fr auto',
|
|
148
|
+
gridTemplate: '"left right" / 1fr auto',
|
|
137
149
|
},
|
|
138
150
|
background:
|
|
139
151
|
theme.palette.mode === 'light'
|
|
@@ -152,178 +164,185 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
152
164
|
},
|
|
153
165
|
]}
|
|
154
166
|
>
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
{
|
|
161
|
-
willChange: 'transform',
|
|
162
|
-
height: 0, // https://stackoverflow.com/questions/44770074/css-grid-row-height-safari-bug
|
|
163
|
-
backgroundColor: theme.palette.background.image,
|
|
164
|
-
position: 'relative',
|
|
165
|
-
paddingTop: `min(${ratio}, ${maxHeight})`,
|
|
166
|
-
[theme.breakpoints.down('md')]: {
|
|
167
|
-
width: '100vw',
|
|
168
|
-
},
|
|
169
|
-
[theme.breakpoints.up('md')]: {
|
|
170
|
-
height: `calc(${dvh(100)} - ${theme.appShell.headerHeightMd} - ${
|
|
171
|
-
theme.spacings.lg
|
|
172
|
-
})`,
|
|
173
|
-
position: 'sticky',
|
|
174
|
-
top: theme.appShell.headerHeightMd,
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
zoomed && {
|
|
178
|
-
position: 'relative',
|
|
179
|
-
top: 0,
|
|
180
|
-
marginTop: 0,
|
|
181
|
-
paddingTop: dvh(100),
|
|
182
|
-
},
|
|
183
|
-
]}
|
|
184
|
-
onLayoutAnimationComplete={() => {
|
|
185
|
-
if (!zoomed) document.body.style.overflow = ''
|
|
186
|
-
}}
|
|
187
|
-
>
|
|
188
|
-
<Scroller
|
|
189
|
-
className={classes.scroller}
|
|
190
|
-
hideScrollbar
|
|
191
|
-
onMouseDown={onMouseDownScroller}
|
|
192
|
-
onMouseUp={onMouseUpScroller}
|
|
167
|
+
<TrapFocus open={zoomed}>
|
|
168
|
+
<MotionBox
|
|
169
|
+
layout
|
|
170
|
+
layoutDependency={zoomed}
|
|
171
|
+
className={classes.scrollerContainer}
|
|
193
172
|
sx={[
|
|
194
173
|
{
|
|
174
|
+
gridArea: 'left',
|
|
195
175
|
willChange: 'transform',
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
176
|
+
height: 0, // https://stackoverflow.com/questions/44770074/css-grid-row-height-safari-bug
|
|
177
|
+
backgroundColor: theme.palette.background.image,
|
|
178
|
+
position: 'relative',
|
|
179
|
+
paddingTop: `min(${ratio}, ${maxHeight})`,
|
|
180
|
+
[theme.breakpoints.down('md')]: {
|
|
181
|
+
width: '100vw',
|
|
182
|
+
},
|
|
183
|
+
[theme.breakpoints.up('md')]: {
|
|
184
|
+
height: `calc(${dvh(100)} - ${theme.appShell.headerHeightMd} - ${
|
|
185
|
+
theme.spacings.lg
|
|
186
|
+
})`,
|
|
187
|
+
position: 'sticky',
|
|
188
|
+
top: theme.appShell.headerHeightMd,
|
|
189
|
+
},
|
|
203
190
|
},
|
|
204
191
|
zoomed && {
|
|
205
|
-
|
|
206
|
-
|
|
192
|
+
position: 'relative',
|
|
193
|
+
top: { xs: 0, md: 0 },
|
|
194
|
+
marginTop: 0,
|
|
195
|
+
paddingTop: dvh(100),
|
|
207
196
|
},
|
|
208
197
|
]}
|
|
198
|
+
onLayoutAnimationComplete={() => {
|
|
199
|
+
if (!zoomed) document.body.style.overflow = ''
|
|
200
|
+
}}
|
|
209
201
|
>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
<Scroller
|
|
203
|
+
className={classes.scroller}
|
|
204
|
+
hideScrollbar
|
|
205
|
+
onMouseDown={onMouseDownScroller}
|
|
206
|
+
onMouseUp={onMouseUpScroller}
|
|
207
|
+
sx={[
|
|
208
|
+
{
|
|
209
|
+
willChange: 'transform',
|
|
210
|
+
position: 'absolute',
|
|
211
|
+
top: 0,
|
|
212
|
+
width: '100%',
|
|
213
|
+
height: '100%',
|
|
214
|
+
gridAutoColumns: `100%`,
|
|
215
|
+
gridTemplateRows: `100%`,
|
|
216
|
+
cursor: disableZoom ? 'auto' : 'zoom-in',
|
|
217
|
+
},
|
|
218
|
+
zoomed && {
|
|
219
|
+
height: `var(--client-size-y)`,
|
|
220
|
+
cursor: 'inherit',
|
|
221
|
+
},
|
|
222
|
+
]}
|
|
223
|
+
>
|
|
224
|
+
{images.map((image, idx) => (
|
|
225
|
+
<MotionImageAspect
|
|
226
|
+
key={typeof image.src === 'string' ? image.src : idx}
|
|
227
|
+
layout
|
|
228
|
+
layoutDependency={zoomed}
|
|
229
|
+
src={image.src}
|
|
230
|
+
width={image.width}
|
|
231
|
+
height={image.height}
|
|
232
|
+
loading={idx === 0 ? 'eager' : 'lazy'}
|
|
233
|
+
sx={{ display: 'block', objectFit: 'contain' }}
|
|
234
|
+
sizes={{
|
|
235
|
+
0: '100vw',
|
|
236
|
+
[theme.breakpoints.values.md]: zoomed ? '100vw' : '60vw',
|
|
237
|
+
}}
|
|
238
|
+
alt={image.alt || `Product Image ${idx}` || undefined}
|
|
239
|
+
dontReportWronglySizedImages
|
|
240
|
+
/>
|
|
241
|
+
))}
|
|
242
|
+
</Scroller>
|
|
243
|
+
<MotionBox
|
|
244
|
+
layout='position'
|
|
245
|
+
layoutDependency={zoomed}
|
|
246
|
+
className={classes.topRight}
|
|
247
|
+
sx={{
|
|
248
|
+
display: 'grid',
|
|
249
|
+
gridAutoFlow: 'column',
|
|
250
|
+
top: theme.spacings.sm,
|
|
251
|
+
gap: theme.spacings.xxs,
|
|
252
|
+
position: 'absolute',
|
|
253
|
+
right: theme.spacings.sm,
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
{!disableZoom && (
|
|
257
|
+
<Fab
|
|
258
|
+
size='small'
|
|
259
|
+
className={classes.toggleIcon}
|
|
260
|
+
disabled={!hasImages}
|
|
261
|
+
onMouseUp={toggle}
|
|
262
|
+
aria-label='Toggle Fullscreen'
|
|
263
|
+
sx={{ boxShadow: 6 }}
|
|
264
|
+
>
|
|
265
|
+
{!zoomed ? (
|
|
266
|
+
<IconSvg src={iconFullscreen} />
|
|
267
|
+
) : (
|
|
268
|
+
<IconSvg src={iconFullscreenExit} />
|
|
269
|
+
)}
|
|
270
|
+
</Fab>
|
|
271
|
+
)}
|
|
272
|
+
</MotionBox>
|
|
273
|
+
<Box
|
|
274
|
+
className={classes.centerLeft}
|
|
275
|
+
sx={{
|
|
276
|
+
display: 'grid',
|
|
277
|
+
gridAutoFlow: 'row',
|
|
278
|
+
gap: theme.spacings.xxs,
|
|
279
|
+
position: 'absolute',
|
|
280
|
+
left: theme.spacings.sm,
|
|
281
|
+
top: `calc(50% - 28px)`,
|
|
282
|
+
}}
|
|
283
|
+
>
|
|
284
|
+
<ScrollerButton
|
|
213
285
|
layout
|
|
214
286
|
layoutDependency={zoomed}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
height={image.height}
|
|
218
|
-
loading={idx === 0 ? 'eager' : 'lazy'}
|
|
219
|
-
sx={{ display: 'block', objectFit: 'contain' }}
|
|
220
|
-
sizes={{
|
|
221
|
-
0: '100vw',
|
|
222
|
-
[theme.breakpoints.values.md]: zoomed ? '100vw' : '60vw',
|
|
223
|
-
}}
|
|
224
|
-
alt={image.alt || `Product Image ${idx}` || undefined}
|
|
225
|
-
dontReportWronglySizedImages
|
|
226
|
-
/>
|
|
227
|
-
))}
|
|
228
|
-
</Scroller>
|
|
229
|
-
<MotionBox
|
|
230
|
-
layout='position'
|
|
231
|
-
layoutDependency={zoomed}
|
|
232
|
-
className={classes.topRight}
|
|
233
|
-
sx={{
|
|
234
|
-
display: 'grid',
|
|
235
|
-
gridAutoFlow: 'column',
|
|
236
|
-
top: theme.spacings.sm,
|
|
237
|
-
gap: theme.spacings.xxs,
|
|
238
|
-
position: 'absolute',
|
|
239
|
-
right: theme.spacings.sm,
|
|
240
|
-
}}
|
|
241
|
-
>
|
|
242
|
-
{!disableZoom && (
|
|
243
|
-
<Fab
|
|
287
|
+
direction='left'
|
|
288
|
+
showButtons={showButtons}
|
|
244
289
|
size='small'
|
|
245
|
-
className={classes.
|
|
246
|
-
disabled={!hasImages}
|
|
247
|
-
onMouseUp={toggle}
|
|
248
|
-
aria-label='Toggle Fullscreen'
|
|
249
|
-
sx={{ boxShadow: 6 }}
|
|
290
|
+
className={classes.sliderButtons}
|
|
250
291
|
>
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
gridAutoFlow: 'row',
|
|
264
|
-
gap: theme.spacings.xxs,
|
|
265
|
-
position: 'absolute',
|
|
266
|
-
left: theme.spacings.sm,
|
|
267
|
-
top: `calc(50% - 28px)`,
|
|
268
|
-
}}
|
|
269
|
-
>
|
|
270
|
-
<ScrollerButton
|
|
271
|
-
layout
|
|
272
|
-
layoutDependency={zoomed}
|
|
273
|
-
direction='left'
|
|
274
|
-
showButtons={showButtons}
|
|
275
|
-
size='small'
|
|
276
|
-
className={classes.sliderButtons}
|
|
277
|
-
>
|
|
278
|
-
<IconSvg src={iconChevronLeft} />
|
|
279
|
-
</ScrollerButton>
|
|
280
|
-
</Box>
|
|
281
|
-
<Box
|
|
282
|
-
className={classes.centerRight}
|
|
283
|
-
sx={{
|
|
284
|
-
display: 'grid',
|
|
285
|
-
gap: theme.spacings.xxs,
|
|
286
|
-
position: 'absolute',
|
|
287
|
-
right: theme.spacings.sm,
|
|
288
|
-
top: `calc(50% - 28px)`,
|
|
289
|
-
}}
|
|
290
|
-
>
|
|
291
|
-
<ScrollerButton
|
|
292
|
-
layout
|
|
293
|
-
layoutDependency={zoomed}
|
|
294
|
-
direction='right'
|
|
295
|
-
showButtons={showButtons}
|
|
296
|
-
size='small'
|
|
297
|
-
className={classes.sliderButtons}
|
|
292
|
+
<IconSvg src={iconChevronLeft} />
|
|
293
|
+
</ScrollerButton>
|
|
294
|
+
</Box>
|
|
295
|
+
<Box
|
|
296
|
+
className={classes.centerRight}
|
|
297
|
+
sx={{
|
|
298
|
+
display: 'grid',
|
|
299
|
+
gap: theme.spacings.xxs,
|
|
300
|
+
position: 'absolute',
|
|
301
|
+
right: theme.spacings.sm,
|
|
302
|
+
top: `calc(50% - 28px)`,
|
|
303
|
+
}}
|
|
298
304
|
>
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
bottom: theme.spacings.xxs,
|
|
311
|
-
justifyContent: 'center',
|
|
312
|
-
width: '100%',
|
|
313
|
-
pointerEvents: 'none',
|
|
314
|
-
'& > *': {
|
|
315
|
-
pointerEvents: 'all',
|
|
316
|
-
},
|
|
317
|
-
}}
|
|
318
|
-
>
|
|
319
|
-
<ScrollerDots layout='position' layoutDependency={zoomed} />
|
|
320
|
-
</Box>
|
|
321
|
-
</MotionBox>
|
|
305
|
+
<ScrollerButton
|
|
306
|
+
layout
|
|
307
|
+
layoutDependency={zoomed}
|
|
308
|
+
direction='right'
|
|
309
|
+
showButtons={showButtons}
|
|
310
|
+
size='small'
|
|
311
|
+
className={classes.sliderButtons}
|
|
312
|
+
>
|
|
313
|
+
<IconSvg src={iconChevronRight} />
|
|
314
|
+
</ScrollerButton>
|
|
315
|
+
</Box>
|
|
322
316
|
|
|
317
|
+
<Box
|
|
318
|
+
className={classes.bottomCenter}
|
|
319
|
+
sx={{
|
|
320
|
+
display: 'flex',
|
|
321
|
+
gap: theme.spacings.xxs,
|
|
322
|
+
position: 'absolute',
|
|
323
|
+
bottom: theme.spacings.xxs,
|
|
324
|
+
justifyContent: 'center',
|
|
325
|
+
width: '100%',
|
|
326
|
+
pointerEvents: 'none',
|
|
327
|
+
'& > *': {
|
|
328
|
+
pointerEvents: 'all',
|
|
329
|
+
},
|
|
330
|
+
}}
|
|
331
|
+
>
|
|
332
|
+
{import.meta.graphCommerce.sidebarGallery?.paginationVariant ===
|
|
333
|
+
'THUMBNAILS_BOTTOM' ? (
|
|
334
|
+
<ScrollerThumbnails images={images} />
|
|
335
|
+
) : (
|
|
336
|
+
<ScrollerDots />
|
|
337
|
+
)}
|
|
338
|
+
</Box>
|
|
339
|
+
</MotionBox>
|
|
340
|
+
</TrapFocus>
|
|
323
341
|
<Box
|
|
324
342
|
className={classes.sidebarWrapper}
|
|
325
343
|
sx={[
|
|
326
344
|
{
|
|
345
|
+
gridArea: 'right',
|
|
327
346
|
boxSizing: 'content-box',
|
|
328
347
|
display: 'grid',
|
|
329
348
|
justifyItems: 'start',
|
package/JsonLd/JsonLd.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { dvh } from '@graphcommerce/framer-utils'
|
|
|
3
3
|
import { Box, SxProps, Theme } from '@mui/material'
|
|
4
4
|
import { useTransform, useScroll } from 'framer-motion'
|
|
5
5
|
import { LayoutProvider } from '../../Layout/components/LayoutProvider'
|
|
6
|
+
import { SkipLink } from '../../SkipLink/SkipLink'
|
|
6
7
|
import { extendableComponent } from '../../Styles'
|
|
7
8
|
import { useFabSize } from '../../Theme'
|
|
8
9
|
|
|
@@ -67,6 +68,7 @@ export function LayoutDefault(props: LayoutDefaultProps) {
|
|
|
67
68
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
68
69
|
]}
|
|
69
70
|
>
|
|
71
|
+
<SkipLink />
|
|
70
72
|
<LayoutProvider scroll={scrollYOffset}>
|
|
71
73
|
{beforeHeader}
|
|
72
74
|
<Box
|
|
@@ -144,7 +146,10 @@ export function LayoutDefault(props: LayoutDefaultProps) {
|
|
|
144
146
|
) : (
|
|
145
147
|
<div />
|
|
146
148
|
)}
|
|
147
|
-
<div className={classes.children}>
|
|
149
|
+
<div className={classes.children}>
|
|
150
|
+
<div id='skip-nav' tabIndex={-1} />
|
|
151
|
+
{children}
|
|
152
|
+
</div>
|
|
148
153
|
<div className={classes.footer}>{footer}</div>
|
|
149
154
|
</LayoutProvider>
|
|
150
155
|
</Box>
|
|
@@ -69,6 +69,7 @@ export function DesktopNavBar(props: MenuTabsProps) {
|
|
|
69
69
|
}}
|
|
70
70
|
direction='left'
|
|
71
71
|
size='small'
|
|
72
|
+
tabIndex={-1}
|
|
72
73
|
className={`${classes.left} ${classes.button}`}
|
|
73
74
|
>
|
|
74
75
|
<IconSvg src={iconLeft ?? iconChevronLeft} />
|
|
@@ -94,6 +95,7 @@ export function DesktopNavBar(props: MenuTabsProps) {
|
|
|
94
95
|
}}
|
|
95
96
|
direction='right'
|
|
96
97
|
size='small'
|
|
98
|
+
tabIndex={-1}
|
|
97
99
|
className={`${classes.right} ${classes.button}`}
|
|
98
100
|
>
|
|
99
101
|
<IconSvg src={iconRight ?? iconChevronRight} />
|
package/LayoutParts/Logo.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
SxProps,
|
|
6
6
|
Theme,
|
|
7
7
|
unstable_composeClasses as composeClasses,
|
|
8
|
+
Box,
|
|
8
9
|
} from '@mui/material'
|
|
9
10
|
import { useRouter } from 'next/router'
|
|
10
11
|
import { forwardRef } from 'react'
|
|
@@ -22,12 +23,7 @@ const getLogoUtilityClass = (slot: string) => generateUtilityClass(name, slot)
|
|
|
22
23
|
const useUtilityClasses = ({ classes }: LogoClassProps) =>
|
|
23
24
|
composeClasses({ logo: ['logo'], parent: ['parent'] }, getLogoUtilityClass, classes)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
const LogoContainer = styled(NextLink, {
|
|
27
|
-
name,
|
|
28
|
-
slot: 'parent',
|
|
29
|
-
overridesResolver: (_props, styles) => styles.parent,
|
|
30
|
-
})(({ theme }) => ({
|
|
26
|
+
const commonLogoStyles: SxProps<Theme> = {
|
|
31
27
|
height: '100%',
|
|
32
28
|
width: 'max-content',
|
|
33
29
|
display: 'flex',
|
|
@@ -35,12 +31,13 @@ const LogoContainer = styled(NextLink, {
|
|
|
35
31
|
margin: '0 auto',
|
|
36
32
|
justifyContent: 'center',
|
|
37
33
|
pointerEvents: 'all',
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const LogoContainer = styled(NextLink, {
|
|
37
|
+
name,
|
|
38
|
+
slot: 'parent',
|
|
39
|
+
overridesResolver: (_props, styles) => styles.parent,
|
|
40
|
+
})({})
|
|
44
41
|
|
|
45
42
|
export type LogoProps = {
|
|
46
43
|
href?: `/${string}`
|
|
@@ -51,7 +48,6 @@ export type LogoProps = {
|
|
|
51
48
|
export const Logo = forwardRef<HTMLAnchorElement, LogoProps>((props, ref) => {
|
|
52
49
|
const { href = '/', image, sx } = props
|
|
53
50
|
const router = useRouter()
|
|
54
|
-
|
|
55
51
|
const classes = useUtilityClasses(props)
|
|
56
52
|
|
|
57
53
|
const img = (
|
|
@@ -63,13 +59,17 @@ export const Logo = forwardRef<HTMLAnchorElement, LogoProps>((props, ref) => {
|
|
|
63
59
|
/>
|
|
64
60
|
)
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
const shouldRedirect = router.asPath.split('?')[0] !== href
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Box
|
|
66
|
+
component={shouldRedirect ? LogoContainer : 'div'}
|
|
67
|
+
href={shouldRedirect ? href : undefined}
|
|
68
|
+
ref={ref}
|
|
69
|
+
sx={[...(Array.isArray(sx) ? sx : [sx]), commonLogoStyles]}
|
|
70
|
+
className={classes.parent}
|
|
71
|
+
>
|
|
72
72
|
{img}
|
|
73
|
-
</
|
|
73
|
+
</Box>
|
|
74
74
|
)
|
|
75
75
|
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useState, useRef, startTransition, useLayoutEffect, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
// Make sure the server doesn't choke on the useLayoutEffect
|
|
4
|
+
export const useLayoutEffect2 = typeof window !== 'undefined' ? useLayoutEffect : useEffect
|
|
5
|
+
|
|
6
|
+
export type LazyHydrateProps = {
|
|
7
|
+
/**
|
|
8
|
+
* The content is always rendered on the server and on the client it uses the server rendered HTML until it is hydrated.
|
|
9
|
+
*/
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* When a boolean is provided, the IntersectionObserver is disabled and hydrates the component when the value becomes true.
|
|
14
|
+
*
|
|
15
|
+
* For example:
|
|
16
|
+
* - Disable the hydration functionality completely: `<LazyHydrate hydrated={true}>`
|
|
17
|
+
* - Hydrate the component on some state `<LazyHydrate hydrated={someState}>` where someState initially is false and later becomes true.
|
|
18
|
+
*/
|
|
19
|
+
hydrated?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* LazyHydrate can defer the hydration of a component until it becomes visible.
|
|
24
|
+
* OR manually by using the hydrated prop.
|
|
25
|
+
* This can be a way to improve the TBT of a page.
|
|
26
|
+
*/
|
|
27
|
+
export function LazyHydrate(props: LazyHydrateProps) {
|
|
28
|
+
const { hydrated, children } = props
|
|
29
|
+
const rootRef = useRef<HTMLElement>(null)
|
|
30
|
+
|
|
31
|
+
const [isHydrated, setIsHydrated] = useState(hydrated || false)
|
|
32
|
+
if (!isHydrated && hydrated) setIsHydrated(true)
|
|
33
|
+
|
|
34
|
+
useLayoutEffect2(() => {
|
|
35
|
+
// If we are manually hydrating, we watch that value and do not use the IntersectionObserver
|
|
36
|
+
if (isHydrated || !rootRef.current) return undefined
|
|
37
|
+
|
|
38
|
+
// If the element wasn't rendered on the server, we hydrate it immediately
|
|
39
|
+
if (!rootRef.current?.hasAttribute('data-lazy-hydrate')) {
|
|
40
|
+
setIsHydrated(true)
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The user has opted to manually hydrate the component
|
|
45
|
+
if (hydrated !== undefined) return undefined
|
|
46
|
+
|
|
47
|
+
const observer = new IntersectionObserver(
|
|
48
|
+
([entry]) => {
|
|
49
|
+
if (entry.isIntersecting && entry.intersectionRatio > 0) {
|
|
50
|
+
startTransition(() => setIsHydrated(true))
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{ rootMargin: '200px' },
|
|
54
|
+
)
|
|
55
|
+
observer.observe(rootRef.current)
|
|
56
|
+
|
|
57
|
+
return () => observer.disconnect()
|
|
58
|
+
}, [hydrated, isHydrated])
|
|
59
|
+
|
|
60
|
+
if (isHydrated) {
|
|
61
|
+
return <section>{children}</section>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof window === 'undefined') {
|
|
65
|
+
return <section data-lazy-hydrate>{children}</section>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
// eslint-disable-next-line react/no-danger
|
|
70
|
+
<section ref={rootRef} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning />
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './LazyHydrate'
|