@graphcommerce/next-ui 4.7.2 → 4.8.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.
@@ -0,0 +1,129 @@
1
+ import { Theme } from '@emotion/react'
2
+ import { SxProps, ButtonBase, Box } from '@mui/material'
3
+ import React, { FormEvent } from 'react'
4
+
5
+ type ActionCardProps = {
6
+ sx?: SxProps<Theme>
7
+ title?: string | React.ReactNode
8
+ image?: React.ReactNode
9
+ action?: React.ReactNode
10
+ details?: React.ReactNode
11
+ secondaryAction?: React.ReactNode
12
+ onClick?: (e: FormEvent<HTMLButtonElement>, v: string) => void
13
+ onChange?: (e: FormEvent<HTMLButtonElement>, v: string) => void
14
+ selected?: boolean
15
+ hidden?: boolean | (() => boolean)
16
+ value: string
17
+ reset?: React.ReactNode
18
+ }
19
+
20
+ export function ActionCard(props: ActionCardProps) {
21
+ const {
22
+ title,
23
+ image,
24
+ action,
25
+ details,
26
+ secondaryAction,
27
+ sx = [],
28
+ onChange,
29
+ onClick,
30
+ value,
31
+ selected,
32
+ hidden,
33
+ reset,
34
+ } = props
35
+
36
+ const handleChange = (event: FormEvent<HTMLButtonElement>) => onChange?.(event, value)
37
+ const handleClick = (event: FormEvent<HTMLButtonElement>) => {
38
+ if (onClick) {
39
+ onClick(event, value)
40
+ if (event.isDefaultPrevented()) return
41
+ }
42
+ handleChange(event)
43
+ }
44
+
45
+ const actionButtonStyles: SxProps = {
46
+ '& .MuiButton-root': {
47
+ '&.MuiButton-textSecondary': {
48
+ padding: '5px',
49
+ margin: '-5px',
50
+ '&:hover': {
51
+ background: 'none',
52
+ },
53
+ },
54
+ },
55
+ }
56
+
57
+ return (
58
+ <ButtonBase
59
+ component='button'
60
+ className='ActionCard-root'
61
+ onClick={handleClick}
62
+ onChange={handleChange}
63
+ value={value}
64
+ sx={[
65
+ {
66
+ display: 'grid',
67
+ width: '100%',
68
+ gridTemplateColumns: 'min-content',
69
+ gridTemplateAreas: `
70
+ "image title action"
71
+ "image details secondaryDetails"
72
+ "image secondaryAction additionalDetails"
73
+ "additionalContent additionalContent additionalContent"
74
+ `,
75
+ justifyContent: 'unset',
76
+ },
77
+ (theme) => ({
78
+ typography: 'body1',
79
+ textAlign: 'left',
80
+ background: theme.palette.background.paper,
81
+ padding: `calc(${theme.spacings.xs} + 1px)`,
82
+ columnGap: theme.spacings.xxs,
83
+ border: `1px solid ${theme.palette.divider}`,
84
+ borderBottomColor: `transparent`,
85
+ '&:first-of-type': {
86
+ borderTopLeftRadius: theme.shape.borderRadius,
87
+ borderTopRightRadius: theme.shape.borderRadius,
88
+ },
89
+ '&:last-of-type': {
90
+ borderBottomLeftRadius: theme.shape.borderRadius,
91
+ borderBottomRightRadius: theme.shape.borderRadius,
92
+ borderBottom: `1px solid ${theme.palette.divider}`,
93
+ },
94
+ }),
95
+ !!hidden && {
96
+ display: 'none',
97
+ },
98
+ !!selected &&
99
+ ((theme) => ({
100
+ border: `2px solid ${theme.palette.secondary.main} !important`,
101
+ borderTopLeftRadius: theme.shape.borderRadius,
102
+ borderTopRightRadius: theme.shape.borderRadius,
103
+ borderBottomLeftRadius: theme.shape.borderRadius,
104
+ borderBottomRightRadius: theme.shape.borderRadius,
105
+ padding: theme.spacings.xs,
106
+ })),
107
+ ...(Array.isArray(sx) ? sx : [sx]),
108
+ ]}
109
+ >
110
+ {image && <Box sx={{ gridArea: 'image', justifySelf: 'center', padding: 1 }}>{image}</Box>}
111
+ {title && <Box sx={{ gridArea: 'title', fontWeight: 'bold' }}>{title}</Box>}
112
+ {action && (
113
+ <Box
114
+ sx={{
115
+ gridArea: 'action',
116
+ textAlign: 'right',
117
+ ...actionButtonStyles,
118
+ }}
119
+ >
120
+ {!selected ? action : reset}
121
+ </Box>
122
+ )}
123
+ {details && <Box sx={{ gridArea: 'details', color: 'text.secondary' }}>{details}</Box>}
124
+ {secondaryAction && (
125
+ <Box sx={{ gridArea: 'secondaryAction', ...actionButtonStyles }}>{secondaryAction}</Box>
126
+ )}
127
+ </ButtonBase>
128
+ )
129
+ }
@@ -0,0 +1,110 @@
1
+ import { Box } from '@mui/material'
2
+ import React from 'react'
3
+ import { isFragment } from 'react-is'
4
+
5
+ type MultiSelect = {
6
+ multiple: true
7
+ value: string[]
8
+
9
+ onChange?: (event: React.MouseEvent<HTMLElement>, value: string[]) => void
10
+ }
11
+ type Select = {
12
+ multiple?: false
13
+ value: string
14
+
15
+ /** Value is null when deselected when not required */
16
+ onChange?: (event: React.MouseEvent<HTMLElement>, value: string | null) => void
17
+ }
18
+
19
+ type ActionCardListProps<SelectOrMulti = MultiSelect | Select> = {
20
+ children?: React.ReactNode
21
+ required?: boolean
22
+ error?: boolean
23
+ } & SelectOrMulti
24
+
25
+ function isMulti(props: ActionCardListProps): props is ActionCardListProps<MultiSelect> {
26
+ return props.multiple === true
27
+ }
28
+
29
+ function isValueSelected(value: string, candidate: string | string[]) {
30
+ if (candidate === undefined || value === undefined) return false
31
+ if (Array.isArray(candidate)) return candidate.indexOf(value) >= 0
32
+ return value === candidate
33
+ }
34
+
35
+ export function ActionCardList(props: ActionCardListProps) {
36
+ const { children, required, value, error = false } = props
37
+
38
+ const handleChange = isMulti(props)
39
+ ? (event: React.MouseEvent<HTMLElement, MouseEvent>, buttonValue: string) => {
40
+ const { onChange } = props
41
+ const index = Boolean(value) && value?.indexOf(buttonValue)
42
+ let newValue: string[]
43
+
44
+ if (Array.isArray(value) && value.length && index && index >= 0) {
45
+ newValue = value.slice()
46
+ newValue.splice(index, 1)
47
+ } else {
48
+ newValue = value ? [...value, buttonValue] : [buttonValue]
49
+ }
50
+ onChange?.(event, newValue)
51
+ }
52
+ : (event: React.MouseEvent<HTMLElement, MouseEvent>, buttonValue: string) => {
53
+ const { onChange } = props
54
+
55
+ if (value === buttonValue) return
56
+ if (required) onChange?.(event, buttonValue)
57
+ else onChange?.(event, value === buttonValue ? null : buttonValue)
58
+ }
59
+
60
+ return (
61
+ <Box
62
+ sx={[
63
+ error &&
64
+ ((theme) => ({
65
+ '& .ActionCard-root': {
66
+ borderLeft: 2,
67
+ borderRight: 2,
68
+ borderLeftColor: 'error.main',
69
+ borderRightColor: 'error.main',
70
+ paddingLeft: theme.spacings.xs,
71
+ paddingRight: theme.spacings.xs,
72
+ },
73
+ '& .ActionCard-root:first-of-type': {
74
+ borderTop: 2,
75
+ borderTopColor: 'error.main',
76
+ paddingTop: theme.spacings.xs,
77
+ },
78
+ '& .ActionCard-root:last-of-type': {
79
+ borderBottom: 2,
80
+ borderBottomColor: 'error.main',
81
+ paddingBottom: theme.spacings.xs,
82
+ },
83
+ })),
84
+ ]}
85
+ >
86
+ {React.Children.map(children, (child) => {
87
+ if (!React.isValidElement(child)) return null
88
+
89
+ if (process.env.NODE_ENV !== 'production') {
90
+ if (isFragment(child)) {
91
+ console.error(
92
+ [
93
+ "@graphcommerce/next-ui: The ActionCardList component doesn't accept a Fragment as a child.",
94
+ 'Consider providing an array instead.',
95
+ ].join('\n'),
96
+ )
97
+ }
98
+ }
99
+
100
+ return React.cloneElement(child, {
101
+ onClick: handleChange,
102
+ selected:
103
+ child.props.selected === undefined
104
+ ? isValueSelected(child.props.value as string, value)
105
+ : child.props.selected,
106
+ })
107
+ })}
108
+ </Box>
109
+ )
110
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1462](https://github.com/graphcommerce-org/graphcommerce/pull/1462) [`3ac90b57c`](https://github.com/graphcommerce-org/graphcommerce/commit/3ac90b57c68b96f9d81771d6664ed9435a28fc1d) Thanks [@mikekeehnen](https://github.com/mikekeehnen)! - Added translation for the pagination
8
+
9
+ ### Patch Changes
10
+
11
+ - [#1467](https://github.com/graphcommerce-org/graphcommerce/pull/1467) [`0363b9671`](https://github.com/graphcommerce-org/graphcommerce/commit/0363b9671db7c2932321d97faf6f1eb385238397) Thanks [@timhofman](https://github.com/timhofman)! - optional feedback message upon adding products to wishlist
12
+
13
+ - Updated dependencies []:
14
+ - @graphcommerce/framer-scroller@2.1.11
15
+
3
16
  ## 4.7.2
4
17
 
5
18
  ### Patch Changes
@@ -58,7 +58,7 @@ export function LayoutDefault(props: LayoutDefaultProps) {
58
58
  display: 'grid',
59
59
  gridTemplateRows: `auto auto 1fr auto`,
60
60
  gridTemplateColumns: '100%',
61
- background: theme.palette.background.default,
61
+ background: theme.palette.background.paper,
62
62
  }),
63
63
  ...(Array.isArray(sx) ? sx : [sx]),
64
64
  ]}
@@ -1,3 +1,4 @@
1
+ import { Trans } from '@lingui/react'
1
2
  import { PaginationProps, Box, SxProps, Theme, IconButton } from '@mui/material'
2
3
  import usePagination, { UsePaginationItem } from '@mui/material/usePagination'
3
4
  import React from 'react'
@@ -75,7 +76,9 @@ export function Pagination(props: PagePaginationProps) {
75
76
  >
76
77
  {page === 1 ? chevronLeft : renderLink(page - 1, chevronLeft, prevBtnProps)}
77
78
 
78
- <Box typography='body1'>Page {`${page} of ${Math.max(1, count)}`}</Box>
79
+ <Box typography='body1'>
80
+ <Trans id='Page {page} of {count}' values={{ page, count: Math.max(1, count) }} />
81
+ </Box>
79
82
 
80
83
  {page === count ? chevronRight : renderLink(page + 1, chevronRight, nextBtnProps)}
81
84
  </Box>
@@ -64,11 +64,18 @@ export default function MessageSnackbarImpl(props: MessageSnackbarImplProps) {
64
64
  setShowSnackbar(!!open)
65
65
  }, [open])
66
66
 
67
- const hideSnackbar = () => {
67
+ const hideSnackbar = (e) => {
68
+ e.preventDefault()
69
+
68
70
  setShowSnackbar(false)
69
71
  onClose?.()
70
72
  }
71
73
 
74
+ const preventAnimationBubble: React.MouseEventHandler<HTMLButtonElement> = (e) => {
75
+ e.preventDefault()
76
+ e.stopPropagation()
77
+ }
78
+
72
79
  let icon = iconCheckmark
73
80
  if (severity === 'error') icon = iconSadFace
74
81
 
@@ -131,6 +138,7 @@ export default function MessageSnackbarImpl(props: MessageSnackbarImplProps) {
131
138
  aria-label='Close'
132
139
  size='small'
133
140
  onClick={hideSnackbar}
141
+ onMouseDown={preventAnimationBubble}
134
142
  sx={(theme) => ({
135
143
  backgroundColor: lighten(theme.palette.background.paper, 0.1),
136
144
  })}
package/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './ActionCard/ActionCardList'
2
+ export * from './ActionCard/ActionCard'
1
3
  export * from './AnimatedRow/AnimatedRow'
2
4
  export * from './Blog/BlogAuthor/BlogAuthor'
3
5
  export * from './Blog/BlogContent/BlogContent'
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.7.2",
5
+ "version": "4.8.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.6.0",
22
22
  "@graphcommerce/framer-next-pages": "3.2.1",
23
- "@graphcommerce/framer-scroller": "2.1.10",
23
+ "@graphcommerce/framer-scroller": "2.1.11",
24
24
  "@graphcommerce/framer-utils": "3.1.2",
25
25
  "@graphcommerce/image": "3.1.5",
26
26
  "react-is": "^17.0.0",