@graphcommerce/next-ui 4.26.0 → 4.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ActionCard/ActionCard.tsx +215 -131
- package/ActionCard/ActionCardList.tsx +92 -51
- package/ActionCard/ActionCardListForm.tsx +11 -11
- package/Button/Button.tsx +6 -2
- package/CHANGELOG.md +29 -0
- package/FramerScroller/SidebarGallery.tsx +2 -3
- package/Navigation/components/NavigationItem.tsx +22 -23
- package/Navigation/components/NavigationOverlay.tsx +7 -3
- package/Navigation/components/NavigationProvider.tsx +15 -2
- package/Navigation/hooks/useNavigation.ts +19 -6
- package/RenderType/RenderType.tsx +8 -1
- package/RenderType/filterNonNullableKeys.ts +1 -1
- package/hooks/index.ts +1 -0
- package/hooks/useMatchMedia.ts +17 -0
- package/package.json +3 -3
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { alpha, Box, BoxProps, ButtonBase, ButtonProps, SxProps, Theme } from '@mui/material'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { extendableComponent } from '../Styles'
|
|
4
4
|
import { breakpointVal } from '../Styles/breakpointVal'
|
|
5
5
|
|
|
6
|
+
type Variants = 'outlined' | 'default'
|
|
7
|
+
type Size = 'large' | 'medium' | 'small'
|
|
8
|
+
type Color = 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'
|
|
9
|
+
type Layout = 'inline' | 'grid' | 'list' | 'stack'
|
|
10
|
+
|
|
11
|
+
function isButtonProps(props: ButtonProps<'div'> | BoxProps<'div'>): props is ButtonProps<'div'> {
|
|
12
|
+
return props.onClick !== undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const RenderComponent = (props: ButtonProps<'div'> | BoxProps<'div'>) =>
|
|
16
|
+
isButtonProps(props) ? <ButtonBase component='div' {...props} /> : <Box {...props} />
|
|
17
|
+
|
|
6
18
|
export type ActionCardProps = {
|
|
19
|
+
variant?: Variants
|
|
20
|
+
size?: Size
|
|
21
|
+
color?: Color
|
|
22
|
+
layout?: Layout
|
|
7
23
|
sx?: SxProps<Theme>
|
|
8
24
|
title?: string | React.ReactNode
|
|
9
25
|
image?: React.ReactNode
|
|
@@ -14,10 +30,10 @@ export type ActionCardProps = {
|
|
|
14
30
|
secondaryAction?: React.ReactNode
|
|
15
31
|
onClick?: (event: React.MouseEvent<HTMLElement>, value: string | number) => void
|
|
16
32
|
selected?: boolean
|
|
17
|
-
hidden?: boolean
|
|
18
33
|
value: string | number
|
|
19
34
|
reset?: React.ReactNode
|
|
20
35
|
disabled?: boolean
|
|
36
|
+
error?: boolean
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
const parts = [
|
|
@@ -34,10 +50,14 @@ const parts = [
|
|
|
34
50
|
const name = 'ActionCard'
|
|
35
51
|
|
|
36
52
|
type StateProps = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
variant: Variants
|
|
54
|
+
size: Size
|
|
55
|
+
color: Color
|
|
56
|
+
layout: Layout
|
|
57
|
+
selected: boolean
|
|
58
|
+
disabled: boolean
|
|
59
|
+
image: boolean
|
|
60
|
+
error: boolean
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
const { withState, selectors } = extendableComponent<StateProps, typeof name, typeof parts>(
|
|
@@ -60,99 +80,131 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
60
80
|
onClick,
|
|
61
81
|
value,
|
|
62
82
|
selected = false,
|
|
63
|
-
hidden = false,
|
|
64
83
|
reset,
|
|
65
84
|
disabled = false,
|
|
85
|
+
size = 'medium',
|
|
86
|
+
color = 'primary',
|
|
87
|
+
variant = 'outlined',
|
|
88
|
+
layout = 'list',
|
|
89
|
+
error = false,
|
|
66
90
|
} = props
|
|
67
91
|
|
|
68
|
-
const classes = withState({
|
|
69
|
-
|
|
70
|
-
|
|
92
|
+
const classes = withState({
|
|
93
|
+
disabled,
|
|
94
|
+
selected,
|
|
95
|
+
image: Boolean(image),
|
|
96
|
+
variant,
|
|
97
|
+
size,
|
|
98
|
+
color,
|
|
99
|
+
layout,
|
|
100
|
+
error,
|
|
101
|
+
})
|
|
71
102
|
|
|
72
103
|
return (
|
|
73
|
-
<
|
|
74
|
-
component='div'
|
|
104
|
+
<RenderComponent
|
|
75
105
|
className={classes.root}
|
|
76
|
-
onClick={
|
|
106
|
+
onClick={onClick && ((event) => onClick?.(event, value))}
|
|
77
107
|
disabled={disabled}
|
|
78
108
|
sx={[
|
|
79
109
|
(theme) => ({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"image title action"
|
|
85
|
-
"image details ${price ? 'price' : 'details'}"
|
|
86
|
-
"image secondaryAction additionalDetails"
|
|
87
|
-
"after after after"
|
|
88
|
-
`,
|
|
89
|
-
justifyContent: 'unset',
|
|
90
|
-
typography: 'body1',
|
|
91
|
-
// textAlign: 'left',
|
|
92
|
-
background: theme.palette.background.paper,
|
|
93
|
-
padding: `calc(${theme.spacings.xxs} + 1px) calc(${theme.spacings.xs} + 1px)`,
|
|
94
|
-
columnGap: theme.spacings.xxs,
|
|
95
|
-
border: `1px solid ${theme.palette.divider}`,
|
|
96
|
-
borderBottomColor: `transparent`,
|
|
97
|
-
'&:first-of-type': {
|
|
98
|
-
...breakpointVal(
|
|
99
|
-
'borderTopLeftRadius',
|
|
100
|
-
theme.shape.borderRadius * 3,
|
|
101
|
-
theme.shape.borderRadius * 4,
|
|
102
|
-
theme.breakpoints.values,
|
|
103
|
-
),
|
|
104
|
-
...breakpointVal(
|
|
105
|
-
'borderTopRightRadius',
|
|
106
|
-
theme.shape.borderRadius * 3,
|
|
107
|
-
theme.shape.borderRadius * 4,
|
|
108
|
-
theme.breakpoints.values,
|
|
109
|
-
),
|
|
110
|
+
'&.sizeSmall': {
|
|
111
|
+
padding: `5px 10px`,
|
|
112
|
+
display: 'flex',
|
|
113
|
+
typography: 'body2',
|
|
110
114
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
theme.breakpoints.values,
|
|
117
|
-
),
|
|
118
|
-
...breakpointVal(
|
|
119
|
-
'borderBottomRightRadius',
|
|
120
|
-
theme.shape.borderRadius * 3,
|
|
121
|
-
theme.shape.borderRadius * 4,
|
|
122
|
-
theme.breakpoints.values,
|
|
123
|
-
),
|
|
124
|
-
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
115
|
+
|
|
116
|
+
'&.sizeMedium': {
|
|
117
|
+
padding: `10px 12px`,
|
|
118
|
+
typography: 'body2',
|
|
119
|
+
display: 'block',
|
|
125
120
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
gridTemplateColumns: 'auto auto',
|
|
129
|
-
gridTemplateAreas: `
|
|
130
|
-
"title action"
|
|
131
|
-
"details ${price ? 'price' : 'details'}"
|
|
132
|
-
"secondaryAction additionalDetails"
|
|
133
|
-
"after after"
|
|
134
|
-
`,
|
|
135
|
-
},
|
|
136
|
-
hidden && {
|
|
137
|
-
display: 'none',
|
|
138
|
-
},
|
|
139
|
-
selected &&
|
|
140
|
-
((theme) => ({
|
|
141
|
-
border: `2px solid ${theme.palette.secondary.main} !important`,
|
|
142
|
-
boxShadow: `0 0 0 4px ${alpha(
|
|
143
|
-
theme.palette.secondary.main,
|
|
144
|
-
theme.palette.action.hoverOpacity,
|
|
145
|
-
)} !important`,
|
|
146
|
-
...breakpointVal(
|
|
147
|
-
'borderRadius',
|
|
148
|
-
theme.shape.borderRadius * 3,
|
|
149
|
-
theme.shape.borderRadius * 4,
|
|
150
|
-
theme.breakpoints.values,
|
|
151
|
-
),
|
|
121
|
+
|
|
122
|
+
'&.sizeLarge': {
|
|
152
123
|
padding: `${theme.spacings.xxs} ${theme.spacings.xs}`,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
124
|
+
display: 'block',
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
'&.variantDefault': {
|
|
128
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
129
|
+
'&.selected': {
|
|
130
|
+
borderBottom: `2px solid ${theme.palette[color].main}`,
|
|
131
|
+
marginBottom: '-1px',
|
|
132
|
+
backgroundColor: `${theme.palette[color].main}10`,
|
|
133
|
+
},
|
|
134
|
+
'&.error': {
|
|
135
|
+
borderBottom: `2px solid ${theme.palette.error.main}`,
|
|
136
|
+
marginBottom: '-1px',
|
|
137
|
+
backgroundColor: `${theme.palette.error.main}10`,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
'&.variantOutlined': {
|
|
142
|
+
backgroundColor: theme.palette.background.paper,
|
|
143
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
144
|
+
'&:not(:last-of-type)': {
|
|
145
|
+
marginBottom: '-1px',
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
'&.layoutList': {
|
|
149
|
+
'&:first-of-type, &.selected': {
|
|
150
|
+
...breakpointVal(
|
|
151
|
+
'borderTopLeftRadius',
|
|
152
|
+
theme.shape.borderRadius * 3,
|
|
153
|
+
theme.shape.borderRadius * 4,
|
|
154
|
+
theme.breakpoints.values,
|
|
155
|
+
),
|
|
156
|
+
...breakpointVal(
|
|
157
|
+
'borderTopRightRadius',
|
|
158
|
+
theme.shape.borderRadius * 3,
|
|
159
|
+
theme.shape.borderRadius * 4,
|
|
160
|
+
theme.breakpoints.values,
|
|
161
|
+
),
|
|
162
|
+
},
|
|
163
|
+
'&:last-of-type, &.selected': {
|
|
164
|
+
...breakpointVal(
|
|
165
|
+
'borderBottomLeftRadius',
|
|
166
|
+
theme.shape.borderRadius * 3,
|
|
167
|
+
theme.shape.borderRadius * 4,
|
|
168
|
+
theme.breakpoints.values,
|
|
169
|
+
),
|
|
170
|
+
...breakpointVal(
|
|
171
|
+
'borderBottomRightRadius',
|
|
172
|
+
theme.shape.borderRadius * 3,
|
|
173
|
+
theme.shape.borderRadius * 4,
|
|
174
|
+
theme.breakpoints.values,
|
|
175
|
+
),
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
'&:not(.layoutList)': {
|
|
179
|
+
...breakpointVal(
|
|
180
|
+
'borderRadius',
|
|
181
|
+
theme.shape.borderRadius * 3,
|
|
182
|
+
theme.shape.borderRadius * 4,
|
|
183
|
+
theme.breakpoints.values,
|
|
184
|
+
),
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
'&.selected': {
|
|
188
|
+
border: `2px solid ${theme.palette[color].main}`,
|
|
189
|
+
boxShadow: `0 0 0 4px ${alpha(
|
|
190
|
+
theme.palette[color].main,
|
|
191
|
+
theme.palette.action.hoverOpacity,
|
|
192
|
+
)}`,
|
|
193
|
+
|
|
194
|
+
'&.sizeSmall': { padding: `4px 9px` },
|
|
195
|
+
'&.sizeMedium': { padding: `9px 11px` },
|
|
196
|
+
'&.sizeLarge': {
|
|
197
|
+
padding: `calc(${theme.spacings.xxs} - 1px) calc(${theme.spacings.xs} - 1px)`,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
'&.error': {
|
|
201
|
+
border: `2px solid ${theme.palette.error.main}`,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
'&.selected': {
|
|
205
|
+
zIndex: 1,
|
|
206
|
+
},
|
|
207
|
+
'&.disabled': {
|
|
156
208
|
'& *': {
|
|
157
209
|
opacity: theme.palette.action.disabledOpacity,
|
|
158
210
|
},
|
|
@@ -160,64 +212,96 @@ export function ActionCard(props: ActionCardProps) {
|
|
|
160
212
|
theme.palette.action.disabledBackground,
|
|
161
213
|
theme.palette.action.disabledOpacity / 10,
|
|
162
214
|
),
|
|
163
|
-
}
|
|
164
|
-
|
|
215
|
+
},
|
|
216
|
+
}),
|
|
165
217
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
166
218
|
]}
|
|
167
219
|
>
|
|
168
|
-
|
|
220
|
+
<Box
|
|
221
|
+
sx={{
|
|
222
|
+
display: 'flex',
|
|
223
|
+
flexDirection: 'row',
|
|
224
|
+
width: '100%',
|
|
225
|
+
justifyContent: 'space-between',
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
169
228
|
<Box
|
|
170
|
-
className={classes.image}
|
|
171
229
|
sx={{
|
|
172
|
-
gridArea: 'image',
|
|
173
230
|
display: 'flex',
|
|
231
|
+
flexDirection: 'row',
|
|
232
|
+
justifyContent: 'space-between',
|
|
174
233
|
}}
|
|
175
234
|
>
|
|
176
|
-
{image
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
235
|
+
{image && (
|
|
236
|
+
<Box
|
|
237
|
+
className={classes.image}
|
|
238
|
+
sx={{ display: 'flex', paddingRight: '15px', alignSelf: 'center' }}
|
|
239
|
+
>
|
|
240
|
+
{image}
|
|
241
|
+
</Box>
|
|
242
|
+
)}
|
|
243
|
+
|
|
244
|
+
<Box
|
|
245
|
+
sx={{
|
|
246
|
+
display: 'flex',
|
|
247
|
+
justifyContent: 'center',
|
|
248
|
+
flexDirection: 'column',
|
|
249
|
+
alignItems: 'flex-start',
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{title && (
|
|
253
|
+
<Box
|
|
254
|
+
className={classes.title}
|
|
255
|
+
sx={{
|
|
256
|
+
typography: 'subtitle2',
|
|
257
|
+
'&.sizeMedium': { typographty: 'subtitle1' },
|
|
258
|
+
'&.sizeLarge': { typography: 'h6' },
|
|
259
|
+
}}
|
|
260
|
+
>
|
|
261
|
+
{title}
|
|
262
|
+
</Box>
|
|
263
|
+
)}
|
|
264
|
+
|
|
265
|
+
{details && (
|
|
266
|
+
<Box className={classes.details} sx={{ color: 'text.secondary' }}>
|
|
267
|
+
{details}
|
|
268
|
+
</Box>
|
|
269
|
+
)}
|
|
270
|
+
|
|
271
|
+
{secondaryAction && <Box className={classes.secondaryAction}>{secondaryAction}</Box>}
|
|
272
|
+
</Box>
|
|
199
273
|
</Box>
|
|
200
|
-
)}
|
|
201
274
|
|
|
202
|
-
{price && !disabled && (
|
|
203
275
|
<Box
|
|
204
|
-
|
|
205
|
-
|
|
276
|
+
sx={{
|
|
277
|
+
display: 'flex',
|
|
278
|
+
flexDirection: 'column',
|
|
279
|
+
justifyContent: 'space-between',
|
|
280
|
+
alignItems: 'flex-end',
|
|
281
|
+
}}
|
|
206
282
|
>
|
|
207
|
-
{
|
|
208
|
-
|
|
209
|
-
|
|
283
|
+
{action && (
|
|
284
|
+
<Box className={classes.action} sx={{ marginBottom: '5px' }}>
|
|
285
|
+
{!selected ? action : reset}
|
|
286
|
+
</Box>
|
|
287
|
+
)}
|
|
210
288
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
289
|
+
{price && !disabled && (
|
|
290
|
+
<Box
|
|
291
|
+
className={classes.price}
|
|
292
|
+
sx={{
|
|
293
|
+
textAlign: 'right',
|
|
294
|
+
typography: 'body1',
|
|
295
|
+
'&.sizeMedium': { typographty: 'subtitle1' },
|
|
296
|
+
'&.sizeLarge': { typography: 'h6' },
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
{price}
|
|
300
|
+
</Box>
|
|
301
|
+
)}
|
|
219
302
|
</Box>
|
|
220
|
-
|
|
221
|
-
|
|
303
|
+
</Box>
|
|
304
|
+
{after && <Box className={classes.after}>{after}</Box>}
|
|
305
|
+
</RenderComponent>
|
|
222
306
|
)
|
|
223
307
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Alert, Box } from '@mui/material'
|
|
1
|
+
import { Alert, Box, SxProps, Theme } from '@mui/material'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { isFragment } from 'react-is'
|
|
4
|
+
import { extendableComponent } from '../Styles'
|
|
4
5
|
import { ActionCardProps } from './ActionCard'
|
|
5
6
|
|
|
6
7
|
type MultiSelect = {
|
|
7
8
|
multiple: true
|
|
9
|
+
collapse?: false
|
|
8
10
|
value: (string | number)[]
|
|
9
11
|
|
|
10
12
|
onChange?: (event: React.MouseEvent<HTMLElement>, value: MultiSelect['value']) => void
|
|
@@ -12,6 +14,7 @@ type MultiSelect = {
|
|
|
12
14
|
type Select = {
|
|
13
15
|
multiple?: false
|
|
14
16
|
value: string | number
|
|
17
|
+
collapse?: boolean
|
|
15
18
|
|
|
16
19
|
/** Value is null when deselected when not required */
|
|
17
20
|
onChange?: (event: React.MouseEvent<HTMLElement>, value: Select['value'] | null) => void
|
|
@@ -22,7 +25,9 @@ export type ActionCardListProps<SelectOrMulti = MultiSelect | Select> = {
|
|
|
22
25
|
required?: boolean
|
|
23
26
|
error?: boolean
|
|
24
27
|
errorMessage?: string
|
|
25
|
-
|
|
28
|
+
sx?: SxProps<Theme>
|
|
29
|
+
} & SelectOrMulti &
|
|
30
|
+
HoistedActionCardProps
|
|
26
31
|
|
|
27
32
|
function isMulti(props: ActionCardListProps): props is ActionCardListProps<MultiSelect> {
|
|
28
33
|
return props.multiple === true
|
|
@@ -37,9 +42,30 @@ function isValueSelected(
|
|
|
37
42
|
return value === candidate
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
type HoistedActionCardProps = Pick<ActionCardProps, 'color' | 'variant' | 'size' | 'layout'>
|
|
46
|
+
|
|
47
|
+
const parts = ['root'] as const
|
|
48
|
+
const name = 'ActionCardList'
|
|
49
|
+
const { withState, selectors } = extendableComponent<
|
|
50
|
+
HoistedActionCardProps,
|
|
51
|
+
typeof name,
|
|
52
|
+
typeof parts
|
|
53
|
+
>(name, parts)
|
|
54
|
+
|
|
40
55
|
export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListProps>(
|
|
41
56
|
(props, ref) => {
|
|
42
|
-
const {
|
|
57
|
+
const {
|
|
58
|
+
children,
|
|
59
|
+
required,
|
|
60
|
+
error = false,
|
|
61
|
+
errorMessage,
|
|
62
|
+
size = 'medium',
|
|
63
|
+
color = 'primary',
|
|
64
|
+
variant = 'outlined',
|
|
65
|
+
layout = 'list',
|
|
66
|
+
collapse = false,
|
|
67
|
+
sx = [],
|
|
68
|
+
} = props
|
|
43
69
|
|
|
44
70
|
const handleChange: ActionCardProps['onClick'] = isMulti(props)
|
|
45
71
|
? (event, v) => {
|
|
@@ -65,8 +91,10 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
|
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
type ActionCardLike = React.ReactElement<
|
|
68
|
-
Pick<ActionCardProps, 'value' | 'selected' | 'disabled' | 'onClick' | '
|
|
94
|
+
Pick<ActionCardProps, 'value' | 'selected' | 'disabled' | 'onClick' | 'error' | 'onClick'> &
|
|
95
|
+
HoistedActionCardProps
|
|
69
96
|
>
|
|
97
|
+
|
|
70
98
|
function isActionCardLike(el: React.ReactElement): el is ActionCardLike {
|
|
71
99
|
const hasValue = (el as ActionCardLike).props.value
|
|
72
100
|
|
|
@@ -100,55 +128,68 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
|
|
|
100
128
|
(child) => child.props.value === props.value && child.props.disabled !== true,
|
|
101
129
|
)?.props.value
|
|
102
130
|
|
|
131
|
+
const classes = withState({ size, color, variant, layout })
|
|
132
|
+
|
|
103
133
|
return (
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
134
|
+
<div>
|
|
135
|
+
<Box
|
|
136
|
+
className={classes.root}
|
|
137
|
+
ref={ref}
|
|
138
|
+
sx={[
|
|
139
|
+
(theme) => ({
|
|
140
|
+
'&.layoutStack': {
|
|
141
|
+
display: 'grid',
|
|
142
|
+
height: 'min-content',
|
|
143
|
+
gap: theme.spacings.xxs,
|
|
144
|
+
},
|
|
145
|
+
'&.layoutList': {
|
|
146
|
+
display: 'grid',
|
|
147
|
+
height: 'min-content',
|
|
148
|
+
},
|
|
149
|
+
'&.layoutGrid': {
|
|
150
|
+
display: 'grid',
|
|
151
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
152
|
+
gap: theme.spacings.xxs,
|
|
153
|
+
},
|
|
154
|
+
'&.layoutInline': {
|
|
155
|
+
display: 'flex',
|
|
156
|
+
flexWrap: 'wrap',
|
|
157
|
+
gap: theme.spacings.xxs,
|
|
158
|
+
},
|
|
159
|
+
}),
|
|
160
|
+
|
|
161
|
+
...(Array.isArray(sx) ? sx : [sx]),
|
|
162
|
+
]}
|
|
163
|
+
>
|
|
164
|
+
{childReactNodes.map((child) => {
|
|
165
|
+
if (collapse && Boolean(value) && !isValueSelected(child.props.value, value))
|
|
166
|
+
return null
|
|
167
|
+
return React.cloneElement(child, {
|
|
168
|
+
onClick: handleChange,
|
|
169
|
+
error: child.props.error ?? error,
|
|
170
|
+
color: child.props.color ?? color,
|
|
171
|
+
variant: child.props.variant ?? variant,
|
|
172
|
+
size: child.props.size ?? size,
|
|
173
|
+
layout: child.props.layout ?? layout,
|
|
174
|
+
selected:
|
|
175
|
+
child.props.selected === undefined
|
|
176
|
+
? isValueSelected(child.props.value, value)
|
|
177
|
+
: child.props.selected,
|
|
178
|
+
})
|
|
179
|
+
})}
|
|
180
|
+
</Box>
|
|
181
|
+
{error && errorMessage && (
|
|
182
|
+
<Alert
|
|
183
|
+
severity='error'
|
|
184
|
+
variant='standard'
|
|
185
|
+
sx={(theme) => ({
|
|
186
|
+
marginTop: theme.spacings.xxs,
|
|
187
|
+
})}
|
|
188
|
+
>
|
|
189
|
+
{errorMessage}
|
|
190
|
+
</Alert>
|
|
150
191
|
)}
|
|
151
|
-
</
|
|
192
|
+
</div>
|
|
152
193
|
)
|
|
153
194
|
},
|
|
154
195
|
)
|
|
@@ -6,37 +6,37 @@ import { ActionCardList, ActionCardListProps } from './ActionCardList'
|
|
|
6
6
|
|
|
7
7
|
export type ActionCardItemBase = Pick<ActionCardProps, 'value'>
|
|
8
8
|
|
|
9
|
-
export type ActionCardItemRenderProps<T> =
|
|
10
|
-
ActionCardProps,
|
|
11
|
-
'selected' | 'hidden' | 'value'
|
|
12
|
-
> & {
|
|
9
|
+
export type ActionCardItemRenderProps<T> = ActionCardProps & {
|
|
13
10
|
onReset: MouseEventHandler<HTMLAnchorElement> & MouseEventHandler<HTMLSpanElement>
|
|
14
11
|
} & T
|
|
15
12
|
|
|
16
13
|
export type ActionCardListFormProps<T extends ActionCardItemBase> = Omit<
|
|
17
14
|
ActionCardListProps,
|
|
18
|
-
'value'
|
|
15
|
+
'value' | 'error' | 'onChange' | 'children' | 'multiple'
|
|
19
16
|
> &
|
|
20
|
-
Omit<ControllerProps<any>, 'render'> & {
|
|
17
|
+
Omit<ControllerProps<any>, 'render' | 'shouldUnregister'> & {
|
|
21
18
|
items: T[]
|
|
22
|
-
render: React.
|
|
19
|
+
render: React.FC<ActionCardItemRenderProps<T>>
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
export function ActionCardListForm<T extends ActionCardItemBase>(
|
|
26
23
|
props: ActionCardListFormProps<T>,
|
|
27
24
|
) {
|
|
28
|
-
const { required, rules, items, render, control, name, errorMessage } =
|
|
29
|
-
|
|
25
|
+
const { required, rules, items, render, control, name, errorMessage, defaultValue, ...other } =
|
|
26
|
+
props
|
|
27
|
+
const RenderItem = render as React.FC<ActionCardItemRenderProps<ActionCardItemBase>>
|
|
30
28
|
|
|
31
29
|
return (
|
|
32
30
|
<Controller
|
|
33
31
|
{...props}
|
|
34
32
|
control={control}
|
|
35
33
|
name={name}
|
|
36
|
-
|
|
34
|
+
defaultValue={defaultValue}
|
|
35
|
+
rules={{ required: errorMessage || required, ...rules }}
|
|
37
36
|
render={({ field: { onChange, value, ref }, fieldState, formState }) => (
|
|
38
37
|
<ActionCardList
|
|
39
|
-
|
|
38
|
+
{...other}
|
|
39
|
+
required={required}
|
|
40
40
|
value={value}
|
|
41
41
|
ref={ref}
|
|
42
42
|
onChange={(_, incomming) => onChange(incomming)}
|
package/Button/Button.tsx
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* eslint-disable react/forbid-foreign-prop-types */
|
|
2
|
-
import { LoadingButton as Button, LoadingButtonProps } from '@mui/lab'
|
|
2
|
+
import { LoadingButton as Button, LoadingButtonProps, LoadingButtonTypeMap } from '@mui/lab'
|
|
3
|
+
|
|
4
|
+
export type ButtonProps<
|
|
5
|
+
D extends React.ElementType = LoadingButtonTypeMap['defaultComponent'],
|
|
6
|
+
P = {},
|
|
7
|
+
> = LoadingButtonProps<D, P>
|
|
3
8
|
|
|
4
|
-
export type ButtonProps = LoadingButtonProps
|
|
5
9
|
export { Button }
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`0c21c5c23`](https://github.com/graphcommerce-org/graphcommerce/commit/0c21c5c233ebab15f6629c234e3de1cc8c0452e1) Thanks [@paales](https://github.com/paales)! - Implement serverRenderDepth prop to the Navigation to limit initial render time and TBT
|
|
8
|
+
|
|
9
|
+
* [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a) Thanks [@paales](https://github.com/paales)! - Move to useMatchMedia to have a simple boolean utility that allows to match to a certain breakpoint
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#1662](https://github.com/graphcommerce-org/graphcommerce/pull/1662) [`de8925aa9`](https://github.com/graphcommerce-org/graphcommerce/commit/de8925aa910b191c62041530c68c697a58a1e52d) Thanks [@paales](https://github.com/paales)! - Allow for a custom Component for magentoMenuToNavigation and allow React.ReactNode for items
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`f5eae0afd`](https://github.com/graphcommerce-org/graphcommerce/commit/f5eae0afdbd474b1f81c450425ffadf2d025187a), [`9e0ca73eb`](https://github.com/graphcommerce-org/graphcommerce/commit/9e0ca73eb50ded578f4a98e40a7eb920bf8ab421)]:
|
|
16
|
+
- @graphcommerce/framer-scroller@2.1.40
|
|
17
|
+
- @graphcommerce/framer-next-pages@3.3.1
|
|
18
|
+
|
|
19
|
+
## 4.27.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- [#1642](https://github.com/graphcommerce-org/graphcommerce/pull/1642) [`ad63ebf4e`](https://github.com/graphcommerce-org/graphcommerce/commit/ad63ebf4e33bfb0e5c9e5e68ab69b14775f3f8a8) Thanks [@paales](https://github.com/paales)! - Introduced `<AddProductsToCartForm/>`, which is allows for adding all product types to the cart with a single react-hook-form form.
|
|
24
|
+
|
|
25
|
+
Which allows you to fully compose the form on the product page without having to modify the page.
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- Updated dependencies []:
|
|
30
|
+
- @graphcommerce/framer-scroller@2.1.39
|
|
31
|
+
|
|
3
32
|
## 4.26.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
|
@@ -107,9 +107,8 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
107
107
|
|
|
108
108
|
const headerHeight = `${theme.appShell.headerHeightSm} - ${theme.spacings.sm} * 2`
|
|
109
109
|
const galleryMargin = theme.spacings.lg
|
|
110
|
-
const extraSpacing = theme.spacings.md
|
|
111
110
|
|
|
112
|
-
const maxHeight = `calc(100vh - ${headerHeight} - ${galleryMargin}
|
|
111
|
+
const maxHeight = `calc(100vh - ${headerHeight} - ${galleryMargin})`
|
|
113
112
|
const ratio = `calc(${height} / ${width} * 100%)`
|
|
114
113
|
|
|
115
114
|
const hasImages = images.length > 0
|
|
@@ -200,7 +199,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
|
|
|
200
199
|
width={image.width}
|
|
201
200
|
height={image.height}
|
|
202
201
|
loading={idx === 0 ? 'eager' : 'lazy'}
|
|
203
|
-
sx={{ display: 'block' }}
|
|
202
|
+
sx={{ display: 'block', objectFit: 'contain' }}
|
|
204
203
|
sizes={{
|
|
205
204
|
0: '100vw',
|
|
206
205
|
[theme.breakpoints.values.md]: zoomed ? '100vw' : '60vw',
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
2
2
|
import { useMotionValueValue } from '@graphcommerce/framer-utils'
|
|
3
|
-
import {
|
|
4
|
-
Box,
|
|
5
|
-
ListItemButton,
|
|
6
|
-
styled,
|
|
7
|
-
Theme,
|
|
8
|
-
useEventCallback,
|
|
9
|
-
useMediaQuery,
|
|
10
|
-
alpha,
|
|
11
|
-
} from '@mui/material'
|
|
3
|
+
import { alpha, Box, ListItemButton, styled, useEventCallback, useTheme } from '@mui/material'
|
|
12
4
|
import PageLink from 'next/link'
|
|
13
5
|
import React from 'react'
|
|
14
6
|
import { IconSvg } from '../../IconSvg'
|
|
15
7
|
import { extendableComponent } from '../../Styles/extendableComponent'
|
|
8
|
+
import { useMatchMedia } from '../../hooks'
|
|
16
9
|
import { iconChevronRight } from '../../icons'
|
|
17
10
|
import {
|
|
18
11
|
isNavigationButton,
|
|
@@ -54,9 +47,9 @@ const NavigationLI = styled('li')({ display: 'contents' })
|
|
|
54
47
|
|
|
55
48
|
export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
56
49
|
const { id, parentPath, idx, first, last, NavigationList, mouseEvent } = props
|
|
50
|
+
const { selection, hideRootOnNavigate, closing, animating, serverRenderDepth } = useNavigation()
|
|
57
51
|
|
|
58
52
|
const row = idx + 1
|
|
59
|
-
const { selection, hideRootOnNavigate, closing, animating } = useNavigation()
|
|
60
53
|
|
|
61
54
|
const itemPath = [...(parentPath ? parentPath.split(',') : []), id]
|
|
62
55
|
|
|
@@ -83,13 +76,18 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
83
76
|
closing.set(true)
|
|
84
77
|
})
|
|
85
78
|
|
|
86
|
-
const
|
|
79
|
+
const matchMedia = useMatchMedia()
|
|
87
80
|
|
|
88
81
|
if (isNavigationButton(props)) {
|
|
89
|
-
const { childItems, name } = props
|
|
82
|
+
const { childItems, name, href } = props
|
|
83
|
+
|
|
84
|
+
const skipChildren = itemPath.length + 1 > serverRenderDepth && !isSelected && !!href
|
|
85
|
+
|
|
90
86
|
return (
|
|
91
87
|
<NavigationLI className={classes.li}>
|
|
92
88
|
<ListItemButton
|
|
89
|
+
component={href ? 'a' : 'div'}
|
|
90
|
+
href={href || undefined}
|
|
93
91
|
className={classes.item}
|
|
94
92
|
role='button'
|
|
95
93
|
sx={[
|
|
@@ -116,14 +114,14 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
116
114
|
tabIndex={tabIndex}
|
|
117
115
|
onClick={(e) => {
|
|
118
116
|
e.preventDefault()
|
|
119
|
-
if (!isSelected && animating.get()
|
|
117
|
+
if (!isSelected && !animating.get()) {
|
|
120
118
|
selection.set(itemPath)
|
|
121
119
|
}
|
|
122
120
|
}}
|
|
123
121
|
onMouseMove={
|
|
124
|
-
itemPath.length > 1 && mouseEvent === 'hover'
|
|
122
|
+
(itemPath.length > 1 || !hideRootOnNavigate) && mouseEvent === 'hover'
|
|
125
123
|
? (e) => {
|
|
126
|
-
if (
|
|
124
|
+
if (!isSelected && !animating.get() && matchMedia.up('md')) {
|
|
127
125
|
e.preventDefault()
|
|
128
126
|
setTimeout(() => selection.set(itemPath), 0)
|
|
129
127
|
}
|
|
@@ -145,18 +143,21 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
145
143
|
<IconSvg src={iconChevronRight} sx={{ flexShrink: 0 }} />
|
|
146
144
|
</ListItemButton>
|
|
147
145
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
{!skipChildren && (
|
|
147
|
+
<NavigationList
|
|
148
|
+
items={childItems}
|
|
149
|
+
selected={isSelected}
|
|
150
|
+
parentPath={itemPath.join(',')}
|
|
151
|
+
mouseEvent={mouseEvent}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
154
|
</NavigationLI>
|
|
155
155
|
)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
if (isNavigationHref(props)) {
|
|
159
159
|
const { name, href } = props
|
|
160
|
+
|
|
160
161
|
return (
|
|
161
162
|
<NavigationLI sx={[hideItem && { display: 'none' }]} className={classes.li}>
|
|
162
163
|
<PageLink href={href} passHref prefetch={false}>
|
|
@@ -198,8 +199,6 @@ export const NavigationItem = React.memo<NavigationItemProps>((props) => {
|
|
|
198
199
|
)
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
if (process.env.NODE_ENV !== 'production') throw Error('NavigationItem: unknown type')
|
|
202
|
-
|
|
203
202
|
return null
|
|
204
203
|
})
|
|
205
204
|
|
|
@@ -11,6 +11,7 @@ import { LayoutTitle } from '../../Layout/components/LayoutTitle'
|
|
|
11
11
|
import { Overlay } from '../../Overlay/components/Overlay'
|
|
12
12
|
import { extendableComponent } from '../../Styles/extendableComponent'
|
|
13
13
|
import { useFabSize } from '../../Theme'
|
|
14
|
+
import { useMatchMedia } from '../../hooks'
|
|
14
15
|
import { iconClose, iconChevronLeft } from '../../icons'
|
|
15
16
|
import { useNavigation } from '../hooks/useNavigation'
|
|
16
17
|
import { mouseEventPref } from './NavigationItem'
|
|
@@ -57,14 +58,15 @@ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
|
57
58
|
mouseEvent,
|
|
58
59
|
itemPadding = 'md',
|
|
59
60
|
} = props
|
|
60
|
-
const { selection, items, animating, closing } = useNavigation()
|
|
61
|
+
const { selection, items, animating, closing, serverRenderDepth } = useNavigation()
|
|
61
62
|
|
|
62
63
|
const fabSize = useFabSize('responsive')
|
|
63
64
|
const svgSize = useIconSvgSize('large')
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
+
const matchMedia = useMatchMedia()
|
|
67
|
+
|
|
66
68
|
const handleOnBack = useEventCallback(() => {
|
|
67
|
-
if (
|
|
69
|
+
if (matchMedia.down('md')) {
|
|
68
70
|
const current = selection.get()
|
|
69
71
|
selection.set(current !== false ? current.slice(0, -1) : false)
|
|
70
72
|
} else selection.set([])
|
|
@@ -87,6 +89,8 @@ export const NavigationOverlay = React.memo<NavigationOverlayProps>((props) => {
|
|
|
87
89
|
|
|
88
90
|
const handleClose = useEventCallback(() => closing.set(true))
|
|
89
91
|
|
|
92
|
+
if (selectedLevel === -1 && serverRenderDepth <= 0) return null
|
|
93
|
+
|
|
90
94
|
return (
|
|
91
95
|
<Overlay
|
|
92
96
|
className={classes.root}
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
NavigationContextType,
|
|
7
7
|
NavigationContext,
|
|
8
8
|
UseNavigationSelection,
|
|
9
|
+
NavigationNodeType,
|
|
10
|
+
NavigationNodeComponent,
|
|
9
11
|
} from '../hooks/useNavigation'
|
|
10
12
|
|
|
11
13
|
export type NavigationProviderProps = {
|
|
@@ -15,6 +17,7 @@ export type NavigationProviderProps = {
|
|
|
15
17
|
children?: React.ReactNode
|
|
16
18
|
animationDuration?: number
|
|
17
19
|
selection: UseNavigationSelection
|
|
20
|
+
serverRenderDepth?: number
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
const nonNullable = <T,>(value: T): value is NonNullable<T> => value !== null && value !== undefined
|
|
@@ -27,6 +30,7 @@ export const NavigationProvider = React.memo<NavigationProviderProps>((props) =>
|
|
|
27
30
|
animationDuration = 0.225,
|
|
28
31
|
children,
|
|
29
32
|
selection,
|
|
33
|
+
serverRenderDepth = 2,
|
|
30
34
|
} = props
|
|
31
35
|
|
|
32
36
|
const animating = useMotionValue(false)
|
|
@@ -39,10 +43,19 @@ export const NavigationProvider = React.memo<NavigationProviderProps>((props) =>
|
|
|
39
43
|
animating,
|
|
40
44
|
closing,
|
|
41
45
|
items: items
|
|
42
|
-
.map((item, index) =>
|
|
46
|
+
.map((item, index) =>
|
|
47
|
+
isElement(item)
|
|
48
|
+
? ({
|
|
49
|
+
type: NavigationNodeType.COMPONENT,
|
|
50
|
+
id: item.key ?? index,
|
|
51
|
+
component: item,
|
|
52
|
+
} as NavigationNodeComponent)
|
|
53
|
+
: item,
|
|
54
|
+
)
|
|
43
55
|
.filter(nonNullable),
|
|
56
|
+
serverRenderDepth,
|
|
44
57
|
}),
|
|
45
|
-
[hideRootOnNavigate, selection, animating, closing, items],
|
|
58
|
+
[hideRootOnNavigate, selection, animating, closing, items, serverRenderDepth],
|
|
46
59
|
)
|
|
47
60
|
|
|
48
61
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MotionValue, useMotionValue } from 'framer-motion'
|
|
2
|
-
import { createContext, MutableRefObject, useContext } from 'react'
|
|
2
|
+
import React, { createContext, MutableRefObject, useContext } from 'react'
|
|
3
3
|
|
|
4
4
|
export type NavigationId = string | number
|
|
5
5
|
export type NavigationPath = NavigationId[]
|
|
@@ -19,38 +19,51 @@ export type NavigationContextType = {
|
|
|
19
19
|
hideRootOnNavigate: boolean
|
|
20
20
|
animating: MotionValue<boolean>
|
|
21
21
|
closing: MotionValue<boolean>
|
|
22
|
+
serverRenderDepth: number
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
type NavigationNodeBase = {
|
|
26
|
+
type?: NavigationNodeType
|
|
25
27
|
id: NavigationId
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
export enum NavigationNodeType {
|
|
31
|
+
LINK,
|
|
32
|
+
BUTTON,
|
|
33
|
+
COMPONENT,
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
export type NavigationNodeHref = NavigationNodeBase & {
|
|
29
|
-
name:
|
|
37
|
+
name: React.ReactNode
|
|
30
38
|
href: string
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export type NavigationNodeButton = NavigationNodeBase & {
|
|
34
|
-
name:
|
|
42
|
+
name: React.ReactNode
|
|
43
|
+
type: NavigationNodeType.BUTTON
|
|
44
|
+
href?: string
|
|
35
45
|
childItems: NavigationNode[]
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
export type NavigationNodeComponent = NavigationNodeBase & {
|
|
49
|
+
type: NavigationNodeType.COMPONENT
|
|
39
50
|
component: React.ReactNode
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export type NavigationNode = NavigationNodeHref | NavigationNodeButton | NavigationNodeComponent
|
|
43
54
|
|
|
44
55
|
export function isNavigationHref(node: NavigationNodeBase): node is NavigationNodeHref {
|
|
45
|
-
return 'href' in node
|
|
56
|
+
return 'href' in node && node.type !== NavigationNodeType.BUTTON
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
export function isNavigationButton(node: NavigationNodeBase): node is NavigationNodeButton {
|
|
49
|
-
return (
|
|
60
|
+
return (
|
|
61
|
+
node.type === NavigationNodeType.BUTTON && (node as NavigationNodeButton).childItems?.length > 0
|
|
62
|
+
)
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
export function isNavigationComponent(node: NavigationNodeBase): node is NavigationNodeComponent {
|
|
53
|
-
return 'component' in node
|
|
66
|
+
return node.type === NavigationNodeType.COMPONENT && 'component' in node
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
export const NavigationContext = createContext(undefined as unknown as NavigationContextType)
|
|
@@ -42,6 +42,13 @@ export function RenderType<
|
|
|
42
42
|
export function findByTypename<T extends TypeObject, Typename extends T['__typename']>(
|
|
43
43
|
type: (T | undefined | null)[] | undefined | null,
|
|
44
44
|
typename: Typename,
|
|
45
|
-
): FilterTypeByTypename<T, Typename> {
|
|
45
|
+
): FilterTypeByTypename<T, Typename> | undefined {
|
|
46
46
|
return type?.find((item) => item?.__typename === typename) as FilterTypeByTypename<T, Typename>
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
export function isTypename<T extends TypeObject, Typenames extends T['__typename'][]>(
|
|
50
|
+
type: FilterTypeByTypename<T, T['__typename']>,
|
|
51
|
+
typename: Typenames,
|
|
52
|
+
): type is FilterTypeByTypename<T, Typenames[number]> {
|
|
53
|
+
return typename.includes(type.__typename)
|
|
54
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { OptionalKeysOf, Simplify } from 'type-fest'
|
|
|
3
3
|
export function filterNonNullableKeys<
|
|
4
4
|
T extends Record<string, unknown>,
|
|
5
5
|
Keys extends OptionalKeysOf<T>,
|
|
6
|
-
>(items: (T | null | undefined)[] | null | undefined, values: Keys[]) {
|
|
6
|
+
>(items: (T | null | undefined)[] | null | undefined, values: Keys[] = []) {
|
|
7
7
|
if (!items) return []
|
|
8
8
|
|
|
9
9
|
type ResultWithRequired = Simplify<
|
package/hooks/index.ts
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Breakpoint, useTheme } from '@mui/material'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
|
|
4
|
+
export function useMatchMedia() {
|
|
5
|
+
const theme = useTheme()
|
|
6
|
+
|
|
7
|
+
return useMemo(() => {
|
|
8
|
+
const callback = (direction: 'up' | 'down', breakpointKey: number | Breakpoint) =>
|
|
9
|
+
window.matchMedia(theme.breakpoints[direction](breakpointKey).replace(/^@media( ?)/m, ''))
|
|
10
|
+
.matches
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
down: (key: number | Breakpoint) => callback('down', key),
|
|
14
|
+
up: (key: number | Breakpoint) => callback('up', key),
|
|
15
|
+
}
|
|
16
|
+
}, [theme.breakpoints])
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/next-ui",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.28.0",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"@emotion/react": "^11.9.3",
|
|
20
20
|
"@emotion/server": "^11.4.0",
|
|
21
21
|
"@emotion/styled": "^11.9.3",
|
|
22
|
-
"@graphcommerce/framer-next-pages": "3.3.
|
|
23
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
22
|
+
"@graphcommerce/framer-next-pages": "3.3.1",
|
|
23
|
+
"@graphcommerce/framer-scroller": "2.1.40",
|
|
24
24
|
"@graphcommerce/framer-utils": "3.2.0",
|
|
25
25
|
"@graphcommerce/image": "3.1.9",
|
|
26
26
|
"cookie": "^0.5.0",
|