@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.
- package/ActionCard/ActionCard.tsx +129 -0
- package/ActionCard/ActionCardList.tsx +110 -0
- package/CHANGELOG.md +13 -0
- package/LayoutDefault/components/LayoutDefault.tsx +1 -1
- package/Pagination/Pagination.tsx +4 -1
- package/Snackbar/MessageSnackbarImpl.tsx +9 -1
- package/index.ts +2 -0
- package/package.json +2 -2
|
@@ -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.
|
|
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'>
|
|
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
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.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.
|
|
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",
|