@graphcommerce/next-ui 6.2.0-canary.6 → 6.2.0-canary.60

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.
@@ -122,7 +122,6 @@ export function ActionCard(props: ActionCardProps) {
122
122
  theme.shape.borderRadius * 3,
123
123
  theme.breakpoints.values,
124
124
  ),
125
-
126
125
  '&.sizeSmall': {
127
126
  px: responsiveVal(8, 12),
128
127
  py: responsiveVal(4, 6),
@@ -144,12 +143,12 @@ export function ActionCard(props: ActionCardProps) {
144
143
  '&.variantDefault': {
145
144
  '&::after': {
146
145
  content: '""',
147
- display: 'block',
148
146
  position: 'absolute',
149
147
  width: '100%',
150
148
  left: 0,
151
149
  bottom: '-1px',
152
150
  borderBottom: `1px solid ${theme.palette.divider}`,
151
+ display: 'block',
153
152
  },
154
153
  '&.selected': {
155
154
  backgroundColor:
@@ -193,7 +192,6 @@ export function ActionCard(props: ActionCardProps) {
193
192
  '&:not(:last-of-type)': {
194
193
  marginBottom: '-1px',
195
194
  },
196
-
197
195
  '&.layoutList': {
198
196
  borderRadius: 0,
199
197
  '&:first-of-type': {
@@ -299,7 +297,8 @@ export function ActionCard(props: ActionCardProps) {
299
297
  <Box
300
298
  className={classes.title}
301
299
  sx={{
302
- typography: 'subtitle2',
300
+ '&.sizeSmall': { typography: 'body2' },
301
+ '&.sizeMedium': { typography: 'body1' },
303
302
  '&.sizeLarge': { typography: 'h6' },
304
303
  }}
305
304
  >
@@ -0,0 +1,58 @@
1
+ import { Accordion, AccordionSummary, AccordionDetails, SxProps, Theme } from '@mui/material'
2
+ import { useState, ReactNode } from 'react'
3
+ import { IconSvg } from '../IconSvg'
4
+ import { iconChevronDown } from '../icons'
5
+
6
+ export type ActionCardAccordionProps = {
7
+ summary: ReactNode
8
+ details: ReactNode
9
+ right: ReactNode
10
+ sx?: SxProps<Theme>
11
+ defaultExpanded?: boolean
12
+ }
13
+
14
+ export function ActionCardAccordion(props: ActionCardAccordionProps) {
15
+ const { summary, details, right, defaultExpanded = true, sx } = props
16
+ const [expanded, setExpanded] = useState(defaultExpanded)
17
+ const handleChange = () => setExpanded(!expanded)
18
+
19
+ return (
20
+ <Accordion
21
+ square
22
+ onChange={handleChange}
23
+ expanded={expanded}
24
+ variant='outlined'
25
+ disableGutters
26
+ sx={[
27
+ (theme) => ({
28
+ backgroundColor: 'transparent ',
29
+ '&.Mui-expanded': { my: 0 },
30
+ '::before': { display: 'none' },
31
+ border: 'none',
32
+ '&:not(.Mui-expanded)': { borderBottom: `1px solid ${theme.palette.divider}` },
33
+ }),
34
+ ...(Array.isArray(sx) ? sx : [sx]),
35
+ ]}
36
+ >
37
+ <AccordionSummary
38
+ onClick={(e) => e.preventDefault()}
39
+ expandIcon={<IconSvg src={iconChevronDown} />}
40
+ sx={{
41
+ px: 0,
42
+ typography: 'h6',
43
+ minHeight: 54,
44
+ '& .MuiAccordionSummary-content': {
45
+ display: 'flex',
46
+ justifyContent: 'space-between',
47
+ alignItems: 'center',
48
+ my: 0,
49
+ },
50
+ }}
51
+ >
52
+ <div>{summary}</div>
53
+ {right}
54
+ </AccordionSummary>
55
+ <AccordionDetails sx={{ p: 0 }}>{details}</AccordionDetails>
56
+ </Accordion>
57
+ )
58
+ }
@@ -1,7 +1,11 @@
1
- import { Alert, Box, SxProps, Theme } from '@mui/material'
1
+ import { Trans } from '@lingui/react'
2
+ import { Alert, SxProps, Theme } from '@mui/material'
2
3
  import React from 'react'
3
4
  import { isFragment } from 'react-is'
5
+ import { Button } from '../Button'
6
+ import { IconSvg } from '../IconSvg'
4
7
  import { extendableComponent } from '../Styles'
8
+ import { iconChevronDown } from '../icons'
5
9
  import { ActionCardProps } from './ActionCard'
6
10
  import { ActionCardLayout } from './ActionCardLayout'
7
11
 
@@ -22,6 +26,7 @@ type Select = {
22
26
  }
23
27
 
24
28
  export type ActionCardListProps<SelectOrMulti = MultiSelect | Select> = {
29
+ showMoreAfter?: number
25
30
  children?: React.ReactNode
26
31
  required?: boolean
27
32
  error?: boolean
@@ -57,6 +62,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
57
62
  const {
58
63
  children,
59
64
  required,
65
+ showMoreAfter = 1000000,
60
66
  error = false,
61
67
  errorMessage,
62
68
  size = 'medium',
@@ -67,6 +73,8 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
67
73
  sx = [],
68
74
  } = props
69
75
 
76
+ const [show, setShow] = React.useState(false)
77
+
70
78
  const handleChange: ActionCardProps['onClick'] = isMulti(props)
71
79
  ? (event, v) => {
72
80
  const { onChange, value } = props
@@ -103,7 +111,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
103
111
  }
104
112
 
105
113
  // Make sure the children are cardlike
106
- const childReactNodes = React.Children.toArray(children)
114
+ const childActionCards = React.Children.toArray(children)
107
115
  .filter(React.isValidElement)
108
116
  .filter(isActionCardLike)
109
117
  .filter((child) => {
@@ -121,7 +129,7 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
121
129
  })
122
130
 
123
131
  // Make sure the selected values is in the list of all possible values
124
- const value = childReactNodes.find(
132
+ const value = childActionCards.find(
125
133
  // eslint-disable-next-line react/destructuring-assignment
126
134
  (child) => child.props.value === props.value && child.props.disabled !== true,
127
135
  )?.props.value
@@ -131,9 +139,10 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
131
139
  return (
132
140
  <div ref={ref}>
133
141
  <ActionCardLayout sx={sx} className={classes.root} layout={layout}>
134
- {childReactNodes.map((child) => {
142
+ {childActionCards.map((child, index) => {
135
143
  if (collapse && Boolean(value) && !isValueSelected(child.props.value, value))
136
144
  return null
145
+ if (index && showMoreAfter && index + 1 > showMoreAfter && !show) return null
137
146
  return React.cloneElement(child, {
138
147
  onClick: handleChange,
139
148
  error,
@@ -149,6 +158,25 @@ export const ActionCardList = React.forwardRef<HTMLDivElement, ActionCardListPro
149
158
  })
150
159
  })}
151
160
  </ActionCardLayout>
161
+
162
+ {childActionCards.length > showMoreAfter && (
163
+ <Button
164
+ sx={{ width: 'fit-content' }}
165
+ color='primary'
166
+ variant='text'
167
+ onClick={() => setShow(!show)}
168
+ >
169
+ {!show ? <Trans id='More options' /> : <Trans id='Less options' />}{' '}
170
+ <IconSvg
171
+ sx={{
172
+ transform: show ? 'rotate(180deg)' : 'rotate(0deg)',
173
+ transition: 'transform 0.3s ease-in-out',
174
+ }}
175
+ src={iconChevronDown}
176
+ />
177
+ </Button>
178
+ )}
179
+
152
180
  {error && errorMessage && (
153
181
  <Alert
154
182
  severity='error'
@@ -64,7 +64,7 @@ export function ActionCardListForm<
64
64
  {items.map((item) => (
65
65
  <RenderItem
66
66
  {...item}
67
- key={item.value ?? 'tralala'}
67
+ key={item.value ?? ''}
68
68
  value={item.value}
69
69
  selected={onSelect(item.value, value)}
70
70
  onReset={(e) => {
@@ -1,4 +1,5 @@
1
1
  export * from './ActionCard'
2
+ export * from './ActionCardAccordion'
2
3
  export * from './ActionCardLayout'
3
4
  export * from './ActionCardList'
4
5
  export * from './ActionCardListForm'
@@ -8,7 +8,7 @@ import { useDateTimeFormat } from '../../hooks'
8
8
  export type BlogListItemProps = {
9
9
  asset: React.ReactNode
10
10
  url: string
11
- date: string
11
+ date: string | undefined
12
12
  title: string
13
13
  sx?: SxProps<Theme>
14
14
  }
@@ -57,18 +57,20 @@ export function BlogListItem(props: BlogListItemProps) {
57
57
  </Box>
58
58
  </Link>
59
59
 
60
- <Box
61
- component='time'
62
- className={classes.date}
63
- dateTime={date}
64
- sx={(theme) => ({
65
- display: 'inline-block',
66
- textDecoration: 'none',
67
- color: theme.palette.text.secondary,
68
- })}
69
- >
70
- {formatter.format(new Date(date))}
71
- </Box>
60
+ {date && (
61
+ <Box
62
+ component='time'
63
+ className={classes.date}
64
+ dateTime={date}
65
+ sx={(theme) => ({
66
+ display: 'inline-block',
67
+ textDecoration: 'none',
68
+ color: theme.palette.text.secondary,
69
+ })}
70
+ >
71
+ {formatter.format(new Date(date))}
72
+ </Box>
73
+ )}
72
74
 
73
75
  <Link href={`/${url}`} className={classes.title} color='inherit' underline='hover'>
74
76
  <Typography component='h2' variant='h4'>
package/CHANGELOG.md CHANGED
@@ -1,5 +1,153 @@
1
1
  # Change Log
2
2
 
3
+ ## 6.2.0-canary.60
4
+
5
+ ## 6.2.0-canary.59
6
+
7
+ ## 6.2.0-canary.58
8
+
9
+ ## 6.2.0-canary.57
10
+
11
+ ## 6.2.0-canary.56
12
+
13
+ ## 6.2.0-canary.55
14
+
15
+ ## 6.2.0-canary.54
16
+
17
+ ## 6.2.0-canary.53
18
+
19
+ ## 6.2.0-canary.52
20
+
21
+ ## 6.2.0-canary.51
22
+
23
+ ## 6.2.0-canary.50
24
+
25
+ ### Minor Changes
26
+
27
+ - [`e55d8c390`](https://github.com/graphcommerce-org/graphcommerce/commit/e55d8c390d90b4bb7bab11c6a99027ac72bd7e3e) - Created a new sidebar layout system, can be configured with productFiltersLayout in the graphcommerce.config.js ([@paales](https://github.com/paales))
28
+
29
+ ## 6.2.0-canary.49
30
+
31
+ ## 6.2.0-canary.48
32
+
33
+ ### Minor Changes
34
+
35
+ - [#1961](https://github.com/graphcommerce-org/graphcommerce/pull/1961) [`4a759c662`](https://github.com/graphcommerce-org/graphcommerce/commit/4a759c66215eaa69edc342b898e05e8f92c3ba9a) - Add Open Graph meta tags to all pages ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
36
+
37
+ ## 6.2.0-canary.47
38
+
39
+ ## 6.2.0-canary.46
40
+
41
+ ## 6.2.0-canary.45
42
+
43
+ ## 6.2.0-canary.44
44
+
45
+ ## 6.2.0-canary.43
46
+
47
+ ### Patch Changes
48
+
49
+ - [#1965](https://github.com/graphcommerce-org/graphcommerce/pull/1965) [`44b2911d7`](https://github.com/graphcommerce-org/graphcommerce/commit/44b2911d73fb6c6c7188f16d5890ca93877e9aa7) - Added prop to LayoutHeader to be able to hide the back button ([@bramvanderholst](https://github.com/bramvanderholst))
50
+
51
+ ## 6.2.0-canary.42
52
+
53
+ ## 6.2.0-canary.41
54
+
55
+ ### Patch Changes
56
+
57
+ - [#1960](https://github.com/graphcommerce-org/graphcommerce/pull/1960) [`f78caf5a8`](https://github.com/graphcommerce-org/graphcommerce/commit/f78caf5a83683f1ae4b901fb94bd22d50943fa2f) - Updated packages @apollo/client, react-hook-form, @emotion/\*, @lingui/\*, @mui/\* and various others. ([@paales](https://github.com/paales))
58
+
59
+ ## 6.2.0-canary.40
60
+
61
+ ## 6.2.0-canary.39
62
+
63
+ ## 6.2.0-canary.38
64
+
65
+ ### Patch Changes
66
+
67
+ - [#1958](https://github.com/graphcommerce-org/graphcommerce/pull/1958) [`0a311b6eb`](https://github.com/graphcommerce-org/graphcommerce/commit/0a311b6ebb5a52e2a7f1d2e6a0fe113904fa2d34) - Left overlays wouldn't properly snap when the overlay gets wider than the viewport ([@paales](https://github.com/paales))
68
+
69
+ ## 6.2.0-canary.37
70
+
71
+ ## 6.2.0-canary.36
72
+
73
+ ## 6.2.0-canary.35
74
+
75
+ ## 6.2.0-canary.34
76
+
77
+ ## 6.2.0-canary.33
78
+
79
+ ## 6.2.0-canary.32
80
+
81
+ ## 6.2.0-canary.31
82
+
83
+ ## 6.2.0-canary.30
84
+
85
+ ## 6.2.0-canary.29
86
+
87
+ ### Patch Changes
88
+
89
+ - [#1946](https://github.com/graphcommerce-org/graphcommerce/pull/1946) [`87260618b`](https://github.com/graphcommerce-org/graphcommerce/commit/87260618b8abaebd727ff4435abb1aea6ed33730) - Firefox: scroll snap overlays would snap to 0 when the scroll snap targets wouldn’t exactly match the possible targets. ([@paales](https://github.com/paales))
90
+
91
+ ## 6.2.0-canary.28
92
+
93
+ ## 6.2.0-canary.27
94
+
95
+ ## 6.2.0-canary.26
96
+
97
+ ## 6.2.0-canary.25
98
+
99
+ ## 6.2.0-canary.24
100
+
101
+ ## 6.2.0-canary.23
102
+
103
+ ## 6.2.0-canary.22
104
+
105
+ ### Patch Changes
106
+
107
+ - [#1939](https://github.com/graphcommerce-org/graphcommerce/pull/1939) [`0cdccf681`](https://github.com/graphcommerce-org/graphcommerce/commit/0cdccf6817d6a769f59cccb68b1b1b8b4405cbd0) - Make blogListItem date prop optional ([@JoshuaS98](https://github.com/JoshuaS98))
108
+
109
+ ## 6.2.0-canary.21
110
+
111
+ ## 6.2.0-canary.20
112
+
113
+ ## 6.2.0-canary.19
114
+
115
+ ## 6.2.0-canary.18
116
+
117
+ ## 6.2.0-canary.17
118
+
119
+ ### Patch Changes
120
+
121
+ - [#1934](https://github.com/graphcommerce-org/graphcommerce/pull/1934) [`96ac0320a`](https://github.com/graphcommerce-org/graphcommerce/commit/96ac0320a30bc55a6db5d46663d28b552fda4db6) - Fixed floating overlays not closing when clicking beside the overlay ([@bramvanderholst](https://github.com/bramvanderholst))
122
+
123
+ ## 6.2.0-canary.16
124
+
125
+ ### Patch Changes
126
+
127
+ - [#1930](https://github.com/graphcommerce-org/graphcommerce/pull/1930) [`c8d023e9e`](https://github.com/graphcommerce-org/graphcommerce/commit/c8d023e9e874131cd9f8fe192b1fca5fe1a26ee3) - Fix the close menu on search and add the option to secondary menu items ([@StefanAngenent](https://github.com/StefanAngenent))
128
+
129
+ ## 6.2.0-canary.15
130
+
131
+ ## 6.2.0-canary.14
132
+
133
+ ## 6.2.0-canary.13
134
+
135
+ ## 6.2.0-canary.12
136
+
137
+ ## 6.2.0-canary.11
138
+
139
+ ## 6.2.0-canary.10
140
+
141
+ ## 6.2.0-canary.9
142
+
143
+ ### Patch Changes
144
+
145
+ - [#1897](https://github.com/graphcommerce-org/graphcommerce/pull/1897) [`f44d7cec6`](https://github.com/graphcommerce-org/graphcommerce/commit/f44d7cec61766f4768c30d29b211a12f2846e9f6) - Overlays can now be configured to get a bgColor ([@FrankHarland](https://github.com/FrankHarland))
146
+
147
+ ## 6.2.0-canary.8
148
+
149
+ ## 6.2.0-canary.7
150
+
3
151
  ## 6.2.0-canary.6
4
152
 
5
153
  ## 6.1.1-canary.5
package/Fab/Fab.tsx CHANGED
@@ -23,6 +23,7 @@ export function Fab(props: FabProps) {
23
23
  sx = [],
24
24
  icon,
25
25
  circularProgress,
26
+ color = 'default',
26
27
  ...fabProps
27
28
  } = props
28
29
 
@@ -33,7 +34,7 @@ export function Fab(props: FabProps) {
33
34
  return (
34
35
  <FabBase
35
36
  type='submit'
36
- color='primary'
37
+ color={color}
37
38
  size={size}
38
39
  {...fabProps}
39
40
  disabled={disabled}
@@ -45,6 +46,7 @@ export function Fab(props: FabProps) {
45
46
  {loading && (
46
47
  <CircularProgress
47
48
  size={fabSize}
49
+ color={color !== 'primary' ? 'primary' : 'inherit'}
48
50
  {...circularProgress}
49
51
  sx={[
50
52
  { display: 'flex', placeContent: 'center', gridArea: '1/1' },
@@ -53,42 +53,40 @@ export function useIconSvgSize(size: keyof typeof sizes) {
53
53
  return sizes[size]
54
54
  }
55
55
 
56
- const Svg = styled('svg', { name, target: name })(() => [
57
- {
58
- userSelect: 'none',
59
- width: '1em',
60
- height: '1em',
61
- display: 'inline-block',
62
-
63
- strokeLinecap: 'square',
64
- strokeLinejoin: 'miter',
65
- strokeMiterlimit: 4,
66
-
67
- fill: 'none',
68
- stroke: 'currentColor',
69
-
70
- fontSize: '1.3em',
71
-
72
- strokeWidth: svgIconStrokeWidth(28, 148, 1.4, 0.8),
73
-
74
- '&.sizeXs': { fontSize: sizes.xs },
75
- '&.sizeSmall': { fontSize: sizes.small },
76
- '&.sizeMedium': { fontSize: sizes.medium },
77
- '&.sizeLarge': { fontSize: sizes.large },
78
- '&.sizeXl': { fontSize: sizes.xl },
79
- '&.sizeXxl': { fontSize: sizes.xxl },
80
-
81
- '&.fillIcon': {
82
- fill: 'currentColor',
83
- stroke: 'none',
84
- },
56
+ const Svg = styled('svg', { name, target: name })(() => ({
57
+ userSelect: 'none',
58
+ width: '1em',
59
+ height: '1em',
60
+ display: 'inline-block',
61
+
62
+ strokeLinecap: 'square',
63
+ strokeLinejoin: 'miter',
64
+ strokeMiterlimit: 4,
65
+
66
+ fill: 'none',
67
+ stroke: 'currentColor',
68
+
69
+ fontSize: '1.3em',
70
+
71
+ strokeWidth: svgIconStrokeWidth(28, 148, 1.4, 0.8),
72
+
73
+ '&.sizeXs': { fontSize: sizes.xs },
74
+ '&.sizeSmall': { fontSize: sizes.small },
75
+ '&.sizeMedium': { fontSize: sizes.medium },
76
+ '&.sizeLarge': { fontSize: sizes.large },
77
+ '&.sizeXl': { fontSize: sizes.xl },
78
+ '&.sizeXxl': { fontSize: sizes.xxl },
79
+
80
+ '&.fillIcon': {
81
+ fill: 'currentColor',
82
+ stroke: 'none',
85
83
  },
86
- ])
84
+ }))
87
85
 
88
86
  /**
89
87
  * IconSvg component is supposed to be used in combination with `icons`
90
88
  *
91
- * @see https://graphcommerce-docs.vercel.app/framework/icons
89
+ * @see https://www.graphcommerce.org/docs/framework/icons
92
90
  */
93
91
  export const IconSvg = forwardRef<SVGSVGElement, IconSvgProps>((props, ref) => {
94
92
  const { src, size, fillIcon, className, ...svgProps } = useThemeProps({ props, name })
@@ -26,6 +26,8 @@ export type LayoutHeaderProps = FloatingProps &
26
26
  noAlign?: boolean
27
27
 
28
28
  sx?: SxProps<Theme>
29
+
30
+ hideBackButton?: boolean
29
31
  }
30
32
 
31
33
  type ComponentStyleProps = {
@@ -46,14 +48,16 @@ export function LayoutHeader(props: LayoutHeaderProps) {
46
48
  const {
47
49
  children,
48
50
  divider,
51
+ hideBackButton = false,
49
52
  primary,
50
53
  secondary,
51
54
  noAlign = false,
52
55
  switchPoint,
53
56
  size = 'responsive',
54
57
  sx = [],
58
+ bgColor,
55
59
  } = props
56
- const showBack = useShowBack()
60
+ const showBack = useShowBack() && !hideBackButton
57
61
  const showClose = useShowClose()
58
62
 
59
63
  const floatFallback = !children
@@ -149,6 +153,7 @@ export function LayoutHeader(props: LayoutHeaderProps) {
149
153
  floatingMd={floatingMd}
150
154
  floatingSm={floatingSm}
151
155
  switchPoint={switchPoint}
156
+ bgColor={bgColor}
152
157
  >
153
158
  {children}
154
159
  </LayoutHeaderContent>
@@ -18,6 +18,7 @@ export type LayoutHeaderContentProps = FloatingProps & {
18
18
  sxBg?: SxProps<Theme>
19
19
  layout?: LayoutProps['layout']
20
20
  size?: 'small' | 'responsive'
21
+ bgColor?: 'paper' | 'default'
21
22
  } & Pick<LayoutProps, 'layout' | 'layoutDependency'>
22
23
 
23
24
  type OwnerState = {
@@ -26,7 +27,9 @@ type OwnerState = {
26
27
  scrolled: boolean
27
28
  divider: boolean
28
29
  size: 'small' | 'responsive'
30
+ bgColor?: 'paper' | 'default'
29
31
  }
32
+
30
33
  const name = 'LayoutHeaderContent' as const
31
34
  const parts = ['bg', 'content', 'left', 'center', 'right', 'divider'] as const
32
35
  const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
@@ -47,12 +50,13 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
47
50
  layout,
48
51
  layoutDependency,
49
52
  size = 'responsive',
53
+ bgColor = 'paper',
50
54
  } = props
51
55
 
52
56
  const scroll = useScrollY()
53
57
  const scrolled = useMotionValueValue(scroll, (y) => y >= switchPoint)
54
58
 
55
- const classes = withState({ floatingSm, floatingMd, scrolled, divider: !!divider, size })
59
+ const classes = withState({ floatingSm, floatingMd, scrolled, divider: !!divider, size, bgColor })
56
60
 
57
61
  return (
58
62
  <>
@@ -63,7 +67,7 @@ export function LayoutHeaderContent(props: LayoutHeaderContentProps) {
63
67
  position: 'absolute',
64
68
  left: 0,
65
69
  width: '100%',
66
- backgroundColor: theme.palette.background.paper,
70
+ backgroundColor: theme.palette.background[bgColor],
67
71
  boxShadow: theme.shadows[1],
68
72
 
69
73
  height: theme.appShell.headerHeightSm,
@@ -127,7 +127,19 @@ export function LayoutDefault(props: LayoutDefaultProps) {
127
127
  })}
128
128
  >
129
129
  {menuFab}
130
- {cartFab}
130
+ <Box
131
+ sx={(theme) => ({
132
+ display: 'flex',
133
+ flexDirection: 'row-reverse',
134
+ gap: theme.spacings.sm,
135
+ [theme.breakpoints.up('md')]: {
136
+ flexDirection: 'column',
137
+ alignItems: 'flex-end',
138
+ },
139
+ })}
140
+ >
141
+ {cartFab}
142
+ </Box>
131
143
  </Box>
132
144
  ) : (
133
145
  <div />
@@ -1,6 +1,6 @@
1
1
  import { ListItemButton, ListItemIcon, ListItemText, SxProps, Theme } from '@mui/material'
2
2
  import { useRouter } from 'next/router'
3
- import React from 'react'
3
+ import React, { MouseEventHandler } from 'react'
4
4
  import { extendableComponent } from '../Styles'
5
5
  import { NextLink } from '../Theme'
6
6
 
@@ -9,6 +9,7 @@ export type FabMenuSecondaryItemProps = {
9
9
  children: React.ReactNode
10
10
  icon: React.ReactNode
11
11
  sx?: SxProps<Theme>
12
+ onClick?: MouseEventHandler<HTMLElement>
12
13
  }
13
14
 
14
15
  const compName = 'MenuFabSecondaryItem' as const
@@ -16,12 +17,18 @@ const parts = ['root', 'icon', 'text'] as const
16
17
  const { classes } = extendableComponent(compName, parts)
17
18
 
18
19
  export function MenuFabSecondaryItem(props: FabMenuSecondaryItemProps) {
19
- const { href, children, icon, sx = [] } = props
20
+ const { href, children, onClick, icon, sx = [] } = props
20
21
  const router = useRouter()
21
22
 
23
+ const handleClick: MouseEventHandler<HTMLElement> = (e) => {
24
+ e.preventDefault()
25
+ onClick?.(e)
26
+ return router.push(href)
27
+ }
28
+
22
29
  return (
23
30
  <ListItemButton
24
- href={href}
31
+ onClick={handleClick}
25
32
  component={NextLink}
26
33
  className={classes.root}
27
34
  sx={[{}, ...(Array.isArray(sx) ? sx : [sx])]}
@@ -1,25 +1,34 @@
1
- import { Container } from '@mui/material'
1
+ import { Container, SxProps, Theme } from '@mui/material'
2
2
  import React from 'react'
3
+ import { extendableComponent } from '../Styles'
3
4
 
4
5
  export type StickyBelowHeaderProps = {
5
6
  children: React.ReactNode
7
+ sx?: SxProps<Theme>
6
8
  }
7
9
 
10
+ const { classes } = extendableComponent('StickyBelowHeader', ['root'])
11
+
8
12
  /** - Makes the children sticky to the parent container */
9
13
  export function StickyBelowHeader(props: StickyBelowHeaderProps) {
14
+ const { sx = [] } = props
10
15
  return (
11
16
  <Container
12
- sx={(theme) => ({
13
- position: 'sticky',
14
- top: { xs: theme.appShell.headerHeightSm, md: `${theme.page.vertical} !important` },
15
- zIndex: 96,
16
- pointerEvents: 'none',
17
- '& > *': {
18
- pointerEvents: 'auto',
19
- },
20
- })}
17
+ className={classes.root}
21
18
  maxWidth={false}
22
19
  {...props}
20
+ sx={[
21
+ (theme) => ({
22
+ position: 'sticky',
23
+ top: { xs: theme.appShell.headerHeightSm, md: `${theme.page.vertical} !important` },
24
+ zIndex: 96,
25
+ pointerEvents: 'none',
26
+ '& > *': {
27
+ pointerEvents: 'auto',
28
+ },
29
+ }),
30
+ ...(Array.isArray(sx) ? sx : [sx]),
31
+ ]}
23
32
  />
24
33
  )
25
34
  }
@@ -32,6 +32,7 @@ type StyleProps = {
32
32
  sizeMd?: LayoutOverlaySize
33
33
  justifySm?: LayoutOverlayAlign
34
34
  justifyMd?: LayoutOverlayAlign
35
+ bgColor?: 'paper' | 'default'
35
36
  }
36
37
 
37
38
  type OverridableProps = {
@@ -102,6 +103,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
102
103
  sxBackdrop = [],
103
104
  active,
104
105
  onClosed,
106
+ bgColor = 'paper',
105
107
  direction = 1,
106
108
  offsetPageY = 0,
107
109
  isPresent,
@@ -329,10 +331,13 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
329
331
 
330
332
  const onClickAway = useCallback(
331
333
  (event: React.MouseEvent<HTMLDivElement>) => {
332
- const isTarget = event.target === beforeRef.current || event.target === overlayPaneRef.current
334
+ const isTarget =
335
+ event.target === beforeRef.current ||
336
+ event.target === overlayPaneRef.current ||
337
+ event.target === scrollerRef.current
333
338
  if (isTarget && snap.get()) closeOverlay()
334
339
  },
335
- [closeOverlay, snap],
340
+ [closeOverlay, snap, scrollerRef],
336
341
  )
337
342
 
338
343
  return (
@@ -405,7 +410,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
405
410
  height: '1px',
406
411
  top: 'calc(100% - 1px)',
407
412
  left: '0',
408
- background: theme.palette.background.paper,
413
+ background: theme.palette.background[bgColor],
409
414
  },
410
415
  },
411
416
  },
@@ -464,9 +469,14 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
464
469
  sx={(theme) => ({
465
470
  display: 'grid',
466
471
  gridArea: 'overlay',
467
- scrollSnapAlign: 'start',
468
472
  scrollSnapStop: 'always',
469
473
  pointerEvents: 'none',
474
+ '&.variantMdBottom, &.variantMdRight': {
475
+ scrollSnapAlign: 'end',
476
+ },
477
+ '&.variantMdLeft': {
478
+ scrollSnapAlign: 'start',
479
+ },
470
480
  [theme.breakpoints.down('md')]: {
471
481
  justifyContent: justifySm,
472
482
  alignItems: justifySm,
@@ -592,7 +602,7 @@ export function OverlayBase(incomingProps: LayoutOverlayBaseProps) {
592
602
  <Box
593
603
  className={classes.background}
594
604
  sx={(theme) => ({
595
- backgroundColor: theme.palette.background.paper,
605
+ backgroundColor: theme.palette.background[bgColor],
596
606
  paddingBottom: '0.1px',
597
607
  [theme.breakpoints.down('md')]: {
598
608
  minHeight: '100%',
@@ -31,6 +31,9 @@ export type PageMetaProps = {
31
31
  metaDescription?: string
32
32
  metaRobots?: MetaRobotsAll | MetaRobots[]
33
33
  children?: React.ReactNode
34
+ ogImage?: string | null
35
+ ogImageUseFallback?: boolean
36
+ ogType?: string | null
34
37
  }
35
38
 
36
39
  type PartialNextRouter = Pick<
@@ -90,7 +93,16 @@ export function useCanonical(incoming?: Canonical) {
90
93
 
91
94
  export function PageMeta(props: PageMetaProps) {
92
95
  const { active } = usePageContext()
93
- const { children, title, canonical: canonicalBare, metaDescription, metaRobots = ['all'] } = props
96
+ const {
97
+ children,
98
+ title,
99
+ canonical: canonicalBare,
100
+ metaDescription,
101
+ ogImage,
102
+ ogType,
103
+ ogImageUseFallback = false,
104
+ metaRobots = ['all'],
105
+ } = props
94
106
 
95
107
  const canonical = useCanonical(canonicalBare)
96
108
 
@@ -99,10 +111,20 @@ export function PageMeta(props: PageMetaProps) {
99
111
  <Head>
100
112
  <title>{title.trim()}</title>
101
113
  {metaDescription && (
102
- <meta name='description' content={metaDescription.trim()} key='meta-description' />
114
+ <>
115
+ <meta name='description' content={metaDescription.trim()} key='meta-description' />
116
+ <meta property='og:description' content={metaDescription.trim()} key='og-description' />
117
+ </>
103
118
  )}
104
119
  <meta name='robots' content={metaRobots.join(',')} key='meta-robots' />
105
120
  {canonical && <link rel='canonical' href={canonical} key='canonical' />}
121
+ <meta property='og:title' content={title.trim()} key='og-title' />
122
+ <meta property='og:type' content={ogType ?? 'website'} key='og-type' />
123
+ <meta property='og:url' content={canonical} key='og-url' />
124
+ {(ogImage || ogImageUseFallback) && (
125
+ <meta property='og:image' content={ogImage ?? '/open-graph-image.jpg'} key='og-image' />
126
+ )}
127
+
106
128
  {children}
107
129
  </Head>
108
130
  )
@@ -22,7 +22,7 @@ const { classes } = extendableComponent('Pagination', parts)
22
22
  * Read more: https://ahrefs.com/blog/rel-prev-next-pagination/
23
23
  */
24
24
  export function Pagination(props: PagePaginationProps) {
25
- const { count, page, renderLink, classes: styles, ...paginationProps } = props
25
+ const { count, page, renderLink, classes: styles, sx = [], ...paginationProps } = props
26
26
 
27
27
  const { items } = usePagination({
28
28
  count,
@@ -61,18 +61,21 @@ export function Pagination(props: PagePaginationProps) {
61
61
  return (
62
62
  <Box
63
63
  className={classes.root}
64
- sx={(theme) => ({
65
- margin: '0 auto',
66
- marginTop: theme.spacings.lg,
67
- marginBottom: theme.spacings.lg,
68
- display: 'flex',
69
- alignItems: 'center',
70
- justifyContent: 'center',
71
- gap: '6px',
72
- '& .Mui-disabled': {
73
- background: 'none',
74
- },
75
- })}
64
+ sx={[
65
+ (theme) => ({
66
+ margin: '0 auto',
67
+ marginTop: theme.spacings.lg,
68
+ marginBottom: theme.spacings.lg,
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ justifyContent: 'center',
72
+ gap: '6px',
73
+ '& .Mui-disabled': {
74
+ background: 'none',
75
+ },
76
+ }),
77
+ ...(Array.isArray(sx) ? sx : [sx]),
78
+ ]}
76
79
  >
77
80
  {page === 1 ? chevronLeft : renderLink(page - 1, chevronLeft, prevBtnProps)}
78
81
 
@@ -8,7 +8,7 @@ type FilterTypeByTypename<A extends TypeObject, Typename extends string> = A ext
8
8
  : never
9
9
  : never
10
10
 
11
- type TypeRenderMethod<P> = (props: P) => React.ReactElement | null
11
+ type TypeRenderMethod<P> = (props: P) => React.ReactNode
12
12
 
13
13
  type TypeRenderMap<
14
14
  T extends TypeObject,
@@ -70,9 +70,13 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
70
70
  onClose?.()
71
71
  }
72
72
 
73
- const preventAnimationBubble: React.MouseEventHandler<HTMLButtonElement> = (e) => {
74
- e.preventDefault()
73
+ const preventAnimationBubble = (
74
+ e: React.TouchEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>,
75
+ ) => {
75
76
  e.stopPropagation()
77
+ if (e.type === 'mousedown') {
78
+ e.preventDefault()
79
+ }
76
80
  }
77
81
 
78
82
  let icon = iconCheckmark
@@ -144,6 +148,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarProps) {
144
148
  size='small'
145
149
  onClick={hideSnackbar}
146
150
  onMouseDown={preventAnimationBubble}
151
+ onTouchStart={preventAnimationBubble}
147
152
  sx={(theme) => ({
148
153
  backgroundColor: lighten(theme.palette.background.paper, 0.1),
149
154
  })}
@@ -89,19 +89,19 @@ export const MuiButtonPill: ButtonVariants = [
89
89
  {
90
90
  props: { variant: 'pill', size: 'small' },
91
91
  style: ({ theme }) => ({
92
- '&:not(.Mui-disabled)': { boxShadow: theme.shadows[2] },
92
+ '&:not(.Mui-disabled):not(.MuiButton-disableElevation)': { boxShadow: theme.shadows[2] },
93
93
  }),
94
94
  },
95
95
  {
96
96
  props: { variant: 'pill', size: 'medium' },
97
97
  style: ({ theme }) => ({
98
- '&:not(.Mui-disabled)': { boxShadow: theme.shadows[4] },
98
+ '&:not(.Mui-disabled):not(.MuiButton-disableElevation)': { boxShadow: theme.shadows[4] },
99
99
  }),
100
100
  },
101
101
  {
102
102
  props: { variant: 'pill', size: 'large' },
103
103
  style: ({ theme }) => ({
104
- '&:not(.Mui-disabled)': { boxShadow: theme.shadows[6] },
104
+ '&:not(.Mui-disabled):not(.MuiButton-disableElevation)': { boxShadow: theme.shadows[6] },
105
105
  }),
106
106
  },
107
107
  {
@@ -0,0 +1,9 @@
1
+ <svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" aria-labelledby="compareIconTitle" stroke="#2329D6" stroke-width="1" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#2329D6">
2
+ <title id="compareIconTitle">Compare</title>
3
+ <symbol id="icon" viewBox="0 0 24 24">
4
+ <path d="M12 9L22 9" />
5
+ <path d="M16.5 4.5L12 9L16.5 13.5"/>
6
+ <path d="M12 15L2 15" />
7
+ <path d="M7.5 19.5L12 15L7.5 10.5" />
8
+ </symbol>
9
+ </svg>
package/icons/index.ts CHANGED
@@ -45,3 +45,4 @@ export { default as iconSearch } from './search.svg'
45
45
  export { default as iconPhone } from './smartphone.svg'
46
46
  export { default as iconStar } from './star.svg'
47
47
  export { default as iconSun } from './sun.svg'
48
+ export { default as iconCompare } from './compare-arrows.svg'
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": "6.2.0-canary.6",
5
+ "version": "6.2.0-canary.60",
6
6
  "author": "",
7
7
  "license": "MIT",
8
8
  "sideEffects": false,
@@ -14,29 +14,30 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@emotion/cache": "^11.10.5",
18
- "@emotion/react": "^11.10.6",
19
- "@emotion/server": "^11.4.0",
20
- "@emotion/styled": "^11.10.6",
21
- "@graphcommerce/framer-next-pages": "6.2.0-canary.6",
22
- "@graphcommerce/framer-scroller": "6.2.0-canary.6",
23
- "@graphcommerce/framer-utils": "6.2.0-canary.6",
24
- "@graphcommerce/image": "6.2.0-canary.6",
17
+ "@emotion/cache": "^11.11.0",
18
+ "@emotion/react": "^11.11.1",
19
+ "@emotion/server": "^11.11.0",
20
+ "@emotion/styled": "^11.11.0",
21
+ "@graphcommerce/framer-next-pages": "6.2.0-canary.60",
22
+ "@graphcommerce/framer-scroller": "6.2.0-canary.60",
23
+ "@graphcommerce/framer-utils": "6.2.0-canary.60",
24
+ "@graphcommerce/image": "6.2.0-canary.60",
25
25
  "cookie": "^0.5.0",
26
26
  "react-is": "^18.2.0",
27
27
  "schema-dts": "^1.1.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@graphcommerce/eslint-config-pwa": "6.2.0-canary.6",
31
- "@graphcommerce/prettier-config-pwa": "6.2.0-canary.6",
32
- "@graphcommerce/typescript-config-pwa": "6.2.0-canary.6",
30
+ "@graphcommerce/eslint-config-pwa": "6.2.0-canary.60",
31
+ "@graphcommerce/prettier-config-pwa": "6.2.0-canary.60",
32
+ "@graphcommerce/typescript-config-pwa": "6.2.0-canary.60",
33
33
  "@types/cookie": "^0.5.1",
34
- "@types/react-is": "^17.0.3",
35
- "typescript": "4.9.5"
34
+ "@types/react-is": "^18.2.0",
35
+ "typescript": "5.1.3"
36
36
  },
37
37
  "peerDependencies": {
38
- "@lingui/core": "^3.13.2",
39
- "@lingui/react": "^3.13.2",
38
+ "@lingui/core": "^4.2.1",
39
+ "@lingui/react": "^4.2.1",
40
+ "@lingui/macro": "^4.2.1",
40
41
  "@mui/lab": "^5.0.0-alpha.68",
41
42
  "@mui/material": "^5.10.16",
42
43
  "framer-motion": "^10.0.0",