@graphcommerce/magento-product 4.0.4 → 4.1.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/CHANGELOG.md +253 -436
- package/components/JsonLdProduct/{index.tsx → jsonLdProduct.tsx} +0 -0
- package/components/ProductAddToCart/ProductAddToCart.tsx +21 -6
- package/components/ProductListFilters/FilterCheckboxType.tsx +8 -6
- package/components/ProductListFilters/FilterEqualType.tsx +2 -2
- package/components/ProductListFilters/FilterRangeType.tsx +9 -26
- package/components/ProductListFilters/ProductListFilters.tsx +5 -5
- package/components/ProductListFiltersContainer/{index.tsx → ProductListFiltersContainer.tsx} +27 -23
- package/components/ProductListItem/{index.tsx → ProductListItem.tsx} +2 -2
- package/components/ProductListItems/ProductListItems.tsx +6 -0
- package/components/ProductListItems/ProductListItemsBase.tsx +2 -2
- package/components/ProductListItems/ProductListParamsProvider.tsx +1 -1
- package/components/ProductListItems/filterTypes.tsx +1 -1
- package/components/ProductListItems/getFilterTypes.ts +1 -2
- package/components/ProductListItems/renderer.tsx +2 -4
- package/components/ProductListLink/ProductListLink.tsx +35 -35
- package/components/ProductListPagination/{index.tsx → ProductListPagination.tsx} +3 -7
- package/components/ProductListPrice/{index.tsx → ProductListPrice.tsx} +1 -1
- package/components/ProductListSort/{index.tsx → ProductListSort.tsx} +2 -2
- package/components/ProductPageCategory/{index.ts → productPageCategory.ts} +1 -1
- package/components/ProductPageDescription/ProductPageDescription.tsx +72 -0
- package/components/ProductPageGallery/ProductImage.tsx +1 -1
- package/components/ProductPageGallery/{index.tsx → ProductPageGallery.tsx} +1 -1
- package/components/ProductPageGallery/ProductVideo.tsx +1 -2
- package/components/ProductPageMeta/{index.tsx → ProductPageMeta.tsx} +1 -2
- package/components/ProductShortDescription/{index.tsx → ProductShortDescription.tsx} +1 -1
- package/components/ProductSidebarDelivery/{index.tsx → ProductSidebarDelivery.tsx} +3 -3
- package/components/ProductSpecs/{index.tsx → ProductSpecs.tsx} +1 -1
- package/components/ProductWeight/{index.tsx → ProductWeight.tsx} +1 -1
- package/components/index.ts +16 -26
- package/index.ts +0 -2
- package/package.json +10 -10
- package/components/ProductListItems/index.tsx +0 -6
- package/components/ProductPageDescription/index.tsx +0 -63
|
File without changes
|
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
MessageSnackbar,
|
|
7
7
|
TextInputNumber,
|
|
8
8
|
iconChevronRight,
|
|
9
|
-
|
|
9
|
+
IconSvg,
|
|
10
10
|
extendableComponent,
|
|
11
|
+
AnimatedRow,
|
|
11
12
|
} from '@graphcommerce/next-ui'
|
|
12
13
|
import { Trans } from '@lingui/macro'
|
|
13
|
-
import { Divider, Typography, ButtonProps, Box } from '@mui/material'
|
|
14
|
+
import { Divider, Typography, ButtonProps, Box, Alert } from '@mui/material'
|
|
15
|
+
import { AnimatePresence } from 'framer-motion'
|
|
14
16
|
import PageLink from 'next/link'
|
|
15
17
|
import React from 'react'
|
|
16
18
|
import { ProductAddToCartDocument, ProductAddToCartMutationVariables } from './ProductAddToCart.gql'
|
|
@@ -24,7 +26,7 @@ const { classes, selectors } = extendableComponent('ProductAddToCart', [
|
|
|
24
26
|
|
|
25
27
|
export type AddToCartProps = React.ComponentProps<typeof ProductAddToCart>
|
|
26
28
|
|
|
27
|
-
export
|
|
29
|
+
export function ProductAddToCart(
|
|
28
30
|
props: Pick<ProductInterface, 'name'> & {
|
|
29
31
|
variables: Omit<ProductAddToCartMutationVariables, 'cartId'>
|
|
30
32
|
name: string
|
|
@@ -38,7 +40,7 @@ export default function ProductAddToCart(
|
|
|
38
40
|
defaultValues: { ...variables },
|
|
39
41
|
})
|
|
40
42
|
|
|
41
|
-
const { handleSubmit, formState, error, muiRegister, required } = form
|
|
43
|
+
const { handleSubmit, formState, error, muiRegister, required, data } = form
|
|
42
44
|
const submitHandler = handleSubmit(() => {})
|
|
43
45
|
|
|
44
46
|
return (
|
|
@@ -85,8 +87,21 @@ export default function ProductAddToCart(
|
|
|
85
87
|
|
|
86
88
|
<ApolloCartErrorAlert error={error} />
|
|
87
89
|
|
|
90
|
+
<AnimatePresence initial={false}>
|
|
91
|
+
{data?.addProductsToCart?.user_errors.map((e) => (
|
|
92
|
+
<AnimatedRow key={e?.code}>
|
|
93
|
+
<Alert severity='error'>{e?.message}</Alert>
|
|
94
|
+
</AnimatedRow>
|
|
95
|
+
))}
|
|
96
|
+
</AnimatePresence>
|
|
97
|
+
|
|
88
98
|
<MessageSnackbar
|
|
89
|
-
open={
|
|
99
|
+
open={
|
|
100
|
+
!formState.isSubmitting &&
|
|
101
|
+
formState.isSubmitSuccessful &&
|
|
102
|
+
!error?.message &&
|
|
103
|
+
!data?.addProductsToCart?.user_errors?.length
|
|
104
|
+
}
|
|
90
105
|
variant='pill'
|
|
91
106
|
action={
|
|
92
107
|
<PageLink href='/cart' passHref>
|
|
@@ -94,7 +109,7 @@ export default function ProductAddToCart(
|
|
|
94
109
|
size='medium'
|
|
95
110
|
variant='pill'
|
|
96
111
|
color='secondary'
|
|
97
|
-
endIcon={<
|
|
112
|
+
endIcon={<IconSvg src={iconChevronRight} />}
|
|
98
113
|
>
|
|
99
114
|
<Trans>View shopping cart</Trans>
|
|
100
115
|
</Button>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { cloneDeep } from '@graphcommerce/graphql'
|
|
2
|
-
import { iconCancelAlt,
|
|
2
|
+
import { iconCancelAlt, IconSvg } from '@graphcommerce/next-ui'
|
|
3
3
|
import { Chip, ChipProps, SxProps, Theme } from '@mui/material'
|
|
4
4
|
import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
|
|
5
5
|
import { useProductListParamsContext } from '../../hooks/useProductListParamsContext'
|
|
6
|
-
import ProductListLink from '../ProductListLink/ProductListLink'
|
|
6
|
+
import { ProductListLink } from '../ProductListLink/ProductListLink'
|
|
7
7
|
import { FilterIn } from './FilterEqualType'
|
|
8
8
|
import { ProductListFiltersFragment } from './ProductListFilters.gql'
|
|
9
9
|
|
|
10
10
|
type Filter = NonNullable<NonNullable<ProductListFiltersFragment['aggregations']>[number]>
|
|
11
|
-
export type FilterCheckboxTypeProps = Filter &
|
|
11
|
+
export type FilterCheckboxTypeProps = Filter &
|
|
12
|
+
Omit<ChipProps<'button'>, 'selected' | 'onDelete' | 'component'> & { sx?: SxProps<Theme> }
|
|
12
13
|
|
|
13
|
-
export
|
|
14
|
+
export function FilterCheckboxType(props: FilterCheckboxTypeProps) {
|
|
14
15
|
const { attribute_code, count, label, options, ...chipProps } = props
|
|
15
16
|
const { params } = useProductListParamsContext()
|
|
16
17
|
const currentFilter = params.filters[attribute_code]
|
|
@@ -32,7 +33,8 @@ export default function FilterCheckboxType(props: FilterCheckboxTypeProps) {
|
|
|
32
33
|
link={{ scroll: false, replace: true }}
|
|
33
34
|
>
|
|
34
35
|
<Chip
|
|
35
|
-
|
|
36
|
+
component='button'
|
|
37
|
+
color={isActive ? 'primary' : 'default'}
|
|
36
38
|
onDelete={
|
|
37
39
|
isActive
|
|
38
40
|
? () => {
|
|
@@ -45,7 +47,7 @@ export default function FilterCheckboxType(props: FilterCheckboxTypeProps) {
|
|
|
45
47
|
}
|
|
46
48
|
: undefined
|
|
47
49
|
}
|
|
48
|
-
deleteIcon={isActive ? <
|
|
50
|
+
deleteIcon={isActive ? <IconSvg src={iconCancelAlt} size='small' /> : undefined}
|
|
49
51
|
label={label}
|
|
50
52
|
clickable
|
|
51
53
|
{...chipProps}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { SetRequired } from 'type-fest'
|
|
12
12
|
import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
|
|
13
13
|
import { useProductListParamsContext } from '../../hooks/useProductListParamsContext'
|
|
14
|
-
import ProductListLink from '../ProductListLink/ProductListLink'
|
|
14
|
+
import { ProductListLink } from '../ProductListLink/ProductListLink'
|
|
15
15
|
import { ProductListFiltersFragment } from './ProductListFilters.gql'
|
|
16
16
|
|
|
17
17
|
type OwnerState = {
|
|
@@ -44,7 +44,7 @@ type Filter = NonNullable<NonNullable<ProductListFiltersFragment['aggregations']
|
|
|
44
44
|
|
|
45
45
|
type FilterEqualTypeProps = Filter & Omit<ChipMenuProps, 'selected'>
|
|
46
46
|
|
|
47
|
-
export
|
|
47
|
+
export function FilterEqualType(props: FilterEqualTypeProps) {
|
|
48
48
|
const { attribute_code, count, label, options, __typename, ...chipProps } = props
|
|
49
49
|
const { params } = useProductListParamsContext()
|
|
50
50
|
const replaceRoute = useProductListLinkReplace({ scroll: false })
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cloneDeep, FilterRangeTypeInput } from '@graphcommerce/graphql'
|
|
2
2
|
import { Money } from '@graphcommerce/magento-store'
|
|
3
3
|
import { ChipMenu, ChipMenuProps, extendableComponent } from '@graphcommerce/next-ui'
|
|
4
|
+
import { Trans } from '@lingui/macro'
|
|
4
5
|
import { Box, Slider } from '@mui/material'
|
|
5
6
|
import React, { useEffect } from 'react'
|
|
6
7
|
import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
|
|
@@ -12,11 +13,9 @@ type FilterRangeTypeProps = NonNullable<
|
|
|
12
13
|
> &
|
|
13
14
|
Omit<ChipMenuProps, 'selected'>
|
|
14
15
|
|
|
15
|
-
const sliderThumbWidth = 28
|
|
16
|
-
|
|
17
16
|
const { classes } = extendableComponent('FilterRangeType', ['root', 'container', 'slider'] as const)
|
|
18
17
|
|
|
19
|
-
export
|
|
18
|
+
export function FilterRangeType(props: FilterRangeTypeProps) {
|
|
20
19
|
const { attribute_code, label, options, ...chipProps } = props
|
|
21
20
|
const { params } = useProductListParamsContext()
|
|
22
21
|
const replaceRoute = useProductListLinkReplace({ scroll: false })
|
|
@@ -79,16 +78,16 @@ export default function FilterRangeType(props: FilterRangeTypeProps) {
|
|
|
79
78
|
|
|
80
79
|
if (from === min && to !== max)
|
|
81
80
|
currentLabel = (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
<Trans>
|
|
82
|
+
Below <Money round value={Number(currentFilter?.to)} />
|
|
83
|
+
</Trans>
|
|
85
84
|
)
|
|
86
85
|
|
|
87
86
|
if (from !== min && to === max)
|
|
88
87
|
currentLabel = (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
<Trans>
|
|
89
|
+
Above <Money round value={Number(currentFilter?.from)} />
|
|
90
|
+
</Trans>
|
|
92
91
|
)
|
|
93
92
|
|
|
94
93
|
if (from !== min && to !== max)
|
|
@@ -128,6 +127,7 @@ export default function FilterRangeType(props: FilterRangeTypeProps) {
|
|
|
128
127
|
<Slider
|
|
129
128
|
min={min}
|
|
130
129
|
max={max}
|
|
130
|
+
size='large'
|
|
131
131
|
aria-labelledby='range-slider'
|
|
132
132
|
value={value}
|
|
133
133
|
onChange={(e, newValue) => {
|
|
@@ -142,23 +142,6 @@ export default function FilterRangeType(props: FilterRangeTypeProps) {
|
|
|
142
142
|
}}
|
|
143
143
|
valueLabelDisplay='off'
|
|
144
144
|
className={classes.slider}
|
|
145
|
-
sx={(theme) => ({
|
|
146
|
-
maxWidth: `calc(100% - ${sliderThumbWidth}px)`,
|
|
147
|
-
margin: `${theme.spacings.xxs} auto`,
|
|
148
|
-
display: 'block',
|
|
149
|
-
paddingBottom: '32px',
|
|
150
|
-
'& .MuiSlider-rail': {
|
|
151
|
-
height: 4,
|
|
152
|
-
borderRadius: '10px',
|
|
153
|
-
},
|
|
154
|
-
'& .MuiSlider-track': {
|
|
155
|
-
height: 4,
|
|
156
|
-
},
|
|
157
|
-
'& .MuiSlider-thumb': {
|
|
158
|
-
width: sliderThumbWidth,
|
|
159
|
-
height: sliderThumbWidth,
|
|
160
|
-
},
|
|
161
|
-
})}
|
|
162
145
|
/>
|
|
163
146
|
</Box>
|
|
164
147
|
</ChipMenu>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ChipMenuProps } from '@graphcommerce/next-ui'
|
|
2
2
|
import { FilterTypes } from '../ProductListItems/filterTypes'
|
|
3
|
-
import FilterCheckboxType from './FilterCheckboxType'
|
|
4
|
-
import FilterEqualType from './FilterEqualType'
|
|
5
|
-
import FilterRangeType from './FilterRangeType'
|
|
3
|
+
import { FilterCheckboxType } from './FilterCheckboxType'
|
|
4
|
+
import { FilterEqualType } from './FilterEqualType'
|
|
5
|
+
import { FilterRangeType } from './FilterRangeType'
|
|
6
6
|
import { ProductListFiltersFragment } from './ProductListFilters.gql'
|
|
7
7
|
|
|
8
8
|
export type ProductFiltersProps = ProductListFiltersFragment & {
|
|
9
9
|
filterTypes: FilterTypes
|
|
10
10
|
} & Omit<ChipMenuProps, 'selected' | 'selectedLabel' | 'children' | 'label' | 'onDelete'>
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export function ProductListFilters(props: ProductFiltersProps) {
|
|
13
13
|
const { aggregations, filterTypes, ...chipMenuProps } = props
|
|
14
14
|
|
|
15
15
|
return (
|
|
@@ -52,7 +52,7 @@ export default function ProductListFilters(props: ProductFiltersProps) {
|
|
|
52
52
|
/>
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
|
-
console.
|
|
55
|
+
console.error(
|
|
56
56
|
'Filter not recognized',
|
|
57
57
|
aggregation.attribute_code,
|
|
58
58
|
filterTypes[aggregation.attribute_code],
|
package/components/ProductListFiltersContainer/{index.tsx → ProductListFiltersContainer.tsx}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
iconChevronLeft,
|
|
4
4
|
iconChevronRight,
|
|
5
5
|
responsiveVal,
|
|
6
|
-
|
|
6
|
+
IconSvg,
|
|
7
7
|
useScrollY,
|
|
8
8
|
extendableComponent,
|
|
9
9
|
} from '@graphcommerce/next-ui'
|
|
@@ -32,8 +32,8 @@ const parts = [
|
|
|
32
32
|
|
|
33
33
|
const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
|
|
34
34
|
|
|
35
|
-
export
|
|
36
|
-
const { children } = props
|
|
35
|
+
export function ProductListFiltersContainer(props: ProductListFiltersContainerProps) {
|
|
36
|
+
const { children, sx = [] } = props
|
|
37
37
|
const scrollY = useScrollY()
|
|
38
38
|
|
|
39
39
|
const [isSticky, setIsSticky] = useState<boolean>(false)
|
|
@@ -83,31 +83,33 @@ export default function ProductListFiltersContainer(props: ProductListFiltersCon
|
|
|
83
83
|
<MotionDiv
|
|
84
84
|
className={classes.wrapper}
|
|
85
85
|
ref={wrapperRef}
|
|
86
|
-
sx={
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
sx={[
|
|
87
|
+
(theme) => ({
|
|
88
|
+
display: 'flex',
|
|
89
|
+
justifyContent: 'center',
|
|
90
|
+
marginBottom: theme.spacings.sm,
|
|
91
|
+
position: 'sticky',
|
|
92
|
+
top: theme.page.vertical,
|
|
93
|
+
zIndex: 9,
|
|
94
|
+
margin: '0 auto',
|
|
95
|
+
maxWidth: `calc(100% - 96px - ${theme.spacings.sm} * 2)`,
|
|
96
|
+
[theme.breakpoints.down('md')]: {
|
|
97
|
+
textAlign: 'center',
|
|
98
|
+
maxWidth: 'unset',
|
|
99
|
+
margin: `0 calc(${theme.page.horizontal} * -1)`,
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
...(Array.isArray(sx) ? sx : [sx]),
|
|
103
|
+
]}
|
|
102
104
|
>
|
|
103
105
|
<ScrollerProvider scrollSnapAlign='none'>
|
|
104
106
|
<ScrollerButton
|
|
105
107
|
direction='left'
|
|
106
108
|
className={classes.sliderPrev}
|
|
107
109
|
size='small'
|
|
108
|
-
sx={{ position: 'absolute', top:
|
|
110
|
+
sx={{ position: 'absolute', top: 4, left: 2, zIndex: 10 }}
|
|
109
111
|
>
|
|
110
|
-
<
|
|
112
|
+
<IconSvg src={iconChevronLeft} />
|
|
111
113
|
</ScrollerButton>
|
|
112
114
|
<Box
|
|
113
115
|
className={classes.container}
|
|
@@ -121,6 +123,8 @@ export default function ProductListFiltersContainer(props: ProductListFiltersCon
|
|
|
121
123
|
background: theme.palette.background.default,
|
|
122
124
|
borderRadius: '99em',
|
|
123
125
|
},
|
|
126
|
+
display: 'grid',
|
|
127
|
+
alignItems: 'center',
|
|
124
128
|
})}
|
|
125
129
|
>
|
|
126
130
|
<Scroller
|
|
@@ -163,9 +167,9 @@ export default function ProductListFiltersContainer(props: ProductListFiltersCon
|
|
|
163
167
|
direction='right'
|
|
164
168
|
className={classes.sliderNext}
|
|
165
169
|
size='small'
|
|
166
|
-
sx={{ position: 'absolute', top:
|
|
170
|
+
sx={{ position: 'absolute', top: 4, right: 2, zIndex: 10 }}
|
|
167
171
|
>
|
|
168
|
-
<
|
|
172
|
+
<IconSvg src={iconChevronRight} />
|
|
169
173
|
</ScrollerButton>
|
|
170
174
|
</ScrollerProvider>
|
|
171
175
|
</MotionDiv>
|
|
@@ -7,7 +7,7 @@ import { useRouter } from 'next/router'
|
|
|
7
7
|
import React, { PropsWithChildren, useMemo } from 'react'
|
|
8
8
|
import { ProductListItemFragment } from '../../Api/ProductListItem.gql'
|
|
9
9
|
import { useProductLink } from '../../hooks/useProductLink'
|
|
10
|
-
import ProductListPrice from '../ProductListPrice'
|
|
10
|
+
import { ProductListPrice } from '../ProductListPrice/ProductListPrice'
|
|
11
11
|
|
|
12
12
|
const { classes, selectors } = extendableComponent('ProductListItem', [
|
|
13
13
|
'root',
|
|
@@ -47,7 +47,7 @@ export type ProductListItemProps = BaseProps & { sx?: SxProps<Theme> }
|
|
|
47
47
|
|
|
48
48
|
const StyledImage = styled(Image)({})
|
|
49
49
|
|
|
50
|
-
export
|
|
50
|
+
export function ProductListItem(props: ProductListItemProps) {
|
|
51
51
|
const {
|
|
52
52
|
subTitle,
|
|
53
53
|
topLeft,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ProductListItemsBase, ProductItemsGridProps } from './ProductListItemsBase'
|
|
2
|
+
import { renderer } from './renderer'
|
|
3
|
+
|
|
4
|
+
export function ProductListItems(props: Omit<ProductItemsGridProps, 'renderers'>) {
|
|
5
|
+
return <ProductListItemsBase renderers={renderer} {...props} />
|
|
6
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RenderType, responsiveVal } from '@graphcommerce/next-ui'
|
|
2
2
|
import { Box, BoxProps } from '@mui/material'
|
|
3
3
|
import { ProductListItemFragment } from '../../Api/ProductListItem.gql'
|
|
4
|
-
import { ProductListItemProps } from '../ProductListItem'
|
|
4
|
+
import { ProductListItemProps } from '../ProductListItem/ProductListItem'
|
|
5
5
|
import { ProductListItemRenderer } from './renderer'
|
|
6
6
|
|
|
7
7
|
export type ProductItemsGridProps = {
|
|
@@ -15,7 +15,7 @@ export type ProductItemsGridProps = {
|
|
|
15
15
|
sx?: BoxProps['sx']
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export
|
|
18
|
+
export function ProductListItemsBase(props: ProductItemsGridProps) {
|
|
19
19
|
const { items, sx = [], renderers, loadingEager = 0, size = 'normal' } = props
|
|
20
20
|
|
|
21
21
|
return (
|
|
@@ -2,7 +2,7 @@ import { PropsWithChildren, useState, useEffect, useMemo } from 'react'
|
|
|
2
2
|
import { productListParamsContext } from '../../context/productListParamsContext'
|
|
3
3
|
import { ProductListParams } from './filterTypes'
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function ProductListParamsProvider({
|
|
6
6
|
children,
|
|
7
7
|
value,
|
|
8
8
|
}: PropsWithChildren<{ value: ProductListParams }>) {
|
|
@@ -21,7 +21,7 @@ export type ProductListParams = Exact<{
|
|
|
21
21
|
|
|
22
22
|
type AnyFilterType = FilterEqualTypeInput | FilterMatchTypeInput | FilterRangeTypeInput | undefined
|
|
23
23
|
|
|
24
|
-
export function isFilterTypeEqual(filter
|
|
24
|
+
export function isFilterTypeEqual(filter?: unknown): filter is FilterEqualTypeInput {
|
|
25
25
|
return Boolean(
|
|
26
26
|
filter && ((filter as FilterEqualTypeInput).eq || (filter as FilterEqualTypeInput).in),
|
|
27
27
|
)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { gql, ApolloClient, NormalizedCacheObject } from '@graphcommerce/graphql'
|
|
2
|
-
import { Exact } from '@graphcommerce/graphql'
|
|
1
|
+
import { gql, ApolloClient, NormalizedCacheObject , Exact } from '@graphcommerce/graphql'
|
|
3
2
|
import { AllFilterInputTypes, FilterTypes } from './filterTypes'
|
|
4
3
|
|
|
5
4
|
const allFilterInputTypes: AllFilterInputTypes[] = [
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { TypeRenderer } from '@graphcommerce/next-ui'
|
|
2
2
|
import { ProductListItemFragment } from '../../Api/ProductListItem.gql'
|
|
3
|
-
import ProductListItem from '../ProductListItem'
|
|
3
|
+
import { ProductListItem } from '../ProductListItem/ProductListItem'
|
|
4
4
|
|
|
5
5
|
export type ProductListItemRenderer = TypeRenderer<ProductListItemFragment>
|
|
6
6
|
|
|
7
|
-
const renderer: ProductListItemRenderer = {
|
|
7
|
+
export const renderer: ProductListItemRenderer = {
|
|
8
8
|
SimpleProduct: ProductListItem,
|
|
9
9
|
ConfigurableProduct: ProductListItem,
|
|
10
10
|
BundleProduct: ProductListItem,
|
|
@@ -12,5 +12,3 @@ const renderer: ProductListItemRenderer = {
|
|
|
12
12
|
DownloadableProduct: ProductListItem,
|
|
13
13
|
GroupedProduct: ProductListItem,
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
export default renderer
|
|
@@ -13,41 +13,41 @@ export type ProductListLinkProps = PropsWithChildren<
|
|
|
13
13
|
}
|
|
14
14
|
>
|
|
15
15
|
|
|
16
|
-
const ProductListLink = React.forwardRef<HTMLAnchorElement, ProductListLinkProps>(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
export const ProductListLink = React.forwardRef<HTMLAnchorElement, ProductListLinkProps>(
|
|
17
|
+
(props, ref) => {
|
|
18
|
+
const { setParams } = useProductListParamsContext()
|
|
19
|
+
const {
|
|
20
|
+
children,
|
|
21
|
+
url,
|
|
22
|
+
sort,
|
|
23
|
+
currentPage,
|
|
24
|
+
pageSize,
|
|
25
|
+
filters,
|
|
26
|
+
search,
|
|
27
|
+
noLink,
|
|
28
|
+
link,
|
|
29
|
+
...linkProps
|
|
30
|
+
} = props
|
|
31
|
+
const newParams = { filters, sort, url, currentPage, pageSize, search }
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const productListLink = useProductListLink(newParams)
|
|
34
|
+
const updateParams = () => setParams(newParams)
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
// We're setting nofollow if a custom sort, pageSize, filters or search is set.
|
|
37
|
+
let rel: string | undefined
|
|
38
|
+
if (Object.keys(sort).length || pageSize || Object.keys(filters).length || search)
|
|
39
|
+
rel = 'nofollow'
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default ProductListLink
|
|
41
|
+
return (
|
|
42
|
+
<PageLink href={productListLink} passHref {...link}>
|
|
43
|
+
{noLink ? (
|
|
44
|
+
children
|
|
45
|
+
) : (
|
|
46
|
+
<Link rel={rel} underline='hover' {...linkProps} ref={ref} onClick={updateParams}>
|
|
47
|
+
{children}
|
|
48
|
+
</Link>
|
|
49
|
+
)}
|
|
50
|
+
</PageLink>
|
|
51
|
+
)
|
|
52
|
+
},
|
|
53
|
+
)
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { Pagination } from '@graphcommerce/next-ui'
|
|
2
2
|
import { PaginationProps } from '@mui/material'
|
|
3
|
-
import React from 'react'
|
|
4
3
|
import { useProductListParamsContext } from '../../hooks/useProductListParamsContext'
|
|
5
|
-
import ProductListLink from '../ProductListLink/ProductListLink'
|
|
4
|
+
import { ProductListLink } from '../ProductListLink/ProductListLink'
|
|
6
5
|
import { ProductListPaginationFragment } from './ProductListPagination.gql'
|
|
7
6
|
|
|
8
7
|
export type ProductPaginationProps = ProductListPaginationFragment &
|
|
9
8
|
Omit<PaginationProps, 'count' | 'defaultPage' | 'page' | 'renderItem'>
|
|
10
9
|
|
|
11
|
-
export
|
|
12
|
-
page_info,
|
|
13
|
-
...paginationProps
|
|
14
|
-
}: ProductPaginationProps) {
|
|
10
|
+
export function ProductListPagination({ page_info, ...paginationProps }: ProductPaginationProps) {
|
|
15
11
|
const { params } = useProductListParamsContext()
|
|
16
12
|
|
|
17
13
|
if (!page_info || !page_info.total_pages || !page_info.current_page) return null
|
|
@@ -20,7 +16,7 @@ export default function ProductListPagination({
|
|
|
20
16
|
<Pagination
|
|
21
17
|
count={page_info?.total_pages}
|
|
22
18
|
page={page_info?.current_page ?? 1}
|
|
23
|
-
renderLink={(
|
|
19
|
+
renderLink={(_, icon, btnProps) => (
|
|
24
20
|
<ProductListLink {...btnProps} {...params} currentPage={btnProps.page} color='inherit'>
|
|
25
21
|
{icon}
|
|
26
22
|
</ProductListLink>
|
|
@@ -10,7 +10,7 @@ const { classes, selectors } = extendableComponent('ProductListPrice', [
|
|
|
10
10
|
|
|
11
11
|
type ProductListPriceProps = ProductListPriceFragment & Pick<TypographyProps, 'sx'>
|
|
12
12
|
|
|
13
|
-
export
|
|
13
|
+
export function ProductListPrice(props: ProductListPriceProps) {
|
|
14
14
|
const { regular_price, final_price, sx } = props
|
|
15
15
|
|
|
16
16
|
return (
|
|
@@ -5,13 +5,13 @@ import { ListItem, ListItemText } from '@mui/material'
|
|
|
5
5
|
import React from 'react'
|
|
6
6
|
import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
|
|
7
7
|
import { useProductListParamsContext } from '../../hooks/useProductListParamsContext'
|
|
8
|
-
import ProductListLink from '../ProductListLink/ProductListLink'
|
|
8
|
+
import { ProductListLink } from '../ProductListLink/ProductListLink'
|
|
9
9
|
import { ProductListSortFragment } from './ProductListSort.gql'
|
|
10
10
|
|
|
11
11
|
export type ProductListSortProps = ProductListSortFragment &
|
|
12
12
|
Omit<ChipMenuProps, 'selected' | 'selectedLabel' | 'children' | 'label' | 'onDelete'>
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export function ProductListSort(props: ProductListSortProps) {
|
|
15
15
|
const { sort_fields, total_count, ...filterMenuProps } = props
|
|
16
16
|
const { params } = useProductListParamsContext()
|
|
17
17
|
const replaceRoute = useProductListLinkReplace()
|
|
@@ -6,7 +6,7 @@ import { ProductPageCategoryFragment } from './ProductPageCategory.gql'
|
|
|
6
6
|
* - Prefers categories that are included in the menu
|
|
7
7
|
* - Prefers categories that have a longer path than shorter ones.
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
9
|
+
export function productPageCategory(product?: ProductPageCategoryFragment | null) {
|
|
10
10
|
if (!product?.categories?.length) return undefined
|
|
11
11
|
return product?.categories?.reduce((carry, value) => {
|
|
12
12
|
if (!value?.include_in_menu) return carry
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ColumnTwoWithTop,
|
|
3
|
+
ColumnTwoWithTopProps,
|
|
4
|
+
extendableComponent,
|
|
5
|
+
responsiveVal,
|
|
6
|
+
} from '@graphcommerce/next-ui'
|
|
7
|
+
import { Box, SxProps, Theme, Typography } from '@mui/material'
|
|
8
|
+
import { Variant } from '@mui/material/styles/createTypography'
|
|
9
|
+
import { ProductPageDescriptionFragment } from './ProductPageDescription.gql'
|
|
10
|
+
|
|
11
|
+
export type ProductPageDescriptionProps = ProductPageDescriptionFragment &
|
|
12
|
+
Omit<ColumnTwoWithTopProps, 'top' | 'left'> & {
|
|
13
|
+
sx?: SxProps<Theme>
|
|
14
|
+
fontSize: 'responsive' | Variant
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const componentName = 'ProductPageDescription'
|
|
18
|
+
const parts = ['root', 'description'] as const
|
|
19
|
+
|
|
20
|
+
const { classes } = extendableComponent(componentName, parts)
|
|
21
|
+
|
|
22
|
+
export function ProductPageDescription(props: ProductPageDescriptionProps) {
|
|
23
|
+
const { description, name, right, fontSize = 'subtitle1', sx = [] } = props
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ColumnTwoWithTop
|
|
27
|
+
className={classes.root}
|
|
28
|
+
sx={sx}
|
|
29
|
+
top={
|
|
30
|
+
<Typography variant='h1' component='h2'>
|
|
31
|
+
{name}
|
|
32
|
+
</Typography>
|
|
33
|
+
}
|
|
34
|
+
left={
|
|
35
|
+
description && (
|
|
36
|
+
<Box
|
|
37
|
+
className={classes.description}
|
|
38
|
+
// eslint-disable-next-line react/no-danger
|
|
39
|
+
dangerouslySetInnerHTML={{ __html: description.html }}
|
|
40
|
+
sx={[
|
|
41
|
+
{
|
|
42
|
+
'& p:first-of-type': {
|
|
43
|
+
marginTop: 0,
|
|
44
|
+
},
|
|
45
|
+
'& ul': {
|
|
46
|
+
padding: 0,
|
|
47
|
+
margin: 0,
|
|
48
|
+
display: 'inline',
|
|
49
|
+
listStyleType: 'none',
|
|
50
|
+
},
|
|
51
|
+
'& li': {
|
|
52
|
+
display: 'inline',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
fontSize === 'responsive' && {
|
|
56
|
+
'& p, & li': {
|
|
57
|
+
fontSize: responsiveVal(16, 30),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
fontSize !== 'responsive' && {
|
|
61
|
+
'& p, & li': {
|
|
62
|
+
fontSize,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
right={right}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Image } from '@graphcommerce/image'
|
|
2
2
|
import { ProductImageFragment } from './ProductImage.gql'
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export function ProductImage(props: ProductImageFragment) {
|
|
5
5
|
const { url, label } = props
|
|
6
6
|
|
|
7
7
|
if (!url) return null
|
|
@@ -9,7 +9,7 @@ export type ProductPageGalleryRenderers = TypeRenderer<
|
|
|
9
9
|
type ProductPageGalleryProps = PropsWithChildren<ProductPageGalleryFragment> &
|
|
10
10
|
Omit<SidebarGalleryProps, 'sidebar' | 'images'>
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export function ProductPageGallery(props: ProductPageGalleryProps) {
|
|
13
13
|
const {
|
|
14
14
|
media_gallery,
|
|
15
15
|
children,
|