@graphcommerce/next-ui 8.1.0-canary.2 → 8.1.0-canary.22

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.
@@ -13,7 +13,7 @@ import { extendableComponent, responsiveVal } from '../Styles'
13
13
  import { breakpointVal } from '../Styles/breakpointVal'
14
14
 
15
15
  type Variants = 'outlined' | 'default'
16
- type Size = 'large' | 'medium' | 'small'
16
+ type Size = 'large' | 'medium' | 'small' | 'responsive'
17
17
  type Color = 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'
18
18
  type Layout = 'inline' | 'grid' | 'list' | 'stack'
19
19
 
@@ -47,6 +47,7 @@ export type ActionCardProps = {
47
47
 
48
48
  const parts = [
49
49
  'root',
50
+ 'rootInner',
50
51
  'image',
51
52
  'title',
52
53
  'action',
@@ -77,6 +78,13 @@ const { withState, selectors } = extendableComponent<StateProps, typeof name, ty
77
78
 
78
79
  export const actionCardSelectors = selectors
79
80
 
81
+ export const actionCardImageSizes = {
82
+ small: responsiveVal(60, 80),
83
+ medium: responsiveVal(60, 80),
84
+ large: responsiveVal(100, 120),
85
+ responsive: responsiveVal(60, 120),
86
+ }
87
+
80
88
  export function ActionCard(props: ActionCardProps) {
81
89
  const {
82
90
  title,
@@ -92,7 +100,7 @@ export function ActionCard(props: ActionCardProps) {
92
100
  selected = false,
93
101
  reset,
94
102
  disabled = false,
95
- size = 'medium',
103
+ size = 'responsive',
96
104
  color = 'primary',
97
105
  variant = 'outlined',
98
106
  layout = 'list',
@@ -140,6 +148,12 @@ export function ActionCard(props: ActionCardProps) {
140
148
  py: responsiveVal(12, 14),
141
149
  display: 'block',
142
150
  },
151
+ '&.sizeResponsive': {
152
+ px: responsiveVal(8, 16),
153
+ py: responsiveVal(4, 14),
154
+ display: { xs: 'flex', md: 'block', lg: 'block' },
155
+ [theme.breakpoints.down('md')]: { typography: 'body2' },
156
+ },
143
157
 
144
158
  '&.variantDefault': {
145
159
  position: 'relative',
@@ -186,6 +200,13 @@ export function ActionCard(props: ActionCardProps) {
186
200
  mb: { xs: '-5px', sm: '-7px', md: '-8px' },
187
201
  },
188
202
  },
203
+ '&.sizeResponsive': {
204
+ mt: responsiveVal(2, 8),
205
+ mb: responsiveVal(3, 9),
206
+ '&::after': {
207
+ mb: responsiveVal(-2, -8),
208
+ },
209
+ },
189
210
  },
190
211
 
191
212
  '&.variantOutlined': {
@@ -261,6 +282,7 @@ export function ActionCard(props: ActionCardProps) {
261
282
  ]}
262
283
  >
263
284
  <Box
285
+ className={classes.rootInner}
264
286
  sx={{
265
287
  display: 'flex',
266
288
  flexDirection: 'row',
@@ -301,6 +323,7 @@ export function ActionCard(props: ActionCardProps) {
301
323
  '&.sizeSmall': { typography: 'body2' },
302
324
  '&.sizeMedium': { typography: 'body1' },
303
325
  '&.sizeLarge': { typography: 'h6' },
326
+ '&.sizeResponsive': { typography: { xs: 'body2', md: 'body1', lg: 'h6' } },
304
327
  }}
305
328
  >
306
329
  {title}
@@ -341,8 +364,9 @@ export function ActionCard(props: ActionCardProps) {
341
364
  sx={{
342
365
  textAlign: 'right',
343
366
  typography: 'body1',
344
- '&.sizeMedium': { typographty: 'subtitle1' },
367
+ '&.sizeMedium': { typography: 'subtitle1' },
345
368
  '&.sizeLarge': { typography: 'h6' },
369
+ '&.sizeResponsive': { typography: { xs: 'body1', md: 'subtitle1', lg: 'h6' } },
346
370
  }}
347
371
  >
348
372
  {price}
@@ -1,5 +1,10 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies */
2
- import { Controller, ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
+ import {
3
+ Controller,
4
+ ControllerProps,
5
+ FieldValues,
6
+ useController,
7
+ } from '@graphcommerce/react-hook-form'
3
8
  import React, { MouseEventHandler } from 'react'
4
9
  import { ActionCardProps } from './ActionCard'
5
10
  import { ActionCardList, ActionCardListProps } from './ActionCardList'
@@ -43,38 +48,41 @@ export function ActionCardListForm<
43
48
  : selectValues === itemValue
44
49
  }
45
50
 
51
+ const {
52
+ field: { onChange, value, ref },
53
+ fieldState,
54
+ formState,
55
+ } = useController({
56
+ ...props,
57
+ control,
58
+ name,
59
+ defaultValue,
60
+ rules: { required: errorMessage || required, ...rules },
61
+ })
62
+
46
63
  return (
47
- <Controller
48
- {...props}
49
- control={control}
50
- name={name}
51
- defaultValue={defaultValue}
52
- rules={{ required: errorMessage || required, ...rules }}
53
- render={({ field: { onChange, value, ref }, fieldState, formState }) => (
54
- <ActionCardList
55
- {...other}
56
- multiple={multiple}
57
- required={required}
58
- value={value}
59
- ref={ref}
60
- onChange={(_, incomming) => onChange(incomming)}
61
- error={formState.isSubmitted && !!fieldState.error}
62
- errorMessage={fieldState.error?.message}
63
- >
64
- {items.map((item) => (
65
- <RenderItem
66
- {...item}
67
- key={item.value ?? ''}
68
- value={item.value}
69
- selected={onSelect(item.value, value)}
70
- onReset={(e) => {
71
- e.preventDefault()
72
- onChange(null)
73
- }}
74
- />
75
- ))}
76
- </ActionCardList>
77
- )}
78
- />
64
+ <ActionCardList
65
+ {...other}
66
+ multiple={multiple}
67
+ required={required}
68
+ value={value}
69
+ ref={ref}
70
+ onChange={(_, incomming) => onChange(incomming)}
71
+ error={formState.isSubmitted && !!fieldState.error}
72
+ errorMessage={fieldState.error?.message}
73
+ >
74
+ {items.map((item) => (
75
+ <RenderItem
76
+ {...item}
77
+ key={item.value ?? ''}
78
+ value={item.value}
79
+ selected={onSelect(item.value, value)}
80
+ onReset={(e) => {
81
+ e.preventDefault()
82
+ onChange(null)
83
+ }}
84
+ />
85
+ ))}
86
+ </ActionCardList>
79
87
  )
80
88
  }
package/CHANGELOG.md CHANGED
@@ -1,6 +1,178 @@
1
1
  # Change Log
2
2
 
3
- ## 8.1.0-canary.2
3
+ ## 8.1.0-canary.22
4
+
5
+ ## 8.1.0-canary.21
6
+
7
+ ## 8.1.0-canary.20
8
+
9
+ ## 8.1.0-canary.19
10
+
11
+ ## 8.1.0-canary.18
12
+
13
+ ## 8.1.0-canary.17
14
+
15
+ ### Minor Changes
16
+
17
+ - [#2209](https://github.com/graphcommerce-org/graphcommerce/pull/2209) [`2872cab`](https://github.com/graphcommerce-org/graphcommerce/commit/2872cabdca9ee4f0378fd411c6a633f71bb92f1f) - Removed useMediaQuery from the wishlist and cart ItemActionCard and replaced it with a new responsive size prop.
18
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
19
+
20
+ ## 8.1.0-canary.16
21
+
22
+ ## 8.1.0-canary.15
23
+
24
+ ## 8.1.0-canary.14
25
+
26
+ ## 8.1.0-canary.13
27
+
28
+ ## 8.1.0-canary.12
29
+
30
+ ## 8.1.0-canary.11
31
+
32
+ ## 8.1.0-canary.10
33
+
34
+ ## 8.1.0-canary.9
35
+
36
+ ### Patch Changes
37
+
38
+ - [#2223](https://github.com/graphcommerce-org/graphcommerce/pull/2223) [`d7459fe`](https://github.com/graphcommerce-org/graphcommerce/commit/d7459feb6e6902af09ab9ff766c0b3b1a74fb723) - Updated canonicalize helper for better multi domain support
39
+ ([@bramvanderholst](https://github.com/bramvanderholst))
40
+
41
+ ## 8.1.0-canary.8
42
+
43
+ ### Patch Changes
44
+
45
+ - [#2247](https://github.com/graphcommerce-org/graphcommerce/pull/2247) [`444e446`](https://github.com/graphcommerce-org/graphcommerce/commit/444e446a218cc9da3defb940a6d5cce0229ff845) - Added clear upgrade instructions for linguiLocale
46
+ ([@paales](https://github.com/paales))
47
+
48
+ ## 8.1.0-canary.7
49
+
50
+ ## 8.1.0-canary.6
51
+
52
+ ## 8.1.0-canary.5
53
+
54
+ ## 8.0.6-canary.4
55
+
56
+ ## 8.0.6-canary.3
57
+
58
+ ## 8.0.6-canary.2
59
+
60
+ ### Patch Changes
61
+
62
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Added useIsSSR hook that will properly resolve when the page is rendered on the server and on first render, but will return false when a component is rendered on the client directly.
63
+ ([@FrankHarland](https://github.com/FrankHarland))
64
+
65
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`0767bc4`](https://github.com/graphcommerce-org/graphcommerce/commit/0767bc40f7b596209f24ca4e745ff0441f3275c9) - Upgrade input components to no longer use muiRegister, which improves INP scores
66
+ ([@FrankHarland](https://github.com/FrankHarland))
67
+
68
+ ## 8.0.6-canary.1
69
+
70
+ ## 8.0.6-canary.0
71
+
72
+ ### Patch Changes
73
+
74
+ - [#2196](https://github.com/graphcommerce-org/graphcommerce/pull/2196) [`84c50e4`](https://github.com/graphcommerce-org/graphcommerce/commit/84c50e49a1a7f154d4a8f4045c37e773e20283ad) - Allow Lingui to use linguiLocale with country identifiers like `en-us`, it would always load `en` in this case. Introced a new `useLocale` hook to use the correct locale string to use in Intl methods.
75
+ ([@paales](https://github.com/paales))
76
+
77
+ ## 8.0.5
78
+
79
+ ### Patch Changes
80
+
81
+ - [#2236](https://github.com/graphcommerce-org/graphcommerce/pull/2236) [`85fb916`](https://github.com/graphcommerce-org/graphcommerce/commit/85fb916e5ec2a1456a93a59bf92a5f414fee8595) - Solve issue where the inert prop wouldnt be properly forwarded
82
+ ([@paales](https://github.com/paales))
83
+
84
+ - [#2235](https://github.com/graphcommerce-org/graphcommerce/pull/2235) [`de99691`](https://github.com/graphcommerce-org/graphcommerce/commit/de9969155e271cc2535147479b80b602a1b9c335) - The Lazyhydrate component to accepts any BoxProps. Replaced `<section>` with a `<Box>` so it doesn't hold symantic meaning.
85
+
86
+ Please note: If you've used child selectors to style the section, please make sure you update your styles. ([@carlocarels90](https://github.com/carlocarels90))
87
+
88
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`6f3fe60`](https://github.com/graphcommerce-org/graphcommerce/commit/6f3fe60441762d55cb46d587279121e8fe469cdd) - Decreased layout shift on product pages by reserving space for sidebar
89
+ ([@bramvanderholst](https://github.com/bramvanderholst))
90
+
91
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`cde3c31`](https://github.com/graphcommerce-org/graphcommerce/commit/cde3c310abf2ac3c82d1062d5fb0a4c00ba50cff) - Removed unnecessary vendor prefixes
92
+ ([@bramvanderholst](https://github.com/bramvanderholst))
93
+
94
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`4c83636`](https://github.com/graphcommerce-org/graphcommerce/commit/4c836366c324881ee5121c645c5f94fc60e3ebb3) - Prevent horizontal scrollbar on small screens when using SidebarGallery
95
+ ([@bramvanderholst](https://github.com/bramvanderholst))
96
+
97
+ - [#2230](https://github.com/graphcommerce-org/graphcommerce/pull/2230) [`1da6b82`](https://github.com/graphcommerce-org/graphcommerce/commit/1da6b82dbb7e1543542d809ea625a8867643ea68) - Fix menu item visibility in accessability tree
98
+ ([@JoshuaS98](https://github.com/JoshuaS98))
99
+
100
+ ## 8.0.5-canary.10
101
+
102
+ ## 8.0.5-canary.9
103
+
104
+ ## 8.0.5-canary.8
105
+
106
+ ## 8.0.5-canary.7
107
+
108
+ ### Patch Changes
109
+
110
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`6f3fe60`](https://github.com/graphcommerce-org/graphcommerce/commit/6f3fe60441762d55cb46d587279121e8fe469cdd) - Decreased layout shift on product pages by reserving space for sidebar
111
+ ([@bramvanderholst](https://github.com/bramvanderholst))
112
+
113
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`cde3c31`](https://github.com/graphcommerce-org/graphcommerce/commit/cde3c310abf2ac3c82d1062d5fb0a4c00ba50cff) - Removed unnecessary vendor prefixes
114
+ ([@bramvanderholst](https://github.com/bramvanderholst))
115
+
116
+ - [#2241](https://github.com/graphcommerce-org/graphcommerce/pull/2241) [`4c83636`](https://github.com/graphcommerce-org/graphcommerce/commit/4c836366c324881ee5121c645c5f94fc60e3ebb3) - Prevent horizontal scrollbar on small screens when using SidebarGallery
117
+ ([@bramvanderholst](https://github.com/bramvanderholst))
118
+
119
+ ## 8.0.5-canary.6
120
+
121
+ ## 8.0.5-canary.5
122
+
123
+ ## 8.0.5-canary.4
124
+
125
+ ## 8.0.5-canary.3
126
+
127
+ ### Patch Changes
128
+
129
+ - [#2236](https://github.com/graphcommerce-org/graphcommerce/pull/2236) [`85fb916`](https://github.com/graphcommerce-org/graphcommerce/commit/85fb916e5ec2a1456a93a59bf92a5f414fee8595) - Solve issue where the inert prop wouldnt be properly forwarded
130
+ ([@paales](https://github.com/paales))
131
+
132
+ ## 8.0.5-canary.2
133
+
134
+ ### Patch Changes
135
+
136
+ - [#2235](https://github.com/graphcommerce-org/graphcommerce/pull/2235) [`de99691`](https://github.com/graphcommerce-org/graphcommerce/commit/de9969155e271cc2535147479b80b602a1b9c335) - The Lazyhydrate component to accepts any BoxProps. Replaced `<section>` with a `<Box>` so it doesn't hold symantic meaning.
137
+
138
+ Please note: If you've used child selectors to style the section, please make sure you update your styles. ([@carlocarels90](https://github.com/carlocarels90))
139
+
140
+ ## 8.0.5-canary.1
141
+
142
+ ## 8.0.5-canary.0
143
+
144
+ ### Patch Changes
145
+
146
+ - [#2230](https://github.com/graphcommerce-org/graphcommerce/pull/2230) [`1da6b82`](https://github.com/graphcommerce-org/graphcommerce/commit/1da6b82dbb7e1543542d809ea625a8867643ea68) - Fix menu item visibility in accessability tree
147
+ ([@JoshuaS98](https://github.com/JoshuaS98))
148
+
149
+ ## 8.0.4
150
+
151
+ ### Patch Changes
152
+
153
+ - [#2228](https://github.com/graphcommerce-org/graphcommerce/pull/2228) [`0c0cacb`](https://github.com/graphcommerce-org/graphcommerce/commit/0c0cacb8725f0a626ea5d3acf154d83c433c3eb7) - apply correct type for the inert property
154
+ ([@carlocarels90](https://github.com/carlocarels90))
155
+
156
+ ## 8.0.4-canary.1
157
+
158
+ ## 8.0.4-canary.0
159
+
160
+ ### Patch Changes
161
+
162
+ - [#2228](https://github.com/graphcommerce-org/graphcommerce/pull/2228) [`0c0cacb`](https://github.com/graphcommerce-org/graphcommerce/commit/0c0cacb8725f0a626ea5d3acf154d83c433c3eb7) - apply correct type for the inert property
163
+ ([@carlocarels90](https://github.com/carlocarels90))
164
+
165
+ ## 8.0.3
166
+
167
+ ## 8.0.3-canary.6
168
+
169
+ ## 8.0.3-canary.5
170
+
171
+ ## 8.0.3-canary.4
172
+
173
+ ## 8.0.3-canary.3
174
+
175
+ ## 8.0.3-canary.2
4
176
 
5
177
  ## 8.0.3-canary.1
6
178
 
@@ -31,7 +31,14 @@ import { iconChevronLeft, iconChevronRight, iconFullscreen, iconFullscreenExit }
31
31
 
32
32
  const MotionBox = styled(m.div)({})
33
33
 
34
- type OwnerState = { zoomed: boolean; disableZoom: boolean }
34
+ type SidebarGalleryVariant = 'default' | 'oneColumn'
35
+
36
+ type OwnerState = {
37
+ zoomed: boolean
38
+ disableZoom: boolean
39
+ sticky: boolean
40
+ variantMd: SidebarGalleryVariant
41
+ }
35
42
  const name = 'SidebarGallery' as const
36
43
  const parts = [
37
44
  'row',
@@ -60,6 +67,8 @@ export type SidebarGalleryProps = {
60
67
  routeHash?: string
61
68
  sx?: SxProps<Theme>
62
69
  disableZoom?: boolean
70
+ disableSticky?: boolean
71
+ variantMd?: SidebarGalleryVariant
63
72
  } & Pick<ScrollerButtonProps, 'showButtons'>
64
73
 
65
74
  export function SidebarGallery(props: SidebarGalleryProps) {
@@ -71,6 +80,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
71
80
  routeHash = 'gallery',
72
81
  showButtons,
73
82
  disableZoom = false,
83
+ disableSticky = false,
84
+ variantMd = 'default',
74
85
  } = props
75
86
 
76
87
  const router = useRouter()
@@ -103,7 +114,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
103
114
  }
104
115
  }
105
116
 
106
- const classes = withState({ zoomed, disableZoom })
117
+ const classes = withState({ zoomed, disableZoom, sticky: !disableSticky, variantMd })
107
118
  const theme = useTheme()
108
119
  const windowRef = useRef(typeof window !== 'undefined' ? window : null)
109
120
 
@@ -144,23 +155,31 @@ export function SidebarGallery(props: SidebarGalleryProps) {
144
155
  display: 'grid',
145
156
  gridTemplate: '"left" "right"',
146
157
  [theme.breakpoints.up('md')]: {
147
- // gridTemplateColumns: '1fr auto',
148
- gridTemplate: '"left right" / 1fr auto',
158
+ '&:not(.variantMdOneColumn)': {
159
+ gridTemplate: `"left right" / 1fr calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
160
+ theme.page.horizontal
161
+ } * 2)`,
162
+ },
149
163
  },
150
164
  background:
151
165
  theme.palette.mode === 'light'
152
166
  ? theme.palette.background.image
153
167
  : theme.palette.background.paper,
154
- paddingRight: `calc((100% - ${theme.breakpoints.values.lg}px) / 2)`,
155
- },
156
- zoomed && {
157
- position: 'relative',
158
- zIndex: theme.zIndex.modal,
159
- marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
160
- [theme.breakpoints.up('md')]: {
161
- marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - ${theme.spacings.lg})`,
168
+
169
+ '&:not(.variantMdOneColumn)': {
170
+ paddingRight: `calc((100% - ${theme.breakpoints.values.lg}px) / 2)`,
171
+ },
172
+
173
+ '&.zoomed': {
174
+ position: 'relative',
175
+ zIndex: theme.zIndex.modal,
176
+ marginTop: `calc(${theme.appShell.headerHeightSm} * -1)`,
177
+ [theme.breakpoints.up('md')]: {
178
+ marginTop: `calc(${theme.appShell.headerHeightMd} * -1 - ${theme.spacings.lg})`,
179
+ gridTemplateColumns: '1fr auto',
180
+ },
181
+ paddingRight: 0,
162
182
  },
163
- paddingRight: 0,
164
183
  },
165
184
  ]}
166
185
  >
@@ -178,14 +197,18 @@ export function SidebarGallery(props: SidebarGalleryProps) {
178
197
  position: 'relative',
179
198
  paddingTop: `min(${ratio}, ${maxHeight})`,
180
199
  [theme.breakpoints.down('md')]: {
181
- width: '100vw',
200
+ width: '100%',
182
201
  },
183
202
  [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,
203
+ '&:not(.variantMdOneColumn)': {
204
+ height: `calc(${dvh(100)} - ${theme.appShell.headerHeightMd} - ${
205
+ theme.spacings.lg
206
+ })`,
207
+ '&.sticky': {
208
+ position: 'sticky',
209
+ },
210
+ top: theme.appShell.headerHeightMd,
211
+ },
189
212
  },
190
213
  },
191
214
  zoomed && {
@@ -348,11 +371,6 @@ export function SidebarGallery(props: SidebarGalleryProps) {
348
371
  justifyItems: 'start',
349
372
  alignContent: 'center',
350
373
  position: 'relative',
351
- [theme.breakpoints.up('md')]: {
352
- width: `calc(${responsiveVal(300, 500, theme.breakpoints.values.lg)} + ${
353
- theme.page.horizontal
354
- } * 2)`,
355
- },
356
374
  },
357
375
  zoomed && {
358
376
  [theme.breakpoints.up('md')]: {
@@ -373,9 +391,11 @@ export function SidebarGallery(props: SidebarGalleryProps) {
373
391
  sx={{
374
392
  boxSizing: 'border-box',
375
393
  width: '100%',
376
- padding: `${theme.spacings.lg} ${theme.page.horizontal}`,
377
- [theme.breakpoints.up('md')]: {
378
- paddingLeft: theme.spacings.lg,
394
+ '&:not(.variantMdOneColumn)': {
395
+ padding: `${theme.spacings.lg} ${theme.page.horizontal}`,
396
+ [theme.breakpoints.up('md')]: {
397
+ paddingLeft: theme.spacings.lg,
398
+ },
379
399
  },
380
400
  }}
381
401
  >
@@ -9,6 +9,7 @@ export type LayoutOverlayState = Omit<
9
9
  | 'mdSpacingTop'
10
10
  | 'smSpacingTop'
11
11
  | 'overlayPaneProps'
12
+ | 'disableInert'
12
13
  | 'widthMd'
13
14
  | 'widthSm'
14
15
  >
@@ -1,9 +1,10 @@
1
+ import { Box, BoxProps } from '@mui/material'
1
2
  import React, { useState, useRef, startTransition, useLayoutEffect, useEffect } from 'react'
2
3
 
3
4
  // Make sure the server doesn't choke on the useLayoutEffect
4
5
  export const useLayoutEffect2 = typeof window !== 'undefined' ? useLayoutEffect : useEffect
5
6
 
6
- export type LazyHydrateProps = {
7
+ export type LazyHydrateProps = BoxProps<'div'> & {
7
8
  /**
8
9
  * The content is always rendered on the server and on the client it uses the server rendered HTML until it is hydrated.
9
10
  */
@@ -25,8 +26,8 @@ export type LazyHydrateProps = {
25
26
  * This can be a way to improve the TBT of a page.
26
27
  */
27
28
  export function LazyHydrate(props: LazyHydrateProps) {
28
- const { hydrated, children } = props
29
- const rootRef = useRef<HTMLElement>(null)
29
+ const { hydrated, children, ...elementProps } = props
30
+ const rootRef = useRef<HTMLDivElement>(null)
30
31
 
31
32
  const [isHydrated, setIsHydrated] = useState(hydrated || false)
32
33
  if (!isHydrated && hydrated) setIsHydrated(true)
@@ -58,15 +59,24 @@ export function LazyHydrate(props: LazyHydrateProps) {
58
59
  }, [hydrated, isHydrated])
59
60
 
60
61
  if (isHydrated) {
61
- return <section>{children}</section>
62
+ return <Box {...elementProps}>{children}</Box>
62
63
  }
63
64
 
64
65
  if (typeof window === 'undefined') {
65
- return <section data-lazy-hydrate>{children}</section>
66
+ return (
67
+ <Box data-lazy-hydrate {...elementProps}>
68
+ {children}
69
+ </Box>
70
+ )
66
71
  }
67
72
 
68
73
  return (
69
- // eslint-disable-next-line react/no-danger
70
- <section ref={rootRef} dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning />
74
+ <Box
75
+ ref={rootRef}
76
+ // eslint-disable-next-line react/no-danger
77
+ dangerouslySetInnerHTML={{ __html: '' }}
78
+ suppressHydrationWarning
79
+ {...elementProps}
80
+ />
71
81
  )
72
82
  }
@@ -162,6 +162,7 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => {
162
162
  }
163
163
  right={
164
164
  <Fab
165
+ disabled={!activeAndNotClosing}
165
166
  color='inherit'
166
167
  onClick={handleClose}
167
168
  sx={{ boxShadow: 'none', my: fabMarginY }}
@@ -52,6 +52,7 @@ export type LayoutOverlayBaseProps = {
52
52
  isPresent: boolean
53
53
  safeToRemove?: (() => void) | null | undefined
54
54
  overlayPaneProps?: MotionProps
55
+ disableInert?: boolean
55
56
 
56
57
  /* For `variantSm='left|right' */
57
58
  widthSm?: string | false
@@ -109,6 +110,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
109
110
  isPresent,
110
111
  safeToRemove,
111
112
  overlayPaneProps,
113
+ disableInert,
112
114
  widthMd = 'max(800px, 50vw)',
113
115
  widthSm = 'max(300px, 80vw)',
114
116
  } = props
@@ -247,7 +249,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
247
249
  const cancelY = scroll.y.on('change', handleScroll)
248
250
 
249
251
  const ro = new ResizeObserver(handleResize)
250
- ro.observe(scrollerRef.current)
252
+ if (scrollerRef.current) ro.observe(scrollerRef.current)
251
253
  ro.observe(beforeRef.current)
252
254
  ro.observe(overlayPaneRef.current)
253
255
  ro.observe(overlayRef.current)
@@ -363,7 +365,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
363
365
  return (
364
366
  <>
365
367
  <MotionDiv
366
- inert={active ? undefined : 'true'}
368
+ inert={active ? undefined : ('true' as unknown as boolean)}
367
369
  className={classes.backdrop}
368
370
  style={{ opacity: positions.open.visible }}
369
371
  sx={[
@@ -385,7 +387,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
385
387
  ]}
386
388
  />
387
389
  <Scroller
388
- inert={active ? undefined : 'true'}
390
+ inert={disableInert || active ? undefined : ('true' as unknown as boolean)}
389
391
  className={`${classes.scroller} ${className ?? ''}`}
390
392
  grid={false}
391
393
  onClick={onClickAway}
@@ -31,6 +31,7 @@ export function OverlaySsr(props: OverlayProps) {
31
31
  <OverlayContainer active={active} hidden={hidden}>
32
32
  <ScrollerProvider {...variantsToScrollSnapType(props)}>
33
33
  <OverlayBase
34
+ disableInert
34
35
  variantMd={variantMd}
35
36
  variantSm={variantSm}
36
37
  active={active}
@@ -70,11 +70,16 @@ export function canonicalize(router: PartialNextRouter, incoming?: Canonical) {
70
70
  if (localeDomain) {
71
71
  canonical = localeDomain
72
72
  } else {
73
- href = addBasePath(addLocale(as, curLocale, router.defaultLocale))
73
+ const conf = storefrontConfig(router.locale)
74
+
75
+ href = addBasePath(
76
+ addLocale(as, curLocale, conf?.domain ? conf.locale : router.defaultLocale),
77
+ )
78
+
79
+ let siteUrl = conf?.canonicalBaseUrl || import.meta.graphCommerce.canonicalBaseUrl
80
+
81
+ if (conf?.domain && !conf?.canonicalBaseUrl) siteUrl = `https://${conf.domain}`
74
82
 
75
- let siteUrl =
76
- storefrontConfig(router.locale)?.canonicalBaseUrl ||
77
- import.meta.graphCommerce.canonicalBaseUrl
78
83
  if (siteUrl.endsWith('/')) siteUrl = siteUrl.slice(0, -1)
79
84
 
80
85
  canonical = `${siteUrl}${href}`
@@ -10,5 +10,5 @@ export const createEmotionCache = () => {
10
10
  insertionPoint = emotionInsertionPoint ?? undefined
11
11
  }
12
12
 
13
- return createCache({ key: 'mui-style', insertionPoint })
13
+ return createCache({ key: 'mui-style', insertionPoint, stylisPlugins: [] })
14
14
  }
@@ -1,17 +1,23 @@
1
+ import { useLocale } from '../hooks/useLocale'
2
+
1
3
  export type TimeAgoProps = {
2
4
  date: Date
5
+ /**
6
+ * @deprecated No longer used
7
+ */
3
8
  locale?: string
4
9
  }
5
10
 
6
11
  export function TimeAgo(props: TimeAgoProps) {
7
- const { date, locale = 'en' } = props
12
+ const { date } = props
8
13
  const msPerMinute = 60 * 1000
9
14
  const msPerHour = msPerMinute * 60
10
15
  const msPerDay = msPerHour * 24
11
16
 
12
17
  const timestamp = date.getTime()
13
18
  const elapsed = Date.now() - timestamp
14
- const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
19
+
20
+ const rtf = new Intl.RelativeTimeFormat(useLocale(), { numeric: 'auto' })
15
21
 
16
22
  if (elapsed < msPerMinute) {
17
23
  return <span>{rtf.format(-Math.floor(elapsed / 1000), 'seconds')}</span>
package/hooks/index.ts CHANGED
@@ -5,3 +5,5 @@ export * from './useNumberFormat'
5
5
  export * from './useMemoObject'
6
6
  export * from './useStorefrontConfig'
7
7
  export * from './useUrlQuery'
8
+ export * from './useSsr'
9
+ export * from './useLocale'
@@ -1,15 +1,11 @@
1
- import { normalizeLocale } from '@graphcommerce/lingui-next'
2
- import { useRouter } from 'next/router'
3
1
  import { useMemo } from 'react'
2
+ import { useLocale } from './useLocale'
4
3
 
5
4
  export type DateTimeFormatProps = Intl.DateTimeFormatOptions
6
5
 
7
6
  export function useDateTimeFormat(props?: DateTimeFormatProps) {
8
- const { locale } = useRouter()
7
+ const locale = useLocale()
9
8
 
10
- const formatter = useMemo(
11
- () => new Intl.DateTimeFormat(normalizeLocale(locale), props),
12
- [locale, props],
13
- )
9
+ const formatter = useMemo(() => new Intl.DateTimeFormat(locale, props), [locale, props])
14
10
  return formatter
15
11
  }
@@ -0,0 +1,7 @@
1
+ import { normalizeLocale } from '@graphcommerce/lingui-next'
2
+ import { useStorefrontConfig } from './useStorefrontConfig'
3
+
4
+ export function useLocale() {
5
+ const { locale } = useStorefrontConfig()
6
+ return normalizeLocale(locale)
7
+ }
@@ -1,5 +1,24 @@
1
1
  import { Breakpoint, useTheme } from '@mui/material'
2
- import { useMemo } from 'react'
2
+ import { useMotionValue } from 'framer-motion'
3
+ import { useEffect, useMemo } from 'react'
4
+
5
+ export function useMatchMediaMotionValue(
6
+ direction: 'up' | 'down',
7
+ breakpointKey: number | Breakpoint,
8
+ ) {
9
+ const matchValue = useMotionValue(false)
10
+ const theme = useTheme()
11
+ const query = theme.breakpoints[direction](breakpointKey).replace(/^@media( ?)/m, '')
12
+
13
+ useEffect(() => {
14
+ const mql = globalThis?.matchMedia(query)
15
+ const matcher = (e: MediaQueryListEvent) => matchValue.set(e.matches)
16
+ mql.addEventListener('change', matcher)
17
+ return () => mql.removeEventListener('change', matcher)
18
+ }, [matchValue, query])
19
+
20
+ return matchValue
21
+ }
3
22
 
4
23
  export function useMatchMedia() {
5
24
  const theme = useTheme()
@@ -1,15 +1,10 @@
1
- import { normalizeLocale } from '@graphcommerce/lingui-next'
2
- import { useRouter } from 'next/router'
3
1
  import { useMemo } from 'react'
2
+ import { useLocale } from './useLocale'
4
3
 
5
4
  export type NumberFormatProps = Intl.NumberFormatOptions
6
5
 
7
6
  export function useNumberFormat(props?: NumberFormatProps) {
8
- const { locale } = useRouter()
9
-
10
- const formatter = useMemo(
11
- () => new Intl.NumberFormat(normalizeLocale(locale), props),
12
- [locale, props],
13
- )
7
+ const locale = useLocale()
8
+ const formatter = useMemo(() => new Intl.NumberFormat(locale, props), [locale, props])
14
9
  return formatter
15
10
  }
@@ -0,0 +1,11 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ const emptySubscribe = () => () => {}
4
+
5
+ export function useIsSSR() {
6
+ return useSyncExternalStore(
7
+ emptySubscribe,
8
+ () => false,
9
+ () => true,
10
+ )
11
+ }
package/icons.ts ADDED
@@ -0,0 +1,50 @@
1
+ export { default as iconArrowDown } from './icons/arrow-down.svg'
2
+ export { default as iconArrowBack } from './icons/arrow-left.svg'
3
+ export { default as iconArrowForward } from './icons/arrow-right.svg'
4
+ export { default as iconArrowUp } from './icons/arrow-up.svg'
5
+ export { default as iconShoppingBag } from './icons/bag.svg'
6
+ export { default as iconInvoice } from './icons/box-alt.svg'
7
+ export { default as iconBox } from './icons/box.svg'
8
+ export { default as iconOrderBefore } from './icons/calendar.svg'
9
+ export { default as iconCancelAlt } from './icons/cancel-alt.svg'
10
+ export { default as iconCartAdd } from './icons/cart-add.svg'
11
+ export { default as iconCart } from './icons/cart.svg'
12
+ export { default as iconChat } from './icons/chat-alt.svg'
13
+ export { default as iconCustomerService } from './icons/chat.svg'
14
+ export { default as iconChevronDown } from './icons/chevron-down.svg'
15
+ export { default as iconChevronBack, default as iconChevronLeft } from './icons/chevron-left.svg'
16
+ export { default as iconChevronRight } from './icons/chevron-right.svg'
17
+ export { default as iconChevronUp } from './icons/chevron-up.svg'
18
+ export { default as iconCirle } from './icons/circle.svg'
19
+ export { default as iconClose } from './icons/close.svg'
20
+ export { default as iconCompare } from './icons/compare-arrows.svg'
21
+ export { default as iconCreditCard, default as iconId } from './icons/credit-card.svg'
22
+ export { default as iconEllypsis } from './icons/ellypsis.svg'
23
+ export { default as iconEmail, default as iconEmailOutline } from './icons/envelope-alt.svg'
24
+ export { default as icon404 } from './icons/explore.svg'
25
+ export { default as iconEyeClosed } from './icons/eye-closed.svg'
26
+ export { default as iconEyeCrossed } from './icons/eye-crossed.svg'
27
+ export { default as iconEye } from './icons/eye.svg'
28
+ export { default as iconHeart } from './icons/favourite.svg'
29
+ export { default as iconMenu } from './icons/hamburger.svg'
30
+ export { default as iconParty } from './icons/happy-face.svg'
31
+ export { default as iconAddresses, default as iconHome } from './icons/home-alt.svg'
32
+ export { default as iconLanguage } from './icons/language.svg'
33
+ export { default as iconLocation } from './icons/location.svg'
34
+ export { default as iconLock } from './icons/lock.svg'
35
+ export { default as iconFullscreen } from './icons/maximise.svg'
36
+ export { default as iconFullscreenExit } from './icons/minimise.svg'
37
+ export { default as iconMin } from './icons/minus.svg'
38
+ export { default as iconMoon } from './icons/moon.svg'
39
+ export { default as iconNewspaper } from './icons/news.svg'
40
+ export { default as iconCheckmark } from './icons/ok.svg'
41
+ export { default as iconPerson } from './icons/person-alt.svg'
42
+ export { default as iconPlay } from './icons/play.svg'
43
+ export { default as iconPlus } from './icons/plus.svg'
44
+ export { default as iconShutdown } from './icons/power.svg'
45
+ export { default as iconRefresh } from './icons/refresh.svg'
46
+ export { default as iconSadFace } from './icons/sad-face.svg'
47
+ export { default as iconSearch } from './icons/search.svg'
48
+ export { default as iconPhone } from './icons/smartphone.svg'
49
+ export { default as iconStar } from './icons/star.svg'
50
+ export { default as iconSun } from './icons/sun.svg'
package/index.ts CHANGED
@@ -59,3 +59,5 @@ export * from './UspList/UspListItem'
59
59
  export * from './hooks'
60
60
  export * from './icons'
61
61
  export * from './utils/cookie'
62
+ export * from './utils/sitemap'
63
+ export * from './utils/robots'
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": "8.1.0-canary.2",
5
+ "version": "8.1.0-canary.22",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -17,6 +17,7 @@
17
17
  "@emotion/server": "^11.11.0",
18
18
  "@emotion/styled": "^11.11.0",
19
19
  "cookie": "^0.6.0",
20
+ "next-sitemap": "4.2.3",
20
21
  "react-is": "^18.2.0"
21
22
  },
22
23
  "devDependencies": {
@@ -25,14 +26,14 @@
25
26
  "typescript": "5.3.3"
26
27
  },
27
28
  "peerDependencies": {
28
- "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.2",
29
- "@graphcommerce/framer-next-pages": "^8.1.0-canary.2",
30
- "@graphcommerce/framer-scroller": "^8.1.0-canary.2",
31
- "@graphcommerce/framer-utils": "^8.1.0-canary.2",
32
- "@graphcommerce/image": "^8.1.0-canary.2",
33
- "@graphcommerce/lingui-next": "^8.1.0-canary.2",
34
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.2",
35
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.2",
29
+ "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.22",
30
+ "@graphcommerce/framer-next-pages": "^8.1.0-canary.22",
31
+ "@graphcommerce/framer-scroller": "^8.1.0-canary.22",
32
+ "@graphcommerce/framer-utils": "^8.1.0-canary.22",
33
+ "@graphcommerce/image": "^8.1.0-canary.22",
34
+ "@graphcommerce/lingui-next": "^8.1.0-canary.22",
35
+ "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.22",
36
+ "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.22",
36
37
  "@lingui/core": "^4.2.1",
37
38
  "@lingui/macro": "^4.2.1",
38
39
  "@lingui/react": "^4.2.1",
package/types.d.ts CHANGED
@@ -6,12 +6,6 @@ import './Theme/createTheme'
6
6
  // eslint-disable-next-line react/no-typos
7
7
  import 'react'
8
8
 
9
- declare module 'react' {
10
- interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
11
- inert?: 'true'
12
- }
13
- }
14
-
15
9
  declare module '*.po' {
16
10
  const messages: Record<
17
11
  string,
@@ -0,0 +1,41 @@
1
+ import { GetServerSidePropsContext } from 'next'
2
+
3
+ type Stringifyable = boolean | string | number | null | undefined
4
+
5
+ /**
6
+ * Tagged template literal for robots.txt that will automatically stringify values and indent them correctly.
7
+ * https://developers.google.com/search/docs/crawling-indexing/robots/robots_txt#syntax
8
+ */
9
+ export function robotsTxt(strings: TemplateStringsArray, ...values: Stringifyable[]) {
10
+ return strings
11
+ .reduce((acc, str, i) => {
12
+ let value = values[i]
13
+
14
+ if (Array.isArray(value)) {
15
+ const [conditional, val] = value
16
+ value = conditional ? val : null
17
+ }
18
+ if (!value) value = ''
19
+ if (typeof value === 'boolean') value = value ? 'true' : 'false'
20
+ if (typeof value === 'number') value = String(value)
21
+
22
+ return acc + str + value
23
+ }, '')
24
+ .split('\n')
25
+ .map((v) => v.trim())
26
+ .join('\n')
27
+ .trim()
28
+ }
29
+
30
+ // eslint-disable-next-line @typescript-eslint/require-await
31
+ export async function getServerSidePropsRobotsTxt(
32
+ context: GetServerSidePropsContext,
33
+ robots: string,
34
+ ) {
35
+ context.res.setHeader('Content-Type', 'text/plain')
36
+ context.res.write(robots)
37
+ context.res.end()
38
+ context.res.setHeader('Cache-Control', `public, s-maxage=${60 * 60 * 2}`)
39
+
40
+ return { props: {} }
41
+ }
@@ -0,0 +1,47 @@
1
+ import { GetServerSidePropsContext, GetStaticPathsResult } from 'next'
2
+ import { getServerSideSitemapLegacy, ISitemapField } from 'next-sitemap'
3
+ import { canonicalize } from '../PageMeta/PageMeta'
4
+
5
+ export function excludeSitemap(excludes: string[]) {
6
+ const regexp = excludes.map((exclude) => new RegExp(exclude.replace(/\*/g, '.*?')))
7
+
8
+ return (path: string) => !regexp.some((pattern) => pattern.test(`/${path}`))
9
+ }
10
+
11
+ export function staticPathsToString(
12
+ path: GetStaticPathsResult<{ url: string[] | string }>['paths'][0],
13
+ ) {
14
+ if (typeof path === 'string') return path
15
+ if (typeof path.params.url === 'string') return path.params.url
16
+ return path.params.url.join('/')
17
+ }
18
+
19
+ export function toSitemapFields(
20
+ context: GetServerSidePropsContext,
21
+ paths: string[],
22
+ ): ISitemapField[] {
23
+ const lastmod = new Date().toISOString()
24
+ const options = {
25
+ locale: context.locale,
26
+ defaultLocale: context.defaultLocale,
27
+ pathname: '/',
28
+ isLocaleDomain: false,
29
+ }
30
+
31
+ const sitemapPaths = paths.map<ISitemapField>((path) => ({
32
+ loc: canonicalize(options, `/${path}`) ?? '',
33
+ lastmod,
34
+ changefreq: 'daily',
35
+ priority: 0.7,
36
+ }))
37
+
38
+ return sitemapPaths
39
+ }
40
+
41
+ export function getServerSidePropsSitemap(
42
+ context: GetServerSidePropsContext,
43
+ paths: ISitemapField[],
44
+ ) {
45
+ context.res.setHeader('Cache-Control', `public, s-maxage=${60 * 60 * 2}`)
46
+ return getServerSideSitemapLegacy(context, paths)
47
+ }
package/icons/index.ts DELETED
@@ -1,48 +0,0 @@
1
- export { default as iconArrowBack } from './arrow-left.svg'
2
- export { default as iconArrowForward } from './arrow-right.svg'
3
- export { default as iconShoppingBag } from './bag.svg'
4
- export { default as iconInvoice } from './box-alt.svg'
5
- export { default as iconBox } from './box.svg'
6
- export { default as iconOrderBefore } from './calendar.svg'
7
- export { default as iconCancelAlt } from './cancel-alt.svg'
8
- export { default as iconCartAdd } from './cart-add.svg'
9
- export { default as iconCart } from './cart.svg'
10
- export { default as iconChat } from './chat-alt.svg'
11
- export { default as iconCustomerService } from './chat.svg'
12
- export { default as iconChevronDown } from './chevron-down.svg'
13
- export { default as iconChevronBack, default as iconChevronLeft } from './chevron-left.svg'
14
- export { default as iconChevronRight } from './chevron-right.svg'
15
- export { default as iconChevronUp } from './chevron-up.svg'
16
- export { default as iconCirle } from './circle.svg'
17
- export { default as iconClose } from './close.svg'
18
- export { default as iconCreditCard, default as iconId } from './credit-card.svg'
19
- export { default as iconEllypsis } from './ellypsis.svg'
20
- export { default as iconEmail, default as iconEmailOutline } from './envelope-alt.svg'
21
- export { default as icon404 } from './explore.svg'
22
- export { default as iconEyeClosed } from './eye-closed.svg'
23
- export { default as iconEyeCrossed } from './eye-crossed.svg'
24
- export { default as iconEye } from './eye.svg'
25
- export { default as iconHeart } from './favourite.svg'
26
- export { default as iconMenu } from './hamburger.svg'
27
- export { default as iconParty } from './happy-face.svg'
28
- export { default as iconAddresses, default as iconHome } from './home-alt.svg'
29
- export { default as iconLanguage } from './language.svg'
30
- export { default as iconLocation } from './location.svg'
31
- export { default as iconLock } from './lock.svg'
32
- export { default as iconFullscreen } from './maximise.svg'
33
- export { default as iconFullscreenExit } from './minimise.svg'
34
- export { default as iconMin } from './minus.svg'
35
- export { default as iconMoon } from './moon.svg'
36
- export { default as iconNewspaper } from './news.svg'
37
- export { default as iconCheckmark } from './ok.svg'
38
- export { default as iconPerson } from './person-alt.svg'
39
- export { default as iconPlay } from './play.svg'
40
- export { default as iconPlus } from './plus.svg'
41
- export { default as iconShutdown } from './power.svg'
42
- export { default as iconRefresh } from './refresh.svg'
43
- export { default as iconSadFace } from './sad-face.svg'
44
- export { default as iconSearch } from './search.svg'
45
- export { default as iconPhone } from './smartphone.svg'
46
- export { default as iconStar } from './star.svg'
47
- export { default as iconSun } from './sun.svg'
48
- export { default as iconCompare } from './compare-arrows.svg'