@graphcommerce/next-ui 4.26.0 → 4.27.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.
@@ -1,9 +1,25 @@
1
- import { SxProps, ButtonBase, Box, Theme, alpha } from '@mui/material'
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
- selected?: boolean
38
- hidden?: boolean
39
- disabled?: boolean
40
- image?: boolean
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({ hidden, disabled, selected, image: Boolean(image) })
69
-
70
- const handleClick = (event: React.MouseEvent<HTMLElement>) => onClick?.(event, value)
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
- <ButtonBase
74
- component='div'
104
+ <RenderComponent
75
105
  className={classes.root}
76
- onClick={handleClick}
106
+ onClick={onClick && ((event) => onClick?.(event, value))}
77
107
  disabled={disabled}
78
108
  sx={[
79
109
  (theme) => ({
80
- display: 'grid',
81
- width: '100%',
82
- gridTemplateColumns: 'min-content auto auto',
83
- gridTemplateAreas: `
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
- '&:last-of-type': {
112
- ...breakpointVal(
113
- 'borderBottomLeftRadius',
114
- theme.shape.borderRadius * 3,
115
- theme.shape.borderRadius * 4,
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
- !image && {
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
- disabled &&
155
- ((theme) => ({
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
- {image && (
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
- </Box>
178
- )}
179
- {title && (
180
- <Box className={classes.title} sx={{ gridArea: 'title', display: 'flex' }}>
181
- {title}
182
- </Box>
183
- )}
184
- {action && (
185
- <Box className={classes.action} sx={{ gridArea: 'action', textAlign: 'right' }}>
186
- {!selected ? action : reset}
187
- </Box>
188
- )}
189
- {details && (
190
- <Box
191
- className={classes.details}
192
- sx={(theme) => ({
193
- typography: 'body2',
194
- gridArea: 'details',
195
- color: 'text.secondary',
196
- })}
197
- >
198
- {details}
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
- className={classes.price}
205
- sx={{ gridArea: 'price', textAlign: 'right', typography: 'h5' }}
276
+ sx={{
277
+ display: 'flex',
278
+ flexDirection: 'column',
279
+ justifyContent: 'space-between',
280
+ alignItems: 'flex-end',
281
+ }}
206
282
  >
207
- {price}
208
- </Box>
209
- )}
283
+ {action && (
284
+ <Box className={classes.action} sx={{ marginBottom: '5px' }}>
285
+ {!selected ? action : reset}
286
+ </Box>
287
+ )}
210
288
 
211
- {secondaryAction && (
212
- <Box className={classes.secondaryAction} sx={{ gridArea: 'secondaryAction' }}>
213
- {secondaryAction}
214
- </Box>
215
- )}
216
- {after && (
217
- <Box className={classes.after} sx={{ gridArea: 'after' }}>
218
- {after}
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
- </ButtonBase>
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
- } & SelectOrMulti
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 { children, required, error = false, errorMessage } = props
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' | 'hidden'>
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
- <Box
105
- ref={ref}
106
- sx={[
107
- error && {
108
- '& .ActionCard-root': {
109
- borderLeft: 2,
110
- borderRight: 2,
111
- borderLeftColor: 'error.main',
112
- borderRightColor: 'error.main',
113
- },
114
- '& > div:first-of-type.ActionCard-root': {
115
- borderTop: 2,
116
- borderTopColor: 'error.main',
117
- },
118
- '& > div:last-of-type.ActionCard-root': {
119
- borderBottom: 2,
120
- borderBottomColor: 'error.main',
121
- },
122
- },
123
- ]}
124
- >
125
- {childReactNodes.map((child) =>
126
- React.cloneElement(child, {
127
- onClick: handleChange,
128
- hidden: !!value && value !== child.props.value,
129
- selected:
130
- child.props.selected === undefined
131
- ? isValueSelected(child.props.value, value)
132
- : child.props.selected,
133
- }),
134
- )}
135
- {error && (
136
- <Box component='span'>
137
- <Alert
138
- severity='error'
139
- variant='standard'
140
- sx={(theme) => ({
141
- marginTop: 0.5,
142
- borderStartStartRadius: 0,
143
- borderStartEndRadius: 0,
144
- borderRadius: theme.shape.borderRadius * 1,
145
- })}
146
- >
147
- {errorMessage}
148
- </Alert>
149
- </Box>
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
- </Box>
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> = Pick<
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.VFC<ActionCardItemRenderProps<T>>
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 } = props
29
- const RenderItem = render as React.VFC<ActionCardItemRenderProps<ActionCardItemBase>>
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
- rules={{ required, ...rules, validate: (v) => (v ? true : errorMessage) }}
34
+ defaultValue={defaultValue}
35
+ rules={{ required: errorMessage || required, ...rules }}
37
36
  render={({ field: { onChange, value, ref }, fieldState, formState }) => (
38
37
  <ActionCardList
39
- required
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,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#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.
8
+
9
+ Which allows you to fully compose the form on the product page without having to modify the page.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies []:
14
+ - @graphcommerce/framer-scroller@2.1.39
15
+
3
16
  ## 4.26.0
4
17
 
5
18
  ### Minor Changes
@@ -10,7 +10,7 @@ import {
10
10
  import { Fab, useTheme, Box, styled, SxProps, Theme } from '@mui/material'
11
11
  import { m, useDomEvent, useMotionValue } from 'framer-motion'
12
12
  import { useRouter } from 'next/router'
13
- import React, { useEffect, useRef } from 'react'
13
+ import React, { useEffect, useMemo, useRef, useState } from 'react'
14
14
  import { IconSvg } from '../IconSvg'
15
15
  import { Row } from '../Row/Row'
16
16
  import { extendableComponent } from '../Styles'
@@ -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/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.26.0",
5
+ "version": "4.27.0",
6
6
  "author": "",
7
7
  "license": "MIT",
8
8
  "sideEffects": false,
@@ -20,7 +20,7 @@
20
20
  "@emotion/server": "^11.4.0",
21
21
  "@emotion/styled": "^11.9.3",
22
22
  "@graphcommerce/framer-next-pages": "3.3.0",
23
- "@graphcommerce/framer-scroller": "2.1.38",
23
+ "@graphcommerce/framer-scroller": "2.1.39",
24
24
  "@graphcommerce/framer-utils": "3.2.0",
25
25
  "@graphcommerce/image": "3.1.9",
26
26
  "cookie": "^0.5.0",