@graphcommerce/next-ui 7.1.0-canary.9 → 8.0.0-canary.69

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.
@@ -55,6 +55,7 @@ const parts = [
55
55
  'after',
56
56
  'secondaryAction',
57
57
  'reset',
58
+ 'end',
58
59
  ] as const
59
60
  const name = 'ActionCard'
60
61
 
@@ -317,6 +318,7 @@ export function ActionCard(props: ActionCardProps) {
317
318
  </Box>
318
319
 
319
320
  <Box
321
+ className={classes.end}
320
322
  sx={{
321
323
  display: 'flex',
322
324
  flexDirection: 'column',
package/CHANGELOG.md CHANGED
@@ -1,5 +1,160 @@
1
1
  # Change Log
2
2
 
3
+ ## 8.0.0-canary.69
4
+
5
+ ## 7.1.0-canary.68
6
+
7
+ ## 7.1.0-canary.67
8
+
9
+ ### Patch Changes
10
+
11
+ - [#2108](https://github.com/graphcommerce-org/graphcommerce/pull/2108) [`7fc4bb9`](https://github.com/graphcommerce-org/graphcommerce/commit/7fc4bb925c59da46961c9656a2a67b37a9c2d652) - Removed the 'NoSSR' functionality from `<WaitForQueries/>` component as it slows down rendering. The 'feature' was necessary for the following use case: When hydrating a component that was server rendered and was living inside a `<Suspense />` component. It would cause an hydration error and this was the workaround. With useSuspenseQuery and React 18, this problem will not occur.
12
+ ([@StefanAngenent](https://github.com/StefanAngenent))
13
+
14
+ ## 7.1.0-canary.66
15
+
16
+ ## 7.1.0-canary.65
17
+
18
+ ## 7.1.0-canary.64
19
+
20
+ ## 7.1.0-canary.63
21
+
22
+ ## 7.1.0-canary.62
23
+
24
+ ## 7.1.0-canary.61
25
+
26
+ ### Patch Changes
27
+
28
+ - [#2125](https://github.com/graphcommerce-org/graphcommerce/pull/2125) [`5224ee500`](https://github.com/graphcommerce-org/graphcommerce/commit/5224ee5001c94a19f226fa36106e76739319297c) - If there is an open menu in an overlay, pressing the escape button now closes the menu instead of the overlay. ([@Jessevdpoel](https://github.com/Jessevdpoel))
29
+
30
+ ## 7.1.0-canary.60
31
+
32
+ ## 7.1.0-canary.59
33
+
34
+ ## 7.1.0-canary.58
35
+
36
+ ### Patch Changes
37
+
38
+ - [#2121](https://github.com/graphcommerce-org/graphcommerce/pull/2121) [`a5da6ffc8`](https://github.com/graphcommerce-org/graphcommerce/commit/a5da6ffc8be359e93c7bde986134f7162aae13b9) - Change the critical css injection location to be in the head instead of `<style>` tags in the body. It has a number of negative consequences, such as the famous "flash of unstyled content" (FOUC) and the re-paint and re-layout required. ([@paales](https://github.com/paales))
39
+
40
+ ## 7.1.0-canary.57
41
+
42
+ ## 7.1.0-canary.56
43
+
44
+ ### Patch Changes
45
+
46
+ - [#2123](https://github.com/graphcommerce-org/graphcommerce/pull/2123) [`8ad60f255`](https://github.com/graphcommerce-org/graphcommerce/commit/8ad60f255b747858c35dd6b6cf5c90147d960082) - Fixed schema-dts dependency issue ([@bramvanderholst](https://github.com/bramvanderholst))
47
+
48
+ ## 7.1.0-canary.55
49
+
50
+ ### Patch Changes
51
+
52
+ - [#2004](https://github.com/graphcommerce-org/graphcommerce/pull/2004) [`da2135744`](https://github.com/graphcommerce-org/graphcommerce/commit/da2135744dddfa0d211c59589090ebb1977c38c9) - Added info icon for Snackbar when severity is set to info ([@bramvanderholst](https://github.com/bramvanderholst))
53
+
54
+ - [#2004](https://github.com/graphcommerce-org/graphcommerce/pull/2004) [`d608830ce`](https://github.com/graphcommerce-org/graphcommerce/commit/d608830ce77f85ff725cc106b9fc55a22012c74c) - Added ‘disableBackdropClick’ prop to MessageSnackbar to allow page interaction without closing the snackbar ([@bramvanderholst](https://github.com/bramvanderholst))
55
+
56
+ - [#2004](https://github.com/graphcommerce-org/graphcommerce/pull/2004) [`94e1ae811`](https://github.com/graphcommerce-org/graphcommerce/commit/94e1ae811fe9eb0051863e8be91c6399ddcdf22f) - Added DismissibleSnackbar component to allow messages to be shown only once ([@bramvanderholst](https://github.com/bramvanderholst))
57
+
58
+ - [#2004](https://github.com/graphcommerce-org/graphcommerce/pull/2004) [`53947d39f`](https://github.com/graphcommerce-org/graphcommerce/commit/53947d39f2f3ee578c14903c96a2b356d99d9475) - Implemented Message variant for RowColumnOne to show an important message which, after dismissing, will not show again ([@bramvanderholst](https://github.com/bramvanderholst))
59
+
60
+ ## 7.1.0-canary.54
61
+
62
+ ## 7.1.0-canary.53
63
+
64
+ ## 7.1.0-canary.52
65
+
66
+ ## 7.1.0-canary.51
67
+
68
+ ## 7.1.0-canary.50
69
+
70
+ ## 7.1.0-canary.49
71
+
72
+ ## 7.1.0-canary.48
73
+
74
+ ## 7.1.0-canary.47
75
+
76
+ ## 7.1.0-canary.46
77
+
78
+ ## 7.1.0-canary.45
79
+
80
+ ### Patch Changes
81
+
82
+ - [#2077](https://github.com/graphcommerce-org/graphcommerce/pull/2077) [`727d1004d`](https://github.com/graphcommerce-org/graphcommerce/commit/727d1004dfcb7dddf6e35b6b157a34491bb05cc6) - Fixed ItemScroller component className. Changed from SidebarSlider to ItemScroller ([@bramvanderholst](https://github.com/bramvanderholst))
83
+
84
+ ## 7.1.0-canary.38
85
+
86
+ ### Patch Changes
87
+
88
+ - [#2048](https://github.com/graphcommerce-org/graphcommerce/pull/2048) [`695f40cf2`](https://github.com/graphcommerce-org/graphcommerce/commit/695f40cf220636d17f04bc9b0ce86c549c740386) - filterNonNullable keys simplified the types which caused unions to be collapsed ([@Jessevdpoel](https://github.com/Jessevdpoel))
89
+
90
+ ## 7.1.0-canary.37
91
+
92
+ ## 7.1.0-canary.36
93
+
94
+ ## 7.1.0-canary.35
95
+
96
+ ## 7.1.0-canary.34
97
+
98
+ ## 7.1.0-canary.33
99
+
100
+ ## 7.1.0-canary.32
101
+
102
+ ## 7.1.0-canary.31
103
+
104
+ ## 7.1.0-canary.30
105
+
106
+ ### Patch Changes
107
+
108
+ - [#2105](https://github.com/graphcommerce-org/graphcommerce/pull/2105) [`185f9ddeb`](https://github.com/graphcommerce-org/graphcommerce/commit/185f9ddebff0eaf1f388faebe88a5d400294512a) - Fixed bug where the mobile menu wouldn't open after the first selected level ([@mikekeehnen](https://github.com/mikekeehnen))
109
+
110
+ ## 7.1.0-canary.29
111
+
112
+ ## 7.1.0-canary.28
113
+
114
+ ### Minor Changes
115
+
116
+ - [#2018](https://github.com/graphcommerce-org/graphcommerce/pull/2018) [`750aa6a72`](https://github.com/graphcommerce-org/graphcommerce/commit/750aa6a72710869d54244467253212e551d335e0) - Changed the layout of the succes page. We are using ActionCards right now to match the design of the cart. ([@Jessevdpoel](https://github.com/Jessevdpoel))
117
+
118
+ ## 7.1.0-canary.27
119
+
120
+ ## 7.1.0-canary.26
121
+
122
+ ## 7.1.0-canary.25
123
+
124
+ ## 7.1.0-canary.24
125
+
126
+ ## 7.1.0-canary.23
127
+
128
+ ## 7.1.0-canary.22
129
+
130
+ ## 7.1.0-canary.21
131
+
132
+ ## 7.1.0-canary.20
133
+
134
+ ## 7.1.0-canary.19
135
+
136
+ ## 7.1.0-canary.18
137
+
138
+ ## 7.1.0-canary.17
139
+
140
+ ## 7.1.0-canary.16
141
+
142
+ ## 7.1.0-canary.15
143
+
144
+ ## 7.1.0-canary.14
145
+
146
+ ### Patch Changes
147
+
148
+ - [#2045](https://github.com/graphcommerce-org/graphcommerce/pull/2045) [`1ac1e0989`](https://github.com/graphcommerce-org/graphcommerce/commit/1ac1e09897daadd646200cb3ddc2aa75a51e182e) - Make sure the product image gallery traps focus and scrollbar doesn't disappear suddenly ([@JoshuaS98](https://github.com/JoshuaS98))
149
+
150
+ ## 7.1.0-canary.13
151
+
152
+ ## 7.1.0-canary.12
153
+
154
+ ## 7.1.0-canary.11
155
+
156
+ ## 7.1.0-canary.10
157
+
3
158
  ## 7.1.0-canary.9
4
159
 
5
160
  ## 7.1.0-canary.8
@@ -1434,31 +1589,31 @@
1434
1589
  All occurences of `<Trans>` and `t` need to be replaced:
1435
1590
 
1436
1591
  ```tsx
1437
- import { Trans, t } from '@lingui/macro'
1592
+ import { Trans, t } from "@lingui/macro";
1438
1593
 
1439
1594
  function MyComponent() {
1440
- const foo = 'bar'
1595
+ const foo = "bar";
1441
1596
  return (
1442
1597
  <div aria-label={t`Account ${foo}`}>
1443
1598
  <Trans>My Translation {foo}</Trans>
1444
1599
  </div>
1445
- )
1600
+ );
1446
1601
  }
1447
1602
  ```
1448
1603
 
1449
1604
  Needs to be replaced with:
1450
1605
 
1451
1606
  ```tsx
1452
- import { Trans } from '@lingui/react'
1453
- import { i18n } from '@lingui/core'
1607
+ import { Trans } from "@lingui/react";
1608
+ import { i18n } from "@lingui/core";
1454
1609
 
1455
1610
  function MyComponent() {
1456
- const foo = 'bar'
1611
+ const foo = "bar";
1457
1612
  return (
1458
1613
  <div aria-label={i18n._(/* i18n */ `Account {foo}`, { foo })}>
1459
- <Trans key='My Translation {foo}' values={{ foo }}></Trans>
1614
+ <Trans key="My Translation {foo}" values={{ foo }}></Trans>
1460
1615
  </div>
1461
- )
1616
+ );
1462
1617
  }
1463
1618
  ```
1464
1619
 
@@ -0,0 +1,24 @@
1
+ extend input GraphCommerceConfig {
2
+ """
3
+ Configuration for the SidebarGallery component
4
+ """
5
+ sidebarGallery: SidebarGalleryConfig
6
+ }
7
+
8
+ """
9
+ Enumeration of all possible positions for the sidebar gallery thumbnails.
10
+ """
11
+ enum SidebarGalleryPaginationVariant {
12
+ DOTS
13
+ THUMBNAILS_BOTTOM
14
+ }
15
+
16
+ """
17
+ SidebarGalleryConfig will contain all configuration values for the Sidebar Gallery component.
18
+ """
19
+ input SidebarGalleryConfig {
20
+ """
21
+ Variant used for the pagination
22
+ """
23
+ paginationVariant: SidebarGalleryPaginationVariant
24
+ }
@@ -10,7 +10,7 @@ import { extendableComponent, responsiveVal } from '../Styles'
10
10
  import { useFabSize } from '../Theme'
11
11
  import { iconChevronLeft, iconChevronRight } from '../icons'
12
12
 
13
- const { classes } = extendableComponent('SidebarSlider', [
13
+ const { classes } = extendableComponent('ItemScroller', [
14
14
  'root',
15
15
  'grid',
16
16
  'sidebar',
@@ -21,13 +21,13 @@ const { classes } = extendableComponent('SidebarSlider', [
21
21
  'centerRight',
22
22
  ] as const)
23
23
 
24
- type SliderProps = {
24
+ export type ItemScrollerProps = {
25
25
  children: React.ReactNode
26
26
  sx?: SxProps<Theme>
27
27
  buttonSize?: ScrollerButtonProps['size']
28
28
  } & Pick<ScrollerButtonProps, 'showButtons'>
29
29
 
30
- export function ItemScroller(props: SliderProps) {
30
+ export function ItemScroller(props: ItemScrollerProps) {
31
31
  const { children, sx, buttonSize = 'responsive', showButtons } = props
32
32
 
33
33
  const size = useFabSize(buttonSize)
@@ -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 { Fab, useTheme, Box, styled, SxProps, Theme } from '@mui/material'
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
- <MotionBox
156
- layout
157
- layoutDependency={zoomed}
158
- className={classes.scrollerContainer}
159
- sx={[
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
- position: 'absolute',
197
- top: 0,
198
- width: '100%',
199
- height: '100%',
200
- gridAutoColumns: `100%`,
201
- gridTemplateRows: `100%`,
202
- cursor: disableZoom ? 'auto' : 'zoom-in',
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
- height: `var(--client-size-y)`,
206
- cursor: 'inherit',
192
+ position: 'relative',
193
+ top: 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
- {images.map((image, idx) => (
211
- <MotionImageAspect
212
- key={typeof image.src === 'string' ? image.src : idx}
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
- src={image.src}
216
- width={image.width}
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.toggleIcon}
246
- disabled={!hasImages}
247
- onMouseUp={toggle}
248
- aria-label='Toggle Fullscreen'
249
- sx={{ boxShadow: 6 }}
290
+ className={classes.sliderButtons}
250
291
  >
251
- {!zoomed ? (
252
- <IconSvg src={iconFullscreen} />
253
- ) : (
254
- <IconSvg src={iconFullscreenExit} />
255
- )}
256
- </Fab>
257
- )}
258
- </MotionBox>
259
- <Box
260
- className={classes.centerLeft}
261
- sx={{
262
- display: 'grid',
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
- <IconSvg src={iconChevronRight} />
300
- </ScrollerButton>
301
- </Box>
302
-
303
- <Box
304
- className={classes.bottomCenter}
305
- sx={{
306
- display: 'flex',
307
- px: theme.page.horizontal,
308
- gap: theme.spacings.xxs,
309
- position: 'absolute',
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
@@ -1,8 +1,6 @@
1
1
  import Head from 'next/head'
2
2
  import { safeJsonLdReplacer } from './safeJsonLdReplacer'
3
3
 
4
- export * as SchemaDts from 'schema-dts'
5
-
6
4
  export function JsonLd<T extends { '@type': string }>(props: {
7
5
  item: T & { '@context': 'https://schema.org' }
8
6
  }) {
@@ -178,6 +178,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
178
178
  (theme) => ({
179
179
  display: 'grid',
180
180
  alignItems: !stretchColumns ? 'start' : undefined,
181
+ justifyContent: 'end',
181
182
  [theme.breakpoints.down('md')]: {
182
183
  width:
183
184
  sizeSm !== 'floating'
@@ -311,7 +311,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
311
311
  const handleEscape = (e: KeyboardEvent | Event) => {
312
312
  if (active && (e as KeyboardEvent)?.key === 'Escape') closeOverlay()
313
313
  }
314
- useDomEvent(windowRef, 'keyup', handleEscape, { passive: true })
314
+ useDomEvent(windowRef, 'keydown', handleEscape, { passive: true })
315
315
 
316
316
  const dragging = useMotionValue(false)
317
317
  const scrollerElementRef = scrollerRef as React.RefObject<HTMLElement>
@@ -1,2 +1,3 @@
1
1
  export * from './OverlayBase'
2
2
  export * from './Overlay'
3
+ export * from './OverlayStickyBottom'
@@ -1,9 +1,8 @@
1
1
  import { GlobalStyles } from '@mui/material'
2
2
  import { LazyMotion } from 'framer-motion'
3
+ import { EmotionProvider, EmotionProviderProps } from '../Styles/EmotionProvider'
3
4
 
4
- export type GraphCommerceProviderProps = {
5
- children: React.ReactNode
6
- }
5
+ export type CssAndFramerMotionProviderProps = EmotionProviderProps
7
6
 
8
7
  /**
9
8
  * For [@emotion/core](https://emotion.sh/docs/introduction) and
@@ -12,22 +11,25 @@ export type GraphCommerceProviderProps = {
12
11
  * - Wrapps the app to lazily load framer-motion.
13
12
  * - Wrapps the app to have Emotion CSS styles
14
13
  */
15
- export function CssAndFramerMotionProvider({ children }: GraphCommerceProviderProps) {
14
+ export function CssAndFramerMotionProvider(props: CssAndFramerMotionProviderProps) {
15
+ const { children, emotionCache } = props
16
16
  return (
17
- <LazyMotion features={async () => (await import('./framerFeatures')).default} strict>
18
- {children}
19
- <GlobalStyles
20
- styles={{
21
- ':root': {
22
- '--client-size-y': '100vh',
23
- '--client-size-x': '100vw',
24
- '@supports(height: 100dvh)': {
25
- '--client-size-y': '100dvh',
26
- '--client-size-x': '100dvw',
17
+ <EmotionProvider emotionCache={emotionCache}>
18
+ <LazyMotion features={async () => (await import('./framerFeatures')).default} strict>
19
+ {children}
20
+ <GlobalStyles
21
+ styles={{
22
+ ':root': {
23
+ '--client-size-y': '100vh',
24
+ '--client-size-x': '100vw',
25
+ '@supports(height: 100dvh)': {
26
+ '--client-size-y': '100dvh',
27
+ '--client-size-x': '100dvw',
28
+ },
27
29
  },
28
- },
29
- }}
30
- />
31
- </LazyMotion>
30
+ }}
31
+ />
32
+ </LazyMotion>
33
+ </EmotionProvider>
32
34
  )
33
35
  }
@@ -2,8 +2,8 @@ import { usePageContext } from '@graphcommerce/framer-next-pages'
2
2
  import { addBasePath } from 'next/dist/client/add-base-path'
3
3
  import { addLocale } from 'next/dist/client/add-locale'
4
4
  import { getDomainLocale } from 'next/dist/client/get-domain-locale'
5
+ import { resolveHref } from 'next/dist/client/resolve-href'
5
6
  import { NextRouter } from 'next/dist/shared/lib/router/router'
6
- import { resolveHref } from 'next/dist/shared/lib/router/utils/resolve-href'
7
7
  import Head from 'next/head'
8
8
  import { useRouter } from 'next/router'
9
9
  import type {} from '@graphcommerce/next-config'
@@ -1,26 +1,23 @@
1
- import type { OptionalKeysOf, Simplify } from 'type-fest'
1
+ import type { OptionalKeysOf } from 'type-fest'
2
2
 
3
- export type RequiredKeys<
4
- T extends Record<string, unknown>,
5
- Keys extends OptionalKeysOf<T>,
6
- > = Simplify<
7
- Omit<T, Keys> & {
8
- [K in Keys]: NonNullable<T[K]>
9
- }
10
- >
3
+ export type RequiredKeys<T extends Record<string, unknown>, Keys extends OptionalKeysOf<T>> = Omit<
4
+ T,
5
+ Keys
6
+ > & { [K in Keys]: NonNullable<T[K]> }
11
7
 
12
8
  export function filterNonNullableKeys<
13
9
  T extends Record<string, unknown>,
14
10
  Keys extends OptionalKeysOf<T>,
15
- >(items: (T | null | undefined)[] | null | undefined, values: Keys[] = []) {
11
+ >(
12
+ items: (T | null | undefined)[] | null | undefined,
13
+ values: Keys[] = [],
14
+ ): RequiredKeys<T, Keys>[] {
16
15
  if (!items) return []
17
16
 
18
- const result = items.filter(
17
+ return items.filter(
19
18
  (item) =>
20
19
  item !== null &&
21
20
  typeof item !== 'undefined' &&
22
21
  values.every((v) => item?.[v] !== null && typeof item?.[v] !== 'undefined'),
23
- )
24
-
25
- return result as RequiredKeys<T, Keys>[]
22
+ ) as RequiredKeys<T, Keys>[]
26
23
  }
@@ -0,0 +1,12 @@
1
+ import {
2
+ DismissibleSnackbar,
3
+ DismissibleSnackbarProps,
4
+ } from '../../../Snackbar/DismissibleSnackbar'
5
+
6
+ export type VariantMessageProps = DismissibleSnackbarProps
7
+
8
+ export function VariantMessage(props: VariantMessageProps) {
9
+ const { ...rest } = props
10
+
11
+ return <DismissibleSnackbar variant='pill' severity='info' disableBackdropClick {...rest} />
12
+ }
@@ -0,0 +1 @@
1
+ export * from './VariantMessage'
package/Row/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './Row'
2
2
  export * from './ButtonLinkList'
3
3
  export * from './ColumnOne/ColumnOne'
4
+ export * from './ColumnOne/variant'
4
5
  export * from './ColumnOneBoxed/ColumnOneBoxed'
5
6
  export * from './ColumnOneCentered/ColumnOneCentered'
6
7
  export * from './ColumnThree/ColumnThree'
@@ -0,0 +1,28 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { MessageSnackbar } from './MessageSnackbar'
3
+ import { MessageSnackbarProps } from './MessageSnackbarImpl'
4
+
5
+ export type DismissibleSnackbarProps = MessageSnackbarProps & {
6
+ id: string
7
+ storageType?: 'localStorage' | 'sessionStorage'
8
+ }
9
+ export function DismissibleSnackbar(props: DismissibleSnackbarProps) {
10
+ const { storageType = 'localStorage', id, onClose, ...rest } = props
11
+ const messageId = `DismissibleSnackbar_${id}`
12
+ const [open, setOpen] = useState(false)
13
+
14
+ useEffect(() => {
15
+ setOpen(!globalThis[storageType]?.getItem(messageId))
16
+ }, [messageId, storageType])
17
+
18
+ return (
19
+ <MessageSnackbar
20
+ {...rest}
21
+ open={open}
22
+ onClose={() => {
23
+ globalThis[storageType]?.setItem(messageId, `${Date.now()}`)
24
+ onClose?.()
25
+ }}
26
+ />
27
+ )
28
+ }
@@ -14,6 +14,7 @@ import React, { useEffect, useState } from 'react'
14
14
  import { IconSvg } from '../IconSvg'
15
15
  import { extendableComponent, breakpointVal } from '../Styles'
16
16
  import { iconClose, iconCheckmark, iconSadFace } from '../icons'
17
+ import iconInfo from '../icons/info.svg'
17
18
 
18
19
  type Size = 'normal' | 'wide'
19
20
  type Variant = 'contained' | 'pill'
@@ -34,6 +35,10 @@ type OwnerState = {
34
35
  size?: Size
35
36
  severity?: 'success' | 'info' | 'warning' | 'error'
36
37
  variant?: Variant
38
+ /**
39
+ * Setting this to true allows interaction with the rest of the page without closing the Snackbar
40
+ */
41
+ disableBackdropClick?: boolean
37
42
  }
38
43
 
39
44
  const name = 'MessageSnackbarImpl' as const
@@ -56,6 +61,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
56
61
  onClose,
57
62
  severity = 'info',
58
63
  sx,
64
+ disableBackdropClick,
59
65
  ...snackbarProps
60
66
  } = props
61
67
 
@@ -65,7 +71,11 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
65
71
  setShowSnackbar(!!open)
66
72
  }, [open])
67
73
 
68
- const hideSnackbar = () => {
74
+ const hideSnackbar = (event: React.SyntheticEvent | Event, reason?: string) => {
75
+ if (disableBackdropClick && reason === 'clickaway') {
76
+ return
77
+ }
78
+
69
79
  setShowSnackbar(false)
70
80
  onClose?.()
71
81
  }
@@ -80,6 +90,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
80
90
  }
81
91
 
82
92
  let icon = iconCheckmark
93
+ if (severity === 'info') icon = iconInfo
83
94
  if (severity === 'error') icon = iconSadFace
84
95
 
85
96
  return (
@@ -91,7 +102,13 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
91
102
  open={showSnackbar}
92
103
  autoHideDuration={autoHide ? 5000 : null}
93
104
  className={classes.root}
94
- sx={sx}
105
+ sx={[
106
+ {
107
+ pointerEvents: 'none',
108
+ '& > *': { pointerEvents: 'auto' },
109
+ },
110
+ ...(Array.isArray(sx) ? sx : [sx]),
111
+ ]}
95
112
  onClose={hideSnackbar}
96
113
  >
97
114
  <SnackbarContent
@@ -1,14 +1,14 @@
1
- import createCache from '@emotion/cache'
2
1
  import type { EmotionCache } from '@emotion/cache'
3
2
  import { CacheProvider } from '@emotion/react'
3
+ import { createEmotionCache } from './createEmotionCache'
4
4
 
5
- let muiCache: EmotionCache | undefined
6
- export const createMuiCache = () => {
7
- muiCache = createCache({ key: 'mui' })
8
- return muiCache
9
- }
5
+ export type EmotionProviderProps = { children?: React.ReactNode; emotionCache?: EmotionCache }
6
+
7
+ const clientSideEmotionCache = createEmotionCache()
10
8
 
11
9
  /** Provider that is supposed to be used in your `pages/_app.tsx` */
12
- export function EmotionProvider({ children }: { children: React.ReactNode }) {
13
- return <CacheProvider value={muiCache ?? createMuiCache()}>{children}</CacheProvider>
10
+ export function EmotionProvider(props: EmotionProviderProps) {
11
+ const { children, emotionCache = clientSideEmotionCache } = props
12
+
13
+ return <CacheProvider value={emotionCache}>{children}</CacheProvider>
14
14
  }
@@ -0,0 +1,14 @@
1
+ import createCache from '@emotion/cache'
2
+
3
+ export const createEmotionCache = () => {
4
+ let insertionPoint: HTMLElement | undefined
5
+
6
+ if (typeof window !== 'undefined') {
7
+ const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
8
+ 'meta[name="emotion-insertion-point"]',
9
+ )
10
+ insertionPoint = emotionInsertionPoint ?? undefined
11
+ }
12
+
13
+ return createCache({ key: 'mui-style', insertionPoint })
14
+ }
@@ -1,36 +1,46 @@
1
1
  import type { EmotionJSX } from '@emotion/react/types/jsx-namespace'
2
2
  import createEmotionServer from '@emotion/server/create-instance'
3
3
  // eslint-disable-next-line @next/next/no-document-import-in-page
4
+ import { AppType } from 'next/app'
4
5
  import type NextDocument from 'next/document'
5
6
  // eslint-disable-next-line @next/next/no-document-import-in-page
6
7
  import type { DocumentContext, DocumentInitialProps } from 'next/document'
7
- import { createMuiCache } from './EmotionProvider'
8
+ import { EmotionProviderProps } from './EmotionProvider'
9
+ import { createEmotionCache } from './createEmotionCache'
8
10
 
9
11
  export type EmotionCacheProps = { emotionStyleTags: EmotionJSX.Element[] }
10
12
 
11
13
  export function withEmotionCache(Document: typeof NextDocument): typeof NextDocument {
12
14
  return class DocumentWithEmotionCache extends Document {
13
15
  static async getInitialProps(ctx: DocumentContext) {
14
- const emotionServer = createEmotionServer(createMuiCache())
15
- const initialProps = await Document.getInitialProps(ctx)
16
+ const cache = createEmotionCache()
17
+ const emotionServer = createEmotionServer(cache)
18
+
19
+ const originalRenderPage = ctx.renderPage
20
+ ctx.renderPage = () =>
21
+ originalRenderPage({
22
+ enhanceApp:
23
+ (App: React.ComponentType<React.ComponentProps<AppType> & EmotionProviderProps>) =>
24
+ (props) => <App emotionCache={cache} {...props} />,
25
+ })
16
26
 
17
- const emotionStyleTags = emotionServer
18
- .extractCriticalToChunks(initialProps.html)
19
- .styles.filter(({ css }) => css !== '')
20
- .map((style) => (
21
- <style
22
- data-emotion={`${style.key} ${style.ids.join(' ')}`}
23
- key={style.key}
24
- // eslint-disable-next-line react/no-danger
25
- dangerouslySetInnerHTML={{ __html: style.css }}
26
- />
27
- ))
27
+ const initialProps = await Document.getInitialProps(ctx)
28
+ // This is important. It prevents Emotion to render invalid HTML.
29
+ // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
30
+ const emotionStyles = emotionServer.extractCriticalToChunks(initialProps.html)
31
+ const emotionStyleTags = emotionStyles.styles.map((style) => (
32
+ <style
33
+ data-emotion={`${style.key} ${style.ids.join(' ')}`}
34
+ key={style.key}
35
+ // eslint-disable-next-line react/no-danger
36
+ dangerouslySetInnerHTML={{ __html: style.css }}
37
+ />
38
+ ))
28
39
 
29
- const props: DocumentInitialProps & EmotionCacheProps = {
40
+ return {
30
41
  ...initialProps,
31
42
  emotionStyleTags,
32
43
  }
33
- return props
34
44
  }
35
45
  }
36
46
  }
@@ -102,6 +102,9 @@ export function TextInputNumber(props: TextInputNumberProps) {
102
102
  width: responsiveVal(90, 120),
103
103
  },
104
104
  {
105
+ '& input[type=number]': {
106
+ MozAppearance: 'textfield',
107
+ },
105
108
  '& .MuiOutlinedInput-root': {
106
109
  px: '3px',
107
110
  display: 'grid',
@@ -22,8 +22,10 @@ export const MuiSnackbar: SnackbarVariants = [
22
22
  },
23
23
  },
24
24
  '&.MuiSnackbar-anchorOriginBottomCenter': {
25
- left: 0,
26
- right: 0,
25
+ [theme.breakpoints.up('md')]: {
26
+ left: theme.page.horizontal,
27
+ right: theme.page.horizontal,
28
+ },
27
29
  transform: 'unset',
28
30
  },
29
31
  }),
package/package.json CHANGED
@@ -2,9 +2,7 @@
2
2
  "name": "@graphcommerce/next-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "7.1.0-canary.9",
6
- "author": "",
7
- "license": "MIT",
5
+ "version": "8.0.0-canary.69",
8
6
  "sideEffects": false,
9
7
  "prettier": "@graphcommerce/prettier-config-pwa",
10
8
  "eslintConfig": {
@@ -18,30 +16,30 @@
18
16
  "@emotion/react": "^11.11.1",
19
17
  "@emotion/server": "^11.11.0",
20
18
  "@emotion/styled": "^11.11.0",
21
- "@graphcommerce/framer-next-pages": "7.1.0-canary.9",
22
- "@graphcommerce/framer-scroller": "7.1.0-canary.9",
23
- "@graphcommerce/framer-utils": "7.1.0-canary.9",
24
- "@graphcommerce/image": "7.1.0-canary.9",
25
- "cookie": "^0.5.0",
26
- "react-is": "^18.2.0",
27
- "schema-dts": "^1.1.0"
19
+ "cookie": "^0.6.0",
20
+ "react-is": "^18.2.0"
28
21
  },
29
22
  "devDependencies": {
30
- "@graphcommerce/eslint-config-pwa": "7.1.0-canary.9",
31
- "@graphcommerce/prettier-config-pwa": "7.1.0-canary.9",
32
- "@graphcommerce/typescript-config-pwa": "7.1.0-canary.9",
33
- "@types/cookie": "^0.5.2",
34
- "@types/react-is": "^18.2.1",
35
- "typescript": "5.2.2"
23
+ "@types/cookie": "^0.6.0",
24
+ "@types/react-is": "^18.2.4",
25
+ "schema-dts": "^1.1.2",
26
+ "typescript": "5.3.3"
36
27
  },
37
28
  "peerDependencies": {
29
+ "@graphcommerce/eslint-config-pwa": "^8.0.0-canary.69",
30
+ "@graphcommerce/framer-next-pages": "^8.0.0-canary.69",
31
+ "@graphcommerce/framer-scroller": "^8.0.0-canary.69",
32
+ "@graphcommerce/framer-utils": "^8.0.0-canary.69",
33
+ "@graphcommerce/image": "^8.0.0-canary.69",
34
+ "@graphcommerce/prettier-config-pwa": "^8.0.0-canary.69",
35
+ "@graphcommerce/typescript-config-pwa": "^8.0.0-canary.69",
38
36
  "@lingui/core": "^4.2.1",
39
37
  "@lingui/macro": "^4.2.1",
40
38
  "@lingui/react": "^4.2.1",
41
39
  "@mui/lab": "^5.0.0-alpha.68",
42
40
  "@mui/material": "^5.10.16",
43
41
  "framer-motion": "^10.0.0",
44
- "next": "^13.2.0",
42
+ "next": "*",
45
43
  "react": "^18.2.0",
46
44
  "react-dom": "^18.2.0"
47
45
  }